diff options
author | Daniel Genrich <daniel.genrich@gmx.net> | 2014-10-23 17:12:28 +0400 |
---|---|---|
committer | Daniel Genrich <daniel.genrich@gmx.net> | 2014-10-23 17:12:28 +0400 |
commit | 9ff1ebed52e0f858a395eeea4caf89304e068b2d (patch) | |
tree | b05d0f4b229de61b088a128ad412dd7bba347928 /source/blender/editors | |
parent | a2ed11c6eeab5fab8cb81e32e1c68fdafdd5dbbc (diff) | |
parent | eaaeae469968c5c78a5d7e6d202f1af00b382a79 (diff) |
Merge remote-tracking branch 'origin/master' into soc-2014-fluid
Conflicts:
.gitignore
intern/cycles/CMakeLists.txt
source/blender/blenkernel/intern/smoke.c
source/blender/python/intern/bpy_interface.c
source/creator/CMakeLists.txt
Diffstat (limited to 'source/blender/editors')
335 files changed, 18386 insertions, 7108 deletions
diff --git a/source/blender/editors/animation/CMakeLists.txt b/source/blender/editors/animation/CMakeLists.txt index d6030a967d5..b9f50a6cdf1 100644 --- a/source/blender/editors/animation/CMakeLists.txt +++ b/source/blender/editors/animation/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -57,4 +59,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_animation "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/animation/SConscript b/source/blender/editors/animation/SConscript index 91d480978c4..ed4b794cbce 100644 --- a/source/blender/editors/animation/SConscript +++ b/source/blender/editors/animation/SConscript @@ -31,17 +31,19 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 1cd1a26678d..7d8e278f0cf 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -344,7 +344,7 @@ static void acf_generic_idblock_name(bAnimListElem *ale, char *name) /* name property for ID block entries */ static bool acf_generic_idblock_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop) { - RNA_id_pointer_create(ale->id, ptr); + RNA_id_pointer_create(ale->data, ptr); *prop = RNA_struct_name_property(ptr->type); return (*prop != NULL); @@ -650,6 +650,15 @@ static void acf_object_name(bAnimListElem *ale, char *name) BLI_strncpy(name, ob->id.name + 2, ANIM_CHAN_NAME_SIZE); } +/* name property for object */ +static bool acf_object_name_prop(bAnimListElem *ale, PointerRNA *ptr, PropertyRNA **prop) +{ + RNA_id_pointer_create(ale->id, ptr); + *prop = RNA_struct_name_property(ptr->type); + + return (*prop != NULL); +} + /* check if some setting exists for this channel */ static bool acf_object_setting_valid(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting) { @@ -740,7 +749,7 @@ static bAnimChannelType ACF_OBJECT = NULL, /* offset */ acf_object_name, /* name */ - acf_generic_idblock_name_prop, /* name prop */ + acf_object_name_prop, /* name prop */ acf_object_icon, /* icon */ acf_object_setting_valid, /* has setting */ @@ -3219,7 +3228,7 @@ void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level) /* Check if some setting for a channel is enabled * Returns: 1 = On, 0 = Off, -1 = Invalid */ -short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting) +short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting) { bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); @@ -3292,7 +3301,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting * - setting: eAnimChannel_Settings * - mode: eAnimChannels_SetFlag */ -void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting, short mode) +void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode) { bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); @@ -3569,7 +3578,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void ANIM_flush_setting_anim_channels(&ac, &anim_data, ale_setting, setting, on); /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* callback for wrapping NLA Track "solo" toggle logic */ @@ -3837,7 +3846,7 @@ void ANIM_channel_draw_widgets(bContext *C, bAnimContext *ac, bAnimListElem *ale short offset; /* sanity checks - don't draw anything */ - if (ELEM3(NULL, acf, ale, block)) + if (ELEM(NULL, acf, ale, block)) return; /* get initial offset */ diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 5d97b7be9f7..b6ab0407711 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -49,6 +49,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "BKE_animsys.h" #include "BKE_action.h" #include "BKE_fcurve.h" #include "BKE_gpencil.h" @@ -74,7 +75,7 @@ /* Set the given animation-channel as the active one for the active context */ // TODO: extend for animdata types... -void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type) +void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -173,6 +174,8 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f case ANIMTYPE_DSLAT: case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: + case ANIMTYPE_DSNTREE: + case ANIMTYPE_DSTEX: { /* need to verify that this data is valid for now */ if (ale && ale->adt) { @@ -180,11 +183,22 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f } break; } + + /* unhandled currently, but may be interesting */ + case ANIMTYPE_GPLAYER: + case ANIMTYPE_MASKLAYER: + case ANIMTYPE_SHAPEKEY: + case ANIMTYPE_NLAACTION: + break; + + /* other types */ + default: + break; } } /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* Deselect all animation channels @@ -193,7 +207,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int f * - test: check if deselecting instead of selecting * - sel: eAnimChannels_SetFlag; */ -void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel) +void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -373,7 +387,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ---------------------------- Graph Editor ------------------------------------- */ @@ -387,7 +401,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, s * - setting: type of setting to set * - on: whether the visibility setting has been enabled or disabled */ -void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short mode) +void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode) { bAnimListElem *ale, *match = NULL; int prevLevel = 0, matchLevel = 0; @@ -591,7 +605,7 @@ static int animedit_poll_channels_active(bContext *C) if (ELEM(NULL, sa, CTX_wm_region(C))) return 0; /* animation editor test */ - if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) + if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) return 0; return 1; @@ -608,7 +622,7 @@ static int animedit_poll_channels_nla_tweakmode_off(bContext *C) if (ELEM(NULL, sa, CTX_wm_region(C))) return 0; /* animation editor test */ - if (ELEM3(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) + if (ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA) == 0) return 0; /* NLA TweakMode test */ @@ -623,13 +637,13 @@ static int animedit_poll_channels_nla_tweakmode_off(bContext *C) /* ****************** Rearrange Channels Operator ******************* */ /* constants for channel rearranging */ -/* WARNING: don't change exising ones without modifying rearrange func accordingly */ -enum { +/* WARNING: don't change existing ones without modifying rearrange func accordingly */ +typedef enum eRearrangeAnimChan_Mode { REARRANGE_ANIMCHAN_TOP = -2, REARRANGE_ANIMCHAN_UP = -1, REARRANGE_ANIMCHAN_DOWN = 1, REARRANGE_ANIMCHAN_BOTTOM = 2 -}; +} eRearrangeAnimChan_Mode; /* defines for rearranging channels */ static EnumPropertyItem prop_animchannel_rearrange_types[] = { @@ -733,13 +747,13 @@ static bool rearrange_island_down(ListBase *list, tReorderChannelIsland *island) /* push it down */ BLI_insertlinkafter(list, next, island); - return 1; + return true; } } /* else: no next channel, so we're at the bottom already, so can't move */ } - return 0; + return false; } static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *island) @@ -761,23 +775,25 @@ static bool rearrange_island_bottom(ListBase *list, tReorderChannelIsland *islan } - return 1; + return true; } - return 0; + return false; } /* ............................. */ -/* typedef for channel rearranging function - * < list: list that channels belong to - * < island: island to be moved - * > return[0]: whether operation was a success +/** + * typedef for channel rearranging function + * + * \param list List of tReorderChannelIsland's that channels belong to + * \param island Island to be moved + * \return Whether operation was a success */ typedef bool (*AnimChanRearrangeFp)(ListBase *list, tReorderChannelIsland *island); /* get rearranging function, given 'rearrange' mode */ -static AnimChanRearrangeFp rearrange_get_mode_func(short mode) +static AnimChanRearrangeFp rearrange_get_mode_func(eRearrangeAnimChan_Mode mode) { switch (mode) { case REARRANGE_ANIMCHAN_TOP: @@ -796,7 +812,9 @@ static AnimChanRearrangeFp rearrange_get_mode_func(short mode) /* Rearrange Islands Generics ------------------------------------- */ /* add channel into list of islands */ -static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, Link *channel, short type, const bool is_hidden) +static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *srcList, + Link *channel, eAnim_ChannelType type, + const bool is_hidden) { tReorderChannelIsland *island = islands->last; /* always try to add to last island if possible */ bool is_sel = false, is_untouchable = false; @@ -839,7 +857,7 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr (is_sel == 0) || /* 4) hidden status changes */ ((island->flag & REORDER_ISLAND_HIDDEN) != is_hidden) - ) + ) { /* create a new island now */ island = MEM_callocN(sizeof(tReorderChannelIsland), "tReorderChannelIsland"); @@ -878,16 +896,33 @@ static void rearrange_animchannel_flatten_islands(ListBase *islands, ListBase *s /* ............................. */ -static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, short type) +/* get a list of all bAnimListElem's of a certain type which are currently visible */ +static void rearrange_animchannels_filter_visible(ListBase *anim_data_visible, bAnimContext *ac, eAnim_ChannelType type) { - int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); - - ANIM_animdata_filter(ac, anim_data_visible, filter, ac->data, type); + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale, *ale_next; + int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + + /* get all visible channels */ + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* now, only keep the ones that are of the types we are interested in */ + for (ale = anim_data.first; ale; ale = ale_next) { + ale_next = ale->next; + + if (ale->type != type) { + BLI_freelinkN(&anim_data, ale); + } + } + + /* return cleaned up list */ + *anim_data_visible = anim_data; } /* performing rearranging of channels using islands */ static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp rearrange_func, - short mode, short type, ListBase *anim_data_visible) + eRearrangeAnimChan_Mode mode, eAnim_ChannelType type, + ListBase *anim_data_visible) { ListBase islands = {NULL, NULL}; Link *channel, *chanNext = NULL; @@ -937,7 +972,7 @@ static bool rearrange_animchannel_islands(ListBase *list, AnimChanRearrangeFp re * ! NLA tracks are displayed in opposite order, so directions need care * mode: REARRANGE_ANIMCHAN_* */ -static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode) +static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode) { AnimChanRearrangeFp rearrange_func; ListBase anim_data_visible = {NULL, NULL}; @@ -950,10 +985,6 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode) if (rearrange_func == NULL) return; - /* only consider NLA data if it's accessible */ - //if (EXPANDED_DRVD(adt) == 0) - // return; - /* Filter visible data. */ rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLATRACK); @@ -969,7 +1000,7 @@ static void rearrange_nla_channels(bAnimContext *ac, AnimData *adt, short mode) /* Change the order drivers within AnimData block * mode: REARRANGE_ANIMCHAN_* */ -static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, short mode) +static void rearrange_driver_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode) { /* get rearranging function */ AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode); @@ -1035,6 +1066,13 @@ static void split_groups_action_temp(bAction *act, bActionGroup *tgrp) fcu->next = NULL; tgrp->channels.last = fcu; act->curves.last = NULL; + + /* ensure that all of these get their group set to this temp group + * (so that visibility filtering works) + */ + for (fcu = tgrp->channels.first; fcu; fcu = fcu->next) { + fcu->grp = tgrp; + } } /* Add temp-group to list */ @@ -1057,8 +1095,17 @@ static void join_groups_action_temp(bAction *act) /* clear moved flag */ agrp->flag &= ~AGRP_MOVED; - /* if temp-group... remove from list (but don't free as it's on the stack!) */ + /* if group was temporary one: + * - unassign all FCurves which were temporarily added to it + * - remove from list (but don't free as it's on the stack!) + */ if (agrp->flag & AGRP_TEMP) { + FCurve *fcu; + + for (fcu = agrp->channels.first; fcu; fcu = fcu->next) { + fcu->grp = NULL; + } + BLI_remlink(&act->groups, agrp); break; } @@ -1068,7 +1115,7 @@ static void join_groups_action_temp(bAction *act) /* Change the order of anim-channels within action * mode: REARRANGE_ANIMCHAN_* */ -static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode) +static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrangeAnimChan_Mode mode) { bActionGroup tgrp; ListBase anim_data_visible = {NULL, NULL}; @@ -1123,7 +1170,7 @@ static void rearrange_action_channels(bAnimContext *ac, bAction *act, short mode static int animchannels_rearrange_exec(bContext *C, wmOperator *op) { bAnimContext ac; - short mode; + eRearrangeAnimChan_Mode mode; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1177,7 +1224,7 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* send notifier that things have changed */ @@ -1290,7 +1337,7 @@ static void animchannels_group_channels(bAnimContext *ac, bAnimListElem *adt_ref } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } } @@ -1321,7 +1368,7 @@ static int animchannels_group_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* updatss */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -1393,7 +1440,7 @@ static int animchannels_ungroup_exec(bContext *C, wmOperator *UNUSED(op)) } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* updates */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -1470,7 +1517,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* filter data */ @@ -1515,7 +1562,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -1586,7 +1633,7 @@ static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op)) ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 0); } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* make all the selected channels visible */ filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); @@ -1605,7 +1652,7 @@ static int animchannels_visibility_set_exec(bContext *C, wmOperator *UNUSED(op)) ANIM_flush_setting_anim_channels(&ac, &all_data, ale, ACHANNEL_SETTING_VISIBLE, 1); } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); BLI_freelistN(&all_data); @@ -1683,7 +1730,7 @@ static int animchannels_visibility_toggle_exec(bContext *C, wmOperator *UNUSED(o } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); BLI_freelistN(&all_data); /* send notifier that things have changed */ @@ -1735,7 +1782,7 @@ static EnumPropertyItem prop_animchannel_settings_types[] = { * onlysel: only selected channels get the flag set */ // TODO: enable a setting which turns flushing on/off? -static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, short onlysel, short flush) +static void setflag_anim_channels(bAnimContext *ac, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode, bool onlysel, bool flush) { ListBase anim_data = {NULL, NULL}; ListBase all_data = {NULL, NULL}; @@ -1799,7 +1846,7 @@ static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, s ANIM_flush_setting_anim_channels(ac, &all_data, ale, setting, mode); } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); BLI_freelistN(&all_data); } @@ -1808,8 +1855,9 @@ static void setflag_anim_channels(bAnimContext *ac, short setting, short mode, s static int animchannels_setflag_exec(bContext *C, wmOperator *op) { bAnimContext ac; - short mode, setting; - short flush = 1; + eAnimChannel_Settings setting; + eAnimChannels_SetFlag mode; + bool flush = true; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1821,12 +1869,12 @@ static int animchannels_setflag_exec(bContext *C, wmOperator *op) /* check if setting is flushable */ if (setting == ACHANNEL_SETTING_EXPAND) - flush = 0; + flush = false; /* modify setting * - only selected channels are affected */ - setflag_anim_channels(&ac, setting, mode, 1, flush); + setflag_anim_channels(&ac, setting, mode, true, flush); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -1939,7 +1987,7 @@ static void ANIM_OT_channels_editable_toggle(wmOperatorType *ot) static int animchannels_expand_exec(bContext *C, wmOperator *op) { bAnimContext ac; - short onlysel = 1; + bool onlysel = true; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1947,10 +1995,10 @@ static int animchannels_expand_exec(bContext *C, wmOperator *op) /* only affect selected channels? */ if (RNA_boolean_get(op->ptr, "all")) - onlysel = 0; + onlysel = false; /* modify setting */ - setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, 0); + setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_ADD, onlysel, false); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -1981,7 +2029,7 @@ static void ANIM_OT_channels_expand(wmOperatorType *ot) static int animchannels_collapse_exec(bContext *C, wmOperator *op) { bAnimContext ac; - short onlysel = 1; + bool onlysel = true; /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) @@ -1989,10 +2037,10 @@ static int animchannels_collapse_exec(bContext *C, wmOperator *op) /* only affect selected channels? */ if (RNA_boolean_get(op->ptr, "all")) - onlysel = 0; + onlysel = false; /* modify setting */ - setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, 0); + setflag_anim_channels(&ac, ACHANNEL_SETTING_EXPAND, ACHANNEL_SETFLAG_CLEAR, onlysel, false); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -2015,7 +2063,114 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - ot->prop = RNA_def_boolean(ot->srna, "all", 1, "All", "Collapse all channels (not just selected ones)"); + ot->prop = RNA_def_boolean(ot->srna, "all", true, "All", "Collapse all channels (not just selected ones)"); +} + +/* ************ Remove All "Empty" AnimData Blocks Operator ********* */ +/* We define "empty" AnimData blocks here as those which have all 3 of criteria: + * 1) No active action OR that active actions are empty + * Assuming that all legitimate entries will have an action, + * and that empty actions + * 2) No NLA Tracks + NLA Strips + * Assuming that users haven't set up any of these as "placeholders" + * for convenience sake, and that most that exist were either unintentional + * or are no longer wanted + * 3) No drivers + */ + +static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bAnimContext ac; + + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* get animdata blocks */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_ANIMDATA); + ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); + + for (ale = anim_data.first; ale; ale = ale->next) { + ID *id = ale->id; + AnimData *adt = ale->data; + + bool action_empty = false; + bool nla_empty = false; + bool drivers_empty = false; + + /* sanity checks */ + BLI_assert((id != NULL) && (adt != NULL)); + + /* check if this is "empty" and can be deleted */ + /* (For now, there are only these 3 criteria) */ + + /* 1) Active Action is missing or empty */ + if (ELEM(NULL, adt->action, adt->action->curves.first)) { + action_empty = true; + } + else { + /* TODO: check for keyframe + fmodifier data on these too */ + } + + /* 2) No NLA Tracks and/or NLA Strips */ + if (adt->nla_tracks.first == NULL) { + nla_empty = true; + } + else { + NlaTrack *nlt; + + /* empty tracks? */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + if (nlt->strips.first) { + /* stop searching, as we found one that actually had stuff we don't want lost + * NOTE: nla_empty gets reset to false, as a previous track may have been empty + */ + nla_empty = false; + break; + } + else if (nlt->strips.first == NULL) { + /* this track is empty, but another one may still have stuff in it, so can't break yet */ + nla_empty = true; + } + } + } + + /* 3) Drivers */ + drivers_empty = (adt->drivers.first == NULL); + + + /* remove AnimData? */ + if (action_empty && nla_empty && drivers_empty) { + BKE_free_animdata(id); + } + } + + /* free temp data */ + ANIM_animdata_freelist(&anim_data); + + /* send notifier that things have changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +static void ANIM_OT_channels_clean_empty(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Empty Animation Data"; + ot->idname = "ANIM_OT_channels_clean_empty"; + ot->description = "Delete all empty animation data containers from visible datablocks"; + + /* api callbacks */ + ot->exec = animchannels_clean_empty_exec; + ot->poll = animedit_poll_channels_nla_tweakmode_off; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************* Reenable Disabled Operator ******************* */ @@ -2023,7 +2178,7 @@ static void ANIM_OT_channels_collapse(wmOperatorType *ot) static int animchannels_enable_poll(bContext *C) { ScrArea *sa = CTX_wm_area(C); - + /* channels region test */ /* TODO: could enhance with actually testing if channels region? */ if (ELEM(NULL, sa, CTX_wm_region(C))) @@ -2064,11 +2219,11 @@ static int animchannels_enable_exec(bContext *C, wmOperator *UNUSED(op)) fcu->driver->flag &= ~DRIVER_FLAG_INVALID; /* tag everything for updates - in particular, this is needed to get drivers working again */ - ANIM_list_elem_update(ac.scene, ale); + ale->update |= ANIM_UPDATE_DEPS; } - /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_update(&ac, &anim_data); + ANIM_animdata_freelist(&anim_data); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); @@ -2091,6 +2246,82 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** Find / Set Filter Operator ******************** */ + +/* XXX: make this generic? */ +static int animchannels_find_poll(bContext *C) +{ + ScrArea *sa = CTX_wm_area(C); + + if (sa == NULL) + return 0; + + /* animation editor with dopesheet */ + return ELEM(sa->spacetype, SPACE_ACTION, SPACE_IPO, SPACE_NLA); +} + +/* find_invoke() - Get initial channels */ +static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* set initial filter text, and enable filter */ + RNA_string_set(op->ptr, "query", ac.ads->searchstr); + + /* defer to popup */ + return WM_operator_props_popup(C, op, evt); +} + +/* find_exec() - Called to set the value */ +static int animchannels_find_exec(bContext *C, wmOperator *op) +{ + bAnimContext ac; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + /* update filter text, and ensure that filter is enabled if there's something there + * NOTE: we turn the filter off if there's nothing (this is a quick shortcut for dismissing) + */ + RNA_string_get(op->ptr, "query", ac.ads->searchstr); + + if (ac.ads->searchstr[0]) { + ac.ads->filterflag |= ADS_FILTER_BY_FCU_NAME; + } + else { + ac.ads->filterflag &= ~ADS_FILTER_BY_FCU_NAME; + } + + /* redraw */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +static void ANIM_OT_channels_find(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Find Channels"; + ot->idname = "ANIM_OT_channels_find"; + ot->description = "Filter the set of channels shown to only include those with matching names"; + + /* callbacks */ + ot->invoke = animchannels_find_invoke; + ot->exec = animchannels_find_exec; + ot->poll = animchannels_find_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_string(ot->srna, "query", "Query", sizeof(((bDopeSheet *)NULL)->searchstr), "", "Text to search for in channel names"); +} + /* ********************** Select All Operator *********************** */ static int animchannels_deselectall_exec(bContext *C, wmOperator *op) @@ -2103,9 +2334,9 @@ static int animchannels_deselectall_exec(bContext *C, wmOperator *op) /* 'standard' behavior - check if selected, then apply relevant selection */ if (RNA_boolean_get(op->ptr, "invert")) - ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 0, ACHANNEL_SETFLAG_TOGGLE); + ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, false, ACHANNEL_SETFLAG_TOGGLE); else - ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_ADD); + ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_ADD); /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_SELECTED, NULL); @@ -2128,7 +2359,7 @@ static void ANIM_OT_channels_select_all_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - ot->prop = RNA_def_boolean(ot->srna, "invert", 0, "Invert", ""); + ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", ""); } /* ******************** Borderselect Operator *********************** */ @@ -2228,7 +2459,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -2252,7 +2483,7 @@ static int animchannels_borderselect_exec(bContext *C, wmOperator *op) extend = RNA_boolean_get(op->ptr, "extend"); if (!extend) - ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, 1, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(&ac, ac.data, ac.datatype, true, ACHANNEL_SETFLAG_CLEAR); if (gesture_mode == GESTURE_MODAL_SELECT) selectmode = ACHANNEL_SETFLAG_ADD; @@ -2312,7 +2543,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index) if (G.debug & G_DEBUG) printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } @@ -2338,7 +2569,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index) } /* free temp data and tag for refresh */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); ED_region_tag_redraw(ac->ar); } @@ -2413,7 +2644,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, if (G.debug & G_DEBUG) printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return 0; } @@ -2421,7 +2652,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, /* TODO: should this feature be extended to work with other channel types too? */ if ((selectmode == -1) && (ale->type != ANIMTYPE_GROUP)) { /* normal channels should not behave normally in this case */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return 0; } @@ -2515,7 +2746,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select AnimData block by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); ale->adt->flag |= ADT_UI_SELECTED; } @@ -2570,8 +2801,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, FCurve *fcu; /* deselect all other channels */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - if (pchan) ED_pose_deselectall(ob, 0); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); + if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false); /* only select channels in group and group itself */ for (fcu = agrp->channels.first; fcu && fcu->grp == agrp; fcu = fcu->next) @@ -2580,8 +2811,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select group by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); - if (pchan) ED_pose_deselectall(ob, 0); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); + if (pchan) ED_pose_de_selectall(ob, SEL_DESELECT, false); agrp->flag |= AGRP_SELECTED; } @@ -2610,7 +2841,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select F-Curve by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); fcu->flag |= FCURVE_SELECTED; } @@ -2632,7 +2863,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select ShapeKey by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); kb->flag |= KEYBLOCK_SEL; } @@ -2662,7 +2893,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select layer by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); gpl->flag |= GP_LAYER_SELECT; } @@ -2692,7 +2923,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } else { /* select layer by itself */ - ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, 0, ACHANNEL_SETFLAG_CLEAR); + ANIM_deselect_anim_channels(ac, ac->data, ac->datatype, false, ACHANNEL_SETFLAG_CLEAR); masklay->flag |= MASK_LAYERFLAG_SELECT; } @@ -2706,7 +2937,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, } /* free channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* return notifier flags */ return notifierFlags; @@ -2777,10 +3008,10 @@ static void ANIM_OT_channels_click(wmOperatorType *ot) /* properties */ /* NOTE: don't save settings, otherwise, can end up with some weird behaviour (sticky extend) */ - prop = RNA_def_boolean(ot->srna, "extend", 0, "Extend Select", ""); // SHIFTKEY + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", ""); // SHIFTKEY RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_boolean(ot->srna, "children_only", 0, "Select Children Only", ""); // CTRLKEY|SHIFTKEY + prop = RNA_def_boolean(ot->srna, "children_only", false, "Select Children Only", ""); // CTRLKEY|SHIFTKEY RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -2795,6 +3026,8 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channels_click); WM_operatortype_append(ANIM_OT_channels_rename); + WM_operatortype_append(ANIM_OT_channels_find); + WM_operatortype_append(ANIM_OT_channels_setting_enable); WM_operatortype_append(ANIM_OT_channels_setting_disable); WM_operatortype_append(ANIM_OT_channels_setting_toggle); @@ -2814,6 +3047,8 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channels_fcurves_enable); + WM_operatortype_append(ANIM_OT_channels_clean_empty); + WM_operatortype_append(ANIM_OT_channels_group); WM_operatortype_append(ANIM_OT_channels_ungroup); } @@ -2823,8 +3058,7 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf) { wmKeyMap *keymap = WM_keymap_find(keyconf, "Animation Channels", 0, 0); wmKeyMapItem *kmi; - - /* selection */ + /* click-select */ /* XXX for now, only leftmouse.... */ WM_keymap_add_item(keymap, "ANIM_OT_channels_click", LEFTMOUSE, KM_PRESS, 0, 0); @@ -2833,6 +3067,10 @@ void ED_keymap_animchannels(wmKeyConfig *keyconf) /* rename */ WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ANIM_OT_channels_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0); + + /* find (i.e. a shortcut for setting the name filter) */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0); /* deselect all */ WM_keymap_add_item(keymap, "ANIM_OT_channels_select_all_toggle", AKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index c0543862265..f3b47b168e9 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -45,6 +45,7 @@ #include "BKE_animsys.h" #include "BKE_action.h" +#include "BKE_fcurve.h" #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_global.h" @@ -192,7 +193,7 @@ static void animchan_sync_fcurve(bAnimContext *ac, bAnimListElem *ale, FCurve ** /* major priority is selection status, so refer to the checks done in anim_filter.c * skip_fcurve_selected_data() for reference about what's going on here... */ - if (ELEM3(NULL, fcu, fcu->rna_path, owner_id)) + if (ELEM(NULL, fcu, fcu->rna_path, owner_id)) return; if (GS(owner_id->name) == ID_OB) { @@ -341,5 +342,58 @@ void ANIM_sync_animchannels_to_data(const bContext *C) } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); +} + +void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) +{ + bAnimListElem *ale; + + if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { +#ifdef DEBUG + /* quiet assert */ + for (ale = anim_data->first; ale; ale = ale->next) { + ale->update = 0; + } +#endif + return; + } + + for (ale = anim_data->first; ale; ale = ale->next) { + FCurve *fcu = ale->key_data; + + if (ale->update & ANIM_UPDATE_ORDER) { + ale->update &= ~ANIM_UPDATE_ORDER; + if (fcu) + sort_time_fcurve(fcu); + } + + if (ale->update & ANIM_UPDATE_HANDLES) { + ale->update &= ~ANIM_UPDATE_HANDLES; + if (fcu) + calchandles_fcurve(fcu); + } + + if (ale->update & ANIM_UPDATE_DEPS) { + ale->update &= ~ANIM_UPDATE_DEPS; + ANIM_list_elem_update(ac->scene, ale); + } + + BLI_assert(ale->update == 0); + } +} + +void ANIM_animdata_freelist(ListBase *anim_data) +{ +#ifndef NDEBUG + bAnimListElem *ale, *ale_next; + for (ale = anim_data->first; ale; ale = ale_next) { + ale_next = ale->next; + BLI_assert(ale->update == 0); + MEM_freeN(ale); + } + BLI_listbase_clear(anim_data); +#else + BLI_freelistN(anim_data); +#endif } diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 6c28d05110f..dad25eb04af 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -987,8 +987,10 @@ static bool skip_fcurve_with_name(bDopeSheet *ads, FCurve *fcu, ID *owner_id) return true; } -/* Check if F-Curve has errors and/or is disabled - * > returns: (bool) True if F-Curve has errors/is disabled +/** + * Check if F-Curve has errors and/or is disabled + * + * \return true if F-Curve has errors/is disabled */ static bool fcurve_has_errors(FCurve *fcu) { @@ -2593,7 +2595,7 @@ static size_t animdata_filter_remove_duplis(ListBase *anim_data) * - just use ale->data for now, though it would be nicer to involve * ale->type in combination too to capture corner cases (where same data performs differently) */ - if (BLI_gset_reinsert(gs, ale->data, NULL)) { + if (BLI_gset_add(gs, ale->data)) { /* this entry is 'unique' and can be kept */ items++; } @@ -2619,7 +2621,7 @@ static size_t animdata_filter_remove_duplis(ListBase *anim_data) * will be placed for use. * filter_mode: how should the data be filtered - bitmapping accessed flags */ -size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, int filter_mode, void *data, short datatype) +size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode, void *data, eAnimCont_Types datatype) { size_t items = 0; @@ -2712,6 +2714,13 @@ size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, int filter_mo items = animdata_filter_animchan(ac, anim_data, ads, data, filter_mode); break; } + + /* unhandled */ + default: + { + printf("ANIM_animdata_filter() - Invalid datatype argument %d\n", datatype); + break; + } } /* remove any 'weedy' entries */ diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index d3e6d8f474f..57df6d32f03 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -63,7 +63,7 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) /* sanity checks */ if (name == NULL) return icon; - else if (ELEM3(NULL, id, fcu, fcu->rna_path)) { + else if (ELEM(NULL, id, fcu, fcu->rna_path)) { if (fcu == NULL) strcpy(name, IFACE_("<invalid>")); else if (fcu->rna_path == NULL) diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 04363f61fd6..7cd47fab83a 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -115,12 +115,15 @@ ListBase *ED_animcontext_get_markers(const bAnimContext *ac) /* --------------------------------- */ -/* Apply some transformation to markers after the fact - * < markers: list of markers to affect - this may or may not be the scene markers list, so don't assume anything - * < scene: current scene (for getting current frame) - * < mode: (TfmMode) transform mode that this transform is for - * < value: from the transform code, this is t->vec[0] (which is delta transform for grab/extend, and scale factor for scale) - * < side: (B/L/R) for 'extend' functionality, which side of current frame to use +/** + * Apply some transformation to markers after the fact + * + * \param markers List of markers to affect - this may or may not be the scene markers list, so don't assume anything + * \param scene Current scene (for getting current frame) + * \param mode (TfmMode) transform mode that this transform is for + * \param value From the transform code, this is ``t->vec[0]`` + * (which is delta transform for grab/extend, and scale factor for scale) + * \param side (B/L/R) for 'extend' functionality, which side of current frame to use */ int ED_markers_post_apply_transform(ListBase *markers, Scene *scene, int mode, float value, char side) { @@ -201,7 +204,7 @@ void ED_markers_get_minmax(ListBase *markers, short sel, float *first, float *la /* sanity check */ //printf("markers = %p - %p, %p\n", markers, markers->first, markers->last); - if (ELEM3(NULL, markers, markers->first, markers->last)) { + if (ELEM(NULL, markers, markers->first, markers->last)) { *first = 0.0f; *last = 0.0f; return; @@ -472,13 +475,14 @@ static int ed_markers_poll_markers_exist(bContext *C) /* ------------------------ */ -/* Second-tier invoke() callback that performs context validation before running the +/** + * Second-tier invoke() callback that performs context validation before running the * "custom"/third-tier invoke() callback supplied as the last arg (which would normally * be the operator's invoke() callback elsewhere) * - * < invoke_func: (fn(bContext *, wmOperator *, wmEvent *)=int) "standard" invoke function - * that operator would otherwise have used. If NULL, the operator's standard - * exec() callback will be called instead in the appropriate places. + * \param invoke_func "standard" invoke function that operator would otherwise have used. + * If NULL, the operator's standard exec() + * callback will be called instead in the appropriate places. */ static int ed_markers_opwrap_invoke_custom(bContext *C, wmOperator *op, const wmEvent *event, int (*invoke_func)(bContext *, wmOperator *, const wmEvent *)) @@ -600,24 +604,88 @@ typedef struct MarkerMove { NumInput num; } MarkerMove; +static bool ed_marker_move_use_time(MarkerMove *mm) +{ + if (((mm->slink->spacetype == SPACE_TIME) && !(((SpaceTime *)mm->slink)->flag & TIME_DRAWFRAMES)) || + ((mm->slink->spacetype == SPACE_SEQ) && !(((SpaceSeq *)mm->slink)->flag & SEQ_DRAWFRAMES)) || + ((mm->slink->spacetype == SPACE_ACTION) && (((SpaceAction *)mm->slink)->flag & SACTION_DRAWTIME)) || + ((mm->slink->spacetype == SPACE_IPO) && !(((SpaceIpo *)mm->slink)->flag & SIPO_DRAWTIME)) || + ((mm->slink->spacetype == SPACE_NLA) && !(((SpaceNla *)mm->slink)->flag & SNLA_DRAWTIME))) + { + return true; + } + + return false; +} + +static void ed_marker_move_update_header(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + MarkerMove *mm = op->customdata; + TimeMarker *marker, *selmarker = NULL; + const int offs = RNA_int_get(op->ptr, "frames"); + char str[256]; + char str_offs[NUM_STR_REP_LEN]; + int totmark; + const bool use_time = ed_marker_move_use_time(mm); + + for (totmark = 0, marker = mm->markers->first; marker; marker = marker->next) { + if (marker->flag & SELECT) { + selmarker = marker; + totmark++; + } + } + + if (hasNumInput(&mm->num)) { + outputNumInput(&mm->num, str_offs, &scene->unit); + } + else if (use_time) { + BLI_snprintf(str_offs, sizeof(str_offs), "%.2f", FRA2TIME(offs)); + } + else { + BLI_snprintf(str_offs, sizeof(str_offs), "%d", offs); + } + + if (totmark == 1 && selmarker) { + /* we print current marker value */ + if (use_time) { + BLI_snprintf(str, sizeof(str), "Marker %.2f offset %s", FRA2TIME(selmarker->frame), str_offs); + } + else { + BLI_snprintf(str, sizeof(str), "Marker %d offset %s", selmarker->frame, str_offs); + } + } + else { + BLI_snprintf(str, sizeof(str), "Marker offset %s", str_offs); + } + + ED_area_headerprint(CTX_wm_area(C), str); +} + /* copy selection to temp buffer */ /* return 0 if not OK */ -static int ed_marker_move_init(bContext *C, wmOperator *op) +static bool ed_marker_move_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); ListBase *markers = ED_context_get_markers(C); MarkerMove *mm; TimeMarker *marker; - int totmark = 0; - int a; + int a, totmark; + + if (markers == NULL) { + return false; + } + + for (totmark = 0, marker = markers->first; marker; marker = marker->next) { + if (marker->flag & SELECT) { + totmark++; + } + } + + if (totmark == 0) { + return false; + } - if (markers == NULL) return 0; - - for (marker = markers->first; marker; marker = marker->next) - if (marker->flag & SELECT) totmark++; - - if (totmark == 0) return 0; - op->customdata = mm = MEM_callocN(sizeof(MarkerMove), "Markermove"); mm->slink = CTX_wm_space_data(C); mm->markers = markers; @@ -628,16 +696,16 @@ static int ed_marker_move_init(bContext *C, wmOperator *op) mm->num.val_flag[0] |= NUM_NO_FRACTION; mm->num.unit_sys = scene->unit.system; /* No time unit supporting frames currently... */ - mm->num.unit_type[0] = B_UNIT_NONE; - + mm->num.unit_type[0] = ed_marker_move_use_time(mm) ? B_UNIT_TIME : B_UNIT_NONE; + for (a = 0, marker = markers->first; marker; marker = marker->next) { if (marker->flag & SELECT) { mm->oldframe[a] = marker->frame; a++; } } - - return 1; + + return true; } /* free stuff */ @@ -668,7 +736,9 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve /* reset frs delta */ RNA_int_set(op->ptr, "frames", 0); - + + ed_marker_move_update_header(C, op); + return OPERATOR_RUNNING_MODAL; } @@ -722,138 +792,88 @@ static void ed_marker_move_cancel(bContext *C, wmOperator *op) ed_marker_move_exit(C, op); } - - static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); MarkerMove *mm = op->customdata; View2D *v2d = UI_view2d_fromcontext(C); - TimeMarker *marker, *selmarker = NULL; - char str[256]; - - switch (event->type) { - case ESCKEY: - ed_marker_move_cancel(C, op); - return OPERATOR_CANCELLED; - case RIGHTMOUSE: - /* press = user manually demands transform to be canceled */ - if (event->val == KM_PRESS) { + const bool has_numinput = hasNumInput(&mm->num); + const bool use_time = ed_marker_move_use_time(mm); + + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &mm->num, event)) { + float value = (float)RNA_int_get(op->ptr, "frames"); + + applyNumInput(&mm->num, &value); + if (use_time) { + value = TIME2FRA(value); + } + + RNA_int_set(op->ptr, "frames", (int)value); + ed_marker_move_apply(C, op); + ed_marker_move_update_header(C, op); + } + else { + bool handled = false; + switch (event->type) { + case ESCKEY: ed_marker_move_cancel(C, op); return OPERATOR_CANCELLED; - } - /* else continue; <--- see if release event should be caught for tweak-end */ - - case RETKEY: - case PADENTER: - case LEFTMOUSE: - case MIDDLEMOUSE: - if (WM_modal_tweak_exit(event, mm->event_type)) { - ed_marker_move_exit(C, op); - WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL); - WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); - return OPERATOR_FINISHED; - } - break; - case MOUSEMOVE: - { - float dx, fac; - - if (hasNumInput(&mm->num)) - break; - - dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); - - if (event->x != mm->evtx) { /* XXX maybe init for first time */ - int a, offs, totmark = 0; - - mm->evtx = event->x; - - fac = ((float)(event->x - mm->firstx) * dx); - - if (mm->slink->spacetype == SPACE_TIME) - apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); - else - apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, 1.0, 0.1, 0 /*was: U.flag & USER_AUTOGRABGRID*/); - - offs = (int)fac; - RNA_int_set(op->ptr, "frames", offs); - ed_marker_move_apply(C, op); - - /* cruft below is for header print */ - for (a = 0, marker = mm->markers->first; marker; marker = marker->next) { - if (marker->flag & SELECT) { - selmarker = marker; - a++; totmark++; - } + case RIGHTMOUSE: + /* press = user manually demands transform to be canceled */ + if (event->val == KM_PRESS) { + ed_marker_move_cancel(C, op); + return OPERATOR_CANCELLED; } - - if (totmark == 1) { - /* we print current marker value */ - if (mm->slink->spacetype == SPACE_TIME) { - SpaceTime *stime = (SpaceTime *)mm->slink; - if (stime->flag & TIME_DRAWFRAMES) - BLI_snprintf(str, sizeof(str), "Marker %d offset %d", selmarker->frame, offs); - else - BLI_snprintf(str, sizeof(str), "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); - } - else if (mm->slink->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)mm->slink; - if (saction->flag & SACTION_DRAWTIME) - BLI_snprintf(str, sizeof(str), "Marker %.2f offset %.2f", FRA2TIME(selmarker->frame), FRA2TIME(offs)); - else - BLI_snprintf(str, sizeof(str), "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); - } - else { - BLI_snprintf(str, sizeof(str), "Marker %.2f offset %.2f", (double)(selmarker->frame), (double)(offs)); - } + /* else continue; <--- see if release event should be caught for tweak-end */ + + case RETKEY: + case PADENTER: + case LEFTMOUSE: + case MIDDLEMOUSE: + if (WM_modal_tweak_exit(event, mm->event_type)) { + ed_marker_move_exit(C, op); + WM_event_add_notifier(C, NC_SCENE | ND_MARKERS, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_MARKERS, NULL); + return OPERATOR_FINISHED; } - else { - /* we only print the offset */ - if (mm->slink->spacetype == SPACE_TIME) { - SpaceTime *stime = (SpaceTime *)mm->slink; - if (stime->flag & TIME_DRAWFRAMES) - BLI_snprintf(str, sizeof(str), "Marker offset %d ", offs); - else - BLI_snprintf(str, sizeof(str), "Marker offset %.2f ", FRA2TIME(offs)); - } - else if (mm->slink->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)mm->slink; - if (saction->flag & SACTION_DRAWTIME) - BLI_snprintf(str, sizeof(str), "Marker offset %.2f ", FRA2TIME(offs)); + break; + case MOUSEMOVE: + if (!has_numinput) { + float dx; + + dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); + + if (event->x != mm->evtx) { /* XXX maybe init for first time */ + float fac; + + mm->evtx = event->x; + fac = ((float)(event->x - mm->firstx) * dx); + + if (mm->slink->spacetype == SPACE_TIME) + apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); else - BLI_snprintf(str, sizeof(str), "Marker offset %.2f ", (double)(offs)); - } - else { - BLI_snprintf(str, sizeof(str), "Marker offset %.2f ", (double)(offs)); + apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, 1.0, 0.1, 0 /*was: U.flag & USER_AUTOGRABGRID*/); + + RNA_int_set(op->ptr, "frames", (int)fac); + ed_marker_move_apply(C, op); + ed_marker_move_update_header(C, op); } } - - ED_area_headerprint(CTX_wm_area(C), str); - } - break; + break; } - } - if (event->val == KM_PRESS) { - if (handleNumInput(C, &mm->num, event)) { - char str_tx[NUM_STR_REP_LEN]; - float value = RNA_int_get(op->ptr, "frames"); - applyNumInput(&mm->num, &value); + if (!handled && event->val == KM_PRESS && handleNumInput(C, &mm->num, event)) { + float value = (float)RNA_int_get(op->ptr, "frames"); - if (hasNumInput(&mm->num)) { - outputNumInput(&mm->num, str_tx); - } - else { - BLI_snprintf(str_tx, sizeof(str_tx), "%d", (int)value); + applyNumInput(&mm->num, &value); + if (use_time) { + value = TIME2FRA(value); } - RNA_int_set(op->ptr, "frames", value); + RNA_int_set(op->ptr, "frames", (int)value); ed_marker_move_apply(C, op); - // ed_marker_header_update(C, op, str, (int)value); - // strcat(str, str_tx); - BLI_snprintf(str, sizeof(str), "Marker offset %s", str_tx); - ED_area_headerprint(CTX_wm_area(C), str); + ed_marker_move_update_header(C, op); } } @@ -1008,14 +1028,14 @@ static void select_timeline_marker_frame(ListBase *markers, int frame, bool exte } } - LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) { + BLI_LISTBASE_CIRCULAR_FORWARD_BEGIN (markers, marker, marker_first) { /* this way a not-extend select will allways give 1 selected marker */ if (marker->frame == frame) { marker->flag ^= SELECT; break; } } - LISTBASE_CIRCULAR_FORWARD_END (markers, marker, marker_first); + BLI_LISTBASE_CIRCULAR_FORWARD_END (markers, marker, marker_first); } static int ed_marker_select(bContext *C, const wmEvent *event, bool extend, bool camera) diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 88429aa3867..0f202003dd8 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -72,7 +72,7 @@ static int change_frame_poll(bContext *C) * this shouldn't show up in 3D editor (or others without 2D timeline view) via search */ if (sa) { - if (ELEM5(sa->spacetype, SPACE_TIME, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP)) { + if (ELEM(sa->spacetype, SPACE_TIME, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP)) { return true; } else if (sa->spacetype == SPACE_IPO) { @@ -168,6 +168,7 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event) case LEFTMOUSE: case RIGHTMOUSE: + case MIDDLEMOUSE: /* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init * the modal op) doesn't work for some reason */ diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 839284905ff..296a52e7f20 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -109,7 +109,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde fcu->flag = (FCURVE_VISIBLE | FCURVE_SELECTED); /* store path - make copy, and store that */ - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + fcu->rna_path = BLI_strdup(rna_path); fcu->array_index = array_index; /* if add is negative, don't init this data yet, since it will be filled in by the pasted driver */ @@ -426,74 +426,6 @@ bool ANIM_paste_driver(ReportList *reports, ID *id, const char rna_path[], int a /* ************************************************** */ /* UI-Button Interface */ -/* Temporary wrapper for driver operators for buttons to make it easier to create - * such drivers by rerouting all paths through the active object instead so that - * they will get picked up by the dependency system. - * - * < C: context pointer - for getting active data - * <> ptr: RNA pointer for property's datablock. May be modified as result of path remapping. - * < prop: RNA definition of property to add for - * - * > returns: MEM_alloc'd string representing the path to the property from the given PointerRNA - */ -static char *get_driver_path_hack(bContext *C, PointerRNA *ptr, PropertyRNA *prop) -{ - ID *id = (ID *)ptr->id.data; - ScrArea *sa = CTX_wm_area(C); - - /* get standard path which may be extended */ - char *basepath = RNA_path_from_ID_to_property(ptr, prop); - char *path = basepath; /* in case no remapping is needed */ - - - /* Remapping will only be performed in the Properties Editor, as only this - * restricts the subspace of options to the 'active' data (a manageable state) - */ - // TODO: watch out for pinned context? - if ((sa) && (sa->spacetype == SPACE_BUTS)) { - Object *ob = CTX_data_active_object(C); - - if (ob && id) { - /* only id-types which can be remapped to go through objects should be considered */ - switch (GS(id->name)) { - case ID_TE: /* textures */ - { - Material *ma = give_current_material(ob, ob->actcol); - Tex *tex = give_current_material_texture(ma); - - /* assumes: texture will only be shown if it is active material's active texture it's ok */ - if ((ID *)tex == id) { - char name_esc_ma[(sizeof(ma->id.name) - 2) * 2]; - char name_esc_tex[(sizeof(tex->id.name) - 2) * 2]; - - BLI_strescape(name_esc_ma, ma->id.name + 2, sizeof(name_esc_ma)); - BLI_strescape(name_esc_tex, tex->id.name + 2, sizeof(name_esc_tex)); - - /* create new path */ - // TODO: use RNA path functions to construct step by step instead? - // FIXME: maybe this isn't even needed anymore... - path = BLI_sprintfN("material_slots[\"%s\"].material.texture_slots[\"%s\"].texture.%s", - name_esc_ma, name_esc_tex, basepath); - - /* free old one */ - MEM_freeN(basepath); - } - break; - } - } - - /* fix RNA pointer, as we've now changed the ID root by changing the paths */ - if (basepath != path) { - /* rebase provided pointer so that it starts from object... */ - RNA_pointer_create(&ob->id, ptr->type, ptr->data, ptr); - } - } - } - - /* the path should now have been corrected for use */ - return path; -} - /* Add Driver Button Operator ------------------------ */ static int add_driver_button_exec(bContext *C, wmOperator *op) @@ -511,7 +443,7 @@ static int add_driver_button_exec(bContext *C, wmOperator *op) index = -1; if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { - char *path = get_driver_path_hack(C, &ptr, prop); + char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); short flags = CREATEDRIVER_WITH_DEFAULT_DVAR; if (path) { @@ -566,7 +498,7 @@ static int remove_driver_button_exec(bContext *C, wmOperator *op) index = -1; if (ptr.id.data && ptr.data && prop) { - char *path = get_driver_path_hack(C, &ptr, prop); + char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); success = ANIM_remove_driver(op->reports, ptr.id.data, path, index, 0); MEM_freeN(path); @@ -613,7 +545,7 @@ static int copy_driver_button_exec(bContext *C, wmOperator *op) uiContextActiveProperty(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { - char *path = get_driver_path_hack(C, &ptr, prop); + char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { /* only copy the driver for the button that this was involved for */ @@ -657,7 +589,7 @@ static int paste_driver_button_exec(bContext *C, wmOperator *op) uiContextActiveProperty(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop && RNA_property_animateable(&ptr, prop)) { - char *path = get_driver_path_hack(C, &ptr, prop); + char *path = BKE_animdata_driver_path_hack(C, &ptr, prop, NULL); if (path) { /* only copy the driver for the button that this was involved for */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index e233608dea2..c5e54cc1c7c 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -798,7 +798,7 @@ void summary_to_keylist(bAnimContext *ac, DLRBT_Tree *keys, DLRBT_Tree *blocks) } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } } @@ -832,7 +832,7 @@ void scene_to_keylist(bDopeSheet *ads, Scene *sce, DLRBT_Tree *keys, DLRBT_Tree for (ale = anim_data.first; ale; ale = ale->next) fcurve_to_keylist(ale->adt, ale->data, keys, blocks); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *blocks) @@ -868,7 +868,7 @@ void ob_to_keylist(bDopeSheet *ads, Object *ob, DLRBT_Tree *keys, DLRBT_Tree *bl for (ale = anim_data.first; ale; ale = ale->next) fcurve_to_keylist(ale->adt, ale->data, keys, blocks); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } void fcurve_to_keylist(AnimData *adt, FCurve *fcu, DLRBT_Tree *keys, DLRBT_Tree *blocks) diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c index 7e7453487b8..7f612de14b7 100644 --- a/source/blender/editors/animation/keyframes_edit.c +++ b/source/blender/editors/animation/keyframes_edit.c @@ -37,6 +37,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_lasso.h" +#include "BLI_math.h" #include "DNA_anim_types.h" #include "DNA_object_types.h" @@ -222,7 +223,7 @@ static short ob_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, Object *o } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* return return code - defaults to zero if nothing happened */ return ret; @@ -264,7 +265,7 @@ static short scene_keyframes_loop(KeyframeEditData *ked, bDopeSheet *ads, Scene } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* return return code - defaults to zero if nothing happened */ return ret; @@ -300,7 +301,7 @@ static short summary_keyframes_loop(KeyframeEditData *ked, bAnimContext *ac, Key break; } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ret_code; } @@ -381,9 +382,6 @@ void ANIM_editkeyframes_refresh(bAnimContext *ac) ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - /* when not in graph view, don't use handles */ - SpaceIpo *sipo = (ac->spacetype == SPACE_IPO) ? (SpaceIpo *)ac->sl : NULL; - const bool use_handle = sipo ? !(sipo->flag & SIPO_NOHANDLES) : false; /* filter animation data */ filter = ANIMFILTER_DATA_VISIBLE; @@ -395,11 +393,11 @@ void ANIM_editkeyframes_refresh(bAnimContext *ac) /* make sure keyframes in F-Curve are all in order, and handles are in valid positions */ sort_time_fcurve(fcu); - testhandles_fcurve(fcu, use_handle); + calchandles_fcurve(fcu); } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ************************************************************************** */ @@ -645,7 +643,7 @@ static short snap_bezier_nearestsec(KeyframeEditData *ked, BezTriple *bezt) const float secf = (float)FPS; if (bezt->f2 & SELECT) - bezt->vec[1][0] = ((float)floor(bezt->vec[1][0] / secf + 0.5f) * secf); + bezt->vec[1][0] = (floorf(bezt->vec[1][0] / secf + 0.5f) * secf); return 0; } @@ -672,8 +670,8 @@ static short snap_bezier_horizontal(KeyframeEditData *UNUSED(ked), BezTriple *be if (bezt->f2 & SELECT) { bezt->vec[0][1] = bezt->vec[2][1] = bezt->vec[1][1]; - if (ELEM3(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) bezt->h1 = HD_ALIGN; - if (ELEM3(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) bezt->h2 = HD_ALIGN; + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) bezt->h1 = HD_ALIGN; + if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM, HD_VECT)) bezt->h2 = HD_ALIGN; } return 0; } @@ -709,14 +707,38 @@ KeyframeEditFunc ANIM_editkeyframes_snap(short type) /* --------- */ +static void mirror_bezier_xaxis_ex(BezTriple *bezt, const float center) +{ + float diff; + int i; + + for (i = 0; i < 3; i++) { + diff = (center - bezt->vec[i][0]); + bezt->vec[i][0] = (center + diff); + } + swap_v3_v3(bezt->vec[0], bezt->vec[2]); + + SWAP(char, bezt->h1, bezt->h2); + SWAP(char, bezt->f1, bezt->f3); +} + +static void mirror_bezier_yaxis_ex(BezTriple *bezt, const float center) +{ + float diff; + int i; + + for (i = 0; i < 3; i++) { + diff = (center - bezt->vec[i][1]); + bezt->vec[i][1] = (center + diff); + } +} + static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt) { const Scene *scene = ked->scene; - float diff; if (bezt->f2 & SELECT) { - diff = ((float)CFRA - bezt->vec[1][0]); - bezt->vec[1][0] = ((float)CFRA + diff); + mirror_bezier_xaxis_ex(bezt, CFRA); } return 0; @@ -724,11 +746,8 @@ static short mirror_bezier_cframe(KeyframeEditData *ked, BezTriple *bezt) static short mirror_bezier_yaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - float diff; - if (bezt->f2 & SELECT) { - diff = (0.0f - bezt->vec[1][0]); - bezt->vec[1][0] = (0.0f + diff); + mirror_bezier_yaxis_ex(bezt, 0.0f); } return 0; @@ -736,11 +755,8 @@ static short mirror_bezier_yaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) static short mirror_bezier_xaxis(KeyframeEditData *UNUSED(ked), BezTriple *bezt) { - float diff; - if (bezt->f2 & SELECT) { - diff = (0.0f - bezt->vec[1][1]); - bezt->vec[1][1] = (0.0f + diff); + mirror_bezier_xaxis_ex(bezt, 0.0f); } return 0; @@ -750,8 +766,7 @@ static short mirror_bezier_marker(KeyframeEditData *ked, BezTriple *bezt) { /* mirroring time stored in f1 */ if (bezt->f2 & SELECT) { - const float diff = (ked->f1 - bezt->vec[1][0]); - bezt->vec[1][0] = (ked->f1 + diff); + mirror_bezier_xaxis_ex(bezt, ked->f1); } return 0; @@ -759,12 +774,9 @@ static short mirror_bezier_marker(KeyframeEditData *ked, BezTriple *bezt) static short mirror_bezier_value(KeyframeEditData *ked, BezTriple *bezt) { - float diff; - /* value to mirror over is stored in the custom data -> first float value slot */ if (bezt->f2 & SELECT) { - diff = (ked->f1 - bezt->vec[1][1]); - bezt->vec[1][1] = (ked->f1 + diff); + mirror_bezier_xaxis_ex(bezt, ked->f1); } return 0; @@ -798,9 +810,9 @@ KeyframeEditFunc ANIM_editkeyframes_mirror(short type) */ #define ENSURE_HANDLES_MATCH(bezt) \ if (bezt->h1 != bezt->h2) { \ - if (ELEM3(bezt->h1, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) \ + if (ELEM(bezt->h1, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) \ bezt->h1 = HD_FREE; \ - if (ELEM3(bezt->h2, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) \ + if (ELEM(bezt->h2, HD_ALIGN, HD_AUTO, HD_AUTO_ANIM)) \ bezt->h2 = HD_FREE; \ } (void)0 diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index c610595c4ba..56165c36efa 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -408,8 +408,8 @@ void sample_fcurve(FCurve *fcu) * keyframes while sampling will affect the outcome... * - only start sampling+adding from index=1, so that we don't overwrite original keyframe */ - range = (int)(ceil(end->vec[1][0] - start->vec[1][0]) ); - sfra = (int)(floor(start->vec[1][0]) ); + range = (int)(ceil(end->vec[1][0] - start->vec[1][0])); + sfra = (int)(floor(start->vec[1][0])); if (range) { value_cache = MEM_callocN(sizeof(TempFrameValCache) * range, "IcuFrameValCache"); @@ -732,9 +732,9 @@ static void paste_animedit_keys_fcurve(FCurve *fcu, tAnimCopybufItem *aci, float bezt->vec[2][0] += offset; /* insert the keyframe - * NOTE: no special flags here for now + * NOTE: we do not want to inherit handles from existing keyframes in this case! */ - insert_bezt_fcurve(fcu, bezt, 0); + insert_bezt_fcurve(fcu, bezt, INSERTKEY_OVERWRITE_FULL); /* un-apply offset from src beztriple after copying */ bezt->vec[0][0] -= offset; @@ -763,8 +763,10 @@ EnumPropertyItem keyframe_paste_merge_items[] = { {0, NULL, 0, NULL, NULL}}; -/* This function pastes data from the keyframes copy/paste buffer - * > return status code is whether the method FAILED to do anything +/** + * This function pastes data from the keyframes copy/paste buffer + * + * \return Status code is whether the method FAILED to do anything */ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, const eKeyPasteOffset offset_mode, const eKeyMergeMode merge_mode) @@ -857,6 +859,8 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, totmatch++; paste_animedit_keys_fcurve(fcu, aci, offset, merge_mode); } + + ale->update |= ANIM_UPDATE_DEFAULT; } /* don't continue if some fcurves were pasted */ @@ -865,6 +869,8 @@ short paste_animedit_keys(bAnimContext *ac, ListBase *anim_data, } } + ANIM_animdata_update(ac, anim_data); + return 0; } diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 78c87d58766..4c2d90179a7 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -186,7 +186,7 @@ FCurve *verify_fcurve(bAction *act, const char group[], PointerRNA *ptr, fcu->flag |= FCURVE_ACTIVE; /* first one added active */ /* store path - make copy, and store that */ - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + fcu->rna_path = BLI_strdup(rna_path); fcu->array_index = array_index; /* if a group name has been provided, try to add or find a group, then add F-Curve to it */ @@ -310,20 +310,25 @@ int insert_bezt_fcurve(FCurve *fcu, BezTriple *bezt, short flag) if (replace) { /* sanity check: 'i' may in rare cases exceed arraylen */ if ((i >= 0) && (i < fcu->totvert)) { - /* just change the values when replacing, so as to not overwrite handles */ - BezTriple *dst = (fcu->bezt + i); - float dy = bezt->vec[1][1] - dst->vec[1][1]; - - /* just apply delta value change to the handle values */ - dst->vec[0][1] += dy; - dst->vec[1][1] += dy; - dst->vec[2][1] += dy; - - dst->f1 = bezt->f1; - dst->f2 = bezt->f2; - dst->f3 = bezt->f3; - - /* TODO: perform some other operations? */ + if (flag & INSERTKEY_OVERWRITE_FULL) { + fcu->bezt[i] = *bezt; + } + else { + /* just change the values when replacing, so as to not overwrite handles */ + BezTriple *dst = (fcu->bezt + i); + float dy = bezt->vec[1][1] - dst->vec[1][1]; + + /* just apply delta value change to the handle values */ + dst->vec[0][1] += dy; + dst->vec[1][1] += dy; + dst->vec[2][1] += dy; + + dst->f1 = bezt->f1; + dst->f2 = bezt->f2; + dst->f3 = bezt->f3; + + /* TODO: perform some other operations? */ + } } } /* keyframing modes allow to not replace keyframe */ @@ -394,23 +399,42 @@ int insert_vert_fcurve(FCurve *fcu, float x, float y, short flag) beztr.vec[2][0] = x + 1.0f; beztr.vec[2][1] = y; beztr.f1 = beztr.f2 = beztr.f3 = SELECT; - + + /* set default handle types and interpolation mode */ if (flag & INSERTKEY_NO_USERPREF) { + /* for Py-API, we want scripts to have predictable behaviour, + * hence the option to not depend on the userpref defaults + */ beztr.h1 = beztr.h2 = HD_AUTO_ANIM; beztr.ipo = BEZT_IPO_BEZ; } else { + /* for UI usage - defaults should come from the */ beztr.h1 = beztr.h2 = U.keyhandles_new; /* use default handle type here */ //BEZKEYTYPE(&beztr)= scene->keytype; /* default keyframe type */ - + /* use default interpolation mode, with exceptions for int/discrete values */ beztr.ipo = U.ipo_new; } - - if (fcu->flag & FCURVE_DISCRETE_VALUES) + + /* interpolation type used is constrained by the type of values the curve can take */ + if (fcu->flag & FCURVE_DISCRETE_VALUES) { beztr.ipo = BEZT_IPO_CONST; - else if (beztr.ipo == BEZT_IPO_BEZ && (fcu->flag & FCURVE_INT_VALUES)) + } + else if ((beztr.ipo == BEZT_IPO_BEZ) && (fcu->flag & FCURVE_INT_VALUES)) { beztr.ipo = BEZT_IPO_LIN; + } + + /* set default values for "easing" interpolation mode settings + * NOTE: Even if these modes aren't currently used, if users switch + * to these later, we want these to work in a sane way out of + * the box. + */ + beztr.back = 1.70158f; /* "back" easing - this value used to be used when overshoot=0, but that */ + /* introduced discontinuities in how the param worked */ + + beztr.amplitude = 0.8f; /* "elastic" easing - values here were hand-optimised for a default duration of */ + beztr.period = 4.1f; /* ~10 frames (typical mograph motion length) */ /* add temp beztriple to keyframes */ a = insert_bezt_fcurve(fcu, &beztr, flag); @@ -633,7 +657,7 @@ static bool visualkey_can_use(PointerRNA *ptr, PropertyRNA *prop) const char *identifier = NULL; /* validate data */ - if (ELEM3(NULL, ptr, ptr->data, prop)) + if (ELEM(NULL, ptr, ptr->data, prop)) return 0; /* get first constraint and determine type of keyframe constraints to check for @@ -1023,7 +1047,7 @@ short insert_keyframe(ReportList *reports, ID *id, bAction *act, const char grou /* for Loc/Rot/Scale and also Color F-Curves, the color of the F-Curve in the Graph Editor, * is determined by the array index for the F-Curve */ - if (ELEM5(RNA_property_subtype(prop), PROP_TRANSLATION, PROP_XYZ, PROP_EULER, PROP_COLOR, PROP_COORDS)) { + if (ELEM(RNA_property_subtype(prop), PROP_TRANSLATION, PROP_XYZ, PROP_EULER, PROP_COLOR, PROP_COORDS)) { fcu->color_mode = FCURVE_COLOR_AUTO_RGB; } } diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index ca2dc1b66e2..fc211f0e60b 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -73,4 +75,6 @@ if(WITH_OPENNL) ) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_armature "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/armature/SConscript b/source/blender/editors/armature/SConscript index b3c1ea2dbe9..97bc1a138b3 100644 --- a/source/blender/editors/armature/SConscript +++ b/source/blender/editors/armature/SConscript @@ -31,19 +31,21 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/opennl/extern', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs += ' ' + env['BF_PTHREADS_INC'] diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 16975eed75e..eba1bc4d78d 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -84,7 +84,7 @@ EditBone *ED_armature_edit_bone_add(bArmature *arm, const char *name) return bone; } -void add_primitive_bone(Object *obedit_arm, bool view_aligned) +EditBone *ED_armature_edit_bone_add_primitive(Object *obedit_arm, float length, bool view_aligned) { bArmature *arm = obedit_arm->data; EditBone *bone; @@ -99,10 +99,9 @@ void add_primitive_bone(Object *obedit_arm, bool view_aligned) zero_v3(bone->head); zero_v3(bone->tail); - if (view_aligned) - bone->tail[1] = 1.0f; - else - bone->tail[2] = 1.0f; + bone->tail[view_aligned ? 1 : 2] = length; + + return bone; } diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index c5aa81c3a37..8bcee52baab 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -70,7 +70,7 @@ void ED_armature_apply_transform(Object *ob, float mat[4][4]) /* Put the armature into editmode */ ED_armature_to_edit(arm); - /* Transform the bones*/ + /* Transform the bones */ ED_armature_transform_bones(arm, mat); /* Turn the list into an armature */ @@ -100,7 +100,7 @@ void ED_armature_transform_bones(struct bArmature *arm, float mat[4][4]) mul_m4_v3(mat, ebone->head); mul_m4_v3(mat, ebone->tail); - /* apply the transfiormed roll back */ + /* apply the transformed roll back */ mat3_to_vec_roll(tmat, NULL, &ebone->roll); ebone->rad_head *= scale; @@ -190,7 +190,7 @@ void ED_armature_origin_set(Scene *scene, Object *ob, float cursor[3], int cente /* Adjust object location for new centerpoint */ if (centermode && obedit == NULL) { - mul_mat3_m4_v3(ob->obmat, cent); /* ommit translation part */ + mul_mat3_m4_v3(ob->obmat, cent); /* omit translation part */ add_v3_v3(ob->loc, cent); } } @@ -209,17 +209,12 @@ float ED_rollBoneToVector(EditBone *bone, const float align_axis[3], const bool sub_v3_v3v3(nor, bone->tail, bone->head); - /* if tail == head! */ - if (is_zero_v3(nor)) { + /* If tail == head or the bone is aligned with the axis... */ + if (normalize_v3(nor) <= FLT_EPSILON || (fabsf(dot_v3v3(align_axis, nor)) >= (1.0f - FLT_EPSILON))) { return roll; } - vec_roll_to_mat3(nor, 0.0f, mat); - - /* check the bone isn't aligned with the axis */ - if (dot_v3v3(align_axis, mat[2]) >= (1.0f - FLT_EPSILON)) { - return roll; - } + vec_roll_to_mat3_normalized(nor, 0.0f, mat); /* project the new_up_axis along the normal */ project_v3_v3v3(vec, align_axis, nor); @@ -301,7 +296,9 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) float cursor_rel[3]; sub_v3_v3v3(cursor_rel, cursor_local, ebone->head); if (axis_flip) negate_v3(cursor_rel); - ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only); + if (normalize_v3(cursor_rel) != 0.0f) { + ebone->roll = ED_rollBoneToVector(ebone, cursor_rel, axis_only); + } } } } @@ -405,7 +402,7 @@ static int armature_calc_roll_exec(bContext *C, wmOperator *op) } /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); return OPERATOR_FINISHED; } @@ -1136,7 +1133,7 @@ static int armature_align_bones_exec(bContext *C, wmOperator *op) } /* note, notifier might evolve */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); return OPERATOR_FINISHED; } @@ -1348,7 +1345,9 @@ static int armature_reveal_exec(bContext *C, wmOperator *UNUSED(op)) for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (arm->layer & ebone->layer) { if (ebone->flag & BONE_HIDDEN_A) { - ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + if (!(ebone->flag & BONE_UNSELECTABLE)) { + ebone->flag |= (BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + } ebone->flag &= ~BONE_HIDDEN_A; } } diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 1d054ffc2e9..6d616384b9a 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -237,7 +237,7 @@ void armature_select_mirrored(struct bArmature *arm); void armature_tag_unselect(struct bArmature *arm); void *get_nearest_bone(struct bContext *C, short findunsel, int x, int y); -void *get_bone_from_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, short hits, short findunsel); +void *get_bone_from_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, short hits, short findunsel, bool do_nearest); int bone_looper(struct Object *ob, struct Bone *bone, void *data, int (*bone_func)(struct Object *, struct Bone *, void *)); diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index e4ba8728e55..75fa4a5433f 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -245,7 +245,7 @@ int join_armature_exec(bContext *C, wmOperator *op) invert_m4_m4(imat, premat); mul_m4_m4m4(difmat, imat, postmat); - curbone->roll -= (float)atan2(difmat[2][0], difmat[2][2]); + curbone->roll -= atan2f(difmat[2][0], difmat[2][2]); } /* Fix Constraints and Other Links to this Bone and Armature */ diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 9c3c93e4850..c6ef76c570c 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -73,7 +73,7 @@ Bone *get_indexed_bone(Object *ob, int index) /* See if there are any selected bones in this buffer */ /* only bones from base are checked on */ -void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel) +void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel, bool do_nearest) { Object *obedit = scene->obedit; // XXX get from context Bone *bone; @@ -82,6 +82,7 @@ void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, unsigned int hitresult; short i; bool takeNext = false; + int minsel = 0xffffffff, minunsel = 0xffffffff; for (i = 0; i < hits; i++) { hitresult = buffer[3 + (i * 4)]; @@ -102,7 +103,7 @@ void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, else sel = !(bone->flag & BONE_SELECTED); - data = bone; + data = bone; } else { data = NULL; @@ -123,14 +124,28 @@ void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, if (data) { if (sel) { - if (!firstSel) firstSel = data; - takeNext = 1; + if (do_nearest) { + if (minsel > buffer[4 * i + 1]) { + firstSel = data; + minsel = buffer[4 * i + 1]; + } + } + else { + if (!firstSel) firstSel = data; + takeNext = 1; + } } else { - if (!firstunSel) - firstunSel = data; - if (takeNext) - return data; + if (do_nearest) { + if (minunsel > buffer[4 * i + 1]) { + firstunSel = data; + minunsel = buffer[4 * i + 1]; + } + } + else { + if (!firstunSel) firstunSel = data; + if (takeNext) return data; + } } } } @@ -160,10 +175,10 @@ void *get_nearest_bone(bContext *C, short findunsel, int x, int y) rect.ymin = rect.ymax = y; glInitNames(); - hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect); + hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect, true); if (hits > 0) - return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel); + return get_bone_from_selectbuffer(vc.scene, vc.scene->basact, buffer, hits, findunsel, true); return NULL; } @@ -295,13 +310,13 @@ static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2], rect.ymin = mval[1] - 5; rect.ymax = mval[1] + 5; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, true); if (hits == 0) { rect.xmin = mval[0] - 12; rect.xmax = mval[0] + 12; rect.ymin = mval[1] - 12; rect.ymax = mval[1] + 12; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, true); } /* See if there are any selected bones in this group */ if (hits > 0) { diff --git a/source/blender/editors/armature/armature_skinning.c b/source/blender/editors/armature/armature_skinning.c index e898e600e9b..5ff15b84284 100644 --- a/source/blender/editors/armature/armature_skinning.c +++ b/source/blender/editors/armature/armature_skinning.c @@ -180,15 +180,6 @@ static int dgroup_skinnable_cb(Object *ob, Bone *bone, void *datap) return 0; } -static void add_vgroups__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) -{ - /* DerivedMesh mapFunc for getting final coords in weight paint mode */ - - float (*verts)[3] = userData; - copy_v3_v3(verts[index], co); -} - static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], const int *selected, float scale) @@ -200,10 +191,22 @@ static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], i float distance; int i, iflip, j; bool use_topology = (mesh->editflag & ME_EDIT_MIRROR_TOPO) != 0; + bool use_mask = false; + + if ((ob->mode & OB_MODE_WEIGHT_PAINT) && + (mesh->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL))) + { + use_mask = true; + } /* for each vertex in the mesh */ for (i = 0; i < mesh->totvert; i++) { - iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i, use_topology) : 0; + + if (use_mask && !(mesh->mvert[i].flag & SELECT)) { + continue; + } + + iflip = (dgroupflip) ? mesh_get_x_mirror_vert(ob, i, use_topology) : -1; /* for each skinnable bone */ for (j = 0; j < numbones; ++j) { @@ -224,7 +227,7 @@ static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], i ED_vgroup_vert_remove(ob, dgroup, i); /* do same for mirror */ - if (dgroupflip && dgroupflip[j] && iflip >= 0) { + if (dgroupflip && dgroupflip[j] && iflip != -1) { if (distance != 0.0f) ED_vgroup_vert_add(ob, dgroupflip[j], iflip, distance, WEIGHT_REPLACE); @@ -235,7 +238,8 @@ static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], i } } -static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, Object *par, int heat, bool mirror) +static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, Object *par, + int heat, const bool mirror) { /* This functions implements the automatic computation of vertex group * weights, either through envelopes or using a heat equilibrium. @@ -362,7 +366,7 @@ static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); if (dm->foreachMappedVert) { - dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void *)verts, DM_FOREACH_NOP); + mesh_get_mapped_verts_coords(dm, verts, mesh->totvert); vertsfilled = 1; } @@ -415,7 +419,8 @@ static void add_verts_to_dgroups(ReportList *reports, Scene *scene, Object *ob, MEM_freeN(verts); } -void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, int mode, bool mirror) +void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, Object *par, + const int mode, const bool mirror) { /* Lets try to create some vertex groups * based on the bones of the parent armature. @@ -436,7 +441,7 @@ void create_vgroups_from_armature(ReportList *reports, Scene *scene, Object *ob, ED_vgroup_data_clamp_range(ob->data, defbase_tot); } } - else if (mode == ARM_GROUPS_ENVELOPE || mode == ARM_GROUPS_AUTO) { + else if (ELEM(mode, ARM_GROUPS_ENVELOPE, ARM_GROUPS_AUTO)) { /* Traverse the bone list, trying to create vertex groups * that are populated with the vertices for which the * bone is closest. diff --git a/source/blender/editors/armature/editarmature_sketch.c b/source/blender/editors/armature/editarmature_sketch.c index 475ffd23617..d75a23a6322 100644 --- a/source/blender/editors/armature/editarmature_sketch.c +++ b/source/blender/editors/armature/editarmature_sketch.c @@ -53,6 +53,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "GPU_select.h" + typedef int (*GestureDetectFct)(bContext *, SK_Gesture *, SK_Sketch *); typedef void (*GestureApplyFct)(bContext *, SK_Gesture *, SK_Sketch *); @@ -493,7 +495,7 @@ static void sk_drawStroke(SK_Stroke *stk, int id, float color[3], int start, int gluQuadricNormals(quad, GLU_SMOOTH); if (id != -1) { - glLoadName(id); + GPU_select_load_id(id); for (i = 0; i < stk->nb_points; i++) { glPushMatrix(); @@ -1490,9 +1492,9 @@ static int sk_getSelfIntersections(bContext *C, ListBase *list, SK_Stroke *gestu return added; } -static int cmpIntersections(void *i1, void *i2) +static int cmpIntersections(const void *i1, const void *i2) { - SK_Intersection *isect1 = i1, *isect2 = i2; + const SK_Intersection *isect1 = i1, *isect2 = i2; if (isect1->stroke == isect2->stroke) { if (isect1->before < isect2->before) { @@ -1969,7 +1971,7 @@ static int sk_selectStroke(bContext *C, SK_Sketch *sketch, const int mval[2], in rect.ymin = mval[1] - 5; rect.ymax = mval[1] + 5; - hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect); + hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect, true); if (hits > 0) { int besthitresult = -1; @@ -2032,7 +2034,7 @@ static void sk_drawSketch(Scene *scene, View3D *UNUSED(v3d), SK_Sketch *sketch, sk_drawStroke(stk, id, NULL, -1, -1); } - glLoadName(-1); + GPU_select_load_id(-1); } else { float selected_rgb[3] = {1, 0, 0}; diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 56e7bde0081..b7d14e089cf 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -635,8 +635,8 @@ void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numsource, bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; MVert *mvert = me->mvert; - bool use_vert_sel = false; - bool use_face_sel = false; + bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; *err_str = NULL; @@ -652,9 +652,8 @@ void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numsource, return; /* count triangles and create mask */ - if (ob->mode == OB_MODE_WEIGHT_PAINT && - ((use_face_sel = ((me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0)) || - (use_vert_sel = ((me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0)))) + if (ob->mode & OB_MODE_WEIGHT_PAINT && + (use_face_sel || use_vert_sel)) { mask = MEM_callocN(sizeof(int) * me->totvert, "heat_bone_weighting mask"); @@ -1304,7 +1303,7 @@ static int meshdeform_inside_cage(MeshDeformBind *mdb, float *co) /* solving */ -static int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n) +BLI_INLINE int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n) { int size = mdb->size; @@ -1322,7 +1321,7 @@ static int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n) return x + y * size + z * size * size; } -static void meshdeform_cell_center(MeshDeformBind *mdb, int x, int y, int z, int n, float *center) +BLI_INLINE void meshdeform_cell_center(MeshDeformBind *mdb, int x, int y, int z, int n, float *center) { x += MESHDEFORM_OFFSET[n][0]; y += MESHDEFORM_OFFSET[n][1]; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index de8b2e36d5b..da68108285f 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -1034,6 +1034,10 @@ static int pose_hide_exec(bContext *C, wmOperator *op) Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); bArmature *arm = ob->data; + if (ob->proxy != NULL) { + BKE_report(op->reports, RPT_INFO, "Undo of hiding can only be done with Reveal Selected"); + } + if (RNA_boolean_get(op->ptr, "unselected")) bone_looper(ob, arm->bonebase.first, NULL, hide_unselected_pose_bone_cb); else @@ -1070,7 +1074,9 @@ static int show_pose_bone_cb(Object *ob, Bone *bone, void *UNUSED(ptr)) if (arm->layer & bone->layer) { if (bone->flag & BONE_HIDDEN_P) { bone->flag &= ~BONE_HIDDEN_P; - bone->flag |= BONE_SELECTED; + if (!(bone->flag & BONE_UNSELECTABLE)) { + bone->flag |= BONE_SELECTED; + } } } diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 376c1bc0838..50d9d300d15 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -62,12 +62,12 @@ static int pose_group_add_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object */ - if (ob == NULL) + /* only continue if there's an object and pose */ + if (ELEM(NULL, ob, ob->pose)) return OPERATOR_CANCELLED; /* for now, just call the API function for this */ - BKE_pose_add_group(ob); + BKE_pose_add_group(ob->pose, NULL); /* notifiers for updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); @@ -95,12 +95,12 @@ static int pose_group_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_pose_object_from_context(C); - /* only continue if there's an object */ - if (ob == NULL) + /* only continue if there's an object and pose */ + if (ELEM(NULL, ob, ob->pose)) return OPERATOR_CANCELLED; /* for now, just call the API function for this */ - BKE_pose_remove_group(ob); + BKE_pose_remove_group_index(ob->pose, ob->pose->active_group); /* notifiers for updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, ob); @@ -189,7 +189,7 @@ static int pose_group_assign_exec(bContext *C, wmOperator *op) */ pose->active_group = RNA_int_get(op->ptr, "type"); if (pose->active_group == 0) - BKE_pose_add_group(ob); + BKE_pose_add_group(ob->pose, NULL); /* add selected bones to group then */ CTX_DATA_BEGIN (C, bPoseChannel *, pchan, selected_pose_bones) diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 3f4f720ccdb..0609fcc29e8 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -189,7 +189,9 @@ static bAction *poselib_init_new(Object *ob) /* init object's poselib action (unlink old one if there) */ if (ob->poselib) id_us_min(&ob->poselib->id); + ob->poselib = add_empty_action(G.main, "PoseLib"); + ob->poselib->idroot = ID_OB; return ob->poselib; } @@ -305,7 +307,7 @@ static int poselib_sanitize_exec(bContext *C, wmOperator *op) /* check if any pose matches this */ /* TODO: don't go looking through the list like this every time... */ for (marker = act->markers.first; marker; marker = marker->next) { - if (IS_EQ(marker->frame, (double)ak->cfra)) { + if (IS_EQ((double)marker->frame, (double)ak->cfra)) { marker->flag = -1; break; } @@ -1415,7 +1417,7 @@ static void poselib_preview_init_data(bContext *C, wmOperator *op) pld->marker = (pld->act) ? BLI_findlink(&pld->act->markers, pose_index) : NULL; /* check if valid poselib */ - if (ELEM3(NULL, pld->ob, pld->pose, pld->arm)) { + if (ELEM(NULL, pld->ob, pld->pose, pld->arm)) { BKE_report(op->reports, RPT_ERROR, "Pose lib is only for armatures in pose mode"); pld->state = PL_PREVIEW_ERROR; return; @@ -1488,7 +1490,6 @@ static void poselib_preview_cleanup(bContext *C, wmOperator *op) DAG_id_tag_update(&ob->id, OB_RECALC_DATA); /* sets recalc flags */ else BKE_pose_where_is(scene, ob); - } else if (pld->state == PL_PREVIEW_CONFIRM) { /* tag poses as appropriate */ @@ -1509,6 +1510,9 @@ static void poselib_preview_cleanup(bContext *C, wmOperator *op) BKE_pose_where_is(scene, ob); } + /* Request final redraw of the view. */ + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, pld->ob); + /* free memory used for backups and searching */ poselib_backup_free_data(pld); BLI_freelistN(&pld->searchp); diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index d783c1dcfde..ba5ef4f3b5d 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -45,6 +45,7 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" #include "BKE_object.h" +#include "BKE_report.h" #include "RNA_access.h" #include "RNA_define.h" @@ -68,6 +69,29 @@ /* ***************** Pose Select Utilities ********************* */ +/* Note: SEL_TOGGLE is assumed to have already been handled! */ +static void pose_do_bone_select(bPoseChannel *pchan, const int select_mode) +{ + /* select pchan only if selectable, but deselect works always */ + switch (select_mode) { + case SEL_SELECT: + if (!(pchan->bone->flag & BONE_UNSELECTABLE)) + pchan->bone->flag |= BONE_SELECTED; + break; + case SEL_DESELECT: + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + break; + case SEL_INVERT: + if (pchan->bone->flag & BONE_SELECTED) { + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else if (!(pchan->bone->flag & BONE_UNSELECTABLE)) { + pchan->bone->flag |= BONE_SELECTED; + } + break; + } +} + /* Utility method for changing the selection status of a bone */ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) { @@ -75,7 +99,7 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) /* sanity checks */ // XXX: actually, we can probably still get away with no object - at most we have no updates - if (ELEM4(NULL, ob, ob->pose, pchan, pchan->bone)) + if (ELEM(NULL, ob, ob->pose, pchan, pchan->bone)) return; arm = ob->data; @@ -109,14 +133,14 @@ void ED_pose_bone_select(Object *ob, bPoseChannel *pchan, bool select) /* called from editview.c, for mode-less pose selection */ /* assumes scene obact and basact is still on old situation */ int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, - bool extend, bool deselect, bool toggle) + bool extend, bool deselect, bool toggle, bool do_nearest) { Object *ob = base->object; Bone *nearBone; if (!ob || !ob->pose) return 0; - nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1); + nearBone = get_bone_from_selectbuffer(scene, base, buffer, hits, 1, do_nearest); /* if the bone cannot be affected, don't do anything */ if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { @@ -138,7 +162,7 @@ int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, shor } if (!extend && !deselect && !toggle) { - ED_pose_deselectall(ob, 0); + ED_pose_de_selectall(ob, SEL_DESELECT, true); nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); arm->act_bone = nearBone; } @@ -190,16 +214,12 @@ int ED_do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, shor return nearBone != NULL; } -/* test==0: deselect all - * test==1: swap select (apply to all the opposite of current situation) - * test==2: only clear active tag - * test==3: swap select (no test / inverse selection status of all independently) - */ -void ED_pose_deselectall(Object *ob, int test) +/* 'select_mode' is usual SEL_SELECT/SEL_DESELECT/SEL_TOGGLE/SEL_INVERT. + * When true, 'ignore_visibility' makes this func also affect invisible bones (hidden or on hidden layers). */ +void ED_pose_de_selectall(Object *ob, int select_mode, const bool ignore_visibility) { bArmature *arm = ob->data; bPoseChannel *pchan; - int selectmode = 0; /* we call this from outliner too */ if (ob->pose == NULL) { @@ -207,31 +227,23 @@ void ED_pose_deselectall(Object *ob, int test) } /* Determine if we're selecting or deselecting */ - if (test == 1) { + if (select_mode == SEL_TOGGLE) { + select_mode = SEL_SELECT; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) { + select_mode = SEL_DESELECT; break; + } } } - - if (pchan == NULL) - selectmode = 1; } - else if (test == 2) - selectmode = 2; - /* Set the flags accordingly */ + /* Set the flags accordingly */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { /* ignore the pchan if it isn't visible or if its selection cannot be changed */ - if ((pchan->bone->layer & arm->layer) && !(pchan->bone->flag & (BONE_HIDDEN_P | BONE_UNSELECTABLE))) { - if (test == 3) { - pchan->bone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else { - if (selectmode == 0) pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - else if (selectmode == 1) pchan->bone->flag |= BONE_SELECTED; - } + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + pose_do_bone_select(pchan, select_mode); } } } @@ -242,7 +254,7 @@ static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) { Bone *curBone; - /* stop when unconnected child is encontered, or when unselectable bone is encountered */ + /* stop when unconnected child is encountered, or when unselectable bone is encountered */ if (!(bone->flag & BONE_CONNECTED) || (bone->flag & BONE_UNSELECTABLE)) return; @@ -352,24 +364,7 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op) /* Set the flags */ CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones) { - /* select pchan only if selectable, but deselect works always */ - switch (action) { - case SEL_SELECT: - if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) - pchan->bone->flag |= BONE_SELECTED; - break; - case SEL_DESELECT: - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - case SEL_INVERT: - if (pchan->bone->flag & BONE_SELECTED) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - } - else if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) { - pchan->bone->flag |= BONE_SELECTED; - } - break; - } + pose_do_bone_select(pchan, action); } CTX_DATA_END; @@ -627,6 +622,13 @@ void POSE_OT_select_hierarchy(wmOperatorType *ot) /* -------------------------------------- */ +/* modes for select same */ +typedef enum ePose_SelectSame_Mode { + POSE_SEL_SAME_LAYER = 0, + POSE_SEL_SAME_GROUP = 1, + POSE_SEL_SAME_KEYINGSET = 2, +} ePose_SelectSame_Mode; + static bool pose_select_same_group(bContext *C, Object *ob, bool extend) { bArmature *arm = (ob) ? ob->data : NULL; @@ -636,7 +638,7 @@ static bool pose_select_same_group(bContext *C, Object *ob, bool extend) bool changed = false, tagged = false; /* sanity checks */ - if (ELEM3(NULL, ob, pose, arm)) + if (ELEM(NULL, ob, pose, arm)) return 0; /* count the number of groups */ @@ -693,7 +695,7 @@ static bool pose_select_same_layer(bContext *C, Object *ob, bool extend) bool changed = false; int layers = 0; - if (ELEM3(NULL, ob, pose, arm)) + if (ELEM(NULL, ob, pose, arm)) return 0; /* figure out what bones are selected */ @@ -725,7 +727,7 @@ static bool pose_select_same_layer(bContext *C, Object *ob, bool extend) return changed; } -static bool pose_select_same_keyingset(bContext *C, Object *ob, bool extend) +static bool pose_select_same_keyingset(bContext *C, ReportList *reports, Object *ob, bool extend) { KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); KS_Path *ksp; @@ -735,11 +737,26 @@ static bool pose_select_same_keyingset(bContext *C, Object *ob, bool extend) bool changed = false; /* sanity checks: validate Keying Set and object */ - if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0)) - return 0; + if (ks == NULL) { + BKE_report(reports, RPT_ERROR, "No active Keying Set to use"); + return false; + } + else if (ANIM_validate_keyingset(C, NULL, ks) != 0) { + if (ks->paths.first == NULL) { + if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) { + BKE_report(reports, RPT_ERROR, + "Use another Keying Set, as the active one depends on the currently " + "selected items or cannot find any targets due to unsuitable context"); + } + else { + BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths"); + } + } + return false; + } - if (ELEM3(NULL, ob, pose, arm)) - return 0; + if (ELEM(NULL, ob, pose, arm)) + return false; /* if not extending selection, deselect all selected first */ if (extend == false) { @@ -785,6 +802,7 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); bArmature *arm = (bArmature *)ob->data; + const ePose_SelectSame_Mode type = RNA_enum_get(op->ptr, "type"); const bool extend = RNA_boolean_get(op->ptr, "extend"); bool changed = false; @@ -792,18 +810,22 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) if (ob->pose == NULL) return OPERATOR_CANCELLED; - /* selection types - * NOTE: for the order of these, see the enum in POSE_OT_select_grouped() - */ - switch (RNA_enum_get(op->ptr, "type")) { - case 1: /* group */ + /* selection types */ + switch (type) { + case POSE_SEL_SAME_LAYER: /* layer */ + changed = pose_select_same_layer(C, ob, extend); + break; + + case POSE_SEL_SAME_GROUP: /* group */ changed = pose_select_same_group(C, ob, extend); break; - case 2: /* Keying Set */ - changed = pose_select_same_keyingset(C, ob, extend); + + case POSE_SEL_SAME_KEYINGSET: /* Keying Set */ + changed = pose_select_same_keyingset(C, op->reports, ob, extend); break; - default: /* layer */ - changed = pose_select_same_layer(C, ob, extend); + + default: + printf("pose_select_grouped() - Unknown selection type %d\n", type); break; } @@ -825,9 +847,9 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) void POSE_OT_select_grouped(wmOperatorType *ot) { static EnumPropertyItem prop_select_grouped_types[] = { - {0, "LAYER", 0, "Layer", "Shared layers"}, - {1, "GROUP", 0, "Group", "Shared group"}, - {2, "KEYINGSET", 0, "Keying Set", "All bones affected by active Keying Set"}, + {POSE_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"}, + {POSE_SEL_SAME_GROUP, "GROUP", 0, "Group", "Shared group"}, + {POSE_SEL_SAME_KEYINGSET, "KEYINGSET", 0, "Keying Set", "All bones affected by active Keying Set"}, {0, NULL, 0, NULL, NULL} }; diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 375cbb0fe2b..5f9f24d23a4 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -134,7 +134,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, short mode) pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); /* check the settings from the context */ - if (ELEM4(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action)) + if (ELEM(NULL, pso->ob, pso->arm, pso->ob->adt, pso->ob->adt->action)) return 0; else act = pso->ob->adt->action; diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 094af99776b..0cde8f30ace 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -290,13 +290,14 @@ static void set_pose_keys(Object *ob) } } -/* perform paste pose, for a single bone - * < ob: object where bone to paste to lives - * < chan: bone that pose to paste comes from - * < selOnly: only paste on selected bones - * < flip: flip on x-axis +/** + * Perform paste pose, for a single bone. * - * > returns: whether the bone that we pasted to if we succeeded + * \param ob Object where bone to paste to lives + * \param chan Bone that pose to paste comes from + * \param selOnly Only paste on selected bones + * \param flip Flip on x-axis + * \return Whether the bone that we pasted to if we succeeded */ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bool selOnly, const bool flip) { diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 2fdc9ff846b..a52b2a619dc 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1042,7 +1042,7 @@ static void curve_rename_fcurves(Curve *cu, ListBase *orig_curves) { int nu_index = 0, a, pt_index; EditNurb *editnurb = cu->editnurb; - Nurb *nu = editnurb->nurbs.first; + Nurb *nu; CVKeyIndex *keyIndex; char rna_path[64], orig_rna_path[64]; AnimData *adt = BKE_animdata_from_id(&cu->id); @@ -1191,7 +1191,7 @@ static int *initialize_index_map(Object *obedit, int *r_old_totvert) for (nu = editnurb->nurbs.first, vertex_index = 0; nu != NULL; - nu = nu->next, vertex_index++) + nu = nu->next) { if (nu->bezt) { BezTriple *bezt = nu->bezt; @@ -1238,9 +1238,18 @@ static void remap_hooks_and_vertex_parents(Object *obedit) { Object *object; Curve *curve = (Curve *) obedit->data; + EditNurb *editnurb = curve->editnurb; int *old_to_new_map = NULL; int old_totvert; + if (editnurb->keyindex == NULL) { + /* TODO(sergey): Happens when separating curves, this would lead to + * the wrong indices in the hook modifier, address this together with + * other indices issues. + */ + return; + } + for (object = G.main->object.first; object; object = object->id.next) { ModifierData *md; int index; @@ -1467,37 +1476,6 @@ void ED_curve_select_swap(EditNurb *editnurb, bool hide_handles) } } -/******************** transform operator **********************/ - -void ED_curve_transform(Curve *cu, float mat[4][4]) -{ - Nurb *nu; - BPoint *bp; - BezTriple *bezt; - int a; - - float scale = mat4_to_scale(mat); - - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (nu->type == CU_BEZIER) { - a = nu->pntsu; - for (bezt = nu->bezt; a--; bezt++) { - mul_m4_v3(mat, bezt->vec[0]); - mul_m4_v3(mat, bezt->vec[1]); - mul_m4_v3(mat, bezt->vec[2]); - bezt->radius *= scale; - } - BKE_nurb_handles_calc(nu); - } - else { - a = nu->pntsu * nu->pntsv; - for (bp = nu->bp; a--; bp++) - mul_m4_v3(mat, bp->vec); - } - } - DAG_id_tag_update(&cu->id, 0); -} - /******************** separate operator ***********************/ static int separate_exec(bContext *C, wmOperator *op) @@ -1926,7 +1904,7 @@ static void ed_curve_delete_selected(Object *obedit) } if (a == 0) { if (cu->actnu == nuindex) - cu->actnu = -1; + cu->actnu = CU_ACT_NONE; BLI_remlink(nubase, nu); keyIndex_delNurb(editnurb, nu); @@ -1950,7 +1928,7 @@ static void ed_curve_delete_selected(Object *obedit) } if (a == 0) { if (cu->actnu == nuindex) - cu->actnu = -1; + cu->actnu = CU_ACT_NONE; BLI_remlink(nubase, nu); keyIndex_delNurb(editnurb, nu); @@ -1975,17 +1953,15 @@ static void ed_curve_delete_selected(Object *obedit) next = nu->next; type = 0; if (nu->type == CU_BEZIER) { - int delta = 0; bezt = nu->bezt; for (a = 0; a < nu->pntsu; a++) { if (BEZSELECTED_HIDDENHANDLES(cu, bezt)) { memmove(bezt, bezt + 1, (nu->pntsu - a - 1) * sizeof(BezTriple)); - keyIndex_delBezt(editnurb, bezt + delta); + keyIndex_delBezt(editnurb, bezt); keyIndex_updateBezt(editnurb, bezt + 1, bezt, nu->pntsu - a - 1); nu->pntsu--; a--; type = 1; - delta++; } else { bezt++; @@ -2001,18 +1977,16 @@ static void ed_curve_delete_selected(Object *obedit) } } else if (nu->pntsv == 1) { - int delta = 0; bp = nu->bp; for (a = 0; a < nu->pntsu; a++) { if (bp->f1 & SELECT) { memmove(bp, bp + 1, (nu->pntsu - a - 1) * sizeof(BPoint)); - keyIndex_delBP(editnurb, bp + delta); + keyIndex_delBP(editnurb, bp); keyIndex_updateBP(editnurb, bp + 1, bp, nu->pntsu - a - 1); nu->pntsu--; a--; type = 1; - delta++; } else { bp++; @@ -2427,11 +2401,15 @@ static int switch_direction_exec(bContext *C, wmOperator *UNUSED(op)) Curve *cu = (Curve *)obedit->data; EditNurb *editnurb = cu->editnurb; Nurb *nu; + int i; - for (nu = editnurb->nurbs.first; nu; nu = nu->next) { + for (nu = editnurb->nurbs.first, i = 0; nu; nu = nu->next, i++) { if (isNurbsel(nu)) { BKE_nurb_direction_switch(nu); keyData_switchDirectionNurb(cu, nu); + if ((i == cu->actnu) && (cu->actvert != CU_ACT_NONE)) { + cu->actvert = (nu->pntsu - 1) - cu->actvert; + } } } @@ -3916,6 +3894,7 @@ static int set_spline_type_exec(bContext *C, wmOperator *op) ListBase *editnurb = object_editcurve_get(obedit); Nurb *nu; bool changed = false; + bool changed_size = false; const bool use_handles = RNA_boolean_get(op->ptr, "use_handles"); const int type = RNA_enum_get(op->ptr, "type"); @@ -3926,10 +3905,16 @@ static int set_spline_type_exec(bContext *C, wmOperator *op) for (nu = editnurb->first; nu; nu = nu->next) { if (isNurbsel(nu)) { - if (BKE_nurb_type_convert(nu, type, use_handles) == false) - BKE_report(op->reports, RPT_ERROR, "No conversion possible"); - else + const int pntsu_prev = nu->pntsu; + if (BKE_nurb_type_convert(nu, type, use_handles)) { changed = true; + if (pntsu_prev != nu->pntsu) { + changed_size = true; + } + } + else { + BKE_report(op->reports, RPT_ERROR, "No conversion possible"); + } } } @@ -3940,6 +3925,11 @@ static int set_spline_type_exec(bContext *C, wmOperator *op) DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + if (changed_size) { + Curve *cu = obedit->data; + cu->actvert = CU_ACT_NONE; + } + return OPERATOR_FINISHED; } else { @@ -4927,7 +4917,7 @@ static int addvert_Nurb(bContext *C, short mode, float location[3]) if ((nu == NULL) || (nu->type == CU_BEZIER && bezt == NULL) || (nu->type != CU_BEZIER && bp == NULL)) { if (mode != 'e') { - if (cu->actnu >= 0) + if (cu->actnu != CU_ACT_NONE) nu = BLI_findlink(&editnurb->nurbs, cu->actnu); if (!nu || nu->type == CU_BEZIER) { @@ -5701,7 +5691,7 @@ static int select_more_exec(bContext *C, wmOperator *UNUSED(op)) bp = nu->bp; selbpoints = BLI_BITMAP_NEW(a, "selectlist"); while (a > 0) { - if ((!BLI_BITMAP_GET(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) { + if ((!BLI_BITMAP_TEST(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) { /* upper control point */ if (a % nu->pntsu != 0) { tempbp = bp - 1; @@ -5714,7 +5704,7 @@ static int select_more_exec(bContext *C, wmOperator *UNUSED(op)) tempbp = bp + nu->pntsu; if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE); /* make sure selected bpoint is discarded */ - if (sel == 1) BLI_BITMAP_SET(selbpoints, a - nu->pntsu); + if (sel == 1) BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu); } /* right control point */ @@ -5798,7 +5788,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) } else { bp--; - if (BLI_BITMAP_GET(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; + if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; bp++; } @@ -5816,7 +5806,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) } else { bp -= nu->pntsu; - if (BLI_BITMAP_GET(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; + if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++; bp += nu->pntsu; } @@ -5831,7 +5821,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op)) if (sel != 4) { select_bpoint(bp, DESELECT, SELECT, VISIBLE); - BLI_BITMAP_SET(selbpoints, a); + BLI_BITMAP_ENABLE(selbpoints, a); } } else { @@ -7007,7 +6997,7 @@ static int match_texture_space_poll(bContext *C) { Object *object = CTX_data_active_object(C); - return object && ELEM3(object->type, OB_CURVE, OB_SURF, OB_FONT); + return object && ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT); } static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index c2a7a509fb3..8a3d4f3f4f5 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -109,21 +109,23 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type { static int xzproj = 0; /* this function calls itself... */ ListBase *editnurb = object_editcurve_get(obedit); - View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = ED_view3d_context_rv3d(C); Nurb *nu = NULL; BezTriple *bezt; BPoint *bp; Curve *cu = (Curve *)obedit->data; float vec[3], zvec[3] = {0.0f, 0.0f, 1.0f}; - float umat[4][4] = MAT4_UNITY, viewmat[4][4] = MAT4_UNITY; + float umat[4][4], viewmat[4][4]; float fac; int a, b; - const float grid = v3d ? v3d->grid : 1.0f; + const float grid = 1.0f; const int cutype = (type & CU_TYPE); // poly, bezier, nurbs, etc const int stype = (type & CU_PRIMITIVE); const int force_3d = ((Curve *)obedit->data)->flag & CU_3D; /* could be adding to an existing 3D curve */ + unit_m4(umat); + unit_m4(viewmat); + if (rv3d) { copy_m4_m4(viewmat, rv3d->viewmat); copy_v3_v3(zvec, rv3d->viewinv[2]); diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 5c448effcd5..4655deec4c4 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -247,18 +247,10 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) struct Main *bmain = CTX_data_main(C); Curve *cu = obedit->data; EditFont *ef = cu->editfont; - cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0]; - - if (obedit->totcol > 0) { - obedit->actcol = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0].mat_nr; - /* since this array is calloc'd, it can be 0 even though we try ensure - * (mat_nr > 0) almost everywhere */ - if (obedit->actcol < 1) { - obedit->actcol = 1; - } - } + BLI_assert(ef->len >= 0); + /* run update first since it can move the cursor */ if (mode == FO_EDIT) { /* re-tesselllate */ DAG_id_tag_update(obedit->data, 0); @@ -268,6 +260,18 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) BKE_vfont_to_curve(bmain, obedit, mode); } + cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0]; + + if (obedit->totcol > 0) { + obedit->actcol = cu->curinfo.mat_nr; + + /* since this array is calloc'd, it can be 0 even though we try ensure + * (mat_nr > 0) almost everywhere */ + if (obedit->actcol < 1) { + obedit->actcol = 1; + } + } + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); } @@ -973,9 +977,12 @@ static int move_cursor(bContext *C, int type, const bool select) EditFont *ef = cu->editfont; int cursmove = -1; + if ((select) && (ef->selstart == 0)) { + ef->selstart = ef->selend = ef->pos + 1; + } + switch (type) { case LINE_BEGIN: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; while (ef->pos > 0) { if (ef->textbuf[ef->pos - 1] == '\n') break; if (ef->textbufinfo[ef->pos - 1].flag & CU_CHINFO_WRAP) break; @@ -985,7 +992,6 @@ static int move_cursor(bContext *C, int type, const bool select) break; case LINE_END: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; while (ef->pos < ef->len) { if (ef->textbuf[ef->pos] == 0) break; if (ef->textbuf[ef->pos] == '\n') break; @@ -998,7 +1004,6 @@ static int move_cursor(bContext *C, int type, const bool select) case PREV_WORD: { int pos = ef->pos; - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, true); ef->pos = pos; cursmove = FO_CURS; @@ -1008,7 +1013,6 @@ static int move_cursor(bContext *C, int type, const bool select) case NEXT_WORD: { int pos = ef->pos; - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, true); ef->pos = pos; cursmove = FO_CURS; @@ -1016,35 +1020,29 @@ static int move_cursor(bContext *C, int type, const bool select) } case PREV_CHAR: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; ef->pos--; cursmove = FO_CURS; break; case NEXT_CHAR: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; ef->pos++; cursmove = FO_CURS; break; case PREV_LINE: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; cursmove = FO_CURSUP; break; case NEXT_LINE: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; cursmove = FO_CURSDOWN; break; case PREV_PAGE: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; cursmove = FO_PAGEUP; break; case NEXT_PAGE: - if ((select) && (ef->selstart == 0)) ef->selstart = ef->selend = ef->pos + 1; cursmove = FO_PAGEDOWN; break; } @@ -1056,6 +1054,14 @@ static int move_cursor(bContext *C, int type, const bool select) else if (ef->pos >= MAXTEXT) ef->pos = MAXTEXT; else if (ef->pos < 0) ef->pos = 0; + /* apply virtical cursor motion to position immediately + * otherwise the selection will lag behind */ + if (FO_CURS_IS_MOTION(cursmove)) { + struct Main *bmain = CTX_data_main(C); + BKE_vfont_to_curve(bmain, obedit, cursmove); + cursmove = FO_CURS; + } + if (select == 0) { if (ef->selstart) { struct Main *bmain = CTX_data_main(C); @@ -1573,6 +1579,7 @@ void make_editText(Object *obedit) len_wchar = BLI_strncpy_wchar_from_utf8(ef->textbuf, cu->str, MAXTEXT + 4); BLI_assert(len_wchar == cu->len_wchar); ef->len = len_wchar; + BLI_assert(ef->len >= 0); memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo)); @@ -1584,6 +1591,9 @@ void make_editText(Object *obedit) ef->pos = cu->pos; ef->selstart = cu->selstart; ef->selend = cu->selend; + + /* text may have been modified by Python */ + BKE_vfont_select_clamp(obedit); } void load_editText(Object *obedit) diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 3fc6e2e6f0d..2a84ca7f297 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -93,6 +93,8 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/soften.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/subtract.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/texdraw.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/texfill.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/texmask.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/thumb.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/twist.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/vertexdraw.png SRC) diff --git a/source/blender/editors/datafiles/SConscript b/source/blender/editors/datafiles/SConscript index 47819d0e33c..6bc8f21e384 100644 --- a/source/blender/editors/datafiles/SConscript +++ b/source/blender/editors/datafiles/SConscript @@ -77,6 +77,8 @@ sources.extend(( os.path.join(env['DATA_SOURCES'], "soften.png.c"), os.path.join(env['DATA_SOURCES'], "subtract.png.c"), os.path.join(env['DATA_SOURCES'], "texdraw.png.c"), + os.path.join(env['DATA_SOURCES'], "texfill.png.c"), + os.path.join(env['DATA_SOURCES'], "texmask.png.c"), os.path.join(env['DATA_SOURCES'], "thumb.png.c"), os.path.join(env['DATA_SOURCES'], "twist.png.c"), os.path.join(env['DATA_SOURCES'], "vertexdraw.png.c"), diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 904ad4892ed..5dc9679777f 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenkernel ../../blenlib ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -50,4 +52,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_gpencil "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/gpencil/SConscript b/source/blender/editors/gpencil/SConscript index 8b891fcb8cf..ab42bad52dc 100644 --- a/source/blender/editors/gpencil/SConscript +++ b/source/blender/editors/gpencil/SConscript @@ -31,7 +31,8 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/elbeem/extern', '../include', '../../blenfont', @@ -46,7 +47,7 @@ incs = [ '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 38dd9048790..5a838d7bc39 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -676,7 +676,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, * ............................ */ /* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ -void draw_gpencil_2dimage(const bContext *C) +void ED_gpencil_draw_2dimage(const bContext *C) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); @@ -685,7 +685,7 @@ void draw_gpencil_2dimage(const bContext *C) int offsx, offsy, sizex, sizey; int dflag = GP_DRAWDATA_NOSTATUS; - gpd = gpencil_data_get_active(C); // XXX + gpd = ED_gpencil_data_get_active(C); // XXX if (gpd == NULL) return; /* calculate rect */ @@ -738,7 +738,7 @@ void draw_gpencil_2dimage(const bContext *C) /* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly * Note: this gets called twice - first time with onlyv2d=1 to draw 'canvas' strokes, * second time with onlyv2d=0 for screen-aligned strokes */ -void draw_gpencil_view2d(const bContext *C, bool onlyv2d) +void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); @@ -748,7 +748,7 @@ void draw_gpencil_view2d(const bContext *C, bool onlyv2d) /* check that we have grease-pencil stuff to draw */ if (sa == NULL) return; - gpd = gpencil_data_get_active(C); // XXX + gpd = ED_gpencil_data_get_active(C); // XXX if (gpd == NULL) return; /* special hack for Image Editor */ @@ -764,7 +764,7 @@ void draw_gpencil_view2d(const bContext *C, bool onlyv2d) /* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly * Note: this gets called twice - first time with only3d=1 to draw 3d-strokes, * second time with only3d=0 for screen-aligned strokes */ -void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) +void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) { bGPdata *gpd; int dflag = 0; @@ -772,7 +772,7 @@ void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) int offsx, offsy, winx, winy; /* check that we have grease-pencil stuff to draw */ - gpd = gpencil_data_get_active_v3d(scene); // XXX + gpd = ED_gpencil_data_get_active_v3d(scene, v3d); if (gpd == NULL) return; /* when rendering to the offscreen buffer we don't want to @@ -799,4 +799,11 @@ void draw_gpencil_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) gp_draw_data(gpd, offsx, offsy, winx, winy, CFRA, dflag); } +void ED_gpencil_draw_ex(bGPdata *gpd, int winx, int winy, const int cfra) +{ + int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; + + gp_draw_data(gpd, 0, 0, winx, winy, cfra, dflag); +} + /* ************************************************** */ diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index f5bf1422488..dba80164e93 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -61,30 +61,30 @@ /* Generics - Loopers */ /* Loops over the gp-frames for a gp-layer, and applies the given callback */ -short ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *)) +bool ED_gplayer_frames_looper(bGPDlayer *gpl, Scene *scene, short (*gpf_cb)(bGPDframe *, Scene *)) { bGPDframe *gpf; /* error checker */ if (gpl == NULL) - return 0; + return false; /* do loop */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { /* execute callback */ if (gpf_cb(gpf, scene)) - return 1; + return true; } /* nothing to return */ - return 0; + return false; } /* ****************************************** */ /* Data Conversion Tools */ /* make a listing all the gp-frames in a layer as cfraelems */ -void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, short onlysel) +void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, bool onlysel) { bGPDframe *gpf; CfraElem *ce; @@ -110,22 +110,22 @@ void ED_gplayer_make_cfra_list(bGPDlayer *gpl, ListBase *elems, short onlysel) /* Selection Tools */ /* check if one of the frames in this layer is selected */ -short ED_gplayer_frame_select_check(bGPDlayer *gpl) +bool ED_gplayer_frame_select_check(bGPDlayer *gpl) { bGPDframe *gpf; /* error checking */ if (gpl == NULL) - return 0; + return false; /* stop at the first one found */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { if (gpf->flag & GP_FRAME_SELECT) - return 1; + return true; } /* not found */ - return 0; + return false; } /* helper function - select gp-frame based on SELECT_* mode */ diff --git a/source/blender/editors/gpencil/gpencil_buttons.c b/source/blender/editors/gpencil/gpencil_buttons.c index 0fe4d7e7157..0acff8fc0a5 100644 --- a/source/blender/editors/gpencil/gpencil_buttons.c +++ b/source/blender/editors/gpencil/gpencil_buttons.c @@ -350,7 +350,7 @@ static void draw_gpencil_panel(bContext *C, uiLayout *layout, bGPdata *gpd, Poin } } -void gpencil_panel_standard_header(const bContext *C, Panel *pa) +void ED_gpencil_panel_standard_header(const bContext *C, Panel *pa) { PointerRNA ptr; RNA_pointer_create((ID *)CTX_wm_screen(C), &RNA_Space, CTX_wm_space_data(C), &ptr); @@ -359,7 +359,7 @@ void gpencil_panel_standard_header(const bContext *C, Panel *pa) } /* Standard panel to be included wherever Grease Pencil is used... */ -void gpencil_panel_standard(const bContext *C, Panel *pa) +void ED_gpencil_panel_standard(const bContext *C, Panel *pa) { bGPdata **gpd_ptr = NULL; PointerRNA ptr; @@ -369,7 +369,7 @@ void gpencil_panel_standard(const bContext *C, Panel *pa) draw_gpencil_space_specials(C, pa->layout); /* get pointer to Grease Pencil Data */ - gpd_ptr = gpencil_data_get_pointers((bContext *)C, &ptr); + gpd_ptr = ED_gpencil_data_get_pointers((bContext *)C, &ptr); if (gpd_ptr) draw_gpencil_panel((bContext *)C, pa->layout, *gpd_ptr, &ptr); diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index d25de906e31..13334448941 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -62,6 +62,7 @@ #include "BKE_object.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BKE_tracking.h" #include "UI_interface.h" @@ -86,7 +87,7 @@ /* Context Wrangling... */ /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ -bGPdata **gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) +bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) { ID *screen_id = (ID *)CTX_wm_screen(C); Scene *scene = CTX_data_scene(C); @@ -180,21 +181,21 @@ bGPdata **gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) } /* Get the active Grease Pencil datablock */ -bGPdata *gpencil_data_get_active(const bContext *C) +bGPdata *ED_gpencil_data_get_active(const bContext *C) { - bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } -/* needed for offscreen rendering */ -bGPdata *gpencil_data_get_active_v3d(Scene *scene) +bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, View3D *v3d) { Base *base = scene->basact; bGPdata *gpd = NULL; /* We have to make sure active object is actually visible and selected, else we must use default scene gpd, - * to be consistent with gpencil_data_get_active's behavior. + * to be consistent with ED_gpencil_data_get_active's behavior. */ - if (base && (scene->lay & base->lay) && (base->object->flag & SELECT)) { + + if (base && TESTBASE(v3d, base)) { gpd = base->object->gpd; } return gpd ? gpd : scene->gpd; @@ -207,7 +208,7 @@ bGPdata *gpencil_data_get_active_v3d(Scene *scene) static int gp_add_poll(bContext *C) { /* the base line we have is that we have somewhere to add Grease Pencil data */ - return gpencil_data_get_pointers(C, NULL) != NULL; + return ED_gpencil_data_get_pointers(C, NULL) != NULL; } /* ******************* Add New Data ************************ */ @@ -215,7 +216,7 @@ static int gp_add_poll(bContext *C) /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { - bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); @@ -253,7 +254,7 @@ void GPENCIL_OT_data_add(wmOperatorType *ot) /* poll callback for adding data/layers - special */ static int gp_data_unlink_poll(bContext *C) { - bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); /* if we have access to some active data, make sure there's a datablock before enabling this */ return (gpd_ptr && *gpd_ptr); @@ -263,7 +264,7 @@ static int gp_data_unlink_poll(bContext *C) /* unlink datablock - wrapper around API */ static int gp_data_unlink_exec(bContext *C, wmOperator *op) { - bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); @@ -301,7 +302,7 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - bGPdata **gpd_ptr = gpencil_data_get_pointers(C, NULL); + 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) { @@ -337,7 +338,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) static int gp_actframe_delete_poll(bContext *C) { - bGPdata *gpd = gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = gpencil_layer_getactive(gpd); /* only if there's an active layer with an active frame */ @@ -348,7 +349,7 @@ static int gp_actframe_delete_poll(bContext *C) static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - bGPdata *gpd = gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = gpencil_layer_getactive(gpd); bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); @@ -1399,6 +1400,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd) { struct Main *bmain = CTX_data_main(C); + View3D *v3d = CTX_wm_view3d(C); /* may be NULL */ Scene *scene = CTX_data_scene(C); bGPDframe *gpf = gpencil_layer_getframe(gpl, CFRA, 0); bGPDstroke *gps, *prev_gps = NULL; @@ -1412,7 +1414,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG rctf subrect, *subrect_ptr = NULL; /* error checking */ - if (ELEM3(NULL, gpd, gpl, gpf)) + if (ELEM(NULL, gpd, gpl, gpf)) return; /* only convert if there are any strokes on this layer's frame to convert */ @@ -1493,7 +1495,7 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG } /* set the layer and select */ - base_new->lay = ob->lay = base_orig ? base_orig->lay : scene->lay; + base_new->lay = ob->lay = base_orig ? base_orig->lay : BKE_screen_view3d_layer_active(v3d, scene); base_new->flag = ob->flag = base_new->flag | SELECT; } @@ -1559,7 +1561,7 @@ static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UN static int gp_convert_poll(bContext *C) { - bGPdata *gpd = gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = NULL; bGPDframe *gpf = NULL; ScrArea *sa = CTX_wm_area(C); @@ -1578,7 +1580,7 @@ static int gp_convert_poll(bContext *C) static int gp_convert_layer_exec(bContext *C, wmOperator *op) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); - bGPdata *gpd = gpencil_data_get_active(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = gpencil_layer_getactive(gpd); Scene *scene = CTX_data_scene(C); const int mode = RNA_enum_get(op->ptr, "type"); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 2f941142d9e..7c1976df3e0 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -113,6 +113,7 @@ typedef struct tGPsdata { float imat[4][4]; /* inverted transformation matrix applying when converting coords from screen-space * to region space */ + float mat[4][4]; float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ @@ -177,7 +178,7 @@ static int gpencil_draw_poll(bContext *C) { if (ED_operator_regionactive(C)) { /* check if current context can support GPencil data */ - if (gpencil_data_get_pointers(C, NULL) != NULL) { + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { /* check if Grease Pencil isn't already running */ if (ED_gpencil_session_active() == 0) return 1; @@ -891,9 +892,12 @@ static short gp_stroke_eraser_strokeinside(const int mval[2], const int UNUSED(m return false; } -static void gp_point_to_xy(ARegion *ar, View2D *v2d, rctf *subrect, bGPDstroke *gps, bGPDspoint *pt, +static void gp_point_to_xy(tGPsdata *p, bGPDstroke *gps, bGPDspoint *pt, int *r_x, int *r_y) { + ARegion *ar = p->ar; + View2D *v2d = p->v2d; + rctf *subrect = p->subrect; int xyval[2]; if (gps->flag & GP_STROKE_3DSPACE) { @@ -907,7 +911,9 @@ static void gp_point_to_xy(ARegion *ar, View2D *v2d, rctf *subrect, bGPDstroke * } } else if (gps->flag & GP_STROKE_2DSPACE) { - UI_view2d_view_to_region_clip(v2d, pt->x, pt->y, r_x, r_y); + float vec[3] = {pt->x, pt->y, 0.0f}; + mul_m4_v3(p->mat, vec); + UI_view2d_view_to_region_clip(v2d, vec[0], vec[1], r_x, r_y); } else { if (subrect == NULL) { /* normal 3D view */ @@ -939,7 +945,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, BLI_freelinkN(&gpf->strokes, gps); } else if (gps->totpoints == 1) { - gp_point_to_xy(p->ar, p->v2d, p->subrect, gps, gps->points, &x0, &y0); + gp_point_to_xy(p, gps, gps->points, &x0, &y0); /* do boundbox check first */ if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) { @@ -960,8 +966,8 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, pt1 = gps->points + i; pt2 = gps->points + i + 1; - gp_point_to_xy(p->ar, p->v2d, p->subrect, gps, pt1, &x0, &y0); - gp_point_to_xy(p->ar, p->v2d, p->subrect, gps, pt2, &x1, &y1); + gp_point_to_xy(p, gps, pt1, &x0, &y0); + gp_point_to_xy(p, gps, pt2, &x1, &y1); /* check that point segment of the boundbox of the eraser stroke */ if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) || @@ -1148,6 +1154,7 @@ static int gp_session_initdata(bContext *C, tGPsdata *p) p->imat[3][0] -= marker->pos[0]; p->imat[3][1] -= marker->pos[1]; } + invert_m4_m4(p->mat, p->imat); break; } /* unsupported views */ @@ -1161,7 +1168,7 @@ static int gp_session_initdata(bContext *C, tGPsdata *p) } /* get gp-data */ - gpd_ptr = gpencil_data_get_pointers(C, &p->ownerPtr); + gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); if (gpd_ptr == NULL) { p->status = GP_STATUS_ERROR; if (G.debug & G_DEBUG) @@ -1861,7 +1868,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ if (ISKEYBOARD(event->type)) { - if (ELEM4(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY)) { /* allow some keys - for frame changing: [#33412] */ } else { @@ -1874,7 +1881,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 (ELEM4(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { + if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) { /* exit() ends the current stroke before cleaning up */ /* printf("\t\tGP - end of paint op + end of stroke\n"); */ p->status = GP_STATUS_DONE; @@ -1949,7 +1956,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } /* eraser size */ else if ((p->paintmode == GP_PAINTMODE_ERASER) && - ELEM4(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) { /* just resize the brush (local version) * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index 136e9da389d..ff92d69f39d 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -70,7 +70,7 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) { bGPdata **gpd_ptr = NULL, *new_gpd = NULL; - gpd_ptr = gpencil_data_get_pointers(C, NULL); + gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); if (step == 1) { /* undo */ //printf("\t\tGP - undo step\n"); diff --git a/source/blender/editors/include/BIF_gl.h b/source/blender/editors/include/BIF_gl.h index 477a7c0ce17..cd26bb22ada 100644 --- a/source/blender/editors/include/BIF_gl.h +++ b/source/blender/editors/include/BIF_gl.h @@ -33,7 +33,7 @@ #ifndef __BIF_GL_H__ #define __BIF_GL_H__ -#include "GL/glew.h" +#include "GPU_glew.h" #ifdef __APPLE__ @@ -58,8 +58,24 @@ * */ void cpack(unsigned int x); -#define glMultMatrixf(x) glMultMatrixf( (float *)(x)) -#define glLoadMatrixf(x) glLoadMatrixf( (float *)(x)) + +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) +# define glMultMatrixf(x) \ + glMultMatrixf(_Generic((x), \ + float *: (float *)(x), \ + float (*)[4]: (float *)(x), \ + const float *: (float *)(x), \ + const float (*)[4]: (float *)(x)) \ +) +# define glLoadMatrixf(x) \ + glLoadMatrixf(_Generic((x), \ + float *: (float *)(x), \ + float (*)[4]: (float *)(x)) \ +) +#else +# define glMultMatrixf(x) glMultMatrixf((float *)(x)) +# define glLoadMatrixf(x) glLoadMatrixf((float *)(x)) +#endif #define GLA_PIXEL_OFS 0.375f diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 48440d10ae3..4272fd49f7f 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -41,10 +41,12 @@ struct ColorManagedDisplaySettings; void fdrawbezier(float vec[4][3]); void fdrawline(float x1, float y1, float x2, float y2); void fdrawbox(float x1, float y1, float x2, float y2); -void sdrawline(short x1, short y1, short x2, short y2); -void sdrawtri(short x1, short y1, short x2, short y2); -void sdrawtrifill(short x1, short y1, short x2, short y2); -void sdrawbox(short x1, short y1, short x2, short y2); +void sdrawline(int x1, int y1, int x2, int y2); +#if 0 +void sdrawtri(int x1, int y1, int x2, int y2); +void sdrawtrifill(int x1, int y1, int x2, int y2); +#endif +void sdrawbox(int x1, int y1, int x2, int y2); void sdrawXORline(int x0, int y0, int x1, int y1); void sdrawXORline4(int nr, int x0, int y0, int x1, int y1); @@ -222,5 +224,7 @@ void glaDrawImBuf_glsl(struct ImBuf *ibuf, float x, float y, int zoomfilter, /* Draw imbuf on a screen, preferably using GLSL display transform */ void glaDrawImBuf_glsl_ctx(const struct bContext *C, struct ImBuf *ibuf, float x, float y, int zoomfilter); +void glaDrawBorderCorners(const struct rcti *border, float zoomx, float zoomy); + #endif /* __BIF_GLUTIL_H__ */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 10f3f1bef4c..956ec308daa 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -111,11 +111,12 @@ typedef struct bAnimListElem { struct bAnimListElem *next, *prev; void *data; /* source data this elem represents */ - int type; /* one of the ANIMTYPE_* values */ + int type; /* (eAnim_ChannelType) one of the ANIMTYPE_* values */ int flag; /* copy of elem's flags for quick access */ - int index; /* for un-named data, the index of the data in it's collection */ + int index; /* for un-named data, the index of the data in its collection */ - short datatype; /* type of motion data to expect */ + short update; /* (eAnim_Update_Flags) tag the element for updating */ + short datatype; /* (eAnim_KeyType) type of motion data to expect */ void *key_data; /* motion data - mostly F-Curves, but can be other types too */ @@ -189,6 +190,20 @@ typedef enum eAnim_KeyType { ALE_GROUP /* Action Group summary */ } eAnim_KeyType; +/* Flags for specifying the types of updates (i.e. recalculation/refreshing) that + * needs to be performed to the data contained in a channel following editing. + * For use with ANIM_animdata_update() + */ +typedef enum eAnim_Update_Flags { + ANIM_UPDATE_DEPS = (1 << 0), /* referenced data and dependencies get refreshed */ + ANIM_UPDATE_ORDER = (1 << 1), /* keyframes need to be sorted */ + ANIM_UPDATE_HANDLES = (1 << 2), /* recalculate handles */ +} eAnim_Update_Flags; + +/* used for most tools which change keyframes (flushed by ANIM_animdata_update) */ +#define ANIM_UPDATE_DEFAULT (ANIM_UPDATE_DEPS | ANIM_UPDATE_ORDER | ANIM_UPDATE_HANDLES) +#define ANIM_UPDATE_DEFAULT_NOHANDLES (ANIM_UPDATE_DEFAULT & ~ANIM_UPDATE_HANDLES) + /* ----------------- Filtering -------------------- */ /* filtering flags - under what circumstances should a channel be returned */ @@ -343,7 +358,7 @@ typedef enum eAnimFilter_Flags { /* Obtain list of filtered Animation channels to operate on. * Returns the number of channels in the list */ -size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, int filter_mode, void *data, short datatype); +size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_Flags filter_mode, void *data, eAnimCont_Types datatype); /* Obtain current anim-data context from Blender Context info. * Returns whether the operation was successful. @@ -356,6 +371,11 @@ bool ANIM_animdata_get_context(const struct bContext *C, bAnimContext *ac); */ bool ANIM_animdata_context_getdata(bAnimContext *ac); +/* Acts on bAnimListElem eAnim_Update_Flags */ +void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data); + +void ANIM_animdata_freelist(ListBase *anim_data); + /* ************************************************ */ /* ANIMATION CHANNELS LIST */ /* anim_channels_*.c */ @@ -447,13 +467,13 @@ void ANIM_channel_draw_widgets(struct bContext *C, bAnimContext *ac, bAnimListEl * * - setting: eAnimChannel_Settings */ -short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, int setting); +short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting); /* Change value of some setting for a channel * - setting: eAnimChannel_Settings * - mode: eAnimChannels_SetFlag */ -void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting, short mode); +void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode); /* Flush visibility (for Graph Editor) changes up/down hierarchy for changes in the given setting @@ -465,14 +485,14 @@ void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, int setting, * - setting: type of setting to set * - on: whether the visibility setting has been enabled or disabled */ -void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, int setting, short mode); +void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAnimListElem *ale_setting, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode); /* Deselect all animation channels */ -void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, short datatype, short test, short sel); +void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types datatype, bool test, eAnimChannels_SetFlag sel); /* Set the 'active' channel of type channel_type, in the given action */ -void ANIM_set_active_channel(bAnimContext *ac, void *data, short datatype, int filter, void *channel_data, short channel_type); +void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datatype, eAnimFilter_Flags filter, void *channel_data, eAnim_ChannelType channel_type); /* Delete the F-Curve from the given AnimData block (if possible), as appropriate according to animation context */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 7579f6cba65..b08cc12dc3e 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -123,7 +123,7 @@ void ED_armature_deselect_all(struct Object *obedit, int toggle); void ED_armature_deselect_all_visible(struct Object *obedit); int ED_do_pose_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, - short hits, bool extend, bool deselect, bool toggle); + short hits, bool extend, bool deselect, bool toggle, bool do_nearest); bool mouse_armature(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); int join_armature_exec(struct bContext *C, struct wmOperator *op); struct Bone *get_indexed_bone(struct Object *ob, int index); @@ -133,8 +133,8 @@ EditBone *ED_armature_bone_get_mirrored(const struct ListBase *edbo, EditBone *e void ED_armature_sync_selection(struct ListBase *edbo); void ED_armature_validate_active(struct bArmature *arm); -void add_primitive_bone(struct Object *obedit_arm, bool view_aligned); -struct EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *name); +EditBone *ED_armature_edit_bone_add_primitive(struct Object *obedit_arm, float length, bool view_aligned); +EditBone *ED_armature_edit_bone_add(struct bArmature *arm, const char *name); void ED_armature_edit_bone_remove(struct bArmature *arm, EditBone *exBone); bool ED_armature_ebone_is_child_recursive(EditBone *ebone_parent, EditBone *ebone_child); @@ -157,9 +157,11 @@ void ED_armature_transform(struct bArmature *arm, float mat[4][4]); #define ARM_GROUPS_ENVELOPE 2 #define ARM_GROUPS_AUTO 3 -void create_vgroups_from_armature(struct ReportList *reports, struct Scene *scene, struct Object *ob, struct Object *par, int mode, bool mirror); +void create_vgroups_from_armature(struct ReportList *reports, struct Scene *scene, struct Object *ob, + struct Object *par, const int mode, const bool mirror); -void unique_editbone_name(struct ListBase *ebones, char *name, EditBone *bone); /* if bone is already in list, pass it as param to ignore it */ +/* if bone is already in list, pass it as param to ignore it */ +void unique_editbone_name(struct ListBase *ebones, char *name, EditBone *bone); void ED_armature_bone_rename(struct bArmature *arm, const char *oldnamep, const char *newnamep); void undo_push_armature(struct bContext *C, const char *name); @@ -174,7 +176,7 @@ void ED_armature_ebone_selectflag_disable(EditBone *ebone, int flag); /* poseobject.c */ void ED_armature_exit_posemode(struct bContext *C, struct Base *base); void ED_armature_enter_posemode(struct bContext *C, struct Base *base); -void ED_pose_deselectall(struct Object *ob, int test); +void ED_pose_de_selectall(struct Object *ob, int select_mode, const bool ignore_visibility); void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select); void ED_pose_recalculate_paths(struct Scene *scene, struct Object *ob); struct Object *ED_pose_object_from_context(struct bContext *C); diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h index 330147db077..58e64f30b05 100644 --- a/source/blender/editors/include/ED_curve.h +++ b/source/blender/editors/include/ED_curve.h @@ -51,7 +51,6 @@ void ED_operatormacros_curve(void); void ED_keymap_curve(struct wmKeyConfig *keyconf); /* editcurve.c */ -void ED_curve_transform(struct Curve *cu, float mat[4][4]); void ED_curve_deselect_all(struct EditNurb *editnurb); void ED_curve_select_all(struct EditNurb *editnurb); void ED_curve_select_swap(struct EditNurb *editnurb, bool hide_handles); diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index 9022a1481aa..661ab58b98c 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -150,6 +150,12 @@ extern char datatoc_subtract_png[]; extern int datatoc_texdraw_png_size; extern char datatoc_texdraw_png[]; +extern int datatoc_texfill_png_size; +extern char datatoc_texfill_png[]; + +extern int datatoc_texmask_png_size; +extern char datatoc_texmask_png[]; + extern int datatoc_thumb_png_size; extern char datatoc_thumb_png[]; diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index e31221a42df..05fb76b7aea 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -65,9 +65,9 @@ typedef struct tGPspoint { /* ----------- Grease Pencil Tools/Context ------------- */ -struct bGPdata **gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr); -struct bGPdata *gpencil_data_get_active(const struct bContext *C); -struct bGPdata *gpencil_data_get_active_v3d(struct Scene *scene); /* for offscreen rendering */ +struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr); +struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C); +struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct View3D *v3d); /* ----------- Grease Pencil Operators ----------------- */ @@ -77,19 +77,20 @@ void ED_operatortypes_gpencil(void); /* ------------ Grease-Pencil Drawing API ------------------ */ /* drawgpencil.c */ -void draw_gpencil_2dimage(const struct bContext *C); -void draw_gpencil_view2d(const struct bContext *C, bool onlyv2d); -void draw_gpencil_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d); +void ED_gpencil_draw_2dimage(const struct bContext *C); +void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d); +void ED_gpencil_draw_view3d(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, bool only3d); +void ED_gpencil_draw_ex(struct bGPdata *gpd, int winx, int winy, const int cfra); -void gpencil_panel_standard_header(const struct bContext *C, struct Panel *pa); -void gpencil_panel_standard(const struct bContext *C, struct Panel *pa); +void ED_gpencil_panel_standard_header(const struct bContext *C, struct Panel *pa); +void ED_gpencil_panel_standard(const struct bContext *C, struct Panel *pa); /* ----------- Grease-Pencil AnimEdit API ------------------ */ -short ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, +bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, short (*gpf_cb)(struct bGPDframe *, struct Scene *)); -void ED_gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, short onlysel); +void ED_gplayer_make_cfra_list(struct bGPDlayer *gpl, ListBase *elems, bool onlysel); -short ED_gplayer_frame_select_check(struct bGPDlayer *gpl); +bool ED_gplayer_frame_select_check(struct bGPDlayer *gpl); void ED_gplayer_frame_select_set(struct bGPDlayer *gpl, short mode); void ED_gplayer_frames_select_border(struct bGPDlayer *gpl, float min, float max, short select_mode); void ED_gpencil_select_frames(struct bGPDlayer *gpl, short select_mode); diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index b15a83809f5..a9995de068e 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -69,7 +69,8 @@ void ED_image_point_pos__reverse(struct SpaceImage *sima, struct ARegion *ar, co bool ED_space_image_show_render(struct SpaceImage *sima); bool ED_space_image_show_paint(struct SpaceImage *sima); bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit); -bool ED_space_image_show_uvshadow(struct SpaceImage *sima, struct Object *obedit); + +bool ED_space_image_paint_curve(const struct bContext *C); bool ED_space_image_check_show_maskedit(struct Scene *scene, struct SpaceImage *sima); int ED_space_image_maskedit_poll(struct bContext *C); diff --git a/source/blender/editors/include/ED_lattice.h b/source/blender/editors/include/ED_lattice.h index 4d19b6d5548..6636319dc9b 100644 --- a/source/blender/editors/include/ED_lattice.h +++ b/source/blender/editors/include/ED_lattice.h @@ -37,6 +37,5 @@ struct Lattice; void free_editLatt(struct Object *ob); void make_editLatt(struct Object *obedit); void load_editLatt(struct Object *obedit); -void ED_lattice_transform(struct Lattice *lt, float mat[4][4]); #endif /* __ED_LATTICE_H__ */ diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h index 680e9d79109..5e774c63841 100644 --- a/source/blender/editors/include/ED_mball.h +++ b/source/blender/editors/include/ED_mball.h @@ -50,6 +50,4 @@ void load_editMball(struct Object *obedit); void undo_push_mball(struct bContext *C, const char *name); -void ED_mball_transform(struct MetaBall *mb, float mat[4][4]); - #endif /* __ED_MBALL_H__ */ diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index ebc97c58e24..6a562da0a0e 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -119,7 +119,7 @@ void EDBM_mesh_reveal(struct BMEditMesh *em); void EDBM_update_generic(struct BMEditMesh *em, const bool do_tessface, const bool is_destructive); -struct UvElementMap *BM_uv_element_map_create(struct BMesh *em, const bool selected, const bool do_islands); +struct UvElementMap *BM_uv_element_map_create(struct BMesh *bm, const bool selected, const bool do_islands); void BM_uv_element_map_free(struct UvElementMap *vmap); struct UvElement *BM_uv_element_get(struct UvElementMap *map, struct BMFace *efa, struct BMLoop *l); @@ -264,7 +264,6 @@ void ED_mesh_faces_remove(struct Mesh *mesh, struct ReportList *reports, int cou void ED_mesh_edges_remove(struct Mesh *mesh, struct ReportList *reports, int count); void ED_mesh_vertices_remove(struct Mesh *mesh, struct ReportList *reports, int count); -void ED_mesh_transform(struct Mesh *me, float mat[4][4]); void ED_mesh_calc_tessface(struct Mesh *mesh); void ED_mesh_update(struct Mesh *mesh, struct bContext *C, int calc_edges, int calc_tessface); diff --git a/source/blender/editors/include/ED_numinput.h b/source/blender/editors/include/ED_numinput.h index e7b52a49e8c..f9a22429fc2 100644 --- a/source/blender/editors/include/ED_numinput.h +++ b/source/blender/editors/include/ED_numinput.h @@ -64,6 +64,8 @@ enum { /* (1 << 9) and above are reserved for internal flags! */ }; +struct UnitSettings; + /*********************** NumInput ********************************/ /* There are important things to note here for code using numinput: @@ -76,7 +78,7 @@ enum { */ void initNumInput(NumInput *n); -void outputNumInput(NumInput *n, char *str); +void outputNumInput(NumInput *n, char *str, struct UnitSettings *unit_settings); bool hasNumInput(const NumInput *n); bool applyNumInput(NumInput *n, float *vec); bool handleNumInput(struct bContext *C, NumInput *n, const struct wmEvent *event); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 5504b7e0352..39586039e8f 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -101,9 +101,10 @@ extern struct EnumPropertyItem prop_clear_parent_types[]; extern struct EnumPropertyItem prop_make_parent_types[]; #endif -int ED_object_parent_set(struct ReportList *reports, struct Main *bmain, struct Scene *scene, struct Object *ob, - struct Object *par, int partype, bool xmirror, bool keep_transform, const int vert_par[3]); -void ED_object_parent_clear(struct Object *ob, int type); +bool ED_object_parent_set(struct ReportList *reports, struct Main *bmain, struct Scene *scene, struct Object *ob, + struct Object *par, int partype, const bool xmirror, const bool keep_transform, + const int vert_par[3]); +void ED_object_parent_clear(struct Object *ob, const int type); struct Base *ED_object_scene_link(struct Scene *scene, struct Object *ob); void ED_keymap_proportional_cycle(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap); @@ -122,7 +123,7 @@ void ED_base_object_free_and_unlink(struct Main *bmain, struct Scene *scene, str /* single object duplicate, if (dupflag == 0), fully linked, else it uses the flags given */ struct Base *ED_object_add_duplicate(struct Main *bmain, struct Scene *scene, struct Base *base, int dupflag); -void ED_object_parent(struct Object *ob, struct Object *parent, int type, const char *substr); +void ED_object_parent(struct Object *ob, struct Object *parent, const int type, const char *substr); bool ED_object_mode_compat_set(struct bContext *C, struct Object *ob, int mode, struct ReportList *reports); void ED_object_toggle_modes(struct bContext *C, int mode); @@ -150,10 +151,11 @@ bool ED_object_add_generic_get_opts(struct bContext *C, struct wmOperator *op, c float loc[3], float rot[3], bool *enter_editmode, unsigned int *layer, bool *is_view_aligned); -struct Object *ED_object_add_type(struct bContext *C, int type, const float loc[3], const float rot[3], - bool enter_editmode, unsigned int layer); +struct Object *ED_object_add_type( + struct bContext *C, int type, const float loc[3], const float rot[3], + bool enter_editmode, unsigned int layer) ATTR_RETURNS_NONNULL; -void ED_object_single_users(struct Main *bmain, struct Scene *scene, bool full, bool copy_groups); +void ED_object_single_users(struct Main *bmain, struct Scene *scene, const bool full, const bool copy_groups); void ED_object_single_user(struct Main *bmain, struct Scene *scene, struct Object *ob); /* object motion paths */ diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h new file mode 100644 index 00000000000..decd79fcc7b --- /dev/null +++ b/source/blender/editors/include/ED_paint.h @@ -0,0 +1,65 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ED_paint.h + * \ingroup editors + */ + +#ifndef __ED_PAINT_H__ +#define __ED_PAINT_H__ + +struct bContext; +struct RegionView3D; +struct wmKeyConfig; +struct wmOperator; + +/* paint_ops.c */ +void ED_operatortypes_paint(void); +void ED_operatormacros_paint(void); +void ED_keymap_paint(struct wmKeyConfig *keyconf); + +/* paint_undo.c */ +enum { + UNDO_PAINT_IMAGE = 0, + UNDO_PAINT_MESH = 1, +}; + +typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); +typedef void (*UndoFreeCb)(struct ListBase *lb); +typedef bool (*UndoCleanupCb)(struct bContext *C, struct ListBase *lb); + +int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name); +void ED_undo_paint_step_num(struct bContext *C, int type, int num); +const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, int *active); +void ED_undo_paint_free(void); +int ED_undo_paint_valid(int type, const char *name); +bool ED_undo_paint_empty(int type); +void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup); +void ED_undo_paint_push_end(int type); + +/* paint_image.c */ +/* image painting specific undo */ +void ED_image_undo_restore(struct bContext *C, struct ListBase *lb); +void ED_image_undo_free(struct ListBase *lb); +void ED_imapaint_clear_partial_redraw(void); +void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op); + +#endif /* __ED_PAINT_H__ */ diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index 14595a4c668..ab1dbabe793 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -35,7 +35,6 @@ struct ID; struct Main; struct MTex; struct Render; -struct RenderInfo; struct Scene; struct ScrArea; struct RegionView3D; @@ -56,18 +55,6 @@ void ED_render_scene_update(struct Main *bmain, struct Scene *scene, int updated void ED_viewport_render_kill_jobs(const struct bContext *C, bool free_database); struct Scene *ED_render_job_get_scene(const struct bContext *C); -/* render_preview.c */ - -/* stores rendered preview - is also used for icons */ -typedef struct RenderInfo { - int pr_rectx; - int pr_recty; - short curtile, tottile; - rcti disprect; /* storage for view3d preview rect */ - unsigned int *rect; - struct Render *re; /* persistent render */ -} RenderInfo; - /* Render the preview * * pr_method: diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 060702f6d71..ed2465647da 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -60,6 +60,7 @@ void ED_region_init(struct bContext *C, struct ARegion *ar); void ED_region_tag_redraw(struct ARegion *ar); void ED_region_tag_redraw_partial(struct ARegion *ar, struct rcti *rct); void ED_region_tag_redraw_overlay(struct ARegion *ar); +void ED_region_tag_refresh_ui(struct ARegion *ar); void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *ar); void ED_region_panels(const struct bContext *C, struct ARegion *ar, int vertical, const char *context, int contextnr); void ED_region_header_init(struct ARegion *ar); @@ -109,7 +110,7 @@ void ED_screen_animation_timer_update(struct bScreen *screen, int redraws, in ScrArea *ED_screen_full_newspace(struct bContext *C, ScrArea *sa, int type); void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); void ED_screen_full_restore(struct bContext *C, ScrArea *sa); -struct ScrArea *ED_screen_full_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa); +struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state); void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); /* anim */ diff --git a/source/blender/editors/include/ED_screen_types.h b/source/blender/editors/include/ED_screen_types.h index 2b02606c6d9..effecf43839 100644 --- a/source/blender/editors/include/ED_screen_types.h +++ b/source/blender/editors/include/ED_screen_types.h @@ -93,10 +93,13 @@ typedef struct AZone { short x1, y1, x2, y2; /* for clip */ rcti rect; + /* for fade in/out */ + float alpha; } AZone; /* actionzone type */ #define AZONE_AREA 1 /* corner widgets for splitting areas */ #define AZONE_REGION 2 /* when a region is collapsed, draw a handle to expose */ +#define AZONE_FULLSCREEN 3 /* when in editor fullscreen draw a corner to go to normal mode */ #endif /* __ED_SCREEN_TYPES_H__ */ diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 8fcb228803b..85ff9b5d246 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -32,47 +32,17 @@ struct ARegion; struct bContext; -struct MultiresModifierData; struct Object; struct RegionView3D; -struct wmKeyConfig; -struct wmWindowManager; struct ViewContext; struct rcti; /* sculpt.c */ void ED_operatortypes_sculpt(void); -void sculpt_get_redraw_planes(float planes[4][4], struct ARegion *ar, - struct RegionView3D *rv3d, struct Object *ob); -void ED_sculpt_get_average_stroke(struct Object *ob, float stroke[3]); +void ED_sculpt_redraw_planes_get(float planes[4][4], struct ARegion *ar, + struct RegionView3D *rv3d, struct Object *ob); +void ED_sculpt_stroke_get_average(struct Object *ob, float stroke[3]); bool ED_sculpt_minmax(struct bContext *C, float min[3], float max[3]); -int do_sculpt_mask_box_select(struct ViewContext *vc, struct rcti *rect, bool select, bool extend); +int ED_sculpt_mask_box_select(struct bContext *C, struct ViewContext *vc, const struct rcti *rect, bool select, bool extend); -/* paint_ops.c */ -void ED_operatortypes_paint(void); -void ED_keymap_paint(struct wmKeyConfig *keyconf); - -/* paint_undo.c */ -#define UNDO_PAINT_IMAGE 0 -#define UNDO_PAINT_MESH 1 - -typedef void (*UndoRestoreCb)(struct bContext *C, struct ListBase *lb); -typedef void (*UndoFreeCb)(struct ListBase *lb); - -int ED_undo_paint_step(struct bContext *C, int type, int step, const char *name); -void ED_undo_paint_step_num(struct bContext *C, int type, int num); -const char *ED_undo_paint_get_name(int type, int nr, int *active); -void ED_undo_paint_free(void); -int ED_undo_paint_valid(int type, const char *name); -bool ED_undo_paint_empty(int type); -void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free); -void ED_undo_paint_push_end(int type); - -/* image painting specific undo */ -void ED_image_undo_restore(struct bContext *C, struct ListBase *lb); -void ED_image_undo_free(struct ListBase *lb); -void ED_imapaint_clear_partial_redraw(void); -void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h); - - -#endif +#endif /* __ED_SCULPT_H__ */ diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index 4fbe01a5fc7..d268c578cf2 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -35,6 +35,7 @@ struct ARegionType; struct bContext; void ED_spacetypes_init(void); +void ED_spacemacros_init(void); /* the pluginnable API for export to editors */ diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index 41ff9b88da9..daa6864b5aa 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -98,6 +98,7 @@ enum TfmMode { #define CTX_NDOF (1 << 5) #define CTX_MOVIECLIP (1 << 6) #define CTX_MASK (1 << 7) +#define CTX_PAINT_CURVE (1 << 8) /* Standalone call to get the transformation center corresponding to the current situation * returns 1 if successful, 0 otherwise (usually means there's no selection) diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 6d9f1c4eda0..e26e03473e0 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -46,7 +46,7 @@ struct Mesh; void ED_editors_init(struct bContext *C); void ED_editors_exit(struct bContext *C); -void ED_editors_flush_edits(const struct bContext *C, bool for_render); +bool ED_editors_flush_edits(const struct bContext *C, bool for_render); /* ************** Undo ************************ */ diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 04eb829979f..4b82fa40c6a 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -54,7 +54,7 @@ void ED_uvedit_assign_image(struct Main *bmain, struct Scene *scene, struct Obje bool ED_uvedit_minmax(struct Scene *scene, struct Image *ima, struct Object *obedit, float min[2], float max[2]); bool ED_object_get_active_image(struct Object *ob, int mat_nr, - struct Image **r_ima, struct ImageUser **r_iuser, struct bNode **r_node); + struct Image **r_ima, struct ImageUser **r_iuser, struct bNode **r_node, struct bNodeTree **r_ntree); void ED_object_assign_active_image(struct Main *bmain, struct Object *ob, int mat_nr, struct Image *ima); bool ED_uvedit_test(struct Object *obedit); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 377bb7cebb1..bd4f37cfb1e 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -56,6 +56,7 @@ struct bContext; struct bPoseChannel; struct bScreen; struct bglMats; +struct rctf; struct rcti; struct wmOperator; struct wmOperatorType; @@ -84,6 +85,7 @@ typedef struct ViewDepths { float *ED_view3d_cursor3d_get(struct Scene *scene, struct View3D *v3d); void ED_view3d_cursor3d_position(struct bContext *C, float fp[3], const int mval[2]); +void ED_view3d_cursor3d_update(struct bContext *C, const int mval[2]); struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d); @@ -217,7 +219,8 @@ void ED_view3d_unproject(struct bglMats *mats, float out[3], const float x, cons /* end */ - +void ED_view3d_dist_range_get(struct View3D *v3d, + float r_dist_range[2]); bool ED_view3d_clip_range_get(struct View3D *v3d, struct RegionView3D *rv3d, float *r_clipsta, float *r_clipend, const bool use_ortho_factor); bool ED_view3d_viewplane_get(struct View3D *v3d, struct RegionView3D *rv3d, int winxi, int winyi, @@ -269,7 +272,7 @@ bool ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], con /* select */ #define MAXPICKBUF 10000 -short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input); +short view3d_opengl_select(struct ViewContext *vc, unsigned int *buffer, unsigned int bufsize, const struct rcti *input, bool do_nearest); /* view3d_select.c */ float ED_view3d_select_dist_px(void); @@ -332,6 +335,13 @@ void ED_view3d_camera_lock_init(struct View3D *v3d, struct RegionView3D *rv3d); /* copy the view to the camera, return true if */ bool ED_view3d_camera_lock_sync(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_camera_autokey( + struct Scene *scene, struct ID *id_key, + struct bContext *C, const bool do_rotate, const bool do_translate); +bool ED_view3d_camera_lock_autokey( + struct View3D *v3d, struct RegionView3D *rv3d, + struct bContext *C, const bool do_rotate, const bool do_translate); + void ED_view3D_lock_clear(struct View3D *v3d); struct BGpic *ED_view3D_background_image_new(struct View3D *v3d); @@ -340,13 +350,14 @@ void ED_view3D_background_image_clear(struct View3D *v3d); #define VIEW3D_MARGIN 1.4f #define VIEW3D_DIST_FALLBACK 1.0f + float ED_view3d_offset_distance(float mat[4][4], const float ofs[3], const float dist_fallback); void ED_view3d_distance_set(struct RegionView3D *rv3d, const float dist); float ED_scene_grid_scale(struct Scene *scene, const char **grid_unit); float ED_view3d_grid_scale(struct Scene *scene, struct View3D *v3d, const char **grid_unit); -void ED_scene_draw_fps(struct Scene *scene, struct rcti *rect); +void ED_scene_draw_fps(struct Scene *scene, const struct rcti *rect); /* view matrix properties utilities */ /* unused */ @@ -357,6 +368,6 @@ void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, #endif /* render */ -void ED_view3d_shade_update(struct Main *bmain, struct View3D *v3d, struct ScrArea *sa); +void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa); #endif /* __ED_VIEW3D_H__ */ diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index c776026a811..618fa44349e 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -426,13 +426,11 @@ DEF_ICON(CURVE_PATH) DEF_ICON(COLOR_RED) DEF_ICON(COLOR_GREEN) DEF_ICON(COLOR_BLUE) -#ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK652) - DEF_ICON(BLANK653) - DEF_ICON(BLANK654) - DEF_ICON(BLANK655) -#endif - +DEF_ICON(TRIA_RIGHT_BAR) +DEF_ICON(TRIA_DOWN_BAR) +DEF_ICON(TRIA_LEFT_BAR) +DEF_ICON(TRIA_UP_BAR) + /* EMPTY */ DEF_ICON(FORCE_FORCE) DEF_ICON(FORCE_WIND) @@ -668,8 +666,8 @@ DEF_ICON(IPO_EASE_IN_OUT) DEF_ICON(VERTEXSEL) DEF_ICON(EDGESEL) DEF_ICON(FACESEL) +DEF_ICON(LOOPSEL) #ifndef DEF_ICON_BLANK_SKIP - DEF_ICON(BLANK209) DEF_ICON(BLANK210) #endif DEF_ICON(ROTATE) @@ -976,6 +974,8 @@ DEF_ICON(BRUSH_SNAKE_HOOK) DEF_ICON(BRUSH_SOFTEN) DEF_ICON(BRUSH_SUBTRACT) DEF_ICON(BRUSH_TEXDRAW) +DEF_ICON(BRUSH_TEXFILL) +DEF_ICON(BRUSH_TEXMASK) DEF_ICON(BRUSH_THUMB) DEF_ICON(BRUSH_ROTATE) DEF_ICON(BRUSH_VERTEXDRAW) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 8de9650ddef..bc794bf3350 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -75,6 +75,9 @@ struct ImBuf; struct bNodeTree; struct bNode; struct bNodeSocket; +struct wmDropBox; +struct wmDrag; +struct wmEvent; typedef struct uiBut uiBut; typedef struct uiBlock uiBlock; @@ -99,6 +102,7 @@ typedef struct uiLayout uiLayout; #define UI_EMBOSSN 1 /* Nothing, only icon and/or text */ #define UI_EMBOSSP 2 /* Pulldown menu style */ #define UI_EMBOSST 3 /* Table */ +#define UI_EMBOSSR 4 /* Pie Menu */ /* uiBlock->direction */ #define UI_DIRECTION (UI_TOP | UI_DOWN | UI_LEFT | UI_RIGHT) @@ -134,6 +138,7 @@ typedef struct uiLayout uiLayout; /* block->flag bits 14-17 are identical to but->drawflag bits */ #define UI_BLOCK_LIST_ITEM (1 << 19) +#define UI_BLOCK_RADIAL (1 << 20) /* uiPopupBlockHandle->menuretval */ #define UI_RETURN_CANCEL (1 << 0) /* cancel all menus cascading */ @@ -175,6 +180,7 @@ enum { UI_BUT_DRAG_MULTI = (1 << 25), /* edit this button as well as the active button (not just dragging) */ UI_BUT_SCA_LINK_GREY = (1 << 26), /* used to flag if sca links shoud be grey out */ UI_BUT_HAS_SEP_CHAR = (1 << 27), /* but->str contains UI_SEP_CHAR, used for key shortcuts */ + UI_BUT_TIP_FORCE = (1 << 28), /* force show tooltips when holding option/alt if U's USER_TOOLTIPS is off */ }; #define UI_PANEL_WIDTH 340 @@ -288,6 +294,8 @@ typedef enum { #define UI_GRAD_V_ALT 9 #define UI_GRAD_L_ALT 10 +#define UI_PALETTE_COLOR 20 + /* Drawing * * Functions to draw various shapes, taking theme settings into account. @@ -353,6 +361,17 @@ struct uiLayout *uiPupMenuLayout(uiPopupMenu *head); void uiPupMenuReports(struct bContext *C, struct ReportList *reports) ATTR_NONNULL(); bool uiPupMenuInvoke(struct bContext *C, const char *idname, struct ReportList *reports) ATTR_NONNULL(1, 2); +/* Pie menus */ +typedef struct uiPieMenu uiPieMenu; + +void uiPieMenuInvoke(struct bContext *C, const char *idname, const struct wmEvent *event); +void uiPieOperatorEnumInvoke(struct bContext *C, const char *title, const char *opname, + const char *propname, const struct wmEvent *event); +void uiPieEnumInvoke(struct bContext *C, const char *title, const char *path, const struct wmEvent *event); + +struct uiPieMenu *uiPieMenuBegin(struct bContext *C, const char *title, int icon, const struct wmEvent *event) ATTR_NONNULL(); +void uiPieMenuEnd(struct bContext *C, uiPieMenu *pie); +struct uiLayout *uiPieMenuLayout(struct uiPieMenu *pie); /* Popup Blocks * * Functions used to create popup blocks. These are like popup menus @@ -380,8 +399,10 @@ void uiPupBlockClose(struct bContext *C, uiBlock *block); * */ uiBlock *uiBeginBlock(const struct bContext *C, struct ARegion *region, const char *name, short dt); +void uiEndBlock_ex(const struct bContext *C, uiBlock *block, const int xy[2]); void uiEndBlock(const struct bContext *C, uiBlock *block); void uiDrawBlock(const struct bContext *C, struct uiBlock *block); +void uiBlockUpdateFromOld(const struct bContext *C, struct uiBlock *block); uiBlock *uiGetBlock(const char *name, struct ARegion *ar); @@ -408,7 +429,8 @@ typedef enum { UI_BLOCK_BOUNDS_TEXT, UI_BLOCK_BOUNDS_POPUP_MOUSE, UI_BLOCK_BOUNDS_POPUP_MENU, - UI_BLOCK_BOUNDS_POPUP_CENTER + UI_BLOCK_BOUNDS_POPUP_CENTER, + UI_BLOCK_BOUNDS_PIE_CENTER, } eBlockBoundsCalc; void uiBoundsBlock(struct uiBlock *block, int addval); @@ -435,6 +457,7 @@ void uiButSetDragValue(uiBut *but); void uiButSetDragImage(uiBut *but, const char *path, int icon, struct ImBuf *ima, float scale); bool UI_but_active_drop_name(struct bContext *C); +bool UI_but_active_drop_color(struct bContext *C); void uiButSetFlag(uiBut *but, int flag); void uiButClearFlag(uiBut *but, int flag); @@ -695,7 +718,7 @@ void UI_panel_category_draw_all(struct ARegion *ar, const * as screen/ if ED_KEYMAP_UI is set, or internally in popup functions. */ void UI_add_region_handlers(struct ListBase *handlers); -void UI_add_popup_handlers(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup); +void UI_add_popup_handlers(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click); void UI_remove_popup_handlers(struct ListBase *handlers, uiPopupBlockHandle *popup); void UI_remove_popup_handlers_all(struct bContext *C, struct ListBase *handlers); @@ -706,7 +729,6 @@ void UI_remove_popup_handlers_all(struct bContext *C, struct ListBase *handlers) void UI_init(void); void UI_init_userdef(void); -void UI_init_userdef_factory(void); void UI_reinit_font(void); void UI_exit(void); @@ -728,6 +750,7 @@ void UI_exit(void); #define UI_LAYOUT_HEADER 1 #define UI_LAYOUT_MENU 2 #define UI_LAYOUT_TOOLBAR 3 +#define UI_LAYOUT_PIEMENU 4 #define UI_UNIT_X ((void)0, U.widget_unit) #define UI_UNIT_Y ((void)0, U.widget_unit) @@ -818,8 +841,8 @@ uiLayout *uiLayoutListBox(uiLayout *layout, struct uiList *ui_list, struct Point uiLayout *uiLayoutAbsolute(uiLayout *layout, int align); uiLayout *uiLayoutSplit(uiLayout *layout, float percentage, int align); uiLayout *uiLayoutOverlap(uiLayout *layout); - uiBlock *uiLayoutAbsoluteBlock(uiLayout *layout); +uiLayout *uiLayoutRadial(uiLayout *layout); /* templates */ void uiTemplateHeader(uiLayout *layout, struct bContext *C); @@ -842,8 +865,10 @@ void uiTemplateIconView(uiLayout *layout, struct PointerRNA *ptr, const char *pr void uiTemplateHistogram(uiLayout *layout, struct PointerRNA *ptr, const char *propname); void uiTemplateWaveform(uiLayout *layout, struct PointerRNA *ptr, const char *propname); void uiTemplateVectorscope(uiLayout *layout, struct PointerRNA *ptr, const char *propname); -void uiTemplateCurveMapping(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int type, int levels, int brush); +void uiTemplateCurveMapping(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int type, + int levels, int brush, int neg_slope); void uiTemplateColorPicker(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int value_slider, int lock, int lock_luminosity, int cubic); +void uiTemplatePalette(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color); void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_layer); void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char *propname, @@ -913,7 +938,14 @@ void uiItemMenuEnumO(uiLayout *layout, struct bContext *C, const char *opname, c void uiItemMenuEnumR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name, int icon); /* UI Operators */ +typedef struct uiDragColorHandle { + float color[3]; + bool gamma_corrected; +} uiDragColorHandle; + void UI_buttons_operatortypes(void); +void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); +int UI_drop_color_poll(struct bContext *C, struct wmDrag *drag, const struct wmEvent *event); /* Helpers for Operators */ uiBut *uiContextActiveButton(const struct bContext *C); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 0f11994e2d1..5b61e76f514 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -48,6 +48,9 @@ typedef enum { #undef DEF_ICON #undef DEF_VICO +/* use to denote intentionally unset theme color */ +#define TH_UNDEFINED -1 + enum { TH_REDALERT, @@ -84,6 +87,7 @@ enum { TH_GRID, TH_WIRE, + TH_WIRE_INNER, TH_WIRE_EDIT, TH_SELECT, TH_ACTIVE, @@ -103,6 +107,7 @@ enum { TH_FACE_SELECT, TH_NORMAL, TH_VNORMAL, + TH_LNORMAL, TH_FACE_DOT, TH_FACEDOT_SIZE, TH_CFRAME, @@ -234,6 +239,9 @@ enum { TH_STITCH_PREVIEW_UNSTITCHABLE, TH_STITCH_PREVIEW_ACTIVE, + TH_PAINT_CURVE_HANDLE, + TH_PAINT_CURVE_PIVOT, + TH_UV_SHADOW, TH_UV_OTHERS, @@ -311,6 +319,7 @@ int UI_GetThemeValue(int colorid); // get three color values, scaled to 0.0-1.0 range void UI_GetThemeColor3fv(int colorid, float col[3]); +void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned char col[3]); // get the color, range 0.0-1.0, complete with shading offset void UI_GetThemeColorShade3fv(int colorid, int offset, float col[3]); void UI_GetThemeColorShade3ubv(int colorid, int offset, unsigned char col[3]); diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index e13517adbb3..b921d17104c 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../../python ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -70,4 +71,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_interface "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/interface/SConscript b/source/blender/editors/interface/SConscript index 1936e17a7bb..303ab7ff286 100644 --- a/source/blender/editors/interface/SConscript +++ b/source/blender/editors/interface/SConscript @@ -31,7 +31,8 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', @@ -45,7 +46,7 @@ incs = [ '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 19d4e32152f..41bf5d5494e 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -51,6 +51,7 @@ #include "BKE_context.h" #include "BKE_unit.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_idprop.h" @@ -97,6 +98,11 @@ bool ui_block_is_menu(const uiBlock *block) ((block->flag & UI_BLOCK_KEEP_OPEN) == 0)); } +bool ui_block_is_pie_menu(const uiBlock *block) +{ + return ((block->flag & UI_BLOCK_RADIAL) != 0); +} + static bool ui_is_but_unit_radians_ex(UnitSettings *unit, const int unit_type) { return (unit->system_rotation == USER_UNIT_ROT_RADIANS && unit_type == PROP_UNIT_ROTATION); @@ -295,9 +301,8 @@ void ui_bounds_block(uiBlock *block) block->safety.ymax = block->rect.ymax + xof; } -static void ui_centered_bounds_block(const bContext *C, uiBlock *block) +static void ui_centered_bounds_block(wmWindow *window, uiBlock *block) { - wmWindow *window = CTX_wm_window(C); int xmax, ymax; int startx, starty; int width, height; @@ -322,9 +327,23 @@ static void ui_centered_bounds_block(const bContext *C, uiBlock *block) ui_bounds_block(block); } -static void ui_popup_bounds_block(const bContext *C, uiBlock *block, eBlockBoundsCalc bounds_calc) + +static void ui_centered_pie_bounds_block(uiBlock *block) +{ + const int xy[2] = { + block->pie_data.pie_center_spawned[0], + block->pie_data.pie_center_spawned[1] + }; + + ui_block_translate(block, xy[0], xy[1]); + + /* now recompute bounds and safety */ + ui_bounds_block(block); +} + +static void ui_popup_bounds_block(wmWindow *window, uiBlock *block, + eBlockBoundsCalc bounds_calc, const int xy[2]) { - wmWindow *window = CTX_wm_window(C); int startx, starty, endx, endy, width, height, oldwidth, oldheight; int oldbounds, xmax, ymax; const int margin = UI_SCREEN_MARGIN; @@ -362,8 +381,8 @@ static void ui_popup_bounds_block(const bContext *C, uiBlock *block, eBlockBound /* offset block based on mouse position, user offset is scaled * along in case we resized the block in ui_text_bounds_block */ - startx = window->eventstate->x + block->rect.xmin + (block->mx * width) / oldwidth; - starty = window->eventstate->y + block->rect.ymin + (block->my * height) / oldheight; + startx = xy[0] + block->rect.xmin + (block->mx * width) / oldwidth; + starty = xy[1] + block->rect.ymin + (block->my * height) / oldheight; if (startx < margin) startx = margin; @@ -831,7 +850,7 @@ static void ui_menu_block_set_keyaccels(uiBlock *block) * fun first pass on all buttons so first word chars always get first priority */ for (but = block->buttons.first; but; but = but->next) { - if (!ELEM5(but->type, BUT, BUTM, MENU, BLOCK, PULLDOWN) || (but->flag & UI_HIDDEN)) { + if (!ELEM(but->type, BUT, BUTM, MENU, BLOCK, PULLDOWN) || (but->flag & UI_HIDDEN)) { /* pass */ } else if (but->menu_key == '\0') { @@ -1063,6 +1082,41 @@ static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but, return found; } +/* this goes in a seemingly weird pattern: + * + * 4 + * 5 6 + * 1 2 + * 7 8 + * 3 + * + * but it's actually quite logical. It's designed to be 'upwards compatible' + * for muscle memory so that the menu item locations are fixed and don't move + * 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 + * added to complete the compass directions. From here, it's just a matter of + * subdividing the rest of the angles for the last 4 items. + * + * --Matt 07/2006 + */ +const char ui_radial_dir_order[8] = { + UI_RADIAL_W, UI_RADIAL_E, UI_RADIAL_S, UI_RADIAL_N, + UI_RADIAL_NW, UI_RADIAL_NE, UI_RADIAL_SW, UI_RADIAL_SE}; + +const char ui_radial_dir_to_numpad[8] = {8, 9, 6, 3, 2, 1, 4, 7}; +const short ui_radial_dir_to_angle[8] = {90, 45, 0, 315, 270, 225, 180, 135}; + +static void ui_but_pie_direction_string(uiBut *but, char *buf, int size) +{ + BLI_assert(but->pie_dir < ARRAY_SIZE(ui_radial_dir_to_numpad)); + BLI_snprintf(buf, size, "%d", ui_radial_dir_to_numpad[but->pie_dir]); +} + static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) { uiBut *but; @@ -1072,40 +1126,71 @@ static void ui_menu_block_set_keymaps(const bContext *C, uiBlock *block) if (block->rect.xmin != block->rect.xmax) return; - for (but = block->buttons.first; but; but = but->next) { - - if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) { - ui_but_add_shortcut(but, buf, false); + if (block->flag & UI_BLOCK_RADIAL) { + for (but = block->buttons.first; but; but = but->next) { + if (but->pie_dir != UI_RADIAL_NONE) { + ui_but_pie_direction_string(but, buf, sizeof(buf)); + ui_but_add_shortcut(but, buf, false); + } } - else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) { - ui_but_add_shortcut(but, buf, false); + } + else { + for (but = block->buttons.first; but; but = but->next) { + + if (ui_but_event_operator_string(C, but, buf, sizeof(buf))) { + ui_but_add_shortcut(but, buf, false); + } + else if (ui_but_event_property_operator_string(C, but, buf, sizeof(buf))) { + ui_but_add_shortcut(but, buf, false); + } } } } -void uiEndBlock(const bContext *C, uiBlock *block) +void uiBlockUpdateFromOld(const bContext *C, uiBlock *block) { - const bool has_old = (block->oldblock != NULL); - /* avoid searches when old/new lists align */ - uiBut *but_old = has_old ? block->oldblock->buttons.first : NULL; - + uiBut *but_old; uiBut *but; - Scene *scene = CTX_data_scene(C); + if (!block->oldblock) + return; + + but_old = block->oldblock->buttons.first; - if (has_old && BLI_listbase_is_empty(&block->oldblock->butstore) == false) { + if (BLI_listbase_is_empty(&block->oldblock->butstore) == false) { UI_butstore_update(block); } + for (but = block->buttons.first; but; but = but->next) { + if (ui_but_update_from_old_block(C, block, &but, &but_old)) { + ui_check_but(but); + } + } + + block->auto_open = block->oldblock->auto_open; + block->auto_open_last = block->oldblock->auto_open_last; + block->tooltipdisabled = block->oldblock->tooltipdisabled; + copy_v3_v3(ui_block_hsv_get(block), + ui_block_hsv_get(block->oldblock)); + + block->oldblock = NULL; +} + +void uiEndBlock_ex(const bContext *C, uiBlock *block, const int xy[2]) +{ + wmWindow *window = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); + uiBut *but; + + BLI_assert(block->active); + + uiBlockUpdateFromOld(C, block); + /* inherit flags from 'old' buttons that was drawn here previous, based * on matching buttons, we need this to make button event handling non * blocking, while still allowing buttons to be remade each redraw as it * is expected by blender code */ for (but = block->buttons.first; but; but = but->next) { - if (has_old && ui_but_update_from_old_block(C, block, &but, &but_old)) { - ui_check_but(but); - } - /* temp? Proper check for graying out */ if (but->optype) { wmOperatorType *ot = but->optype; @@ -1125,15 +1210,7 @@ void uiEndBlock(const bContext *C, uiBlock *block) ui_but_anim_flag(but, (scene) ? scene->r.cfra : 0.0f); } - if (block->oldblock) { - block->auto_open = block->oldblock->auto_open; - block->auto_open_last = block->oldblock->auto_open_last; - block->tooltipdisabled = block->oldblock->tooltipdisabled; - copy_v3_v3(ui_block_hsv_get(block), - ui_block_hsv_get(block->oldblock)); - block->oldblock = NULL; - } /* handle pending stuff */ if (block->layouts.first) { @@ -1159,13 +1236,16 @@ void uiEndBlock(const bContext *C, uiBlock *block) ui_text_bounds_block(block, 0.0f); break; case UI_BLOCK_BOUNDS_POPUP_CENTER: - ui_centered_bounds_block(C, block); + ui_centered_bounds_block(window, block); + break; + case UI_BLOCK_BOUNDS_PIE_CENTER: + ui_centered_pie_bounds_block(block); break; /* fallback */ case UI_BLOCK_BOUNDS_POPUP_MOUSE: case UI_BLOCK_BOUNDS_POPUP_MENU: - ui_popup_bounds_block(C, block, block->bounds_type); + ui_popup_bounds_block(window, block, block->bounds_type, xy); break; } @@ -1179,6 +1259,13 @@ void uiEndBlock(const bContext *C, uiBlock *block) block->endblock = 1; } +void uiEndBlock(const bContext *C, uiBlock *block) +{ + wmWindow *window = CTX_wm_window(C); + + uiEndBlock_ex(C, block, &window->eventstate->x); +} + /* ************** BLOCK DRAWING FUNCTION ************* */ void ui_fontscale(short *points, float aspect) @@ -1256,11 +1343,13 @@ void uiDrawBlock(const bContext *C, uiBlock *block) glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); - - wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f); + + wmOrtho2_region_ui(ar); /* back */ - if (block->flag & UI_BLOCK_LOOP) + if (block->flag & UI_BLOCK_RADIAL) + ui_draw_pie_center(block); + else if (block->flag & UI_BLOCK_LOOP) ui_draw_menu_back(&style, block, &rect); else if (block->panel) ui_draw_aligned_panel(&style, block, &rect, UI_panel_category_is_visible(ar)); @@ -1301,7 +1390,7 @@ int ui_is_but_push_ex(uiBut *but, double *value) int is_push = 0; if (but->bit) { - const bool state = ELEM3(but->type, TOGN, ICONTOGN, OPTIONN) ? false : true; + const bool state = ELEM(but->type, TOGN, ICONTOGN, OPTIONN) ? false : true; int lvalue; UI_GET_BUT_VALUE_INIT(but, *value); lvalue = (int)*value; @@ -1430,7 +1519,7 @@ void uiComposeLinks(uiBlock *block) } } else if (link->poin) { - bt = ui_find_inlink(block, *(link->poin) ); + bt = ui_find_inlink(block, *link->poin); if (bt) { if ((but->flag & UI_BUT_SCA_LINK_GREY) || (bt->flag & UI_BUT_SCA_LINK_GREY)) { ui_add_link_line(&link->lines, but, bt, true); @@ -1611,7 +1700,7 @@ bool ui_is_but_float(const uiBut *but) bool ui_is_but_bool(const uiBut *but) { - if (ELEM4(but->type, TOG, TOGN, ICONTOG, ICONTOGN)) + if (ELEM(but->type, TOG, TOGN, ICONTOG, ICONTOGN)) return true; if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_BOOLEAN) @@ -1796,18 +1885,7 @@ void ui_set_but_val(uiBut *but, double value) value = (char)floor(value + 0.5); } else if (but->pointype == UI_BUT_POIN_SHORT) { - /* gcc 3.2.1 seems to have problems - * casting a double like 32772.0 to - * a short so we cast to an int, then - * to a short. - * - * Update: even in gcc.4.6 using intermediate int cast gives -32764, - * where as a direct cast from double to short gives -32768, - * if this difference isn't important we could remove this hack, - * since we dont support gcc3 anymore - Campbell */ - int gcckludge; - gcckludge = (int) floor(value + 0.5); - value = (short)gcckludge; + value = (short)floor(value + 0.5); } else if (but->pointype == UI_BUT_POIN_INT) value = (int)floor(value + 0.5); @@ -1835,7 +1913,7 @@ void ui_set_but_val(uiBut *but, double value) int ui_get_but_string_max_length(uiBut *but) { - if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) + if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) return but->hardmax; else return UI_MAX_DRAW_STR; @@ -1861,27 +1939,13 @@ static double ui_get_but_scale_unit(uiBut *but, double value) UnitSettings *unit = but->block->unit; int unit_type = uiButGetUnitType(but); - if (unit_type == PROP_UNIT_LENGTH) { - return value * (double)unit->scale_length; - } - else if (unit_type == PROP_UNIT_CAMERA) { - return value * (double)unit->scale_length; - } - else if (unit_type == PROP_UNIT_AREA) { - return value * pow(unit->scale_length, 2); - } - else if (unit_type == PROP_UNIT_VOLUME) { - return value * pow(unit->scale_length, 3); - } - else if (unit_type == PROP_UNIT_MASS) { - return value * pow(unit->scale_length, 3); - } - else if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */ + /* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */ + if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */ Scene *scene = CTX_data_scene(but->block->evil_C); return FRA2TIME(value); } else { - return value; + return BKE_scene_unit_scale(unit, RNA_SUBTYPE_UNIT_VALUE(unit_type), value); } } @@ -1893,8 +1957,7 @@ void ui_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) int unit_type = uiButGetUnitType(but); char *orig_str; - orig_str = MEM_callocN(sizeof(char) * maxlen + 1, "textedit sub str"); - memcpy(orig_str, str, maxlen); + orig_str = BLI_strdup(str); bUnit_ToUnitAltName(str, maxlen, orig_str, unit->system, RNA_SUBTYPE_UNIT_VALUE(unit_type)); @@ -1951,7 +2014,7 @@ static float ui_get_but_step_unit(uiBut *but, float step_default) */ void ui_get_but_string_ex(uiBut *but, char *str, const size_t maxlen, const int float_precision) { - if (but->rnaprop && ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (but->rnaprop && ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { PropertyType type; const char *buf = NULL; int buf_len; @@ -1984,7 +2047,7 @@ void ui_get_but_string_ex(uiBut *but, char *str, const size_t maxlen, const int } else if (buf && buf != str) { /* string was too long, we have to truncate */ - memcpy(str, buf, MIN2(maxlen, (size_t)buf_len + 1)); + memcpy(str, buf, MIN2(maxlen, (size_t)(buf_len + 1))); MEM_freeN((void *)buf); } } @@ -2088,7 +2151,7 @@ bool ui_set_but_string_eval_num(bContext *C, uiBut *but, const char *str, double bool ui_set_but_string(bContext *C, uiBut *but, const char *str) { - if (but->rnaprop && ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (but->rnaprop && ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (RNA_property_editable(&but->rnapoin, but->rnaprop)) { PropertyType type; @@ -2430,6 +2493,7 @@ void uiBlockSetRegion(uiBlock *block, ARegion *region) if (oldblock) { oldblock->active = 0; oldblock->panel = NULL; + oldblock->handle = NULL; } /* at the beginning of the list! for dynamical menus/blocks */ @@ -2739,7 +2803,7 @@ void uiBlockEndAlign(uiBlock *block) bool ui_but_can_align(uiBut *but) { - return !ELEM5(but->type, LABEL, OPTION, OPTIONN, SEPR, SEPRLINE); + return !ELEM(but->type, LABEL, OPTION, OPTIONN, SEPR, SEPRLINE); } static void ui_block_do_align_but(uiBut *first, short nr) @@ -2994,6 +3058,7 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, but->lock = block->lock; but->lockstr = block->lockstr; but->dt = block->dt; + but->pie_dir = UI_RADIAL_NONE; but->block = block; /* pointer back, used for frontbuffer status, and picker */ @@ -3020,8 +3085,11 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, } } - if ((block->flag & UI_BLOCK_LOOP) || - ELEM8(but->type, MENU, TEX, LABEL, BLOCK, BUTM, SEARCH_MENU, PROGRESSBAR, SEARCH_MENU_UNLINK)) + if (block->flag & UI_BLOCK_RADIAL) { + but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); + } + else if ((block->flag & UI_BLOCK_LOOP) || + ELEM(but->type, MENU, TEX, LABEL, BLOCK, BUTM, SEARCH_MENU, PROGRESSBAR, SEARCH_MENU_UNLINK)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -3040,7 +3108,7 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, } /* keep track of UI_interface.h */ - if (ELEM11(but->type, BLOCK, BUT, LABEL, PULLDOWN, ROUNDBOX, LISTBOX, BUTM, SCROLL, SEPR, SEPRLINE, GRIP)) {} + if (ELEM(but->type, BLOCK, BUT, LABEL, PULLDOWN, ROUNDBOX, LISTBOX, BUTM, SCROLL, SEPR, SEPRLINE, GRIP)) {} else if (but->type >= SEARCH_MENU) {} else but->flag |= UI_BUT_UNDO; @@ -3170,12 +3238,12 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu } else { if (item->icon) { - uiDefIconTextButF(block, BUTM, B_NOP, item->icon, item->name, 0, 0, - UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description); + uiDefIconTextButI(block, BUTM, B_NOP, item->icon, item->name, 0, 0, + UI_UNIT_X * 5, UI_UNIT_Y, &handle->retvalue, item->value, 0.0, 0, -1, item->description); } else { - uiDefButF(block, BUTM, B_NOP, item->name, 0, 0, - UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, (float) item->value, 0.0, 0, -1, item->description); + uiDefButI(block, BUTM, B_NOP, item->name, 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &handle->retvalue, item->value, 0.0, 0, -1, item->description); } } } @@ -3205,12 +3273,12 @@ static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *s int icon = 0; uiMenuCreateFunc func = NULL; - if (ELEM3(type, COLOR, HSVCIRCLE, HSVCUBE)) { + if (ELEM(type, COLOR, HSVCIRCLE, HSVCUBE)) { BLI_assert(index == -1); } /* use rna values if parameters are not specified */ - if ((proptype == PROP_ENUM) && ELEM3(type, MENU, ROW, LISTROW)) { + if ((proptype == PROP_ENUM) && ELEM(type, MENU, ROW, LISTROW)) { /* MENU is handled a little differently here */ EnumPropertyItem *item; int value; @@ -3783,8 +3851,7 @@ void uiBlockSetDirection(uiBlock *block, char direction) /* this call escapes if there's alignment flags */ void uiBlockFlipOrder(uiBlock *block) { - ListBase lb; - uiBut *but, *next; + uiBut *but; float centy, miny = 10000, maxy = -10000; if (U.uiflag & USER_MENUFIXEDORDER) @@ -3804,17 +3871,6 @@ void uiBlockFlipOrder(uiBlock *block) but->rect.ymax = centy - (but->rect.ymax - centy); SWAP(float, but->rect.ymin, but->rect.ymax); } - - /* also flip order in block itself, for example for arrowkey */ - BLI_listbase_clear(&lb); - but = block->buttons.first; - while (but) { - next = but->next; - BLI_remlink(&block->buttons, but); - BLI_addtail(&lb, but); - but = next; - } - block->buttons = lb; } @@ -4340,7 +4396,7 @@ void uiButGetStrInfo(bContext *C, uiBut *but, ...) } tmp = BLI_strdup(_tmp); } - else if (ELEM3(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) { + else if (ELEM(type, BUT_GET_RNAENUM_IDENTIFIER, BUT_GET_RNAENUM_LABEL, BUT_GET_RNAENUM_TIP)) { PointerRNA *ptr = NULL; PropertyRNA *prop = NULL; int value = 0; @@ -4430,11 +4486,6 @@ void UI_init_userdef(void) uiStyleInit(); } -void UI_init_userdef_factory(void) -{ - init_userdef_factory(); -} - void UI_reinit_font(void) { uiStyleInit(); diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index f0f08358013..48e54270e95 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -61,7 +61,7 @@ static FCurve *ui_but_get_fcurve(uiBut *but, bAction **action, bool *r_driven) * but works well enough in typical cases */ int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; - return rna_get_fcurve(&but->rnapoin, but->rnaprop, rnaindex, action, r_driven); + return rna_get_fcurve_context_ui(but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, action, r_driven); } void ui_but_anim_flag(uiBut *but, float cfra) @@ -172,6 +172,9 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* get path */ path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); + if (path == NULL) { + return false; + } /* create driver */ fcu = verify_driver_fcurve(id, path, but->rnaindex, 1); diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index d0f238bc5ba..235d7652539 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -587,7 +587,7 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), int i, c; float w, w3, h, alpha, yofs; GLint scissor[4]; - float colors[3][3] = MAT3_UNITY; + float colors[3][3]; float colorsycc[3][3] = {{1, 0, 1}, {1, 1, 0}, {0, 1, 1}}; float colors_alpha[3][3], colorsycc_alpha[3][3]; /* colors pre multiplied by alpha for speed up */ float min, max; @@ -609,6 +609,8 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), /* log scale for alpha */ alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha; + unit_m3(colors); + for (c = 0; c < 3; c++) { for (i = 0; i < 3; i++) { colors_alpha[c][i] = colors[c][i] * alpha; @@ -693,11 +695,11 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol), } /* RGB / YCC (3 channels) */ - else if (ELEM4(scopes->wavefrm_mode, - SCOPES_WAVEFRM_RGB, - SCOPES_WAVEFRM_YCC_601, - SCOPES_WAVEFRM_YCC_709, - SCOPES_WAVEFRM_YCC_JPEG)) + else if (ELEM(scopes->wavefrm_mode, + SCOPES_WAVEFRM_RGB, + SCOPES_WAVEFRM_YCC_601, + SCOPES_WAVEFRM_YCC_709, + SCOPES_WAVEFRM_YCC_JPEG)) { int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB); @@ -1026,13 +1028,13 @@ static void ui_draw_colorband_handle( if (active) glColor3ub(196, 196, 196); else - glColor3ub(128, 128, 128); + glColor3ub(96, 96, 96); ui_draw_colorband_handle_tri(x, y1 + height, half_width, half_width, true); if (active) glColor3ub(255, 255, 255); else - glColor3ub(196, 196, 196); + glColor3ub(128, 128, 128); ui_draw_colorband_handle_tri_hlight(x, y1 + height - 1, (half_width - 1), (half_width - 1)); glColor3ub(0, 0, 0); @@ -1149,9 +1151,11 @@ void ui_draw_but_COLORBAND(uiBut *but, uiWidgetColors *UNUSED(wcol), const rcti } /* layer: active handle */ - cbd = &coba->data[coba->cur]; - pos = x1 + cbd->pos * (sizex - 1) + 1; - ui_draw_colorband_handle(rect, pos, &cbd->r, display, true); + if (coba->tot != 0) { + cbd = &coba->data[coba->cur]; + pos = x1 + cbd->pos * (sizex - 1) + 1; + ui_draw_colorband_handle(rect, pos, &cbd->r, display, true); + } } void ui_draw_but_NORMAL(uiBut *but, uiWidgetColors *wcol, const rcti *rect) @@ -1324,9 +1328,9 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti float col[3] = {0.0f, 0.0f, 0.0f}; /* dummy arg */ grid.xmin = rect->xmin + zoomx * (-offsx); - grid.xmax = rect->xmax + zoomx * (-offsx); + grid.xmax = grid.xmin + zoomx; grid.ymin = rect->ymin + zoomy * (-offsy); - grid.ymax = rect->ymax + zoomy * (-offsy); + grid.ymax = grid.ymin + zoomy; ui_draw_gradient(&grid, col, UI_GRAD_H, 1.0f); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 2da04ed56f3..a3c52ec3e3c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -38,6 +38,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_brush_types.h" #include "DNA_sensor_types.h" #include "DNA_controller_types.h" #include "DNA_actuator_types.h" @@ -60,6 +61,7 @@ #include "PIL_time.h" #include "BKE_blender.h" +#include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_idprop.h" @@ -99,6 +101,8 @@ /* drag popups by their header */ #define USE_DRAG_POPUP +#define UI_MAX_PASSWORD_STR 128 + /* proto */ static void ui_add_smart_controller(bContext *C, uiBut *from, uiBut *to); static void ui_add_link(bContext *C, uiBut *from, uiBut *to); @@ -114,6 +118,7 @@ static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEve #define BUTTON_TOOLTIP_DELAY 0.500 #define BUTTON_FLASH_DELAY 0.020 #define MENU_SCROLL_INTERVAL 0.1 +#define PIE_MENU_INTERVAL 0.01 #define BUTTON_AUTO_OPEN_THRESH 0.3 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0 /* pixels to move the cursor to get out of keyboard navigation */ @@ -151,7 +156,7 @@ typedef enum uiHandleButtonState { * note: half the height of a button is about right... */ #define DRAG_MULTINUM_THRESHOLD_DRAG_X (UI_UNIT_Y / 4) -/* how far to drag horizontally before we stop checkign which buttons the gesture spans (in pixels), +/* how far to drag horizontally before we stop checking which buttons the gesture spans (in pixels), * locking down the buttons so we can drag freely without worrying about vertical movement. */ #define DRAG_MULTINUM_THRESHOLD_DRAG_Y (UI_UNIT_Y / 4) @@ -383,16 +388,16 @@ void ui_pan_to_scroll(const wmEvent *event, int *type, int *val) } } -static bool ui_but_editable(uiBut *but) +bool ui_but_is_editable(const uiBut *but) { - return ELEM6(but->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX, PROGRESSBAR); + return !ELEM(but->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX, PROGRESSBAR); } static uiBut *ui_but_prev(uiBut *but) { while (but->prev) { but = but->prev; - if (!ui_but_editable(but)) return but; + if (ui_but_is_editable(but)) return but; } return NULL; } @@ -401,7 +406,7 @@ static uiBut *ui_but_next(uiBut *but) { while (but->next) { but = but->next; - if (!ui_but_editable(but)) return but; + if (ui_but_is_editable(but)) return but; } return NULL; } @@ -412,7 +417,7 @@ static uiBut *ui_but_first(uiBlock *block) but = block->buttons.first; while (but) { - if (!ui_but_editable(but)) return but; + if (ui_but_is_editable(but)) return but; but = but->next; } return NULL; @@ -424,7 +429,7 @@ static uiBut *ui_but_last(uiBlock *block) but = block->buttons.last; while (but) { - if (!ui_but_editable(but)) return but; + if (ui_but_is_editable(but)) return but; but = but->prev; } return NULL; @@ -433,7 +438,7 @@ static uiBut *ui_but_last(uiBlock *block) static bool ui_is_a_warp_but(uiBut *but) { if (U.uiflag & USER_CONTINUOUS_MOUSE) { - if (ELEM6(but->type, NUM, NUMSLI, HSVCIRCLE, TRACKPREVIEW, HSVCUBE, BUT_CURVE)) { + if (ELEM(but->type, NUM, NUMSLI, HSVCIRCLE, TRACKPREVIEW, HSVCUBE, BUT_CURVE)) { return true; } } @@ -463,7 +468,7 @@ bool ui_is_but_utf8(const uiBut *but) { if (but->rnaprop) { const int subtype = RNA_property_subtype(but->rnaprop); - return !(ELEM4(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING)); + return !(ELEM(subtype, PROP_FILEPATH, PROP_DIRPATH, PROP_FILENAME, PROP_BYTESTRING)); } else { return !(but->flag & UI_BUT_NO_UTF8); @@ -599,7 +604,13 @@ static void ui_apply_autokey(bContext *C, uiBut *but) /* make a little report about what we've done! */ if (but->rnaprop) { - char *buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); + char *buf; + + if (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD) { + return; + } + + buf = WM_prop_pystring_assign(C, &but->rnapoin, but->rnaprop, but->rnaindex); if (buf) { BKE_report(CTX_wm_reports(C), RPT_PROPERTY, buf); MEM_freeN(buf); @@ -633,7 +644,7 @@ static void ui_apply_but_funcs_after(bContext *C) } if (after.optype) - WM_operator_name_call(C, after.optype->idname, after.opcontext, (after.opptr) ? &opptr : NULL); + WM_operator_name_call_ptr(C, after.optype, after.opcontext, (after.opptr) ? &opptr : NULL); if (after.opptr) WM_operator_properties_free(&opptr); @@ -717,7 +728,7 @@ static void ui_apply_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data) if (value == 0.0) push = 1; else push = 0; - if (ELEM3(but->type, TOGN, ICONTOGN, OPTIONN)) push = !push; + if (ELEM(but->type, TOGN, ICONTOGN, OPTIONN)) push = !push; ui_set_but_val(but, (double)push); if (but->type == ICONTOG || but->type == ICONTOGN) ui_check_but(but); } @@ -986,6 +997,9 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl else { but->active->value = mbut_state->origvalue + value_delta; } + + /* clamp based on soft limits, see: T40154 */ + CLAMP(but->active->value, (double)but->softmin, (double)but->softmax); } ui_button_execute_end(C, ar, but, active_back); } @@ -1186,7 +1200,7 @@ static bool ui_but_mouse_inside_icon(uiBut *but, ARegion *ar, const wmEvent *eve BLI_rcti_rctf_copy(&rect, &but->rect); - if (but->imb) { + if (but->imb || but->type == COLOR) { /* use button size itself */ } else if (but->drawflag & UI_BUT_ICON_LEFT) { @@ -1233,16 +1247,48 @@ static bool ui_but_start_drag(bContext *C, uiBut *but, uiHandleButtonData *data, WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_drag_toggle, ui_handler_region_drag_toggle_remove, - drag_info); + drag_info, false); CTX_wm_region_set(C, ar_prev); } else #endif - { + if (but->type == COLOR) { + bool valid = false; + uiDragColorHandle *drag_info = MEM_callocN(sizeof(*drag_info), __func__); + + /* TODO support more button pointer types */ + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, drag_info->color); + drag_info->gamma_corrected = true; + valid = true; + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, drag_info->color); + drag_info->gamma_corrected = false; + valid = true; + } + else if (but->pointype == UI_BUT_POIN_FLOAT) { + copy_v3_v3(drag_info->color, (float *)but->poin); + valid = true; + } + else if (but->pointype == UI_BUT_POIN_CHAR) { + rgb_uchar_to_float(drag_info->color, (unsigned char *)but->poin); + valid = true; + } + + if (valid) { + WM_event_start_drag(C, ICON_COLOR, WM_DRAG_COLOR, drag_info, 0.0, WM_DRAG_FREE_DATA); + } + else { + MEM_freeN(drag_info); + return false; + } + } + else { wmDrag *drag; - drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(but)); + drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_get_but_val(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)); } @@ -1652,7 +1698,7 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB for (wmd = drags->first; wmd; wmd = wmd->next) { if (wmd->type == WM_DRAG_ID) { /* align these types with UI_but_active_drop_name */ - if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { ID *id = (ID *)wmd->poin; button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); @@ -1799,7 +1845,7 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, } /* text/string and ID data */ - else if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + else if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { uiHandleButtonData *active_data = but->active; if (but->poin == NULL && but->rnapoin.data == NULL) { @@ -1908,28 +1954,35 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, static int ui_text_position_from_hidden(uiBut *but, int pos) { - const char *strpos; + const char *strpos, *butstr; int i; - for (i = 0, strpos = but->drawstr; i < pos; i++) + butstr = (but->editstr) ? but->editstr : but->drawstr; + + for (i = 0, strpos = butstr; i < pos; i++) strpos = BLI_str_find_next_char_utf8(strpos, NULL); - return (strpos - but->drawstr); + return (strpos - butstr); } static int ui_text_position_to_hidden(uiBut *but, int pos) { - return BLI_strnlen_utf8(but->drawstr, pos); + const char *butstr = (but->editstr) ? but->editstr : but->drawstr; + return BLI_strnlen_utf8(butstr, pos); } -void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore) +void ui_button_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], uiBut *but, const bool restore) { + char *butstr; + if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) return; + butstr = (but->editstr) ? but->editstr : but->drawstr; + if (restore) { /* restore original string */ - BLI_strncpy(but->drawstr, password_str, UI_MAX_DRAW_STR); + BLI_strncpy(butstr, password_str, UI_MAX_PASSWORD_STR); /* remap cursor positions */ if (but->pos >= 0) { @@ -1939,8 +1992,8 @@ void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but } } else { - /* convert text to hidden test using asterisks (e.g. pass -> ****) */ - const size_t len = BLI_strlen_utf8(but->drawstr); + /* convert text to hidden text using asterisks (e.g. pass -> ****) */ + const size_t len = BLI_strlen_utf8(butstr); /* remap cursor positions */ if (but->pos >= 0) { @@ -1950,10 +2003,9 @@ void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but } /* save original string */ - BLI_strncpy(password_str, but->drawstr, UI_MAX_DRAW_STR); - - memset(but->drawstr, '*', len); - but->drawstr[len] = '\0'; + BLI_strncpy(password_str, butstr, UI_MAX_PASSWORD_STR); + memset(butstr, '*', len); + butstr[len] = '\0'; } } @@ -1989,7 +2041,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con float startx = but->rect.xmin; float starty_dummy = 0.0f; - char *origstr, password_str[UI_MAX_DRAW_STR]; + char *origstr, password_str[UI_MAX_PASSWORD_STR]; ui_block_to_window_fl(data->region, but->block, &startx, &starty_dummy); @@ -2006,7 +2058,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con BLI_strncpy(origstr, but->editstr, data->maxlen); - if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (but->flag & UI_HAS_ICON) { startx += UI_DPI_ICON_SIZE / aspect; } @@ -2369,6 +2421,18 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) data->str = NULL; } +#ifdef USE_DRAG_MULTINUM + /* this can happen from multi-drag */ + if (data->applied_interactive) { + /* remove any small changes so canceling edit doesn't restore invalid value: T40538 */ + data->cancel = true; + ui_apply_button(C, but->block, but, data, true); + data->cancel = false; + + data->applied_interactive = false; + } +#endif + /* retrieve string */ data->maxlen = ui_get_but_string_max_length(but); data->str = MEM_callocN(sizeof(char) * data->maxlen + 1, "textedit str"); @@ -2389,11 +2453,6 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) data->selextend = 0; data->selstartx = 0.0f; -#ifdef USE_DRAG_MULTINUM - /* this can happen from multi-drag */ - data->applied_interactive = false; -#endif - /* set cursor pos to the end of the text */ but->editstr = data->str; but->pos = len; @@ -2432,6 +2491,9 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) (ui_searchbox_find_index(data->searchbox, but->editstr) == -1)) { data->cancel = true; + + /* ensure menu (popup) too is closed! */ + data->escapecancel = true; } } @@ -2451,11 +2513,11 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa uiBut *but; /* label and roundbox can overlap real buttons (backdrops...) */ - if (ELEM5(actbut->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX)) + if (ELEM(actbut->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX)) return; for (but = actbut->next; but; but = but->next) { - if (ELEM5(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; data->posttype = BUTTON_ACTIVATE_TEXT_EDITING; @@ -2464,7 +2526,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } for (but = block->buttons.first; but != actbut; but = but->next) { - if (ELEM5(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; data->posttype = BUTTON_ACTIVATE_TEXT_EDITING; @@ -2479,11 +2541,11 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa uiBut *but; /* label and roundbox can overlap real buttons (backdrops...) */ - if (ELEM5(actbut->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX)) + if (ELEM(actbut->type, LABEL, SEPR, SEPRLINE, ROUNDBOX, LISTBOX)) return; for (but = actbut->prev; but; but = but->prev) { - if (ELEM5(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; data->posttype = BUTTON_ACTIVATE_TEXT_EDITING; @@ -2492,7 +2554,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } for (but = block->buttons.last; but != actbut; but = but->prev) { - if (ELEM5(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, NUM, NUMSLI, SEARCH_MENU, SEARCH_MENU_UNLINK)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; data->posttype = BUTTON_ACTIVATE_TEXT_EDITING; @@ -2793,7 +2855,7 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) data->coba = (ColorBand *)but->poin; but->editcoba = data->coba; } - else if (ELEM4(but->type, BUT_NORMAL, HSVCUBE, HSVCIRCLE, COLOR)) { + else if (ELEM(but->type, BUT_NORMAL, HSVCUBE, HSVCIRCLE, COLOR)) { ui_get_but_vectorf(but, data->origvec); copy_v3_v3(data->vec, data->origvec); but->editvec = data->vec; @@ -2980,7 +3042,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { but->drawstr[0] = 0; but->modifier_key = 0; button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); @@ -3043,14 +3105,15 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { button_activate_state(C, but, BUTTON_STATE_WAIT_KEY_EVENT); return WM_UI_HANDLER_BREAK; } } else if (data->state == BUTTON_STATE_WAIT_KEY_EVENT) { - if (event->type == MOUSEMOVE) + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { return WM_UI_HANDLER_CONTINUE; + } if (event->val == KM_PRESS) { if (WM_key_event_string(event->type)[0]) @@ -3068,7 +3131,7 @@ static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, c static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM4(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS) { if (ELEM(event->type, PADENTER, RETKEY) && (!ui_is_but_utf8(but))) { /* pass - allow filesel, enter to execute */ } @@ -3096,7 +3159,7 @@ static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButton static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { /* unlink icon is on right */ - if (ELEM4(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS && + if (ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS && ui_is_but_search_unlink_visible(but)) { ARegion *ar = data->region; @@ -3146,7 +3209,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons } #endif if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { #if 0 /* UNUSED */ data->togdual = event->ctrl; data->togonly = !event->shift; @@ -3184,7 +3247,7 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con } #endif - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { int ret = WM_UI_HANDLER_BREAK; /* XXX (a bit ugly) Special case handling for filebrowser drag button */ if (but->dragpoin && but->imb && ui_but_mouse_inside_icon(but, data->region, event)) { @@ -3233,7 +3296,7 @@ static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, floa if (bUnit_IsValid(unit->system, unit_type)) { fac = (float)bUnit_BaseScalar(unit->system, unit_type); - if (ELEM3(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) { + if (ELEM(unit_type, B_UNIT_LENGTH, B_UNIT_AREA, B_UNIT_VOLUME)) { fac /= unit->scale_length; } } @@ -3305,6 +3368,11 @@ static bool ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, if (data->draglock) { if (abs(mx - data->dragstartx) <= 3) return changed; +#ifdef USE_DRAG_MULTINUM + if (ELEM(data->multi_data.init, BUTTON_MULTI_INIT_UNSET, BUTTON_MULTI_INIT_SETUP)) { + return changed; + } +#endif data->draglock = false; data->dragstartx = mx; /* ignore mouse movement within drag-lock */ @@ -3463,7 +3531,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton click = 1; } else if (event->val == KM_PRESS) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -3752,7 +3820,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton click = 2; } else if (event->val == KM_PRESS) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->ctrl) { button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -4009,7 +4077,7 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, /* hack to pass on ctrl+click and double click to overlapping text * editing field for editing list item names */ - if ((ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS && event->ctrl) || + if ((ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS && event->ctrl) || (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) { uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING); @@ -4038,7 +4106,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co } } #ifdef USE_DRAG_TOGGLE - if (event->type == LEFTMOUSE && ui_is_but_drag_toggle(but)) { + if (event->type == LEFTMOUSE && event->val == KM_PRESS && (ui_is_but_drag_toggle(but))) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); data->dragstartx = event->x; data->dragstarty = event->y; @@ -4046,7 +4114,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co } #endif /* regular open menu */ - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; } @@ -4188,14 +4256,44 @@ static bool ui_numedit_but_NORMAL(uiBut *but, uiHandleButtonData *data, return changed; } +static void ui_palette_set_active(uiBut *but) +{ + if ((int)(but->a1) == UI_PALETTE_COLOR) { + Palette *palette = but->rnapoin.id.data; + PaletteColor *color = but->rnapoin.data; + palette->active_color = BLI_findindex(&palette->colors, color); + } +} + static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { - if (ELEM3(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + /* first handle click on icondrag type button */ + if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { + ui_palette_set_active(but); + if (ui_but_mouse_inside_icon(but, data->region, event)) { + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->x; + data->dragstarty = event->y; + return WM_UI_HANDLER_BREAK; + } + } +#ifdef USE_DRAG_TOGGLE + if (event->type == LEFTMOUSE && event->val == KM_PRESS) { + ui_palette_set_active(but); + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->x; + data->dragstarty = event->y; + return WM_UI_HANDLER_BREAK; + } +#endif + /* regular open menu */ + if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) { + ui_palette_set_active(but); button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; } - else if (ELEM3(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { + else if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { float *hsv = ui_block_hsv_get(but->block); float col[3]; @@ -4218,6 +4316,75 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co ui_apply_button(C, but->block, but, data, true); return WM_UI_HANDLER_BREAK; } + else if ((int)(but->a1) == UI_PALETTE_COLOR && + event->type == DELKEY && event->val == KM_PRESS) + { + Palette *palette = but->rnapoin.id.data; + PaletteColor *color = but->rnapoin.data; + + BKE_palette_color_remove(palette, color); + + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + } + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + + /* this function also ends state */ + if (ui_but_start_drag(C, but, data, event)) { + return WM_UI_HANDLER_BREAK; + } + + /* outside icon quit, not needed if drag activated */ + if (0 == ui_but_mouse_inside_icon(but, data->region, event)) { + button_activate_state(C, but, BUTTON_STATE_EXIT); + data->cancel = true; + return WM_UI_HANDLER_BREAK; + } + + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { + if ((int)(but->a1) == UI_PALETTE_COLOR) { + if (!event->ctrl) { + float color[3]; + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active(scene); + Brush *brush = BKE_paint_brush(paint); + + if (brush->flag & BRUSH_USE_GRADIENT) { + float *target = &brush->gradient->data[brush->gradient->cur].r; + + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); + ui_block_to_scene_linear_v3(but->block, target); + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, target); + } + } + else { + if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); + BKE_brush_color_set(scene, brush, color); + } + else if (but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + RNA_property_float_get_array(&but->rnapoin, but->rnaprop, color); + ui_block_to_display_space_v3(but->block, color); + BKE_brush_color_set(scene, brush, color); + } + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); + } + else { + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + } + } + else { + button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); + } + return WM_UI_HANDLER_BREAK; + } + } return WM_UI_HANDLER_CONTINUE; @@ -4397,7 +4564,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, } if (snap != SNAP_OFF) { - if (ELEM3((int)but->a1, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { + if (ELEM((int)but->a1, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { ui_color_snap_hue(snap, &hsv[0]); } } @@ -4474,7 +4641,7 @@ static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, } if (snap != SNAP_OFF) { - if (ELEM3((int)but->a1, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { + if (ELEM((int)but->a1, UI_GRAD_HV, UI_GRAD_HS, UI_GRAD_H)) { ui_color_snap_hue(snap, &hsv[0]); } } @@ -4863,6 +5030,9 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m if (data->draglastx == mx) return changed; + if (data->coba->tot == 0) + return changed; + dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect); data->dragcbd->pos += dx; CLAMP(data->dragcbd->pos, 0.0f, 1.0f); @@ -5501,7 +5671,6 @@ static uiBlock *menu_change_shortcut(bContext *C, ARegion *ar, void *arg) uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE); uiPopupBoundsBlock(block, 6, -50, 26); - uiEndBlock(C, block); return block; } @@ -5546,11 +5715,22 @@ static uiBlock *menu_add_shortcut(bContext *C, ARegion *ar, void *arg) uiItemR(layout, &ptr, "type", UI_ITEM_R_FULL_EVENT | UI_ITEM_R_IMMEDIATE, "", ICON_NONE); uiPopupBoundsBlock(block, 6, -50, 26); - uiEndBlock(C, block); return block; } +static void menu_add_shortcut_cancel(struct bContext *C, void *arg1) +{ + uiBut *but = (uiBut *)arg1; + wmKeyMap *km; + wmKeyMapItem *kmi; + IDProperty *prop = (but->opptr) ? but->opptr->data : NULL; + int kmi_id = WM_key_event_operator_id(C, but->optype->idname, but->opcontext, prop, true, &km); + + kmi = WM_keymap_item_find_id(km, kmi_id); + WM_keymap_remove_item(km, kmi); +} + static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; @@ -5576,7 +5756,7 @@ static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; button_timers_tooltip_remove(C, but); - uiPupBlock(C, menu_add_shortcut, but); + uiPupBlockEx(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but); } /** @@ -5987,7 +6167,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle drivers */ else if ((event->type == DKEY) && - !ELEM3(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && + !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && (event->val == KM_PRESS)) { if (event->alt) @@ -6001,7 +6181,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * } /* handle keyingsets */ else if ((event->type == KKEY) && - !ELEM3(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && + !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift) && (event->val == KM_PRESS)) { if (event->alt) @@ -6225,7 +6405,34 @@ static bool ui_but_contains_pt(uiBut *but, float mx, float my) return BLI_rctf_isect_pt(&but->rect, mx, my); } -static uiBut *ui_but_find_activated(ARegion *ar) +void ui_but_pie_dir(RadialDirection dir, float vec[2]) +{ + float angle; + + BLI_assert(dir != UI_RADIAL_NONE); + + angle = DEG2RADF((float)ui_radial_dir_to_angle[dir]); + vec[0] = cosf(angle); + vec[1] = sinf(angle); +} + +static bool ui_but_isect_pie_seg(uiBlock *block, uiBut *but) +{ + const float angle_range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_4 : M_PI_4 / 2.0; + float vec[2]; + + if (block->pie_data.flags & UI_PIE_INVALID_DIR) + return false; + + ui_but_pie_dir(but->pie_dir, vec); + + if (saacos(dot_v2v2(vec, block->pie_data.pie_dir)) < angle_range) + return true; + + return false; +} + +uiBut *ui_but_find_activated(ARegion *ar) { uiBlock *block; uiBut *but; @@ -6273,13 +6480,27 @@ bool UI_but_active_drop_name(bContext *C) uiBut *but = ui_but_find_activated(ar); if (but) { - if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) + if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) return 1; } return 0; } +bool UI_but_active_drop_color(bContext *C) +{ + ARegion *ar = CTX_wm_region(C); + + if (ar) { + uiBut *but = ui_but_find_activated(ar); + + if (but && but->type == COLOR) + return true; + } + + return false; +} + static void ui_blocks_set_tooltips(ARegion *ar, const bool enable) { uiBlock *block; @@ -6329,6 +6550,7 @@ static bool ui_mouse_inside_region(ARegion *ar, int x, int y) static bool ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y) { + uiBlock *block = but->block; float mx, my; if (!ui_mouse_inside_region(ar, x, y)) return false; @@ -6336,10 +6558,16 @@ static bool ui_mouse_inside_button(ARegion *ar, uiBut *but, int x, int y) mx = x; my = y; - ui_window_to_block_fl(ar, but->block, &mx, &my); + ui_window_to_block_fl(ar, block, &mx, &my); - if (!ui_but_contains_pt(but, mx, my)) + if (but->pie_dir != UI_RADIAL_NONE) { + if (!ui_but_isect_pie_seg(block, but)) { + return false; + } + } + else if (!ui_but_contains_pt(but, mx, my)) { return false; + } return true; } @@ -6353,7 +6581,7 @@ static bool ui_is_but_interactive(const uiBut *but, const bool labeledit) /* note, LABEL is included for highlights, this allows drags */ if ((but->type == LABEL) && but->dragpoin == NULL) return false; - if (ELEM4(but->type, ROUNDBOX, SEPR, SEPRLINE, LISTBOX)) + if (ELEM(but->type, ROUNDBOX, SEPR, SEPRLINE, LISTBOX)) return false; if (but->flag & UI_HIDDEN) return false; @@ -6393,7 +6621,13 @@ static uiBut *ui_but_find_mouse_over_ex(ARegion *ar, const int x, const int y, c for (but = block->buttons.last; but; but = but->prev) { if (ui_is_but_interactive(but, labeledit)) { - if (ui_but_contains_pt(but, mx, my)) { + if (but->pie_dir != UI_RADIAL_NONE) { + if (ui_but_isect_pie_seg(block, but)) { + butover = but; + break; + } + } + else if (ui_but_contains_pt(but, mx, my)) { butover = but; break; } @@ -6446,9 +6680,13 @@ static uiBut *ui_list_find_mouse_over(ARegion *ar, int x, int y) static bool button_modal_state(uiHandleButtonState state) { - return ELEM6(state, BUTTON_STATE_WAIT_RELEASE, BUTTON_STATE_WAIT_KEY_EVENT, - BUTTON_STATE_NUM_EDITING, BUTTON_STATE_TEXT_EDITING, - BUTTON_STATE_TEXT_SELECTING, BUTTON_STATE_MENU_OPEN); + return ELEM(state, + BUTTON_STATE_WAIT_RELEASE, + BUTTON_STATE_WAIT_KEY_EVENT, + BUTTON_STATE_NUM_EDITING, + BUTTON_STATE_TEXT_EDITING, + BUTTON_STATE_TEXT_SELECTING, + BUTTON_STATE_MENU_OPEN); } static void button_timers_tooltip_remove(bContext *C, uiBut *but) @@ -6486,10 +6724,13 @@ static void button_tooltip_timer_reset(bContext *C, uiBut *but) data->tooltiptimer = NULL; } - if (U.flag & USER_TOOLTIPS) - if (!but->block->tooltipdisabled) - if (!wm->drags.first) + if ((U.flag & USER_TOOLTIPS) || (but->flag & UI_BUT_TIP_FORCE)) { + if (!but->block->tooltipdisabled) { + if (!wm->drags.first) { data->tooltiptimer = WM_event_add_timer(data->wm, data->window, TIMER, BUTTON_TOOLTIP_DELAY); + } + } + } } static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state) @@ -6594,7 +6835,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s if (!(but->block->handle && but->block->handle->popup)) { if (button_modal_state(state)) { if (!button_modal_state(data->state)) - WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data); + WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data, false); } else { if (button_modal_state(data->state)) { @@ -6639,7 +6880,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA copy_v2_fl(data->ungrab_mval, FLT_MAX); #endif - if (ELEM3(but->type, BUT_CURVE, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, BUT_CURVE, SEARCH_MENU, SEARCH_MENU_UNLINK)) { /* XXX curve is temp */ } else { @@ -6695,7 +6936,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA if (but->type == GRIP) { const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect)); - WM_cursor_modal_set(data->window, horizontal ? BC_EW_ARROWCURSOR : BC_NS_ARROWCURSOR); + WM_cursor_modal_set(data->window, horizontal ? CURSOR_X_MOVE : CURSOR_Y_MOVE); } } @@ -6891,7 +7132,7 @@ void uiContextActivePropertyHandle(bContext *C) * operator redo panel - campbell */ uiBlock *block = activebut->block; if (block->handle_func) { - block->handle_func(C, block->handle_func_arg, 0); + block->handle_func(C, block->handle_func_arg, activebut->retval); } } } @@ -6998,6 +7239,13 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *ar) if (event->type == MOUSEMOVE) { but = ui_but_find_mouse_over(ar, event); if (but) { + if (event->alt) { + /* display tooltips if holding alt on mouseover when tooltips are off in prefs */ + but->flag |= UI_BUT_TIP_FORCE; + } + else { + but->flag &= ~UI_BUT_TIP_FORCE; + } button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER); } } @@ -7029,9 +7277,20 @@ void ui_button_activate_do(bContext *C, ARegion *ar, uiBut *but) ui_do_button(C, but->block, but, &event); } +/** + * Simulate moving the mouse over a button (or navigating to it with arrow keys). + * + * exported so menus can start with a highlighted button, + * even if the mouse isnt over it + */ +void ui_button_activate_over(bContext *C, ARegion *ar, uiBut *but) +{ + button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER); +} + void ui_button_execute_begin(struct bContext *UNUSED(C), struct ARegion *ar, uiBut *but, void **active_back) { - /* note: ideally we would not have to change 'but->active' howevwer + /* note: ideally we would not have to change 'but->active' however * some functions we call don't use data (as they should be doing) */ uiHandleButtonData *data; *active_back = but->active; @@ -7091,12 +7350,20 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) retval = WM_UI_HANDLER_CONTINUE; break; case MOUSEMOVE: - /* verify if we are still over the button, if not exit */ - if (!ui_mouse_inside_button(ar, but, event->x, event->y)) { - data->cancel = true; - button_activate_state(C, but, BUTTON_STATE_EXIT); + { + uiBut *but_other = ui_but_find_mouse_over(ar, event); + bool exit = false; + + if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(but->block)) && + !ui_mouse_inside_button(ar, but, event->x, event->y)) + { + exit = true; + } + else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) { + exit = true; } - else if (ui_but_find_mouse_over(ar, event) != but) { + + if (exit) { data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -7107,6 +7374,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) } break; + } case TIMER: { /* handle tooltip timer */ @@ -7690,6 +7958,25 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock int retval; if (but) { + /* Its possible there is an active menu item NOT under the mouse, + * in this case ignore mouse clicks outside the button (but Enter etc is accepted) */ + if (event->val == KM_RELEASE) { + /* pass, needed so we can exit active menu-items when click-dragging out of them */ + } + else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) { + /* pass, skip for dialogs */ + } + else if (!ui_mouse_inside_region(but->active->region, event->x, event->y)) { + /* pass, needed to click-exit outside of non-flaoting menus */ + } + else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) && ISMOUSE(event->type)) { + if (!ui_mouse_inside_button(but->active->region, but, event->x, event->y)) { + but = NULL; + } + } + } + + if (but) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); @@ -7708,6 +7995,32 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock return retval; } +float ui_block_calculate_pie_segment(uiBlock *block, const float event_xy[2]) +{ + float seg1[2]; + float seg2[2]; + float len; + + if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) { + copy_v2_v2(seg1, block->pie_data.pie_center_init); + } + else { + copy_v2_v2(seg1, block->pie_data.pie_center_spawned); + } + + sub_v2_v2v2(seg2, event_xy, seg1); + + len = normalize_v2_v2(block->pie_data.pie_dir, seg2); + + /* ten pixels for now, a bit arbitrary */ + if (len < U.pie_menu_threshold * U.pixelsize) + block->pie_data.flags |= UI_PIE_INVALID_DIR; + else + block->pie_data.flags &= ~UI_PIE_INVALID_DIR; + + return len; +} + static int ui_handle_menu_event( bContext *C, const wmEvent *event, uiPopupBlockHandle *menu, int level, const bool is_parent_inside, const bool is_parent_menu, const bool is_floating) @@ -7739,6 +8052,7 @@ static int ui_handle_menu_event( if (menu->is_grab) { if (event->type == LEFTMOUSE) { menu->is_grab = false; + retval = WM_UI_HANDLER_BREAK; } else { if (event->type == MOUSEMOVE) { @@ -7747,6 +8061,8 @@ static int ui_handle_menu_event( sub_v2_v2v2_int(mdiff, &event->x, menu->grab_xy_prev); copy_v2_v2_int(menu->grab_xy_prev, &event->x); + add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff); + ui_popup_translate(C, ar, mdiff); } @@ -7941,7 +8257,7 @@ static int ui_handle_menu_event( for (but = block->buttons.first; but; but = but->next) { bool doit = false; - if (!ELEM3(but->type, LABEL, SEPR, SEPRLINE)) + if (!ELEM(but->type, LABEL, SEPR, SEPRLINE)) count++; /* exception for rna layer buts */ @@ -8052,7 +8368,7 @@ static int ui_handle_menu_event( if (inside == 0) { uiSafetyRct *saferct = block->saferct.first; - if (ELEM3(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && + if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { @@ -8094,9 +8410,14 @@ static int ui_handle_menu_event( else if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && (inside && is_floating && inside_title)) { - if (!ui_but_find_activated(ar)) { + if (!but || !ui_mouse_inside_button(ar, but, event->x, event->y)) { + if (but) { + button_timers_tooltip_remove(C, but); + } + menu->is_grab = true; copy_v2_v2_int(menu->grab_xy_prev, &event->x); + retval = WM_UI_HANDLER_BREAK; } } #endif @@ -8201,10 +8522,336 @@ static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPo ui_mouse_motion_towards_reinit(menu, &event->x); } - if (menu->menuretval) + if (menu->menuretval) { return WM_UI_HANDLER_CONTINUE; - else + } + else { + return WM_UI_HANDLER_BREAK; + } +} + +static bool ui_but_pie_menu_supported_apply(uiBut *but) +{ + return (!ELEM(but->type, NUMSLI, NUM)); +} + +static int ui_but_pie_menu_apply(bContext *C, uiPopupBlockHandle *menu, uiBut *but, bool force_close) +{ + int retval = WM_UI_HANDLER_BREAK; + + if (but && ui_but_pie_menu_supported_apply(but)) { + if (but->type == MENU) { + /* forcing the pie menu to close will not handle menus */ + if (!force_close) { + uiBut *active_but = ui_but_find_activated(menu->region); + + if (active_but) { + button_activate_exit(C, active_but, active_but->active, false, false); + } + + button_activate_init(C, menu->region, but, BUTTON_ACTIVATE_OPEN); + return retval; + } + else { + menu->menuretval = UI_RETURN_CANCEL; + } + } + else { + ui_apply_button(C, but->block, but, but->active, false); + button_activate_exit((bContext *)C, but, but->active, false, true); + + menu->menuretval = UI_RETURN_OK; + } + } + else { + menu->menuretval = UI_RETURN_CANCEL; + + ED_region_tag_redraw(menu->region); + } + + return retval; +} + +static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, RadialDirection dir) +{ + uiBut *but; + + if ((block->flag & UI_BLOCK_NUMSELECT) && event->val == KM_PRESS) { + for (but = block->buttons.first; but; but = but->next) { + if (but->pie_dir == dir && !ELEM(but->type, SEPR, SEPRLINE)) { + return but; + } + } + } + + return NULL; +} + +static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu) +{ + uiBut *active_but; + + if (but == NULL) return WM_UI_HANDLER_BREAK; + + active_but = ui_but_find_activated(menu->region); + + if (active_but) + button_activate_exit(C, active_but, active_but->active, false, false); + + button_activate_init(C, menu->region, but, BUTTON_ACTIVATE_OVER); + return ui_but_pie_menu_apply(C, menu, but, false); +} + +static int ui_handler_pie(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu) +{ + ARegion *ar; + uiBlock *block; + uiBut *but; + float event_xy[2]; + double duration; + bool is_click_style; + float dist; + + /* we block all events, this is modal interaction, except for drop events which is described below */ + int retval = WM_UI_HANDLER_BREAK; + + if (event->type == EVT_DROP) { + /* may want to leave this here for later if we support pie ovens */ + + retval = WM_UI_HANDLER_CONTINUE; + } + + ar = menu->region; + block = ar->uiblocks.first; + + is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE); + + if (menu->scrolltimer == NULL) { + menu->scrolltimer = + WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, PIE_MENU_INTERVAL); + menu->scrolltimer->duration = 0.0; + } + + duration = menu->scrolltimer->duration; + + event_xy[0] = event->x; + event_xy[1] = event->y; + + ui_window_to_block_fl(ar, block, &event_xy[0], &event_xy[1]); + + dist = ui_block_calculate_pie_segment(block, event_xy); + + if (event->type == TIMER) { + if (event->customdata == menu->scrolltimer) { + /* deactivate initial direction after a while */ + if (duration > 0.01 * U.pie_initial_timeout) { + block->pie_data.flags &= ~UI_PIE_INITIAL_DIRECTION; + } + + /* handle animation */ + if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) { + uiBut *but; + double final_time = 0.01 * U.pie_animation_timeout; + float fac = duration / final_time; + float pie_radius = U.pie_menu_radius * UI_DPI_FAC; + + if (fac > 1.0f) { + fac = 1.0f; + block->pie_data.flags |= UI_PIE_ANIMATION_FINISHED; + } + + for (but = block->buttons.first; but; but = but->next) { + if (but->pie_dir != UI_RADIAL_NONE) { + float vec[2]; + float center[2]; + + ui_but_pie_dir(but->pie_dir, vec); + + center[0] = (vec[0] > 0.01f) ? 0.5f : ((vec[0] < -0.01f) ? -0.5f : 0.0f); + center[1] = (vec[1] > 0.99f) ? 0.5f : ((vec[1] < -0.99f) ? -0.5f : 0.0f); + + center[0] *= BLI_rctf_size_x(&but->rect); + center[1] *= BLI_rctf_size_y(&but->rect); + + mul_v2_fl(vec, pie_radius); + add_v2_v2(vec, center); + mul_v2_fl(vec, fac); + add_v2_v2(vec, block->pie_data.pie_center_spawned); + + BLI_rctf_recenter(&but->rect, vec[0], vec[1]); + } + } + block->pie_data.alphafac = fac; + + ED_region_tag_redraw(ar); + } + } + + /* check pie velociy here if gesture has ended */ + if (block->pie_data.flags & UI_PIE_GESTURE_END_WAIT) { + float len_sq = 10; + + /* use a time threshold to ensure we leave time to the mouse to move */ + if (duration - block->pie_data.duration_gesture > 0.02) { + len_sq = len_squared_v2v2(event_xy, block->pie_data.last_pos); + copy_v2_v2(block->pie_data.last_pos, event_xy); + block->pie_data.duration_gesture = duration; + } + + if (len_sq < 1.0) { + uiBut *but = ui_but_find_activated(menu->region); + + if (but) { + return ui_but_pie_menu_apply(C, menu, but, true); + } + } + } + } + + if (event->type == block->pie_data.event && !is_click_style) { + if (event->val != KM_RELEASE) { + ui_handle_menu_button(C, event, menu); + + if (len_squared_v2v2(event_xy, block->pie_data.pie_center_init) > PIE_CLICK_THRESHOLD_SQ) { + block->pie_data.flags |= UI_PIE_DRAG_STYLE; + } + /* why redraw here? It's simple, we are getting many double click events here. + * Those operate like mouse move events almost */ + ED_region_tag_redraw(ar); + } + else { + /* distance from initial point */ + if (!(block->pie_data.flags & UI_PIE_DRAG_STYLE)) { + block->pie_data.flags |= UI_PIE_CLICK_STYLE; + } + else { + uiBut *but = ui_but_find_activated(menu->region); + + if (but && (U.pie_menu_confirm > 0) && + (dist >= U.pie_menu_threshold + U.pie_menu_confirm)) + { + if (but) + return ui_but_pie_menu_apply(C, menu, but, true); + } + + retval = ui_but_pie_menu_apply(C, menu, but, true); + + } + } + } + else { + /* direction from numpad */ + RadialDirection num_dir = UI_RADIAL_NONE; + + switch (event->type) { + case MOUSEMOVE: + if (!is_click_style) { + float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); + + /* here we use the initial position explicitly */ + if (len_sq > PIE_CLICK_THRESHOLD_SQ) { + block->pie_data.flags |= UI_PIE_DRAG_STYLE; + } + + /* here instead, we use the offset location to account for the initial direction timeout */ + if ((U.pie_menu_confirm > 0) && + (dist >= U.pie_menu_threshold + U.pie_menu_confirm)) + { + block->pie_data.flags |= UI_PIE_GESTURE_END_WAIT; + copy_v2_v2(block->pie_data.last_pos, event_xy); + block->pie_data.duration_gesture = duration; + } + } + + ui_handle_menu_button(C, event, menu); + + /* mouse move should always refresh the area for pie menus */ + ED_region_tag_redraw(ar); + break; + + case LEFTMOUSE: + if (is_click_style) { + if (block->pie_data.flags & UI_PIE_INVALID_DIR) { + menu->menuretval = UI_RETURN_CANCEL; + } + else { + retval = ui_handle_menu_button(C, event, menu); + } + } + break; + + case ESCKEY: + case RIGHTMOUSE: + menu->menuretval = UI_RETURN_CANCEL; + break; + + case AKEY: + case BKEY: + case CKEY: + case DKEY: + case EKEY: + case FKEY: + case GKEY: + case HKEY: + case IKEY: + case JKEY: + case KKEY: + case LKEY: + case MKEY: + case NKEY: + case OKEY: + case PKEY: + case QKEY: + case RKEY: + case SKEY: + case TKEY: + case UKEY: + case VKEY: + case WKEY: + case XKEY: + case YKEY: + case ZKEY: + { + if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && + (event->shift == 0) && + (event->ctrl == 0) && + (event->oskey == 0)) + { + for (but = block->buttons.first; but; but = but->next) { + if (but->menu_key == event->type) { + ui_but_pie_button_activate(C, but, menu); + } + } + } + break; + } + +#define CASE_NUM_TO_DIR(n, d) \ + case (ZEROKEY + n): case (PAD0 + n): \ + { if (num_dir == UI_RADIAL_NONE) num_dir = d; } (void)0 + + CASE_NUM_TO_DIR(1, UI_RADIAL_SW); + CASE_NUM_TO_DIR(2, UI_RADIAL_S); + CASE_NUM_TO_DIR(3, UI_RADIAL_SE); + CASE_NUM_TO_DIR(4, UI_RADIAL_W); + CASE_NUM_TO_DIR(6, UI_RADIAL_E); + CASE_NUM_TO_DIR(7, UI_RADIAL_NW); + CASE_NUM_TO_DIR(8, UI_RADIAL_N); + CASE_NUM_TO_DIR(9, UI_RADIAL_NE); + { + but = ui_block_pie_dir_activate(block, event, num_dir); + retval = ui_but_pie_button_activate(C, but, menu); + break; + } +#undef CASE_NUM_TO_DIR + default: + retval = ui_handle_menu_button(C, event, menu); + break; + } + } + + return retval; } static int ui_handle_menus_recursive( @@ -8226,17 +8873,21 @@ static int ui_handle_menus_recursive( uiBlock *block = menu->region->uiblocks.first; const bool is_menu = ui_block_is_menu(block); bool inside = false; + /* root pie menus accept the key that spawned them as double click to improve responsiveness */ + bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || event->type != block->pie_data.event); - if (is_parent_inside == false) { - int mx, my; + if (do_recursion) { + if (is_parent_inside == false) { + int mx, my; - mx = event->x; - my = event->y; - ui_window_to_block(menu->region, block, &mx, &my); - inside = BLI_rctf_isect_pt(&block->rect, mx, my); - } + mx = event->x; + my = event->y; + ui_window_to_block(menu->region, block, &mx, &my); + inside = BLI_rctf_isect_pt(&block->rect, mx, my); + } - retval = ui_handle_menus_recursive(C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false); + retval = ui_handle_menus_recursive(C, event, submenu, level + 1, is_parent_inside || inside, is_menu, false); + } } /* now handle events for our own menu */ @@ -8269,7 +8920,12 @@ static int ui_handle_menus_recursive( } } else { - retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating); + uiBlock *block = menu->region->uiblocks.first; + + if (block->flag & UI_BLOCK_RADIAL) + retval = ui_handler_pie(C, event, menu); + else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK) + retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating); } } @@ -8355,12 +9011,24 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE but = ui_but_find_activated(ar); if (but) { + uiBut *but_other; uiHandleButtonData *data; /* handle activated button events */ data = but->active; - if (data->state == BUTTON_STATE_MENU_OPEN) { + if ((data->state == BUTTON_STATE_MENU_OPEN) && + (but->type == PULLDOWN) && + (but_other = ui_but_find_mouse_over(ar, event)) && + (but != but_other) && + (but->type == but_other->type)) + { + /* if mouse moves to a different root-level menu button, + * open it to replace the current menu */ + ui_handle_button_activate(C, ar, but_other, BUTTON_ACTIVATE_OVER); + button_activate_state(C, but_other, BUTTON_STATE_MENU_OPEN); + } + else if (data->state == BUTTON_STATE_MENU_OPEN) { int retval; /* handle events for menus and their buttons recursively, @@ -8399,9 +9067,13 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata) { uiPopupBlockHandle *menu = userdata; - + struct ARegion *menu_region; /* we block all events, this is modal interaction, except for drop events which is described below */ int retval = WM_UI_HANDLER_BREAK; + bool reset_pie = false; + + menu_region = CTX_wm_menu(C); + CTX_wm_menu_set(C, menu->region); if (event->type == EVT_DROP) { /* if we're handling drop event we'll want it to be handled by popup callee as well, @@ -8416,15 +9088,22 @@ static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata) /* free if done, does not free handle itself */ if (menu->menuretval) { + wmWindow *win = CTX_wm_window(C); /* copy values, we have to free first (closes region) */ uiPopupBlockHandle temp = *menu; + uiBlock *block = menu->region->uiblocks.first; + + /* set last pie event to allow chained pie spawning */ + if (block->flag & UI_BLOCK_RADIAL) { + win->last_pie_event = block->pie_data.event; + reset_pie = true; + } ui_popup_block_free(C, menu); - UI_remove_popup_handlers(&CTX_wm_window(C)->modalhandlers, menu); + UI_remove_popup_handlers(&win->modalhandlers, menu); #ifdef USE_DRAG_TOGGLE { - wmWindow *win = CTX_wm_window(C); WM_event_free_ui_handler_all(C, &win->modalhandlers, ui_handler_region_drag_toggle, ui_handler_region_drag_toggle_remove); } @@ -8434,7 +9113,7 @@ static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata) if (temp.popup_func) temp.popup_func(C, temp.popup_arg, temp.retvalue); if (temp.optype) - WM_operator_name_call(C, temp.optype->idname, temp.opcontext, NULL); + WM_operator_name_call_ptr(C, temp.optype, temp.opcontext, NULL); } else if (temp.cancel_func) temp.cancel_func(C, temp.popup_arg); @@ -8450,6 +9129,16 @@ static int ui_handler_popup(bContext *C, const wmEvent *event, void *userdata) /* delayed apply callbacks */ ui_apply_but_funcs_after(C); + if (reset_pie) { + /* reaqcuire window in case pie invalidates it somehow */ + wmWindow *win = CTX_wm_window(C); + + if (win) + win->last_pie_event = EVENT_NONE; + } + + CTX_wm_region_set(C, menu_region); + return retval; } @@ -8467,12 +9156,12 @@ static void ui_handler_remove_popup(bContext *C, void *userdata) void UI_add_region_handlers(ListBase *handlers) { WM_event_remove_ui_handler(handlers, ui_handler_region, ui_handler_remove_region, NULL, false); - WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL); + WM_event_add_ui_handler(NULL, handlers, ui_handler_region, ui_handler_remove_region, NULL, false); } -void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup) +void UI_add_popup_handlers(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click) { - WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, popup); + WM_event_add_ui_handler(C, handlers, ui_handler_popup, ui_handler_remove_popup, popup, accept_dbl_click); } void UI_remove_popup_handlers(ListBase *handlers, uiPopupBlockHandle *popup) diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 546b2b85af5..51dd9166e46 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -508,6 +508,8 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_SOFTEN, soften); INIT_BRUSH_ICON(ICON_BRUSH_SUBTRACT, subtract); INIT_BRUSH_ICON(ICON_BRUSH_TEXDRAW, texdraw); + INIT_BRUSH_ICON(ICON_BRUSH_TEXFILL, texfill); + INIT_BRUSH_ICON(ICON_BRUSH_TEXMASK, texmask); INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb); INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 3fe6452e56b..47d0e29061c 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -88,6 +88,7 @@ typedef enum { UI_WTYPE_PULLDOWN, UI_WTYPE_MENU_ITEM, + UI_WTYPE_MENU_ITEM_RADIAL, UI_WTYPE_MENU_BACK, /* specials */ @@ -121,6 +122,23 @@ enum { /* warn: rest of uiBut->flag in UI_interface.h */ }; +/* but->pie_dir */ +typedef enum RadialDirection { + UI_RADIAL_NONE = -1, + UI_RADIAL_N = 0, + UI_RADIAL_NE = 1, + UI_RADIAL_E = 2, + UI_RADIAL_SE = 3, + UI_RADIAL_S = 4, + UI_RADIAL_SW = 5, + UI_RADIAL_W = 6, + UI_RADIAL_NW = 7, +} RadialDirection; + +extern const char ui_radial_dir_order[8]; +extern const char ui_radial_dir_to_numpad[8]; +extern const short ui_radial_dir_to_angle[8]; + /* internal panel drawing defines */ #define PNL_GRID (UI_UNIT_Y / 5) /* 4 default */ #define PNL_HEADER (UI_UNIT_Y + 4) /* 24 default */ @@ -144,6 +162,19 @@ enum { /* split numbuts by ':' and align l/r */ #define USE_NUMBUTS_LR_ALIGN +/* PieMenuData->flags */ +enum { + UI_PIE_DEGREES_RANGE_LARGE = (1 << 0), /* pie menu item collision is detected at 90 degrees */ + UI_PIE_INITIAL_DIRECTION = (1 << 1), /* use initial center of pie menu to calculate direction */ + UI_PIE_DRAG_STYLE = (1 << 2), /* pie menu is drag style */ + UI_PIE_INVALID_DIR = (1 << 3), /* mouse not far enough from center position */ + UI_PIE_CLICK_STYLE = (1 << 4), /* pie menu changed to click style, click to confirm */ + UI_PIE_ANIMATION_FINISHED = (1 << 5), /* pie animation finished, do not calculate any more motion */ + UI_PIE_GESTURE_END_WAIT = (1 << 6), /* pie gesture selection has been done, now wait for mouse motion to end */ +}; + +#define PIE_CLICK_THRESHOLD_SQ 50.0f + typedef struct uiLinkLine { /* only for draw/edit */ struct uiLinkLine *next, *prev; struct uiBut *from, *to; @@ -186,6 +217,7 @@ struct uiBut { * (type == LABEL), Use (a1 == 1.0f) to use a2 as a blending factor (wow, this is imaginative!). * (type == SCROLL) Use as scroll size. * (type == SEARCH_MENU) Use as number or rows. + * (type == COLOR) Use as indication of color palette */ float a1; @@ -193,6 +225,7 @@ struct uiBut { * (type == NUM), Use to store RNA 'precision' value, for dragging and click-step. * (type == LABEL), If (a1 == 1.0f) use a2 as a blending factor. * (type == SEARCH_MENU) Use as number or columns. + * (type == COLOR) Use as index in palette (not so good, needs refactor) */ float a2; @@ -225,6 +258,7 @@ struct uiBut { BIFIconID icon; bool lock; char dt; /* drawtype: UI_EMBOSS, UI_EMBOSSN ... etc, copied from the block */ + signed char pie_dir; /* direction in a pie menu, used for collision detection (RadialDirection) */ char changed; /* could be made into a single flag */ unsigned char unit_type; /* so buttons can support unit systems which are not RNA */ short modifier_key; @@ -272,6 +306,17 @@ struct uiBut { uiBlock *block; }; +struct PieMenuData { + float pie_dir[2]; + float pie_center_init[2]; + float pie_center_spawned[2]; + float last_pos[2]; + double duration_gesture; + int flags; + int event; /* initial event used to fire the pie menu, store here so we can query for release */ + float alphafac; +}; + struct uiBlock { uiBlock *next, *prev; @@ -354,6 +399,7 @@ struct uiBlock { char display_device[64]; /* display device name used to display this block, * used by color widgets to transform colors from/to scene linear */ + struct PieMenuData pie_data; }; typedef struct uiSafetyRct { @@ -369,6 +415,7 @@ extern void ui_delete_linkline(uiLinkLine *line, uiBut *but); void ui_fontscale(short *points, float aspect); extern bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; +extern bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; extern void ui_block_to_window_fl(const struct ARegion *ar, uiBlock *block, float *x, float *y); extern void ui_block_to_window(const struct ARegion *ar, uiBlock *block, int *x, int *y); extern void ui_block_to_window_rctf(const struct ARegion *ar, uiBlock *block, rctf *rct_dst, const rctf *rct_src); @@ -428,6 +475,19 @@ struct uiKeyNavLock { int event_xy[2]; }; +typedef uiBlock * (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1); + +struct uiPopupBlockCreate { + uiBlockCreateFunc create_func; + uiBlockHandleCreateFunc handle_create_func; + void *arg; + + int event_xy[2]; + + /* when popup is initialized from a button */ + ARegion *butregion; +}; + struct uiPopupBlockHandle { /* internal */ struct ARegion *region; @@ -442,6 +502,9 @@ struct uiPopupBlockHandle { void (*cancel_func)(struct bContext *C, void *arg); void *popup_arg; + /* store data for refreshing popups */ + struct uiPopupBlockCreate popup_create_vars; + struct wmTimer *scrolltimer; struct uiKeyNavLock keynav_state; @@ -455,7 +518,7 @@ struct uiPopupBlockHandle { /* return values */ int butretval; int menuretval; - float retvalue; + int retvalue; float retvec[4]; /* menu direction */ @@ -496,7 +559,8 @@ bool ui_searchbox_apply(uiBut *but, struct ARegion *ar); void ui_searchbox_free(struct bContext *C, struct ARegion *ar); void ui_but_search_test(uiBut *but); -typedef uiBlock * (*uiBlockHandleCreateFunc)(struct bContext *C, struct uiPopupBlockHandle *handle, void *arg1); +uiBlock *ui_popup_block_refresh(struct bContext *C, uiPopupBlockHandle *handle, + ARegion *butregion, uiBut *but); uiPopupBlockHandle *ui_popup_block_create(struct bContext *C, struct ARegion *butregion, uiBut *but, uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, @@ -533,12 +597,19 @@ void ui_draw_but_NODESOCKET(ARegion *ar, uiBut *but, struct uiWidgetColors *wcol PointerRNA *ui_handle_afterfunc_add_operator(struct wmOperatorType *ot, int opcontext, bool create_props); extern void ui_pan_to_scroll(const struct wmEvent *event, int *type, int *val); extern void ui_button_activate_do(struct bContext *C, struct ARegion *ar, uiBut *but); +extern void ui_button_activate_over(struct bContext *C, struct ARegion *ar, uiBut *but); extern void ui_button_execute_begin(struct bContext *C, struct ARegion *ar, uiBut *but, void **active_back); extern void ui_button_execute_end(struct bContext *C, struct ARegion *ar, uiBut *but, void *active_back); extern void ui_button_active_free(const struct bContext *C, uiBut *but); extern bool ui_button_is_active(struct ARegion *ar) ATTR_WARN_UNUSED_RESULT; extern int ui_button_open_menu_direction(uiBut *but); extern void ui_button_text_password_hide(char password_str[UI_MAX_DRAW_STR], uiBut *but, const bool restore); +extern uiBut *ui_but_find_activated(struct ARegion *ar); +bool ui_but_is_editable(const uiBut *but); +void ui_but_pie_dir_visual(RadialDirection dir, float vec[2]); +void ui_but_pie_dir(RadialDirection dir, float vec[2]); +float ui_block_calculate_pie_segment(struct uiBlock *block, const float event_xy[2]); + void ui_button_clipboard_free(void); void ui_panel_menu(struct bContext *C, ARegion *ar, Panel *pa); uiBut *ui_but_find_old(uiBlock *block_old, const uiBut *but_new); @@ -548,6 +619,7 @@ uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new); void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3); void ui_draw_anti_roundbox(int mode, float minx, float miny, float maxx, float maxy, float rad, bool use_alpha); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); +void ui_draw_pie_center(uiBlock *block); uiWidgetColors *ui_tooltip_get_theme(void); void ui_draw_tooltip_background(uiStyle *UNUSED(style), uiBlock *block, rcti *rect); void ui_draw_search_back(struct uiStyle *style, uiBlock *block, rcti *rect); @@ -572,7 +644,6 @@ int ui_id_icon_get(struct bContext *C, struct ID *id, const bool big); /* resources.c */ void init_userdef_do_versions(void); -void init_userdef_factory(void); void ui_theme_init_default(void); void ui_style_init_default(void); void ui_resources_init(void); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 0bc679dede0..c2bd6d307d1 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -106,6 +106,7 @@ typedef enum uiItemType { ITEM_LAYOUT_ABSOLUTE, ITEM_LAYOUT_SPLIT, ITEM_LAYOUT_OVERLAP, + ITEM_LAYOUT_RADIAL, ITEM_LAYOUT_ROOT #if 0 @@ -218,7 +219,9 @@ static int ui_item_fit(int item, int pos, int all, int available, int last, int static int ui_layout_vary_direction(uiLayout *layout) { - return (layout->root->type == UI_LAYOUT_HEADER || layout->alignment != UI_LAYOUT_ALIGN_EXPAND) ? UI_ITEM_VARY_X : UI_ITEM_VARY_Y; + return ((ELEM(layout->root->type, UI_LAYOUT_HEADER, UI_LAYOUT_PIEMENU) || + (layout->alignment != UI_LAYOUT_ALIGN_EXPAND)) ? + UI_ITEM_VARY_X : UI_ITEM_VARY_Y); } /* estimated size of text + icon */ @@ -553,15 +556,24 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt */ uiBut *but; + uiLayout *layout_radial = NULL; EnumPropertyItem *item, *item_array; const char *name; int itemw, icon, value; bool free; + bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); - RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free); + if (radial) + RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free); + else + RNA_property_enum_items_gettexted(block->evil_C, ptr, prop, &item_array, NULL, &free); /* we dont want nested rows, cols in menus */ - if (layout->root->type != UI_LAYOUT_MENU) { + if (radial) { + layout_radial = uiLayoutRadial(layout); + uiBlockSetCurLayout(block, layout_radial); + } + else if (layout->root->type != UI_LAYOUT_MENU) { uiBlockSetCurLayout(block, ui_item_local_sublayout(layout, layout, 1)); } else { @@ -569,8 +581,11 @@ static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *pt } for (item = item_array; item->identifier; item++) { - if (!item->identifier[0]) + if (!item->identifier[0]) { + if (radial) + uiItemS(layout_radial); continue; + } name = (!uiname || uiname[0]) ? item->name : ""; icon = item->icon; @@ -869,6 +884,8 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname PointerRNA ptr; PropertyRNA *prop; uiBlock *block = layout->root->block; + const bool radial = (layout->item.type == ITEM_LAYOUT_RADIAL) || + ((layout->item.type == ITEM_LAYOUT_ROOT) && (layout->root->type == UI_LAYOUT_PIEMENU)); if (!ot || !ot->srna) { ui_item_disabled(layout, opname); @@ -887,10 +904,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname if (prop && RNA_property_type(prop) == PROP_ENUM) { EnumPropertyItem *item, *item_array = NULL; bool free; - uiLayout *split = uiLayoutSplit(layout, 0.0f, false); - uiLayout *column = uiLayoutColumn(split, layout->align); + uiLayout *split; + uiLayout *target; + + if (radial) { + target = uiLayoutRadial(layout); + } + else { + split = uiLayoutSplit(layout, 0.0f, false); + target = uiLayoutColumn(split, layout->align); + } + + if (radial) { + RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, NULL, &free); + } + else { + RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free); + } - RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, NULL, &free); for (item = item_array; item->identifier; item++) { if (item->identifier[0]) { PointerRNA tptr; @@ -905,20 +936,24 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname } RNA_property_enum_set(&tptr, prop, item->value); - uiItemFullO_ptr(column, ot, item->name, item->icon, tptr.data, context, flag); + uiItemFullO_ptr(target, ot, item->name, item->icon, tptr.data, context, flag); + ui_but_tip_from_enum_item(block->buttons.last, item); } else { if (item->name) { uiBut *but; - if (item != item_array) { - column = uiLayoutColumn(split, layout->align); + + if (item != item_array && !radial) { + target = uiLayoutColumn(split, layout->align); + /* inconsistent, but menus with labels do not look good flipped */ block->flag |= UI_BLOCK_NO_FLIP; } - if (item->icon) { - uiItemL(column, item->name, item->icon); + if (item->icon || radial) { + uiItemL(target, item->name, item->icon); + but = block->buttons.last; } else { @@ -928,8 +963,14 @@ void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname } ui_but_tip_from_enum_item(but, item); } - else { /* XXX bug here, colums draw bottom item badly */ - uiItemS(column); + else { + if (radial) { + uiItemS(target); + } + else { + /* XXX bug here, colums draw bottom item badly */ + uiItemS(target); + } } } } @@ -1181,7 +1222,7 @@ void uiItemFullR(uiLayout *layout, PointerRNA *ptr, PropertyRNA *prop, int index if (flag & UI_ITEM_R_ICON_ONLY) { /* pass */ } - else if (ELEM4(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) { + else if (ELEM(type, PROP_INT, PROP_FLOAT, PROP_STRING, PROP_POINTER)) { name = ui_item_name_add_colon(name, namestr); } else if (type == PROP_BOOLEAN && is_array && index == RNA_NO_INDEX) { @@ -1323,9 +1364,9 @@ void uiItemEnumR_string(uiLayout *layout, struct PointerRNA *ptr, const char *pr for (a = 0; item[a].identifier; a++) { if (item[a].value == ivalue) { - const char *item_name = CTX_IFACE_(RNA_property_translation_context(prop), item[a].name); + const char *item_name = name ? name : CTX_IFACE_(RNA_property_translation_context(prop), item[a].name); - uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, ivalue, 0, item_name ? item_name : name, icon ? icon : item[a].icon); + uiItemFullR(layout, ptr, prop, RNA_ENUM_VALUE, ivalue, 0, item_name, icon ? icon : item[a].icon); break; } } @@ -1401,7 +1442,7 @@ typedef struct CollItemSearch { int iconid; } CollItemSearch; -static int sort_search_items_list(void *a, void *b) +static int sort_search_items_list(const void *a, const void *b) { CollItemSearch *cis1 = (CollItemSearch *)a; CollItemSearch *cis2 = (CollItemSearch *)b; @@ -1559,7 +1600,7 @@ void uiItemPointerR(uiLayout *layout, struct PointerRNA *ptr, const char *propna } type = RNA_property_type(prop); - if (!ELEM3(type, PROP_POINTER, PROP_STRING, PROP_ENUM)) { + if (!ELEM(type, PROP_POINTER, PROP_STRING, PROP_ENUM)) { RNA_warning("Property %s must be a pointer, string or enum", propname); return; } @@ -1759,7 +1800,7 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval) { /* label */ uiBlock *block = layout->root->block; - float *retvalue = (block->handle) ? &block->handle->retvalue : NULL; + int *retvalue = (block->handle) ? &block->handle->retvalue : NULL; int w; uiBlockSetCurLayout(block, layout); @@ -1772,11 +1813,11 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval) w = ui_text_icon_width(layout, name, icon, 0); if (icon && name[0]) - uiDefIconTextButF(block, BUT, argval, icon, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); + uiDefIconTextButI(block, BUT, argval, icon, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); else if (icon) - uiDefIconButF(block, BUT, argval, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); + uiDefIconButI(block, BUT, argval, icon, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); else - uiDefButF(block, BUT, argval, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); + uiDefButI(block, BUT, argval, name, 0, 0, w, UI_UNIT_Y, retvalue, 0.0, 0.0, 0, -1, ""); } /* separator item */ @@ -2072,16 +2113,135 @@ static void ui_litem_layout_column(uiLayout *litem) litem->y = y; } +/* calculates the angle of a specified button in a radial menu, + * stores a float vector in unit circle */ +static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum) +{ + RadialDirection dir; + BLI_assert(itemnum < 8); + + dir = ui_radial_dir_order[itemnum]; + ui_but_pie_dir(dir, vec); + + return dir; +} + +static bool ui_item_is_radial_displayable(uiItem *item) +{ + + if ((item->type == ITEM_BUTTON) && (((uiButtonItem *)item)->but->type == LABEL)) + return false; + + return true; +} + +static bool ui_item_is_radial_drawable(uiButtonItem *bitem) +{ + + if (ELEM(bitem->but->type, SEPR, SEPRLINE)) + return false; + + return true; +} + +static void ui_litem_layout_radial(uiLayout *litem) +{ + uiItem *item; + int itemh, itemw, x, y; + int itemnum = 0; + int totitems = 0; + + int minx, miny, maxx, maxy; + /* For the radial layout we will use Matt Ebb's design + * for radiation, see http://mattebb.com/weblog/radiation/ + * also the old code at http://developer.blender.org/T5103 + */ + + int pie_radius = U.pie_menu_radius * UI_DPI_FAC; + + x = litem->x; + y = litem->y; + + minx = x, miny = y, maxx = x, maxy = y; + + /* first count total items */ + for (item = litem->items.first; item; item = item->next) + totitems++; + + if (totitems < 5) + litem->root->block->pie_data.flags |= UI_PIE_DEGREES_RANGE_LARGE; + + for (item = litem->items.first; item; item = item->next) { + /* not all button types are drawn in a radial menu, do filtering here */ + if (ui_item_is_radial_displayable(item)) { + RadialDirection dir; + float vec[2]; + float factor[2]; + + dir = ui_get_radialbut_vec(vec, itemnum); + factor[0] = (vec[0] > 0.01f) ? 0.0f : ((vec[0] < -0.01f) ? -1.0f : -0.5f); + factor[1] = (vec[1] > 0.99f) ? 0.0f : ((vec[1] < -0.99f) ? -1.0f : -0.5f); + + itemnum++; + + if (item->type == ITEM_BUTTON) { + uiButtonItem *bitem = (uiButtonItem *) item; + + bitem->but->pie_dir = dir; + /* scale the buttons */ + bitem->but->rect.ymax *= 1.5f; + /* add a little bit more here to include number */ + bitem->but->rect.xmax += 1.5f * UI_UNIT_X; + /* enable drawing as pie item if supported by widget */ + if (ui_item_is_radial_drawable(bitem)) + bitem->but->dt = UI_EMBOSSR; + } + + ui_item_size(item, &itemw, &itemh); + + ui_item_position(item, x + vec[0] * pie_radius + factor[0] * itemw, y + vec[1] * pie_radius + factor[1] * itemh, itemw, itemh); + + minx = min_ii(minx, x + vec[0] * pie_radius - itemw / 2); + maxx = max_ii(maxx, x + vec[0] * pie_radius + itemw / 2); + miny = min_ii(miny, y + vec[1] * pie_radius - itemh / 2); + maxy = max_ii(maxy, y + vec[1] * pie_radius + itemh / 2); + } + } + + litem->x = minx; + litem->y = miny; + litem->w = maxx - minx; + litem->h = maxy - miny; +} + /* root layout */ static void ui_litem_estimate_root(uiLayout *UNUSED(litem)) { /* nothing to do */ } +static void ui_litem_layout_root_radial(uiLayout *litem) +{ + /* first item is pie menu title, align on center of menu */ + uiItem *item = litem->items.first; + + if (item->type == ITEM_BUTTON) { + int itemh, itemw, x, y; + x = litem->x; + y = litem->y; + + ui_item_size(item, &itemw, &itemh); + + ui_item_position(item, x - itemw / 2, y + U.pixelsize * (U.pie_menu_threshold + 9.0f), itemw, itemh); + } +} + static void ui_litem_layout_root(uiLayout *litem) { if (litem->root->type == UI_LAYOUT_HEADER) ui_litem_layout_row(litem); + else if (litem->root->type == UI_LAYOUT_PIEMENU) + ui_litem_layout_root_radial(litem); else ui_litem_layout_column(litem); } @@ -2497,6 +2657,40 @@ static uiLayoutItemBx *ui_layout_box(uiLayout *layout, int type) return box; } +uiLayout *uiLayoutRadial(uiLayout *layout) +{ + uiLayout *litem; + uiItem *item; + + /* radial layouts are only valid for radial menus */ + if (layout->root->type != UI_LAYOUT_PIEMENU) + return ui_item_local_sublayout(layout, layout, 0); + + /* only one radial wheel per root layout is allowed, so check and return that, if it exists */ + for (item = layout->root->layout->items.first; item; item = item->next) { + litem = (uiLayout *)item; + if (litem->item.type == ITEM_LAYOUT_RADIAL) { + uiBlockSetCurLayout(layout->root->block, litem); + return litem; + } + } + + litem = MEM_callocN(sizeof(uiLayout), "uiLayoutRadial"); + litem->item.type = ITEM_LAYOUT_RADIAL; + litem->root = layout->root; + litem->active = true; + litem->enabled = true; + litem->context = layout->context; + litem->redalert = layout->redalert; + litem->w = layout->w; + BLI_addtail(&layout->root->layout->items, litem); + + uiBlockSetCurLayout(layout->root->block, litem); + + return litem; +} + + uiLayout *uiLayoutBox(uiLayout *layout) { return (uiLayout *)ui_layout_box(layout, ROUNDBOX); @@ -2843,6 +3037,9 @@ static void ui_item_layout(uiItem *item) case ITEM_LAYOUT_OVERLAP: ui_litem_layout_overlap(litem); break; + case ITEM_LAYOUT_RADIAL: + ui_litem_layout_radial(litem); + break; default: break; } @@ -2916,7 +3113,7 @@ uiLayout *uiBlockLayout(uiBlock *block, int dir, int type, int x, int y, int siz layout->enabled = 1; layout->context = NULL; - if (type == UI_LAYOUT_MENU) + if (type == UI_LAYOUT_MENU || type == UI_LAYOUT_PIEMENU) layout->space = 0; if (dir == UI_LAYOUT_HORIZONTAL) { @@ -2983,6 +3180,8 @@ void uiBlockLayoutResolve(uiBlock *block, int *x, int *y) { uiLayoutRoot *root; + BLI_assert(block->active); + if (x) *x = 0; if (y) *y = 0; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 316a4d34881..817445cc14e 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -35,6 +35,7 @@ #include "DNA_text_types.h" /* for UI_OT_reports_to_text */ #include "BLI_blenlib.h" +#include "BLI_math_color.h" #include "BLF_api.h" #include "BLF_translation.h" @@ -44,6 +45,7 @@ #include "BKE_global.h" #include "BKE_text.h" /* for UI_OT_reports_to_text */ #include "BKE_report.h" +#include "BKE_paint.h" #include "RNA_access.h" #include "RNA_define.h" @@ -55,6 +57,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_paint.h" + /* only for UI_OT_editsource */ #include "ED_screen.h" #include "BKE_main.h" @@ -258,28 +262,43 @@ static void UI_OT_unset_property_button(wmOperatorType *ot) /* Copy To Selected Operator ------------------------ */ -static bool copy_to_selected_list(bContext *C, PointerRNA *ptr, ListBase *lb, bool *use_path) +static bool copy_to_selected_list( + bContext *C, PointerRNA *ptr, PropertyRNA *prop, + ListBase *r_lb, bool *r_use_path_from_id, char **r_path) { - *use_path = false; + *r_use_path_from_id = false; + *r_path = NULL; - if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) - *lb = CTX_data_collection_get(C, "selected_editable_bones"); - else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) - *lb = CTX_data_collection_get(C, "selected_pose_bones"); - else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) - *lb = CTX_data_collection_get(C, "selected_editable_sequences"); - else { + if (RNA_struct_is_a(ptr->type, &RNA_EditBone)) { + *r_lb = CTX_data_collection_get(C, "selected_editable_bones"); + } + else if (RNA_struct_is_a(ptr->type, &RNA_PoseBone)) { + *r_lb = CTX_data_collection_get(C, "selected_pose_bones"); + } + else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) { + *r_lb = CTX_data_collection_get(C, "selected_editable_sequences"); + } + else if (ptr->id.data) { ID *id = ptr->id.data; - if (id && GS(id->name) == ID_OB) { - *lb = CTX_data_collection_get(C, "selected_editable_objects"); - *use_path = true; + if (GS(id->name) == ID_OB) { + *r_lb = CTX_data_collection_get(C, "selected_editable_objects"); + *r_use_path_from_id = true; + *r_path = RNA_path_from_ID_to_property(ptr, prop); } - else { - return false; + else if (GS(id->name) == ID_SCE) { + /* Sequencer's ID is scene :/ */ + /* Try to recursively find an RNA_Sequence ancestor, to handle situations like T41062... */ + if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) { + *r_lb = CTX_data_collection_get(C, "selected_editable_sequences"); + } } + return (*r_path != NULL); } - + else { + return false; + } + return true; } @@ -303,47 +322,54 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) /* if there is a valid property that is editable... */ if (ptr.data && prop) { char *path = NULL; - bool use_path; + bool use_path_from_id; CollectionPointerLink *link; ListBase lb; - if (!copy_to_selected_list(C, &ptr, &lb, &use_path)) + if (!copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) return success; - if (!use_path || (path = RNA_path_from_ID_to_property(&ptr, prop))) { - for (link = lb.first; link; link = link->next) { - if (link->ptr.data != ptr.data) { - if (use_path) { - lprop = NULL; - RNA_id_pointer_create(link->ptr.id.data, &idptr); - RNA_path_resolve_property(&idptr, path, &lptr, &lprop); - } - else { - lptr = link->ptr; - lprop = prop; - } + for (link = lb.first; link; link = link->next) { + if (link->ptr.data != ptr.data) { + if (use_path_from_id) { + /* Path relative to ID. */ + lprop = NULL; + RNA_id_pointer_create(link->ptr.id.data, &idptr); + RNA_path_resolve_property(&idptr, path, &lptr, &lprop); + } + else if (path) { + /* Path relative to elements from list. */ + lprop = NULL; + RNA_path_resolve_property(&link->ptr, path, &lptr, &lprop); + } + else { + lptr = link->ptr; + lprop = prop; + } - if (lprop == prop) { - if (RNA_property_editable(&lptr, lprop)) { - if (poll) { + if (lptr.data == ptr.data) { + /* lptr might not be the same as link->ptr! */ + continue; + } + + if (lprop == prop) { + if (RNA_property_editable(&lptr, lprop)) { + if (poll) { + success = true; + break; + } + else { + if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) { + RNA_property_update(C, &lptr, prop); success = true; - break; - } - else { - if (RNA_property_copy(&lptr, &ptr, prop, (all) ? -1 : index)) { - RNA_property_update(C, &lptr, prop); - success = true; - } } } } } } - - if (path) - MEM_freeN(path); } + MEM_SAFE_FREE(path); BLI_freelistN(&lb); } @@ -693,6 +719,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) int ret = OPERATOR_CANCELLED; if (but) { + wmOperatorType *ot; PointerRNA ptr; char popath[FILE_MAX]; const char *root = U.i18ndir; @@ -714,7 +741,8 @@ static int edittranslation_exec(bContext *C, wmOperator *op) "Directory' path to a valid directory"); return OPERATOR_CANCELLED; } - if (!WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0)) { + ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); + if (ot == NULL) { BKE_reportf(op->reports, RPT_ERROR, "Could not find operator '%s'! Please enable ui_translate addon " "in the User Preferences", EDTSRC_I18N_OP_NAME); return OPERATOR_CANCELLED; @@ -730,7 +758,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) uiButGetStrInfo(C, but, &but_label, &rna_label, &enum_label, &but_tip, &rna_tip, &enum_tip, &rna_struct, &rna_prop, &rna_enum, &rna_ctxt, NULL); - WM_operator_properties_create(&ptr, EDTSRC_I18N_OP_NAME); + WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "lang", uilng); RNA_string_set(&ptr, "po_file", popath); RNA_string_set(&ptr, "but_label", but_label.strinfo); @@ -743,7 +771,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - ret = WM_operator_name_call(C, EDTSRC_I18N_OP_NAME, WM_OP_INVOKE_DEFAULT, &ptr); + ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); /* Clean up */ if (but_label.strinfo) @@ -808,6 +836,99 @@ static void UI_OT_reloadtranslation(wmOperatorType *ot) ot->exec = reloadtranslation_exec; } +int UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + /* should only return true for regions that include buttons, for now + * return true always */ + if (drag->type == WM_DRAG_COLOR) { + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *ar = CTX_wm_region(C); + + if (UI_but_active_drop_color(C)) + return 1; + + if (sima && (sima->mode == SI_MODE_PAINT) && + sima->image && (ar && ar->regiontype == RGN_TYPE_WINDOW)) + { + return 1; + } + } + + return 0; +} + +void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop) +{ + uiDragColorHandle *drag_info = (uiDragColorHandle *)drag->poin; + + RNA_float_set_array(drop->ptr, "color", drag_info->color); + RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected); +} + +static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + ARegion *ar = CTX_wm_region(C); + uiBut *but = NULL; + float color[4]; + bool gamma; + + RNA_float_get_array(op->ptr, "color", color); + gamma = RNA_boolean_get(op->ptr, "gamma"); + + /* find button under mouse, check if it has RNA color property and + * if it does copy the data */ + but = ui_but_find_activated(ar); + + if (but && but->type == COLOR && but->rnaprop) { + const int color_len = RNA_property_array_length(&but->rnapoin, but->rnaprop); + BLI_assert(color_len <= 4); + + /* keep alpha channel as-is */ + if (color_len == 4) { + color[3] = RNA_property_float_get_index(&but->rnapoin, but->rnaprop, 3); + } + + if (RNA_property_subtype(but->rnaprop) == PROP_COLOR_GAMMA) { + if (!gamma) + ui_block_to_display_space_v3(but->block, color); + RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); + RNA_property_update(C, &but->rnapoin, but->rnaprop); + } + else if (RNA_property_subtype(but->rnaprop) == PROP_COLOR) { + if (gamma) + ui_block_to_scene_linear_v3(but->block, color); + RNA_property_float_set_array(&but->rnapoin, but->rnaprop, color); + RNA_property_update(C, &but->rnapoin, but->rnaprop); + } + } + else { + if (gamma) { + srgb_to_linearrgb_v3_v3(color, color); + } + + ED_imapaint_bucket_fill(C, color, op); + } + + ED_region_tag_redraw(ar); + + return OPERATOR_FINISHED; +} + + +static void UI_OT_drop_color(wmOperatorType *ot) +{ + ot->name = "Drop Color"; + ot->idname = "UI_OT_drop_color"; + ot->description = "Drop colors to buttons"; + + ot->invoke = drop_color_invoke; + + RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0); + RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected "); +} + + + /* ********************************************************* */ /* Registration */ @@ -819,7 +940,7 @@ void UI_buttons_operatortypes(void) WM_operatortype_append(UI_OT_unset_property_button); WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_reports_to_textblock); /* XXX: temp? */ - + WM_operatortype_append(UI_OT_drop_color); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 2ccb3740777..9265ca0d4b9 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -120,7 +120,7 @@ static int panel_aligned(ScrArea *sa, ARegion *ar) return BUT_VERTICAL; else if (sa->spacetype == SPACE_IMAGE && ar->regiontype == RGN_TYPE_PREVIEW) return BUT_VERTICAL; - else if (ELEM3(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) + else if (ELEM(ar->regiontype, RGN_TYPE_UI, RGN_TYPE_TOOLS, RGN_TYPE_TOOL_PROPS)) return BUT_VERTICAL; return 0; @@ -201,20 +201,33 @@ static void ui_panel_copy_offset(Panel *pa, Panel *papar) pa->ofsy = papar->ofsy + papar->sizey - pa->sizey; } + +/* XXX Disabled paneltab handling for now. Old 2.4x feature, *DO NOT* confuse it with new tool tabs in 2.70. ;) + * See also T41704. + */ +/* #define UI_USE_PANELTAB */ + Panel *uiPanelFindByType(ARegion *ar, PanelType *pt) { Panel *pa; - const char *idname = pt->idname; - const char *tabname = pt->idname; +#ifdef UI_USE_PANELTAB + const char *tabname = pt->idname; for (pa = ar->panels.first; pa; pa = pa->next) { if (STREQLEN(pa->panelname, idname, sizeof(pa->panelname))) { - if (STREQLEN(pa->tabname, tabname, sizeof(pa->panelname))) { + if (STREQLEN(pa->tabname, tabname, sizeof(pa->tabname))) { return pa; } } } +#else + for (pa = ar->panels.first; pa; pa = pa->next) { + if (STREQLEN(pa->panelname, idname, sizeof(pa->panelname))) { + return pa; + } + } +#endif return NULL; } @@ -224,11 +237,13 @@ Panel *uiPanelFindByType(ARegion *ar, PanelType *pt) */ Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Panel *pa, bool *r_open) { - Panel *patab, *palast, *panext; + Panel *palast, *panext; const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); const char *idname = pt->idname; +#ifdef UI_USE_PANELTAB const char *tabname = pt->idname; const char *hookname = NULL; +#endif const bool newpanel = (pa == NULL); int align = panel_aligned(sa, ar); @@ -240,7 +255,6 @@ Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Pan pa = MEM_callocN(sizeof(Panel), "new panel"); pa->type = pt; BLI_strncpy(pa->panelname, idname, sizeof(pa->panelname)); - BLI_strncpy(pa->tabname, tabname, sizeof(pa->tabname)); if (pt->flag & PNL_DEFAULT_CLOSED) { if (align == BUT_VERTICAL) @@ -256,9 +270,13 @@ Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Pan pa->runtime_flag |= PNL_NEW_ADDED; BLI_addtail(&ar->panels, pa); - + +#ifdef UI_USE_PANELTAB + BLI_strncpy(pa->tabname, tabname, sizeof(pa->tabname)); + /* make new Panel tabbed? */ if (hookname) { + Panel *patab; for (patab = ar->panels.first; patab; patab = patab->next) { if ((patab->runtime_flag & PNL_ACTIVE) && patab->paneltab == NULL) { if (STREQLEN(hookname, patab->panelname, sizeof(patab->panelname))) { @@ -271,6 +289,9 @@ Panel *uiBeginPanel(ScrArea *sa, ARegion *ar, uiBlock *block, PanelType *pt, Pan } } } +#else + BLI_strncpy(pa->tabname, idname, sizeof(pa->tabname)); +#endif } /* Do not allow closed panels without headers! Else user could get "disappeared" UI! */ @@ -462,31 +483,41 @@ static void ui_draw_panel_scalewidget(const rcti *rect) fdrawline(xmin + dx, ymin + 1, xmax, ymax - dy + 1); glDisable(GL_BLEND); } - static void ui_draw_panel_dragwidget(const rctf *rect) { - float xmin, xmax, dx; - float ymin, ymax, dy; - - xmin = rect->xmin; - xmax = rect->xmax; - ymin = rect->ymin; - ymax = rect->ymax; - - dx = (xmax - xmin) / 3.0f; - dy = (ymax - ymin) / 3.0f; - - glEnable(GL_BLEND); - glColor4ub(255, 255, 255, 50); - fdrawline(xmin, ymax, xmax, ymin); - fdrawline(xmin + dx, ymax, xmax, ymin + dy); - fdrawline(xmin + 2 * dx, ymax, xmax, ymin + 2 * dy); - - glColor4ub(0, 0, 0, 50); - fdrawline(xmin, ymax + 1, xmax, ymin + 1); - fdrawline(xmin + dx, ymax + 1, xmax, ymin + dy + 1); - fdrawline(xmin + 2 * dx, ymax + 1, xmax, ymin + 2 * dy + 1); - glDisable(GL_BLEND); + unsigned char col_back[3], col_high[3], col_dark[3]; + const int col_tint = 84; + + const int px = (int)U.pixelsize; + const int px_zoom = max_ii(iroundf(BLI_rctf_size_y(rect) / 22.0f), 1); + + const int box_margin = max_ii(iroundf((float)(px_zoom * 2.0f)), px); + const int box_size = max_ii(iroundf((BLI_rctf_size_y(rect) / 8.0f) - px), px); + + const int x_min = rect->xmin; + const int y_min = rect->ymin; + const int y_ofs = max_ii(iroundf(BLI_rctf_size_y(rect) / 3.0f), px); + const int x_ofs = y_ofs; + int i_x, i_y; + + + UI_GetThemeColor3ubv(UI_GetThemeValue(TH_PANEL_SHOW_HEADER) ? TH_PANEL_HEADER : TH_PANEL_BACK, col_back); + UI_GetColorPtrShade3ubv(col_back, col_high, col_tint); + UI_GetColorPtrShade3ubv(col_back, col_dark, -col_tint); + + + /* draw multiple boxes */ + for (i_x = 0; i_x < 4; i_x++) { + for (i_y = 0; i_y < 2; i_y++) { + const int x_co = (x_min + x_ofs) + (i_x * (box_size + box_margin)); + const int y_co = (y_min + y_ofs) + (i_y * (box_size + box_margin)); + + glColor3ubv(col_dark); + glRectf(x_co - box_size, y_co - px_zoom, x_co, (y_co + box_size) - px_zoom); + glColor3ubv(col_high); + glRectf(x_co - box_size, y_co, x_co, y_co + box_size); + } + } } @@ -1131,7 +1162,7 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in button = 1; else if (event == AKEY) button = 1; - else if (ELEM3(event, 0, RETKEY, LEFTMOUSE) && shift) { + else if (ELEM(event, 0, RETKEY, LEFTMOUSE) && shift) { block->panel->flag ^= PNL_PIN; button = 2; } @@ -1524,6 +1555,12 @@ void UI_panel_category_draw_all(ARegion *ar, const char *category_id_active) const bool is_active = STREQ(category_id, category_id_active); +#ifdef DEBUG + if (STREQ(category_id, PNL_CATEGORY_FALLBACK)) { + printf("WARNING: Panel has no 'bl_category', script needs updating!\n"); + } +#endif + glEnable(GL_BLEND); #ifdef USE_FLAT_INACTIVE @@ -1716,7 +1753,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) /* XXX hardcoded key warning */ if ((inside || inside_header) && event->val == KM_PRESS) { - if (event->type == AKEY && !ELEM4(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift, event->alt)) { + if (event->type == AKEY && !ELEM(KM_MOD_FIRST, event->ctrl, event->oskey, event->shift, event->alt)) { if (pa->flag & PNL_CLOSEDY) { if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) @@ -1902,7 +1939,7 @@ static void panel_activate_state(const bContext *C, Panel *pa, uiHandlePanelStat data = MEM_callocN(sizeof(uiHandlePanelData), "uiHandlePanelData"); pa->activedata = data; - WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa); + WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa, false); } if (ELEM(state, PANEL_STATE_ANIMATION, PANEL_STATE_DRAG)) diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 97d4869bb2d..1574ceace8a 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -43,6 +43,8 @@ #include "BLI_utildefines.h" #include "BLI_ghash.h" +#include "PIL_time.h" + #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_report.h" @@ -146,25 +148,40 @@ static void ui_remove_temporary_region(bContext *C, bScreen *sc, ARegion *ar) /************************* Creating Tooltips **********************/ -typedef enum { - UI_TIP_LC_MAIN, - UI_TIP_LC_NORMAL, - UI_TIP_LC_PYTHON, - UI_TIP_LC_ALERT, - UI_TIP_LC_SUBMENU -} uiTooltipLineColor; -#define UI_TIP_LC_MAX 5 +#define UI_TIP_PAD_FAC 1.3f +#define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y) #define MAX_TOOLTIP_LINES 8 typedef struct uiTooltipData { rcti bbox; uiFontStyle fstyle; char lines[MAX_TOOLTIP_LINES][512]; - uiTooltipLineColor color_id[MAX_TOOLTIP_LINES]; + char header[512], active_info[512]; + struct { + enum { + UI_TIP_STYLE_NORMAL = 0, + UI_TIP_STYLE_HEADER, + UI_TIP_STYLE_MONO, + } style : 3; + enum { + UI_TIP_LC_MAIN = 0, /* primary text */ + UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */ + UI_TIP_LC_ACTIVE, /* titles of active enum values */ + UI_TIP_LC_NORMAL, /* regular text */ + UI_TIP_LC_PYTHON, /* Python snippet */ + UI_TIP_LC_ALERT, /* description of why operator can't run */ + } color_id : 4; + int is_pad : 1; + } format[MAX_TOOLTIP_LINES]; int totline; - int toth, spaceh, lineh; + int toth, lineh; } uiTooltipData; +#define UI_TIP_LC_MAX 6 + +BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max"); +BLI_STATIC_ASSERT(sizeof(((uiTooltipData *)NULL)->format[0]) <= sizeof(int), "oversize"); + static void rgb_tint(float col[3], float h, float h_strength, float v, float v_strength) @@ -183,16 +200,18 @@ static void rgb_tint(float col[3], static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) { + const float pad_px = UI_TIP_PADDING; uiTooltipData *data = ar->regiondata; uiWidgetColors *theme = ui_tooltip_get_theme(); rcti bbox = data->bbox; float tip_colors[UI_TIP_LC_MAX][3]; float *main_color = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */ + float *value_color = tip_colors[UI_TIP_LC_VALUE]; + float *active_color = tip_colors[UI_TIP_LC_ACTIVE]; float *normal_color = tip_colors[UI_TIP_LC_NORMAL]; float *python_color = tip_colors[UI_TIP_LC_PYTHON]; float *alert_color = tip_colors[UI_TIP_LC_ALERT]; - float *submenu_color = tip_colors[UI_TIP_LC_SUBMENU]; float background_color[3]; float tone_bg; @@ -203,6 +222,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) if (multisample_enabled) glDisable(GL_MULTISAMPLE_ARB); + wmOrtho2_region_ui(ar); + /* draw background */ ui_draw_tooltip_background(UI_GetStyle(), NULL, &bbox); @@ -211,32 +232,79 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) /* calculate normal_color */ rgb_uchar_to_float(main_color, (const unsigned char *)theme->text); + copy_v3_v3(active_color, main_color); copy_v3_v3(normal_color, main_color); copy_v3_v3(python_color, main_color); copy_v3_v3(alert_color, main_color); - copy_v3_v3(submenu_color, main_color); + copy_v3_v3(value_color, main_color); /* find the brightness difference between background and text colors */ tone_bg = rgb_to_grayscale(background_color); /* tone_fg = rgb_to_grayscale(main_color); */ - rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.3f); /* a shade darker (to bg) */ - rgb_tint(python_color, 0.666f, 0.25f, tone_bg, 0.3f); /* blue */ - rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* bright red */ - rgb_tint(submenu_color, 0.0f, 0.0f, tone_bg, 0.3f); /* a shade darker (to bg) */ + /* mix the colors */ + rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* light grey */ + rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* light blue */ + rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* grey */ + rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* dark grey */ + rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* red */ /* draw text */ - uiStyleFontSet(&data->fstyle); - bbox.ymax = bbox.ymax - 0.5f * (BLI_rcti_size_y(&bbox) - data->toth); + bbox.xmin += 0.5f * pad_px; /* add padding to the text */ + bbox.ymax -= 0.5f * (BLI_rcti_size_y(&bbox) - data->toth); bbox.ymin = bbox.ymax - data->lineh; for (i = 0; i < data->totline; i++) { - glColor3fv(tip_colors[data->color_id[i]]); - uiStyleFontDraw(&data->fstyle, &bbox, data->lines[i]); - bbox.ymin -= data->lineh + data->spaceh; - bbox.ymax -= data->lineh + data->spaceh; + if (data->format[i].style == UI_TIP_STYLE_HEADER) { + /* draw header and active data (is done here to be able to change color) */ + uiFontStyle fstyle_header = data->fstyle; + float xofs; + + /* override text-style */ + fstyle_header.shadow = 1; + fstyle_header.shadowcolor = rgb_to_luma(tip_colors[UI_TIP_LC_MAIN]); + fstyle_header.shadx = fstyle_header.shady = 0; + fstyle_header.shadowalpha = 1.0f; + + uiStyleFontSet(&fstyle_header); + glColor3fv(tip_colors[UI_TIP_LC_MAIN]); + uiStyleFontDraw(&fstyle_header, &bbox, data->header); + + xofs = BLF_width(fstyle_header.uifont_id, data->header, sizeof(data->header)); + bbox.xmin += xofs; + + glColor3fv(tip_colors[UI_TIP_LC_ACTIVE]); + uiStyleFontDraw(&data->fstyle, &bbox, data->active_info); + + bbox.xmin -= xofs; + } + else if (data->format[i].style == UI_TIP_STYLE_MONO) { + uiFontStyle fstyle_mono = data->fstyle; + fstyle_mono.uifont_id = blf_mono_font; + + uiStyleFontSet(&fstyle_mono); + /* XXX, needed because we dont have mono in 'U.uifonts' */ + BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi); + glColor3fv(tip_colors[data->format[i].color_id]); + uiStyleFontDraw(&fstyle_mono, &bbox, data->lines[i]); + } + else { + BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL); + /* draw remaining data */ + uiStyleFontSet(&data->fstyle); + glColor3fv(tip_colors[data->format[i].color_id]); + uiStyleFontDraw(&data->fstyle, &bbox, data->lines[i]); + } + if ((i + 1 != data->totline) && data->format[i + 1].is_pad) { + bbox.ymax -= data->lineh * UI_TIP_PAD_FAC; + bbox.ymin -= data->lineh * UI_TIP_PAD_FAC; + } + else { + bbox.ymax -= data->lineh; + bbox.ymin -= data->lineh; + } } if (multisample_enabled) @@ -254,6 +322,7 @@ static void ui_tooltip_region_free_cb(ARegion *ar) ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) { + const float pad_px = UI_TIP_PADDING; wmWindow *win = CTX_wm_window(C); uiStyle *style = UI_GetStyle(); static ARegionType type; @@ -264,7 +333,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) /* aspect values that shrink text are likely unreadable */ const float aspect = min_ff(1.0f, but->block->aspect); float fonth, fontw; - int winx /*, winy */, ofsx, ofsy, w, h, a; + int winx, ofsx, ofsy, w = 0, h, i; rctf rect_fl; rcti rect_i; @@ -284,52 +353,58 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) uiButGetStrInfo(C, but, &but_tip, &enum_label, &enum_tip, &op_keymap, &prop_keymap, &rna_struct, &rna_prop, NULL); - /* special case, enum rna buttons only have enum item description, - * use general enum description too before the specific one */ - /* Tip */ if (but_tip.strinfo) { - /* Expanded Bit-flag enums have a specific way to select multiple... */ - if ((but->type & ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), - "%s %s", but_tip.strinfo, IFACE_("(Shift-click to select multiple)")); + BLI_strncpy(data->header, but_tip.strinfo, sizeof(data->lines[0])); + if (enum_label.strinfo) { + BLI_snprintf(data->header, sizeof(data->header), "%s: ", but_tip.strinfo); + BLI_strncpy(data->active_info, enum_label.strinfo, sizeof(data->lines[0])); } - else { - BLI_strncpy(data->lines[data->totline], but_tip.strinfo, sizeof(data->lines[0])); - } - data->color_id[data->totline] = UI_TIP_LC_MAIN; + data->format[data->totline].style = UI_TIP_STYLE_HEADER; data->totline++; + + /* special case enum rna buttons */ + if ((but->type & ROW) && but->rnaprop && RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) { + BLI_strncpy(data->lines[data->totline], IFACE_("(Shift-click to select multiple)"), sizeof(data->lines[0])); + + data->format[data->totline].color_id = UI_TIP_LC_NORMAL; + data->totline++; + } + } /* Enum item label & tip */ - if (enum_label.strinfo && enum_tip.strinfo) { - BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), - "%s: %s", enum_label.strinfo, enum_tip.strinfo); - data->color_id[data->totline] = UI_TIP_LC_SUBMENU; + if (enum_tip.strinfo) { + BLI_strncpy(data->lines[data->totline], enum_tip.strinfo, sizeof(data->lines[0])); + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_VALUE; data->totline++; } /* Op shortcut */ if (op_keymap.strinfo) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), op_keymap.strinfo); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_VALUE; data->totline++; } /* Property context-toggle shortcut */ if (prop_keymap.strinfo) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Shortcut: %s"), prop_keymap.strinfo); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_VALUE; data->totline++; } - if (ELEM3(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, TEX, SEARCH_MENU, SEARCH_MENU_UNLINK)) { /* better not show the value of a password */ if ((but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) == 0) { /* full string */ ui_get_but_string(but, buf, sizeof(buf)); if (buf[0]) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Value: %s"), buf); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_VALUE; data->totline++; } } @@ -344,7 +419,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) RNA_property_float_get_index(&but->rnapoin, but->rnaprop, but->rnaindex) : RNA_property_float_get(&but->rnapoin, but->rnaprop); BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Radians: %f"), value); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].color_id = UI_TIP_LC_NORMAL; data->totline++; } } @@ -353,7 +428,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) if (ui_but_anim_expression_get(but, buf, sizeof(buf))) { /* expression */ BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Expression: %s"), buf); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].color_id = UI_TIP_LC_NORMAL; data->totline++; } } @@ -362,7 +437,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) ID *id = but->rnapoin.id.data; if (id->lib) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name); - data->color_id[data->totline] = UI_TIP_LC_NORMAL; + data->format[data->totline].color_id = UI_TIP_LC_NORMAL; data->totline++; } } @@ -383,7 +458,9 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) /* operator info */ if ((U.flag & USER_TOOLTIPS_PYTHON) == 0) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), str); - data->color_id[data->totline] = UI_TIP_LC_PYTHON; + data->format[data->totline].style = UI_TIP_STYLE_MONO; + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_PYTHON; data->totline++; } @@ -397,7 +474,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) poll_msg = CTX_wm_operator_poll_msg_get(C); if (poll_msg) { BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Disabled: %s"), poll_msg); - data->color_id[data->totline] = UI_TIP_LC_ALERT; /* alert */ + data->format[data->totline].color_id = UI_TIP_LC_ALERT; data->totline++; } } @@ -414,7 +491,9 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Python: %s"), rna_struct.strinfo); } - data->color_id[data->totline] = UI_TIP_LC_PYTHON; + data->format[data->totline].style = UI_TIP_STYLE_MONO; + data->format[data->totline].is_pad = true; + data->format[data->totline].color_id = UI_TIP_LC_PYTHON; data->totline++; if (but->rnapoin.id.data) { @@ -447,7 +526,8 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) } MEM_freeN(id_path); - data->color_id[data->totline] = UI_TIP_LC_PYTHON; + data->format[data->totline].style = UI_TIP_STYLE_MONO; + data->format[data->totline].color_id = UI_TIP_LC_PYTHON; data->totline++; } } @@ -486,22 +566,40 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) /* set font, get bb */ data->fstyle = style->widget; /* copy struct */ - data->fstyle.align = UI_STYLE_TEXT_CENTER; ui_fontscale(&data->fstyle.points, aspect); uiStyleFontSet(&data->fstyle); /* these defines tweaked depending on font */ -#define TIP_MARGIN_Y (2.0f / aspect) #define TIP_BORDER_X (16.0f / aspect) #define TIP_BORDER_Y (6.0f / aspect) h = BLF_height_max(data->fstyle.uifont_id); - for (a = 0, fontw = 0, fonth = 0; a < data->totline; a++) { - w = BLF_width(data->fstyle.uifont_id, data->lines[a], sizeof(data->lines[a])); + for (i = 0, fontw = 0, fonth = 0; i < data->totline; i++) { + if (data->format[i].style == UI_TIP_STYLE_HEADER) { + w = BLF_width(data->fstyle.uifont_id, data->header, sizeof(data->header)); + if (enum_label.strinfo) + w += BLF_width(data->fstyle.uifont_id, data->active_info, sizeof(data->active_info)); + } + else if (data->format[i].style == UI_TIP_STYLE_MONO) { + BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi); + + w = BLF_width(blf_mono_font, data->lines[i], sizeof(data->lines[i])); + } + else { + BLI_assert(data->format[i].style == UI_TIP_STYLE_NORMAL); + w = BLF_width(data->fstyle.uifont_id, data->lines[i], sizeof(data->lines[i])); + } + fontw = max_ff(fontw, (float)w); - fonth += (a == 0) ? h : h + TIP_MARGIN_Y; + + if ((i + 1 != data->totline) && data->format[i + 1].is_pad) { + fonth += h * UI_TIP_PAD_FAC; + } + else { + fonth += h; + } } //fontw *= aspect; @@ -510,30 +608,38 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) data->toth = fonth; data->lineh = h; - data->spaceh = TIP_MARGIN_Y; /* compute position */ ofsx = 0; //(but->block->panel) ? but->block->panel->ofsx : 0; ofsy = 0; //(but->block->panel) ? but->block->panel->ofsy : 0; rect_fl.xmin = BLI_rctf_cent_x(&but->rect) + ofsx - TIP_BORDER_X; - rect_fl.xmax = rect_fl.xmin + fontw + (TIP_BORDER_X * 2); + rect_fl.xmax = rect_fl.xmin + fontw + pad_px; rect_fl.ymax = but->rect.ymin + ofsy - TIP_BORDER_Y; rect_fl.ymin = rect_fl.ymax - fonth - TIP_BORDER_Y; - -#undef TIP_MARGIN_Y -#undef TIP_BORDER_X -#undef TIP_BORDER_Y /* since the text has beens caled already, the size of tooltips is defined now */ /* here we try to figure out the right location */ if (butregion) { + float mx, my; float ofsx_fl = rect_fl.xmin, ofsy_fl = rect_fl.ymax; ui_block_to_window_fl(butregion, but->block, &ofsx_fl, &ofsy_fl); - BLI_rctf_translate(&rect_fl, ofsx_fl - rect_fl.xmin, ofsy_fl - rect_fl.ymax); + +#if 1 + /* use X mouse location */ + mx = (win->eventstate->x + (TIP_BORDER_X * 2)) - BLI_rctf_cent_x(&but->rect); +#else + mx = ofsx_fl - rect_fl.xmin; +#endif + my = ofsy_fl - rect_fl.ymax; + + BLI_rctf_translate(&rect_fl, mx, my); } BLI_rcti_rctf_copy(&rect_i, &rect_fl); +#undef TIP_BORDER_X +#undef TIP_BORDER_Y + /* clip with window boundaries */ winx = WM_window_pixels_x(win); @@ -555,20 +661,25 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) rect_i.ymin = 30; } + /* add padding */ + BLI_rcti_resize(&rect_i, + BLI_rcti_size_x(&rect_i) + pad_px, + BLI_rcti_size_y(&rect_i) + pad_px); + /* widget rect, in region coords */ { int width = UI_ThemeMenuShadowWidth(); data->bbox.xmin = width; - data->bbox.xmax = BLI_rcti_size_x(&rect_i) + width; + data->bbox.xmax = BLI_rcti_size_x(&rect_i) - width; data->bbox.ymin = width; - data->bbox.ymax = BLI_rcti_size_y(&rect_i) + width; + data->bbox.ymax = BLI_rcti_size_y(&rect_i); /* region bigger for shadow */ ar->winrct.xmin = rect_i.xmin - width; ar->winrct.xmax = rect_i.xmax + width; ar->winrct.ymin = rect_i.ymin - width; - ar->winrct.ymax = rect_i.ymax + MENU_TOP; + ar->winrct.ymax = rect_i.ymax + width; } /* adds subwindow */ @@ -704,8 +815,10 @@ static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step) data->active = 0; ui_searchbox_update(C, ar, but, false); } - else if (data->active < -1) - data->active = -1; + else { + /* only let users step into an 'unset' state for unlink buttons */ + data->active = (but->type == SEARCH_MENU_UNLINK) ? -1 : 0; + } } ED_region_tag_redraw(ar); @@ -775,6 +888,12 @@ bool ui_searchbox_apply(uiBut *but, ARegion *ar) return true; } + else if (but->type == SEARCH_MENU_UNLINK) { + /* It is valid for _UNLINK flavor to have no active element (it's a valid way to unlink). */ + but->editstr[0] = '\0'; + + return true; + } else { return false; } @@ -911,7 +1030,7 @@ static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) uiSearchboxData *data = ar->regiondata; /* pixel space */ - wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f); + wmOrtho2_region_ui(ar); if (data->noback == false) ui_draw_search_back(NULL, NULL, &data->bbox); /* style not used yet */ @@ -1056,8 +1175,9 @@ ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but) /* widget rect, in region coords */ data->bbox.xmin = width; data->bbox.xmax = BLI_rcti_size_x(&ar->winrct) - width; - data->bbox.ymin = width; - data->bbox.ymax = BLI_rcti_size_y(&ar->winrct) - width; + /* Do not use shadow width for height, gives insane margin with big shadows, and issue T41548 with small ones */ + data->bbox.ymin = 8 * UI_DPI_FAC; + data->bbox.ymax = BLI_rcti_size_y(&ar->winrct) - 8 * UI_DPI_FAC; /* check if button is lower half */ if (but->rect.ymax < BLI_rctf_cent_y(&but->block->rect)) { @@ -1421,6 +1541,15 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar) { uiBlock *block; + if (ar->do_draw & RGN_DRAW_REFRESH_UI) { + uiBlock *block_next; + 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); + } + } + for (block = ar->uiblocks.first; block; block = block->next) uiDrawBlock(C, block); } @@ -1502,42 +1631,50 @@ void ui_popup_block_scrolltest(uiBlock *block) } } -uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, - uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, - void *arg) +static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) { - wmWindow *window = CTX_wm_window(C); - static ARegionType type; - ARegion *ar; - uiBlock *block; - uiPopupBlockHandle *handle; - uiSafetyRct *saferct; - int width = UI_ThemeMenuShadowWidth(); + ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region); - /* create handle */ - handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); + if (handle->scrolltimer) + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer); +} - /* store context for operator */ - handle->ctx_area = CTX_wm_area(C); - handle->ctx_region = CTX_wm_region(C); - - /* create area region */ - ar = ui_add_temporary_region(CTX_wm_screen(C)); - handle->region = ar; +/** + * Called for creatign new popups and refreshing existing ones. + */ +uiBlock *ui_popup_block_refresh( + bContext *C, uiPopupBlockHandle *handle, + ARegion *butregion, uiBut *but) +{ + const int width = UI_ThemeMenuShadowWidth(); + wmWindow *window = CTX_wm_window(C); + ARegion *ar = handle->region; - memset(&type, 0, sizeof(ARegionType)); - type.draw = ui_block_region_draw; - type.regionid = RGN_TYPE_TEMPORARY; - ar->type = &type; + uiBlockCreateFunc create_func = handle->popup_create_vars.create_func; + uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func; + void *arg = handle->popup_create_vars.arg; - UI_add_region_handlers(&ar->handlers); + uiBlock *block_old = ar->uiblocks.first; + uiBlock *block; + +#ifdef DEBUG + wmEvent *event_back = window->eventstate; +#endif /* create ui block */ if (create_func) - block = create_func(C, handle->region, arg); + block = create_func(C, ar, arg); else block = handle_create_func(C, handle, arg); + /* callbacks _must_ leave this for us, otherwise we can't call uiBlockUpdateFromOld */ + BLI_assert(!block->endblock); + + /* ensure we don't use mouse coords here! */ +#ifdef DEBUG + window->eventstate = NULL; +#endif + if (block->handle) { memcpy(block->handle, handle, sizeof(uiPopupBlockHandle)); MEM_freeN(handle); @@ -1560,8 +1697,11 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut block->flag |= UI_BLOCK_LOOP; + /* defer this until blocks are translated (below) */ + block->oldblock = NULL; + if (!block->endblock) - uiEndBlock(C, block); + uiEndBlock_ex(C, block, handle->popup_create_vars.event_xy); /* if this is being created from a button */ if (but) { @@ -1570,31 +1710,89 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut handle->direction = block->direction; } else { + uiSafetyRct *saferct; /* keep a list of these, needed for pulldown menus */ saferct = MEM_callocN(sizeof(uiSafetyRct), "uiSafetyRct"); saferct->safety = block->safety; BLI_addhead(&block->saferct, saferct); } - /* clip block with window boundary */ - ui_popup_block_clip(window, block); - - /* the block and buttons were positioned in window space as in 2.4x, now - * these menu blocks are regions so we bring it back to region space. - * additionally we add some padding for the menu shadow or rounded menus */ - ar->winrct.xmin = block->rect.xmin - width; - ar->winrct.xmax = block->rect.xmax + width; - ar->winrct.ymin = block->rect.ymin - width; - ar->winrct.ymax = block->rect.ymax + MENU_TOP; - - ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin); + if (block->flag & UI_BLOCK_RADIAL) { + uiBut *but; + int win_width = UI_SCREEN_MARGIN; + int winx, winy; - /* adds subwindow */ - ED_region_init(C, ar); + int x_offset = 0, y_offset = 0; + + winx = WM_window_pixels_x(window); + winy = WM_window_pixels_y(window); + + copy_v2_v2(block->pie_data.pie_center_init, block->pie_data.pie_center_spawned); + + /* only try translation if area is large enough */ + if (BLI_rctf_size_x(&block->rect) < winx - (2.0f * win_width)) { + if (block->rect.xmin < win_width ) x_offset += win_width - block->rect.xmin; + if (block->rect.xmax > winx - win_width) x_offset += winx - win_width - block->rect.xmax; + } + + if (BLI_rctf_size_y(&block->rect) < winy - (2.0f * win_width)) { + if (block->rect.ymin < win_width ) y_offset += win_width - block->rect.ymin; + if (block->rect.ymax > winy - win_width) y_offset += winy - win_width - block->rect.ymax; + } + /* if we are offsetting set up initial data for timeout functionality */ + + if ((x_offset != 0) || (y_offset != 0)) { + block->pie_data.pie_center_spawned[0] += x_offset; + block->pie_data.pie_center_spawned[1] += y_offset; + + ui_block_translate(block, x_offset, y_offset); + + if (U.pie_initial_timeout > 0) + block->pie_data.flags |= UI_PIE_INITIAL_DIRECTION; + } + + ar->winrct.xmin = 0; + ar->winrct.xmax = winx; + ar->winrct.ymin = 0; + ar->winrct.ymax = winy; + + ui_block_calculate_pie_segment(block, block->pie_data.pie_center_init); + + /* lastly set the buttons at the center of the pie menu, ready for animation */ + if (U.pie_animation_timeout > 0) { + for (but = block->buttons.first; but; but = but->next) { + if (but->pie_dir != UI_RADIAL_NONE) { + BLI_rctf_recenter(&but->rect, UNPACK2(block->pie_data.pie_center_spawned)); + } + } + } + } + else { + /* clip block with window boundary */ + ui_popup_block_clip(window, block); + /* the block and buttons were positioned in window space as in 2.4x, now + * these menu blocks are regions so we bring it back to region space. + * additionally we add some padding for the menu shadow or rounded menus */ + ar->winrct.xmin = block->rect.xmin - width; + ar->winrct.xmax = block->rect.xmax + width; + ar->winrct.ymin = block->rect.ymin - width; + ar->winrct.ymax = block->rect.ymax + MENU_TOP; + + ui_block_translate(block, -ar->winrct.xmin, -ar->winrct.ymin); + } + + if (block_old) { + block->oldblock = block_old; + uiBlockUpdateFromOld(C, block); + uiFreeInactiveBlocks(C, &ar->uiblocks); + } /* checks which buttons are visible, sets flags to prevent draw (do after region init) */ ui_popup_block_scrolltest(block); + /* adds subwindow */ + ED_region_init(C, ar); + /* get winmat now that we actually have the subwindow */ wmSubWindowSet(window, ar->swinid); @@ -1603,15 +1801,59 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut /* notify change and redraw */ ED_region_tag_redraw(ar); + ED_region_update_rect(C, ar); + +#ifdef DEBUG + window->eventstate = event_back; +#endif + + return block; +} + +uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, + uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, + void *arg) +{ + wmWindow *window = CTX_wm_window(C); + static ARegionType type; + ARegion *ar; + uiBlock *block; + uiPopupBlockHandle *handle; + + /* create handle */ + handle = MEM_callocN(sizeof(uiPopupBlockHandle), "uiPopupBlockHandle"); + + /* store context for operator */ + handle->ctx_area = CTX_wm_area(C); + handle->ctx_region = CTX_wm_region(C); + + /* store vars to refresh popup (RGN_DRAW_REFRESH_UI) */ + handle->popup_create_vars.create_func = create_func; + handle->popup_create_vars.handle_create_func = handle_create_func; + 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); + + /* create area region */ + ar = ui_add_temporary_region(CTX_wm_screen(C)); + handle->region = ar; + + memset(&type, 0, sizeof(ARegionType)); + type.draw = ui_block_region_draw; + type.regionid = RGN_TYPE_TEMPORARY; + ar->type = &type; + + UI_add_region_handlers(&ar->handlers); + + block = ui_popup_block_refresh(C, handle, butregion, but); + handle = block->handle; + return handle; } void ui_popup_block_free(bContext *C, uiPopupBlockHandle *handle) { - ui_remove_temporary_region(C, CTX_wm_screen(C), handle->region); - - if (handle->scrolltimer) - WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), handle->scrolltimer); + ui_popup_block_remove(C, handle); MEM_freeN(handle); } @@ -1694,7 +1936,7 @@ static void ui_update_block_buts_rgb(uiBlock *block, const float rgb[3], bool is if (rgb_gamma[2] > 1.0f) rgb_gamma[2] = modf(rgb_gamma[2], &intpart); rgb_float_to_uchar(rgb_gamma_uchar, rgb_gamma); - BLI_snprintf(col, sizeof(col), "%02X%02X%02X", UNPACK3OP((unsigned int), rgb_gamma_uchar)); + BLI_snprintf(col, sizeof(col), "%02X%02X%02X", UNPACK3_EX((unsigned int), rgb_gamma_uchar, )); strcpy(bt->poin, col); } @@ -1982,7 +2224,7 @@ static void uiBlockPicker(uiBlock *block, float rgba[4], PointerRNA *ptr, Proper } rgb_float_to_uchar(rgb_gamma_uchar, rgb_gamma); - BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3OP((unsigned int), rgb_gamma_uchar)); + BLI_snprintf(hexcol, sizeof(hexcol), "%02X%02X%02X", UNPACK3_EX((unsigned int), rgb_gamma_uchar, )); yco = -3.0f * UI_UNIT_Y; bt = uiDefBut(block, TEX, 0, IFACE_("Hex: "), 0, yco, butwidth, UI_UNIT_Y, hexcol, 0, 8, 0, 0, TIP_("Hex triplet for color (#RRGGBB)")); @@ -2175,6 +2417,12 @@ struct uiPopupMenu { void *menu_arg; }; +struct uiPieMenu { + uiBlock *block_radial; /* radial block of the pie menu (more could be added later) */ + uiLayout *layout; + int mx, my; +}; + static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, void *arg_pup) { uiBlock *block; @@ -2230,6 +2478,7 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi uiBlockSetFlag(block, UI_BLOCK_MOVEMOUSE_QUIT); if (pup->popup) { + uiBut *but_activate = NULL; uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_NUMSELECT); uiBlockSetDirection(block, direction); @@ -2243,6 +2492,10 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi * block to be under the mouse */ offset[0] = -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect)); offset[1] = -(bt->rect.ymin + 0.5f * UI_UNIT_Y); + + if (ui_but_is_editable(bt)) { + but_activate = bt; + } } else { /* position mouse at 0.8*width of the button and below the tile @@ -2252,6 +2505,20 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi offset[0] = min_ii(offset[0], -(bt->rect.xmin + 0.8f * BLI_rctf_size_x(&bt->rect))); offset[1] = 2.1 * UI_UNIT_Y; + + for (bt = block->buttons.first; bt; bt = bt->next) { + if (ui_but_is_editable(bt)) { + but_activate = bt; + break; + } + } + } + + /* in rare cases this is needed since moving the popup + * to be within the window bounds may move it away from the mouse, + * This ensures we set an item to be active. */ + if (but_activate) { + ui_button_activate_over(C, handle->region, but_activate); } block->minbounds = minwidth; @@ -2278,8 +2545,6 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi if (pup->slideout) uiBlockSetDirection(block, UI_RIGHT); - uiEndBlock(C, block); - return pup->block; } @@ -2331,7 +2596,7 @@ uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut if (!but) { handle->popup = true; - UI_add_popup_handlers(C, &window->modalhandlers, handle); + UI_add_popup_handlers(C, &window->modalhandlers, handle, false); WM_event_add_mousemove(C); } @@ -2393,7 +2658,7 @@ void uiPupMenuEnd(bContext *C, uiPopupMenu *pup) menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup); menu->popup = true; - UI_add_popup_handlers(C, &window->modalhandlers, menu); + UI_add_popup_handlers(C, &window->modalhandlers, menu, false); WM_event_add_mousemove(C); MEM_freeN(pup); @@ -2404,6 +2669,208 @@ uiLayout *uiPupMenuLayout(uiPopupMenu *pup) return pup->layout; } +/*************************** Pie Menus ***************************************/ + +static uiBlock *ui_block_func_PIE(bContext *UNUSED(C), uiPopupBlockHandle *handle, void *arg_pie) +{ + uiBlock *block; + uiPieMenu *pie = arg_pie; + int minwidth, width, height; + + minwidth = 50; + block = pie->block_radial; + + /* in some cases we create the block before the region, + * so we set it delayed here if necessary */ + if (BLI_findindex(&handle->region->uiblocks, block) == -1) + uiBlockSetRegion(block, handle->region); + + uiBlockLayoutResolve(block, &width, &height); + + uiBlockSetFlag(block, UI_BLOCK_LOOP | UI_BLOCK_REDRAW | UI_BLOCK_NUMSELECT); + + block->minbounds = minwidth; + block->bounds = 1; + block->mx = 0; + block->my = 0; + block->bounds_type = UI_BLOCK_BOUNDS_PIE_CENTER; + + block->pie_data.pie_center_spawned[0] = pie->mx; + block->pie_data.pie_center_spawned[1] = pie->my; + + return pie->block_radial; +} + +static float uiPieTitleWidth(const char *name, int icon) +{ + return (UI_GetStringWidth(name) + + (UI_UNIT_X * (1.50f + (icon ? 0.25f : 0.0f)))); +} + +uiPieMenu *uiPieMenuBegin(struct bContext *C, const char *title, int icon, const wmEvent *event) +{ + uiStyle *style; + uiPieMenu *pie; + short event_type; + + wmWindow *win = CTX_wm_window(C); + + style = UI_GetStyleDraw(); + pie = MEM_callocN(sizeof(uiPopupMenu), "pie menu"); + + pie->block_radial = uiBeginBlock(C, NULL, __func__, UI_EMBOSS); + /* may be useful later to allow spawning pies + * from old positions */ + /* pie->block_radial->flag |= UI_BLOCK_POPUP_MEMORY; */ + pie->block_radial->puphash = ui_popup_menu_hash(title); + pie->block_radial->flag |= UI_BLOCK_RADIAL; + + /* if pie is spawned by a left click, it is always assumed to be click style */ + if (event->type == LEFTMOUSE) { + pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; + pie->block_radial->pie_data.event = EVENT_NONE; + win->lock_pie_event = EVENT_NONE; + } + else { + if (win->last_pie_event != EVENT_NONE) { + /* original pie key has been released, so don't propagate the event */ + if (win->lock_pie_event == EVENT_NONE) { + event_type = EVENT_NONE; + pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; + } + else + event_type = win->last_pie_event; + } + else { + event_type = event->type; + } + + pie->block_radial->pie_data.event = event_type; + win->lock_pie_event = event_type; + } + + pie->layout = uiBlockLayout(pie->block_radial, UI_LAYOUT_VERTICAL, UI_LAYOUT_PIEMENU, 0, 0, 200, 0, 0, style); + pie->mx = event->x; + pie->my = event->y; + + /* create title button */ + if (title[0]) { + uiBut *but; + char titlestr[256]; + int w; + if (icon) { + BLI_snprintf(titlestr, sizeof(titlestr), " %s", title); + w = uiPieTitleWidth(titlestr, icon); + but = uiDefIconTextBut(pie->block_radial, LABEL, 0, icon, titlestr, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + } + else { + w = uiPieTitleWidth(title, 0); + but = uiDefBut(pie->block_radial, LABEL, 0, title, 0, 0, w, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + } + /* do not align left */ + but->drawflag &= ~UI_BUT_TEXT_LEFT; + } + + return pie; +} + +void uiPieMenuEnd(bContext *C, uiPieMenu *pie) +{ + wmWindow *window = CTX_wm_window(C); + uiPopupBlockHandle *menu; + + menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_PIE, pie); + menu->popup = true; + menu->towardstime = PIL_check_seconds_timer(); + + UI_add_popup_handlers(C, &window->modalhandlers, menu, true); + WM_event_add_mousemove(C); + + MEM_freeN(pie); +} + +uiLayout *uiPieMenuLayout(uiPieMenu *pie) +{ + return pie->layout; +} + +void uiPieMenuInvoke(struct bContext *C, const char *idname, const wmEvent *event) +{ + uiPieMenu *pie; + uiLayout *layout; + Menu menu; + MenuType *mt = WM_menutype_find(idname, true); + + if (mt == NULL) { + printf("%s: named menu \"%s\" not found\n", __func__, idname); + return; + } + + if (mt->poll && mt->poll(C, mt) == 0) + return; + + pie = uiPieMenuBegin(C, IFACE_(mt->label), ICON_NONE, event); + layout = uiPieMenuLayout(pie); + + menu.layout = layout; + menu.type = mt; + + if (G.debug & G_DEBUG_WM) { + printf("%s: opening menu \"%s\"\n", __func__, idname); + } + + mt->draw(C, &menu); + + uiPieMenuEnd(C, pie); +} + +void uiPieOperatorEnumInvoke(struct bContext *C, const char *title, const char *opname, + const char *propname, const wmEvent *event) +{ + uiPieMenu *pie; + uiLayout *layout; + + pie = uiPieMenuBegin(C, IFACE_(title), ICON_NONE, event); + layout = uiPieMenuLayout(pie); + + layout = uiLayoutRadial(layout); + uiItemsEnumO(layout, opname, propname); + + uiPieMenuEnd(C, pie); +} + +void uiPieEnumInvoke(struct bContext *C, const char *title, const char *path, + const wmEvent *event) +{ + PointerRNA ctx_ptr; + PointerRNA r_ptr; + PropertyRNA *r_prop; + uiPieMenu *pie; + uiLayout *layout; + + RNA_pointer_create(NULL, &RNA_Context, C, &ctx_ptr); + + if (!RNA_path_resolve(&ctx_ptr, path, &r_ptr, &r_prop)) { + return; + } + + /* invalid property, only accept enums */ + if (RNA_property_type(r_prop) != PROP_ENUM) { + BLI_assert(0); + return; + } + + pie = uiPieMenuBegin(C, IFACE_(title), ICON_NONE, event); + + layout = uiPieMenuLayout(pie); + + layout = uiLayoutRadial(layout); + uiItemFullR(layout, &r_ptr, r_prop, RNA_NO_INDEX, 0, UI_ITEM_R_EXPAND, NULL, 0); + + uiPieMenuEnd(C, pie); +} + + /*************************** Standard Popup Menus ****************************/ void uiPupMenuReports(bContext *C, ReportList *reports) @@ -2500,13 +2967,13 @@ void uiPupBlockO(bContext *C, uiBlockCreateFunc func, void *arg, const char *opn handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL; handle->opcontext = opcontext; - UI_add_popup_handlers(C, &window->modalhandlers, handle); + UI_add_popup_handlers(C, &window->modalhandlers, handle, false); WM_event_add_mousemove(C); } void uiPupBlock(bContext *C, uiBlockCreateFunc func, void *arg) { - uiPupBlockO(C, func, arg, NULL, 0); + uiPupBlockO(C, func, arg, NULL, WM_OP_INVOKE_DEFAULT); } void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg) @@ -2523,7 +2990,7 @@ void uiPupBlockEx(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_f handle->cancel_func = cancel_func; // handle->opcontext = opcontext; - UI_add_popup_handlers(C, &window->modalhandlers, handle); + UI_add_popup_handlers(C, &window->modalhandlers, handle, false); WM_event_add_mousemove(C); } diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index fa31c20eb74..c27789c0fc9 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -330,7 +330,7 @@ void UI_DrawString(float x, float y, const char *str) /* reading without uifont will create one */ void uiStyleInit(void) { - uiFont *font = U.uifonts.first; + uiFont *font; uiStyle *style = U.uistyles.first; int monofont_size = datatoc_bmonofont_ttf_size; unsigned char *monofont_ttf = (unsigned char *)datatoc_bmonofont_ttf; @@ -340,11 +340,23 @@ void uiStyleInit(void) U.dpi = 72; CLAMP(U.dpi, 48, 144); + for (font = U.uifonts.first; font; font = font->next) { + BLF_unload_id(font->blf_id); + } + + font = U.uifonts.first; + /* default builtin */ if (font == NULL) { font = MEM_callocN(sizeof(uiFont), "ui font"); BLI_addtail(&U.uifonts, font); - + } + + if (U.font_path_ui[0]) { + BLI_strncpy(font->filename, U.font_path_ui, sizeof(font->filename)); + font->uifont_id = UIFONT_CUSTOM1; + } + else { BLI_strncpy(font->filename, "default", sizeof(font->filename)); font->uifont_id = UIFONT_DEFAULT; } @@ -381,8 +393,12 @@ void uiStyleInit(void) } else { font->blf_id = BLF_load(font->filename); - if (font->blf_id == -1) + if (font->blf_id == -1) { font->blf_id = BLF_load_mem("default", (unsigned char *)datatoc_bfont_ttf, datatoc_bfont_ttf_size); + } + else { + BLF_default_set(font->blf_id); + } } if (font->blf_id == -1) { diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index e4c26d86044..b0bea42e3bc 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -36,6 +36,8 @@ #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_object_force.h" +#include "DNA_brush_types.h" +#include "DNA_texture_types.h" #include "BLI_utildefines.h" #include "BLI_string.h" @@ -60,6 +62,7 @@ #include "BKE_object.h" #include "BKE_packedFile.h" #include "BKE_particle.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_sca.h" #include "BKE_screen.h" @@ -209,7 +212,6 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) uiBoundsBlock(block, 0.3f * U.widget_unit); uiBlockSetDirection(block, UI_DOWN); - uiEndBlock(C, block); /* give search-field focus */ uiButSetFocusOnEnter(win, but); @@ -340,7 +342,7 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_LA: return N_("Browse Lamp Data to be linked"); case ID_CA: return N_("Browse Camera Data to be linked"); case ID_WO: return N_("Browse World Settings to be linked"); - case ID_SCR: return N_("Choose Screen lay-out"); + case ID_SCR: return N_("Choose Screen layout"); case ID_TXT: return N_("Browse Text to be linked"); case ID_SPK: return N_("Browse Speaker Data to be linked"); case ID_SO: return N_("Browse Sound to be linked"); @@ -350,6 +352,8 @@ static const char *template_id_browse_tip(StructRNA *type) case ID_BR: return N_("Browse Brush to be linked"); case ID_PA: return N_("Browse Particle Settings to be linked"); case ID_GD: return N_("Browse Grease Pencil Data to be linked"); + case ID_PAL: return N_("Browse Palette Data to be linked"); + case ID_PC: return N_("Browse Paint Curve Data to be linked"); } } return N_("Browse ID data to be linked"); @@ -418,11 +422,9 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, TIP_(template_id_browse_tip(type))); - if (type) { - but->icon = RNA_struct_ui_icon(type); - if (id) but->icon = ui_id_icon_get(C, id, true); - uiButSetFlag(but, UI_HAS_ICON | UI_ICON_PREVIEW); - } + but->icon = id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type); + uiButSetFlag(but, UI_HAS_ICON | UI_ICON_PREVIEW); + if ((idfrom && idfrom->lib) || !editable) uiButSetFlag(but, UI_BUT_DISABLED); @@ -431,14 +433,11 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str else if (flag & UI_ID_BROWSE) { but = uiDefBlockButN(block, id_search_menu, MEM_dupallocN(template), "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, TIP_(template_id_browse_tip(type))); - - if (type) { - but->icon = RNA_struct_ui_icon(type); - /* default dragging of icon for id browse buttons */ - uiButSetDragID(but, id); - uiButSetFlag(but, UI_HAS_ICON); - uiButSetDrawFlag(but, UI_BUT_ICON_LEFT); - } + but->icon = RNA_struct_ui_icon(type); + /* default dragging of icon for id browse buttons */ + uiButSetDragID(but, id); + uiButSetFlag(but, UI_HAS_ICON); + uiButSetDrawFlag(but, UI_BUT_ICON_LEFT); if ((idfrom && idfrom->lib) || !editable) uiButSetFlag(but, UI_BUT_DISABLED); @@ -495,7 +494,7 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str if (user_alert) uiButSetFlag(but, UI_BUT_REDALERT); - if (id->lib == NULL && !(ELEM5(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB))) { + if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB))) { uiDefButR(block, TOG, 0, "F", 0, 0, UI_UNIT_X, UI_UNIT_Y, &idptr, "use_fake_user", -1, 0, 0, -1, -1, NULL); } } @@ -577,24 +576,33 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str /* delete button */ /* don't use RNA_property_is_unlink here */ - if (id && (flag & UI_ID_DELETE) && (RNA_property_flag(template->prop) & PROP_NEVER_UNLINK) == 0) { + if (id && (flag & UI_ID_DELETE)) { + /* allow unlink if 'unlinkop' is passed, even when 'PROP_NEVER_UNLINK' is set */ + but = NULL; + if (unlinkop) { but = uiDefIconButO(block, BUT, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); /* so we can access the template from operators, font unlinking needs this */ uiButSetNFunc(but, NULL, MEM_dupallocN(template), NULL); } else { - but = uiDefIconBut(block, BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, - TIP_("Unlink datablock " - "(Shift + Click to set users to zero, data will then not be saved)")); - uiButSetNFunc(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_DELETE)); + if ((RNA_property_flag(template->prop) & PROP_NEVER_UNLINK) == 0) { + but = uiDefIconBut(block, BUT, 0, ICON_X, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, + TIP_("Unlink datablock " + "(Shift + Click to set users to zero, data will then not be saved)")); + uiButSetNFunc(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_DELETE)); - if (RNA_property_flag(template->prop) & PROP_NEVER_NULL) - uiButSetFlag(but, UI_BUT_DISABLED); + if (RNA_property_flag(template->prop) & PROP_NEVER_NULL) { + uiButSetFlag(but, UI_BUT_DISABLED); + } + } } - if ((idfrom && idfrom->lib) || !editable) - uiButSetFlag(but, UI_BUT_DISABLED); + if (but) { + if ((idfrom && idfrom->lib) || !editable) { + uiButSetFlag(but, UI_BUT_DISABLED); + } + } } if (idcode == ID_TE) @@ -757,28 +765,6 @@ void uiTemplatePathBuilder(uiLayout *layout, PointerRNA *ptr, const char *propna #define ERROR_LIBDATA_MESSAGE IFACE_("Can't edit external libdata") -static void modifiers_setOnCage(bContext *C, void *ob_v, void *md_v) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = ob_v; - ModifierData *md = md_v; - int i, cageIndex = modifiers_getCageIndex(scene, ob, NULL, 0); - - /* undo button operation */ - md->mode ^= eModifierMode_OnCage; - - for (i = 0, md = ob->modifiers.first; md; ++i, md = md->next) { - if (md == md_v) { - if (i >= cageIndex) - md->mode ^= eModifierMode_OnCage; - break; - } - } - - WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); -} - static void modifiers_convertToReal(bContext *C, void *ob_v, void *md_v) { Object *ob = ob_v; @@ -814,7 +800,7 @@ static int modifier_can_delete(ModifierData *md) static int modifier_is_simulation(ModifierData *md) { /* Physic Tab */ - if (ELEM7(md->type, eModifierType_Cloth, eModifierType_Collision, eModifierType_Fluidsim, eModifierType_Smoke, + if (ELEM(md->type, eModifierType_Cloth, eModifierType_Collision, eModifierType_Fluidsim, eModifierType_Smoke, eModifierType_Softbody, eModifierType_Surface, eModifierType_DynamicPaint)) { return 1; @@ -835,7 +821,7 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, PointerRNA ptr; uiBut *but; uiBlock *block; - uiLayout *box, *column, *row; + uiLayout *box, *column, *row, *sub; uiLayout *result = NULL; int isVirtual = (md->mode & eModifierMode_Virtual); char str[128]; @@ -876,7 +862,11 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, uiBlockSetEmboss(block, UI_EMBOSS); /* modifier name */ + if (mti->isDisabled && mti->isDisabled(md, 0)) { + uiLayoutSetRedAlert(row, true); + } uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiLayoutSetRedAlert(row, false); /* mode enabling buttons */ uiBlockBeginAlign(block); @@ -887,39 +877,32 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); - if (mti->flags & eModifierTypeFlag_SupportsEditmode) - uiItemR(row, &ptr, "show_in_editmode", 0, "", ICON_NONE); + if (mti->flags & eModifierTypeFlag_SupportsEditmode) { + sub = uiLayoutRow(row, true); + if (!(md->mode & eModifierMode_Realtime)) { + uiLayoutSetActive(sub, false); + } + uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + } } if (ob->type == OB_MESH) { - if (modifier_couldBeCage(scene, md) && (index <= lastCageIndex)) { - /* -- convert to rna ? */ - but = uiDefIconButBitI(block, TOG, eModifierMode_OnCage, 0, ICON_MESH_DATA, 0, 0, - UI_UNIT_X - 2, UI_UNIT_Y, &md->mode, 0.0, 0.0, 0.0, 0.0, - TIP_("Apply modifier to editing cage during Edit mode")); - if (index < cageIndex) - uiButSetFlag(but, UI_BUT_DISABLED); - uiButSetFunc(but, modifiers_setOnCage, ob, md); - } - else if (modifier_supportsCage(scene, md) && (index <= lastCageIndex)) { - uiBlockEndAlign(block); - - /* place holder button */ - uiBlockSetEmboss(block, UI_EMBOSSN); - but = uiDefIconBut(block, BUT, 0, ICON_NONE, 0, 0, UI_UNIT_X - 2, UI_UNIT_Y, - NULL, 0.0, 0.0, 0.0, 0.0, NULL); - uiButSetFlag(but, UI_BUT_DISABLED); - uiBlockSetEmboss(block, UI_EMBOSS); + if (modifier_supportsCage(scene, md) && (index <= lastCageIndex)) { + sub = uiLayoutRow(row, true); + if (index < cageIndex || !modifier_couldBeCage(scene, md)) { + uiLayoutSetActive(sub, false); + } + uiItemR(sub, &ptr, "show_on_cage", 0, "", ICON_NONE); } } /* tessellation point for curve-typed objects */ - else if (ELEM3(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { /* some modifiers could work with pre-tessellated curves only */ - if (ELEM3(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { + if (ELEM(md->type, eModifierType_Hook, eModifierType_Softbody, eModifierType_MeshDeform)) { /* add disabled pre-tessellated button, so users could have * message for this modifiers */ but = uiDefIconButBitI(block, TOG, eModifierMode_ApplyOnSpline, 0, ICON_SURFACE_DATA, 0, 0, UI_UNIT_X - 2, UI_UNIT_Y, &md->mode, 0.0, 0.0, 0.0, 0.0, - TIP_("This modifier could be applied on splines' points only")); + TIP_("This modifier can only be applied on splines' points")); uiButSetFlag(but, UI_BUT_DISABLED); } else if (mti->type != eModifierTypeType_Constructive) { @@ -985,7 +968,7 @@ static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, uiBlockClearButLock(block); uiBlockSetButLock(block, ob && ob->id.lib, ERROR_LIBDATA_MESSAGE); - if (!ELEM5(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem, + if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem, eModifierType_Cloth, eModifierType_Smoke)) { uiItemO(row, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE, @@ -1304,7 +1287,7 @@ void uiTemplatePreview(uiLayout *layout, bContext *C, ID *id, int show_buttons, char _preview_id[UI_MAX_NAME_STR]; - if (id && !ELEM5(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) { + if (id && !ELEM(GS(id->name), ID_MA, ID_TE, ID_WO, ID_LA, ID_LS)) { RNA_warning("Expected ID of type material, texture, lamp, world or line style"); return; } @@ -1531,7 +1514,15 @@ static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand row = uiLayoutRow(split, false); - uiItemR(row, &ptr, "interpolation", 0, "", ICON_NONE); + uiBlockBeginAlign(block); + uiItemR(row, &ptr, "color_mode", 0, "", ICON_NONE); + if (ELEM(coba->color_mode, COLBAND_BLEND_HSV, COLBAND_BLEND_HSL)) { + uiItemR(row, &ptr, "hue_interpolation", 0, "", ICON_NONE); + } + else { /* COLBAND_BLEND_RGB */ + uiItemR(row, &ptr, "interpolation", 0, "", ICON_NONE); + } + uiBlockEndAlign(block); row = uiLayoutRow(layout, false); @@ -1569,7 +1560,7 @@ static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand uiDefButS(block, NUM, 0, "", 0, 0, 5.0f * UI_UNIT_X, UI_UNIT_Y, &coba->cur, 0.0, (float)(MAX2(0, coba->tot - 1)), 0, 0, TIP_("Choose active color stop")); row = uiLayoutRow(subsplit, false); - uiItemR(row, &ptr, "position", 0, IFACE_("Pos"), ICON_NONE); + uiItemR(row, &ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE); bt = block->buttons.last; uiButSetFunc(bt, colorband_update_cb, bt, coba); @@ -1656,8 +1647,7 @@ static uiBlock *icon_view_menu(bContext *C, ARegion *ar, void *arg_litem) uiBoundsBlock(block, 0.3f * U.widget_unit); uiBlockSetDirection(block, UI_TOP); - uiEndBlock(C, block); - + if (free) { MEM_freeN(item); } @@ -1957,10 +1947,11 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event) curvemapping_changed(cumap, false); break; } + ED_undo_push(C, "CurveMap tools"); ED_region_tag_redraw(CTX_wm_region(C)); } -static uiBlock *curvemap_tools_func(bContext *C, ARegion *ar, void *cumap_v) +static uiBlock *curvemap_tools_posslope_func(bContext *C, ARegion *ar, void *cumap_v) { uiBlock *block; short yco = 0, menuwidth = 10 * UI_UNIT_X; @@ -1988,6 +1979,34 @@ static uiBlock *curvemap_tools_func(bContext *C, ARegion *ar, void *cumap_v) return block; } +static uiBlock *curvemap_tools_negslope_func(bContext *C, ARegion *ar, void *cumap_v) +{ + uiBlock *block; + short yco = 0, menuwidth = 10 * UI_UNIT_X; + + block = uiBeginBlock(C, ar, __func__, UI_EMBOSS); + uiBlockSetButmFunc(block, curvemap_tools_dofunc, cumap_v); + + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, IFACE_("Reset View"), 0, yco -= UI_UNIT_Y, + menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_RESET_VIEW, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, IFACE_("Vector Handle"), 0, yco -= UI_UNIT_Y, + menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_VECTOR, ""); + uiDefIconTextBut(block, BUTM, 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, BUTM, 1, ICON_BLANK1, IFACE_("Extend Horizontal"), 0, yco -= UI_UNIT_Y, + menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_EXTEND_HOZ, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, IFACE_("Extend Extrapolated"), 0, yco -= UI_UNIT_Y, + menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_EXTEND_EXP, ""); + uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, IFACE_("Reset Curve"), 0, yco -= UI_UNIT_Y, + menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_RESET_NEG, ""); + + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 50); + + uiEndBlock(C, block); + return block; +} + static uiBlock *curvemap_brush_tools_func(bContext *C, ARegion *ar, void *cumap_v) { uiBlock *block; @@ -2044,7 +2063,7 @@ static void curvemap_buttons_reset(bContext *C, void *cb_v, void *cumap_v) /* still unsure how this call evolves... we use labeltype for defining what curve-channels to show */ static void curvemap_buttons_layout(uiLayout *layout, PointerRNA *ptr, char labeltype, int levels, - int brush, RNAUpdateCb *cb) + int brush, int neg_slope, RNAUpdateCb *cb) { CurveMapping *cumap = ptr->data; CurveMap *cm = &cumap->cm[cumap->cur]; @@ -2138,8 +2157,12 @@ static void curvemap_buttons_layout(uiLayout *layout, PointerRNA *ptr, char labe if (brush) bt = uiDefIconBlockBut(block, curvemap_brush_tools_func, cumap, 0, ICON_MODIFIER, 0, 0, dx, dx, TIP_("Tools")); + else if (neg_slope) + bt = uiDefIconBlockBut(block, curvemap_tools_negslope_func, cumap, 0, ICON_MODIFIER, + 0, 0, dx, dx, TIP_("Tools")); else - bt = uiDefIconBlockBut(block, curvemap_tools_func, cumap, 0, ICON_MODIFIER, 0, 0, dx, dx, TIP_("Tools")); + bt = uiDefIconBlockBut(block, curvemap_tools_posslope_func, cumap, 0, ICON_MODIFIER, + 0, 0, dx, dx, TIP_("Tools")); uiButSetNFunc(bt, rna_update_cb, MEM_dupallocN(cb), NULL); @@ -2201,7 +2224,8 @@ static void curvemap_buttons_layout(uiLayout *layout, PointerRNA *ptr, char labe uiBlockSetNFunc(block, NULL, NULL, NULL); } -void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, const char *propname, int type, int levels, int brush) +void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, const char *propname, int type, + int levels, int brush, int neg_slope) { RNAUpdateCb *cb; PropertyRNA *prop = RNA_struct_find_property(ptr, propname); @@ -2232,7 +2256,7 @@ void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, const char *propn id = cptr.id.data; uiBlockSetButLock(block, (id && id->lib), ERROR_LIBDATA_MESSAGE); - curvemap_buttons_layout(layout, &cptr, type, levels, brush, cb); + curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, cb); uiBlockClearButLock(block); @@ -2336,6 +2360,60 @@ void uiTemplateColorPicker(uiLayout *layout, PointerRNA *ptr, const char *propna } } +void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, int UNUSED(colors)) +{ + PropertyRNA *prop = RNA_struct_find_property(ptr, propname); + PointerRNA cptr; + Palette *palette; + PaletteColor *color; + uiBlock *block; + uiLayout *col; + int row_cols = 0, col_id = 0; + int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); + + if (!prop) { + RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); + return; + } + + cptr = RNA_property_pointer_get(ptr, prop); + if (!cptr.data || !RNA_struct_is_a(cptr.type, &RNA_Palette)) + return; + + block = uiLayoutGetBlock(layout); + + palette = cptr.data; + + /* first delete any pending colors */ + BKE_palette_cleanup(palette); + + color = palette->colors.first; + + col = uiLayoutColumn(layout, true); + uiLayoutRow(col, true); + uiDefIconButO(block, BUT, "PALETTE_OT_color_add", WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); + uiDefIconButO(block, BUT, "PALETTE_OT_color_delete", WM_OP_INVOKE_DEFAULT, ICON_ZOOMOUT, 0, 0, UI_UNIT_X, UI_UNIT_Y, NULL); + + col = uiLayoutColumn(layout, true); + uiLayoutRow(col, true); + + for (; color; color = color->next) { + PointerRNA ptr; + + if (row_cols >= cols_per_row) { + uiLayoutRow(col, true); + row_cols = 0; + } + + RNA_pointer_create(&palette->id, &RNA_PaletteColor, color, &ptr); + uiDefButR(block, COLOR, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y, &ptr, "color", -1, 0.0, 1.0, + UI_PALETTE_COLOR, col_id, ""); + row_cols++; + col_id++; + } +} + + /********************* Layer Buttons Template ************************/ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) @@ -2566,8 +2644,8 @@ static void uilist_filter_items_default(struct uiList *ui_list, struct bContext const char *filter_raw = ui_list->filter_byname; char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL; - bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; - bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_ALPHA) != 0; + const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; + const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_ALPHA) != 0; int len = RNA_property_collection_length(dataptr, prop); dyn_data->items_shown = dyn_data->items_len = len; @@ -2851,11 +2929,11 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co ui_list = MEM_callocN(sizeof(uiList), "uiList"); BLI_strncpy(ui_list->list_id, ui_list_id, sizeof(ui_list->list_id)); BLI_addtail(&ar->ui_lists, ui_list); + ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ } if (!ui_list->dyn_data) { ui_list->dyn_data = MEM_callocN(sizeof(uiListDyn), "uiList.dyn_data"); - ui_list->list_grip = -UI_LIST_AUTO_SIZE_THRESHOLD; /* Force auto size by default. */ } dyn_data = ui_list->dyn_data; @@ -2876,8 +2954,8 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co /* Filter list items! (not for compact layout, though) */ if (dataptr->data && prop) { - int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const bool order_reverse = (ui_list->filter_sort_flag & UILST_FLT_SORT_REVERSE) != 0; int items_shown, idx = 0; #if 0 int prev_ii = -1, prev_i; @@ -3161,7 +3239,7 @@ static void operator_call_cb(bContext *C, void *UNUSED(arg1), void *arg2) wmOperatorType *ot = arg2; if (ot) - WM_operator_name_call(C, ot->idname, WM_OP_INVOKE_DEFAULT, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, NULL); } static void operator_search_cb(const bContext *C, void *UNUSED(arg), const char *str, uiSearchItems *items) @@ -3416,13 +3494,9 @@ static void template_keymap_item_properties(uiLayout *layout, const char *title, RNA_STRUCT_BEGIN (ptr, prop) { - int flag = RNA_property_flag(prop); - bool is_set = RNA_property_is_set(ptr, prop); + const bool is_set = RNA_property_is_set(ptr, prop); uiBut *but; - if (flag & PROP_HIDDEN) - continue; - /* recurse for nested properties */ if (RNA_property_type(prop) == PROP_POINTER) { PointerRNA propptr = RNA_property_pointer_get(ptr, prop); @@ -3516,7 +3590,7 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout, bContext *UNUSED(C), P col = uiLayoutColumn(layout, false); row = uiLayoutRow(col, false); - uiItemR(row, &view_transform_ptr, "view_transform", UI_ITEM_R_EXPAND, IFACE_("View"), ICON_NONE); + uiItemR(row, &view_transform_ptr, "view_transform", 0, IFACE_("View"), ICON_NONE); col = uiLayoutColumn(layout, false); uiItemR(col, &view_transform_ptr, "exposure", 0, NULL, ICON_NONE); @@ -3527,7 +3601,7 @@ void uiTemplateColormanagedViewSettings(uiLayout *layout, bContext *UNUSED(C), P col = uiLayoutColumn(layout, false); uiItemR(col, &view_transform_ptr, "use_curve_mapping", 0, NULL, ICON_NONE); if (view_settings->flag & COLORMANAGE_VIEW_USE_CURVES) - uiTemplateCurveMapping(col, &view_transform_ptr, "curve_mapping", 'c', true, 0); + uiTemplateCurveMapping(col, &view_transform_ptr, "curve_mapping", 'c', true, false, false); } /********************************* Component Menu *************************************/ @@ -3551,8 +3625,7 @@ static uiBlock *component_menu(bContext *C, ARegion *ar, void *args_v) uiItemR(layout, &args->ptr, args->propname, UI_ITEM_R_EXPAND, "", ICON_NONE); uiBoundsBlock(block, 6); - uiBlockSetDirection(block, UI_DOWN); - uiEndBlock(C, block); + uiBlockSetDirection(block, UI_DOWN); return block; } diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 2c958c5028a..008ea84b607 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -151,7 +151,7 @@ int uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, const char *name; int tot = 0; - assert(ELEM3(label_align, '\0', 'H', 'V')); + assert(ELEM(label_align, '\0', 'H', 'V')); RNA_STRUCT_BEGIN (ptr, prop) { @@ -253,7 +253,7 @@ int uiFloatPrecisionCalc(int prec, double value) static const double max_pow = 10000000.0; /* pow(10, UI_PRECISION_FLOAT_MAX) */ BLI_assert(prec <= UI_PRECISION_FLOAT_MAX); - BLI_assert(pow10_neg[prec] == pow(10, -prec)); + BLI_assert(fabs(pow10_neg[prec] - pow(10, -prec)) < 1e-16); /* check on the number of decimal places need to display the number, this is so 0.00001 is not displayed as 0.00, * _but_, this is only for small values si 10.0001 will not get the same treatment. diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index b25aac785a7..b7e61400cfa 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -33,6 +33,7 @@ #include <string.h> #include <assert.h> +#include "DNA_brush_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" @@ -477,20 +478,24 @@ static void round_box_edges(uiWidgetBase *wt, int roundboxalign, const rcti *rec /* based on button rect, return scaled array of triangles */ -static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void widget_draw_tria_ex( + uiWidgetTrias *tria, const rcti *rect, float triasize, char where, + /* input data */ + const float verts[][2], const int verts_tot, + const unsigned int tris[][3], const int tris_tot) { float centx, centy, sizex, sizey, minsize; int a, i1 = 0, i2 = 1; - + minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)); - + /* center position and size */ - centx = (float)rect->xmin + 0.5f * minsize; + centx = (float)rect->xmin + 0.4f * minsize; centy = (float)rect->ymin + 0.5f * minsize; sizex = sizey = -0.5f * triasize * minsize; if (where == 'r') { - centx = (float)rect->xmax - 0.5f * minsize; + centx = (float)rect->xmax - 0.4f * minsize; sizex = -sizex; } else if (where == 't') { @@ -502,49 +507,30 @@ static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasiz sizex = -sizex; i2 = 0; i1 = 1; } - - for (a = 0; a < 3; a++) { - tria->vec[a][0] = sizex * num_tria_vert[a][i1] + centx; - tria->vec[a][1] = sizey * num_tria_vert[a][i2] + centy; + + for (a = 0; a < verts_tot; a++) { + tria->vec[a][0] = sizex * verts[a][i1] + centx; + tria->vec[a][1] = sizey * verts[a][i2] + centy; } - - tria->tot = 1; - tria->index = num_tria_face; + + tria->tot = tris_tot; + tria->index = tris; } -static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +static void widget_num_tria(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) { - float centx, centy, sizex, sizey, minsize; - int a, i1 = 0, i2 = 1; - - minsize = min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)); - - /* center position and size */ - centx = (float)rect->xmin + 0.5f * minsize; - centy = (float)rect->ymin + 0.5f * minsize; - sizex = sizey = -0.5f * triasize * minsize; + widget_draw_tria_ex( + tria, rect, triasize, where, + num_tria_vert, ARRAY_SIZE(num_tria_vert), + num_tria_face, ARRAY_SIZE(num_tria_face)); +} - if (where == 'r') { - centx = (float)rect->xmax - 0.5f * minsize; - sizex = -sizex; - } - else if (where == 't') { - centy = (float)rect->ymax - 0.5f * minsize; - sizey = -sizey; - i2 = 0; i1 = 1; - } - else if (where == 'b') { - sizex = -sizex; - i2 = 0; i1 = 1; - } - - for (a = 0; a < 16; a++) { - tria->vec[a][0] = sizex * scroll_circle_vert[a][i1] + centx; - tria->vec[a][1] = sizey * scroll_circle_vert[a][i2] + centy; - } - - tria->tot = 14; - tria->index = scroll_circle_face; +static void widget_scroll_circle(uiWidgetTrias *tria, const rcti *rect, float triasize, char where) +{ + widget_draw_tria_ex( + tria, rect, triasize, where, + scroll_circle_vert, ARRAY_SIZE(scroll_circle_vert), + scroll_circle_face, ARRAY_SIZE(scroll_circle_face)); } static void widget_trias_draw(uiWidgetTrias *tria) @@ -863,7 +849,7 @@ static void widget_draw_icon(const uiBut *but, BIFIconID icon, float alpha, cons height = ICON_DEFAULT_HEIGHT / aspect; /* calculate blend color */ - if (ELEM4(but->type, TOG, ROW, TOGN, LISTROW)) { + if (ELEM(but->type, TOG, ROW, TOGN, LISTROW)) { if (but->flag & UI_SELECT) {} else if (but->flag & UI_ACTIVE) {} else alpha = 0.5f; @@ -936,45 +922,12 @@ static void ui_text_clip_give_next_off(uiBut *but, const char *str) but->ofs += bytes; } -/** - * Cut off the start of the text to fit into the width of \a rect - * - * \note Sets but->ofs to make sure text is correctly visible. - * \note Clips right in some cases, this function could be cleaned up. - */ -static void ui_text_clip_left(uiFontStyle *fstyle, uiBut *but, const rcti *rect) -{ - /* We are not supposed to use labels with that clipping, so we can always apply margins. */ - const int border = (int)(UI_TEXT_CLIP_MARGIN + 0.5f); - const int okwidth = max_ii(BLI_rcti_size_x(rect) - border, 0); - - /* need to set this first */ - uiStyleFontSet(fstyle); - - if (fstyle->kerning == 1) /* for BLF_width */ - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - - but->ofs = 0; - but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr, sizeof(but->drawstr)); - - if ((okwidth > 0.0f) && (but->strwidth > okwidth)) { - float strwidth; - but->ofs = BLF_width_to_rstrlen(fstyle->uifont_id, but->drawstr, - sizeof(but->drawstr), okwidth, &strwidth); - but->strwidth = strwidth; - } - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } -} - /* Helper. * This func assumes things like kerning handling have already been handled! * Return the length of modified (right-clipped + ellipsis) string. */ static void ui_text_clip_right_ex(uiFontStyle *fstyle, char *str, const size_t max_len, const float okwidth, - const char *sep, const int sep_len, const float sep_strwidth) + const char *sep, const int sep_len, const float sep_strwidth, size_t *r_final_len) { float tmp; int l_end; @@ -987,19 +940,27 @@ static void ui_text_clip_right_ex(uiFontStyle *fstyle, char *str, const size_t m if (sep_strwidth / okwidth > 0.2f) { l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth, &tmp); str[l_end] = '\0'; + if (r_final_len) { + *r_final_len = (size_t)l_end; + } } else { l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, okwidth - sep_strwidth, &tmp); memcpy(str + l_end, sep, sep_len + 1); /* +1 for trailing '\0'. */ + if (r_final_len) { + *r_final_len = (size_t)(l_end + sep_len); + } } } /** * Cut off the middle of the text to fit into the given width. * Note in case this middle clipping would just remove a few chars, it rather clips right, which is more readable. + * If rpart_sep is not Null, the part of str starting to first occurrence of rpart_sep is preserved at all cost (useful + * for strings with shortcuts, like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O'). */ -static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float okwidth, const float minwidth, - const size_t max_len) +static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, float okwidth, const float minwidth, + const size_t max_len, const char *rpart_sep) { float strwidth; @@ -1018,37 +979,76 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float /* utf8 ellipsis '...', some compilers complain */ const char sep[] = {0xe2, 0x80, 0xa6, 0x0}; const int sep_len = sizeof(sep) - 1; + const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1); + float parts_strwidth; size_t l_end; - const float sep_strwidth = BLF_width(fstyle->uifont_id, sep, sep_len + 1); - const float parts_strwidth = ((float)okwidth - sep_strwidth) / 2.0f; + char *rpart = NULL, rpart_buf[UI_MAX_DRAW_STR]; + float rpart_width = 0.0f; + size_t rpart_len = 0; + size_t final_lpart_len; + + if (rpart_sep) { + rpart = strstr(str, rpart_sep); + + if (rpart) { + rpart_len = strlen(rpart); + rpart_width = BLF_width(fstyle->uifont_id, rpart, rpart_len); + okwidth -= rpart_width; + strwidth -= rpart_width; + + if (okwidth < 0.0f) { + /* Not enough place for actual label, just display protected right part. + * Here just for safety, should never happen in real life! */ + memmove(str, rpart, rpart_len + 1); + rpart = NULL; + okwidth += rpart_width; + strwidth = rpart_width; + } + } + } + + parts_strwidth = (okwidth - sep_strwidth) / 2.0f; + + if (rpart) { + strcpy(rpart_buf, rpart); + *rpart = '\0'; + rpart = rpart_buf; + } - if (min_ff(parts_strwidth, strwidth - okwidth) < minwidth) { + l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width); + if (l_end < 10 || min_ff(parts_strwidth, strwidth - okwidth) < minwidth) { /* If we really have no place, or we would clip a very small piece of string in the middle, * only show start of string. */ - ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth); + ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len); } else { size_t r_offset, r_len; - l_end = BLF_width_to_strlen(fstyle->uifont_id, str, max_len, parts_strwidth, &strwidth); - r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &strwidth); - r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'... */ + r_offset = BLF_width_to_rstrlen(fstyle->uifont_id, str, max_len, parts_strwidth, &rpart_width); + r_len = strlen(str + r_offset) + 1; /* +1 for the trailing '\0'. */ - if (l_end + sep_len + r_len > max_len) { + if (l_end + sep_len + r_len + rpart_len > max_len) { /* Corner case, the str already takes all available mem, and the ellipsis chars would actually * add more chars... * Better to just trim one or two letters to the right in this case... * Note: with a single-char ellipsis, this should never happen! But better be safe here... */ - ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth); + ui_text_clip_right_ex(fstyle, str, max_len, okwidth, sep, sep_len, sep_strwidth, &final_lpart_len); } else { memmove(str + l_end + sep_len, str + r_offset, r_len); memcpy(str + l_end, sep, sep_len); + final_lpart_len = (size_t)(l_end + sep_len + r_len - 1); /* -1 to remove trailing '\0'! */ } } + + if (rpart) { + /* Add back preserved right part to our shorten str. */ + memcpy(str + final_lpart_len, rpart, rpart_len + 1); /* +1 for trailing '\0'. */ + } + strwidth = BLF_width(fstyle->uifont_id, str, max_len); } @@ -1059,6 +1059,9 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, const float return strwidth; } +/** + * Wrapper around ui_text_clip_middle_ex. + */ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect) { /* No margin for labels! */ @@ -1068,7 +1071,23 @@ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rec const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; but->ofs = 0; - but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len); + but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, NULL); +} + +/** + * Like ui_text_clip_middle(), but protect/preserve at all cost the right part of the string after sep. + * Useful for strings with shortcuts (like 'AVeryLongFooBarLabelForMenuEntry|Ctrl O' -> 'AVeryLong...MenuEntry|Ctrl O'). + */ +static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, const rcti *rect, const char *rsep) +{ + /* No margin for labels! */ + const int border = ELEM(but->type, LABEL, MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); + const float okwidth = (float)max_ii(BLI_rcti_size_x(rect) - border, 0); + const size_t max_len = sizeof(but->drawstr); + const float minwidth = (float)(UI_DPI_ICON_SIZE) / but->block->aspect * 2.0f; + + but->ofs = 0; + but->strwidth = ui_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep); } /** @@ -1248,40 +1267,42 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b /* text button selection and cursor */ if (but->editstr && but->pos != -1) { - short t = 0, pos = 0; - short selsta_tmp, selend_tmp, selsta_draw, selwidth_draw; + /* text button selection */ if ((but->selend - but->selsta) > 0) { - /* text button selection */ - selsta_tmp = but->selsta; - selend_tmp = but->selend; + int selsta_draw, selwidth_draw; if (drawstr[0] != 0) { if (but->selsta >= but->ofs) { - selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, selsta_tmp - but->ofs); + selsta_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selsta - but->ofs); } else { selsta_draw = 0; } - selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, selend_tmp - but->ofs); + selwidth_draw = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->selend - but->ofs); glColor4ubv((unsigned char *)wcol->item); - glRects(rect->xmin + selsta_draw, rect->ymin + 2, rect->xmin + selwidth_draw, rect->ymax - 2); + glRecti(rect->xmin + selsta_draw, + rect->ymin + 2, + min_ii(rect->xmin + selwidth_draw, rect->xmax - 2), + rect->ymax - 2); } } - else { - /* text cursor */ - pos = but->pos; - if (pos >= but->ofs) { - if (drawstr[0] != 0) { - t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, pos - but->ofs); - } - glColor3f(0.20, 0.6, 0.9); - glRects(rect->xmin + t, rect->ymin + 2, rect->xmin + t + 2, rect->ymax - 2); + /* text cursor */ + if (but->pos >= but->ofs) { + int t; + if (drawstr[0] != 0) { + t = BLF_width(fstyle->uifont_id, drawstr + but->ofs, but->pos - but->ofs); + } + else { + t = 0; } + + glColor3f(0.20, 0.6, 0.9); + glRecti(rect->xmin + t, rect->ymin + 2, rect->xmin + t + 2, rect->ymax - 2); } } @@ -1443,14 +1464,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB else if (ELEM(but->type, NUM, NUMSLI)) { ui_text_clip_right_label(fstyle, but, rect); } -#if 0 - /* Special hack for non-embossed TEX buttons in uiList (we want them to behave as much as possible as labels). */ - else if ((but->type == TEX) && (but->flag & UI_BUT_LIST_ITEM) && (but->dt & UI_EMBOSSN)) { - but->ofs = 0; - } -#endif else if ((but->block->flag & UI_BLOCK_LOOP) && (but->type == BUT)) { - ui_text_clip_left(fstyle, but, rect); + /* Clip middle, but protect in all case right part containing the shortcut, if any. */ + ui_text_clip_middle_protect_right(fstyle, but, rect, "|"); } else { ui_text_clip_middle(fstyle, but, rect); @@ -1608,6 +1624,21 @@ static struct uiWidgetColors wcol_menu_back = { 25, -20 }; +/* pie menus */ +static struct uiWidgetColors wcol_pie_menu = { + {10, 10, 10, 200}, + {25, 25, 25, 230}, + {140, 140, 140, 255}, + {45, 45, 45, 230}, + + {160, 160, 160, 255}, + {255, 255, 255, 255}, + + 1, + 10, -10 +}; + + /* tooltip color */ static struct uiWidgetColors wcol_tooltip = { {0, 0, 0, 255}, @@ -1755,6 +1786,7 @@ void ui_widget_color_init(ThemeUI *tui) tui->wcol_menu = wcol_menu; tui->wcol_pulldown = wcol_pulldown; tui->wcol_menu_back = wcol_menu_back; + tui->wcol_pie_menu = wcol_pie_menu; tui->wcol_tooltip = wcol_tooltip; tui->wcol_menu_item = wcol_menu_item; tui->wcol_box = wcol_box; @@ -1826,7 +1858,9 @@ static void widget_state(uiWidgetType *wt, int state) if (state & UI_BUT_DRAG_MULTI) { /* the button isn't SELECT but we're editing this so draw with sel color */ - widget_state_blend(wt->wcol.inner, wt->wcol.inner_sel, 1.0f); + copy_v4_v4_char(wt->wcol.inner, wt->wcol.inner_sel); + SWAP(short, wt->wcol.shadetop, wt->wcol.shadedown); + widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.85f); } if (state & UI_BUT_NODE_ACTIVE) { @@ -1901,6 +1935,34 @@ static void widget_state_pulldown(uiWidgetType *wt, int state) copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); } +/* special case, pie menu items */ +static void widget_state_pie_menu_item(uiWidgetType *wt, int state) +{ + wt->wcol = *(wt->wcol_theme); + + /* active and disabled (not so common) */ + if ((state & UI_BUT_DISABLED) && (state & UI_ACTIVE)) { + widget_state_blend(wt->wcol.text, wt->wcol.text_sel, 0.5f); + /* draw the backdrop at low alpha, helps navigating with keys + * when disabled items are active */ + copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); + wt->wcol.inner[3] = 64; + } + /* regular disabled */ + else if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) { + widget_state_blend(wt->wcol.text, wt->wcol.inner, 0.5f); + } + /* regular active */ + else if (state & UI_SELECT) { + copy_v4_v4_char(wt->wcol.outline, wt->wcol.inner_sel); + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } + else if (state & UI_ACTIVE) { + copy_v4_v4_char(wt->wcol.inner, wt->wcol.item); + copy_v3_v3_char(wt->wcol.text, wt->wcol.text_sel); + } +} + /* special case, menu items */ static void widget_state_menu_item(uiWidgetType *wt, int state) { @@ -2110,8 +2172,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * glVertex2f(centx, centy); for (a = 0; a <= tot; a++, ang += radstep) { - float si = sin(ang); - float co = cos(ang); + float si = sinf(ang); + float co = cosf(ang); ui_hsvcircle_vals_from_pos(hsv, hsv + 1, rect, centx + co * radius, centy + si * radius); @@ -2836,6 +2898,24 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat widgetbase_draw(&wtb, wcol); + if (but->a1 == UI_PALETTE_COLOR && ((Palette *)but->rnapoin.id.data)->active_color == (int)but->a2) { + float width = rect->xmax - rect->xmin; + float height = rect->ymax - rect->ymin; + /* find color luminance and change it slightly */ + float bw = rgb_to_bw(col); + + if (bw > 0.5) + bw -= 0.5; + else + bw += 0.5; + + glColor4f(bw, bw, bw, 1.0); + glBegin(GL_TRIANGLES); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.9f * height); + glVertex2f(rect->xmin + 0.1f * width, rect->ymin + 0.5f * height); + glVertex2f(rect->xmin + 0.5f * width, rect->ymin + 0.9f * height); + glEnd(); + } } static void widget_normal(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) @@ -2972,6 +3052,29 @@ static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta widgetbase_draw(&wtb, wcol); } +static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) +{ + uiWidgetBase wtb; + float rad; + float fac = but->block->pie_data.alphafac; + + widget_init(&wtb); + + wtb.emboss = 0; + + rad = 0.5f * BLI_rcti_size_y(rect); + round_box_edges(&wtb, UI_CNR_ALL, rect, rad); + + wcol->inner[3] *= fac; + wcol->inner_sel[3] *= fac; + wcol->item[3] *= fac; + wcol->text[3] *= fac; + wcol->text_sel[3] *= fac; + wcol->outline[3] *= fac; + + widgetbase_draw(&wtb, wcol); +} + static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; @@ -3290,6 +3393,12 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.wcol_theme = &btheme->tui.wcol_progress; wt.custom = widget_progressbar; break; + + case UI_WTYPE_MENU_ITEM_RADIAL: + wt.wcol_theme = &btheme->tui.wcol_pie_menu; + wt.custom = widget_menu_radial_itembut; + wt.state = widget_state_pie_menu_item; + break; } return &wt; @@ -3396,6 +3505,9 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct /* "nothing" */ wt = widget_type(UI_WTYPE_ICON); } + else if (but->dt == UI_EMBOSSR) { + wt = widget_type(UI_WTYPE_MENU_ITEM_RADIAL); + } else { switch (but->type) { @@ -3648,6 +3760,133 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) } } +static void draw_disk_shaded( + float start, float angle, + float radius_int, float radius_ext, int subd, + const char col1[4], const char col2[4], + bool shaded) +{ + const float radius_ext_scale = (0.5f / radius_ext); /* 1 / (2 * radius_ext) */ + int i; + + float s, c; + float y1, y2; + float fac; + unsigned char r_col[4]; + + glBegin(GL_TRIANGLE_STRIP); + + s = sinf(start); + c = cosf(start); + + y1 = s * radius_int; + y2 = s * radius_ext; + + if (shaded) { + fac = (y1 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + + glVertex2f(c * radius_int, s * radius_int); + + if (shaded) { + fac = (y2 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_ext, s * radius_ext); + + for (i = 1; i < subd; i++) { + float a; + + a = start + ((i) / (float)(subd - 1)) * angle; + s = sinf(a); + c = cosf(a); + y1 = s * radius_int; + y2 = s * radius_ext; + + if (shaded) { + fac = (y1 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_int, s * radius_int); + + if (shaded) { + fac = (y2 + radius_ext) * radius_ext_scale; + round_box_shade_col4_r(r_col, col1, col2, fac); + + glColor4ubv(r_col); + } + glVertex2f(c * radius_ext, s * radius_ext); + } + glEnd(); + +} + +void ui_draw_pie_center(uiBlock *block) +{ + bTheme *btheme = UI_GetTheme(); + float cx = block->pie_data.pie_center_spawned[0]; + float cy = block->pie_data.pie_center_spawned[1]; + + float *pie_dir = block->pie_data.pie_dir; + + float pie_radius_internal = U.pixelsize * U.pie_menu_threshold; + float pie_radius_external = U.pixelsize * (U.pie_menu_threshold + 7.0f); + + int subd = 40; + + float angle = atan2f(pie_dir[1], pie_dir[0]); + float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? ((float)M_PI / 2.0f) : ((float)M_PI / 4.0f); + + glPushMatrix(); + glTranslatef(cx, cy, 0.0f); + + glEnable(GL_BLEND); + if (btheme->tui.wcol_pie_menu.shaded) { + char col1[4], col2[4]; + shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown); + draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, col1, col2, true); + } + else { + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner); + draw_disk_shaded(0.0f, (float)(M_PI * 2.0), pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + } + + if (!(block->pie_data.flags & UI_PIE_INVALID_DIR)) { + if (btheme->tui.wcol_pie_menu.shaded) { + char col1[4], col2[4]; + shadecolors4(col1, col2, btheme->tui.wcol_pie_menu.inner_sel, btheme->tui.wcol_pie_menu.shadetop, btheme->tui.wcol_pie_menu.shadedown); + draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, col1, col2, true); + } + else { + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.inner_sel); + draw_disk_shaded(angle - range / 2.0f, range, pie_radius_internal, pie_radius_external, subd, NULL, NULL, false); + } + } + + glColor4ubv((GLubyte *)btheme->tui.wcol_pie_menu.outline); + glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_internal, subd); + glutil_draw_lined_arc(0.0f, (float)M_PI * 2.0f, pie_radius_external, subd); + + if (U.pie_menu_confirm > 0 && !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) { + float pie_confirm_radius = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm); + float pie_confirm_external = U.pixelsize * (pie_radius_internal + U.pie_menu_confirm + 7.0f); + + glColor4ub(btheme->tui.wcol_pie_menu.text_sel[0], btheme->tui.wcol_pie_menu.text_sel[1], btheme->tui.wcol_pie_menu.text_sel[2], 64); + draw_disk_shaded(angle - range / 2.0f, range, pie_confirm_radius, pie_confirm_external, subd, NULL, NULL, false); + } + + glDisable(GL_BLEND); + glPopMatrix(); +} + + uiWidgetColors *ui_tooltip_get_theme(void) { uiWidgetType *wt = widget_type(UI_WTYPE_TOOLTIP); @@ -3725,7 +3964,7 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic const float minwidth = (float)(UI_DPI_ICON_SIZE); BLI_strncpy(drawstr, name, sizeof(drawstr)); - ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len); + ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, NULL); glColor4ubv((unsigned char *)wt->wcol.text); uiStyleFontDraw(fstyle, rect, drawstr); @@ -3800,7 +4039,7 @@ void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int const float minwidth = (float)(UI_DPI_ICON_SIZE); BLI_strncpy(drawstr, name, sizeof(drawstr)); - ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len); + ui_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, NULL); glColor4ubv((unsigned char *)wt->wcol.text); uiStyleFontDraw(fstyle, &trect, drawstr); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 16550327a5e..bcd85333709 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -36,11 +36,10 @@ #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" -#include "DNA_userdef_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_userdef_types.h" #include "DNA_windowmanager_types.h" -#include "DNA_mesh_types.h" /* init_userdef_factory */ #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -51,7 +50,6 @@ #include "BKE_main.h" #include "BKE_texture.h" - #include "BIF_gl.h" #include "UI_interface.h" @@ -92,6 +90,7 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo /* ensure we're not getting a color after running BKE_userdef_free */ BLI_assert(BLI_findindex(&U.themes, theme_active) != -1); + BLI_assert(colorid != TH_UNDEFINED); if (btheme) { @@ -272,6 +271,8 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->view_overlay; break; case TH_WIRE: cp = ts->wire; break; + case TH_WIRE_INNER: + cp = ts->syntaxr; break; case TH_WIRE_EDIT: cp = ts->wire_edit; break; case TH_LAMP: @@ -336,6 +337,8 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->normal; break; case TH_VNORMAL: cp = ts->vertex_normal; break; + case TH_LNORMAL: + cp = ts->loop_normal; break; case TH_BONE_SOLID: cp = ts->bone_solid; break; case TH_BONE_POSE: @@ -536,6 +539,13 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->preview_stitch_active; break; + case TH_PAINT_CURVE_HANDLE: + cp = ts->paint_curve_handle; + break; + case TH_PAINT_CURVE_PIVOT: + cp = ts->paint_curve_pivot; + break; + case TH_UV_OTHERS: cp = ts->uv_others; break; @@ -773,6 +783,8 @@ static void ui_theme_space_init_handles_color(ThemeSpace *theme_space) rgba_char_args_set(theme_space->handle_sel_auto, 0xf0, 0xff, 0x40, 255); rgba_char_args_set(theme_space->handle_sel_vect, 0x40, 0xc0, 0x30, 255); rgba_char_args_set(theme_space->handle_sel_align, 0xf0, 0x90, 0xa0, 255); + rgba_char_args_set(theme_space->handle_vertex, 0x00, 0x00, 0x00, 0xff); + rgba_char_args_set(theme_space->handle_vertex_select, 0xff, 0xff, 0, 0xff); rgba_char_args_set(theme_space->act_spline, 0xdb, 0x25, 0x12, 255); } @@ -858,6 +870,7 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tv3d.face_select, 255, 133, 0, 60); rgba_char_args_set(btheme->tv3d.normal, 0x22, 0xDD, 0xDD, 255); rgba_char_args_set(btheme->tv3d.vertex_normal, 0x23, 0x61, 0xDD, 255); + rgba_char_args_set(btheme->tv3d.loop_normal, 0xDD, 0x23, 0xDD, 255); rgba_char_args_set(btheme->tv3d.face_dot, 255, 133, 0, 255); rgba_char_args_set(btheme->tv3d.editmesh_active, 255, 255, 255, 128); rgba_char_args_set_fl(btheme->tv3d.edge_crease, 0.8, 0, 0.6, 1.0); @@ -870,6 +883,8 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tv3d.title, 0, 0, 0, 255); rgba_char_args_set(btheme->tv3d.freestyle_edge_mark, 0x7f, 0xff, 0x7f, 255); rgba_char_args_set(btheme->tv3d.freestyle_face_mark, 0x7f, 0xff, 0x7f, 51); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); btheme->tv3d.facedot_size = 4; @@ -1091,6 +1106,7 @@ void ui_theme_init_default(void) /* space node, re-uses syntax and console color storage */ btheme->tnode = btheme->tv3d; + rgba_char_args_set(btheme->tnode.syntaxr, 115, 115, 115, 255); /* wire inner color */ rgba_char_args_set(btheme->tnode.edge_select, 255, 255, 255, 255); /* wire selected */ rgba_char_args_set(btheme->tnode.syntaxl, 155, 155, 155, 160); /* TH_NODE, backdrop */ rgba_char_args_set(btheme->tnode.syntaxn, 100, 100, 100, 255); /* in */ @@ -1128,8 +1144,6 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->tclip.path_after, 0x00, 0x00, 0xff, 255); rgba_char_args_set(btheme->tclip.grid, 0x5e, 0x5e, 0x5e, 255); rgba_char_args_set(btheme->tclip.cframe, 0x60, 0xc0, 0x40, 255); - rgba_char_args_set(btheme->tclip.handle_vertex, 0x00, 0x00, 0x00, 0xff); - rgba_char_args_set(btheme->tclip.handle_vertex_select, 0xff, 0xff, 0, 0xff); rgba_char_args_set(btheme->tclip.list, 0x66, 0x66, 0x66, 0xff); rgba_char_args_set(btheme->tclip.strip, 0x0c, 0x0a, 0x0a, 0x80); rgba_char_args_set(btheme->tclip.strip_select, 0xff, 0x8c, 0x00, 0xff); @@ -1224,21 +1238,25 @@ void UI_ThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset) glColor4ub(r, g, b, a); } -/* blend between to theme colors, and set it */ -void UI_ThemeColorBlend(int colorid1, int colorid2, float fac) +void UI_GetThemeColorBlend3ubv(int colorid1, int colorid2, float fac, unsigned char col[3]) { - int r, g, b; const unsigned char *cp1, *cp2; - + cp1 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid1); cp2 = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid2); CLAMP(fac, 0.0f, 1.0f); - r = floorf((1.0f - fac) * cp1[0] + fac * cp2[0]); - g = floorf((1.0f - fac) * cp1[1] + fac * cp2[1]); - b = floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); - - glColor3ub(r, g, b); + col[0] = floorf((1.0f - fac) * cp1[0] + fac * cp2[0]); + col[1] = floorf((1.0f - fac) * cp1[1] + fac * cp2[1]); + col[2] = floorf((1.0f - fac) * cp1[2] + fac * cp2[2]); +} + +/* blend between to theme colors, and set it */ +void UI_ThemeColorBlend(int colorid1, int colorid2, float fac) +{ + unsigned char col[3]; + UI_GetThemeColorBlend3ubv(colorid1, colorid2, fac, col); + glColor3ubv(col); } /* blend between to theme colors, shade it, and set it */ @@ -2415,9 +2433,47 @@ void init_userdef_do_versions(void) } } - { + if (U.versionfile < 271) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + rgba_char_args_set(btheme->tui.wcol_tooltip.text, 255, 255, 255, 255); + } + } + + if (U.versionfile < 272 || (U.versionfile == 272 && U.subversionfile < 2)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + rgba_char_args_set_fl(btheme->tv3d.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tv3d.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tima.paint_curve_handle, 0.5f, 1.0f, 0.5f, 0.5f); + rgba_char_args_set_fl(btheme->tima.paint_curve_pivot, 1.0f, 0.5f, 0.5f, 0.5f); + rgba_char_args_set(btheme->tnode.syntaxr, 115, 115, 115, 255); + } + } + + if (U.versionfile < 271 || (U.versionfile == 271 && U.subversionfile < 5)) { bTheme *btheme; + + struct uiWidgetColors wcol_pie_menu = { + {10, 10, 10, 200}, + {25, 25, 25, 230}, + {140, 140, 140, 255}, + {45, 45, 45, 230}, + + {160, 160, 160, 255}, + {255, 255, 255, 255}, + + 1, + 10, -10 + }; + + U.pie_menu_radius = 100; + U.pie_menu_threshold = 12; + U.pie_animation_timeout = 6; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + btheme->tui.wcol_pie_menu = wcol_pie_menu; + ui_theme_space_init_handles_color(&btheme->tclip); ui_theme_space_init_handles_color(&btheme->tima); btheme->tima.handle_vertex_size = 5; @@ -2425,6 +2481,16 @@ void init_userdef_do_versions(void) } } + if (U.versionfile < 271 || (U.versionfile == 271 && U.subversionfile < 6)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + /* check for (alpha == 0) is safe, then color was never set */ + if (btheme->tv3d.loop_normal[3] == 0) { + rgba_char_args_set(btheme->tv3d.loop_normal, 0xDD, 0x23, 0xDD, 255); + } + } + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; @@ -2437,26 +2503,3 @@ void init_userdef_do_versions(void) // XXX reset_autosave(); } - -/** - * Override values in in-memory startup.blend, avoids resaving for small changes. - */ -void init_userdef_factory(void) -{ - /* defaults from T37518 */ - - U.uiflag |= USER_AUTOPERSP; - U.uiflag |= USER_ZBUF_CURSOR; - U.uiflag |= USER_QUIT_PROMPT; - U.uiflag |= USER_CONTINUOUS_MOUSE; - - U.versions = 1; - U.savetime = 2; - - { - Mesh *me; - for (me = G.main->mesh.first; me; me = me->id.next) { - me->flag &= ~ME_TWOSIDED; - } - } -} diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 982e8f1a9fe..d48faa34618 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -384,7 +384,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, int resize, int mask_ * - cur must not fall outside of tot * - axis locks (zoom and offset) must be maintained * - zoom must not be excessive (check either sizes or zoom values) - * - aspect ratio should be respected (NOTE: this is quite closely realted to zoom too) + * - aspect ratio should be respected (NOTE: this is quite closely related to zoom too) */ /* Step 1: if keepzoom, adjust the sizes of the rects only @@ -2277,6 +2277,9 @@ typedef struct View2DString { } col; rcti rect; int mval[2]; + + /* str is allocated past the end */ + char str[0]; } View2DString; /* assumes caches are used correctly, so for time being no local storage in v2d */ @@ -2309,7 +2312,7 @@ void UI_view2d_text_cache_add(View2D *v2d, float x, float y, v2s->mval[0] = mval[0]; v2s->mval[1] = mval[1]; - memcpy(v2s + 1, str, alloc_len); + memcpy(v2s->str, str, alloc_len); } } @@ -2340,7 +2343,7 @@ void UI_view2d_text_cache_add_rectf(View2D *v2d, const rctf *rect_view, v2s->mval[0] = v2s->rect.xmin; v2s->mval[1] = v2s->rect.ymin; - memcpy(v2s + 1, str, alloc_len); + memcpy(v2s->str, str, alloc_len); } } @@ -2352,15 +2355,10 @@ void UI_view2d_text_cache_draw(ARegion *ar) /* investigate using BLF_ascender() */ const float default_height = g_v2d_strings ? BLF_height_default("28", 3) : 0.0f; - - // glMatrixMode(GL_PROJECTION); - // glPushMatrix(); - // glMatrixMode(GL_MODELVIEW); - // glPushMatrix(); - ED_region_pixelspace(ar); + + wmOrtho2_region_ui(ar); for (v2s = g_v2d_strings; v2s; v2s = v2s->next) { - const char *str = (const char *)(v2s + 1); int xofs = 0, yofs; yofs = ceil(0.5f * (BLI_rcti_size_y(&v2s->rect) - default_height)); @@ -2372,11 +2370,13 @@ void UI_view2d_text_cache_draw(ARegion *ar) } if (v2s->rect.xmin >= v2s->rect.xmax) - BLF_draw_default((float)v2s->mval[0] + xofs, (float)v2s->mval[1] + yofs, 0.0, str, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default((float)(v2s->mval[0] + xofs), (float)(v2s->mval[1] + yofs), 0.0, + v2s->str, BLF_DRAW_STR_DUMMY_MAX); else { BLF_clipping_default(v2s->rect.xmin - 4, v2s->rect.ymin - 4, v2s->rect.xmax + 4, v2s->rect.ymax + 4); BLF_enable_default(BLF_CLIPPING); - BLF_draw_default(v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, str, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default(v2s->rect.xmin + xofs, v2s->rect.ymin + yofs, 0.0f, + v2s->str, BLF_DRAW_STR_DUMMY_MAX); BLF_disable_default(BLF_CLIPPING); } } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index e30c6ca61ba..a396893f8f2 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -767,6 +767,8 @@ static int view_zoomin_invoke(bContext *C, wmOperator *op, const wmEvent *event) static void VIEW2D_OT_zoom_in(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Zoom In"; ot->description = "Zoom in the view"; @@ -778,10 +780,12 @@ static void VIEW2D_OT_zoom_in(wmOperatorType *ot) ot->poll = view_zoom_poll; /* rna - must keep these in sync with the other operators */ - RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX); - RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX); + prop = RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } - + /* this operator only needs this single callback, where it calls the view_zoom_*() methods */ static int view_zoomout_exec(bContext *C, wmOperator *op) { @@ -828,6 +832,8 @@ static int view_zoomout_invoke(bContext *C, wmOperator *op, const wmEvent *event static void VIEW2D_OT_zoom_out(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Zoom Out"; ot->description = "Zoom out the view"; @@ -839,8 +845,10 @@ static void VIEW2D_OT_zoom_out(wmOperatorType *ot) ot->poll = view_zoom_poll; /* rna - must keep these in sync with the other operators */ - RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX); - RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX); + prop = RNA_def_float(ot->srna, "zoomfacx", 0, -FLT_MAX, FLT_MAX, "Zoom Factor X", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float(ot->srna, "zoomfacy", 0, -FLT_MAX, FLT_MAX, "Zoom Factor Y", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /* ********************************************************* */ @@ -1139,6 +1147,7 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event static void VIEW2D_OT_zoom(wmOperatorType *ot) { + PropertyRNA *prop; /* identifiers */ ot->name = "Zoom 2D View"; ot->description = "Zoom in/out the view"; @@ -1156,8 +1165,10 @@ static void VIEW2D_OT_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; /* rna - must keep these in sync with the other operators */ - RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX); - RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX); + prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_float(ot->srna, "deltay", 0, -FLT_MAX, FLT_MAX, "Delta Y", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /* ********************************************************* */ @@ -1522,6 +1533,7 @@ typedef struct v2dScrollerMove { short zone; /* -1 is min zoomer, 0 is bar, 1 is max zoomer */ // XXX find some way to provide visual feedback of this (active color?) float fac; /* view adjustment factor, based on size of region */ + float fac_round; /* for pixel rounding (avoid visible UI jitter) */ float delta; /* amount moved by mouse on axis of interest */ float scrollbarwidth; /* width of the scrollbar itself, used for page up/down clicks */ @@ -1560,7 +1572,7 @@ enum { */ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max) { - short in_min, in_max, in_bar, out_min, out_max, in_view = 1; + bool in_min, in_max, in_bar, out_min, out_max, in_view = 1; /* firstly, check if * - 'bubble' fills entire scroller @@ -1583,9 +1595,9 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_ /* check if mouse is in or past either handle */ /* TODO: check if these extents are still valid or not */ - in_max = ( (mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE)) ); - in_min = ( (mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE)) ); - in_bar = ( (mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE)) ); + in_max = ((mouse >= (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse <= (sh_max + V2D_SCROLLER_HANDLE_SIZE))); + in_min = ((mouse <= (sh_min + V2D_SCROLLER_HANDLE_SIZE)) && (mouse >= (sh_min - V2D_SCROLLER_HANDLE_SIZE))); + in_bar = ((mouse < (sh_max - V2D_SCROLLER_HANDLE_SIZE)) && (mouse > (sh_min + V2D_SCROLLER_HANDLE_SIZE))); out_min = mouse < (sh_min - V2D_SCROLLER_HANDLE_SIZE); out_max = mouse > (sh_max + V2D_SCROLLER_HANDLE_SIZE); @@ -1640,7 +1652,10 @@ static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *e /* horizontal scroller - calculate adjustment factor first */ mask_size = (float)BLI_rcti_size_x(&v2d->hor); vsm->fac = BLI_rctf_size_x(&tot_cur_union) / mask_size; - + + /* pixel rounding */ + vsm->fac_round = (BLI_rctf_size_x(&v2d->cur)) / (float)(BLI_rcti_size_x(&ar->winrct) + 1); + /* get 'zone' (i.e. which part of scroller is activated) */ vsm->zone = mouse_in_scroller_handle(event->mval[0], v2d->hor.xmin, v2d->hor.xmax, @@ -1659,6 +1674,9 @@ static void scroller_activate_init(bContext *C, wmOperator *op, const wmEvent *e mask_size = (float)BLI_rcti_size_y(&v2d->vert); vsm->fac = BLI_rctf_size_y(&tot_cur_union) / mask_size; + /* pixel rounding */ + vsm->fac_round = (BLI_rctf_size_y(&v2d->cur)) / (float)(BLI_rcti_size_y(&ar->winrct) + 1); + /* get 'zone' (i.e. which part of scroller is activated) */ vsm->zone = mouse_in_scroller_handle(event->mval[1], v2d->vert.ymin, v2d->vert.ymax, @@ -1706,6 +1724,9 @@ static void scroller_activate_apply(bContext *C, wmOperator *op) /* calculate amount to move view by */ temp = vsm->fac * vsm->delta; + + /* round to pixel */ + temp = floorf(temp / vsm->fac_round + 0.5f) * vsm->fac_round; /* type of movement */ switch (vsm->zone) { diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 4d2ea0e64f4..bbf4447dd72 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -96,7 +96,9 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) int use_object_instantiation; int sort_by_name; int export_transformation_type; - int open_sim; + int open_sim; + + int export_count; if (!RNA_struct_property_is_set(op->ptr, "filepath")) { BKE_report(op->reports, RPT_ERROR, "No filename given"); @@ -148,33 +150,36 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op) ED_object_editmode_load(CTX_data_edit_object(C)); - - if (collada_export(CTX_data_scene(C), - filepath, - apply_modifiers, - export_mesh_type, - selected, - include_children, - include_armatures, - include_shapekeys, - deform_bones_only, - - active_uv_only, - include_uv_textures, - include_material_textures, - use_texture_copies, - - triangulate, - use_object_instantiation, - sort_by_name, - export_transformation_type, - open_sim)) - { - return OPERATOR_FINISHED; + export_count = collada_export(CTX_data_scene(C), + filepath, + apply_modifiers, + export_mesh_type, + selected, + include_children, + include_armatures, + include_shapekeys, + deform_bones_only, + + active_uv_only, + include_uv_textures, + include_material_textures, + use_texture_copies, + + triangulate, + use_object_instantiation, + sort_by_name, + export_transformation_type, + open_sim); + + if (export_count == 0) { + BKE_report(op->reports, RPT_WARNING, "Export file is empty"); + return OPERATOR_CANCELLED; } else { - BKE_report(op->reports, RPT_WARNING, "Export file not created"); - return OPERATOR_CANCELLED; + char buff[100]; + sprintf(buff, "Exported %d Objects", export_count); + BKE_report(op->reports, RPT_INFO, buff); + return OPERATOR_FINISHED; } } @@ -250,7 +255,7 @@ static void uiCollada_exportSettings(uiLayout *layout, PointerRNA *imfptr) row = uiLayoutRow(box, false); split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT); - uiItemL(split, IFACE_("Transformation Type"), ICON_NONE); + uiItemL(split, IFACE_("Transformation Type"), ICON_NONE); uiItemR(split, imfptr, "export_transformation_type_selection", 0, "", ICON_NONE); row = uiLayoutRow(box, false); @@ -350,8 +355,8 @@ void WM_OT_collada_export(wmOperatorType *ot) RNA_def_enum(ot->srna, "export_transformation_type_selection", prop_bc_export_transformation_type, 0, "Transform", "Transformation type for translation, scale and rotation"); - RNA_def_boolean(ot->srna, "open_sim", 0, "Export for OpenSim", - "Compatibility mode for OpenSim and compatible online worlds"); + RNA_def_boolean(ot->srna, "open_sim", 0, "Export to SL/OpenSim", + "Compatibility mode for SL, OpenSim and other compatible online worlds"); } diff --git a/source/blender/editors/mask/CMakeLists.txt b/source/blender/editors/mask/CMakeLists.txt index b1cf6db3144..033d034cf4e 100644 --- a/source/blender/editors/mask/CMakeLists.txt +++ b/source/blender/editors/mask/CMakeLists.txt @@ -25,10 +25,12 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -48,4 +50,6 @@ set(SRC mask_intern.h ) +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_mask "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/mask/SConscript b/source/blender/editors/mask/SConscript index 9dd521e3a7c..bcbaaa34960 100644 --- a/source/blender/editors/mask/SConscript +++ b/source/blender/editors/mask/SConscript @@ -29,14 +29,16 @@ Import ('env') sources = env.Glob('*.c') -defs = [] +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index b816103de13..cb47adbe73e 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -57,12 +57,14 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, struct Mask *mask, const float normal_co[2], int threshold, bool feather, + float tangent[2], + const bool use_deform, + const bool use_project, MaskLayer **masklay_r, MaskSpline **spline_r, MaskSplinePoint **point_r, - float *u_r, float tangent[2], - const bool use_deform, - const bool use_project) + float *u_r, + float *score_r) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); @@ -171,6 +173,10 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, *u_r = u; } + if (score_r) { + *score_r = dist; + } + return true; } @@ -339,7 +345,9 @@ static bool add_vertex_subdivide(const bContext *C, Mask *mask, const float co[2 float tangent[2]; float u; - if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, false, &masklay, &spline, &point, &u, tangent, true, true)) { + if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, false, tangent, true, true, + &masklay, &spline, &point, &u, NULL)) + { MaskSplinePoint *new_point; int point_index = point - spline->points; @@ -624,7 +632,9 @@ static int add_feather_vertex_exec(bContext *C, wmOperator *op) if (point) return OPERATOR_FINISHED; - if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, true, &masklay, &spline, &point, &u, NULL, true, true)) { + if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, true, NULL, true, true, + &masklay, &spline, &point, &u, NULL)) + { Scene *scene = CTX_data_scene(C); float w = BKE_mask_point_weight(spline, point, u); float weight_scalar = BKE_mask_point_weight_scalar(spline, point, u); diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 92d55cc1abb..7e767d8f6c8 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -133,9 +133,12 @@ static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float c } static void draw_circle(const float x, const float y, - const float size, const float xscale, const float yscale) + const float size, const bool fill, + const float xscale, const float yscale) { - static GLuint displist = 0; + static GLuint wire_displist = 0; + static GLuint fill_displist = 0; + GLuint displist = fill ? fill_displist : wire_displist; /* Initialize round circle shape. */ if (displist == 0) { @@ -145,11 +148,18 @@ static void draw_circle(const float x, const float y, glNewList(displist, GL_COMPILE); qobj = gluNewQuadric(); - gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); + gluQuadricDrawStyle(qobj, fill ? GLU_FILL : GLU_SILHOUETTE); gluDisk(qobj, 0, 0.7, 8, 1); gluDeleteQuadric(qobj); glEndList(); + + if (fill) { + fill_displist = displist; + } + else { + wire_displist = displist; + } } glPushMatrix(); @@ -213,13 +223,13 @@ static void draw_single_handle(const MaskLayer *mask_layer, const MaskSplinePoin if (point == mask_layer->act_point) glColor3f(1.0f, 1.0f, 1.0f); else - glColor3f(1.0f, 1.0f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX_SELECT); } else { - glColor3f(0.5f, 0.5f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX); } - draw_circle(handle_pos[0], handle_pos[1], handle_size, xscale, yscale); + draw_circle(handle_pos[0], handle_pos[1], handle_size, false, xscale, yscale); } /* return non-zero if spline is selected */ @@ -237,6 +247,7 @@ static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline int i, handle_size, tot_feather_point; float (*feather_points)[2], (*fp)[2]; + float min[2], max[2]; if (!spline->tot_point) return; @@ -280,10 +291,10 @@ static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline if (point == masklay->act_point) glColor3f(1.0f, 1.0f, 1.0f); else - glColor3f(1.0f, 1.0f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX_SELECT); } else { - glColor3f(0.5f, 0.5f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX); } glBegin(GL_POINTS); @@ -302,6 +313,7 @@ static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline } /* control points */ + INIT_MINMAX2(min, max); for (i = 0; i < spline->tot_point; i++) { /* watch it! this is intentionally not the deform array, only check for sel */ @@ -346,14 +358,33 @@ static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline if (point == masklay->act_point) glColor3f(1.0f, 1.0f, 1.0f); else - glColor3f(1.0f, 1.0f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX_SELECT); } else - glColor3f(0.5f, 0.5f, 0.0f); + UI_ThemeColor(TH_HANDLE_VERTEX); glBegin(GL_POINTS); glVertex2fv(vert); glEnd(); + + minmax_v2v2_v2(min, max, vert); + } + + if (is_spline_sel) { + float x = (min[0] + max[0]) / 2.0f; + float y = (min[1] + max[1]) / 2.0f; + /* TODO(sergey): Remove hardcoded colors. */ + if (masklay->act_spline == spline) { + glColor3ub(255, 255, 255); + } + else { + glColor3ub(255, 255, 0); + } + + draw_circle(x, y, 6.0f, true, xscale, yscale); + + glColor3ub(0, 0, 0); + draw_circle(x, y, 6.0f, false, xscale, yscale); } glPointSize(1.0f); @@ -742,7 +773,7 @@ void ED_mask_draw_region(Mask *mask, ARegion *ar, /* w = BLI_rctf_size_x(&v2d->tot); */ - /* h = BLI_rctf_size_y(&v2d->tot);/*/ + /* h = BLI_rctf_size_y(&v2d->tot); */ zoomx = (float)(BLI_rcti_size_x(&ar->winrct) + 1) / BLI_rctf_size_x(&ar->v2d.cur); @@ -803,13 +834,14 @@ void ED_mask_draw_region(Mask *mask, ARegion *ar, /* apply transformation so mask editing tools will assume drawing from the origin in normalized space */ glPushMatrix(); - glTranslatef(x + xofs, y + yofs, 0); - glScalef(maxdim * zoomx, maxdim * zoomy, 0); if (stabmat) { glMultMatrixf(stabmat); } + glTranslatef(x + xofs, y + yofs, 0); + glScalef(maxdim * zoomx, maxdim * zoomy, 0); + if (do_draw_cb) { ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW); } diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index 1acdff8b824..e2eb32e86d9 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -550,6 +550,8 @@ void ED_keymap_mask(wmKeyConfig *keyconf) /* duplicate */ WM_keymap_add_item(keymap, "MASK_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "MASK_OT_copy_splines", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MASK_OT_paste_splines", VKEY, KM_PRESS, KM_CTRL, 0); /* for image editor only */ WM_keymap_add_item(keymap, "UV_OT_cursor_set", ACTIONMOUSE, KM_PRESS, 0, 0); diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h index 6899cf7e6f5..5cdb224ce21 100644 --- a/source/blender/editors/mask/mask_intern.h +++ b/source/blender/editors/mask/mask_intern.h @@ -44,12 +44,14 @@ bool ED_mask_find_nearest_diff_point(const struct bContext *C, struct Mask *mask, const float normal_co[2], int threshold, bool feather, + float tangent[2], + const bool use_deform, + const bool use_project, struct MaskLayer **masklay_r, struct MaskSpline **spline_r, struct MaskSplinePoint **point_r, - float *u_r, float tangent[2], - const bool use_deform, - const bool use_project); + float *u_r, + float *score_r); void MASK_OT_add_vertex(struct wmOperatorType *ot); void MASK_OT_add_feather_vertex(struct wmOperatorType *ot); diff --git a/source/blender/editors/mask/mask_ops.c b/source/blender/editors/mask/mask_ops.c index b7e026ca8e3..93e59f3244e 100644 --- a/source/blender/editors/mask/mask_ops.c +++ b/source/blender/editors/mask/mask_ops.c @@ -458,7 +458,8 @@ enum { SLIDE_ACTION_NONE = 0, SLIDE_ACTION_POINT = 1, SLIDE_ACTION_HANDLE = 2, - SLIDE_ACTION_FEATHER = 3 + SLIDE_ACTION_FEATHER = 3, + SLIDE_ACTION_SPLINE = 4 }; typedef struct SlidePointData { @@ -497,6 +498,108 @@ typedef struct SlidePointData { float weight, weight_scalar; } SlidePointData; +static void mask_point_undistort_pos(SpaceClip *sc, float r_co[2], const float co[2]) +{ + BKE_mask_coord_to_movieclip(sc->clip, &sc->user, r_co, co); + ED_clip_point_undistorted_pos(sc, r_co, r_co); + BKE_mask_coord_from_movieclip(sc->clip, &sc->user, r_co, r_co); +} + +static bool spline_under_mouse_get(const bContext *C, + Mask *mask, const float co[2], + MaskLayer **mask_layer_r, + MaskSpline **mask_spline_r) +{ + const float threshold = 19.0f; + ScrArea *sa = CTX_wm_area(C); + SpaceClip *sc = CTX_wm_space_clip(C); + MaskLayer *mask_layer; + int width, height; + float pixel_co[2]; + float closest_dist_squared; + MaskLayer *closest_layer = NULL; + MaskSpline *closest_spline = NULL; + bool undistort = false; + *mask_layer_r = NULL; + *mask_spline_r = NULL; + ED_mask_get_size(sa, &width, &height); + pixel_co[0] = co[0] * width; + pixel_co[1] = co[1] * height; + if (sc != NULL) { + undistort = (sc->clip != NULL) && + (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0; + } + for (mask_layer = mask->masklayers.first; + mask_layer != NULL; + mask_layer = mask_layer->next) + { + MaskSpline *spline; + if (mask_layer->restrictflag & MASK_RESTRICT_SELECT) { + continue; + } + + for (spline = mask_layer->splines.first; + spline != NULL; + spline = spline->next) + { + MaskSplinePoint *points_array; + float min[2], max[2], center[2]; + float dist_squared; + int i; + float max_bb_side; + if ((spline->flag & SELECT) == 0) { + continue; + } + + points_array = BKE_mask_spline_point_array(spline); + INIT_MINMAX2(min, max); + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point_deform = &points_array[i]; + BezTriple *bezt = &point_deform->bezt; + + float vert[2]; + + copy_v2_v2(vert, bezt->vec[1]); + + if (undistort) { + mask_point_undistort_pos(sc, vert, vert); + } + + minmax_v2v2_v2(min, max, vert); + } + + center[0] = (min[0] + max[0]) / 2.0f * width; + center[1] = (min[1] + max[1]) / 2.0f * height; + dist_squared = len_squared_v2v2(pixel_co, center); + max_bb_side = min_ff((max[0] - min[0]) * width, (max[1] - min[1]) * height); + if (dist_squared <= max_bb_side * max_bb_side * 0.5f && + (closest_spline == NULL || dist_squared < closest_dist_squared)) + { + closest_layer = mask_layer; + closest_spline = spline; + closest_dist_squared = dist_squared; + } + } + } + if (closest_dist_squared < SQUARE(threshold) && closest_spline != NULL) { + float diff_score; + if (ED_mask_find_nearest_diff_point(C, mask, co, threshold, + false, NULL, true, false, + NULL, NULL, NULL, NULL, + &diff_score)) + { + if (SQUARE(diff_score) < closest_dist_squared) { + return false; + } + } + + *mask_layer_r = closest_layer; + *mask_spline_r = closest_spline; + return true; + } + return false; +} + static bool slide_point_check_initial_feather(MaskSpline *spline) { int i; @@ -607,9 +710,14 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * point = cv_point; } - if (action != SLIDE_ACTION_NONE) { - select_sliding_point(mask, masklay, spline, point, which_handle); + if (action == SLIDE_ACTION_NONE) { + if (spline_under_mouse_get(C, mask, co, &masklay, &spline)) { + action = SLIDE_ACTION_SPLINE; + point = NULL; + } + } + if (action != SLIDE_ACTION_NONE) { customdata = MEM_callocN(sizeof(SlidePointData), "mask slide point data"); customdata->event_invoke_type = event->type; customdata->mask = mask; @@ -621,12 +729,14 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * customdata->action = action; customdata->uw = uw; - customdata->old_h1 = point->bezt.h1; - customdata->old_h2 = point->bezt.h2; - customdata->is_sliding_new_point = RNA_boolean_get(op->ptr, "is_new_point"); - check_sliding_handle_type(point, which_handle); + if (customdata->action != SLIDE_ACTION_SPLINE) { + customdata->old_h1 = point->bezt.h1; + customdata->old_h2 = point->bezt.h2; + select_sliding_point(mask, masklay, spline, point, which_handle); + check_sliding_handle_type(point, which_handle); + } if (uw) { float co_uw[2]; @@ -639,7 +749,7 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * madd_v2_v2v2fl(customdata->prev_feather_coord, co_uw, customdata->no, uw->w * weight_scalar); } - else { + else if (customdata->action != SLIDE_ACTION_SPLINE) { BezTriple *bezt = &point->bezt; customdata->weight = bezt->weight; @@ -653,10 +763,12 @@ static void *slide_point_customdata(bContext *C, wmOperator *op, const wmEvent * customdata->is_initial_feather = slide_point_check_initial_feather(spline); } - copy_m3_m3(customdata->vec, point->bezt.vec); - if (which_handle != MASK_WHICH_HANDLE_NONE) { - BKE_mask_point_handle(point, which_handle, customdata->orig_handle_coord); - copy_v2_v2(customdata->prev_handle_coord, customdata->orig_handle_coord); + if (customdata->action != SLIDE_ACTION_SPLINE) { + copy_m3_m3(customdata->vec, point->bezt.vec); + if (which_handle != MASK_WHICH_HANDLE_NONE) { + BKE_mask_point_handle(point, which_handle, customdata->orig_handle_coord); + copy_v2_v2(customdata->prev_handle_coord, customdata->orig_handle_coord); + } } customdata->which_handle = which_handle; @@ -738,7 +850,7 @@ static void cancel_slide_point(SlidePointData *data) else data->point->bezt.weight = data->weight; } - else { + else if (data->action != SLIDE_ACTION_SPLINE) { copy_m3_m3(data->point->bezt.vec, data->vec); data->point->bezt.h1 = data->old_h1; data->point->bezt.h2 = data->old_h2; @@ -935,6 +1047,20 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_v2(data->prev_feather_coord, offco); } } + else if (data->action == SLIDE_ACTION_SPLINE) { + int i; + + if (data->orig_spline == NULL) { + data->orig_spline = BKE_mask_spline_copy(data->spline); + } + + for (i = 0; i < data->spline->tot_point; i++) { + MaskSplinePoint *point = &data->spline->points[i]; + add_v2_v2(point->bezt.vec[0], delta); + add_v2_v2(point->bezt.vec[1], delta); + add_v2_v2(point->bezt.vec[2], delta); + } + } WM_event_add_notifier(C, NC_MASK | NA_EDITED, data->mask); DAG_id_tag_update(&data->mask->id, 0); @@ -970,8 +1096,12 @@ static int slide_point_modal(bContext *C, wmOperator *op, const wmEvent *event) free_slide_point_data(op->customdata); /* keep this last! */ return OPERATOR_FINISHED; } - - break; + else if (event->type != data->event_invoke_type && event->val == KM_PRESS) { + /* pass to ESCKEY */ + } + else { + break; + } case ESCKEY: cancel_slide_point(op->customdata); @@ -1046,7 +1176,7 @@ static bool slide_spline_curvature_check(bContext *C, const wmEvent *event) { Mask *mask = CTX_data_edit_mask(C); float co[2]; - const float threshold = 19; + const float threshold = 19.0f; ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co); @@ -1064,7 +1194,7 @@ static bool slide_spline_curvature_check(bContext *C, const wmEvent *event) static SlideSplineCurvatureData *slide_spline_curvature_customdata( bContext *C, const wmEvent *event) { - const float threshold = 19; + const float threshold = 19.0f; Mask *mask = CTX_data_edit_mask(C); SlideSplineCurvatureData *slide_data; @@ -1077,8 +1207,9 @@ static SlideSplineCurvatureData *slide_spline_curvature_customdata( ED_mask_mouse_pos(CTX_wm_area(C), CTX_wm_region(C), event->mval, co); if (!ED_mask_find_nearest_diff_point(C, mask, co, threshold, false, + NULL, true, false, &mask_layer, &spline, &point, &u, - NULL, true, false)) + NULL)) { return NULL; } @@ -2015,6 +2146,8 @@ static int mask_layer_move_exec(bContext *C, wmOperator *op) mask->masklay_act++; } + WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); + return OPERATOR_FINISHED; } @@ -2075,6 +2208,8 @@ static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) point++; } if (end >= start) { + int tot_point; + int tot_point_shape_start; MaskSpline *new_spline = BKE_mask_spline_add(mask_layer); MaskSplinePoint *new_point; int b; @@ -2098,15 +2233,29 @@ static int mask_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) memcpy(new_spline->points, spline->points + start, new_spline->tot_point * sizeof(MaskSplinePoint)); + tot_point = new_spline->tot_point; + + /* animation requires points added one by one */ + if (mask_layer->splines_shapes.first) { + new_spline->tot_point = 0; + tot_point_shape_start = BKE_mask_layer_shape_spline_to_index(mask_layer, new_spline); + } + /* Select points and duplicate their UWs (if needed). */ for (b = 0, new_point = new_spline->points; - b < new_spline->tot_point; + b < tot_point; b++, new_point++) { if (new_point->uw) { new_point->uw = MEM_dupallocN(new_point->uw); } BKE_mask_point_select_set(new_point, true); + + + if (mask_layer->splines_shapes.first) { + new_spline->tot_point++; + BKE_mask_layer_shape_changed_add(mask_layer, tot_point_shape_start + b, true, false); + } } /* Clear cyclic flag if we didn't copy the whole spline. */ @@ -2158,6 +2307,10 @@ static int copy_splines_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); MaskLayer *mask_layer = BKE_mask_layer_active(mask); + if (mask_layer == NULL) { + return OPERATOR_CANCELLED; + } + BKE_mask_clipboard_copy_from_layer(mask_layer); return OPERATOR_FINISHED; @@ -2195,6 +2348,10 @@ static int paste_splines_exec(bContext *C, wmOperator *UNUSED(op)) Mask *mask = CTX_data_edit_mask(C); MaskLayer *mask_layer = BKE_mask_layer_active(mask); + if (mask_layer == NULL) { + mask_layer = BKE_mask_layer_new(mask, ""); + } + BKE_mask_clipboard_paste_to_layer(CTX_data_main(C), mask_layer); /* TODO: only update edited splines */ diff --git a/source/blender/editors/mask/mask_select.c b/source/blender/editors/mask/mask_select.c index af6f127327c..a4268bddaf5 100644 --- a/source/blender/editors/mask/mask_select.c +++ b/source/blender/editors/mask/mask_select.c @@ -683,7 +683,7 @@ void MASK_OT_select_circle(wmOperatorType *ot) /* properties */ RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX); RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX); } diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 8d91b300ff3..db20d42f39d 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -45,11 +46,13 @@ set(SRC editmesh_bisect.c editmesh_extrude.c editmesh_inset.c + editmesh_intersect.c editmesh_knife.c editmesh_knife_project.c editmesh_loopcut.c editmesh_path.c editmesh_rip.c + editmesh_rip_edge.c editmesh_select.c editmesh_tools.c editmesh_utils.c @@ -84,4 +87,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/mesh/SConscript b/source/blender/editors/mesh/SConscript index 6fa48c12eca..122a7501e58 100644 --- a/source/blender/editors/mesh/SConscript +++ b/source/blender/editors/mesh/SConscript @@ -29,11 +29,12 @@ Import ('env') sources = env.Glob('*.c') -defs = [] +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../uvedit', '../../blenfont', diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 5150a703951..87b429f1165 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -204,7 +204,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, /* only put face under cursor in array */ mp = &me->mpoly[index]; BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); - BLI_BITMAP_SET(poly_tag, index); + BLI_BITMAP_ENABLE(poly_tag, index); } else { /* fill array by selection */ @@ -215,7 +215,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, } else if (mp->flag & ME_FACE_SEL) { BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); - BLI_BITMAP_SET(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, a); } } } @@ -229,13 +229,13 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, if (mp->flag & ME_HIDE) continue; - if (!BLI_BITMAP_GET(poly_tag, a)) { + if (!BLI_BITMAP_TEST(poly_tag, a)) { mark = false; ml = me->mloop + mp->loopstart; for (b = 0; b < mp->totloop; b++, ml++) { if ((me->medge[ml->e].flag & ME_SEAM) == 0) { - if (BLI_BITMAP_GET(edge_tag, ml->e)) { + if (BLI_BITMAP_TEST(edge_tag, ml->e)) { mark = true; break; } @@ -243,7 +243,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, } if (mark) { - BLI_BITMAP_SET(poly_tag, a); + BLI_BITMAP_ENABLE(poly_tag, a); BKE_mesh_poly_edgebitmap_insert(edge_tag, mp, me->mloop + mp->loopstart); do_it = true; } @@ -254,7 +254,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const unsigned int index, MEM_freeN(edge_tag); for (a = 0, mp = me->mpoly; a < me->totpoly; a++, mp++) { - if (BLI_BITMAP_GET(poly_tag, a)) { + if (BLI_BITMAP_TEST(poly_tag, a)) { BKE_BIT_TEST_SET(mp->flag, select, ME_FACE_SEL); } } diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 75ba84aaffd..0e48cbcd589 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -73,25 +73,33 @@ typedef struct { #define HEADER_LENGTH 180 -static void edbm_bevel_update_header(wmOperator *op, bContext *C) +static void edbm_bevel_update_header(bContext *C, wmOperator *op) { - const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Offset: %s, Segments: %d"); + const char *str = IFACE_("Confirm: (Enter/LMB), Cancel: (Esc/RMB), Mode: %s (M), Clamp Overlap: %s (C), " + "Offset: %s, Segments: %d"); char msg[HEADER_LENGTH]; ScrArea *sa = CTX_wm_area(C); + Scene *sce = CTX_data_scene(C); if (sa) { BevelData *opdata = op->customdata; char offset_str[NUM_STR_REP_LEN]; + const char *type_str; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type"); if (hasNumInput(&opdata->num_input)) { - outputNumInput(&opdata->num_input, offset_str); + outputNumInput(&opdata->num_input, offset_str, &sce->unit); } else { BLI_snprintf(offset_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "offset")); } - BLI_snprintf(msg, HEADER_LENGTH, str, offset_str, RNA_int_get(op->ptr, "segments")); + RNA_property_enum_name_gettexted(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &type_str); + + BLI_snprintf(msg, HEADER_LENGTH, str, type_str, + WM_bool_as_string(RNA_boolean_get(op->ptr, "clamp_overlap")), + offset_str, RNA_int_get(op->ptr, "segments")); ED_area_headerprint(sa, msg); } @@ -145,15 +153,21 @@ static bool edbm_bevel_calc(wmOperator *op) const int segments = RNA_int_get(op->ptr, "segments"); const float profile = RNA_float_get(op->ptr, "profile"); const bool vertex_only = RNA_boolean_get(op->ptr, "vertex_only"); + const bool clamp_overlap = RNA_boolean_get(op->ptr, "clamp_overlap"); + int material = RNA_int_get(op->ptr, "material"); /* revert to original mesh */ if (opdata->is_modal) { EDBM_redo_state_restore(opdata->mesh_backup, em, false); } + if (em->ob) + material = CLAMPIS(material, -1, em->ob->totcol - 1); + EDBM_op_init(em, &bmop, op, - "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f", - BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile); + "bevel geom=%hev offset=%f segments=%i vertex_only=%b offset_type=%i profile=%f clamp_overlap=%b " + "material=%i", + BM_ELEM_SELECT, offset, segments, vertex_only, offset_type, profile, clamp_overlap, material); BMO_op_exec(em->bm, &bmop); @@ -253,7 +267,7 @@ static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event) opdata->initial_length = len_v2(mlen); opdata->pixel_size = rv3d ? ED_view3d_pixel_size(rv3d, center_3d) : 1.0f; - edbm_bevel_update_header(op, C); + edbm_bevel_update_header(C, op); if (!edbm_bevel_calc(op)) { edbm_bevel_cancel(C, op); @@ -268,44 +282,40 @@ static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event) static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event) { BevelData *opdata = op->customdata; - bool use_dist = true; - bool is_percent = false; + bool use_dist; + bool is_percent; float mdiff[2]; float factor; mdiff[0] = opdata->mcenter[0] - event->mval[0]; mdiff[1] = opdata->mcenter[1] - event->mval[1]; is_percent = (RNA_enum_get(op->ptr, "offset_type") == BEVEL_AMT_PERCENT); + use_dist = !is_percent; - if (use_dist) { - factor = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length) * opdata->pixel_size; - } - else { - factor = (len_v2(mdiff) - MVAL_PIXEL_MARGIN) / opdata->initial_length; - factor = factor - 1.0f; /* a different kind of buffer where nothing happens */ - } + factor = ((len_v2(mdiff) - MVAL_PIXEL_MARGIN) - opdata->initial_length) * opdata->pixel_size; /* Fake shift-transform... */ if (event->shift) { if (opdata->shift_factor < 0.0f) { opdata->shift_factor = RNA_float_get(op->ptr, "offset"); + if (is_percent) { + opdata->shift_factor /= 100.0f; + } } factor = (factor - opdata->shift_factor) * 0.1f + opdata->shift_factor; } - else if (opdata->shift_factor >= 0.0f) + else if (opdata->shift_factor >= 0.0f) { opdata->shift_factor = -1.0f; + } /* clamp differently based on distance/factor */ if (use_dist) { if (factor < 0.0f) factor = 0.0f; } else { + CLAMP(factor, 0.0f, 1.0f); if (is_percent) { factor *= 100.0f; - CLAMP(factor, 0.0f, 100.0f); - } - else { - CLAMP(factor, 0.0f, 1.0f); } } @@ -316,17 +326,16 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) { BevelData *opdata = op->customdata; int segments = RNA_int_get(op->ptr, "segments"); - - if (event->val == KM_PRESS && hasNumInput(&opdata->num_input)) { - /* Modal numinput active, try to handle numeric inputs first... */ - if (handleNumInput(C, &opdata->num_input, event)) { - float value = RNA_float_get(op->ptr, "offset"); - applyNumInput(&opdata->num_input, &value); - RNA_float_set(op->ptr, "offset", value); - edbm_bevel_calc(op); - edbm_bevel_update_header(op, C); - return OPERATOR_RUNNING_MODAL; - } + const bool has_numinput = hasNumInput(&opdata->num_input); + + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input, event)) { + float value = RNA_float_get(op->ptr, "offset"); + applyNumInput(&opdata->num_input, &value); + RNA_float_set(op->ptr, "offset", value); + edbm_bevel_calc(op); + edbm_bevel_update_header(C, op); + return OPERATOR_RUNNING_MODAL; } else { bool handled = false; @@ -337,12 +346,12 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case MOUSEMOVE: - if (!hasNumInput(&opdata->num_input)) { + if (!has_numinput) { const float factor = edbm_bevel_mval_factor(op, event); RNA_float_set(op->ptr, "offset", factor); edbm_bevel_calc(op); - edbm_bevel_update_header(op, C); + edbm_bevel_update_header(C, op); handled = true; } break; @@ -367,7 +376,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) segments++; RNA_int_set(op->ptr, "segments", segments); edbm_bevel_calc(op); - edbm_bevel_update_header(op, C); + edbm_bevel_update_header(C, op); handled = true; break; @@ -379,29 +388,74 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) segments = max_ii(segments - 1, 1); RNA_int_set(op->ptr, "segments", segments); edbm_bevel_calc(op); - edbm_bevel_update_header(op, C); + edbm_bevel_update_header(C, op); handled = true; break; - } - if (!handled && event->val == KM_PRESS) { - /* Modal numinput inactive, try to handle numeric inputs last... */ - if (handleNumInput(C, &opdata->num_input, event)) { - float value = RNA_float_get(op->ptr, "offset"); - applyNumInput(&opdata->num_input, &value); - RNA_float_set(op->ptr, "offset", value); + case MKEY: + if (event->val == KM_RELEASE) + break; + + { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "offset_type"); + int type = RNA_property_enum_get(op->ptr, prop); + type++; + if (type > BEVEL_AMT_PERCENT) { + type = BEVEL_AMT_OFFSET; + } + RNA_property_enum_set(op->ptr, prop, type); + } + /* Update factor accordingly to new offset_type. */ + if (!has_numinput) { + RNA_float_set(op->ptr, "offset", edbm_bevel_mval_factor(op, event)); + } edbm_bevel_calc(op); - edbm_bevel_update_header(op, C); - return OPERATOR_RUNNING_MODAL; - } + edbm_bevel_update_header(C, op); + handled = true; + break; + case CKEY: + if (event->val == KM_RELEASE) + break; + + { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "clamp_overlap"); + RNA_property_enum_set(op->ptr, prop, !RNA_property_boolean_get(op->ptr, prop)); + } + edbm_bevel_calc(op); + edbm_bevel_update_header(C, op); + handled = true; + break; + } + + /* Modal numinput inactive, try to handle numeric inputs last... */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input, event)) { + float value = RNA_float_get(op->ptr, "offset"); + applyNumInput(&opdata->num_input, &value); + RNA_float_set(op->ptr, "offset", value); + edbm_bevel_calc(op); + edbm_bevel_update_header(C, op); + return OPERATOR_RUNNING_MODAL; } } return OPERATOR_RUNNING_MODAL; } +static void mesh_ot_bevel_offset_range_func(PointerRNA *ptr, PropertyRNA *UNUSED(prop), + float *min, float *max, float *softmin, float *softmax) +{ + const int offset_type = RNA_enum_get(ptr, "offset_type"); + + *min = -FLT_MAX; + *max = FLT_MAX; + *softmin = 0.0f; + *softmax = (offset_type == BEVEL_AMT_PERCENT) ? 100.0f : 1.0f; +} + void MESH_OT_bevel(wmOperatorType *ot) { + PropertyRNA *prop; + static EnumPropertyItem offset_type_items[] = { {BEVEL_AMT_OFFSET, "OFFSET", 0, "Offset", "Amount is offset of new edges from original"}, {BEVEL_AMT_WIDTH, "WIDTH", 0, "Width", "Amount is width of new face"}, @@ -426,8 +480,12 @@ void MESH_OT_bevel(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures"); - RNA_def_float(ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "", 0.0f, 1.0f); + prop = RNA_def_float(ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "", 0.0f, 1.0f); + RNA_def_property_float_array_funcs_runtime(prop, NULL, NULL, mesh_ot_bevel_offset_range_func); RNA_def_int(ot->srna, "segments", 1, 1, 50, "Segments", "Segments for curved edge", 1, 8); RNA_def_float(ot->srna, "profile", 0.5f, 0.15f, 1.0f, "Profile", "Controls profile shape (0.5 = round)", 0.15f, 1.0f); - RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex only", "Bevel only vertices"); + RNA_def_boolean(ot->srna, "vertex_only", false, "Vertex Only", "Bevel only vertices"); + RNA_def_boolean(ot->srna, "clamp_overlap", false, "Clamp Overlap", + "Do not allow beveled edges/vertices to overlap each other"); + RNA_def_int(ot->srna, "material", -1, -1, INT_MAX, "Material", "Material for bevel faces (-1 means use adjacent faces)", -1, 100); } diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 5da33663897..3e403387a67 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -33,6 +33,7 @@ #include "DNA_object_types.h" #include "BLI_math.h" +#include "BLI_listbase.h" #include "BKE_context.h" #include "BKE_global.h" @@ -65,6 +66,75 @@ static void add_normal_aligned(float nor[3], const float add[3]) } } +static void edbm_extrude_edge_exclude_mirror( + Object *obedit, BMEditMesh *em, + const char hflag, + BMOperator *op, BMOpSlot *slot_edges_exclude) +{ + BMesh *bm = em->bm; + ModifierData *md; + + /* If a mirror modifier with clipping is on, we need to adjust some + * of the cases above to handle edges on the line of symmetry. + */ + for (md = obedit->modifiers.first; md; md = md->next) { + if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *) md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + BMIter iter; + BMEdge *edge; + + float mtx[4][4]; + if (mmd->mirror_ob) { + float imtx[4][4]; + invert_m4_m4(imtx, mmd->mirror_ob->obmat); + mul_m4_m4m4(mtx, imtx, obedit->obmat); + } + + BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(edge, hflag) && + BM_edge_is_boundary(edge) && + BM_elem_flag_test(edge->l->f, hflag)) + { + float co1[3], co2[3]; + + copy_v3_v3(co1, edge->v1->co); + copy_v3_v3(co2, edge->v2->co); + + if (mmd->mirror_ob) { + mul_v3_m4v3(co1, mtx, co1); + mul_v3_m4v3(co2, mtx, co2); + } + + if (mmd->flag & MOD_MIR_AXIS_X) { + if ((fabsf(co1[0]) < mmd->tolerance) && + (fabsf(co2[0]) < mmd->tolerance)) + { + BMO_slot_map_empty_insert(op, slot_edges_exclude, edge); + } + } + if (mmd->flag & MOD_MIR_AXIS_Y) { + if ((fabsf(co1[1]) < mmd->tolerance) && + (fabsf(co2[1]) < mmd->tolerance)) + { + BMO_slot_map_empty_insert(op, slot_edges_exclude, edge); + } + } + if (mmd->flag & MOD_MIR_AXIS_Z) { + if ((fabsf(co1[2]) < mmd->tolerance) && + (fabsf(co2[2]) < mmd->tolerance)) + { + BMO_slot_map_empty_insert(op, slot_edges_exclude, edge); + } + } + } + } + } + } + } +} + /* individual face extrude */ /* will use vertex normals for extrusion directions, so *nor is unaffected */ static short edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor)) @@ -75,7 +145,10 @@ static short edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const c BMLoop *l; BMOperator bmop; - EDBM_op_init(em, &bmop, op, "extrude_discrete_faces faces=%hf", hflag); + EDBM_op_init( + em, &bmop, op, + "extrude_discrete_faces faces=%hf use_select_history=%b", + hflag, true); /* deselect original verts */ EDBM_flag_disable_all(em, BM_ELEM_SELECT); @@ -101,12 +174,18 @@ static short edbm_extrude_discrete_faces(BMEditMesh *em, wmOperator *op, const c /* extrudes individual edges */ static short edbm_extrude_edges_indiv(BMEditMesh *em, wmOperator *op, const char hflag, float *UNUSED(nor)) { + BMesh *bm = em->bm; BMOperator bmop; - EDBM_op_init(em, &bmop, op, "extrude_edge_only edges=%he", hflag); + EDBM_op_init( + em, &bmop, op, + "extrude_edge_only edges=%he use_select_history=%b", + hflag, true); /* deselect original verts */ + BM_SELECT_HISTORY_BACKUP(bm); EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BM_SELECT_HISTORY_RESTORE(bm); BMO_op_exec(em->bm, &bmop); BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "geom.out", BM_VERT | BM_EDGE, BM_ELEM_SELECT, true); @@ -123,7 +202,10 @@ static short edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char { BMOperator bmop; - EDBM_op_init(em, &bmop, op, "extrude_vert_indiv verts=%hv", hflag); + EDBM_op_init( + em, &bmop, op, + "extrude_vert_indiv verts=%hv use_select_history=%b", + hflag, true); /* deselect original verts */ BMO_slot_buffer_hflag_disable(em->bm, bmop.slots_in, "verts", BM_VERT, BM_ELEM_SELECT, true); @@ -138,82 +220,32 @@ static short edbm_extrude_verts_indiv(BMEditMesh *em, wmOperator *op, const char return 'g'; /* g is grab */ } -static short edbm_extrude_edge(Object *obedit, BMEditMesh *em, const char hflag, float nor[3]) +static short edbm_extrude_edge_ex( + Object *obedit, BMEditMesh *em, + const char hflag, float nor[3], + const bool use_mirror, + const bool use_select_history) { BMesh *bm = em->bm; - BMIter iter; BMOIter siter; BMOperator extop; - BMEdge *edge; BMFace *f; - ModifierData *md; BMElem *ele; - BMOpSlot *slot_edges_exclude; BMO_op_init(bm, &extop, BMO_FLAG_DEFAULTS, "extrude_face_region"); + BMO_slot_bool_set(extop.slots_in, "use_select_history", use_select_history); BMO_slot_buffer_from_enabled_hflag(bm, &extop, extop.slots_in, "geom", BM_VERT | BM_EDGE | BM_FACE, hflag); - slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude"); + if (use_mirror) { + BMOpSlot *slot_edges_exclude; + slot_edges_exclude = BMO_slot_get(extop.slots_in, "edges_exclude"); - /* If a mirror modifier with clipping is on, we need to adjust some - * of the cases above to handle edges on the line of symmetry. - */ - md = obedit->modifiers.first; - for (; md; md = md->next) { - if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { - MirrorModifierData *mmd = (MirrorModifierData *) md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - float mtx[4][4]; - if (mmd->mirror_ob) { - float imtx[4][4]; - invert_m4_m4(imtx, mmd->mirror_ob->obmat); - mul_m4_m4m4(mtx, imtx, obedit->obmat); - } - - BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(edge, hflag) && - BM_edge_is_boundary(edge) && - BM_elem_flag_test(edge->l->f, hflag)) - { - float co1[3], co2[3]; - - copy_v3_v3(co1, edge->v1->co); - copy_v3_v3(co2, edge->v2->co); - - if (mmd->mirror_ob) { - mul_v3_m4v3(co1, mtx, co1); - mul_v3_m4v3(co2, mtx, co2); - } - - if (mmd->flag & MOD_MIR_AXIS_X) { - if ((fabsf(co1[0]) < mmd->tolerance) && - (fabsf(co2[0]) < mmd->tolerance)) - { - BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge); - } - } - if (mmd->flag & MOD_MIR_AXIS_Y) { - if ((fabsf(co1[1]) < mmd->tolerance) && - (fabsf(co2[1]) < mmd->tolerance)) - { - BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge); - } - } - if (mmd->flag & MOD_MIR_AXIS_Z) { - if ((fabsf(co1[2]) < mmd->tolerance) && - (fabsf(co2[2]) < mmd->tolerance)) - { - BMO_slot_map_empty_insert(&extop, slot_edges_exclude, edge); - } - } - } - } - } - } + edbm_extrude_edge_exclude_mirror(obedit, em, hflag, &extop, slot_edges_exclude); } + BM_SELECT_HISTORY_BACKUP(bm); EDBM_flag_disable_all(em, BM_ELEM_SELECT); + BM_SELECT_HISTORY_RESTORE(bm); BMO_op_exec(bm, &extop); @@ -236,6 +268,13 @@ static short edbm_extrude_edge(Object *obedit, BMEditMesh *em, const char hflag, return is_zero_v3(nor) ? 'g' : 'n'; } +static short edbm_extrude_edge( + Object *obedit, BMEditMesh *em, + const char hflag, float nor[3]) +{ + return edbm_extrude_edge_ex(obedit, em, hflag, nor, true, true); +} + static short edbm_extrude_vert(Object *obedit, BMEditMesh *em, const char hflag, float nor[3]) { BMIter iter; @@ -288,13 +327,12 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op) mul_m3_v3(tmat, dvec); for (a = 0; a < steps; a++) { - edbm_extrude_edge(obedit, em, BM_ELEM_SELECT, nor); - //BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, "extrude_face_region geom=%hef", BM_ELEM_SELECT); - BMO_op_callf(em->bm, BMO_FLAG_DEFAULTS, - "translate vec=%v verts=%hv", - dvec, BM_ELEM_SELECT); - //extrudeflag(obedit, em, SELECT, nor); - //translateflag(em, SELECT, dvec); + edbm_extrude_edge_ex(obedit, em, BM_ELEM_SELECT, nor, false, false); + + BMO_op_callf( + em->bm, BMO_FLAG_DEFAULTS, + "translate vec=%v verts=%hv", + dvec, BM_ELEM_SELECT); } EDBM_mesh_normals_update(em); @@ -693,7 +731,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot) { /* identifiers */ - ot->name = "Duplicate or Extrude at 3D Cursor"; + ot->name = "Duplicate or Extrude to Cursor"; ot->idname = "MESH_OT_dupli_extrude_cursor"; ot->description = "Duplicate and extrude selected vertices, edges or faces towards the mouse cursor"; diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index aa3a2c83243..f1c3ca30610 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -85,11 +85,12 @@ static void edbm_inset_update_header(wmOperator *op, bContext *C) char msg[HEADER_LENGTH]; ScrArea *sa = CTX_wm_area(C); + Scene *sce = CTX_data_scene(C); if (sa) { char flts_str[NUM_STR_REP_LEN * 2]; if (hasNumInput(&opdata->num_input)) - outputNumInput(&opdata->num_input, flts_str); + outputNumInput(&opdata->num_input, flts_str, &sce->unit); else { BLI_snprintf(flts_str, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "thickness")); BLI_snprintf(flts_str + NUM_STR_REP_LEN, NUM_STR_REP_LEN, "%f", RNA_float_get(op->ptr, "depth")); @@ -213,17 +214,21 @@ static bool edbm_inset_calc(wmOperator *op) if (use_individual) { EDBM_op_init(em, &bmop, op, - "inset_individual faces=%hf use_even_offset=%b use_relative_offset=%b" + "inset_individual faces=%hf use_even_offset=%b use_relative_offset=%b " "use_interpolate=%b thickness=%f depth=%f", BM_ELEM_SELECT, use_even_offset, use_relative_offset, use_interpolate, thickness, depth); } else { EDBM_op_init(em, &bmop, op, - "inset_region faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b" - " use_interpolate=%b thickness=%f depth=%f use_outset=%b use_edge_rail=%b", + "inset_region faces=%hf use_boundary=%b use_even_offset=%b use_relative_offset=%b " + "use_interpolate=%b thickness=%f depth=%f use_outset=%b use_edge_rail=%b", BM_ELEM_SELECT, use_boundary, use_even_offset, use_relative_offset, use_interpolate, thickness, depth, use_outset, use_edge_rail); + + if (use_outset) { + BMO_slot_buffer_from_enabled_hflag(em->bm, &bmop, bmop.slots_in, "faces_exclude", BM_FACE, BM_ELEM_HIDDEN); + } } BMO_op_exec(em->bm, &bmop); @@ -296,25 +301,24 @@ static int edbm_inset_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event) { InsetData *opdata = op->customdata; - - if (event->val == KM_PRESS && hasNumInput(&opdata->num_input)) { - /* Modal numinput active, try to handle numeric inputs first... */ - if (handleNumInput(C, &opdata->num_input, event)) { - float amounts[2] = {RNA_float_get(op->ptr, "thickness"), - RNA_float_get(op->ptr, "depth")}; - applyNumInput(&opdata->num_input, amounts); - amounts[0] = max_ff(amounts[0], 0.0f); - RNA_float_set(op->ptr, "thickness", amounts[0]); - RNA_float_set(op->ptr, "depth", amounts[1]); - - if (edbm_inset_calc(op)) { - edbm_inset_update_header(op, C); - return OPERATOR_RUNNING_MODAL; - } - else { - edbm_inset_cancel(C, op); - return OPERATOR_CANCELLED; - } + const bool has_numinput = hasNumInput(&opdata->num_input); + + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &opdata->num_input, event)) { + float amounts[2] = {RNA_float_get(op->ptr, "thickness"), + RNA_float_get(op->ptr, "depth")}; + applyNumInput(&opdata->num_input, amounts); + amounts[0] = max_ff(amounts[0], 0.0f); + RNA_float_set(op->ptr, "thickness", amounts[0]); + RNA_float_set(op->ptr, "depth", amounts[1]); + + if (edbm_inset_calc(op)) { + edbm_inset_update_header(op, C); + return OPERATOR_RUNNING_MODAL; + } + else { + edbm_inset_cancel(C, op); + return OPERATOR_CANCELLED; } } else { @@ -326,7 +330,7 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case MOUSEMOVE: - if (!hasNumInput(&opdata->num_input)) { + if (!has_numinput) { float mdiff[2]; float amount; @@ -454,24 +458,22 @@ static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } - if (!handled && event->val == KM_PRESS) { - /* Modal numinput inactive, try to handle numeric inputs last... */ - if (handleNumInput(C, &opdata->num_input, event)) { - float amounts[2] = {RNA_float_get(op->ptr, "thickness"), - RNA_float_get(op->ptr, "depth")}; - applyNumInput(&opdata->num_input, amounts); - amounts[0] = max_ff(amounts[0], 0.0f); - RNA_float_set(op->ptr, "thickness", amounts[0]); - RNA_float_set(op->ptr, "depth", amounts[1]); - - if (edbm_inset_calc(op)) { - edbm_inset_update_header(op, C); - return OPERATOR_RUNNING_MODAL; - } - else { - edbm_inset_cancel(C, op); - return OPERATOR_CANCELLED; - } + /* Modal numinput inactive, try to handle numeric inputs last... */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &opdata->num_input, event)) { + float amounts[2] = {RNA_float_get(op->ptr, "thickness"), + RNA_float_get(op->ptr, "depth")}; + applyNumInput(&opdata->num_input, amounts); + amounts[0] = max_ff(amounts[0], 0.0f); + RNA_float_set(op->ptr, "thickness", amounts[0]); + RNA_float_set(op->ptr, "depth", amounts[1]); + + if (edbm_inset_calc(op)) { + edbm_inset_update_header(op, C); + return OPERATOR_RUNNING_MODAL; + } + else { + edbm_inset_cancel(C, op); + return OPERATOR_CANCELLED; } } } @@ -512,7 +514,7 @@ void MESH_OT_inset(wmOperatorType *ot) RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.01, 4); RNA_def_boolean(ot->srna, "use_outset", false, "Outset", "Outset rather than inset"); - RNA_def_boolean(ot->srna, "use_select_inset", true, "Select Outer", "Select the new inset faces"); + RNA_def_boolean(ot->srna, "use_select_inset", false, "Select Outer", "Select the new inset faces"); RNA_def_boolean(ot->srna, "use_individual", false, "Individual", "Individual Face Inset"); RNA_def_boolean(ot->srna, "use_interpolate", true, "Interpolate", "Blend face data across the inset"); } diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c new file mode 100644 index 00000000000..df6776950d7 --- /dev/null +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -0,0 +1,400 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mesh/editmesh_intersect.c + * \ingroup edmesh + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" + +#include "BLI_math.h" +#include "BLI_array.h" +#include "BLI_linklist_stack.h" + + +#include "BKE_context.h" +#include "BKE_report.h" +#include "BKE_editmesh.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_mesh.h" +#include "ED_screen.h" + +#include "intern/bmesh_private.h" + +#include "mesh_intern.h" /* own include */ + +#include "tools/bmesh_intersect.h" + + +/* -------------------------------------------------------------------- */ +/* Cut intersections into geometry */ + +/** + * Compare selected with its self. + */ +static int bm_face_isect_self(BMFace *f, void *UNUSED(user_data)) +{ + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + return 0; + } + else { + return -1; + } +} + +/** + * Compare selected/unselected. + */ +static int bm_face_isect_pair(BMFace *f, void *UNUSED(user_data)) +{ + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + return -1; + } + else if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + return 0; + } + else { + return 1; + } +} + +enum { + ISECT_SEL = 0, + ISECT_SEL_UNSEL = 1, +}; + +static EnumPropertyItem isect_mode_items[] = { + {ISECT_SEL, "SELECT", 0, "Self Intersect", + "Self intersect selected faces"}, + {ISECT_SEL_UNSEL, "SELECT_UNSELECT", 0, "Selected/Unselected", + "Intersect selected with unselected faces"}, + {0, NULL, 0, NULL, NULL} +}; + +static int edbm_intersect_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int mode = RNA_enum_get(op->ptr, "mode"); + int (*test_fn)(BMFace *, void *); + bool use_separate = RNA_boolean_get(op->ptr, "use_separate"); + const float eps = RNA_float_get(op->ptr, "threshold"); + bool use_self; + bool has_isect; + + switch (mode) { + case ISECT_SEL: + test_fn = bm_face_isect_self; + use_self = true; + break; + default: /* ISECT_SEL_UNSEL */ + test_fn = bm_face_isect_pair; + use_self = false; + break; + } + + + has_isect = BM_mesh_intersect( + bm, + em->looptris, em->tottri, + test_fn, NULL, + use_self, use_separate, + eps); + + + if (has_isect) { + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + if (em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) { + BMIter iter; + BMEdge *e; + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BM_edge_select_set(bm, e, true); + } + } + } + + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + } + else { + BKE_report(op->reports, RPT_WARNING, "No intersections found"); + } + + return OPERATOR_FINISHED; +} + +void MESH_OT_intersect(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Intersect"; + ot->description = "Cut an intersection into faces"; + ot->idname = "MESH_OT_intersect"; + + /* api callbacks */ + ot->exec = edbm_intersect_exec; + ot->poll = ED_operator_editmesh; + + /* props */ + RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", ""); + RNA_def_boolean(ot->srna, "use_separate", true, "Separate", ""); + RNA_def_float(ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001); + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +/* -------------------------------------------------------------------- */ +/* Face Split by Edges */ + + +/** \name Face/Edge Split + * \{ */ + +static void bm_face_split_by_edges(BMesh *bm, BMFace *f, const char hflag) +{ + BMEdge **edge_net = NULL; + BLI_array_declare(edge_net); + + const int f_index = BM_elem_index_get(f); + + BMLoop *l_iter; + BMLoop *l_first; + BMVert *v; + + BMFace **face_arr; + int face_arr_len; + + /* likely this will stay very small + * all verts pushed into this stack _must_ have their previous edges set! */ + BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *); + BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *); + + + /* collect all edges */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BMIter iter; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, l_iter->v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e, hflag) && + (BM_elem_index_get(e) == f_index)) + { + v = BM_edge_other_vert(e, l_iter->v); + v->e = e; + + BLI_SMALLSTACK_PUSH(vert_stack, v); + BLI_array_append(edge_net, e); + } + } + } while ((l_iter = l_iter->next) != l_first); + + + + /* now assign all */ + /* pop free values into the next stack */ + while ((v = BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next))) { + BMIter eiter; + BMEdge *e_next; + + BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) { + if (BM_elem_flag_test(e_next, hflag) && + (BM_elem_index_get(e_next) == -1)) + { + BMVert *v_next; + v_next = BM_edge_other_vert(e_next, v); + BM_elem_index_set(e_next, f_index); + BLI_SMALLSTACK_PUSH(vert_stack_next, v_next); + BLI_array_append(edge_net, e_next); + } + } + + if (BLI_SMALLSTACK_IS_EMPTY(vert_stack)) { + BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next); + } + } + + BM_face_split_edgenet(bm, f, edge_net, BLI_array_count(edge_net), &face_arr, &face_arr_len); + BLI_array_free(edge_net); + + if (face_arr_len) { + int i; + for (i = 0; i < face_arr_len; i++) { + BM_face_select_set(bm, face_arr[i], true); + BM_elem_flag_disable(face_arr[i], hflag); + } + } + + if (face_arr) { + MEM_freeN(face_arr); + } +} + +static int edbm_face_split_by_edges_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const char hflag = BM_ELEM_TAG; + + BMVert *v; + BMEdge *e; + BMFace *f; + BMIter iter; + + BLI_SMALLSTACK_DECLARE(loop_stack, BMLoop *); + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_flag_disable(v, hflag); + } + + /* edge index is set to -1 then used to assosiate them with faces */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT) && BM_edge_is_wire(e)) { + BM_elem_flag_enable(e, hflag); + + BM_elem_flag_enable(e->v1, hflag); + BM_elem_flag_enable(e->v2, hflag); + + } + else { + BM_elem_flag_disable(e, hflag); + } + BM_elem_index_set(e, -1); /* set_dirty */ + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { + BM_elem_flag_enable(f, hflag); + } + else { + BM_elem_flag_disable(f, hflag); + } + } + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, hflag)) { + BMIter viter; + BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) { + BMIter liter; + BMLoop *l; + + unsigned int loop_stack_len; + BMLoop *l_best = NULL; + + BLI_assert(BLI_SMALLSTACK_IS_EMPTY(loop_stack)); + loop_stack_len = 0; + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l->f, hflag)) { + BLI_SMALLSTACK_PUSH(loop_stack, l); + loop_stack_len++; + } + } + + if (loop_stack_len == 0) { + /* pass */ + } + else if (loop_stack_len == 1) { + l_best = BLI_SMALLSTACK_POP(loop_stack); + } + else { + /* complicated case, match the edge with a face-loop */ + + BMVert *v_other = BM_edge_other_vert(e, v); + float e_dir[3]; + + /* we wan't closest to zero */ + float dot_best = FLT_MAX; + + sub_v3_v3v3(e_dir, v_other->co, v->co); + normalize_v3(e_dir); + + while ((l = BLI_SMALLSTACK_POP(loop_stack))) { + float dot_test; + + /* Check dot first to save on expensive angle-comparison. + * ideal case is 90d difference == 0.0 dot */ + dot_test = fabsf(dot_v3v3(e_dir, l->f->no)); + if (dot_test < dot_best) { + + /* check we're in the correct corner (works with convex loops too) */ + if (angle_signed_on_axis_v3v3v3_v3(l->prev->v->co, l->v->co, v_other->co, l->f->no) < + angle_signed_on_axis_v3v3v3_v3(l->prev->v->co, l->v->co, l->next->v->co, l->f->no)) + { + dot_best = dot_test; + l_best = l; + } + } + } + } + + if (l_best) { + BM_elem_index_set(e, BM_elem_index_get(l_best->f)); /* set_dirty */ + } + } + } + } + + bm->elem_index_dirty |= BM_EDGE; + + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, hflag)) { + bm_face_split_by_edges(bm, f, hflag); + } + } + + EDBM_mesh_normals_update(em); + EDBM_update_generic(em, true, true); + + return OPERATOR_FINISHED; +} + + +void MESH_OT_face_split_by_edges(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split by Edges"; + ot->description = "Split faces by loose edges"; + ot->idname = "MESH_OT_face_split_by_edges"; + + /* api callbacks */ + ot->exec = edbm_face_split_by_edges_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 366a8253849..f5a7e82a8a8 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -27,6 +27,8 @@ /** \file blender/editors/mesh/editmesh_knife.c * \ingroup edmesh + * + * Interactive editmesh knife tool. */ #ifdef _MSC_VER @@ -71,13 +73,12 @@ #include "mesh_intern.h" /* own include */ -/* this code here is kindof messy. . .I might need to eventually rework it - joeedh */ - #define KMAXDIST 10 /* max mouse distance from edge before not detecting it */ #define KNIFE_FLT_EPS 0.00001f #define KNIFE_FLT_EPS_SQUARED (KNIFE_FLT_EPS * KNIFE_FLT_EPS) #define KNIFE_FLT_EPSBIG 0.0005f +#define KNIFE_FLT_EPS_PX 0.2f typedef struct KnifeColors { unsigned char line[3]; @@ -115,7 +116,7 @@ typedef struct KnifeEdge { typedef struct KnifeLineHit { float hit[3], cagehit[3]; - float schit[2]; + float schit[2]; /* screen coordinates for cagehit */ float l; /* lambda along cut line */ float perc; /* lambda along hit line */ float m; /* depth front-to-back */ @@ -181,6 +182,9 @@ typedef struct KnifeTool_OpData { BLI_mempool *refs; float projmat[4][4]; + float projmat_inv[4][4]; + /* vector along view z axis (object space, normalized) */ + float proj_zaxis[3]; KnifeColors colors; @@ -208,6 +212,9 @@ typedef struct KnifeTool_OpData { bool ignore_edge_snapping; bool ignore_vert_snapping; + /* use to check if we're currently dragging an angle snapped line */ + bool is_angle_snapping; + enum { ANGLE_FREE, ANGLE_0, @@ -247,6 +254,27 @@ static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], flo ED_view3d_project_float_v2_m4(kcd->ar, co, sco, (float (*)[4])kcd->projmat); } +/* use when lambda is in screen-space */ +static void knife_interp_v3_v3v3( + const KnifeTool_OpData *kcd, + float r_co[3], const float v1[3], const float v2[3], float lambda_ss) +{ + if (kcd->is_ortho) { + interp_v3_v3v3(r_co, v1, v2, lambda_ss); + } + else { + /* transform into screen-space, interp, then transform back */ + float v1_ss[3], v2_ss[3]; + + mul_v3_project_m4_v3(v1_ss, (float (*)[4])kcd->projmat, v1); + mul_v3_project_m4_v3(v2_ss, (float (*)[4])kcd->projmat, v2); + + interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss); + + mul_project_m4_v3((float (*)[4])kcd->projmat_inv, r_co); + } +} + static void knife_pos_data_clear(KnifePosData *kpd) { zero_v3(kpd->co); @@ -340,7 +368,7 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const copy_v3_v3(kfv->co, co); copy_v3_v3(kfv->cageco, cageco); - knife_project_v2(kcd, kfv->co, kfv->sco); + knife_project_v2(kcd, kfv->cageco, kfv->sco); return kfv; } @@ -441,19 +469,23 @@ static void knife_start_cut(KnifeTool_OpData *kcd) kcd->prev = kcd->curr; kcd->curr.is_space = 0; /*TODO: why do we do this? */ - if (kcd->prev.vert == NULL && kcd->prev.edge == NULL && is_zero_v3(kcd->prev.cage)) { - /* Make prevcage a point on the view ray to mouse closest to a point on model: choose vertex 0 */ + if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) { float origin[3], origin_ofs[3]; - BMVert *v0; + float ofs_local[3]; + + negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs); + invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); + mul_m4_v3(kcd->ob->imat, ofs_local); knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); - v0 = BM_vert_at_index_find(kcd->em->bm, 0); - if (v0) { - closest_to_line_v3(kcd->prev.cage, v0->co, origin_ofs, origin); - copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */ - copy_v3_v3(kcd->curr.cage, kcd->prev.cage); - copy_v3_v3(kcd->curr.co, kcd->prev.co); + + if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) { + zero_v3(kcd->prev.cage); } + + copy_v3_v3(kcd->prev.co, kcd->prev.cage); /*TODO: do we need this? */ + copy_v3_v3(kcd->curr.cage, kcd->prev.cage); + copy_v3_v3(kcd->curr.co, kcd->prev.co); } } @@ -483,21 +515,14 @@ static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace knife_append_list(kcd, &kfe->faces, f); } -static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float co[3], KnifeEdge **newkfe_out) +static KnifeVert *knife_split_edge( + KnifeTool_OpData *kcd, KnifeEdge *kfe, + const float co[3], const float cageco[3], + KnifeEdge **r_kfe) { KnifeEdge *newkfe = new_knife_edge(kcd); Ref *ref; BMFace *f; - float perc, cageco[3], l12; - - l12 = len_v3v3(kfe->v1->co, kfe->v2->co); - if (l12 < KNIFE_FLT_EPS) { - copy_v3_v3(cageco, kfe->v1->cageco); - } - else { - perc = len_v3v3(co, kfe->v1->co) / l12; - interp_v3_v3v3(cageco, kfe->v1->cageco, kfe->v2->cageco, perc); - } newkfe->v1 = kfe->v1; newkfe->v2 = new_knife_vert(kcd, co, cageco); @@ -529,7 +554,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float newkfe->draw = kfe->draw; newkfe->e = kfe->e; - *newkfe_out = newkfe; + *r_kfe = newkfe; return newkfe->v2; } @@ -667,7 +692,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, KnifeLineHit *lh1, Knife kfe->v1 = lh1->v; } else if (lh1->kfe) { - kfe->v1 = knife_split_edge(kcd, lh1->kfe, lh1->cagehit, &kfe2); + kfe->v1 = knife_split_edge(kcd, lh1->kfe, lh1->hit, lh1->cagehit, &kfe2); lh1->v = kfe->v1; /* record the KnifeVert for this hit */ } else { @@ -683,7 +708,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, KnifeLineHit *lh1, Knife kfe->v2 = lh2->v; } else if (lh2->kfe) { - kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->cagehit, &kfe2); + kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2); lh2->v = kfe->v2; /* future uses of lh2 won't split again */ } else { @@ -1068,21 +1093,22 @@ static void knifetool_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) if (v3d->zbuf) glEnable(GL_DEPTH_TEST); } -/* Find intersection of v1-v2 with face f. - * Only take intersections that are at least face_tol (in screen space) away +/** + * Find intersection of v1-v2 with face f. + * Only take intersections that are at least \a face_tol_sq (in screen space) away * from other intersection elements. * If v1-v2 is coplanar with f, call that "no intersection though * it really means "infinite number of intersections". - * In such a case we should have gotten hits on edges or verts of the face. */ -static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, - const float s[2], - const float v1[3], const float v2[3], - BMFace *f, - const float face_tol, - float intersectp[3]) + * In such a case we should have gotten hits on edges or verts of the face. + */ +static bool knife_ray_intersect_face( + KnifeTool_OpData *kcd, + const float s[2], const float v1[3], const float v2[3], + BMFace *f, const float face_tol_sq, + float hit_co[3], float hit_cageco[3]) { int tottri, tri_i; - float lv1[3], lv2[3], lv3[3], raydir[3]; + float raydir[3]; float tri_norm[3], tri_plane[4]; float se1[2], se2[2]; float d, lambda; @@ -1098,12 +1124,14 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { + const float *lv1, *lv2, *lv3; + tri = kcd->em->looptris[tri_i]; if (tri[0]->f != f) break; - copy_v3_v3(lv1, kcd->cagecos[BM_elem_index_get(tri[0]->v)]); - copy_v3_v3(lv2, kcd->cagecos[BM_elem_index_get(tri[1]->v)]); - copy_v3_v3(lv3, kcd->cagecos[BM_elem_index_get(tri[2]->v)]); + lv1 = kcd->cagecos[BM_elem_index_get(tri[0]->v)]; + lv2 = kcd->cagecos[BM_elem_index_get(tri[1]->v)]; + lv3 = kcd->cagecos[BM_elem_index_get(tri[2]->v)]; /* using epsilon test in case ray is directly through an internal * tesselation edge and might not hit either tesselation tri with * an exact test; @@ -1112,24 +1140,29 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, /* check if line coplanar with tri */ normal_tri_v3(tri_norm, lv1, lv2, lv3); plane_from_point_normal_v3(tri_plane, lv1, tri_norm); - if ((fabsf(dist_squared_to_plane_v3(v1, tri_plane)) < KNIFE_FLT_EPS) && - (fabsf(dist_squared_to_plane_v3(v2, tri_plane)) < KNIFE_FLT_EPS)) + if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && + (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - copy_v3_v3(intersectp, v1); - madd_v3_v3fl(intersectp, raydir, lambda); + copy_v3_v3(hit_cageco, v1); + madd_v3_v3fl(hit_cageco, raydir, lambda); /* Now check that far enough away from verts and edges */ lst = knife_get_face_kedges(kcd, f); for (ref = lst->first; ref; ref = ref->next) { kfe = ref->ref; knife_project_v2(kcd, kfe->v1->cageco, se1); knife_project_v2(kcd, kfe->v2->cageco, se2); - d = dist_to_line_segment_v2(s, se1, se2); - if (d < face_tol) { + d = dist_squared_to_line_segment_v2(s, se1, se2); + if (d < face_tol_sq) { return false; } } + + transform_point_by_tri_v3( + hit_co, hit_cageco, + tri[0]->v->co, tri[1]->v->co, tri[2]->v->co, + lv1, lv2, lv3); return true; } } @@ -1158,6 +1191,13 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa { BMFace *f_hit; + /* If box clipping on, make sure p is not clipped */ + if (kcd->vc.rv3d->rflag & RV3D_CLIPPING && + ED_view3d_clipping_test(kcd->vc.rv3d, p, true)) + { + return false; + } + /* If not cutting through, make sure no face is in front of p */ if (!kcd->cut_through) { float dist; @@ -1174,8 +1214,19 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa madd_v3_v3v3fl(p_ofs, p, view, KNIFE_FLT_EPSBIG * 3.0f); /* avoid projecting behind the viewpoint */ - if (kcd->is_ortho) { - dist = FLT_MAX; + if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { + dist = kcd->vc.v3d->far * 2.0f; + } + + if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { + float view_clip[2][3]; + /* note: view_clip[0] should never get clipped */ + copy_v3_v3(view_clip[0], p_ofs); + madd_v3_v3v3fl(view_clip[1], p_ofs, view, dist); + + if (clip_segment_v3_plane_n(view_clip[0], view_clip[1], kcd->vc.rv3d->clip_local, 6)) { + dist = len_v3v3(p_ofs, view_clip[1]); + } } /* see if there's a face hit between p1 and the view */ @@ -1184,13 +1235,6 @@ static bool point_is_visible(KnifeTool_OpData *kcd, const float p[3], const floa return false; } - /* If box clipping on, make sure p is not clipped */ - if (kcd->vc.rv3d->rflag & RV3D_CLIPPING && - ED_view3d_clipping_test(kcd->vc.rv3d, p, true)) - { - return false; - } - return true; } @@ -1208,16 +1252,7 @@ static void clip_to_ortho_planes(float v1[3], float v2[3], float d) static void set_linehit_depth(KnifeTool_OpData *kcd, KnifeLineHit *lh) { - float vnear[3], vfar[3]; - - ED_view3d_win_to_segment(kcd->ar, kcd->vc.v3d, lh->schit, vnear, vfar, true); - mul_m4_v3(kcd->ob->imat, vnear); - if (kcd->is_ortho) { - if (kcd->ortho_extent == 0.0f) - calc_ortho_extent(kcd); - clip_to_ortho_planes(vnear, vfar, kcd->ortho_extent + 10.0f); - } - lh->m = len_v3v3(vnear, lh->cagehit); + lh->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, lh->cagehit); } /* Finds visible (or all, if cutting through) edges that intersects the current screen drag line */ @@ -1241,9 +1276,12 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) void *val; float plane_cos[12]; float s[2], se1[2], se2[2], sint[2]; - float p[3], p2[3], r1[3], r2[3]; + float r1[3], r2[3]; float d, d1, d2, lambda; - float vert_tol, vert_tol_sq, line_tol, face_tol; + float vert_tol, vert_tol_sq; + float line_tol, line_tol_sq; + float face_tol, face_tol_sq; + float eps_scale; int isect_kind; unsigned int tot; int i; @@ -1288,7 +1326,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) * this gives precision error; rather then solving properly * (which may involve using doubles everywhere!), * limit the distance between these points */ - if (kcd->is_ortho) { + if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { if (kcd->ortho_extent == 0.0f) calc_ortho_extent(kcd); clip_to_ortho_planes(v1, v3, kcd->ortho_extent + 10.0f); @@ -1346,10 +1384,22 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) /* Now go through the candidates and find intersections */ /* These tolerances, in screen space, are for intermediate hits, as ends are already snapped to screen */ - vert_tol = KNIFE_FLT_EPS * 2000.0f; - line_tol = KNIFE_FLT_EPS * 2000.0f; - vert_tol_sq = vert_tol * vert_tol; + { + /* Scale the epsilon by the zoom level + * to compensate for projection imprecision, see T41164 */ + float zoom_xy[2] = {kcd->vc.rv3d->winmat[0][0], + kcd->vc.rv3d->winmat[1][1]}; + eps_scale = len_v2(zoom_xy); + } + + vert_tol = KNIFE_FLT_EPS_PX * eps_scale; + line_tol = KNIFE_FLT_EPS_PX * eps_scale; face_tol = max_ff(vert_tol, line_tol); + + vert_tol_sq = vert_tol * vert_tol; + line_tol_sq = line_tol * line_tol; + face_tol_sq = face_tol * face_tol; + /* Assume these tolerances swamp floating point rounding errors in calculations below */ /* first look for vertex hits */ @@ -1362,7 +1412,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) if (point_is_visible(kcd, v->cageco, s, &mats)) { memset(&hit, 0, sizeof(hit)); hit.v = v; - copy_v3_v3(hit.hit, v->cageco); + copy_v3_v3(hit.hit, v->co); copy_v3_v3(hit.cagehit, v->cageco); copy_v2_v2(hit.schit, s); set_linehit_depth(kcd, &hit); @@ -1380,35 +1430,39 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) if (isect_kind == -1) { /* isect_seg_seg_v2 doesn't do tolerance test around ends of s1-s2 */ closest_to_line_segment_v2(sint, s1, se1, se2); - if (len_squared_v2v2(sint, s1) <= vert_tol_sq) + if (len_squared_v2v2(sint, s1) <= line_tol_sq) isect_kind = 1; else { closest_to_line_segment_v2(sint, s2, se1, se2); - if (len_squared_v2v2(sint, s2) <= vert_tol_sq) + if (len_squared_v2v2(sint, s2) <= line_tol_sq) isect_kind = 1; } } if (isect_kind == 1) { d1 = len_v2v2(sint, se1); d2 = len_v2v2(se2, se1); - if (!(d1 <= vert_tol || d2 <= vert_tol || fabsf(d1 - d2) <= vert_tol)) { + if (!(d1 <= line_tol || d2 <= line_tol || fabsf(d1 - d2) <= line_tol)) { + float p_cage[3], p_cage_tmp[3]; lambda = d1 / d2; /* Can't just interpolate between ends of kfe because * that doesn't work with perspective transformation. * Need to find 3d intersection of ray through sint */ knife_input_ray_segment(kcd, sint, 1.0f, r1, r2); - isect_kind = isect_line_line_v3(kfe->v1->cageco, kfe->v2->cageco, r1, r2, p, p2); - if (isect_kind >= 1 && point_is_visible(kcd, p, sint, &mats)) { + isect_kind = isect_line_line_v3(kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp); + if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, &mats)) { memset(&hit, 0, sizeof(hit)); if (kcd->snap_midpoints) { /* choose intermediate point snap too */ - mid_v3_v3v3(p, kfe->v1->cageco, kfe->v2->cageco); + mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco); mid_v2_v2v2(sint, se1, se2); lambda = 0.5f; } hit.kfe = kfe; - copy_v3_v3(hit.hit, p); - copy_v3_v3(hit.cagehit, p); + transform_point_by_seg_v3( + hit.hit, p_cage, + kfe->v1->co, kfe->v2->co, + kfe->v1->cageco, kfe->v2->cageco); + copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, sint); hit.perc = lambda; set_linehit_depth(kcd, &hit); @@ -1421,23 +1475,25 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) for (val = BLI_smallhash_iternew(&faces, &hiter, (uintptr_t *)&f); val; val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { - if (knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol, p)) { - if (point_is_visible(kcd, p, s1, &mats)) { + float p[3], p_cage[3]; + + if (knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) { + if (point_is_visible(kcd, p_cage, s1, &mats)) { memset(&hit, 0, sizeof(hit)); hit.f = f; copy_v3_v3(hit.hit, p); - copy_v3_v3(hit.cagehit, p); + copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s1); set_linehit_depth(kcd, &hit); BLI_array_append(linehits, hit); } } - if (knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol, p)) { - if (point_is_visible(kcd, p, s2, &mats)) { + if (knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) { + if (point_is_visible(kcd, p_cage, s2, &mats)) { memset(&hit, 0, sizeof(hit)); hit.f = f; copy_v3_v3(hit.hit, p); - copy_v3_v3(hit.cagehit, p); + copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s2); set_linehit_depth(kcd, &hit); BLI_array_append(linehits, hit); @@ -1597,48 +1653,76 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo f = knife_find_closest_face(kcd, co, cageco, NULL); *is_space = !f; - /* set p to co, in case we don't find anything, means a face cut */ - copy_v3_v3(p, co); - copy_v3_v3(cagep, cageco); - kcd->curr.bmface = f; if (f) { const float maxdist_sq = maxdist * maxdist; KnifeEdge *cure = NULL; + float cur_cagep[3]; ListBase *lst; Ref *ref; float dis_sq, curdis_sq = FLT_MAX; + /* set p to co, in case we don't find anything, means a face cut */ + copy_v3_v3(p, co); + copy_v3_v3(cagep, cageco); + knife_project_v2(kcd, cageco, sco); /* look through all edges associated with this face */ lst = knife_get_face_kedges(kcd, f); for (ref = lst->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; + float test_cagep[3]; + float lambda; /* project edge vertices into screen space */ knife_project_v2(kcd, kfe->v1->cageco, kfe->v1->sco); knife_project_v2(kcd, kfe->v2->cageco, kfe->v2->sco); - dis_sq = dist_squared_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco); - if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { - if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { - float lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco); - float vec[3]; + /* check if we're close enough and calculate 'lambda' */ + if (kcd->is_angle_snapping) { + /* if snapping, check we're in bounds */ + float sco_snap[2]; + isect_line_line_v2_point(kfe->v1->sco, kfe->v2->sco, kcd->prev.mval, kcd->curr.mval, sco_snap); + lambda = line_point_factor_v2(sco_snap, kfe->v1->sco, kfe->v2->sco); - interp_v3_v3v3(vec, kfe->v1->cageco, kfe->v2->cageco, lambda); + /* be strict about angle-snapping within edge */ + if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) { + continue; + } - if (ED_view3d_clipping_test(kcd->vc.rv3d, vec, true) == 0) { - cure = kfe; - curdis_sq = dis_sq; - } + dis_sq = len_squared_v2v2(sco, sco_snap); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { + /* we already have 'lambda' */ + } + else { + continue; + } + } + else { + dis_sq = dist_squared_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco); + if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { + lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco); } else { - cure = kfe; - curdis_sq = dis_sq; + continue; } } + + /* now we have 'lambda' calculated (in screen-space) */ + knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda); + + if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { + /* check we're in the view */ + if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) { + continue; + } + } + + cure = kfe; + curdis_sq = dis_sq; + copy_v3_v3(cur_cagep, test_cagep); } if (fptr) @@ -1653,11 +1737,9 @@ static KnifeEdge *knife_find_closest_edge(KnifeTool_OpData *kcd, float p[3], flo mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco); } else { - float d; - - closest_to_line_segment_v3(cagep, cageco, cure->v1->cageco, cure->v2->cageco); - d = len_v3v3(cagep, cure->v1->cageco) / len_v3v3(cure->v1->cageco, cure->v2->cageco); - interp_v3_v3v3(p, cure->v1->co, cure->v2->co, d); + float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco); + copy_v3_v3(cagep, cur_cagep); + interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda); } /* update mouse coordinates to the snapped-to edge's screen coordinates @@ -1693,9 +1775,6 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo f = knife_find_closest_face(kcd, co, cageco, is_space); - /* set p to co, in case we don't find anything, means a face cut */ - copy_v3_v3(p, co); - copy_v3_v3(cagep, cageco); kcd->curr.bmface = f; if (f) { @@ -1705,6 +1784,10 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo KnifeVert *curv = NULL; float dis_sq, curdis_sq = FLT_MAX; + /* set p to co, in case we don't find anything, means a face cut */ + copy_v3_v3(p, co); + copy_v3_v3(cagep, cageco); + knife_project_v2(kcd, cageco, sco); lst = knife_get_face_kedges(kcd, f); @@ -1717,6 +1800,13 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo knife_project_v2(kcd, kfv->cageco, kfv->sco); + /* be strict about angle snapping, the vertex needs to be very close to the angle, or we ignore */ + if (kcd->is_angle_snapping) { + if (dist_squared_to_line_segment_v2(kfv->sco, kcd->prev.mval, kcd->curr.mval) > KNIFE_FLT_EPSBIG) { + continue; + } + } + dis_sq = len_squared_v2v2(kfv->sco, sco); if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { if (kcd->vc.rv3d->rflag & RV3D_CLIPPING) { @@ -1764,7 +1854,7 @@ static KnifeVert *knife_find_closest_vert(KnifeTool_OpData *kcd, float p[3], flo } /* update both kcd->curr.mval and kcd->mval to snap to required angle */ -static void knife_snap_angle(KnifeTool_OpData *kcd) +static bool knife_snap_angle(KnifeTool_OpData *kcd) { float dx, dy; float w, abs_tan; @@ -1772,7 +1862,7 @@ static void knife_snap_angle(KnifeTool_OpData *kcd) dx = kcd->curr.mval[0] - kcd->prev.mval[0]; dy = kcd->curr.mval[1] - kcd->prev.mval[1]; if (dx == 0.0f && dy == 0.0f) - return; + return false; if (dx == 0.0f) { kcd->angle_snapping = ANGLE_90; @@ -1801,6 +1891,8 @@ static void knife_snap_angle(KnifeTool_OpData *kcd) } copy_v2_v2(kcd->mval, kcd->curr.mval); + + return true; } /* update active knife edge/vert pointers */ @@ -1810,14 +1902,15 @@ static int knife_update_active(KnifeTool_OpData *kcd) copy_v2_v2(kcd->curr.mval, kcd->mval); /* view matrix may have changed, reproject */ - knife_project_v2(kcd, kcd->prev.co, kcd->prev.mval); + knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval); - if (kcd->angle_snapping != ANGLE_FREE && kcd->mode == MODE_DRAGGING) - knife_snap_angle(kcd); + if (kcd->angle_snapping != ANGLE_FREE && kcd->mode == MODE_DRAGGING) { + kcd->is_angle_snapping = knife_snap_angle(kcd); + } + else { + kcd->is_angle_snapping = false; + } - /* XXX knife_snap_angle updates the view coordinate mouse values to constrained angles, - * which current mouse values are set to current mouse values are then used - * for vertex and edge snap detection, without regard to the exact angle constraint */ kcd->curr.vert = knife_find_closest_vert(kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space); if (!kcd->curr.vert) { @@ -1835,8 +1928,12 @@ static int knife_update_active(KnifeTool_OpData *kcd) knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); - closest_to_line_v3(kcd->curr.cage, kcd->prev.cage, origin_ofs, origin); - copy_v3_v3(kcd->curr.co, kcd->curr.cage); + if (!isect_line_plane_v3(kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) { + copy_v3_v3(kcd->curr.cage, kcd->prev.cage); + + /* should never fail! */ + BLI_assert(0); + } } if (kcd->mode == MODE_DRAGGING) { @@ -2060,7 +2157,7 @@ static ListBase *find_hole(KnifeTool_OpData *kcd, ListBase *fedges) static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, ListBase **mainchain, ListBase **sidechain) { - float **fco, **hco; + float (*fco)[2], (*hco)[2]; BMVert **fv; KnifeVert **hv; KnifeEdge **he; @@ -2082,8 +2179,8 @@ static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, L /* Gather 2d projections of hole and face vertex coordinates. * Use best-axis projection - not completely accurate, maybe revisit */ axis_dominant_v3(&ax, &ay, f->no); - hco = BLI_memarena_alloc(kcd->arena, nh * sizeof(float *)); - fco = BLI_memarena_alloc(kcd->arena, nf * sizeof(float *)); + hco = BLI_memarena_alloc(kcd->arena, nh * sizeof(float[2])); + fco = BLI_memarena_alloc(kcd->arena, nf * sizeof(float[2])); hv = BLI_memarena_alloc(kcd->arena, nh * sizeof(KnifeVert *)); fv = BLI_memarena_alloc(kcd->arena, nf * sizeof(BMVert *)); he = BLI_memarena_alloc(kcd->arena, nh * sizeof(KnifeEdge *)); @@ -2101,7 +2198,6 @@ static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, L kfv = kfvother; BLI_assert(kfv == kfe->v1 || kfv == kfe->v2); } - hco[i] = BLI_memarena_alloc(kcd->arena, 2 * sizeof(float)); hco[i][0] = kfv->co[ax]; hco[i][1] = kfv->co[ay]; hv[i] = kfv; @@ -2111,7 +2207,6 @@ static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, L j = 0; BM_ITER_ELEM (v, &iter, f, BM_VERTS_OF_FACE) { - fco[j] = BLI_memarena_alloc(kcd->arena, 2 * sizeof(float)); fco[j][0] = v->co[ax]; fco[j][1] = v->co[ay]; fv[j] = v; @@ -2203,33 +2298,30 @@ static bool find_hole_chains(KnifeTool_OpData *kcd, ListBase *hole, BMFace *f, L static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) { - BMLoop *l1, *l2, *l; - float mid[3]; - BMIter iter; - int v1inside, v2inside; + bool v1_inside, v2_inside; + bool v1_inface, v2_inface; if (!f || !v1 || !v2) return false; - l1 = NULL; - l2 = NULL; - /* find out if v1 and v2, if set, are part of the face */ - BM_ITER_ELEM (l, &iter, f, BM_LOOPS_OF_FACE) { - if (v1->v && l->v == v1->v) - l1 = l; - if (v2->v && l->v == v2->v) - l2 = l; - } + v1_inface = v1->v ? BM_vert_in_face(f, v1->v) : false; + v2_inface = v2->v ? BM_vert_in_face(f, v2->v) : false; /* BM_face_point_inside_test uses best-axis projection so this isn't most accurate test... */ - v1inside = l1 ? 0 : BM_face_point_inside_test(f, v1->co); - v2inside = l2 ? 0 : BM_face_point_inside_test(f, v2->co); - if ((l1 && v2inside) || (l2 && v1inside) || (v1inside && v2inside)) + v1_inside = v1_inface ? false : BM_face_point_inside_test(f, v1->co); + v2_inside = v2_inface ? false : BM_face_point_inside_test(f, v2->co); + if ((v1_inface && v2_inside) || + (v2_inface && v1_inside) || + (v1_inside && v2_inside)) + { return true; - if (l1 && l2) { + } + + if (v1_inface && v2_inface) { + float mid[3]; /* Can have case where v1 and v2 are on shared chain between two faces. - * BM_face_legal_splits does visibility and self-intersection tests, + * BM_face_splits_check_legal does visibility and self-intersection tests, * but it is expensive and maybe a bit buggy, so use a simple * "is the midpoint in the face" test */ mid_v3_v3v3(mid, v1->co, v2->co); @@ -2326,12 +2418,12 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe KnifeEdge *kfe; BMFace *fnew, *fnew2, *fhole; ListBase *chain, *hole, *sidechain; - ListBase *fnew_kfedges, *fnew2_kfedges; Ref *ref, *refnext; int count, oldcount; oldcount = BLI_countlist(kfedges); while ((chain = find_chain(kcd, kfedges)) != NULL) { + ListBase fnew_kfedges; knife_make_chain_cut(kcd, f, chain, &fnew); if (!fnew) { return; @@ -2339,18 +2431,22 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe /* Move kfedges to fnew_kfedges if they are now in fnew. * The chain edges were removed already */ - fnew_kfedges = knife_empty_list(kcd); + BLI_listbase_clear(&fnew_kfedges); for (ref = kfedges->first; ref; ref = refnext) { kfe = ref->ref; refnext = ref->next; if (knife_edge_in_face(kfe, fnew)) { BLI_remlink(kfedges, ref); kfe->basef = fnew; - knife_append_list(kcd, fnew_kfedges, kfe); + BLI_addtail(&fnew_kfedges, ref); + } + else if (!knife_edge_in_face(kfe, f)) { + /* Concave ngon's - this edge might not be in either faces, T41730 */ + BLI_remlink(kfedges, ref); } } - if (fnew_kfedges->first) - knife_make_face_cuts(kcd, fnew, fnew_kfedges); + if (fnew_kfedges.first) + knife_make_face_cuts(kcd, fnew, &fnew_kfedges); /* find_chain should always remove edges if it returns true, * but guard against infinite loop anyway */ @@ -2364,6 +2460,8 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe while ((hole = find_hole(kcd, kfedges)) != NULL) { if (find_hole_chains(kcd, hole, f, &chain, &sidechain)) { + ListBase fnew_kfedges, fnew2_kfedges; + /* chain goes across f and sidechain comes back * from the second last vertex to the second vertex. */ @@ -2394,28 +2492,28 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe BM_face_kill(bm, fhole); /* Move kfedges to either fnew or fnew2 if appropriate. * The hole edges were removed already */ - fnew_kfedges = knife_empty_list(kcd); - fnew2_kfedges = knife_empty_list(kcd); + BLI_listbase_clear(&fnew_kfedges); + BLI_listbase_clear(&fnew2_kfedges); for (ref = kfedges->first; ref; ref = refnext) { kfe = ref->ref; refnext = ref->next; if (knife_edge_in_face(kfe, fnew)) { BLI_remlink(kfedges, ref); kfe->basef = fnew; - knife_append_list(kcd, fnew_kfedges, kfe); + BLI_addtail(&fnew_kfedges, ref); } else if (knife_edge_in_face(kfe, fnew2)) { BLI_remlink(kfedges, ref); kfe->basef = fnew2; - knife_append_list(kcd, fnew2_kfedges, kfe); + BLI_addtail(&fnew2_kfedges, ref); } } /* We'll skip knife edges that are in the newly formed hole. * (Maybe we shouldn't have made a hole in the first place?) */ - if (fnew != fhole && fnew_kfedges->first) - knife_make_face_cuts(kcd, fnew, fnew_kfedges); - if (fnew2 != fhole && fnew2_kfedges->first) - knife_make_face_cuts(kcd, fnew2, fnew2_kfedges); + if (fnew != fhole && fnew_kfedges.first) + knife_make_face_cuts(kcd, fnew, &fnew_kfedges); + if (fnew2 != fhole && fnew2_kfedges.first) + knife_make_face_cuts(kcd, fnew2, &fnew2_kfedges); if (f == fhole) break; /* find_hole should always remove edges if it returns true, @@ -2530,7 +2628,11 @@ static void knife_recalc_projmat(KnifeTool_OpData *kcd) { invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); ED_view3d_ob_project_mat_get(kcd->ar->regiondata, kcd->ob, kcd->projmat); - //mul_m4_m4m4(kcd->projmat, kcd->vc.rv3d->winmat, kcd->vc.rv3d->viewmat); + invert_m4_m4(kcd->projmat_inv, kcd->projmat); + + copy_v3_v3(kcd->proj_zaxis, kcd->vc.rv3d->viewinv[2]); + mul_mat3_m4_v3(kcd->ob->imat, kcd->proj_zaxis); + normalize_v3(kcd->proj_zaxis); kcd->is_ortho = ED_view3d_clip_range_get(kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index cc26d6079a9..553c1faa36a 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -65,7 +65,7 @@ static LinkNode *knifeproject_poly_from_object(ARegion *ar, Scene *scene, Object dm = ob->derivedFinal ? ob->derivedFinal : mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); dm_needsFree = false; } - else if (ELEM3(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { dm = CDDM_from_curve(ob); dm_needsFree = true; } diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 4ef6c13ec3e..b5508ef8b82 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -75,6 +75,9 @@ typedef struct RingSelOpData { float (*edges)[2][3]; int totedge; + float (*points)[3]; + int totpoint; + ViewContext vc; Object *ob; @@ -91,9 +94,8 @@ static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) { View3D *v3d = CTX_wm_view3d(C); RingSelOpData *lcd = arg; - int i; - if (lcd->totedge > 0) { + if ((lcd->totedge > 0) || (lcd->totpoint > 0)) { if (v3d && v3d->zbuf) glDisable(GL_DEPTH_TEST); @@ -101,12 +103,23 @@ static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg) glMultMatrixf(lcd->ob->obmat); glColor3ub(255, 0, 255); - glBegin(GL_LINES); - for (i = 0; i < lcd->totedge; i++) { - glVertex3fv(lcd->edges[i][0]); - glVertex3fv(lcd->edges[i][1]); + if (lcd->totedge > 0) { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, lcd->edges); + glDrawArrays(GL_LINES, 0, lcd->totedge * 2); + glDisableClientState(GL_VERTEX_ARRAY); + } + + if (lcd->totpoint > 0) { + glPointSize(3.0f); + + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(3, GL_FLOAT, 0, lcd->points); + glDrawArrays(GL_POINTS, 0, lcd->totpoint); + glDisableClientState(GL_VERTEX_ARRAY); + + glPointSize(1.0f); } - glEnd(); glPopMatrix(); if (v3d && v3d->zbuf) @@ -178,52 +191,43 @@ static void edgering_vcos_get(DerivedMesh *dm, BMVert *v[2][2], float r_cos[2][2 } } -static void edgering_sel(RingSelOpData *lcd, int previewlines, bool select) +static void edgering_vcos_get_pair(DerivedMesh *dm, BMVert *v[2], float r_cos[2][3]) { - BMEditMesh *em = lcd->em; - DerivedMesh *dm = EDBM_mesh_deform_dm_get(em); - BMEdge *eed_start = lcd->eed; - BMEdge *eed, *eed_last; - BMVert *v[2][2], *v_last; - BMWalker walker; - float (*edges)[2][3] = NULL; - BLI_array_declare(edges); - int i, tot = 0; - - memset(v, 0, sizeof(v)); - - if (!eed_start) - return; - - if (lcd->edges) { - MEM_freeN(lcd->edges); - lcd->edges = NULL; - lcd->totedge = 0; + if (dm) { + int j; + for (j = 0; j < 2; j++) { + dm->getVertCo(dm, BM_elem_index_get(v[j]), r_cos[j]); + } } - - if (!lcd->extend) { - EDBM_flag_disable_all(lcd->em, BM_ELEM_SELECT); + else { + int j; + for (j = 0; j < 2; j++) { + copy_v3_v3(r_cos[j], v[j]->co); + } } +} - if (select) { - BMW_init(&walker, em->bm, BMW_EDGERING, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); - - for (eed = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) { - BM_edge_select_set(em->bm, eed, true); - } - BMW_end(&walker); +static void edgering_preview_free(RingSelOpData *lcd) +{ + MEM_SAFE_FREE(lcd->edges); + lcd->totedge = 0; - return; - } + MEM_SAFE_FREE(lcd->points); + lcd->totpoint = 0; +} - if (dm) { - BM_mesh_elem_table_ensure(lcd->em->bm, BM_VERT); - } +static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, const int previewlines) +{ + BMesh *bm = lcd->em->bm; + BMWalker walker; + BMEdge *eed_start = lcd->eed; + BMEdge *eed, *eed_last; + BMVert *v[2][2] = {{NULL}}, *v_last; + float (*edges)[2][3] = NULL; + BLI_array_declare(edges); + int i, tot = 0; - BMW_init(&walker, em->bm, BMW_EDGERING, + BMW_init(&walker, bm, BMW_EDGERING, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); @@ -261,7 +265,7 @@ static void edgering_sel(RingSelOpData *lcd, int previewlines, bool select) } eed_last = eed; } - + if ((eed_last != eed_start) && #ifdef BMW_EDGERING_NGON BM_edge_share_face_check(eed_last, eed_start) @@ -274,7 +278,7 @@ static void edgering_sel(RingSelOpData *lcd, int previewlines, bool select) v[1][1] = v[0][1]; edgering_find_order(eed_last, eed_start, v_last, v); - + BLI_array_grow_items(edges, previewlines); for (i = 1; i <= previewlines; i++) { @@ -298,15 +302,83 @@ static void edgering_sel(RingSelOpData *lcd, int previewlines, bool select) lcd->totedge = tot; } +static void edgering_preview_calc_points(RingSelOpData *lcd, DerivedMesh *dm, const int previewlines) +{ + float v_cos[2][3]; + float (*points)[3]; + int i, tot = 0; + + if (dm) { + BM_mesh_elem_table_ensure(lcd->em->bm, BM_VERT); + } + + points = MEM_mallocN(sizeof(*lcd->points) * previewlines, __func__); + + edgering_vcos_get_pair(dm, &lcd->eed->v1, v_cos); + + for (i = 1; i <= previewlines; i++) { + const float fac = (i / ((float)previewlines + 1)); + interp_v3_v3v3(points[tot], v_cos[0], v_cos[1], fac); + tot++; + } + + lcd->points = points; + lcd->totpoint = previewlines; +} + +static void edgering_preview_calc(RingSelOpData *lcd, const int previewlines) +{ + DerivedMesh *dm; + + BLI_assert(lcd->eed != NULL); + + edgering_preview_free(lcd); + + dm = EDBM_mesh_deform_dm_get(lcd->em); + if (dm) { + BM_mesh_elem_table_ensure(lcd->em->bm, BM_VERT); + } + + if (BM_edge_is_wire(lcd->eed)) { + edgering_preview_calc_points(lcd, dm, previewlines); + } + else { + edgering_preview_calc_edges(lcd, dm, previewlines); + } +} + +static void edgering_select(RingSelOpData *lcd) +{ + BMEditMesh *em = lcd->em; + BMEdge *eed_start = lcd->eed; + BMWalker walker; + BMEdge *eed; + + if (!eed_start) + return; + + if (!lcd->extend) { + EDBM_flag_disable_all(lcd->em, BM_ELEM_SELECT); + } + + BMW_init(&walker, em->bm, BMW_EDGERING, + BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + for (eed = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) { + BM_edge_select_set(em->bm, eed, true); + } + BMW_end(&walker); +} + static void ringsel_find_edge(RingSelOpData *lcd, const int previewlines) { if (lcd->eed) { - edgering_sel(lcd, previewlines, false); + edgering_preview_calc(lcd, previewlines); } - else if (lcd->edges) { - MEM_freeN(lcd->edges); - lcd->edges = NULL; - lcd->totedge = 0; + else { + edgering_preview_free(lcd); } } @@ -324,26 +396,37 @@ static void ringsel_finish(bContext *C, wmOperator *op) if (lcd->eed) { BMEditMesh *em = lcd->em; + BMVert *v_eed_orig[2] = {lcd->eed->v1, lcd->eed->v2}; - edgering_sel(lcd, cuts, true); + edgering_select(lcd); if (lcd->do_cut) { const bool is_macro = (op->opm != NULL); + /* a single edge (rare, but better support) */ + const bool is_single = (BM_edge_is_wire(lcd->eed)); + const int seltype = is_single ? SUBDIV_SELECT_INNER : SUBDIV_SELECT_LOOPCUT; + /* Enable gridfill, so that intersecting loopcut works as one would expect. * Note though that it will break edgeslide in this specific case. * See [#31939]. */ BM_mesh_esubdivide(em->bm, BM_ELEM_SELECT, smoothness, smooth_falloff, true, 0.0f, 0.0f, - cuts, - SUBDIV_SELECT_LOOPCUT, SUBD_PATH, 0, true, + cuts, seltype, SUBD_PATH, 0, true, use_only_quads, 0); /* when used in a macro tessface is already re-recalculated */ EDBM_update_generic(em, (is_macro == false), true); + if (is_single) { + /* de-select endpoints */ + BM_vert_select_set(em->bm, v_eed_orig[0], false); + BM_vert_select_set(em->bm, v_eed_orig[1], false); + + EDBM_selectmode_flush_ex(lcd->em, SCE_SELECT_VERTEX); + } /* we cant slide multiple edges in vertex select mode */ - if (is_macro && (cuts > 1) && (em->selectmode & SCE_SELECT_VERTEX)) { + else if (is_macro && (cuts > 1) && (em->selectmode & SCE_SELECT_VERTEX)) { EDBM_selectmode_disable(lcd->vc.scene, em, SCE_SELECT_VERTEX, SCE_SELECT_EDGE); } /* force edge slide to edge select mode in in face select mode */ @@ -378,8 +461,7 @@ static void ringsel_exit(bContext *UNUSED(C), wmOperator *op) /* deactivate the extra drawing stuff in 3D-View */ ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle); - if (lcd->edges) - MEM_freeN(lcd->edges); + edgering_preview_free(lcd); ED_region_tag_redraw(lcd->ar); @@ -555,39 +637,27 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) int cuts = RNA_int_get(op->ptr, "number_cuts"); RingSelOpData *lcd = op->customdata; bool show_cuts = false; + const bool has_numinput = hasNumInput(&lcd->num); view3d_operator_needs_opengl(C); /* using the keyboard to input the number of cuts */ - if (event->val == KM_PRESS && hasNumInput(&lcd->num)) { - /* Modal numinput active, try to handle numeric inputs first... */ - if (handleNumInput(C, &lcd->num, event)) { - float values[2] = {(float)cuts, smoothness}; - applyNumInput(&lcd->num, values); - - /* allow zero so you can backspace and type in a value - * otherwise 1 as minimum would make more sense */ - cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); - smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); - - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; - RNA_float_set(op->ptr, "smoothness", smoothness); - - ED_region_tag_redraw(lcd->ar); - } - else { - switch (event->type) { - case RETKEY: - case PADENTER: - case LEFTMOUSE: /* confirm */ // XXX hardcoded - return loopcut_finish(lcd, C, op); - default: - /* do nothing */; - break; - } - } + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && has_numinput && handleNumInput(C, &lcd->num, event)) { + float values[2] = {(float)cuts, smoothness}; + applyNumInput(&lcd->num, values); + + /* allow zero so you can backspace and type in a value + * otherwise 1 as minimum would make more sense */ + cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); + smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); + + RNA_int_set(op->ptr, "number_cuts", cuts); + ringsel_find_edge(lcd, cuts); + show_cuts = true; + RNA_float_set(op->ptr, "smoothness", smoothness); + + ED_region_tag_redraw(lcd->ar); } else { bool handled = false; @@ -606,7 +676,7 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) ringsel_exit(C, op); ED_area_headerprint(CTX_wm_area(C), NULL); - return OPERATOR_FINISHED; + return OPERATOR_CANCELLED; case ESCKEY: if (event->val == KM_RELEASE) { /* cancel */ @@ -663,43 +733,42 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) handled = true; break; case MOUSEMOVE: /* mouse moved somewhere to select another loop */ - { - lcd->vc.mval[0] = event->mval[0]; - lcd->vc.mval[1] = event->mval[1]; - loopcut_mouse_move(lcd, cuts); + if (!has_numinput) { + lcd->vc.mval[0] = event->mval[0]; + lcd->vc.mval[1] = event->mval[1]; + loopcut_mouse_move(lcd, cuts); - ED_region_tag_redraw(lcd->ar); - handled = true; + ED_region_tag_redraw(lcd->ar); + handled = true; + } break; - } } - if (!handled && event->val == KM_PRESS) { - /* Modal numinput inactive, try to handle numeric inputs last... */ - if (handleNumInput(C, &lcd->num, event)) { - float values[2] = {(float)cuts, smoothness}; - applyNumInput(&lcd->num, values); - - /* allow zero so you can backspace and type in a value - * otherwise 1 as minimum would make more sense */ - cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); - smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); - - RNA_int_set(op->ptr, "number_cuts", cuts); - ringsel_find_edge(lcd, cuts); - show_cuts = true; - RNA_float_set(op->ptr, "smoothness", smoothness); - - ED_region_tag_redraw(lcd->ar); - } + /* Modal numinput inactive, try to handle numeric inputs last... */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &lcd->num, event)) { + float values[2] = {(float)cuts, smoothness}; + applyNumInput(&lcd->num, values); + + /* allow zero so you can backspace and type in a value + * otherwise 1 as minimum would make more sense */ + cuts = CLAMPIS(values[0], 0, SUBD_CUTS_MAX); + smoothness = CLAMPIS(values[1], -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); + + RNA_int_set(op->ptr, "number_cuts", cuts); + ringsel_find_edge(lcd, cuts); + show_cuts = true; + RNA_float_set(op->ptr, "smoothness", smoothness); + + ED_region_tag_redraw(lcd->ar); } } if (show_cuts) { + Scene *sce = CTX_data_scene(C); char buf[64 + NUM_STR_REP_LEN * 2]; char str_rep[NUM_STR_REP_LEN * 2]; if (hasNumInput(&lcd->num)) { - outputNumInput(&lcd->num, str_rep); + outputNumInput(&lcd->num, str_rep, &sce->unit); } else { BLI_snprintf(str_rep, NUM_STR_REP_LEN, "%d", cuts); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 84952297235..4f149bf2c52 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -217,8 +217,8 @@ static BMEdge *edbm_ripsel_edge_mark_step(BMVert *v, const int uid) BM_edge_loop_pair(e, &l_a, &l_b); /* no need to check, we know this will be true */ /* so (IS_VISIT_DONE == true) */ - BM_elem_index_set(l_a, uid); - BM_elem_index_set(l_b, uid); + BM_elem_index_set(l_a, uid); /* set_dirty */ + BM_elem_index_set(l_b, uid); /* set_dirty */ return e; } @@ -250,9 +250,10 @@ static EdgeLoopPair *edbm_ripsel_looptag_helper(BMesh *bm) /* initialize loops with dummy invalid index values */ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BM_elem_index_set(l, INVALID_UID); + BM_elem_index_set(l, INVALID_UID); /* set_dirty */ } } + bm->elem_index_dirty |= BM_LOOP; /* set contiguous loops ordered 'uid' values for walking after split */ while (true) { @@ -308,8 +309,7 @@ static EdgeLoopPair *edbm_ripsel_looptag_helper(BMesh *bm) uid_start = uid; uid = uid_end + bm->totedge; - BLI_array_grow_one(eloop_pairs); - lp = &eloop_pairs[BLI_array_count(eloop_pairs) - 1]; + lp = BLI_array_append_ret(eloop_pairs); BM_edge_loop_pair(e_last, &lp->l_a, &lp->l_b); /* no need to check, we know this will be true */ @@ -322,8 +322,7 @@ static EdgeLoopPair *edbm_ripsel_looptag_helper(BMesh *bm) } /* null terminate */ - BLI_array_grow_one(eloop_pairs); - lp = &eloop_pairs[BLI_array_count(eloop_pairs) - 1]; + lp = BLI_array_append_ret(eloop_pairs); lp->l_a = lp->l_b = NULL; return eloop_pairs; @@ -794,8 +793,6 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve BM_mesh_edgesplit(em->bm, true, true, true); } - dist_sq = FLT_MAX; - { /* --- select which vert --- */ BMVert *v_best = NULL; @@ -994,6 +991,12 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } + /* we could support this, but not for now */ + if ((bm->totvertsel > 1) && (bm->totedgesel == 0)) { + BKE_report(op->reports, RPT_ERROR, "Cannot rip multiple disconnected vertices"); + return OPERATOR_CANCELLED; + } + /* note on selection: * When calling edge split we operate on tagged edges rather then selected * this is important because the edges to operate on are extended by one, diff --git a/source/blender/editors/mesh/editmesh_rip_edge.c b/source/blender/editors/mesh/editmesh_rip_edge.c new file mode 100644 index 00000000000..5daf33fae3b --- /dev/null +++ b/source/blender/editors/mesh/editmesh_rip_edge.c @@ -0,0 +1,252 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/mesh/editmesh_rip_edge.c + * \ingroup edmesh + * + * based on mouse cursor position, split of vertices along the closest edge. + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_report.h" +#include "BKE_editmesh.h" + +#include "RNA_define.h" +#include "RNA_access.h" + +#include "WM_types.h" + +#include "ED_mesh.h" +#include "ED_screen.h" +#include "ED_transform.h" +#include "ED_view3d.h" + +#include "bmesh.h" + +#include "mesh_intern.h" /* own include */ + +/* uses total number of selected edges around a vertex to choose how to extend */ +#define USE_TRICKY_EXTEND + +static int edbm_rip_edge_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMIter viter; + BMVert *v; + const float mval_fl[2] = {UNPACK2(event->mval)}; + float cent_sco[2]; + int cent_tot; + bool changed = false; + + /* mouse direction to view center */ + float mval_dir[2]; + + float projectMat[4][4]; + + if (bm->totvertsel == 0) + return OPERATOR_CANCELLED; + + ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat); + + zero_v2(cent_sco); + cent_tot = 0; + + /* clear tags and calc screen center */ + BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { + BM_elem_flag_disable(v, BM_ELEM_TAG); + + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + float v_sco[2]; + ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat); + + add_v2_v2(cent_sco, v_sco); + cent_tot += 1; + } + } + mul_v2_fl(cent_sco, 1.0f / (float)cent_tot); + + /* not essential, but gives more expected results with edge selection */ + if (bm->totedgesel) { + /* angle against center can give odd result, + * try re-position the center to the closest edge */ + BMIter eiter; + BMEdge *e; + float dist_sq_best = len_squared_v2v2(cent_sco, mval_fl); + + BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + float e_sco[2][2]; + float cent_sco_test[2]; + float dist_sq_test; + + ED_view3d_project_float_v2_m4(ar, e->v1->co, e_sco[0], projectMat); + ED_view3d_project_float_v2_m4(ar, e->v2->co, e_sco[1], projectMat); + + closest_to_line_segment_v2(cent_sco_test, mval_fl, e_sco[0], e_sco[1]); + dist_sq_test = len_squared_v2v2(cent_sco_test, mval_fl); + if (dist_sq_test < dist_sq_best) { + dist_sq_best = dist_sq_test; + + /* we have a new screen center */ + copy_v2_v2(cent_sco, cent_sco_test); + } + } + } + } + + sub_v2_v2v2(mval_dir, mval_fl, cent_sco); + normalize_v2(mval_dir); + + /* operate on selected verts */ + BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) { + BMIter eiter; + BMEdge *e; + float v_sco[2]; + + if (BM_elem_flag_test(v, BM_ELEM_SELECT) && + BM_elem_flag_test(v, BM_ELEM_TAG) == false) + { + /* Rules for */ + float angle_best = FLT_MAX; + BMEdge *e_best = NULL; + +#ifdef USE_TRICKY_EXTEND + /* first check if we can select the edge to split based on selection-only */ + int tot_sel = 0, tot = 0; + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + e_best = e; + tot_sel += 1; + } + tot += 1; + } + } + if (tot_sel != 1) { + e_best = NULL; + } + + /* only one edge selected, operate on that */ + if (e_best) { + goto found_edge; + } + /* none selected, fall through and find one */ + else if (tot_sel == 0) { + /* pass */ + } + /* selection not 0 or 1, do nothing */ + else { + goto found_edge; + } +#endif + ED_view3d_project_float_v2_m4(ar, v->co, v_sco, projectMat); + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + BMVert *v_other = BM_edge_other_vert(e, v); + float v_other_sco[2]; + float angle_test; + + ED_view3d_project_float_v2_m4(ar, v_other->co, v_other_sco, projectMat); + + /* avoid comparing with view-axis aligned edges (less then a pixel) */ + if (len_squared_v2v2(v_sco, v_other_sco) > 1.0f) { + float v_dir[2]; + + sub_v2_v2v2(v_dir, v_other_sco, v_sco); + normalize_v2(v_dir); + + angle_test = angle_normalized_v2v2(mval_dir, v_dir); + + if (angle_test < angle_best) { + angle_best = angle_test; + e_best = e; + } + } + } + } + +#ifdef USE_TRICKY_EXTEND +found_edge: +#endif + if (e_best) { + const bool e_select = BM_elem_flag_test_bool(e_best, BM_ELEM_SELECT); + BMVert *v_new; + BMEdge *e_new; + + v_new = BM_edge_split(bm, e_best, v, &e_new, 0.0f); + + BM_vert_select_set(bm, v, false); + BM_edge_select_set(bm, e_new, false); + + BM_vert_select_set(bm, v_new, true); + if (e_select) { + BM_edge_select_set(bm, e_best, true); + } + BM_elem_flag_enable(v_new, BM_ELEM_TAG); /* prevent further splitting */ + + changed = true; + } + } + } + + if (changed) { + BM_select_history_clear(bm); + + BM_mesh_select_mode_flush(bm); + + EDBM_update_generic(em, true, true); + + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + + +void MESH_OT_rip_edge(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Extend Vertices"; + ot->idname = "MESH_OT_rip_edge"; + ot->description = "Extend vertices along the edge closest to the cursor"; + + /* api callbacks */ + ot->invoke = edbm_rip_edge_invoke; + ot->poll = EDBM_view3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* to give to transform */ + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR_DUMMY); +} diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 15bfeea8918..473da4c9756 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -66,6 +66,8 @@ #include "UI_resources.h" +#include "bmesh_tools.h" + #include "mesh_intern.h" /* own include */ /* use bmesh operator flags for a few operators */ @@ -213,7 +215,7 @@ bool EDBM_backbuf_border_init(ViewContext *vc, short xmin, short ymin, short xma a = (xmax - xmin + 1) * (ymax - ymin + 1); while (a--) { if (*dr > 0 && *dr <= bm_vertoffs) { - BLI_BITMAP_SET(selbuf, *dr); + BLI_BITMAP_ENABLE(selbuf, *dr); } dr++; } @@ -230,7 +232,7 @@ bool EDBM_backbuf_check(unsigned int index) return true; if (index > 0 && index <= bm_vertoffs) - return BLI_BITMAP_GET_BOOL(selbuf, index); + return BLI_BITMAP_TEST_BOOL(selbuf, index); return false; } @@ -297,7 +299,7 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short a = (xmax - xmin + 1) * (ymax - ymin + 1); while (a--) { if (*dr > 0 && *dr <= bm_vertoffs && *dr_mask == true) { - BLI_BITMAP_SET(selbuf, *dr); + BLI_BITMAP_ENABLE(selbuf, *dr); } dr++; dr_mask++; } @@ -340,7 +342,7 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads) for (xc = -rads; xc <= rads; xc++, dr++) { if (xc * xc + yc * yc < radsq) { if (*dr > 0 && *dr <= bm_vertoffs) { - BLI_BITMAP_SET(selbuf, *dr); + BLI_BITMAP_ENABLE(selbuf, *dr); } } } @@ -929,6 +931,96 @@ void MESH_OT_select_similar(wmOperatorType *ot) } +/* -------------------------------------------------------------------- */ +/* Select Similar Regions */ + +static int edbm_select_similar_region_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + bool changed = false; + + /* group vars */ + int *groups_array; + int (*group_index)[2]; + int group_tot; + int i; + + if (bm->totfacesel < 2) { + BKE_report(op->reports, RPT_ERROR, "No face regions selected"); + return OPERATOR_CANCELLED; + } + + groups_array = MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__); + group_tot = BM_mesh_calc_face_groups(bm, groups_array, &group_index, + NULL, NULL, + BM_ELEM_SELECT, BM_VERT); + + BM_mesh_elem_table_ensure(bm, BM_FACE); + + for (i = 0; i < group_tot; i++) { + ListBase faces_regions; + int tot; + + const int fg_sta = group_index[i][0]; + const int fg_len = group_index[i][1]; + int j; + BMFace **fg = MEM_mallocN(sizeof(*fg) * fg_len, __func__); + + + for (j = 0; j < fg_len; j++) { + fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]); + } + + tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions); + + MEM_freeN(fg); + + if (tot) { + LinkData *link; + while ((link = BLI_pophead(&faces_regions))) { + BMFace *f, **faces = link->data; + unsigned int i = 0; + while ((f = faces[i++])) { + BM_face_select_set(bm, f, true); + } + MEM_freeN(faces); + MEM_freeN(link); + + changed = true; + } + } + } + + MEM_freeN(groups_array); + + if (changed) { + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); + } + else { + BKE_report(op->reports, RPT_WARNING, "No matching face regions found"); + } + + return OPERATOR_FINISHED; +} + +void MESH_OT_select_similar_region(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Similar Regions"; + ot->idname = "MESH_OT_select_similar_region"; + ot->description = "Select similar face regions to the current selection"; + + /* api callbacks */ + ot->exec = edbm_select_similar_region_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + /* **************** Mode Select *************** */ static int edbm_select_mode_exec(bContext *C, wmOperator *op) @@ -2039,7 +2131,7 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); } - BMW_init(&walker, em->bm, BMW_SHELL, + BMW_init(&walker, em->bm, BMW_VERT_SHELL, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); @@ -2157,7 +2249,7 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE eed = eve->e; } - BMW_init(&walker, bm, BMW_SHELL, + BMW_init(&walker, bm, BMW_VERT_SHELL, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); @@ -2433,6 +2525,27 @@ void MESH_OT_select_less(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** + * Check if we're connected to another selected efge. + */ +static bool bm_edge_is_select_isolated(BMEdge *e) +{ + BMIter viter; + BMVert *v; + + BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) { + BMIter eiter; + BMEdge *e_other; + + BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) { + if ((e_other != e) && BM_elem_flag_test(e_other, BM_ELEM_SELECT)) { + return false; + } + } + } + return true; +} + /* Walk all reachable elements of the same type as h_act in breadth-first * order, starting from h_act. Deselects elements if the depth when they * are reached is not a multiple of "nth". */ @@ -2460,8 +2573,10 @@ static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h mask_vert = BMO_ELE_TAG; break; case BM_EDGE: + /* When an edge has no connected-selected edges, + * use face-stepping (supports edge-rings) */ itertype = BM_EDGES_OF_MESH; - walktype = BMW_SHELL; + walktype = bm_edge_is_select_isolated((BMEdge *)h_act) ? BMW_FACE_SHELL : BMW_VERT_SHELL; flushtype = SCE_SELECT_EDGE; mask_edge = BMO_ELE_TAG; break; @@ -2600,6 +2715,8 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op) /* so input of offset zero ends up being (nth - 1) */ offset = mod_i(offset, nth); + /* depth starts at 1, this keeps active item selected */ + offset -= 1; if (edbm_deselect_nth(em, nth, offset) == false) { BKE_report(op->reports, RPT_ERROR, "Mesh has no active vert/edge/face"); @@ -2782,6 +2899,13 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) BMEdge *e; BMIter iter; + const bool use_wire = RNA_boolean_get(op->ptr, "use_wire"); + const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary"); + const bool use_multi_face = RNA_boolean_get(op->ptr, "use_multi_face"); + const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous"); + const bool use_verts = RNA_boolean_get(op->ptr, "use_verts"); + + if (!RNA_boolean_get(op->ptr, "extend")) EDBM_flag_disable_all(em, BM_ELEM_SELECT); @@ -2794,15 +2918,30 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN) && !BM_vert_is_manifold(v)) { - BM_vert_select_set(em->bm, v, true); + if (use_verts) { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + if (!BM_vert_is_manifold(v)) { + BM_vert_select_set(em->bm, v, true); + } + } } } - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && !BM_edge_is_manifold(e)) { - BM_edge_select_set(em->bm, e, true); + if (use_wire || use_boundary || use_multi_face || use_non_contiguous) { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + if ((use_wire && BM_edge_is_wire(e)) || + (use_boundary && BM_edge_is_boundary(e)) || + (use_non_contiguous && (BM_edge_is_manifold(e) && !BM_edge_is_contiguous(e))) || + (use_multi_face && (BM_edge_face_count(e) > 2))) + { + /* check we never select perfect edge (in test above) */ + BLI_assert(!(BM_edge_is_manifold(e) && BM_edge_is_contiguous(e))); + + BM_edge_select_set(em->bm, e, true); + } + } } } @@ -2829,6 +2968,18 @@ void MESH_OT_select_non_manifold(wmOperatorType *ot) /* props */ RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection"); + /* edges */ + RNA_def_boolean(ot->srna, "use_wire", true, "Wire", + "Wire edges"); + RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", + "Boundary edges"); + RNA_def_boolean(ot->srna, "use_multi_face", true, + "Multiple Faces", "Edges shared by 3+ faces"); + RNA_def_boolean(ot->srna, "use_non_contiguous", true, "Non Contiguous", + "Edges between faces pointing in alternate directions"); + /* verts */ + RNA_def_boolean(ot->srna, "use_verts", true, "Vertices", + "Vertices connecting multiple face regions"); } static int edbm_select_random_exec(bContext *C, wmOperator *op) @@ -3164,7 +3315,7 @@ void MESH_OT_region_to_loop(wmOperatorType *ot) } static int loop_find_region(BMLoop *l, int flag, - SmallHash *fhash, BMFace ***region_out) + GSet *visit_face_set, BMFace ***region_out) { BMFace **region = NULL; BMFace **stack = NULL; @@ -3173,7 +3324,7 @@ static int loop_find_region(BMLoop *l, int flag, BMFace *f; BLI_array_append(stack, l->f); - BLI_smallhash_insert(fhash, (uintptr_t)l->f, NULL); + BLI_gset_insert(visit_face_set, l->f); while (BLI_array_count(stack) > 0) { BMIter liter1, liter2; @@ -3187,11 +3338,15 @@ static int loop_find_region(BMLoop *l, int flag, continue; BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) { - if (BLI_smallhash_haskey(fhash, (uintptr_t)l2->f)) + /* avoids finding same region twice + * (otherwise) the logic works fine without */ + if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) { continue; - - BLI_array_append(stack, l2->f); - BLI_smallhash_insert(fhash, (uintptr_t)l2->f, NULL); + } + + if (BLI_gset_add(visit_face_set, l2->f)) { + BLI_array_append(stack, l2->f); + } } } } @@ -3216,21 +3371,22 @@ static int verg_radial(const void *va, const void *vb) return 0; } +/** + * This function leaves faces tagged which are apart of the new region. + * + * \note faces already tagged are ignored, to avoid finding the same regions twice: + * important when we have regions with equal face counts, see: T40309 + */ static int loop_find_regions(BMEditMesh *em, const bool selbigger) { - SmallHash visithash; + GSet *visit_face_set; BMIter iter; const int edges_len = em->bm->totedgesel; BMEdge *e, **edges; - BMFace *f; int count = 0, i; - BLI_smallhash_init_ex(&visithash, edges_len); + visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len); edges = MEM_mallocN(sizeof(*edges) * edges_len, __func__); - - BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_flag_disable(f, BM_ELEM_TAG); - } i = 0; BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { @@ -3258,10 +3414,10 @@ static int loop_find_regions(BMEditMesh *em, const bool selbigger) continue; BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { - if (BLI_smallhash_haskey(&visithash, (uintptr_t)l->f)) + if (BLI_gset_haskey(visit_face_set, l->f)) continue; - - c = loop_find_region(l, BM_ELEM_SELECT, &visithash, ®ion_out); + + c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, ®ion_out); if (!region || (selbigger ? c >= tot : c < tot)) { /* this region is the best seen so far */ @@ -3296,7 +3452,7 @@ static int loop_find_regions(BMEditMesh *em, const bool selbigger) } MEM_freeN(edges); - BLI_smallhash_release(&visithash); + BLI_gset_free(visit_face_set, NULL); return count; } @@ -3310,13 +3466,14 @@ static int edbm_loop_to_region_exec(bContext *C, wmOperator *op) const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger"); int a, b; + /* find the set of regions with smallest number of total faces */ + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); a = loop_find_regions(em, select_bigger); b = loop_find_regions(em, !select_bigger); - - if ((a <= b) ^ select_bigger) { - loop_find_regions(em, select_bigger); - } + + BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); + loop_find_regions(em, ((a <= b) != select_bigger) ? select_bigger : !select_bigger); EDBM_flag_disable_all(em, BM_ELEM_SELECT); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 159eac4a275..20c7f4eb521 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -881,12 +881,18 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) int len; if (is_pair) { - if (!EDBM_op_init(em, &bmop, op, "connect_vert_pair verts=%hv", BM_ELEM_SELECT)) { + if (!EDBM_op_init(em, &bmop, op, + "connect_vert_pair verts=%hv verts_exclude=%hv faces_exclude=%hf", + BM_ELEM_SELECT, BM_ELEM_HIDDEN, BM_ELEM_HIDDEN)) + { return OPERATOR_CANCELLED; } } else { - if (!EDBM_op_init(em, &bmop, op, "connect_verts verts=%hv check_degenerate=%b", BM_ELEM_SELECT, true)) { + if (!EDBM_op_init(em, &bmop, op, + "connect_verts verts=%hv faces_exclude=%hf check_degenerate=%b", + BM_ELEM_SELECT, BM_ELEM_HIDDEN, true)) + { return OPERATOR_CANCELLED; } } @@ -1019,28 +1025,23 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(ob); BMesh *bm = em->bm; BMOperator bmop; - ListBase bm_selected_store = {NULL, NULL}; - /* de-select all would clear otherwise */ - SWAP(ListBase, bm->selected, bm_selected_store); + EDBM_op_init( + em, &bmop, op, + "duplicate geom=%hvef use_select_history=%b", + BM_ELEM_SELECT, true); - EDBM_op_init(em, &bmop, op, "duplicate geom=%hvef", BM_ELEM_SELECT); - BMO_op_exec(bm, &bmop); + + /* de-select all would clear otherwise */ + BM_SELECT_HISTORY_BACKUP(bm); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "geom.out", BM_ALL_NOLOOP, BM_ELEM_SELECT, true); /* rebuild editselection */ - bm->selected = bm_selected_store; - - if (bm->selected.first) { - BMOpSlot *slot_vert_map_out = BMO_slot_get(bmop.slots_out, "vert_map.out"); - BMOpSlot *slot_edge_map_out = BMO_slot_get(bmop.slots_out, "edge_map.out"); - BMOpSlot *slot_face_map_out = BMO_slot_get(bmop.slots_out, "face_map.out"); - - BMO_mesh_selected_remap(bm, slot_vert_map_out, slot_edge_map_out, slot_face_map_out); - } + BM_SELECT_HISTORY_RESTORE(bm); if (!EDBM_op_finish(em, &bmop, op, true)) { return OPERATOR_CANCELLED; @@ -1287,7 +1288,8 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) bool mirrx = false, mirry = false, mirrz = false; int i, repeat; float clip_dist = 0.0f; - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + const float fac = RNA_float_get(op->ptr, "factor"); + const bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; const bool xaxis = RNA_boolean_get(op->ptr, "xaxis"); const bool yaxis = RNA_boolean_get(op->ptr, "yaxis"); @@ -1321,12 +1323,12 @@ static int edbm_do_smooth_vertex_exec(bContext *C, wmOperator *op) repeat = RNA_int_get(op->ptr, "repeat"); if (!repeat) repeat = 1; - + for (i = 0; i < repeat; i++) { if (!EDBM_op_callf(em, op, - "smooth_vert verts=%hv mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b clip_dist=%f " - "use_axis_x=%b use_axis_y=%b use_axis_z=%b", - BM_ELEM_SELECT, mirrx, mirry, mirrz, clip_dist, xaxis, yaxis, zaxis)) + "smooth_vert verts=%hv factor=%f mirror_clip_x=%b mirror_clip_y=%b mirror_clip_z=%b " + "clip_dist=%f use_axis_x=%b use_axis_y=%b use_axis_z=%b", + BM_ELEM_SELECT, fac, mirrx, mirry, mirrz, clip_dist, xaxis, yaxis, zaxis)) { return OPERATOR_CANCELLED; } @@ -1357,7 +1359,8 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int(ot->srna, "repeat", 1, 1, 1000, "Number of times to smooth the mesh", "", 1, 100); + RNA_def_float(ot->srna, "factor", 0.5f, -10.0f, 10.0f, "Smoothing", "Smoothing factor", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 1000, "Repeat", "Number of times to smooth the mesh", 1, 100); RNA_def_boolean(ot->srna, "xaxis", 1, "X-Axis", "Smooth along the X axis"); RNA_def_boolean(ot->srna, "yaxis", 1, "Y-Axis", "Smooth along the Y axis"); RNA_def_boolean(ot->srna, "zaxis", 1, "Z-Axis", "Smooth along the Z axis"); @@ -2424,7 +2427,7 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) float (*screen_vert_coords)[2], (*sco)[2], (*mouse_path)[2]; /* edit-object needed for matrix, and ar->regiondata for projections to work */ - if (ELEM3(NULL, obedit, ar, ar->regiondata)) + if (ELEM(NULL, obedit, ar, ar->regiondata)) return OPERATOR_CANCELLED; if (bm->totvertsel < 2) { @@ -2460,7 +2463,7 @@ static int edbm_knife_cut_exec(bContext *C, wmOperator *op) if (ED_view3d_project_float_object(ar, bv->co, *sco, V3D_PROJ_TEST_CLIP_NEAR) != V3D_PROJ_RET_OK) { copy_v2_fl(*sco, FLT_MAX); /* set error value */ } - BM_elem_index_set(bv, i); /* set_ok */ + BM_elem_index_set(bv, i); /* set_inline */ sco++; } @@ -2783,7 +2786,7 @@ static bool mesh_separate_loose(Main *bmain, Scene *scene, Base *base_old, BMesh /* Walk from the single vertex, selecting everything connected * to it */ - BMW_init(&walker, bm_old, BMW_SHELL, + BMW_init(&walker, bm_old, BMW_VERT_SHELL, BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_NOP, BMW_NIL_LAY); @@ -3047,7 +3050,7 @@ static void edbm_fill_grid_prepare(BMesh *bm, int offset, int *r_span, bool span } /* set this vertex first */ - BLI_rotatelist_first(verts, v_act_link); + BLI_listbase_rotate_first(verts, v_act_link); BM_edgeloop_edges_get(el_store, edges); @@ -3479,6 +3482,11 @@ static void edbm_dissolve_prop__use_face_split(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_face_split", 0, "Face Split", "Split off face corners to maintain surrounding geometry"); } +static void edbm_dissolve_prop__use_boundary_tear(wmOperatorType *ot) +{ + RNA_def_boolean(ot->srna, "use_boundary_tear", 0, "Tear Boundary", + "Split off face corners instead of merging faces"); +} static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) { @@ -3486,9 +3494,14 @@ static int edbm_dissolve_verts_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); const bool use_face_split = RNA_boolean_get(op->ptr, "use_face_split"); + const bool use_boundary_tear = RNA_boolean_get(op->ptr, "use_boundary_tear"); - if (!EDBM_op_callf(em, op, "dissolve_verts verts=%hv use_face_split=%b", BM_ELEM_SELECT, use_face_split)) + if (!EDBM_op_callf(em, op, + "dissolve_verts verts=%hv use_face_split=%b use_boundary_tear=%b", + BM_ELEM_SELECT, use_face_split, use_boundary_tear)) + { return OPERATOR_CANCELLED; + } EDBM_update_generic(em, true, true); @@ -3510,6 +3523,7 @@ void MESH_OT_dissolve_verts(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; edbm_dissolve_prop__use_face_split(ot); + edbm_dissolve_prop__use_boundary_tear(ot); } static int edbm_dissolve_edges_exec(bContext *C, wmOperator *op) @@ -3621,6 +3635,7 @@ void MESH_OT_dissolve_mode(wmOperatorType *ot) edbm_dissolve_prop__use_verts(ot); edbm_dissolve_prop__use_face_split(ot); + edbm_dissolve_prop__use_boundary_tear(ot); } static int edbm_dissolve_limited_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 758cbd59794..c7d1d883537 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -1269,7 +1269,7 @@ void EDBM_mesh_reveal(BMEditMesh *em) /* Use tag flag to remember what was hidden before all is revealed. * BM_ELEM_HIDDEN --> BM_ELEM_TAG */ -#pragma omp parallel for schedule(dynamic) if (em->bm->totvert + em->bm->totedge + em->bm->totface >= BM_OMP_LIMIT) +#pragma omp parallel for schedule(static) if (em->bm->totvert + em->bm->totedge + em->bm->totface >= BM_OMP_LIMIT) for (i = 0; i < 3; i++) { BMIter iter; BMElem *ele; @@ -1328,6 +1328,15 @@ void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_d /* don't keep stale derivedMesh data around, see: [#38872] */ BKE_editmesh_free_derivedmesh(em); + +#ifdef DEBUG + { + BMEditSelection *ese; + for (ese = em->bm->selected.first; ese; ese = ese->next) { + BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)); + } + } +#endif } /* poll call for mesh operators requiring a view3d context */ diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 83947534d06..68471bfc2ba 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -47,6 +47,7 @@ #include "BKE_library.h" #include "BKE_main.h" #include "BKE_mesh.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_editmesh.h" @@ -502,6 +503,12 @@ static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op)) if (ED_mesh_uv_texture_add(me, NULL, true) == -1) return OPERATOR_CANCELLED; + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + Scene *scene = CTX_data_scene(C); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + return OPERATOR_FINISHED; } @@ -622,6 +629,12 @@ static int mesh_uv_texture_remove_exec(bContext *C, wmOperator *UNUSED(op)) if (!ED_mesh_uv_texture_remove_active(me)) return OPERATOR_CANCELLED; + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + Scene *scene = CTX_data_scene(C); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + return OPERATOR_FINISHED; } @@ -880,17 +893,6 @@ static void mesh_add_verts(Mesh *mesh, int len) mesh->totvert = totvert; } -void ED_mesh_transform(Mesh *me, float mat[4][4]) -{ - int i; - MVert *mvert = me->mvert; - - for (i = 0; i < me->totvert; i++, mvert++) - mul_m4_v3(mat, mvert->co); - - /* don't update normals, caller can do this explicitly */ -} - static void mesh_add_edges(Mesh *mesh, int len) { CustomData edata; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 8aea932e93a..6ba91097ec4 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -112,6 +112,10 @@ void MESH_OT_screw(struct wmOperatorType *ot); /* *** editmesh_inset.c *** */ void MESH_OT_inset(struct wmOperatorType *ot); +/* *** editmesh_intersect.c *** */ +void MESH_OT_intersect(struct wmOperatorType *ot); +void MESH_OT_face_split_by_edges(struct wmOperatorType *ot); + /* *** editmesh_knife.c *** */ void MESH_OT_knife_tool(struct wmOperatorType *ot); @@ -128,10 +132,12 @@ void MESH_OT_loopcut(struct wmOperatorType *ot); /* *** editmesh_rip.c *** */ void MESH_OT_rip(struct wmOperatorType *ot); +void MESH_OT_rip_edge(struct wmOperatorType *ot); /* *** editmesh_select.c *** */ void MESH_OT_select_similar(struct wmOperatorType *ot); +void MESH_OT_select_similar_region(struct wmOperatorType *ot); void MESH_OT_select_mode(struct wmOperatorType *ot); void MESH_OT_loop_multi_select(struct wmOperatorType *ot); void MESH_OT_loop_select(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_navmesh.c b/source/blender/editors/mesh/mesh_navmesh.c index 433fd176217..440ab14dacd 100644 --- a/source/blender/editors/mesh/mesh_navmesh.c +++ b/source/blender/editors/mesh/mesh_navmesh.c @@ -407,7 +407,7 @@ static Object *createRepresentation(bContext *C, struct recast_polyMesh *pmesh, BM_vert_at_index(em->bm, face[0]), BM_vert_at_index(em->bm, face[2]), BM_vert_at_index(em->bm, face[1]), NULL, - NULL, false); + NULL, BM_CREATE_NOP); /* set navigation polygon idx to the custom layer */ polygonIdx = (int *)CustomData_bmesh_get(&em->bm->pdata, newFace->head.data, CD_RECAST); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 7c38ef21791..e7dc5a69d53 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -130,6 +130,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_edge_face_add); WM_operatortype_append(MESH_OT_shortest_path_pick); WM_operatortype_append(MESH_OT_select_similar); + WM_operatortype_append(MESH_OT_select_similar_region); WM_operatortype_append(MESH_OT_select_mode); WM_operatortype_append(MESH_OT_loop_multi_select); WM_operatortype_append(MESH_OT_mark_seam); @@ -142,6 +143,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_noise); WM_operatortype_append(MESH_OT_flip_normals); WM_operatortype_append(MESH_OT_rip); + WM_operatortype_append(MESH_OT_rip_edge); WM_operatortype_append(MESH_OT_blend_from_shape); WM_operatortype_append(MESH_OT_shape_propagate_to_all); @@ -169,6 +171,8 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); + WM_operatortype_append(MESH_OT_intersect); + WM_operatortype_append(MESH_OT_face_split_by_edges); WM_operatortype_append(MESH_OT_poke); WM_operatortype_append(MESH_OT_wireframe); WM_operatortype_append(MESH_OT_edge_split); @@ -239,6 +243,13 @@ void ED_operatormacros_mesh(void) RNA_enum_set(otmacro->ptr, "proportional", 0); RNA_boolean_set(otmacro->ptr, "mirror", false); + ot = WM_operatortype_append_macro("MESH_OT_rip_edge_move", "Extend Vertices", "Extend vertices and move the result", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_rip_edge"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + RNA_enum_set(otmacro->ptr, "proportional", 0); + RNA_boolean_set(otmacro->ptr, "mirror", false); + ot = WM_operatortype_append_macro("MESH_OT_extrude_region_move", "Extrude Region and Move", "Extrude region and move result", OPTYPE_UNDO | OPTYPE_REGISTER); otmacro = WM_operatortype_macro_define(ot, "MESH_OT_extrude_region"); @@ -332,7 +343,7 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "MESH_OT_faces_select_linked_flat", FKEY, KM_PRESS, (KM_CTRL | KM_SHIFT | KM_ALT), 0); - WM_keymap_add_item(keymap, "MESH_OT_select_similar", GKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_mesh_select_similar", GKEY, KM_PRESS, KM_SHIFT, 0); /* selection mode */ WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_mesh_select_mode", TABKEY, KM_PRESS, KM_CTRL, 0); @@ -372,6 +383,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "MESH_OT_rip_move", VKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "MESH_OT_rip_move_fill", VKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "MESH_OT_rip_edge_move", DKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "MESH_OT_merge", MKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "TRANSFORM_OT_shrink_fatten", SKEY, KM_PRESS, KM_ALT, 0); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index d252ae20270..16f4f61f92b 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -999,7 +999,7 @@ static int mirror_facerotation(MFace *a, MFace *b) return -1; } -static int mirror_facecmp(const void *a, const void *b) +static bool mirror_facecmp(const void *a, const void *b) { return (mirror_facerotation((MFace *)a, (MFace *)b) == -1); } diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 1441a1ef4c0..f6a54beb8c8 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -191,7 +191,7 @@ enum { static EnumPropertyItem prop_similar_types[] = { {SIMMBALL_TYPE, "TYPE", 0, "Type", ""}, {SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""}, - {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""}, + {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""}, {SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""}, {0, NULL, 0, NULL, NULL} }; @@ -594,7 +594,7 @@ bool mouse_mball(bContext *C, const int mval[2], bool extend, bool deselect, boo rect.ymin = mval[1] - 12; rect.ymax = mval[1] + 12; - hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect); + hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect, true); /* does startelem exist? */ ml = mb->editelems->first; @@ -743,28 +743,3 @@ void undo_push_mball(bContext *C, const char *name) { undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL); } - -void ED_mball_transform(MetaBall *mb, float mat[4][4]) -{ - MetaElem *me; - float quat[4]; - const float scale = mat4_to_scale(mat); - const float scale_sqrt = sqrtf(scale); - - mat4_to_quat(quat, mat); - - for (me = mb->elems.first; me; me = me->next) { - mul_m4_v3(mat, &me->x); - mul_qt_qtqt(me->quat, quat, me->quat); - me->rad *= scale; - /* hrmf, probably elems shouldn't be - * treating scale differently - campbell */ - if (!MB_TYPE_SIZE_SQUARED(me->type)) { - mul_v3_fl(&me->expx, scale); - } - else { - mul_v3_fl(&me->expx, scale_sqrt); - } - } - DAG_id_tag_update(&mb->id, 0); -} diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 1bb35b65918..9b380ff8d48 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -33,10 +33,11 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS - + ${GLEW_INCLUDE_PATH} ) set(SRC @@ -62,6 +63,8 @@ set(SRC object_intern.h ) +add_definitions(${GL_DEFINITIONS}) + if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 99dd8b75609..8972dd7cf08 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -34,6 +34,7 @@ #include "MEM_guardedalloc.h" #include "DNA_anim_types.h" +#include "DNA_camera_types.h" #include "DNA_curve_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" @@ -83,6 +84,7 @@ #include "BKE_report.h" #include "BKE_sca.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BKE_speaker.h" #include "BKE_texture.h" @@ -95,6 +97,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_lattice.h" #include "ED_mball.h" #include "ED_mesh.h" #include "ED_node.h" @@ -336,10 +339,7 @@ bool ED_object_add_generic_get_opts(bContext *C, wmOperator *op, const char view } else { Scene *scene = CTX_data_scene(C); - if (v3d) - *layer = (v3d->scenelock && !v3d->localvd) ? scene->layact : v3d->layact; - else - *layer = scene->layact; + *layer = BKE_screen_view3d_layer_active_ex(v3d, scene, false); for (a = 0; a < 20; a++) { layer_values[a] = *layer & (1 << a); } @@ -446,14 +446,17 @@ Object *ED_object_add_type(bContext *C, int type, const float loc[3], const floa /* for object add operator */ static int object_add_exec(bContext *C, wmOperator *op) { + Object *ob; bool enter_editmode; unsigned int layer; float loc[3], rot[3]; + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) return OPERATOR_CANCELLED; - ED_object_add_type(C, RNA_enum_get(op->ptr, "type"), loc, rot, enter_editmode, layer); + ob = ED_object_add_type(C, RNA_enum_get(op->ptr, "type"), loc, rot, enter_editmode, layer); + BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); return OPERATOR_FINISHED; } @@ -472,6 +475,8 @@ void OBJECT_OT_add(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* properties */ + ED_object_add_unit_props(ot); RNA_def_enum(ot->srna, "type", object_type_items, 0, "Type", ""); ED_object_add_generic_props(ot, true); @@ -488,32 +493,31 @@ static int effector_add_exec(bContext *C, wmOperator *op) unsigned int layer; float loc[3], rot[3]; float mat[4][4]; + float dia; + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) return OPERATOR_CANCELLED; type = RNA_enum_get(op->ptr, "type"); + dia = RNA_float_get(op->ptr, "radius"); if (type == PFIELD_GUIDE) { Curve *cu; ob = ED_object_add_type(C, OB_CURVE, loc, rot, false, layer); - if (!ob) - return OPERATOR_CANCELLED; rename_id(&ob->id, CTX_DATA_(BLF_I18NCONTEXT_ID_OBJECT, "CurveGuide")); cu = ob->data; cu->flag |= CU_PATH | CU_3D; ED_object_editmode_enter(C, 0); ED_object_new_primitive_matrix(C, ob, loc, rot, mat, false); - BLI_addtail(&cu->editnurb->nurbs, add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); + BLI_addtail(&cu->editnurb->nurbs, add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, dia)); if (!enter_editmode) ED_object_editmode_exit(C, EM_FREEDATA); } else { ob = ED_object_add_type(C, OB_EMPTY, loc, rot, false, layer); - if (!ob) - return OPERATOR_CANCELLED; - + BKE_object_obdata_size_init(ob, dia); rename_id(&ob->id, CTX_DATA_(BLF_I18NCONTEXT_ID_OBJECT, "Field")); if (ELEM(type, PFIELD_WIND, PFIELD_VORTEX)) ob->empty_drawtype = OB_SINGLE_ARROW; @@ -540,8 +544,10 @@ void OBJECT_OT_effector_add(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", field_type_items, 0, "Type", ""); + ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, true); } @@ -552,6 +558,7 @@ static int object_camera_add_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); Object *ob; + Camera *cam; bool enter_editmode; unsigned int layer; float loc[3], rot[3]; @@ -572,6 +579,9 @@ static int object_camera_add_exec(bContext *C, wmOperator *op) } } + cam = ob->data; + cam->drawsize = v3d ? ED_view3d_grid_scale(scene, v3d, NULL) : ED_scene_grid_scale(scene, NULL); + return OPERATOR_FINISHED; } @@ -668,6 +678,7 @@ static int object_add_text_exec(bContext *C, wmOperator *op) unsigned int layer; float loc[3], rot[3]; + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) return OPERATOR_CANCELLED; @@ -675,6 +686,7 @@ static int object_add_text_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; obedit = ED_object_add_type(C, OB_FONT, loc, rot, enter_editmode, layer); + BKE_object_obdata_size_init(obedit, RNA_float_get(op->ptr, "radius")); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obedit); @@ -694,6 +706,9 @@ void OBJECT_OT_text_add(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, true); } @@ -706,9 +721,10 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) bool newob = false; bool enter_editmode; unsigned int layer; - float loc[3], rot[3]; + float loc[3], rot[3], dia; bool view_aligned = rv3d && (U.flag & USER_ADD_VIEWALIGNED); + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) return OPERATOR_CANCELLED; @@ -726,7 +742,8 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - add_primitive_bone(obedit, view_aligned); + dia = RNA_float_get(op->ptr, "radius"); + ED_armature_edit_bone_add_primitive(obedit, dia, view_aligned); /* userdef */ if (newob && !enter_editmode) @@ -750,6 +767,9 @@ void OBJECT_OT_armature_add(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, true); } @@ -762,12 +782,14 @@ static int object_empty_add_exec(bContext *C, wmOperator *op) unsigned int layer; float loc[3], rot[3]; + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &layer, NULL)) return OPERATOR_CANCELLED; ob = ED_object_add_type(C, OB_EMPTY, loc, rot, false, layer); BKE_object_empty_draw_type_set(ob, type); + BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); return OPERATOR_FINISHED; } @@ -790,6 +812,7 @@ void OBJECT_OT_empty_add(wmOperatorType *ot) /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", object_empty_drawtype_items, 0, "Type", ""); + ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, false); } @@ -899,12 +922,14 @@ static int object_lamp_add_exec(bContext *C, wmOperator *op) unsigned int layer; float loc[3], rot[3]; + WM_operator_view3d_unit_defaults(C, op); if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, NULL, &layer, NULL)) return OPERATOR_CANCELLED; ob = ED_object_add_type(C, OB_LAMP, loc, rot, false, layer); - la = (Lamp *)ob->data; + BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); + la = (Lamp *)ob->data; la->type = type; rename_id(&ob->id, get_lamp_defname(type)); rename_id(&la->id, get_lamp_defname(type)); @@ -936,6 +961,7 @@ void OBJECT_OT_lamp_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", lamp_type_items, 0, "Type", ""); RNA_def_property_translation_context(ot->prop, BLF_I18NCONTEXT_ID_LAMP); + ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, false); } @@ -1470,7 +1496,7 @@ static void convert_ensure_curve_cache(Main *bmain, Scene *scene, Object *ob) /* Force creation. This is normally not needed but on operator * redo we might end up with an object which isn't evaluated yet. */ - if (ELEM3(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { + if (ELEM(ob->type, OB_SURF, OB_CURVE, OB_FONT)) { BKE_displist_make_curveTypes(scene, ob, false); } else if (ob->type == OB_MBALL) { @@ -2191,6 +2217,7 @@ static int add_named_exec(bContext *C, wmOperator *op) wmWindow *win = CTX_wm_window(C); const wmEvent *event = win ? win->eventstate : NULL; Main *bmain = CTX_data_main(C); + View3D *v3d = CTX_wm_view3d(C); /* may be NULL */ Scene *scene = CTX_data_scene(C); Base *basen, *base; Object *ob; @@ -2223,7 +2250,7 @@ static int add_named_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - basen->lay = basen->object->lay = scene->lay; + basen->lay = basen->object->lay = BKE_screen_view3d_layer_active(v3d, scene); basen->object->restrictflag &= ~OB_RESTRICT_VIEW; if (event) { @@ -2274,7 +2301,7 @@ static int join_poll(bContext *C) if (!ob || ob->id.lib) return 0; - if (ELEM4(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE)) + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE)) return ED_operator_screenactive(C); else return 0; diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 94574e81b81..b8957514159 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -682,6 +682,7 @@ static void finish_bake_internal(BakeRender *bkr) } BKE_image_release_ibuf(ima, ibuf, NULL); + DAG_id_tag_update(&ima->id, 0); } } @@ -770,7 +771,7 @@ static int objects_bake_render_modal(bContext *C, wmOperator *UNUSED(op), const static bool is_multires_bake(Scene *scene) { - if (ELEM4(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE, RE_BAKE_AO)) + if (ELEM(scene->r.bake_mode, RE_BAKE_NORMALS, RE_BAKE_DISPLACEMENT, RE_BAKE_DERIVATIVE, RE_BAKE_AO)) return scene->r.bake_flag & R_BAKE_MULTIRES; return 0; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 46555f89da9..5746f9efd56 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -51,9 +51,12 @@ #include "BKE_image.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_node.h" #include "BKE_report.h" #include "BKE_modifier.h" #include "BKE_mesh.h" +#include "BKE_screen.h" +#include "BKE_depsgraph.h" #include "RE_engine.h" #include "RE_pipeline.h" @@ -73,6 +76,63 @@ #include "object_intern.h" + +typedef struct BakeAPIRender { + Object *ob; + Main *main; + Scene *scene; + ReportList *reports; + ListBase selected_objects; + + ScenePassType pass_type; + int margin; + + int save_mode; + + bool is_clear; + bool is_split_materials; + bool is_automatic_name; + bool is_selected_to_active; + bool is_cage; + + float cage_extrusion; + int normal_space; + BakeNormalSwizzle normal_swizzle[3]; + + char uv_layer[MAX_CUSTOMDATA_LAYER_NAME]; + char custom_cage[MAX_NAME]; + char filepath[FILE_MAX]; + + int width; + int height; + const char *identifier; + + int result; + bool ready; + + /* callbacks */ + Render *render; + float *progress; + short *do_update; + + /* for redrawing */ + ScrArea *sa; +} BakeAPIRender; + +/* callbacks */ + +static void bake_progress_update(void *bjv, float progress) +{ + BakeAPIRender *bj = bjv; + + if (bj->progress && *bj->progress != progress) { + *bj->progress = progress; + + /* make jobs timer to send notifier */ + *(bj->do_update) = true; + } +} + /* catch esc */ static int bake_modal(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { @@ -100,6 +160,16 @@ static int bake_break(void *UNUSED(rjv)) return 0; } + +static void bake_update_image(ScrArea *sa, Image *image) +{ + if (sa && sa->spacetype == SPACE_IMAGE) { /* in case the user changed while baking */ + SpaceImage *sima = sa->spacedata.first; + if (sima) + sima->image = image; + } +} + static bool write_internal_bake_pixels( Image *image, BakePixel pixel_array[], float *buffer, const int width, const int height, const int margin, @@ -109,7 +179,7 @@ static bool write_internal_bake_pixels( void *lock; bool is_float; char *mask_buffer = NULL; - const int num_pixels = width * height; + const size_t num_pixels = (size_t)width * (size_t)height; ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); @@ -145,7 +215,7 @@ static bool write_internal_bake_pixels( IMB_buffer_float_from_float( ibuf->rect_float, buffer, ibuf->channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, - ibuf->x, ibuf->y, ibuf->x, ibuf->y); + ibuf->x, ibuf->y, ibuf->x, ibuf->x); } else { IMB_buffer_byte_from_float( @@ -158,7 +228,7 @@ static bool write_internal_bake_pixels( if (is_float) { IMB_buffer_float_from_float_mask( ibuf->rect_float, buffer, ibuf->channels, - ibuf->x, ibuf->y, ibuf->x, ibuf->y, mask_buffer); + ibuf->x, ibuf->y, ibuf->x, ibuf->x, mask_buffer); } else { IMB_buffer_byte_from_float_mask( @@ -191,13 +261,14 @@ static bool write_internal_bake_pixels( } /* force OpenGL reload */ -static void reset_images_gpu(BakeImages *bake_images) +static void refresh_images(BakeImages *bake_images) { int i; for (i = 0; i < bake_images->size; i++) { Image *ima = bake_images->data[i].image; if (ima->ok == IMA_OK_LOADED) { GPU_free_image(ima); + DAG_id_tag_update(&ima->id, 0); } } } @@ -224,7 +295,7 @@ static bool write_external_bake_pixels( IMB_buffer_float_from_float( ibuf->rect_float, buffer, ibuf->channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, - ibuf->x, ibuf->y, ibuf->x, ibuf->y); + ibuf->x, ibuf->y, ibuf->x, ibuf->x); } else { if (!is_noncolor) { @@ -242,7 +313,7 @@ static bool write_external_bake_pixels( /* margins */ if (margin > 0) { char *mask_buffer = NULL; - const int num_pixels = width * height; + const size_t num_pixels = (size_t)width * (size_t)height; mask_buffer = MEM_callocN(sizeof(char) * num_pixels, "Bake Mask"); RE_bake_mask_fill(pixel_array, num_pixels, mask_buffer); @@ -267,44 +338,161 @@ static bool write_external_bake_pixels( static bool is_noncolor_pass(ScenePassType pass_type) { - return ELEM7(pass_type, - SCE_PASS_Z, - SCE_PASS_NORMAL, - SCE_PASS_VECTOR, - SCE_PASS_INDEXOB, - SCE_PASS_UV, - SCE_PASS_RAYHITS, - SCE_PASS_INDEXMA); + return ELEM(pass_type, + SCE_PASS_Z, + SCE_PASS_NORMAL, + SCE_PASS_VECTOR, + SCE_PASS_INDEXOB, + SCE_PASS_UV, + SCE_PASS_RAYHITS, + SCE_PASS_INDEXMA); } -static bool build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images, ReportList *reports) +/* if all is good tag image and return true */ +static bool bake_object_check(Object *ob, ReportList *reports) { - const int tot_mat = ob->totcol; - int i, j; - int tot_images = 0; + Image *image; + void *lock; + int i; - /* error handling and tag (in case multiple materials share the same image) */ - BKE_main_id_tag_idcode(bmain, ID_IM, false); + if (ob->type != OB_MESH) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob->id.name + 2); + return false; + } + else { + Mesh *me = (Mesh *)ob->data; - for (i = 0; i < tot_mat; i++) { - Image *image; - ED_object_get_active_image(ob, i + 1, &image, NULL, NULL); + if (CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV) == -1) { + BKE_reportf(reports, RPT_ERROR, + "No active UV layer found in the object \"%s\"", ob->id.name + 2); + return false; + } + } - if (!image) { + for (i = 0; i < ob->totcol; i++) { + bNodeTree *ntree = NULL; + bNode *node = NULL; + ED_object_get_active_image(ob, i + 1, &image, NULL, &node, &ntree); + + if (image) { + ImBuf *ibuf; + + if (node) { + if (BKE_node_is_connected_to_output(ntree, node)) { + BKE_reportf(reports, RPT_ERROR, + "Circular dependency for image \"%s\" from object \"%s\"", + image->id.name + 2, ob->id.name + 2); + } + } + + ibuf = BKE_image_acquire_ibuf(image, NULL, &lock); + + if (ibuf) { + BKE_image_release_ibuf(image, ibuf, lock); + } + else { + BKE_reportf(reports, RPT_ERROR, + "Uninitialized image \"%s\" from object \"%s\"", + image->id.name + 2, ob->id.name + 2); + + BKE_image_release_ibuf(image, ibuf, lock); + return false; + } + } + else { if (ob->mat[i]) { BKE_reportf(reports, RPT_ERROR, - "No active image found in material %d (%s)", i, ob->mat[i]->id.name + 2); + "No active image found in material \"%s\" (%d) for object \"%s\"", + ob->mat[i]->id.name + 2, i, ob->id.name + 2); } else if (((Mesh *) ob->data)->mat[i]) { BKE_reportf(reports, RPT_ERROR, - "No active image found in material %d (%s)", i, ((Mesh *) ob->data)->mat[i]->id.name + 2); + "No active image found in material \"%s\" (%d) for object \"%s\"", + ((Mesh *) ob->data)->mat[i]->id.name + 2, i, ob->id.name + 2); } else { BKE_reportf(reports, RPT_ERROR, - "No active image found in material %d", i); + "No active image found in material (%d) for object \"%s\"", + i, ob->id.name + 2); + } + return false; + } + + image->id.flag |= LIB_DOIT; + } + return true; +} + +/* before even getting in the bake function we check for some basic errors */ +static bool bake_objects_check(Main *bmain, Object *ob, ListBase *selected_objects, + ReportList *reports, const bool is_selected_to_active) +{ + CollectionPointerLink *link; + + /* error handling and tag (in case multiple materials share the same image) */ + BKE_main_id_tag_idcode(bmain, ID_IM, false); + + if (is_selected_to_active) { + int tot_objects = 0; + + if (!bake_object_check(ob, reports)) + return false; + + for (link = selected_objects->first; link; link = link->next) { + Object *ob_iter = (Object *)link->ptr.data; + + if (ob_iter == ob) + continue; + + if (ELEM(ob_iter->type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL) == false) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh or can't be converted to a mesh (Curve, Text, Surface or Metaball)", ob_iter->id.name + 2); + return false; } + tot_objects += 1; + } + + if (tot_objects == 0) { + BKE_report(reports, RPT_ERROR, "No valid selected objects"); return false; } + } + else { + if (BLI_listbase_is_empty(selected_objects)) { + BKE_report(reports, RPT_ERROR, "No valid selected objects"); + return false; + } + + for (link = selected_objects->first; link; link = link->next) { + if (!bake_object_check(link->ptr.data, reports)) + return false; + } + } + return true; +} + +/* it needs to be called after bake_objects_check since the image tagging happens there */ +static void bake_images_clear(Main *bmain, const bool is_tangent) +{ + Image *image; + for (image = bmain->image.first; image; image = image->id.next) { + if ((image->id.flag & LIB_DOIT) != 0) { + RE_bake_ibuf_clear(image, is_tangent); + } + } +} + +static void build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images) +{ + const int tot_mat = ob->totcol; + int i, j; + int tot_images = 0; + + /* error handling and tag (in case multiple materials share the same image) */ + BKE_main_id_tag_idcode(bmain, ID_IM, false); + + for (i = 0; i < tot_mat; i++) { + Image *image; + ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); if ((image->id.flag & LIB_DOIT)) { for (j = 0; j < i; j++) { @@ -323,16 +511,15 @@ static bool build_image_lookup(Main *bmain, Object *ob, BakeImages *bake_images, } bake_images->size = tot_images; - return true; } /* * returns the total number of pixels */ -static int initialize_internal_images(BakeImages *bake_images, ReportList *reports) +static size_t initialize_internal_images(BakeImages *bake_images, ReportList *reports) { int i; - int tot_size = 0; + size_t tot_size = 0; for (i = 0; i < bake_images->size; i++) { ImBuf *ibuf; @@ -346,11 +533,11 @@ static int initialize_internal_images(BakeImages *bake_images, ReportList *repor bk_image->height = ibuf->y; bk_image->offset = tot_size; - tot_size += ibuf->x * ibuf->y; + tot_size += (size_t)ibuf->x * (size_t)ibuf->y; } else { BKE_image_release_ibuf(bk_image->image, ibuf, lock); - BKE_reportf(reports, RPT_ERROR, "Not initialized image %s", bk_image->image->id.name + 2); + BKE_reportf(reports, RPT_ERROR, "Uninitialized image %s", bk_image->image->id.name + 2); return 0; } BKE_image_release_ibuf(bk_image->image, ibuf, lock); @@ -358,60 +545,28 @@ static int initialize_internal_images(BakeImages *bake_images, ReportList *repor return tot_size; } -typedef struct BakeAPIRender { - Object *ob; - Main *main; - Scene *scene; - ReportList *reports; - ListBase selected_objects; - - ScenePassType pass_type; - int margin; - - int save_mode; - - bool is_clear; - bool is_split_materials; - bool is_automatic_name; - bool use_selected_to_active; - - float cage_extrusion; - int normal_space; - BakeNormalSwizzle normal_swizzle[3]; - - char custom_cage[MAX_NAME]; - char filepath[FILE_MAX]; - - int width; - int height; - const char *identifier; - - int result; - bool ready; -} BakeAPIRender; - static int bake( - Main *bmain, Scene *scene, Object *ob_low, ListBase *selected_objects, ReportList *reports, + Render *re, Main *bmain, Scene *scene, Object *ob_low, ListBase *selected_objects, ReportList *reports, const ScenePassType pass_type, const int margin, const BakeSaveMode save_mode, const bool is_clear, const bool is_split_materials, - const bool is_automatic_name, const bool use_selected_to_active, + const bool is_automatic_name, const bool is_selected_to_active, const bool is_cage, const float cage_extrusion, const int normal_space, const BakeNormalSwizzle normal_swizzle[], const char *custom_cage, const char *filepath, const int width, const int height, - const char *identifier) + const char *identifier, ScrArea *sa, const char *uv_layer) { int op_result = OPERATOR_CANCELLED; bool ok = false; Object *ob_cage = NULL; - BakeHighPolyData *highpoly; + BakeHighPolyData *highpoly = NULL; int tot_highpoly; char restrict_flag_low = ob_low->restrictflag; - char restrict_flag_cage; + char restrict_flag_cage = 0; Mesh *me_low = NULL; - Render *re; + Mesh *me_cage = NULL; float *result = NULL; @@ -421,31 +576,40 @@ static int bake( const bool is_noncolor = is_noncolor_pass(pass_type); const int depth = RE_pass_depth(pass_type); - bool is_highpoly = false; - bool is_tangent; - BakeImages bake_images = {NULL}; - int num_pixels; + size_t num_pixels; int tot_materials; int i; - re = RE_NewRender(scene->id.name); - RE_SetReports(re, NULL); + RE_bake_engine_set_engine_parameters(re, bmain, scene); + + if (!RE_bake_has_engine(re)) { + BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); + goto cleanup; + } - is_tangent = pass_type == SCE_PASS_NORMAL && normal_space == R_BAKE_SPACE_TANGENT; tot_materials = ob_low->totcol; + if (uv_layer && uv_layer[0] != '\0') { + Mesh *me = (Mesh *)ob_low->data; + if (CustomData_get_named_layer(&me->ldata, CD_MLOOPUV, uv_layer) == -1) { + BKE_reportf(reports, RPT_ERROR, + "No UV layer named \"%s\" found in the object \"%s\"", uv_layer, ob_low->id.name + 2); + goto cleanup; + } + } + if (tot_materials == 0) { if (is_save_internal) { BKE_report(reports, RPT_ERROR, - "No active image found. Add a material or bake to an external file"); + "No active image found, add a material or bake to an external file"); goto cleanup; } else if (is_split_materials) { BKE_report(reports, RPT_ERROR, - "No active image found. Add a material or bake without the Split Materials option"); + "No active image found, add a material or bake without the Split Materials option"); goto cleanup; } @@ -456,11 +620,10 @@ static int bake( } /* we overallocate in case there is more materials than images */ - bake_images.data = MEM_callocN(sizeof(BakeImage) * tot_materials, "bake images dimensions (width, height, offset)"); - bake_images.lookup = MEM_callocN(sizeof(int) * tot_materials, "bake images lookup (from material to BakeImage)"); + bake_images.data = MEM_mallocN(sizeof(BakeImage) * tot_materials, "bake images dimensions (width, height, offset)"); + bake_images.lookup = MEM_mallocN(sizeof(int) * tot_materials, "bake images lookup (from material to BakeImage)"); - if (!build_image_lookup(bmain, ob_low, &bake_images, reports)) - goto cleanup; + build_image_lookup(bmain, ob_low, &bake_images); if (is_save_internal) { num_pixels = initialize_internal_images(&bake_images, reports); @@ -468,15 +631,11 @@ static int bake( if (num_pixels == 0) { goto cleanup; } - - if (is_clear) { - RE_bake_ibuf_clear(&bake_images, is_tangent); - } } else { /* when saving extenally always use the size specified in the UI */ - num_pixels = width * height * bake_images.size; + num_pixels = (size_t)width * (size_t)height * bake_images.size; for (i = 0; i < bake_images.size; i++) { bake_images.data[i].width = width; @@ -492,7 +651,7 @@ static int bake( } } - if (use_selected_to_active) { + if (is_selected_to_active) { CollectionPointerLink *link; tot_highpoly = 0; @@ -505,56 +664,48 @@ static int bake( tot_highpoly ++; } - if (tot_highpoly == 0) { - BKE_report(reports, RPT_ERROR, "No valid selected objects"); - op_result = OPERATOR_CANCELLED; - - goto cleanup; - } - else { - is_highpoly = true; - } - } - - if (custom_cage[0] != '\0') { - ob_cage = BLI_findstring(&bmain->object, custom_cage, offsetof(ID, name) + 2); + if (is_cage && custom_cage[0] != '\0') { + ob_cage = BLI_findstring(&bmain->object, custom_cage, offsetof(ID, name) + 2); - /* TODO check if cage object has the same topology (num of triangles and a valid UV) */ - if (ob_cage == NULL || ob_cage->type != OB_MESH) { - BKE_report(reports, RPT_ERROR, "No valid cage object"); - op_result = OPERATOR_CANCELLED; - - goto cleanup; - } - else { - restrict_flag_cage = ob_cage->restrictflag; + if (ob_cage == NULL || ob_cage->type != OB_MESH) { + BKE_report(reports, RPT_ERROR, "No valid cage object"); + goto cleanup; + } + else { + restrict_flag_cage = ob_cage->restrictflag; + ob_cage->restrictflag |= OB_RESTRICT_RENDER; + } } } - RE_bake_engine_set_engine_parameters(re, bmain, scene); - - /* blender_test_break uses this global */ - G.is_break = false; + pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); + result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); - RE_test_break_cb(re, NULL, bake_break); + /* get the mesh as it arrives in the renderer */ + me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); - pixel_array_low = MEM_callocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); - result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); + /* populate the pixel array with the face data */ + if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) + RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images, uv_layer); + /* else populate the pixel array with the 'cage' mesh (the smooth version of the mesh) */ - if (is_highpoly) { + if (is_selected_to_active) { CollectionPointerLink *link; ModifierData *md, *nmd; ListBase modifiers_tmp, modifiers_original; - float mat_low[4][4]; int i = 0; - highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); /* prepare cage mesh */ if (ob_cage) { - me_low = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0); - copy_m4_m4(mat_low, ob_cage->obmat); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0); + if (me_low->totface != me_cage->totface) { + BKE_report(reports, RPT_ERROR, + "Invalid cage object, the cage mesh must have the same number " + "of faces as the active object"); + goto cleanup; + } } - else { + else if (is_cage) { modifiers_original = ob_low->modifiers; BLI_listbase_clear(&modifiers_tmp); @@ -578,10 +729,12 @@ static int bake( ob_low->modifiers = modifiers_tmp; /* get the cage mesh as it arrives in the renderer */ - me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); - copy_m4_m4(mat_low, ob_low->obmat); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); } + highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); + /* populate highpoly array */ for (link = selected_objects->first; link; link = link->next) { TriangulateModifierData *tmd; @@ -592,10 +745,8 @@ static int bake( /* initialize highpoly_data */ highpoly[i].ob = ob_iter; - highpoly[i].me = NULL; - highpoly[i].tri_mod = NULL; highpoly[i].restrict_flag = ob_iter->restrictflag; - highpoly[i].pixel_array = MEM_callocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); + highpoly[i].pixel_array = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); /* triangulating so BVH returns the primitive_id that will be used for rendering */ @@ -610,70 +761,71 @@ static int bake( highpoly[i].ob->restrictflag &= ~OB_RESTRICT_RENDER; /* lowpoly to highpoly transformation matrix */ - invert_m4_m4(highpoly[i].mat_lowtohigh, highpoly[i].ob->obmat); - mul_m4_m4m4(highpoly[i].mat_lowtohigh, highpoly[i].mat_lowtohigh, mat_low); + copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); + invert_m4_m4(highpoly[i].imat, highpoly[i].obmat); + + /* rotation */ + normalize_m4_m4(highpoly[i].rotmat, highpoly[i].imat); + zero_v3(highpoly[i].rotmat[3]); + if (is_negative_m4(highpoly[i].rotmat)) + negate_m3(highpoly[i].rotmat); i++; } BLI_assert(i == tot_highpoly); - /* populate the pixel array with the face data */ - RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images); - ob_low->restrictflag |= OB_RESTRICT_RENDER; /* populate the pixel arrays with the corresponding face data for each high poly object */ - RE_bake_pixels_populate_from_objects( - me_low, pixel_array_low, highpoly, tot_highpoly, - num_pixels, cage_extrusion); + if (!RE_bake_pixels_populate_from_objects( + me_low, pixel_array_low, highpoly, tot_highpoly, num_pixels, ob_cage != NULL, + cage_extrusion, ob_low->obmat, (ob_cage ? ob_cage->obmat : ob_low->obmat), me_cage)) + { + BKE_report(reports, RPT_ERROR, "Error handling selected objects"); + goto cage_cleanup; + } /* the baking itself */ for (i = 0; i < tot_highpoly; i++) { - if (RE_bake_has_engine(re)) { - ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels, - depth, pass_type, result); - } - else { - ok = RE_bake_internal(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels, - depth, pass_type, result); + ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels, + depth, pass_type, result); + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Error baking from object \"%s\"", highpoly[i].ob->id.name + 2); + goto cage_cleanup; } - - if (!ok) - break; } +cage_cleanup: /* reverting data back */ - if (ob_cage) { - ob_cage->restrictflag |= OB_RESTRICT_RENDER; - } - else { + if ((ob_cage == NULL) && is_cage) { ob_low->modifiers = modifiers_original; while ((md = BLI_pophead(&modifiers_tmp))) { modifier_free(md); } } + + if (!ok) { + goto cleanup; + } } else { - /* get the mesh as it arrives in the renderer */ - me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); - - /* populate the pixel array with the face data */ - RE_bake_pixels_populate(me_low, pixel_array_low, num_pixels, &bake_images); - /* make sure low poly renders */ ob_low->restrictflag &= ~OB_RESTRICT_RENDER; - if (RE_bake_has_engine(re)) + if (RE_bake_has_engine(re)) { ok = RE_bake_engine(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result); - else - ok = RE_bake_internal(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result); + } + else { + BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); + goto cleanup; + } } /* normal space conversion * the normals are expected to be in world space, +X +Y +Z */ - if (pass_type == SCE_PASS_NORMAL) { + if (ok && pass_type == SCE_PASS_NORMAL) { switch (normal_space) { case R_BAKE_SPACE_WORLD: { @@ -696,8 +848,8 @@ static int bake( } case R_BAKE_SPACE_TANGENT: { - if (is_highpoly) { - RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_low, normal_swizzle); + if (is_selected_to_active) { + RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_low, normal_swizzle, ob_low->obmat); } else { /* from multiresolution */ @@ -713,9 +865,9 @@ static int bake( } me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); - RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images); + RE_bake_pixels_populate(me_nores, pixel_array_low, num_pixels, &bake_images, uv_layer); - RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle); + RE_bake_normal_world_to_tangent(pixel_array_low, num_pixels, depth, result, me_nores, normal_swizzle, ob_low->obmat); BKE_libblock_free(bmain, me_nores); if (md) @@ -729,7 +881,7 @@ static int bake( } if (!ok) { - BKE_report(reports, RPT_ERROR, "Problem baking object map"); + BKE_reportf(reports, RPT_ERROR, "Problem baking object \"%s\"", ob_low->id.name + 2); op_result = OPERATOR_CANCELLED; } else { @@ -745,10 +897,12 @@ static int bake( bk_image->width, bk_image->height, margin, is_clear, is_noncolor); + /* might be read by UI to set active image for display */ + bake_update_image(sa, bk_image->image); + if (!ok) { - BKE_report(reports, RPT_ERROR, - "Problem saving the bake map internally, " - "make sure there is a Texture Image node in the current object material"); + BKE_reportf(reports, RPT_ERROR, + "Problem saving the bake map internally for object \"%s\"", ob_low->id.name + 2); op_result = OPERATOR_CANCELLED; } else { @@ -798,11 +952,11 @@ static int bake( margin, &bake->im_format, is_noncolor); if (!ok) { - BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\".", name); + BKE_reportf(reports, RPT_ERROR, "Problem saving baked map in \"%s\"", name); op_result = OPERATOR_CANCELLED; } else { - BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\".", name); + BKE_reportf(reports, RPT_INFO, "Baking map written to \"%s\"", name); op_result = OPERATOR_FINISHED; } @@ -814,11 +968,11 @@ static int bake( } if (is_save_internal) - reset_images_gpu(&bake_images); + refresh_images(&bake_images); cleanup: - if (is_highpoly) { + if (highpoly) { int i; for (i = 0; i < tot_highpoly; i++) { highpoly[i].ob->restrictflag = highpoly[i].restrict_flag; @@ -855,16 +1009,21 @@ cleanup: if (me_low) BKE_libblock_free(bmain, me_low); + if (me_cage) + BKE_libblock_free(bmain, me_cage); + return op_result; } static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) { bool is_save_internal; + bScreen *sc = CTX_wm_screen(C); bkr->ob = CTX_data_active_object(C); bkr->main = CTX_data_main(C); bkr->scene = CTX_data_scene(C); + bkr->sa = sc ? BKE_screen_find_big_area(sc, SPACE_IMAGE, 10) : NULL; bkr->pass_type = RNA_enum_get(op->ptr, "type"); bkr->margin = RNA_int_get(op->ptr, "margin"); @@ -875,7 +1034,8 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) bkr->is_clear = RNA_boolean_get(op->ptr, "use_clear"); bkr->is_split_materials = (!is_save_internal) && RNA_boolean_get(op->ptr, "use_split_materials"); bkr->is_automatic_name = RNA_boolean_get(op->ptr, "use_automatic_name"); - bkr->use_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active"); + bkr->is_selected_to_active = RNA_boolean_get(op->ptr, "use_selected_to_active"); + bkr->is_cage = RNA_boolean_get(op->ptr, "use_cage"); bkr->cage_extrusion = RNA_float_get(op->ptr, "cage_extrusion"); bkr->normal_space = RNA_enum_get(op->ptr, "normal_space"); @@ -887,18 +1047,23 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) bkr->height = RNA_int_get(op->ptr, "height"); bkr->identifier = ""; - RNA_string_get(op->ptr, "cage", bkr->custom_cage); + RNA_string_get(op->ptr, "uv_layer", bkr->uv_layer); + + RNA_string_get(op->ptr, "cage_object", bkr->custom_cage); if ((!is_save_internal) && bkr->is_automatic_name) { PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); RNA_property_enum_identifier(C, op->ptr, prop, bkr->pass_type, &bkr->identifier); } - if (bkr->use_selected_to_active) - CTX_data_selected_objects(C, &bkr->selected_objects); + CTX_data_selected_objects(C, &bkr->selected_objects); bkr->reports = op->reports; + bkr->result = OPERATOR_CANCELLED; + + bkr->render = RE_NewRender(bkr->scene->id.name); + /* XXX hack to force saving to always be internal. Whether (and how) to support * external saving will be addressed later */ bkr->save_mode = R_BAKE_SAVE_INTERNAL; @@ -906,33 +1071,104 @@ static void bake_init_api_data(wmOperator *op, bContext *C, BakeAPIRender *bkr) static int bake_exec(bContext *C, wmOperator *op) { - int result; + Render *re; + int result = OPERATOR_CANCELLED; BakeAPIRender bkr = {NULL}; bake_init_api_data(op, C, &bkr); + re = bkr.render; + + /* setup new render */ + RE_test_break_cb(re, NULL, bake_break); - result = bake( - bkr.main, bkr.scene, bkr.ob, &bkr.selected_objects, bkr.reports, - bkr.pass_type, bkr.margin, bkr.save_mode, - bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, bkr.use_selected_to_active, - bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle, - bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier); + if (!bake_objects_check(bkr.main, bkr.ob, &bkr.selected_objects, bkr.reports, bkr.is_selected_to_active)) + return OPERATOR_CANCELLED; + + if (bkr.is_clear) { + const bool is_tangent = ((bkr.pass_type == SCE_PASS_NORMAL) && (bkr.normal_space == R_BAKE_SPACE_TANGENT)); + bake_images_clear(bkr.main, is_tangent); + } + + RE_SetReports(re, bkr.reports); + + if (bkr.is_selected_to_active) { + result = bake( + bkr.render, bkr.main, bkr.scene, bkr.ob, &bkr.selected_objects, bkr.reports, + bkr.pass_type, bkr.margin, bkr.save_mode, + bkr.is_clear, bkr.is_split_materials, bkr.is_automatic_name, true, bkr.is_cage, + bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle, + bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa, + bkr.uv_layer); + } + else { + CollectionPointerLink *link; + const bool is_clear = bkr.is_clear && BLI_listbase_is_single(&bkr.selected_objects); + for (link = bkr.selected_objects.first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + result = bake( + bkr.render, bkr.main, bkr.scene, ob_iter, NULL, bkr.reports, + bkr.pass_type, bkr.margin, bkr.save_mode, + is_clear, bkr.is_split_materials, bkr.is_automatic_name, false, bkr.is_cage, + bkr.cage_extrusion, bkr.normal_space, bkr.normal_swizzle, + bkr.custom_cage, bkr.filepath, bkr.width, bkr.height, bkr.identifier, bkr.sa, + bkr.uv_layer); + } + } + + RE_SetReports(re, NULL); BLI_freelistN(&bkr.selected_objects); return result; } -static void bake_startjob(void *bkv, short *UNUSED(stop), short *UNUSED(do_update), float *UNUSED(progress)) +static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, float *progress) { BakeAPIRender *bkr = (BakeAPIRender *)bkv; - bkr->result = bake( - bkr->main, bkr->scene, bkr->ob, &bkr->selected_objects, bkr->reports, - bkr->pass_type, bkr->margin, bkr->save_mode, - bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, bkr->use_selected_to_active, - bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle, - bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier - ); + /* setup new render */ + bkr->do_update = do_update; + bkr->progress = progress; + + RE_SetReports(bkr->render, bkr->reports); + + if (!bake_objects_check(bkr->main, bkr->ob, &bkr->selected_objects, bkr->reports, bkr->is_selected_to_active)) { + bkr->result = OPERATOR_CANCELLED; + return; + } + + if (bkr->is_clear) { + const bool is_tangent = ((bkr->pass_type == SCE_PASS_NORMAL) && (bkr->normal_space == R_BAKE_SPACE_TANGENT)); + bake_images_clear(bkr->main, is_tangent); + } + + if (bkr->is_selected_to_active) { + bkr->result = bake( + bkr->render, bkr->main, bkr->scene, bkr->ob, &bkr->selected_objects, bkr->reports, + bkr->pass_type, bkr->margin, bkr->save_mode, + bkr->is_clear, bkr->is_split_materials, bkr->is_automatic_name, true, bkr->is_cage, + bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle, + bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa, + bkr->uv_layer); + } + else { + CollectionPointerLink *link; + const bool is_clear = bkr->is_clear && BLI_listbase_is_single(&bkr->selected_objects); + for (link = bkr->selected_objects.first; link; link = link->next) { + Object *ob_iter = link->ptr.data; + bkr->result = bake( + bkr->render, bkr->main, bkr->scene, ob_iter, NULL, bkr->reports, + bkr->pass_type, bkr->margin, bkr->save_mode, + is_clear, bkr->is_split_materials, bkr->is_automatic_name, false, bkr->is_cage, + bkr->cage_extrusion, bkr->normal_space, bkr->normal_swizzle, + bkr->custom_cage, bkr->filepath, bkr->width, bkr->height, bkr->identifier, bkr->sa, + bkr->uv_layer); + + if (bkr->result == OPERATOR_CANCELLED) + return; + } + } + + RE_SetReports(bkr->render, NULL); } static void bake_freejob(void *bkv) @@ -980,7 +1216,7 @@ static void bake_set_props(wmOperator *op, Scene *scene) RNA_property_float_set(op->ptr, prop, bake->cage_extrusion); } - prop = RNA_struct_find_property(op->ptr, "cage"); + prop = RNA_struct_find_property(op->ptr, "cage_object"); if (!RNA_property_is_set(op->ptr, prop)) { RNA_property_string_set(op->ptr, prop, bake->cage); } @@ -1015,6 +1251,11 @@ static void bake_set_props(wmOperator *op, Scene *scene) RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CLEAR)); } + prop = RNA_struct_find_property(op->ptr, "use_cage"); + if (!RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_CAGE)); + } + prop = RNA_struct_find_property(op->ptr, "use_split_materials"); if (!RNA_property_is_set(op->ptr, prop)) { RNA_property_boolean_set(op->ptr, prop, (bake->flag & R_BAKE_SPLIT_MAT)); @@ -1030,6 +1271,7 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) { wmJob *wm_job; BakeAPIRender *bkr; + Render *re; Scene *scene = CTX_data_scene(C); bake_set_props(op, scene); @@ -1038,10 +1280,15 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_OBJECT_BAKE)) return OPERATOR_CANCELLED; - bkr = MEM_callocN(sizeof(BakeAPIRender), "render bake"); + bkr = MEM_mallocN(sizeof(BakeAPIRender), "render bake"); /* init bake render */ bake_init_api_data(op, C, bkr); + re = bkr->render; + + /* setup new render */ + RE_test_break_cb(re, NULL, bake_break); + RE_progress_cb(re, bkr, bake_progress_update); /* setup job */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Texture Bake", @@ -1089,10 +1336,10 @@ void OBJECT_OT_bake(wmOperatorType *ot) "Extends the baked result as a post process filter", 0, 64); RNA_def_boolean(ot->srna, "use_selected_to_active", false, "Selected to Active", "Bake shading on the surface of selected objects to the active object"); - RNA_def_float(ot->srna, "cage_extrusion", 0.0, 0.0, 1.0, "Cage Extrusion", - "Distance to use for the inward ray cast when using selected to active", 0.0, 1.0); - RNA_def_string(ot->srna, "cage", NULL, MAX_NAME, "Cage", - "Object to use as cage"); + RNA_def_float(ot->srna, "cage_extrusion", 0.0f, 0.0f, FLT_MAX, "Cage Extrusion", + "Distance to use for the inward ray cast when using selected to active", 0.0f, 1.0f); + RNA_def_string(ot->srna, "cage_object", NULL, MAX_NAME, "Cage Object", + "Object to use as cage, instead of calculating the cage from the active object with cage extrusion"); RNA_def_enum(ot->srna, "normal_space", normal_space_items, R_BAKE_SPACE_TANGENT, "Normal Space", "Choose normal space for baking"); RNA_def_enum(ot->srna, "normal_r", normal_swizzle_items, R_BAKE_POSX, "R", "Axis to bake in red channel"); @@ -1102,8 +1349,11 @@ void OBJECT_OT_bake(wmOperatorType *ot) "Choose how to save the baking map"); RNA_def_boolean(ot->srna, "use_clear", false, "Clear", "Clear Images before baking (only for internal saving)"); + RNA_def_boolean(ot->srna, "use_cage", false, "Cage", + "Cast rays to active object from a cage"); RNA_def_boolean(ot->srna, "use_split_materials", false, "Split Materials", "Split baked maps per material, using material name in output file (external only)"); RNA_def_boolean(ot->srna, "use_automatic_name", false, "Automatic Name", "Automatically name the output file with the pass type"); + RNA_def_string(ot->srna, "uv_layer", NULL, MAX_CUSTOMDATA_LAYER_NAME, "UV Layer", "UV layer to override active"); } diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 85e4bbce8dc..92ed84b7f5e 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -485,7 +485,7 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) } /* target checks for specific constraints */ - if (ELEM3(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { + if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { if (ct->tar) { if (ct->tar->type != OB_CURVE) { ct->tar = NULL; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 002bec4ef0b..93956128b84 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -139,7 +139,9 @@ static int object_hide_view_clear_exec(bContext *C, wmOperator *UNUSED(op)) /* XXX need a context loop to handle such cases */ for (base = FIRSTBASE; base; base = base->next) { if ((base->lay & v3d->lay) && base->object->restrictflag & OB_RESTRICT_VIEW) { - base->flag |= SELECT; + if (!(base->object->restrictflag & OB_RESTRICT_SELECT)) { + base->flag |= SELECT; + } base->object->flag = base->flag; base->object->restrictflag &= ~OB_RESTRICT_VIEW; changed = true; @@ -361,6 +363,11 @@ static bool ED_object_editmode_load_ex(Object *obedit, const bool freedata) if (freedata) free_editMball(obedit); } + /* Tag update so no access to freed data referenced from + * derived cache will happen. + */ + DAG_id_tag_update((ID *)obedit->data, 0); + return true; } @@ -447,7 +454,7 @@ void ED_object_editmode_enter(bContext *C, int flag) base = scene->basact; } - if (ELEM3(NULL, base, base->object, base->object->data)) return; + if (ELEM(NULL, base, base->object, base->object->data)) return; ob = base->object; @@ -589,7 +596,7 @@ static int editmode_toggle_poll(bContext *C) if ((ob->restrictflag & OB_RESTRICT_VIEW) && !(ob->mode & OB_MODE_EDIT)) return 0; - return (ELEM7(ob->type, OB_MESH, OB_ARMATURE, OB_FONT, OB_MBALL, OB_LATTICE, OB_SURF, OB_CURVE)); + return OB_TYPE_SUPPORT_EDITMODE(ob->type); } void OBJECT_OT_editmode_toggle(wmOperatorType *ot) @@ -760,7 +767,7 @@ static void copy_texture_space(Object *to, Object *ob) texflag = ((Mesh *)ob->data)->texflag; poin2 = ((Mesh *)ob->data)->loc; } - else if (ELEM3(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { texflag = ((Curve *)ob->data)->texflag; poin2 = ((Curve *)ob->data)->loc; } @@ -775,7 +782,7 @@ static void copy_texture_space(Object *to, Object *ob) ((Mesh *)to->data)->texflag = texflag; poin1 = ((Mesh *)to->data)->loc; } - else if (ELEM3(to->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(to->type, OB_CURVE, OB_SURF, OB_FONT)) { ((Curve *)to->data)->texflag = texflag; poin1 = ((Curve *)to->data)->loc; } @@ -1112,7 +1119,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object) /* add/remove modifier as needed */ if (!md) { if (pd && (pd->shape == PFIELD_SHAPE_SURFACE) && ELEM(pd->forcefield, PFIELD_GUIDE, PFIELD_TEXTURE) == 0) - if (ELEM4(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) + if (ELEM(object->type, OB_MESH, OB_SURF, OB_FONT, OB_CURVE)) ED_object_modifier_add(NULL, bmain, scene, object, NULL, eModifierType_Surface); } else { @@ -1453,7 +1460,7 @@ static void UNUSED_FUNCTION(image_aspect) (Scene *scene, View3D *v3d) BKE_mesh_texspace_get(ob->data, NULL, NULL, size); space = size[0] / size[1]; } - else if (ELEM3(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVE, OB_FONT, OB_SURF)) { float size[3]; BKE_curve_texspace_get(ob->data, NULL, NULL, size); space = size[0] / size[1]; @@ -1500,7 +1507,7 @@ static EnumPropertyItem *object_mode_set_itemsf(bContext *C, PointerRNA *UNUSED( if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) || (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) || (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || - (ELEM4(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, + (ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || (input->value == OB_MODE_OBJECT)) { @@ -1620,20 +1627,20 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) /* Exit current mode if it's not the mode we're setting */ if (mode != OB_MODE_OBJECT && (ob->mode != mode || toggle)) { /* Enter new mode */ - WM_operator_name_call(C, object_mode_op_string(mode), WM_OP_EXEC_REGION_WIN, NULL); + ED_object_toggle_modes(C, mode); } if (toggle) { /* Special case for Object mode! */ if (mode == OB_MODE_OBJECT && restore_mode == OB_MODE_OBJECT && ob->restore_mode != OB_MODE_OBJECT) { - WM_operator_name_call(C, object_mode_op_string(ob->restore_mode), WM_OP_EXEC_REGION_WIN, NULL); + ED_object_toggle_modes(C, ob->restore_mode); } else if (ob->mode == mode) { /* For toggling, store old mode so we know what to go back to */ ob->restore_mode = restore_mode; } else if (ob->restore_mode != OB_MODE_OBJECT && ob->restore_mode != mode) { - WM_operator_name_call(C, object_mode_op_string(ob->restore_mode), WM_OP_EXEC_REGION_WIN, NULL); + ED_object_toggle_modes(C, ob->restore_mode); } } @@ -1669,24 +1676,12 @@ void OBJECT_OT_mode_set(wmOperatorType *ot) void ED_object_toggle_modes(bContext *C, int mode) { - /* Couldn't we use object_mode_op_string() here? - * Also, if several bits are set in mode, several toggle ops will be called, is this expected? - * If so, would be nice to explain why. ;) --mont29 - */ - if (mode & OB_MODE_SCULPT) - WM_operator_name_call(C, "SCULPT_OT_sculptmode_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_VERTEX_PAINT) - WM_operator_name_call(C, "PAINT_OT_vertex_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_WEIGHT_PAINT) - WM_operator_name_call(C, "PAINT_OT_weight_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_TEXTURE_PAINT) - WM_operator_name_call(C, "PAINT_OT_texture_paint_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_PARTICLE_EDIT) - WM_operator_name_call(C, "PARTICLE_OT_particle_edit_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_POSE) - WM_operator_name_call(C, "OBJECT_OT_posemode_toggle", WM_OP_EXEC_REGION_WIN, NULL); - if (mode & OB_MODE_EDIT) - WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL); + if (mode != OB_MODE_OBJECT) { + const char *opstring = object_mode_op_string(mode); + if (opstring) { + WM_operator_name_call(C, opstring, WM_OP_EXEC_REGION_WIN, NULL); + } + } } /************************ Game Properties ***********************/ diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c index 69bd64542f4..20e2e22cdf8 100644 --- a/source/blender/editors/object/object_group.c +++ b/source/blender/editors/object/object_group.c @@ -74,33 +74,31 @@ static bool group_link_early_exit_check(Group *group, Object *object) return false; } -static bool check_group_contains_object_recursive(Group *group, Object *object) +static bool check_object_instances_group_recursive(Object *object, Group *group) { - GroupObject *group_object; - - if ((group->id.flag & LIB_DOIT) == 0) { - /* Cycle already exists in groups, let's prevent further crappyness */ - return true; - } - - group->id.flag &= ~LIB_DOIT; - - for (group_object = group->gobject.first; group_object; group_object = group_object->next) { - Object *current_object = group_object->ob; - - if (current_object == object) { + if (object->dup_group) { + Group *dup_group = object->dup_group; + if ((dup_group->id.flag & LIB_DOIT) == 0) { + /* Cycle already exists in groups, let's prevent further crappyness */ return true; } - - if (current_object->dup_group) { - if (check_group_contains_object_recursive(current_object->dup_group, object)) { - return true; + /* flag the object to identify cyclic dependencies in further dupli groups */ + dup_group->id.flag &= ~LIB_DOIT; + + if (dup_group == group) + return true; + else { + GroupObject *gob; + for (gob = dup_group->gobject.first; gob; gob = gob->next) { + if (check_object_instances_group_recursive(gob->ob, group)) + return true; } } + + /* un-flag the object, it's allowed to have the same group multiple times in parallel */ + dup_group->id.flag |= LIB_DOIT; } - - group->id.flag |= LIB_DOIT; - + return false; } @@ -195,7 +193,7 @@ static int objects_add_active_exec(bContext *C, wmOperator *op) if (group_link_early_exit_check(group, base->object)) continue; - if (base->object->dup_group != group && !check_group_contains_object_recursive(group, base->object)) { + if (!check_object_instances_group_recursive(base->object, group)) { BKE_group_object_add(group, base->object, scene, base); updated = true; } @@ -498,7 +496,7 @@ static int group_link_exec(bContext *C, wmOperator *op) * contains our current object. */ BKE_main_id_tag_listbase(&bmain->group, true); - if (ob->dup_group == group || check_group_contains_object_recursive(group, ob)) { + if (check_object_instances_group_recursive(ob, group)) { BKE_report(op->reports, RPT_ERROR, "Could not add the group because of dependency cycle detected"); return OPERATOR_CANCELLED; } @@ -565,3 +563,67 @@ void OBJECT_OT_group_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int group_unlink_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Group *group = CTX_data_pointer_get_type(C, "group", &RNA_Group).data; + + if (!group) + return OPERATOR_CANCELLED; + + BKE_group_unlink(group); + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_group_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink Group"; + ot->idname = "OBJECT_OT_group_unlink"; + ot->description = "Unlink the group from all objects"; + + /* api callbacks */ + ot->exec = group_unlink_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int select_grouped_exec(bContext *C, wmOperator *UNUSED(op)) /* Select objects in the same group as the active */ +{ + Group *group = CTX_data_pointer_get_type(C, "group", &RNA_Group).data; + + if (!group) + return OPERATOR_CANCELLED; + + CTX_DATA_BEGIN (C, Base *, base, visible_bases) + { + if (!(base->flag & SELECT) && BKE_group_object_exists(group, base->object)) { + ED_base_object_select(base, BA_SELECT); + } + } + CTX_DATA_END; + + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_grouped_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Grouped"; + ot->idname = "OBJECT_OT_grouped_select"; + ot->description = "Select all objects in group"; + + /* api callbacks */ + ot->exec = select_grouped_exec; + ot->poll = ED_operator_objectmode; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 6407d2eb8b8..9f9a647c9f1 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -331,6 +331,8 @@ static bool object_hook_index_array(Scene *scene, Object *obedit, } case OB_CURVE: case OB_SURF: + load_editNurb(obedit); + make_editNurb(obedit); return return_editcurve_indexar(obedit, r_tot, r_indexar, r_cent); case OB_LATTICE: { @@ -526,8 +528,7 @@ static int add_hook_object(Main *bmain, Scene *scene, Object *obedit, Object *ob invert_m4_m4(ob->imat, ob->obmat); /* apparently this call goes from right to left... */ - mul_serie_m4(hmd->parentinv, pose_mat, ob->imat, obedit->obmat, - NULL, NULL, NULL, NULL, NULL); + mul_m4_series(hmd->parentinv, pose_mat, ob->imat, obedit->obmat); DAG_relations_tag_update(bmain); diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index fd6b9a1bad0..6fa3caa6172 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -72,6 +72,7 @@ void OBJECT_OT_make_links_scene(struct wmOperatorType *ot); void OBJECT_OT_make_links_data(struct wmOperatorType *ot); void OBJECT_OT_move_to_layer(struct wmOperatorType *ot); void OBJECT_OT_drop_named_material(struct wmOperatorType *ot); +void OBJECT_OT_unlink_data(struct wmOperatorType *ot); /* object_edit.c */ void OBJECT_OT_mode_set(struct wmOperatorType *ot); @@ -251,6 +252,8 @@ void OBJECT_OT_shape_key_move(struct wmOperatorType *ot); void OBJECT_OT_group_add(struct wmOperatorType *ot); void OBJECT_OT_group_link(struct wmOperatorType *ot); void OBJECT_OT_group_remove(struct wmOperatorType *ot); +void OBJECT_OT_group_unlink(struct wmOperatorType *ot); +void OBJECT_OT_grouped_select(struct wmOperatorType *ot); /* object_bake.c */ void OBJECT_OT_bake_image(wmOperatorType *ot); diff --git a/source/blender/editors/object/object_lattice.c b/source/blender/editors/object/object_lattice.c index 2af2ca3b0e9..c24a127ed8b 100644 --- a/source/blender/editors/object/object_lattice.c +++ b/source/blender/editors/object/object_lattice.c @@ -174,21 +174,6 @@ void load_editLatt(Object *obedit) } } -/*************************** Transform Operator ************************/ - -void ED_lattice_transform(Lattice *lt, float mat[4][4]) -{ - BPoint *bp = lt->def; - int a = lt->pntsu * lt->pntsv * lt->pntsw; - - while (a--) { - mul_m4_v3(mat, bp->vec); - bp++; - } - - DAG_id_tag_update(<->id, 0); -} - static void bpoint_select_set(BPoint *bp, bool select) { if (select) { @@ -285,7 +270,7 @@ static int lattice_select_mirror_exec(bContext *C, wmOperator *op) const int i_flip = BKE_lattice_index_flip(lt, i, flip_uvw[0], flip_uvw[1], flip_uvw[2]); bp = <->def[i]; if (!bp->hide) { - if (BLI_BITMAP_GET(selpoints, i_flip)) { + if (BLI_BITMAP_TEST(selpoints, i_flip)) { bp->f1 |= SELECT; } else { @@ -338,7 +323,7 @@ static bool lattice_test_bitmap_uvw(Lattice *lt, BLI_bitmap *selpoints, int u, i else { int i = BKE_lattice_index_from_uvw(lt, u, v, w); if (lt->def[i].hide == 0) { - return (BLI_BITMAP_GET(selpoints, i) != 0) == selected; + return (BLI_BITMAP_TEST(selpoints, i) != 0) == selected; } return false; } diff --git a/source/blender/editors/object/object_lod.c b/source/blender/editors/object/object_lod.c index a7cc4131a96..48e980015a7 100644 --- a/source/blender/editors/object/object_lod.c +++ b/source/blender/editors/object/object_lod.c @@ -29,15 +29,11 @@ * \ingroup edobj */ - #include "DNA_object_types.h" #include "BKE_context.h" #include "BKE_object.h" -#include "ED_screen.h" -#include "ED_object.h" - #include "WM_api.h" #include "WM_types.h" @@ -45,6 +41,9 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "ED_screen.h" +#include "ED_object.h" + #include "object_intern.h" static int object_lod_add_exec(bContext *C, wmOperator *UNUSED(op)) @@ -69,7 +68,7 @@ void OBJECT_OT_lod_add(wmOperatorType *ot) /* api callbacks */ ot->exec = object_lod_add_exec; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_object_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -101,7 +100,7 @@ void OBJECT_OT_lod_remove(wmOperatorType *ot) /* api callbacks */ ot->exec = object_lod_remove_exec; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_object_active; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 3e33268704c..b05840b5823 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -98,7 +98,7 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc ModifierTypeInfo *mti = modifierType_getInfo(type); /* only geometry objects should be able to get modifiers [#25291] */ - if (!ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); return NULL; } @@ -1650,9 +1650,9 @@ static void skin_armature_bone_create(Object *skin_ob, int v; /* ignore edge if already visited */ - if (BLI_BITMAP_GET(edges_visited, endx)) + if (BLI_BITMAP_TEST(edges_visited, endx)) continue; - BLI_BITMAP_SET(edges_visited, endx); + BLI_BITMAP_ENABLE(edges_visited, endx); v = (e->v1 == parent_v ? e->v2 : e->v1); @@ -1876,7 +1876,7 @@ static int meshdeform_bind_exec(bContext *C, wmOperator *op) else if (ob->type == OB_MBALL) { BKE_displist_make_mball(CTX_data_main(C)->eval_ctx, scene, ob); } - else if (ELEM3(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_displist_make_curveTypes(scene, ob, 0); } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index a8f07747d3a..7cf661de52c 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -229,6 +229,8 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_group_add); WM_operatortype_append(OBJECT_OT_group_link); WM_operatortype_append(OBJECT_OT_group_remove); + WM_operatortype_append(OBJECT_OT_group_unlink); + WM_operatortype_append(OBJECT_OT_grouped_select); WM_operatortype_append(OBJECT_OT_hook_add_selob); WM_operatortype_append(OBJECT_OT_hook_add_newob); @@ -241,6 +243,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_bake_image); WM_operatortype_append(OBJECT_OT_bake); WM_operatortype_append(OBJECT_OT_drop_named_material); + WM_operatortype_append(OBJECT_OT_unlink_data); WM_operatortype_append(OBJECT_OT_laplaciandeform_bind); WM_operatortype_append(OBJECT_OT_lod_add); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 6ff21f75733..8f74278b424 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -128,9 +128,9 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) BPoint *bp; Object *par; int a, v1 = 0, v2 = 0, v3 = 0, v4 = 0, nr = 1; - + /* we need 1 to 3 selected vertices */ - + if (obedit->type == OB_MESH) { Mesh *me = obedit->data; BMEditMesh *em; @@ -160,7 +160,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) { ListBase *editnurb = object_editcurve_get(obedit); - + cu = obedit->data; nu = editnurb->first; @@ -200,7 +200,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } else if (obedit->type == OB_LATTICE) { Lattice *lt = obedit->data; - + a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw; bp = lt->editlatt->latt->def; while (a--) { @@ -215,28 +215,24 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) bp++; } } - + if (v4 || !((v1 && v2 == 0 && v3 == 0) || (v1 && v2 && v3))) { BKE_report(op->reports, RPT_ERROR, "Select either 1 or 3 vertices to parent to"); return OPERATOR_CANCELLED; } - + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { if (ob != obedit) { DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); par = obedit->parent; - - while (par) { - if (par == ob) break; - par = par->parent; - } - if (par) { + + if (BKE_object_parent_loop_check(par, ob)) { BKE_report(op->reports, RPT_ERROR, "Loop in parents"); } else { Object workob; - + ob->parent = BASACT->object; if (v3) { ob->partype = PARVERT3; @@ -260,7 +256,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } } CTX_DATA_END; - + DAG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT, NULL); @@ -274,12 +270,12 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot) ot->name = "Make Vertex Parent"; ot->description = "Parent selected objects to the selected vertices"; ot->idname = "OBJECT_OT_vertex_parent_set"; - + /* api callbacks */ ot->invoke = WM_operator_confirm; ot->poll = vertex_parent_set_poll; ot->exec = vertex_parent_set_exec; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -291,26 +287,26 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Scene *scene = CTX_data_scene(C); Object *ob = ED_object_active_context(C); - + /* sanity checks */ if (!scene || scene->id.lib || !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) { /* gives menu with list of objects in group */ - //proxy_group_objects_menu(C, op, ob, ob->dup_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) { uiPopupMenu *pup = uiPupMenuBegin(C, IFACE_("OK?"), ICON_QUESTION); uiLayout *layout = uiPupMenuLayout(pup); - + /* create operator menu item with relevant properties filled in */ - uiItemFullO_ptr(layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS); - + uiItemFullO_ptr(layout, op->type, op->type->name, ICON_NONE, NULL, + WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS); + /* present the menu and be done... */ uiPupMenuEnd(C, pup); } @@ -318,7 +314,7 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* error.. cannot continue */ BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or group"); } - + /* this invoke just calls another instance of this operator... */ return OPERATOR_CANCELLED; } @@ -338,32 +334,32 @@ static int make_proxy_exec(bContext *C, wmOperator *op) ob = gob; gob = NULL; } - + if (ob) { Object *newob; Base *newbase, *oldbase = BASACT; char name[MAX_ID_NAME + 4]; - + /* Add new object for the proxy */ newob = BKE_object_add(bmain, scene, OB_EMPTY); BLI_snprintf(name, sizeof(name), "%s_proxy", ((ID *)(gob ? gob : ob))->name + 2); rename_id(&newob->id, name); - + /* set layers OK */ newbase = BASACT; /* BKE_object_add sets active... */ newbase->lay = oldbase->lay; newob->lay = newbase->lay; - + /* remove base, leave user count of object, it gets linked in BKE_object_make_proxy */ if (gob == NULL) { BKE_scene_base_unlink(scene, oldbase); MEM_freeN(oldbase); } - + BKE_object_make_proxy(newob, ob, gob); - + /* depsgraph flushes are needed for the new data */ DAG_relations_tag_update(bmain); DAG_id_tag_update(&newob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); @@ -373,12 +369,13 @@ static int make_proxy_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "No object to make proxy for"); return OPERATOR_CANCELLED; } - + return OPERATOR_FINISHED; } /* Generic itemf's for operators that take library args */ -static EnumPropertyItem *proxy_group_object_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +static EnumPropertyItem *proxy_group_object_itemf(bContext *C, PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), bool *r_free) { EnumPropertyItem item_tmp = {0}, *item = NULL; int totitem = 0; @@ -410,17 +407,19 @@ void OBJECT_OT_proxy_make(wmOperatorType *ot) ot->name = "Make Proxy"; ot->idname = "OBJECT_OT_proxy_make"; ot->description = "Add empty object to become local replacement data of a library-linked object"; - + /* callbacks */ ot->invoke = make_proxy_invoke; ot->exec = make_proxy_exec; ot->poll = ED_operator_object_active; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* properties */ - prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Proxy Object", "Name of lib-linked/grouped object to make a proxy for"); /* XXX, relies on hard coded ID at the moment */ + /* XXX, relies on hard coded ID at the moment */ + prop = RNA_def_enum(ot->srna, "object", DummyRNA_DEFAULT_items, 0, "Proxy Object", + "Name of lib-linked/grouped object to make a proxy for"); RNA_def_enum_funcs(prop, proxy_group_object_itemf); RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); ot->prop = prop; @@ -435,24 +434,27 @@ typedef enum eObClearParentTypes { } eObClearParentTypes; EnumPropertyItem prop_clear_parent_types[] = { - {CLEAR_PARENT_ALL, "CLEAR", 0, "Clear Parent", ""}, - {CLEAR_PARENT_KEEP_TRANSFORM, "CLEAR_KEEP_TRANSFORM", 0, "Clear and Keep Transformation", ""}, - {CLEAR_PARENT_INVERSE, "CLEAR_INVERSE", 0, "Clear Parent Inverse", ""}, + {CLEAR_PARENT_ALL, "CLEAR", 0, "Clear Parent", + "Completely clear the parenting relationship, including involved modifiers is any"}, + {CLEAR_PARENT_KEEP_TRANSFORM, "CLEAR_KEEP_TRANSFORM", 0, "Clear and Keep Transformation", + "As 'Clear Parent', but keep the current visual transformations of the object"}, + {CLEAR_PARENT_INVERSE, "CLEAR_INVERSE", 0, "Clear Parent Inverse", + "Reset the transform corrections applied to the parenting relationship, does not remove parenting itself"}, {0, NULL, 0, NULL, NULL} }; /* Helper for ED_object_parent_clear() - Remove deform-modifiers associated with parent */ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) { - if (ELEM3(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVE)) { + if (ELEM(par->type, OB_ARMATURE, OB_LATTICE, OB_CURVE)) { ModifierData *md, *mdn; - + /* assume that we only need to remove the first instance of matching deform modifier here */ for (md = ob->modifiers.first; md; md = mdn) { bool free = false; - + mdn = md->next; - + /* need to match types (modifier + parent) and references */ if ((md->type == eModifierType_Armature) && (par->type == OB_ARMATURE)) { ArmatureModifierData *amd = (ArmatureModifierData *)md; @@ -472,7 +474,7 @@ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) free = true; } } - + /* free modifier if match */ if (free) { BLI_remlink(&ob->modifiers, md); @@ -482,17 +484,17 @@ static void object_remove_parent_deform_modifiers(Object *ob, const Object *par) } } -void ED_object_parent_clear(Object *ob, int type) +void ED_object_parent_clear(Object *ob, const int type) { if (ob->parent == NULL) return; - + switch (type) { case CLEAR_PARENT_ALL: { /* for deformers, remove corresponding modifiers to prevent a large number of modifiers building up */ object_remove_parent_deform_modifiers(ob, ob->parent); - + /* clear parenting relationship completely */ ob->parent = NULL; break; @@ -506,12 +508,15 @@ void ED_object_parent_clear(Object *ob, int type) } case CLEAR_PARENT_INVERSE: { - /* object stays parented, but the parent inverse (i.e. offset from parent to retain binding state) is cleared */ - unit_m4(ob->parentinv); + /* object stays parented, but the parent inverse (i.e. offset from parent to retain binding state) + * is cleared. In other words: nothing to do here! */ break; } } - + + /* Always clear parentinv matrix for sake of consistency, see T41950. */ + unit_m4(ob->parentinv); + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); } @@ -519,7 +524,7 @@ void ED_object_parent_clear(Object *ob, int type) static int parent_clear_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); + const int type = RNA_enum_get(op->ptr, "type"); CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { @@ -539,23 +544,26 @@ void OBJECT_OT_parent_clear(wmOperatorType *ot) ot->name = "Clear Parent"; ot->description = "Clear the object's parenting"; ot->idname = "OBJECT_OT_parent_clear"; - + /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = parent_clear_exec; - + ot->poll = ED_operator_object_active_editable; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + ot->prop = RNA_def_enum(ot->srna, "type", prop_clear_parent_types, CLEAR_PARENT_ALL, "Type", ""); } /* ******************** Make Parent Operator *********************** */ -void ED_object_parent(Object *ob, Object *par, int type, const char *substr) +void ED_object_parent(Object *ob, Object *par, const int type, const char *substr) { + /* Always clear parentinv matrix for sake of consistency, see T41950. */ + unit_m4(ob->parentinv); + if (!par || BKE_object_parent_loop_check(par, ob)) { ob->parent = NULL; ob->partype = PAROBJECT; @@ -589,21 +597,21 @@ EnumPropertyItem prop_make_parent_types[] = { {0, NULL, 0, NULL, NULL} }; -int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object *ob, Object *par, - int partype, bool xmirror, bool keep_transform, const int vert_par[3]) +bool ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object *ob, Object *par, + int partype, const bool xmirror, const bool keep_transform, const int vert_par[3]) { bPoseChannel *pchan = NULL; - int pararm = ELEM4(partype, PAR_ARMATURE, PAR_ARMATURE_NAME, PAR_ARMATURE_ENVELOPE, PAR_ARMATURE_AUTO); - + const bool pararm = ELEM(partype, PAR_ARMATURE, PAR_ARMATURE_NAME, PAR_ARMATURE_ENVELOPE, PAR_ARMATURE_AUTO); + DAG_id_tag_update(&par->id, OB_RECALC_OB); - + /* preconditions */ if (partype == PAR_FOLLOW || partype == PAR_PATH_CONST) { if (par->type != OB_CURVE) return 0; else { Curve *cu = par->data; - + if ((cu->flag & CU_PATH) == 0) { cu->flag |= CU_PATH | CU_FOLLOW; BKE_displist_make_curveTypes(scene, par, 0); /* force creation of path data */ @@ -617,12 +625,12 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object /* get or create F-Curve */ bAction *act = verify_adt_action(&cu->id, 1); FCurve *fcu = verify_fcurve(act, NULL, NULL, "eval_time", 0, 1); - + /* setup dummy 'generator' modifier here to get 1-1 correspondence still working */ if (!fcu->bezt && !fcu->fpt && !fcu->modifiers.first) add_fmodifier(&fcu->modifiers, FMODIFIER_TYPE_GENERATOR); } - + /* fall back on regular parenting now (for follow only) */ if (partype == PAR_FOLLOW) partype = PAR_OBJECT; @@ -630,17 +638,17 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object } else if (ELEM(partype, PAR_BONE, PAR_BONE_RELATIVE)) { pchan = BKE_pose_channel_active(par); - + if (pchan == NULL) { BKE_report(reports, RPT_ERROR, "No active bone"); - return 0; + return false; } } - + if (ob != par) { if (BKE_object_parent_loop_check(par, ob)) { BKE_report(reports, RPT_ERROR, "Loop in parents"); - return 0; + return false; } else { Object workob; @@ -655,14 +663,16 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object /* set the parent (except for follow-path constraint option) */ if (partype != PAR_PATH_CONST) { ob->parent = par; + /* Always clear parentinv matrix for sake of consistency, see T41950. */ + unit_m4(ob->parentinv); } - + /* handle types */ if (pchan) BLI_strncpy(ob->parsubstr, pchan->name, sizeof(ob->parsubstr)); else ob->parsubstr[0] = 0; - + if (partype == PAR_PATH_CONST) { /* don't do anything here, since this is not technically "parenting" */ } @@ -670,17 +680,18 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object /* partype is now set to PAROBJECT so that invisible 'virtual' modifiers don't need to be created * NOTE: the old (2.4x) method was to set ob->partype = PARSKEL, creating the virtual modifiers */ - ob->partype = PAROBJECT; /* note, dna define, not operator property */ - //ob->partype = PARSKEL; /* note, dna define, not operator property */ - - /* BUT, to keep the deforms, we need a modifier, and then we need to set the object that it uses + ob->partype = PAROBJECT; /* note, dna define, not operator property */ + /* ob->partype = PARSKEL; */ /* note, dna define, not operator property */ + + /* BUT, to keep the deforms, we need a modifier, and then we need to set the object that it uses * - We need to ensure that the modifier we're adding doesn't already exist, so we check this by * assuming that the parent is selected too... */ - // XXX currently this should only happen for meshes, curves, surfaces, and lattices - this stuff isn't available for metas yet - if (ELEM5(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { + /* XXX currently this should only happen for meshes, curves, surfaces, + * and lattices - this stuff isn't available for metas yet */ + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; - + switch (partype) { case PAR_CURVE: /* curve deform */ if (modifiers_isDeformedByCurve(ob) != par) { @@ -688,6 +699,9 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object if (md) { ((CurveModifierData *)md)->object = par; } + if (par->curve_cache && par->curve_cache->path == NULL) { + DAG_id_tag_update(&par->id, OB_RECALC_DATA); + } } break; case PAR_LATTICE: /* lattice deform */ @@ -730,21 +744,21 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object else { ob->partype = PAROBJECT; /* note, dna define, not operator property */ } - + /* constraint */ if (partype == PAR_PATH_CONST) { bConstraint *con; bFollowPathConstraint *data; float cmat[4][4], vec[3]; - + con = BKE_constraint_add_for_object(ob, "AutoPath", CONSTRAINT_TYPE_FOLLOWPATH); - + data = con->data; data->tar = par; - + BKE_constraint_target_matrix_get(scene, con, 0, CONSTRAINT_OBTYPE_OBJECT, NULL, cmat, scene->r.cfra); sub_v3_v3v3(vec, ob->obmat[3], cmat[3]); - + copy_v3_v3(ob->loc, vec); } else if (pararm && (ob->type == OB_MESH) && (par->type == OB_ARMATURE)) { @@ -760,7 +774,7 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object /* get corrected inverse */ ob->partype = PAROBJECT; BKE_object_workob_calc_parent(scene, ob, &workob); - + invert_m4_m4(ob->parentinv, workob.obmat); } else { @@ -768,12 +782,12 @@ int ED_object_parent_set(ReportList *reports, Main *bmain, Scene *scene, Object BKE_object_workob_calc_parent(scene, ob, &workob); invert_m4_m4(ob->parentinv, workob.obmat); } - + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); } } - return 1; + return true; } @@ -808,8 +822,8 @@ static int parent_set_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *par = ED_object_active_context(C); int partype = RNA_enum_get(op->ptr, "type"); - bool xmirror = RNA_boolean_get(op->ptr, "xmirror"); - bool keep_transform = RNA_boolean_get(op->ptr, "keep_transform"); + const bool xmirror = RNA_boolean_get(op->ptr, "xmirror"); + const bool keep_transform = RNA_boolean_get(op->ptr, "keep_transform"); bool ok = true; /* vertex parent (kdtree) */ @@ -828,27 +842,25 @@ static int parent_set_exec(bContext *C, wmOperator *op) if (tree_tot < (is_tri ? 3 : 1)) { BKE_report(op->reports, RPT_ERROR, "Not enough vertices for vertex-parent"); ok = false; - goto cleanup; } } + if (ok) { + /* Non vertex-parent */ + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + { + if (is_vert_par) { + parent_set_vert_find(tree, ob, vert_par, is_tri); + } - /* Non vertex-parent */ - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) - { - if (is_vert_par) { - parent_set_vert_find(tree, ob, vert_par, is_tri); - } - - if (!ED_object_parent_set(op->reports, bmain, scene, ob, par, partype, xmirror, keep_transform, vert_par_p)) { - ok = false; - break; + if (!ED_object_parent_set(op->reports, bmain, scene, ob, par, partype, xmirror, keep_transform, vert_par_p)) { + ok = false; + break; + } } + CTX_DATA_END; } - CTX_DATA_END; - -cleanup: if (is_vert_par) { BLI_kdtree_free(tree); } @@ -902,7 +914,7 @@ static int parent_set_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent else if (ob->type == OB_LATTICE) { uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_LATTICE); } - + /* vertex parenting */ if (OB_TYPE_SUPPORT_PARVERT(ob->type)) { uiItemEnumO_ptr(layout, ot, NULL, 0, "type", PAR_VERTEX); @@ -910,14 +922,14 @@ static int parent_set_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent } uiPupMenuEnd(C, pup); - + return OPERATOR_CANCELLED; } static bool parent_set_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) { const char *prop_id = RNA_property_identifier(prop); - int type = RNA_enum_get(ptr, "type"); + const int type = RNA_enum_get(ptr, "type"); /* Only show XMirror for PAR_ARMATURE_ENVELOPE and PAR_ARMATURE_AUTO! */ if (STREQ(prop_id, "xmirror")) { @@ -948,22 +960,21 @@ void OBJECT_OT_parent_set(wmOperatorType *ot) ot->name = "Make Parent"; ot->description = "Set the object's parenting"; ot->idname = "OBJECT_OT_parent_set"; - + /* api callbacks */ ot->invoke = parent_set_invoke; ot->exec = parent_set_exec; ot->poll = ED_operator_object_active; ot->ui = parent_set_ui; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + RNA_def_enum(ot->srna, "type", prop_make_parent_types, 0, "Type", ""); RNA_def_boolean(ot->srna, "xmirror", false, "X Mirror", "Apply weights symmetrically along X axis, for Envelope/Automatic vertex groups creation"); RNA_def_boolean(ot->srna, "keep_transform", false, "Keep Transform", "Apply transformation before parenting"); - } /* ************ Make Parent Without Inverse Operator ******************* */ @@ -972,9 +983,9 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Object *par = ED_object_active_context(C); - + DAG_id_tag_update(&par->id, OB_RECALC_OB); - + /* context iterator */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { @@ -986,10 +997,10 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op) /* clear inverse matrix and also the object location */ unit_m4(ob->parentinv); memset(ob->loc, 0, 3 * sizeof(float)); - + /* set recalc flags */ DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA); - + /* set parenting type for object - object only... */ ob->parent = par; ob->partype = PAROBJECT; /* note, dna define, not operator property */ @@ -997,10 +1008,10 @@ static int parent_noinv_set_exec(bContext *C, wmOperator *op) } } CTX_DATA_END; - + DAG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - + return OPERATOR_FINISHED; } @@ -1010,12 +1021,12 @@ void OBJECT_OT_parent_no_inverse_set(wmOperatorType *ot) ot->name = "Make Parent without Inverse"; ot->description = "Set the object's parenting without setting the inverse parent correction"; ot->idname = "OBJECT_OT_parent_no_inverse_set"; - + /* api callbacks */ ot->invoke = WM_operator_confirm; ot->exec = parent_noinv_set_exec; ot->poll = ED_operator_object_active_editable; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -1040,23 +1051,22 @@ static int object_slow_parent_clear_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; WM_event_add_notifier(C, NC_SCENE, scene); - + return OPERATOR_FINISHED; } void OBJECT_OT_slow_parent_clear(wmOperatorType *ot) { - /* identifiers */ ot->name = "Clear Slow Parent"; ot->description = "Clear the object's slow parent"; ot->idname = "OBJECT_OT_slow_parent_clear"; - + /* api callbacks */ ot->invoke = WM_operator_confirm; ot->exec = object_slow_parent_clear_exec; ot->poll = ED_operator_view3d_active; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -1073,37 +1083,40 @@ static int object_slow_parent_set_exec(bContext *C, wmOperator *UNUSED(op)) ob->partype |= PARSLOW; DAG_id_tag_update(&ob->id, OB_RECALC_OB); - } CTX_DATA_END; WM_event_add_notifier(C, NC_SCENE, scene); - + return OPERATOR_FINISHED; } void OBJECT_OT_slow_parent_set(wmOperatorType *ot) { - /* identifiers */ ot->name = "Set Slow Parent"; ot->description = "Set the object's slow parent"; ot->idname = "OBJECT_OT_slow_parent_set"; - + /* api callbacks */ ot->invoke = WM_operator_confirm; ot->exec = object_slow_parent_set_exec; ot->poll = ED_operator_view3d_active; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************** Clear Track Operator ******************* */ +enum { + CLEAR_TRACK = 1, + CLEAR_TRACK_KEEP_TRANSFORM = 2, +}; + static EnumPropertyItem prop_clear_track_types[] = { - {0, "CLEAR", 0, "Clear Track", ""}, - {1, "CLEAR_KEEP_TRANSFORM", 0, "Clear and Keep Transformation (Clear Track)", ""}, + {CLEAR_TRACK, "CLEAR", 0, "Clear Track", ""}, + {CLEAR_TRACK_KEEP_TRANSFORM, "CLEAR_KEEP_TRANSFORM", 0, "Clear and Keep Transformation (Clear Track)", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1111,7 +1124,7 @@ static EnumPropertyItem prop_clear_track_types[] = { static int object_track_clear_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - int type = RNA_enum_get(op->ptr, "type"); + const int type = RNA_enum_get(op->ptr, "type"); if (CTX_data_edit_object(C)) { BKE_report(op->reports, RPT_ERROR, "Operation cannot be performed in edit mode"); @@ -1120,19 +1133,19 @@ static int object_track_clear_exec(bContext *C, wmOperator *op) CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { bConstraint *con, *pcon; - + /* remove track-object for old track */ ob->track = NULL; DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - + /* also remove all tracking constraints */ for (con = ob->constraints.last; con; con = pcon) { pcon = con->prev; - if (ELEM3(con->type, CONSTRAINT_TYPE_TRACKTO, CONSTRAINT_TYPE_LOCKTRACK, CONSTRAINT_TYPE_DAMPTRACK)) + if (ELEM(con->type, CONSTRAINT_TYPE_TRACKTO, CONSTRAINT_TYPE_LOCKTRACK, CONSTRAINT_TYPE_DAMPTRACK)) BKE_constraint_remove(&ob->constraints, con); } - - if (type == 1) + + if (type == CLEAR_TRACK_KEEP_TRANSFORM) BKE_object_apply_mat4(ob, ob->obmat, true, true); } CTX_DATA_END; @@ -1149,25 +1162,31 @@ void OBJECT_OT_track_clear(wmOperatorType *ot) ot->name = "Clear Track"; ot->description = "Clear tracking constraint or flag from object"; ot->idname = "OBJECT_OT_track_clear"; - + /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = object_track_clear_exec; - + ot->poll = ED_operator_objectmode; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + ot->prop = RNA_def_enum(ot->srna, "type", prop_clear_track_types, 0, "Type", ""); } /************************** Make Track Operator *****************************/ +enum { + CREATE_TRACK_DAMPTRACK = 1, + CREATE_TRACK_TRACKTO = 2, + CREATE_TRACK_LOCKTRACK = 3, +}; + static EnumPropertyItem prop_make_track_types[] = { - {1, "DAMPTRACK", 0, "Damped Track Constraint", ""}, - {2, "TRACKTO", 0, "Track To Constraint", ""}, - {3, "LOCKTRACK", 0, "Lock Track Constraint", ""}, + {CREATE_TRACK_DAMPTRACK, "DAMPTRACK", 0, "Damped Track Constraint", ""}, + {CREATE_TRACK_TRACKTO, "TRACKTO", 0, "Track To Constraint", ""}, + {CREATE_TRACK_LOCKTRACK, "LOCKTRACK", 0, "Lock Track Constraint", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1175,78 +1194,86 @@ static int track_set_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Object *obact = ED_object_active_context(C); - - int type = RNA_enum_get(op->ptr, "type"); - - if (type == 1) { - bConstraint *con; - bDampTrackConstraint *data; - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + const int type = RNA_enum_get(op->ptr, "type"); + + switch (type) { + case CREATE_TRACK_DAMPTRACK: { - if (ob != obact) { - con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_DAMPTRACK); + bConstraint *con; + bDampTrackConstraint *data; - data = con->data; - data->tar = obact; - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - - /* Lamp, Camera and Speaker track differently by default */ - if (ELEM3(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { - data->trackflag = TRACK_nZ; + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + { + if (ob != obact) { + con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_DAMPTRACK); + + data = con->data; + data->tar = obact; + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + /* Lamp, Camera and Speaker track differently by default */ + if (ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { + data->trackflag = TRACK_nZ; + } } } + CTX_DATA_END; + break; } - CTX_DATA_END; - } - else if (type == 2) { - bConstraint *con; - bTrackToConstraint *data; - - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + case CREATE_TRACK_TRACKTO: { - if (ob != obact) { - con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_TRACKTO); + bConstraint *con; + bTrackToConstraint *data; - data = con->data; - data->tar = obact; - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - - /* Lamp, Camera and Speaker track differently by default */ - if (ELEM3(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { - data->reserved1 = TRACK_nZ; - data->reserved2 = UP_Y; + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + { + if (ob != obact) { + con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_TRACKTO); + + data = con->data; + data->tar = obact; + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + /* Lamp, Camera and Speaker track differently by default */ + if (ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { + data->reserved1 = TRACK_nZ; + data->reserved2 = UP_Y; + } } } + CTX_DATA_END; + break; } - CTX_DATA_END; - } - else if (type == 3) { - bConstraint *con; - bLockTrackConstraint *data; - - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + case CREATE_TRACK_LOCKTRACK: { - if (ob != obact) { - con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_LOCKTRACK); + bConstraint *con; + bLockTrackConstraint *data; - data = con->data; - data->tar = obact; - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); - - /* Lamp, Camera and Speaker track differently by default */ - if (ELEM3(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { - data->trackflag = TRACK_nZ; - data->lockflag = LOCK_Y; + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) + { + if (ob != obact) { + con = BKE_constraint_add_for_object(ob, "AutoTrack", CONSTRAINT_TYPE_LOCKTRACK); + + data = con->data; + data->tar = obact; + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + /* Lamp, Camera and Speaker track differently by default */ + if (ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_SPEAKER)) { + data->trackflag = TRACK_nZ; + data->lockflag = LOCK_Y; + } } } + CTX_DATA_END; + break; } - CTX_DATA_END; } - + DAG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - + return OPERATOR_FINISHED; } @@ -1254,18 +1281,18 @@ void OBJECT_OT_track_set(wmOperatorType *ot) { /* identifiers */ ot->name = "Make Track"; - ot->description = "Make the object track another object, either by constraint or old way or locked track"; + ot->description = "Make the object track another object, using various methods/constraints"; ot->idname = "OBJECT_OT_track_set"; - + /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = track_set_exec; - + ot->poll = ED_operator_objectmode; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", prop_make_track_types, 0, "Type", ""); } @@ -1287,7 +1314,7 @@ static unsigned int move_to_layer_init(bContext *C, wmOperator *op) for (a = 0; a < 20; a++) values[a] = (lay & (1 << a)); - + RNA_boolean_set_array(op->ptr, "layers", values); } else { @@ -1320,12 +1347,12 @@ static int move_to_layer_exec(bContext *C, wmOperator *op) View3D *v3d = CTX_wm_view3d(C); unsigned int lay, local; /* bool is_lamp = false; */ /* UNUSED */ - + lay = move_to_layer_init(C, op); lay &= 0xFFFFFF; if (lay == 0) return OPERATOR_CANCELLED; - + if (v3d && v3d->localvd) { /* now we can move out of localview. */ /* note: layers are set in bases, library objects work for this */ @@ -1353,9 +1380,9 @@ static int move_to_layer_exec(bContext *C, wmOperator *op) } CTX_DATA_END; } - + /* warning, active object may be hidden now */ - + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, scene); WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); @@ -1370,15 +1397,15 @@ void OBJECT_OT_move_to_layer(wmOperatorType *ot) ot->name = "Move to Layer"; ot->description = "Move the object to different layers"; ot->idname = "OBJECT_OT_move_to_layer"; - + /* api callbacks */ ot->invoke = move_to_layer_invoke; ot->exec = move_to_layer_exec; ot->poll = ED_operator_objectmode; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* properties */ RNA_def_boolean_layer_member(ot->srna, "layers", 20, NULL, "Layer", ""); } @@ -1390,13 +1417,12 @@ static void link_to_scene(Main *UNUSED(bmain), unsigned short UNUSED(nr)) { Scene *sce = (Scene *) BLI_findlink(&bmain->scene, G.curscreen->scenenr - 1); Base *base, *nbase; - - if (sce == 0) return; + + if (sce == NULL) return; if (sce->id.lib) return; - + for (base = FIRSTBASE; base; base = base->next) { if (TESTBASE(v3d, base)) { - nbase = MEM_mallocN(sizeof(Base), "newbase"); *nbase = *base; BLI_addhead(&(sce->base), nbase); @@ -1453,48 +1479,45 @@ static int make_links_scene_exec(bContext *C, wmOperator *op) } enum { - MAKE_LINKS_OBDATA = 1, - MAKE_LINKS_MATERIALS, - MAKE_LINKS_ANIMDATA, - MAKE_LINKS_GROUP, - MAKE_LINKS_DUPLIGROUP, - MAKE_LINKS_MODIFIERS, - MAKE_LINKS_FONTS + MAKE_LINKS_OBDATA = 1, + MAKE_LINKS_MATERIALS = 2, + MAKE_LINKS_ANIMDATA = 3, + MAKE_LINKS_GROUP = 4, + MAKE_LINKS_DUPLIGROUP = 5, + MAKE_LINKS_MODIFIERS = 6, + MAKE_LINKS_FONTS = 7, }; -/* Return 1 if make link data is allow, zero otherwise */ -static int allow_make_links_data(const int type, Object *ob_src, Object *ob_dst) +/* Return true if make link data is allowed, false otherwise */ +static bool allow_make_links_data(const int type, Object *ob_src, Object *ob_dst) { switch (type) { case MAKE_LINKS_OBDATA: - if (ob_src->type == ob_dst->type && ob_src->type != OB_EMPTY) - return 1; + if (ob_src->type == ob_dst->type && ob_src->type != OB_EMPTY) { + return true; + } break; case MAKE_LINKS_MATERIALS: - if (OB_TYPE_SUPPORT_MATERIAL(ob_src->type) && - OB_TYPE_SUPPORT_MATERIAL(ob_dst->type)) - { - return 1; + if (OB_TYPE_SUPPORT_MATERIAL(ob_src->type) && OB_TYPE_SUPPORT_MATERIAL(ob_dst->type)) { + return true; } break; case MAKE_LINKS_ANIMDATA: case MAKE_LINKS_GROUP: case MAKE_LINKS_DUPLIGROUP: - return 1; + return true; case MAKE_LINKS_MODIFIERS: - if (ob_src->type != OB_EMPTY && ob_dst->type != OB_EMPTY) - return 1; + if (!ELEM(OB_EMPTY, ob_src->type, ob_dst->type)) { + return true; + } break; case MAKE_LINKS_FONTS: - if ((ob_src->data != ob_dst->data) && - (ob_src->type == OB_FONT) && - (ob_dst->type == OB_FONT)) - { - return 1; + if ((ob_src->data != ob_dst->data) && (ob_src->type == OB_FONT) && (ob_dst->type == OB_FONT)) { + return true; } break; } - return 0; + return false; } static int make_links_data_exec(bContext *C, wmOperator *op) @@ -1635,6 +1658,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op) DAG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C)); + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, CTX_wm_view3d(C)); WM_event_add_notifier(C, NC_OBJECT, NULL); return OPERATOR_FINISHED; @@ -1679,7 +1703,7 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /* identifiers */ ot->name = "Link Data"; - ot->description = "Make links from the active object to other selected objects"; + ot->description = "Apply active object links to other selected objects"; ot->idname = "OBJECT_OT_make_links_data"; /* api callbacks */ @@ -1696,13 +1720,13 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /**************************** Make Single User ********************************/ -static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, int flag, bool copy_groups) +static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const int flag, const bool copy_groups) { Base *base; Object *ob, *obn; Group *group, *groupn; GroupObject *go; - + clear_sca_new_poins(); /* sensor/contr/act */ /* newid may still have some trash from Outliner tree building, @@ -1713,7 +1737,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, int flag /* duplicate (must set newid) */ for (base = FIRSTBASE; base; base = base->next) { ob = base->object; - + if ((base->flag & flag) == flag) { if (ob->id.lib == NULL && ob->id.us > 1) { /* base gets copy of object */ @@ -1760,10 +1784,10 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, int flag /* group pointers in scene */ BKE_scene_groups_relink(scene); - + ID_NEW(scene->camera); if (v3d) ID_NEW(v3d->camera); - + /* object and group pointers */ for (base = FIRSTBASE; base; base = base->next) { BKE_object_relink(base->object); @@ -1777,7 +1801,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, int flag void ED_object_single_user(Main *bmain, Scene *scene, Object *ob) { Base *base; - bool copy_groups = false; + const bool copy_groups = false; for (base = FIRSTBASE; base; base = base->next) { if (base->object == ob) base->flag |= OB_DONE; @@ -1787,11 +1811,11 @@ 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, int totcol) +static void new_id_matar(Material **matar, const int totcol) { ID *id; int a; - + for (a = 0; a < totcol; a++) { id = (ID *)matar[a]; if (id && id->lib == NULL) { @@ -1809,12 +1833,12 @@ static void new_id_matar(Material **matar, int totcol) } } -static void single_obdata_users(Main *bmain, Scene *scene, int flag) +static void single_obdata_users(Main *bmain, Scene *scene, const int flag) { Object *ob; Lamp *la; Curve *cu; - //Camera *cam; + /* Camera *cam; */ Base *base; Mesh *me; Lattice *lat; @@ -1825,7 +1849,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, int flag) ob = base->object; if (ob->id.lib == NULL && (base->flag & flag) == flag) { id = ob->data; - + if (id && id->us > 1 && id->lib == NULL) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -1886,12 +1910,10 @@ static void single_obdata_users(Main *bmain, Scene *scene, int flag) id->us--; id->newid = ob->data; - } - } } - + me = bmain->mesh.first; while (me) { ID_NEW(me->texcomesh); @@ -1899,11 +1921,11 @@ static void single_obdata_users(Main *bmain, Scene *scene, int flag) } } -static void single_object_action_users(Scene *scene, int flag) +static void single_object_action_users(Scene *scene, const int flag) { Object *ob; Base *base; - + for (base = FIRSTBASE; base; base = base->next) { ob = base->object; if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) { @@ -1913,27 +1935,26 @@ static void single_object_action_users(Scene *scene, int flag) } } -static void single_mat_users(Scene *scene, int flag, int do_textures) +static void single_mat_users(Scene *scene, const int flag, const bool do_textures) { Object *ob; Base *base; Material *ma, *man; Tex *tex; int a, b; - + for (base = FIRSTBASE; base; base = base->next) { ob = base->object; if (ob->id.lib == NULL && (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_NEW: this functions guaranteed delivers single_users! */ - + if (ma->id.us > 1) { man = BKE_material_copy(ma); BKE_copy_animdata_id_action(&man->id); - + man->id.us = 0; assign_material(ob, man, a, BKE_MAT_ASSIGN_USERPREF); @@ -1959,10 +1980,10 @@ static void single_mat_users(Scene *scene, int flag, int do_textures) static void do_single_tex_user(Tex **from) { Tex *tex, *texn; - + tex = *from; if (tex == NULL) return; - + if (tex->id.newid) { *from = (Tex *)tex->id.newid; id_us_plus(tex->id.newid); @@ -1984,7 +2005,7 @@ static void single_tex_users_expand(Main *bmain) Lamp *la; World *wo; int b; - + for (ma = bmain->mat.first; ma; ma = ma->id.next) { if (ma->id.flag & LIB_NEW) { for (b = 0; b < MAX_MTEX; b++) { @@ -2025,7 +2046,7 @@ static void single_mat_users_expand(Main *bmain) MetaBall *mb; Material *ma; int a; - + for (ob = bmain->object.first; ob; ob = ob->id.next) if (ob->id.flag & LIB_NEW) new_id_matar(ob->mat, ob->totcol); @@ -2051,7 +2072,7 @@ static void single_mat_users_expand(Main *bmain) } /* used for copying scenes */ -void ED_object_single_users(Main *bmain, Scene *scene, bool full, bool copy_groups) +void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bool copy_groups) { single_object_users(bmain, scene, NULL, 0, copy_groups); @@ -2072,13 +2093,13 @@ static void make_local_makelocalmaterial(Material *ma) { AnimData *adt; int b; - + id_make_local(&ma->id, 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); - + adt = BKE_animdata_from_id(&ma->id); if (adt) BKE_animdata_make_local(adt); @@ -2086,13 +2107,13 @@ static void make_local_makelocalmaterial(Material *ma) } enum { - MAKE_LOCAL_SELECT_OB, - MAKE_LOCAL_SELECT_OBDATA, - MAKE_LOCAL_SELECT_OBDATA_MATERIAL, - MAKE_LOCAL_ALL + MAKE_LOCAL_SELECT_OB = 1, + MAKE_LOCAL_SELECT_OBDATA = 2, + MAKE_LOCAL_SELECT_OBDATA_MATERIAL = 3, + MAKE_LOCAL_ALL = 4, }; -static bool tag_localizable_looper(void *UNUSED(user_data), ID **id_pointer, int UNUSED(cd_flag)) +static bool tag_localizable_looper(void *UNUSED(user_data), ID **id_pointer, const int UNUSED(cd_flag)) { if (*id_pointer) { (*id_pointer)->flag &= ~LIB_DOIT; @@ -2100,7 +2121,7 @@ static bool tag_localizable_looper(void *UNUSED(user_data), ID **id_pointer, int return true; } -static void tag_localizable_objects(bContext *C, int mode) +static void tag_localizable_objects(bContext *C, const int mode) { Main *bmain = CTX_data_main(C); Object *object; @@ -2142,17 +2163,57 @@ static void tag_localizable_objects(bContext *C, int mode) /* TODO(sergey): Drivers targets? */ } +/** + * Instance indirectly referenced zero user objects, + * otherwise they're lost on reload, see T40595. + */ +static bool make_local_all__instance_indirect_unused(Main *bmain, Scene *scene) +{ + Object *ob; + bool changed = false; + + for (ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->id.lib && (ob->id.us == 0)) { + Base *base; + + ob->id.us = 1; + + /* not essential, but for correctness */ + id_lib_extern(&ob->id); + + base = BKE_scene_base_add(scene, ob); + base->flag |= SELECT; + base->object->flag = base->flag; + DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + + changed = true; + } + } + + return changed; +} + static int make_local_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); AnimData *adt; ParticleSystem *psys; Material *ma, ***matarar; Lamp *la; ID *id; - int a, b, mode = RNA_enum_get(op->ptr, "type"); - + const int mode = RNA_enum_get(op->ptr, "type"); + int a, b; + if (mode == MAKE_LOCAL_ALL) { + /* de-select so the user can differentiate newly instanced from existing objects */ + BKE_scene_base_deselect_all(scene); + + if (make_local_all__instance_indirect_unused(bmain, scene)) { + BKE_report(op->reports, RPT_INFO, + "Orphan library objects added to the current scene to avoid loss"); + } + BKE_library_make_local(bmain, NULL, false); /* NULL is all libs */ WM_event_add_notifier(C, NC_WINDOW, NULL); return OPERATOR_FINISHED; @@ -2160,7 +2221,7 @@ static int make_local_exec(bContext *C, wmOperator *op) tag_localizable_objects(C, mode); BKE_main_id_clear_newpoins(bmain); - + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { if ((ob->id.flag & LIB_DOIT) == 0) { @@ -2171,7 +2232,7 @@ static int make_local_exec(bContext *C, wmOperator *op) id_make_local(&ob->id, false); } CTX_DATA_END; - + /* maybe object pointers */ CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { @@ -2188,12 +2249,12 @@ 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); adt = BKE_animdata_from_id(id); if (adt) BKE_animdata_make_local(adt); - + /* tag indirect data direct */ matarar = give_matarar(ob); if (matarar) { @@ -2233,7 +2294,7 @@ static int make_local_exec(bContext *C, wmOperator *op) if (ma) make_local_makelocalmaterial(ma); } - + matarar = (Material ***)give_matarar(ob); if (matarar) { for (a = 0; a < ob->totcol; a++) { @@ -2266,26 +2327,33 @@ void OBJECT_OT_make_local(wmOperatorType *ot) ot->name = "Make Local"; ot->description = "Make library linked datablocks local to this file"; ot->idname = "OBJECT_OT_make_local"; - + /* api callbacks */ ot->invoke = WM_menu_invoke; ot->exec = make_local_exec; ot->poll = ED_operator_objectmode; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* properties */ ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); } +enum { + /* Be careful with those values, they are used as bitflags in some cases, in others as bool... + * See single_object_users, single_obdata_users, single_object_action_users, etc.< */ + MAKE_SINGLE_USER_ALL = 0, + MAKE_SINGLE_USER_SELECTED = SELECT, +}; + static int make_single_user_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); /* ok if this is NULL */ - int flag = RNA_enum_get(op->ptr, "type"); /* 0==ALL, SELECTED==selected objecs */ - bool copy_groups = false; + const int flag = RNA_enum_get(op->ptr, "type"); + const bool copy_groups = false; bool update_deps = false; BKE_main_id_clear_newpoins(bmain); @@ -2332,8 +2400,8 @@ static int make_single_user_exec(bContext *C, wmOperator *op) void OBJECT_OT_make_single_user(wmOperatorType *ot) { static EnumPropertyItem type_items[] = { - {SELECT, "SELECTED_OBJECTS", 0, "Selected Objects", ""}, - {0, "ALL", 0, "All", ""}, + {MAKE_SINGLE_USER_SELECTED, "SELECTED_OBJECTS", 0, "Selected Objects", ""}, + {MAKE_SINGLE_USER_ALL, "ALL", 0, "All", ""}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ @@ -2355,7 +2423,8 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) RNA_def_boolean(ot->srna, "object", 0, "Object", "Make single user objects"); RNA_def_boolean(ot->srna, "obdata", 0, "Object Data", "Make single user object data"); RNA_def_boolean(ot->srna, "material", 0, "Materials", "Make materials local to each datablock"); - RNA_def_boolean(ot->srna, "texture", 0, "Textures", "Make textures local to each material"); + RNA_def_boolean(ot->srna, "texture", 0, "Textures", + "Make textures local to each material (needs 'Materials' to be set too)"); RNA_def_boolean(ot->srna, "animation", 0, "Object Animation", "Make animation data local to each object"); } @@ -2364,17 +2433,20 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent Base *base = ED_view3d_give_base_under_cursor(C, event->mval); Material *ma; char name[MAX_ID_NAME - 2]; - + RNA_string_get(op->ptr, "name", name); ma = (Material *)BKE_libblock_find_name(ID_MA, name); if (base == NULL || ma == NULL) return OPERATOR_CANCELLED; - + assign_material(base->object, ma, 1, BKE_MAT_ASSIGN_USERPREF); - + + DAG_id_tag_update(&base->object->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, base->object); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C)); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); - + return OPERATOR_FINISHED; } @@ -2382,19 +2454,70 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent /* assigns to object under cursor, only first material slot */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { - /* identifiers */ ot->name = "Drop Named Material on Object"; ot->description = ""; ot->idname = "OBJECT_OT_drop_named_material"; - + /* api callbacks */ ot->invoke = drop_named_material_invoke; ot->poll = ED_operator_objectmode; - + /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; - + /* properties */ RNA_def_string(ot->srna, "name", "Material", MAX_ID_NAME - 2, "Name", "Material name to assign"); } + +static int object_unlink_data_exec(bContext *C, wmOperator *op) +{ + ID *id; + PropertyPointerRNA pprop; + + uiIDContextProperty(C, &pprop.ptr, &pprop.prop); + + if (pprop.prop == NULL) { + BKE_report(op->reports, RPT_ERROR, "Incorrect context for running object data unlink"); + return OPERATOR_CANCELLED; + } + + id = pprop.ptr.id.data; + + if (GS(id->name) == ID_OB) { + Object *ob = (Object *)id; + if (ob->data) { + ID *id_data = ob->data; + + if (GS(id_data->name) == ID_IM) { + id_us_min(id_data); + ob->data = NULL; + } + else { + BKE_report(op->reports, RPT_ERROR, "Can't unlink this object data"); + return OPERATOR_CANCELLED; + } + } + } + + RNA_property_update(C, &pprop.ptr, pprop.prop); + + return OPERATOR_FINISHED; +} + +/** + * \note Only for empty-image objects, this operator is needed + */ +void OBJECT_OT_unlink_data(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink"; + ot->idname = "OBJECT_OT_unlink_data"; + ot->description = ""; + + /* api callbacks */ + ot->exec = object_unlink_data_exec; + + /* flags */ + ot->flag = OPTYPE_INTERNAL; +} diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index b1a78407491..e295a63848a 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -315,22 +315,20 @@ static bool object_select_all_by_dup_group(bContext *C, Object *ob) static bool object_select_all_by_particle(bContext *C, Object *ob) { + ParticleSystem *psys_act = psys_get_current(ob); bool changed = false; CTX_DATA_BEGIN (C, Base *, base, visible_bases) { if ((base->flag & SELECT) == 0) { - /* loop through other, then actives particles*/ + /* loop through other particles*/ ParticleSystem *psys; - ParticleSystem *psys_act; - + for (psys = base->object->particlesystem.first; psys; psys = psys->next) { - for (psys_act = ob->particlesystem.first; psys_act; psys_act = psys_act->next) { - if (psys->part == psys_act->part) { - base->flag |= SELECT; - changed = true; - break; - } + if (psys->part == psys_act->part) { + base->flag |= SELECT; + changed = true; + break; } if (base->flag & SELECT) { @@ -512,21 +510,36 @@ void OBJECT_OT_select_linked(wmOperatorType *ot) /*********************** Selected Grouped ********************/ +enum { + OBJECT_GRPSEL_CHILDREN_RECURSIVE = 0, + OBJECT_GRPSEL_CHILDREN = 1, + OBJECT_GRPSEL_PARENT = 2, + OBJECT_GRPSEL_SIBLINGS = 3, + OBJECT_GRPSEL_TYPE = 4, + OBJECT_GRPSEL_LAYER = 5, + OBJECT_GRPSEL_GROUP = 6, + OBJECT_GRPSEL_HOOK = 7, + OBJECT_GRPSEL_PASS = 8, + OBJECT_GRPSEL_COLOR = 9, + OBJECT_GRPSEL_PROPERTIES = 10, + OBJECT_GRPSEL_KEYINGSET = 11, + OBJECT_GRPSEL_LAMP_TYPE = 12, +}; + static EnumPropertyItem prop_select_grouped_types[] = { - {1, "CHILDREN_RECURSIVE", 0, "Children", ""}, - {2, "CHILDREN", 0, "Immediate Children", ""}, - {3, "PARENT", 0, "Parent", ""}, - {4, "SIBLINGS", 0, "Siblings", "Shared Parent"}, - {5, "TYPE", 0, "Type", "Shared object type"}, - {6, "LAYER", 0, "Layer", "Shared layers"}, - {7, "GROUP", 0, "Group", "Shared group"}, - {8, "HOOK", 0, "Hook", ""}, - {9, "PASS", 0, "Pass", "Render pass Index"}, - {10, "COLOR", 0, "Color", "Object Color"}, - {11, "PROPERTIES", 0, "Properties", "Game Properties"}, - {12, "KEYINGSET", 0, "Keying Set", "Objects included in active Keying Set"}, - {13, "LAMP_TYPE", 0, "Lamp Type", "Matching lamp types"}, - {14, "PASS_INDEX", 0, "Pass Index", "Matching object pass index"}, + {OBJECT_GRPSEL_CHILDREN_RECURSIVE, "CHILDREN_RECURSIVE", 0, "Children", ""}, + {OBJECT_GRPSEL_CHILDREN, "CHILDREN", 0, "Immediate Children", ""}, + {OBJECT_GRPSEL_PARENT, "PARENT", 0, "Parent", ""}, + {OBJECT_GRPSEL_SIBLINGS, "SIBLINGS", 0, "Siblings", "Shared Parent"}, + {OBJECT_GRPSEL_TYPE, "TYPE", 0, "Type", "Shared object type"}, + {OBJECT_GRPSEL_LAYER, "LAYER", 0, "Layer", "Shared layers"}, + {OBJECT_GRPSEL_GROUP, "GROUP", 0, "Group", "Shared group"}, + {OBJECT_GRPSEL_HOOK, "HOOK", 0, "Hook", ""}, + {OBJECT_GRPSEL_PASS, "PASS", 0, "Pass", "Render pass Index"}, + {OBJECT_GRPSEL_COLOR, "COLOR", 0, "Color", "Object Color"}, + {OBJECT_GRPSEL_PROPERTIES, "PROPERTIES", 0, "Properties", "Game Properties"}, + {OBJECT_GRPSEL_KEYINGSET, "KEYINGSET", 0, "Keying Set", "Objects included in active Keying Set"}, + {OBJECT_GRPSEL_LAMP_TYPE, "LAMP_TYPE", 0, "Lamp Type", "Matching lamp types"}, {0, NULL, 0, NULL, NULL} }; @@ -658,7 +671,7 @@ static bool select_grouped_siblings(bContext *C, Object *ob) CTX_DATA_END; return changed; } -static bool select_similar_lamps(bContext *C, Object *ob) +static bool select_grouped_lamptype(bContext *C, Object *ob) { Lamp *la = ob->data; @@ -677,20 +690,6 @@ static bool select_similar_lamps(bContext *C, Object *ob) CTX_DATA_END; return changed; } -static bool select_similar_pass_index(bContext *C, Object *ob) -{ - bool changed = false; - - CTX_DATA_BEGIN (C, Base *, base, selectable_bases) - { - if ((base->object->index == ob->index) && !(base->flag & SELECT)) { - ED_base_object_select(base, BA_SELECT); - changed = true; - } - } - CTX_DATA_END; - return changed; -} static bool select_grouped_type(bContext *C, Object *ob) { bool changed = false; @@ -754,11 +753,11 @@ static bool select_grouped_color(bContext *C, Object *ob) static bool objects_share_gameprop(Object *a, Object *b) { bProperty *prop; - /*make a copy of all its properties*/ for (prop = a->prop.first; prop; prop = prop->next) { - if (BKE_bproperty_object_get(b, prop->name) ) + if (BKE_bproperty_object_get(b, prop->name)) { return 1; + } } return 0; } @@ -778,14 +777,29 @@ static bool select_grouped_gameprops(bContext *C, Object *ob) return changed; } -static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob)) +static bool select_grouped_keyingset(bContext *C, Object *UNUSED(ob), ReportList *reports) { KeyingSet *ks = ANIM_scene_get_active_keyingset(CTX_data_scene(C)); bool changed = false; /* firstly, validate KeyingSet */ - if ((ks == NULL) || (ANIM_validate_keyingset(C, NULL, ks) != 0)) - return 0; + if (ks == NULL) { + BKE_report(reports, RPT_ERROR, "No active Keying Set to use"); + return false; + } + else if (ANIM_validate_keyingset(C, NULL, ks) != 0) { + if (ks->paths.first == NULL) { + if ((ks->flag & KEYINGSET_ABSOLUTE) == 0) { + BKE_report(reports, RPT_ERROR, + "Use another Keying Set, as the active one depends on the currently " + "selected objects or cannot find any targets due to unsuitable context"); + } + else { + BKE_report(reports, RPT_ERROR, "Keying Set does not contain any paths"); + } + } + return false; + } /* select each object that Keying Set refers to */ /* TODO: perhaps to be more in line with the rest of these, we should only take objects @@ -818,11 +832,11 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Object *ob; - int nr = RNA_enum_get(op->ptr, "type"); + const int type = RNA_enum_get(op->ptr, "type"); bool changed = false, extend; extend = RNA_boolean_get(op->ptr, "extend"); - + if (extend == 0) { CTX_DATA_BEGIN (C, Base *, base, visible_bases) { @@ -831,38 +845,66 @@ static int object_select_grouped_exec(bContext *C, wmOperator *op) } CTX_DATA_END; } - + ob = OBACT; if (ob == NULL) { BKE_report(op->reports, RPT_ERROR, "No active object"); return OPERATOR_CANCELLED; } - if (nr == 13 && ob->type != OB_LAMP) { - BKE_report(op->reports, RPT_ERROR, "Active object must be a lamp"); - return OPERATOR_CANCELLED; + switch (type) { + case OBJECT_GRPSEL_CHILDREN_RECURSIVE: + changed = select_grouped_children(C, ob, true); + break; + case OBJECT_GRPSEL_CHILDREN: + changed = select_grouped_children(C, ob, false); + break; + case OBJECT_GRPSEL_PARENT: + changed = select_grouped_parent(C); + break; + case OBJECT_GRPSEL_SIBLINGS: + changed = select_grouped_siblings(C, ob); + break; + case OBJECT_GRPSEL_TYPE: + changed = select_grouped_type(C, ob); + break; + case OBJECT_GRPSEL_LAYER: + changed = select_grouped_layer(C, ob); + break; + case OBJECT_GRPSEL_GROUP: + changed = select_grouped_group(C, ob); + break; + case OBJECT_GRPSEL_HOOK: + changed = select_grouped_object_hooks(C, ob); + break; + case OBJECT_GRPSEL_PASS: + changed = select_grouped_index_object(C, ob); + break; + case OBJECT_GRPSEL_COLOR: + changed = select_grouped_color(C, ob); + break; + case OBJECT_GRPSEL_PROPERTIES: + changed = select_grouped_gameprops(C, ob); + break; + case OBJECT_GRPSEL_KEYINGSET: + changed = select_grouped_keyingset(C, ob, op->reports); + break; + case OBJECT_GRPSEL_LAMP_TYPE: + if (ob->type != OB_LAMP) { + BKE_report(op->reports, RPT_ERROR, "Active object must be a lamp"); + break; + } + changed = select_grouped_lamptype(C, ob); + break; + default: + break; } - if (nr == 1) changed |= select_grouped_children(C, ob, 1); - else if (nr == 2) changed |= select_grouped_children(C, ob, 0); - else if (nr == 3) changed |= select_grouped_parent(C); - else if (nr == 4) changed |= select_grouped_siblings(C, ob); - else if (nr == 5) changed |= select_grouped_type(C, ob); - else if (nr == 6) changed |= select_grouped_layer(C, ob); - else if (nr == 7) changed |= select_grouped_group(C, ob); - else if (nr == 8) changed |= select_grouped_object_hooks(C, ob); - else if (nr == 9) changed |= select_grouped_index_object(C, ob); - else if (nr == 10) changed |= select_grouped_color(C, ob); - else if (nr == 11) changed |= select_grouped_gameprops(C, ob); - else if (nr == 12) changed |= select_grouped_keyingset(C, ob); - else if (nr == 13) changed |= select_similar_lamps(C, ob); - else if (nr == 14) changed |= select_similar_pass_index(C, ob); - if (changed) { WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); return OPERATOR_FINISHED; } - + return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index 5dd20a76e28..4a24ab66721 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -301,6 +301,16 @@ static int shape_key_mode_exists_poll(bContext *C) (BKE_keyblock_from_object(ob) != NULL); } +static int shape_key_move_poll(bContext *C) +{ + /* Same as shape_key_mode_exists_poll above, but ensure we have at least two shapes! */ + Object *ob = ED_object_context(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); +} + static int shape_key_poll(bContext *C) { Object *ob = ED_object_context(C); @@ -334,7 +344,7 @@ void OBJECT_OT_shape_key_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "from_mix", 1, "From Mix", "Create the new shape key from the existing mix of keys"); + RNA_def_boolean(ot->srna, "from_mix", true, "From Mix", "Create the new shape key from the existing mix of keys"); } static int shape_key_remove_exec(bContext *C, wmOperator *op) @@ -482,51 +492,40 @@ void OBJECT_OT_shape_key_mirror(wmOperatorType *ot) } +enum { + KB_MOVE_TOP = -2, + KB_MOVE_UP = -1, + KB_MOVE_DOWN = 1, + KB_MOVE_BOTTOM = 2, +}; + static int shape_key_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - int type = RNA_enum_get(op->ptr, "type"); Key *key = BKE_key_from_object(ob); + const int type = RNA_enum_get(op->ptr, "type"); + const int totkey = key->totkey; + const int act_index = ob->shapenr - 1; + int new_index; + + switch (type) { + case KB_MOVE_TOP: + /* Replace the ref key only if we're at the top already (only for relative keys) */ + new_index = (ELEM(act_index, 0, 1) || key->type == KEY_NORMAL) ? 0 : 1; + break; + case KB_MOVE_BOTTOM: + new_index = totkey - 1; + break; + case KB_MOVE_UP: + case KB_MOVE_DOWN: + default: + new_index = (totkey + act_index + type) % totkey; + break; + } - if (key) { - KeyBlock *kb, *kb_other; - int shapenr_act = ob->shapenr - 1; - int shapenr_swap = shapenr_act + type; - kb = BLI_findlink(&key->block, shapenr_act); - - if ((type == -1 && kb->prev == NULL) || (type == 1 && kb->next == NULL)) { - return OPERATOR_CANCELLED; - } - - for (kb_other = key->block.first; kb_other; kb_other = kb_other->next) { - if (kb_other->relative == shapenr_act) { - kb_other->relative += type; - } - else if (kb_other->relative == shapenr_swap) { - kb_other->relative -= type; - } - } - - if (type == -1) { - /* move back */ - kb_other = kb->prev; - BLI_remlink(&key->block, kb); - BLI_insertlinkbefore(&key->block, kb_other, kb); - ob->shapenr--; - } - else { - /* move next */ - kb_other = kb->next; - BLI_remlink(&key->block, kb); - BLI_insertlinkafter(&key->block, kb_other, kb); - ob->shapenr++; - } - - SWAP(float, kb_other->pos, kb->pos); /* for absolute shape keys */ - - /* First key is refkey, matches interface and BKE_key_sort */ - key->refkey = key->block.first; + if (!BKE_keyblock_move(ob, act_index, new_index)) { + return OPERATOR_CANCELLED; } DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -538,9 +537,11 @@ static int shape_key_move_exec(bContext *C, wmOperator *op) void OBJECT_OT_shape_key_move(wmOperatorType *ot) { static EnumPropertyItem slot_move[] = { - {-1, "UP", 0, "Up", ""}, - {1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} + {KB_MOVE_TOP, "TOP", 0, "Top", "Top of the list"}, + {KB_MOVE_UP, "UP", 0, "Up", ""}, + {KB_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {KB_MOVE_BOTTOM, "BOTTOM", 0, "Bottom", "Bottom of the list"}, + { 0, NULL, 0, NULL, NULL } }; /* identifiers */ @@ -549,7 +550,7 @@ void OBJECT_OT_shape_key_move(wmOperatorType *ot) ot->description = "Move the active shape key up/down in the list"; /* api callbacks */ - ot->poll = shape_key_mode_poll; + ot->poll = shape_key_move_poll; ot->exec = shape_key_move_exec; /* flags */ diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 6e59f9f4aea..f19765a5344 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -385,7 +385,7 @@ static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_l /* first check if we can execute */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (ELEM6(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF)) { + if (ELEM(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF)) { ID *obdata = ob->data; if (ID_REAL_USERS(obdata) > 1) { BKE_reportf(reports, RPT_ERROR, @@ -472,27 +472,12 @@ static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_l /* apply to object data */ if (ob->type == OB_MESH) { Mesh *me = ob->data; - MVert *mvert; - int a; if (apply_scale) multiresModifier_scale_disp(scene, ob); /* adjust data */ - mvert = me->mvert; - for (a = 0; a < me->totvert; a++, mvert++) - mul_m4_v3(mat, mvert->co); - - if (me->key) { - KeyBlock *kb; - - for (kb = me->key->block.first; kb; kb = kb->next) { - float *fp = kb->data; - - for (a = 0; a < kb->totelem; a++, fp += 3) - mul_m4_v3(mat, fp); - } - } + BKE_mesh_transform(me, mat, true); /* update normals */ BKE_mesh_calc_normals(me); @@ -502,45 +487,17 @@ static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_l } else if (ob->type == OB_LATTICE) { Lattice *lt = ob->data; - BPoint *bp = lt->def; - int a = lt->pntsu * lt->pntsv * lt->pntsw; - - while (a--) { - mul_m4_v3(mat, bp->vec); - bp++; - } + + BKE_lattice_transform(lt, mat, true); } else if (ob->type == OB_MBALL) { MetaBall *mb = ob->data; - ED_mball_transform(mb, mat); + BKE_mball_transform(mb, mat); } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { Curve *cu = ob->data; - - Nurb *nu; - BPoint *bp; - BezTriple *bezt; - int a; - scale = mat3_to_scale(rsmat); - - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (nu->type == CU_BEZIER) { - a = nu->pntsu; - for (bezt = nu->bezt; a--; bezt++) { - mul_m4_v3(mat, bezt->vec[0]); - mul_m4_v3(mat, bezt->vec[1]); - mul_m4_v3(mat, bezt->vec[2]); - bezt->radius *= scale; - } - BKE_nurb_handles_calc(nu); - } - else { - a = nu->pntsu * nu->pntsv; - for (bp = nu->bp; a--; bp++) - mul_m4_v3(mat, bp->vec); - } - } + BKE_curve_transform_ex(cu, mat, true, scale); } else if (ob->type == OB_CAMERA) { MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); @@ -784,7 +741,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) } if (ctx_ob_act) { - BLI_rotatelist_first(&ctx_data_list, (LinkData *)ctx_ob_act); + BLI_listbase_rotate_first(&ctx_data_list, (LinkData *)ctx_ob_act); } for (tob = bmain->object.first; tob; tob = tob->id.next) { @@ -1017,7 +974,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) DAG_id_tag_update(&ob_other->id, OB_RECALC_OB | OB_RECALC_DATA); copy_v3_v3(centn, cent); - mul_mat3_m4_v3(ob_other->obmat, centn); /* ommit translation part */ + mul_mat3_m4_v3(ob_other->obmat, centn); /* omit translation part */ add_v3_v3(ob_other->loc, centn); BKE_object_where_is_calc(scene, ob_other); diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index ccc3e2e8278..777bbabb6e8 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -62,6 +62,7 @@ #include "BKE_depsgraph.h" #include "BKE_mesh_mapping.h" #include "BKE_editmesh.h" +#include "BKE_modifier.h" #include "BKE_report.h" #include "BKE_DerivedMesh.h" #include "BKE_object_deform.h" @@ -2301,7 +2302,6 @@ static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const i if (dvert_array) MEM_freeN(dvert_array); - BLI_SMALLSTACK_FREE(dv_stack); /* not so efficient to get 'dvert_array' again just so unselected verts are NULL'd */ if (use_mirror) { @@ -4324,26 +4324,67 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) return OPERATOR_FINISHED; } -static int vgroup_sort(void *def_a_ptr, void *def_b_ptr) +static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr) { - bDeformGroup *def_a = (bDeformGroup *)def_a_ptr; - bDeformGroup *def_b = (bDeformGroup *)def_b_ptr; + const bDeformGroup *def_a = def_a_ptr; + const bDeformGroup *def_b = 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) */ +static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) +{ + if (bonebase == NULL) { + Object *armobj = modifiers_isDeformedByArmature(ob); + if (armobj != NULL) { + bArmature *armature = armobj->data; + bonebase = &armature->bonebase; + } + } + + if (bonebase != NULL) { + Bone *bone; + for (bone = bonebase->last; bone; bone = bone->prev) { + bDeformGroup *dg = defgroup_find_name(ob, bone->name); + vgroup_sort_bone_hierarchy(ob, &bone->childbase); + + if (dg != NULL) { + BLI_remlink(&ob->defbase, dg); + BLI_addhead(&ob->defbase, dg); + } + } + } + + return; +} + +enum { + SORT_TYPE_NAME = 0, + SORT_TYPE_BONEHIERARCHY = 1 +}; + static int vertex_group_sort_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); char *name_array; int ret; + int sort_type = RNA_enum_get(op->ptr, "sort_type"); /*init remapping*/ name_array = vgroup_init_remap(ob); /*sort vgroup names*/ - BLI_sortlist(&ob->defbase, vgroup_sort); - + switch (sort_type) { + case SORT_TYPE_NAME: + BLI_sortlist(&ob->defbase, vgroup_sort_name); + break; + case SORT_TYPE_BONEHIERARCHY: + vgroup_sort_bone_hierarchy(ob, NULL); + break; + } + /*remap vgroup data to map to correct names*/ ret = vgroup_do_remap(ob, name_array, op); @@ -4359,9 +4400,15 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op) void OBJECT_OT_vertex_group_sort(wmOperatorType *ot) { + static EnumPropertyItem vgroup_sort_type[] = { + {SORT_TYPE_NAME, "NAME", 0, "Name", ""}, + {SORT_TYPE_BONEHIERARCHY, "BONE_HIERARCHY", 0, "Bone Hierarchy", ""}, + {0, NULL, 0, NULL, NULL} + }; + ot->name = "Sort Vertex Groups"; ot->idname = "OBJECT_OT_vertex_group_sort"; - ot->description = "Sort vertex groups alphabetically"; + ot->description = "Sort vertex groups"; /* api callbacks */ ot->poll = vertex_group_poll; @@ -4369,6 +4416,8 @@ void OBJECT_OT_vertex_group_sort(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "sort_type", vgroup_sort_type, SORT_TYPE_NAME, "Sort type", "Sort type"); } static int vgroup_move_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/physics/CMakeLists.txt b/source/blender/editors/physics/CMakeLists.txt index 892d71befb4..40d555226f3 100644 --- a/source/blender/editors/physics/CMakeLists.txt +++ b/source/blender/editors/physics/CMakeLists.txt @@ -23,11 +23,13 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/elbeem/extern ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -64,4 +66,6 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_physics "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/physics/SConscript b/source/blender/editors/physics/SConscript index 9436280de43..983d1c4b4ba 100644 --- a/source/blender/editors/physics/SConscript +++ b/source/blender/editors/physics/SConscript @@ -31,20 +31,22 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/rigidbody', - '#/extern/glew/include', '#/intern/elbeem/extern', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs += ' ' + env['BF_PTHREADS_INC'] diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 3c6bdf86a50..9a3433b0ccf 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -319,7 +319,7 @@ void PE_hide_keys_time(Scene *scene, PTCacheEdit *edit, float cfra) if (pset->flag & PE_FADE_TIME && pset->selectmode==SCE_SELECT_POINT) { LOOP_POINTS { LOOP_KEYS { - if (fabs(cfra-*key->time) < pset->fade_frames) + if (fabsf(cfra - *key->time) < pset->fade_frames) key->flag &= ~PEK_HIDE; else { key->flag |= PEK_HIDE; @@ -463,7 +463,7 @@ static bool key_inside_circle(PEData *data, float rad, const float co[3], float dx= data->mval[0] - screen_co[0]; dy= data->mval[1] - screen_co[1]; - dist= sqrt(dx*dx + dy*dy); + dist = sqrtf(dx * dx + dy * dy); if (dist > rad) return 0; @@ -616,7 +616,10 @@ static void foreach_mouse_hit_key(PEData *data, ForKeyMatFunc func, int selected ParticleSystemModifierData *psmd = NULL; ParticleEditSettings *pset= PE_settings(data->scene); POINT_P; KEY_K; - float mat[4][4] = MAT4_UNITY, imat[4][4] = MAT4_UNITY; + float mat[4][4], imat[4][4]; + + unit_m4(mat); + unit_m4(imat); if (edit->psys) psmd= psys_get_modifier(data->ob, edit->psys); @@ -1585,6 +1588,87 @@ void PARTICLE_OT_select_tips(wmOperatorType *ot) WM_operator_properties_select_action(ot, SEL_SELECT); } +/*********************** select random operator ************************/ + +enum { RAN_HAIR, RAN_POINTS }; + +static EnumPropertyItem select_random_type_items[] = { + {RAN_HAIR, "HAIR", 0, "Hair", ""}, + {RAN_POINTS, "POINTS", 0, "Points", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static int select_random_exec(bContext *C, wmOperator *op) +{ + PEData data; + int type; + Scene *scene; + Object *ob; + + /* used by LOOP_VISIBLE_POINTS, LOOP_VISIBLE_KEYS and LOOP_KEYS */ + PTCacheEdit *edit; + PTCacheEditPoint *point; + PTCacheEditKey *key; + int p; + int k; + + const float randf = RNA_float_get (op->ptr, "percent") / 100.0f; + + type = RNA_enum_get(op->ptr, "type"); + + PE_set_data(C, &data); + data.select_action = SEL_SELECT; + scene = CTX_data_scene(C); + ob = CTX_data_active_object(C); + edit = PE_get_current(scene, ob); + + switch (type) { + case RAN_HAIR: + LOOP_VISIBLE_POINTS { + int flag = (BLI_frand() < randf) ? SEL_SELECT : SEL_DESELECT; + LOOP_KEYS { + select_action_apply (point, key, flag); + } + } + break; + case RAN_POINTS: + LOOP_VISIBLE_POINTS { + LOOP_VISIBLE_KEYS { + int flag = (BLI_frand() < randf) ? SEL_SELECT : SEL_DESELECT; + select_action_apply (point, key, flag); + } + } + break; + } + + PE_update_selection(data.scene, data.ob, 1); + WM_event_add_notifier(C, NC_OBJECT|ND_PARTICLE|NA_SELECTED, data.ob); + + return OPERATOR_FINISHED; +} + +void PARTICLE_OT_select_random(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Random"; + ot->idname = "PARTICLE_OT_select_random"; + ot->description = "Select a randomly distributed set of hair or points"; + + /* api callbacks */ + ot->exec = select_random_exec; + ot->poll = PE_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; + + /* properties */ + RNA_def_float_percentage (ot->srna, "percent", 50.0f, 0.0f, 100.0f, "Percent", + "Percentage (mean) of elements in randomly selected set", + 0.0f, 100.0f); + ot->prop = RNA_def_enum (ot->srna, "type", select_random_type_items, RAN_HAIR, + "Type", "Select either hair or points"); +} + /************************ select linked operator ************************/ static int select_linked_exec(bContext *C, wmOperator *op) @@ -1710,11 +1794,13 @@ int PE_lasso_select(bContext *C, const int mcords[][2], const short moves, bool ParticleSystem *psys = edit->psys; ParticleSystemModifierData *psmd = psys_get_modifier(ob, psys); POINT_P; KEY_K; - float co[3], mat[4][4] = MAT4_UNITY; + float co[3], mat[4][4]; int screen_co[2]; PEData data; + unit_m4(mat); + if (!PE_start_edit(edit)) return OPERATOR_CANCELLED; @@ -2927,7 +3013,7 @@ static void brush_cut(PEData *data, int pa_index) d= dv * rad2 - d*d; if (d > 0.0f) { - d= sqrt(d); + d= sqrtf(d); cut_time= -(v0*xo0 + v1*xo1 + d); @@ -3482,7 +3568,7 @@ static int brush_add(PEData *data, short number) } pa->size= 1.0f; - initialize_particle(pa); + initialize_particle(&sim, pa); reset_particle(&sim, pa, 0.0, 1.0); point->flag |= PEP_EDIT_RECALC; if (pe_x_mirror(ob)) @@ -3673,7 +3759,7 @@ static void brush_edit_apply(bContext *C, wmOperator *op, PointerRNA *itemptr) pset->flag &= ~PE_LOCK_FIRST; if (((pset->brushtype == PE_BRUSH_ADD) ? - (sqrt(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0)) || bedit->first) + (sqrtf(dx * dx + dy * dy) > pset->brush[PE_BRUSH_ADD].step) : (dx != 0 || dy != 0)) || bedit->first) { PEData data= bedit->data; diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 25bc2aeed07..5a61f77e5a8 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -633,7 +633,8 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) HairKey *key; BVHTreeFromMesh bvhtree= {NULL}; BVHTreeNearest nearest; - MFace *mface, *mf; + MFace *mface = NULL, *mf; + MEdge *medge = NULL, *me; MVert *mvert; DerivedMesh *dm = NULL; int numverts; @@ -663,13 +664,23 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) numverts = dm->getNumVerts(dm); mvert = dm->getVertArray(dm); - mface = dm->getTessFaceArray(dm); /* convert to global coordinates */ for (i=0; i<numverts; i++) mul_m4_v3(ob->obmat, mvert[i].co); - bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6); + if (dm->getNumTessFaces(dm) != 0) { + mface = dm->getTessFaceArray(dm); + bvhtree_from_mesh_faces(&bvhtree, dm, 0.0, 2, 6); + } + else if (dm->getNumEdges(dm) != 0) { + medge = dm->getEdgeArray(dm); + bvhtree_from_mesh_edges(&bvhtree, dm, 0.0, 2, 6); + } + else { + dm->release(dm); + return false; + } for (i=0, pa= psys->particles; i<psys->totpart; i++, pa++) { key = pa->hair; @@ -685,21 +696,35 @@ static bool connect_hair(Scene *scene, Object *ob, ParticleSystem *psys) continue; } - mf = &mface[nearest.index]; + if (mface) { + mf = &mface[nearest.index]; + + copy_v3_v3(v[0], mvert[mf->v1].co); + copy_v3_v3(v[1], mvert[mf->v2].co); + copy_v3_v3(v[2], mvert[mf->v3].co); + if (mf->v4) { + copy_v3_v3(v[3], mvert[mf->v4].co); + interp_weights_poly_v3(pa->fuv, v, 4, nearest.co); + } + else + interp_weights_poly_v3(pa->fuv, v, 3, nearest.co); + + pa->num = nearest.index; + pa->num_dmcache = psys_particle_dm_face_lookup(ob, psmd->dm, pa->num, pa->fuv, NULL); + } + else { + me = &medge[nearest.index]; + + pa->fuv[1] = line_point_factor_v3(nearest.co, + mvert[me->v2].co, + mvert[me->v2].co); + pa->fuv[0] = 1.0f - pa->fuv[1]; + pa->fuv[2] = pa->fuv[3] = 0.0f; - copy_v3_v3(v[0], mvert[mf->v1].co); - copy_v3_v3(v[1], mvert[mf->v2].co); - copy_v3_v3(v[2], mvert[mf->v3].co); - if (mf->v4) { - copy_v3_v3(v[3], mvert[mf->v4].co); - interp_weights_poly_v3(pa->fuv, v, 4, nearest.co); + pa->num = nearest.index; + pa->num_dmcache = -1; } - else - interp_weights_poly_v3(pa->fuv, v, 3, nearest.co); - pa->num = nearest.index; - pa->num_dmcache = psys_particle_dm_face_lookup(ob, psmd->dm, pa->num, pa->fuv, NULL); - psys_mat_hair_to_global(ob, psmd->dm, psys->part->from, pa, hairmat); invert_m4_m4(imat, hairmat); diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c index 7c49319139b..fb224da6ec4 100644 --- a/source/blender/editors/physics/physics_fluid.c +++ b/source/blender/editors/physics/physics_fluid.c @@ -1067,8 +1067,8 @@ static int fluidsimBake(bContext *C, ReportList *reports, Object *fsDomain, shor WM_jobs_start(CTX_wm_manager(C), wm_job); } else { - short dummy_stop, dummy_do_update; - float dummy_progress; + short dummy_stop = 0, dummy_do_update = 0; + float dummy_progress = 0.0f; /* blocking, use with exec() */ fluidbake_startjob((void *)fb, &dummy_stop, &dummy_do_update, &dummy_progress); @@ -1259,4 +1259,4 @@ void MANTA_OT_stop_sim(wmOperatorType *ot) ot->invoke = manta_stop_sim_invoke; ot->exec = manta_stop_sim_exec; ot->poll = ED_operator_object_active_editable; -}
\ No newline at end of file +} diff --git a/source/blender/editors/physics/physics_intern.h b/source/blender/editors/physics/physics_intern.h index 2826782c762..7668a850951 100644 --- a/source/blender/editors/physics/physics_intern.h +++ b/source/blender/editors/physics/physics_intern.h @@ -39,6 +39,7 @@ struct wmOperatorType; void PARTICLE_OT_select_all(struct wmOperatorType *ot); void PARTICLE_OT_select_roots(struct wmOperatorType *ot); void PARTICLE_OT_select_tips(struct wmOperatorType *ot); +void PARTICLE_OT_select_random(struct wmOperatorType *ot); void PARTICLE_OT_select_linked(struct wmOperatorType *ot); void PARTICLE_OT_select_less(struct wmOperatorType *ot); void PARTICLE_OT_select_more(struct wmOperatorType *ot); diff --git a/source/blender/editors/physics/physics_ops.c b/source/blender/editors/physics/physics_ops.c index cb4bd5e4dff..1038e8b64e3 100644 --- a/source/blender/editors/physics/physics_ops.c +++ b/source/blender/editors/physics/physics_ops.c @@ -51,6 +51,7 @@ static void operatortypes_particle(void) WM_operatortype_append(PARTICLE_OT_select_all); WM_operatortype_append(PARTICLE_OT_select_roots); WM_operatortype_append(PARTICLE_OT_select_tips); + WM_operatortype_append(PARTICLE_OT_select_random); WM_operatortype_append(PARTICLE_OT_select_linked); WM_operatortype_append(PARTICLE_OT_select_less); WM_operatortype_append(PARTICLE_OT_select_more); diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index 7ba37bbb76b..13a3d7a523f 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -100,10 +100,6 @@ bool ED_rigidbody_object_add(Scene *scene, Object *ob, int type, ReportList *rep BKE_report(reports, RPT_ERROR, "Can't add Rigid Body to non mesh object"); return false; } - if (((Mesh *)ob->data)->totpoly == 0) { - BKE_report(reports, RPT_ERROR, "Can't create Rigid Body from mesh with no polygons"); - return false; - } /* Add rigid body world and group if they don't exist for convenience */ if (rbw == NULL) { @@ -489,78 +485,6 @@ static EnumPropertyItem *rigidbody_materials_itemf(bContext *UNUSED(C), PointerR /* ------------------------------------------ */ -/* helper function to calculate volume of rigidbody object */ -// TODO: allow a parameter to specify method used to calculate this? -static float rigidbody_object_calc_volume(Object *ob) -{ - RigidBodyOb *rbo = ob->rigidbody_object; - - float size[3] = {1.0f, 1.0f, 1.0f}; - float radius = 1.0f; - float height = 1.0f; - - float volume = 0.0f; - - /* if automatically determining dimensions, use the Object's boundbox - * - assume that all quadrics are standing upright on local z-axis - * - assume even distribution of mass around the Object's pivot - * (i.e. Object pivot is centralised in boundbox) - * - boundbox gives full width - */ - // XXX: all dimensions are auto-determined now... later can add stored settings for this - BKE_object_dimensions_get(ob, size); - - if (ELEM3(rbo->shape, RB_SHAPE_CAPSULE, RB_SHAPE_CYLINDER, RB_SHAPE_CONE)) { - /* take radius as largest x/y dimension, and height as z-dimension */ - radius = MAX2(size[0], size[1]) * 0.5f; - height = size[2]; - } - else if (rbo->shape == RB_SHAPE_SPHERE) { - /* take radius to the the largest dimension to try and encompass everything */ - radius = max_fff(size[0], size[1], size[2]) * 0.5f; - } - - /* calculate volume as appropriate */ - switch (rbo->shape) { - case RB_SHAPE_BOX: - volume = size[0] * size[1] * size[2]; - break; - - case RB_SHAPE_SPHERE: - volume = 4.0f / 3.0f * (float)M_PI * radius * radius * radius; - break; - - /* for now, assume that capsule is close enough to a cylinder... */ - case RB_SHAPE_CAPSULE: - case RB_SHAPE_CYLINDER: - volume = (float)M_PI * radius * radius * height; - break; - - case RB_SHAPE_CONE: - volume = (float)M_PI / 3.0f * radius * radius * height; - break; - - /* for now, all mesh shapes are just treated as boxes... - * NOTE: this may overestimate the volume, but other methods are overkill - */ - case RB_SHAPE_CONVEXH: - case RB_SHAPE_TRIMESH: - volume = size[0] * size[1] * size[2]; - break; - -#if 0 // XXX: not defined yet - case RB_SHAPE_COMPOUND: - volume = 0.0f; - break; -#endif - } - - /* return the volume calculated */ - return volume; -} - -/* ------------------------------------------ */ - static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op) { int material = RNA_enum_get(op->ptr, "material"); @@ -593,7 +517,7 @@ static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op) /* mass is calculated from the approximate volume of the object, * and the density of the material we're simulating */ - volume = rigidbody_object_calc_volume(ob); + BKE_rigidbody_calc_volume(ob, &volume); mass = volume * density; /* use RNA-system to change the property and perform all necessary changes */ diff --git a/source/blender/editors/render/CMakeLists.txt b/source/blender/editors/render/CMakeLists.txt index 24015bd4ea3..42dafc076ed 100644 --- a/source/blender/editors/render/CMakeLists.txt +++ b/source/blender/editors/render/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -64,6 +65,8 @@ if(WITH_HEADLESS) add_definitions(-DWITH_HEADLESS) endif() +add_definitions(${GL_DEFINITIONS}) + if(WITH_FREESTYLE) list(APPEND INC ../../freestyle diff --git a/source/blender/editors/render/SConscript b/source/blender/editors/render/SConscript index 41576f9b485..cbb7988695b 100644 --- a/source/blender/editors/render/SConscript +++ b/source/blender/editors/render/SConscript @@ -31,7 +31,8 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/elbeem/extern', '../include', '../../blenfont', @@ -48,7 +49,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs += ' ' + env['BF_PTHREADS_INC'] diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index 8f8cc542821..f9377d576bf 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -72,6 +72,7 @@ void SCENE_OT_freestyle_geometry_modifier_add(struct wmOperatorType *ot); void SCENE_OT_freestyle_modifier_remove(struct wmOperatorType *ot); void SCENE_OT_freestyle_modifier_move(struct wmOperatorType *ot); void SCENE_OT_freestyle_modifier_copy(struct wmOperatorType *ot); +void SCENE_OT_freestyle_stroke_material_create(struct wmOperatorType *ot); #endif diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 9fa312884da..baf25a49f7c 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -35,6 +35,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_threads.h" #include "BLI_utildefines.h" #include "PIL_time.h" @@ -316,10 +317,12 @@ static int screen_render_exec(bContext *C, wmOperator *op) RE_SetReports(re, op->reports); + BLI_begin_threaded_malloc(); if (is_animation) RE_BlenderAnim(re, mainp, scene, camera_override, lay_override, scene->r.sfra, scene->r.efra, scene->r.frame_step); else RE_BlenderFrame(re, mainp, scene, srl, camera_override, lay_override, scene->r.cfra, is_write_still); + BLI_end_threaded_malloc(); RE_SetReports(re, NULL); @@ -1026,6 +1029,10 @@ typedef struct RenderPreview { RenderEngine *engine; float viewmat[4][4]; + + int start_resolution_divider; + int resolution_divider; + bool has_freestyle; } RenderPreview; static int render_view3d_disprect(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, rcti *disprect) @@ -1121,19 +1128,52 @@ static void render_view3d_renderinfo_cb(void *rjp, RenderStats *rs) } } +BLI_INLINE void rcti_scale_coords(rcti *scaled_rect, const rcti *rect, + const float scale) +{ + scaled_rect->xmin = rect->xmin * scale; + scaled_rect->ymin = rect->ymin * scale; + scaled_rect->xmax = rect->xmax * scale; + scaled_rect->ymax = rect->ymax * scale; +} + +static void render_update_resolution(Render *re, const RenderPreview *rp, + bool use_border, const rcti *clip_rect) +{ + int winx = rp->ar->winx / rp->resolution_divider, + winy = rp->ar->winy / rp->resolution_divider; + if (use_border) { + rcti scaled_cliprct; + rcti_scale_coords(&scaled_cliprct, clip_rect, + 1.0f / rp->resolution_divider); + RE_ChangeResolution(re, winx, winy, &scaled_cliprct); + } + else { + RE_ChangeResolution(re, winx, winy, NULL); + } + + if (rp->has_freestyle) { + if (rp->resolution_divider == 1) { + RE_ChangeModeFlag(re, R_EDGE_FRS, false); + } + else { + RE_ChangeModeFlag(re, R_EDGE_FRS, true); + } + } +} static void render_view3d_startjob(void *customdata, short *stop, short *do_update, float *UNUSED(progress)) { RenderPreview *rp = customdata; Render *re; RenderStats *rstats; - RenderData rdata; rctf viewplane; rcti cliprct; float clipsta, clipend, pixsize; bool orth, restore = 0; char name[32]; int update_flag; + bool use_border; update_flag = rp->engine->job_update_flag; rp->engine->job_update_flag = 0; @@ -1161,18 +1201,37 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda rstats = RE_GetStats(re); + if (update_flag & PR_UPDATE_VIEW) { + Object *object; + rp->resolution_divider = rp->start_resolution_divider; + + /* Same as database_init_objects(), loop over all objects. + * We might consider de-duplicating the code between this two cases. + */ + for (object = rp->bmain->object.first; object; object = object->id.next) { + float mat[4][4]; + mul_m4_m4m4(mat, rp->viewmat, object->obmat); + invert_m4_m4(object->imat_ren, mat); + } + } + + use_border = render_view3d_disprect(rp->scene, rp->ar, rp->v3d, + rp->rv3d, &cliprct); + if ((update_flag & (PR_UPDATE_RENDERSIZE | PR_UPDATE_DATABASE)) || rstats->convertdone == 0) { + RenderData rdata; + /* no osa, blur, seq, layers, etc for preview render */ rdata = rp->scene->r; rdata.mode &= ~(R_OSA | R_MBLUR | R_BORDER | R_PANORAMA); rdata.scemode &= ~(R_DOSEQ | R_DOCOMP | R_FREE_IMAGE); rdata.scemode |= R_VIEWPORT_PREVIEW; - + /* we do use layers, but only active */ rdata.scemode |= R_SINGLE_LAYER; /* initalize always */ - if (render_view3d_disprect(rp->scene, rp->ar, rp->v3d, rp->rv3d, &cliprct)) { + if (use_border) { rdata.mode |= R_BORDER; RE_InitState(re, NULL, &rdata, NULL, rp->ar->winx, rp->ar->winy, &cliprct); } @@ -1225,8 +1284,34 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda /* OK, can we enter render code? */ if (rstats->convertdone) { - RE_TileProcessor(re); - + bool first_time = true; + for (;;) { + if (first_time == false) { + if (restore) + RE_DataBase_IncrementalView(re, rp->viewmat, 1); + + rp->resolution_divider /= 2; + *do_update = 1; + + render_update_resolution(re, rp, use_border, &cliprct); + + RE_DataBase_IncrementalView(re, rp->viewmat, 0); + RE_DataBase_ApplyWindow(re); + restore = 1; + } + else { + render_update_resolution(re, rp, use_border, &cliprct); + } + + RE_TileProcessor(re); + + first_time = false; + + if (*stop || rp->resolution_divider == 1) { + break; + } + } + /* always rotate back */ if (restore) RE_DataBase_IncrementalView(re, rp->viewmat, 1); @@ -1322,7 +1407,12 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C) wmJob *wm_job; RenderPreview *rp; Scene *scene = CTX_data_scene(C); - + ARegion *ar = CTX_wm_region(C); + int width = ar->winx, height = ar->winy; + int divider = 1; + int resolution_threshold = scene->r.preview_start_resolution * + scene->r.preview_start_resolution; + if (CTX_wm_window(C) == NULL) return; if (!render_view3d_flag_changed(engine, C)) @@ -1333,6 +1423,12 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C) rp = MEM_callocN(sizeof(RenderPreview), "render preview"); rp->job = wm_job; + while (width * height > resolution_threshold) { + width = max_ii(1, width / 2); + height = max_ii(1, height / 2); + divider *= 2; + } + /* customdata for preview thread */ rp->scene = scene; rp->engine = engine; @@ -1341,6 +1437,9 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C) rp->v3d = rp->sa->spacedata.first; rp->rv3d = CTX_wm_region_view3d(C); rp->bmain = CTX_data_main(C); + rp->resolution_divider = divider; + rp->start_resolution_divider = divider; + rp->has_freestyle = scene->r.mode & R_EDGE_FRS; copy_m4_m4(rp->viewmat, rp->rv3d->viewmat); /* clear info text */ @@ -1385,24 +1484,42 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) RE_AcquireResultImage(re, &rres); if (rres.rectf) { + RegionView3D *rv3d = CTX_wm_region_view3d(C); + View3D *v3d = CTX_wm_view3d(C); Scene *scene = CTX_data_scene(C); + ARegion *ar = CTX_wm_region(C); bool force_fallback = false; bool need_fallback = true; float dither = scene->r.dither_intensity; - - /* Dithering is not supported on GLSL yet */ - force_fallback |= dither != 0.0f; + float scale_x, scale_y; + rcti clip_rect; + int xof, yof; + + if (render_view3d_disprect(scene, ar, v3d, rv3d, &clip_rect)) { + scale_x = (float) BLI_rcti_size_x(&clip_rect) / rres.rectx; + scale_y = (float) BLI_rcti_size_y(&clip_rect) / rres.recty; + xof = clip_rect.xmin; + yof = clip_rect.ymin; + } + else { + scale_x = (float) ar->winx / rres.rectx; + scale_y = (float) ar->winy / rres.recty; + xof = rres.xof; + yof = rres.yof; + } /* If user decided not to use GLSL, fallback to glaDrawPixelsAuto */ force_fallback |= (U.image_draw_method != IMAGE_DRAW_METHOD_GLSL); /* Try using GLSL display transform. */ if (force_fallback == false) { - if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, 0.0f, true)) { + if (IMB_colormanagement_setup_glsl_draw(&scene->view_settings, &scene->display_settings, dither, true)) { glEnable(GL_BLEND); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glaDrawPixelsTex(rres.xof, rres.yof, rres.rectx, rres.recty, GL_RGBA, GL_FLOAT, - GL_NEAREST, rres.rectf); + glPixelZoom(scale_x, scale_y); + glaDrawPixelsTex(xof, yof, rres.rectx, rres.recty, + GL_RGBA, GL_FLOAT, GL_NEAREST, rres.rectf); + glPixelZoom(1.0f, 1.0f); glDisable(GL_BLEND); IMB_colormanagement_finish_glsl_draw(); @@ -1420,8 +1537,11 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) glEnable(GL_BLEND); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - glaDrawPixelsAuto(rres.xof, rres.yof, rres.rectx, rres.recty, GL_RGBA, GL_UNSIGNED_BYTE, + glPixelZoom(scale_x, scale_y); + glaDrawPixelsAuto(xof, yof, rres.rectx, rres.recty, + GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, display_buffer); + glPixelZoom(1.0f, 1.0f); glDisable(GL_BLEND); MEM_freeN(display_buffer); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index c1f8dd89f71..559c86bf48c 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -31,11 +31,10 @@ #include <string.h> #include <stddef.h> -#include <GL/glew.h> - #include "MEM_guardedalloc.h" #include "BLI_math.h" +#include "BLI_math_color_blend.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_jitter.h" @@ -57,6 +56,7 @@ #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "RE_pipeline.h" #include "IMB_imbuf_types.h" @@ -67,6 +67,7 @@ #include "RNA_define.h" #include "GPU_extensions.h" +#include "GPU_glew.h" #include "wm_window.h" @@ -139,7 +140,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender) if (oglrender->is_sequencer) { SeqRenderData context; - int chanshown = oglrender->sseq ? oglrender->sseq->chanshown : 0; + SpaceSeq *sseq = oglrender->sseq; + int chanshown = sseq ? sseq->chanshown : 0; + struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_SHOW_GPENCIL)) ? sseq->gpd : NULL; context = BKE_sequencer_new_render_data(oglrender->bmain->eval_ctx, oglrender->bmain, scene, oglrender->sizex, oglrender->sizey, 100.0f); @@ -171,6 +174,33 @@ static void screen_opengl_render_apply(OGLRender *oglrender) IMB_freeImBuf(linear_ibuf); } + + if (gpd) { + int i; + unsigned char *gp_rect; + + GPU_offscreen_bind(oglrender->ofs); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + wmOrtho2(0, sizex, 0, sizey); + glTranslatef(sizex / 2, sizey / 2, 0.0f); + + ED_gpencil_draw_ex(gpd, sizex, sizey, scene->r.cfra); + + gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect"); + GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect); + + for (i = 0; i < sizex * sizey * 4; i += 4) { + float col_src[4]; + rgba_uchar_to_float(col_src, &gp_rect[i]); + blend_color_mix_float(&rr->rectf[i], &rr->rectf[i], col_src); + } + GPU_offscreen_unbind(oglrender->ofs); + + MEM_freeN(gp_rect); + } } else if (view_context) { ED_view3d_draw_offscreen_init(scene, v3d); @@ -340,20 +370,24 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) return false; } - /* ensure we have a 3d view */ + /* only one render job at a time */ + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) + return false; - if (!ED_view3d_context_activate(C)) { - RNA_boolean_set(op->ptr, "view_context", false); + if (is_sequencer) { is_view_context = false; } + else { + /* ensure we have a 3d view */ + if (!ED_view3d_context_activate(C)) { + RNA_boolean_set(op->ptr, "view_context", false); + is_view_context = false; + } - /* only one render job at a time */ - if (WM_jobs_test(wm, scene, WM_JOB_TYPE_RENDER)) - return false; - - if (!is_view_context && scene->camera == NULL) { - BKE_report(op->reports, RPT_ERROR, "Scene has no camera"); - return false; + if (!is_view_context && scene->camera == NULL) { + BKE_report(op->reports, RPT_ERROR, "Scene has no camera"); + return false; + } } if (!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) { diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c index 3401577ee55..0d334082a2b 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -75,6 +75,7 @@ void ED_operatortypes_render(void) WM_operatortype_append(SCENE_OT_freestyle_modifier_remove); WM_operatortype_append(SCENE_OT_freestyle_modifier_move); WM_operatortype_append(SCENE_OT_freestyle_modifier_copy); + WM_operatortype_append(SCENE_OT_freestyle_stroke_material_create); #endif WM_operatortype_append(TEXTURE_OT_slot_copy); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 4c34c4edba9..450a3b19889 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -88,6 +88,7 @@ #include "PIL_time.h" #include "RE_pipeline.h" +#include "RE_engine.h" #include "WM_api.h" #include "WM_types.h" @@ -213,6 +214,12 @@ void ED_preview_init_dbase(void) #endif } +static bool check_engine_supports_textures(Scene *scene) +{ + RenderEngineType *type = RE_engines_find(scene->r.engine); + return type->flag & RE_USE_TEXTURE_PREVIEW; +} + void ED_preview_free_dbase(void) { if (G_pr_main) @@ -299,10 +306,10 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre sce->r.cfra = scene->r.cfra; - if (id_type == ID_TE && sp->pr_method == PR_ICON_RENDER) { - /* force blender internal for texture icons render, + if (id_type == ID_TE && !check_engine_supports_textures(scene)) { + /* Force blender internal for texture icons and nodes render, * seems commonly used render engines does not support - * such kind of rendering + * such kind of rendering. */ BLI_strncpy(sce->r.engine, "BLENDER_RENDER", sizeof(sce->r.engine)); } @@ -751,12 +758,6 @@ static void shader_preview_render(ShaderPreview *sp, ID *id, int split, int firs if (sp->pr_rect) RE_ResultGet32(re, sp->pr_rect); } - else { - /* validate owner */ - //if (ri->rect == NULL) - // ri->rect= MEM_mallocN(sizeof(int) * ri->pr_rectx*ri->pr_recty, "BIF_previewrender"); - //RE_ResultGet32(re, ri->rect); - } /* unassign the pointers, reset vars */ preview_prepare_scene(sp->scene, NULL, GS(id->name), sp); @@ -1137,10 +1138,13 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M wmJob *wm_job; ShaderPreview *sp; Scene *scene = CTX_data_scene(C); + short id_type = GS(id->name); + bool use_new_shading = BKE_scene_use_new_shading_nodes(scene); - /* node previews not supported for cycles */ - if (BKE_scene_use_new_shading_nodes(scene) && method == PR_NODE_RENDER) + /* Only texture node preview is supported with Cycles. */ + if (use_new_shading && method == PR_NODE_RENDER && id_type != ID_TE) { return; + } wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), owner, "Shader Preview", WM_JOB_EXCL_RENDER, WM_JOB_TYPE_RENDER_PREVIEW); @@ -1158,10 +1162,12 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M /* hardcoded preview .blend for cycles/internal, this should be solved * once with custom preview .blend path for external engines */ - if (BKE_scene_use_new_shading_nodes(scene)) + if ((method != PR_NODE_RENDER) && id_type != ID_TE && use_new_shading) { sp->pr_main = G_pr_main_cycles; - else + } + else { sp->pr_main = G_pr_main; + } if (ob && ob->totcol) copy_v4_v4(sp->col, ob->col); else sp->col[0] = sp->col[1] = sp->col[2] = sp->col[3] = 1.0f; diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 97f6b346666..6c996bcbd67 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -58,6 +58,7 @@ #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_material.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_texture.h" @@ -102,8 +103,15 @@ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op)) if (!ob) return OPERATOR_CANCELLED; - + object_add_material_slot(ob); + + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + Scene *scene = CTX_data_scene(C); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob); @@ -138,8 +146,15 @@ static int material_slot_remove_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Unable to remove material slot in edit mode"); return OPERATOR_CANCELLED; } - + object_remove_material_slot(ob); + + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + Scene *scene = CTX_data_scene(C); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); @@ -449,6 +464,14 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) * pointer se also increases user, so this compensates it */ tex->id.us--; + if (ptr.id.data && GS(((ID *)ptr.id.data)->name) == ID_MA && + RNA_property_pointer_get(&ptr, prop).id.data == NULL) + { + /* In case we are assigning new texture to a material, and active slot was empty, reset 'use' flag. */ + Material *ma = (Material *)ptr.id.data; + ma->septex &= ~(1 << ma->texact); + } + RNA_id_pointer_create(&tex->id, &idptr); RNA_property_pointer_set(&ptr, prop, idptr); RNA_property_update(C, &ptr, prop); @@ -590,7 +613,7 @@ void SCENE_OT_render_layer_remove(wmOperatorType *ot) static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) { if (!lineset) { - BKE_report(reports, RPT_ERROR, "No active lineset and associated line style to add the modifier to"); + BKE_report(reports, RPT_ERROR, "No active lineset and associated line style to manipulate the modifier"); return false; } if (!lineset->linestyle) { @@ -644,6 +667,7 @@ static int freestyle_module_remove_exec(bContext *C, wmOperator *UNUSED(op)) BKE_freestyle_module_delete(&srl->freestyleConfig, module); + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -678,6 +702,7 @@ static int freestyle_module_move_exec(bContext *C, wmOperator *op) else { BKE_freestyle_module_move_down(&srl->freestyleConfig, module); } + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -714,6 +739,7 @@ static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op)) BKE_freestyle_lineset_add(&srl->freestyleConfig, NULL); + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -752,8 +778,6 @@ static int freestyle_lineset_copy_exec(bContext *C, wmOperator *UNUSED(op)) FRS_copy_active_lineset(&srl->freestyleConfig); - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); - return OPERATOR_FINISHED; } @@ -779,6 +803,7 @@ static int freestyle_lineset_paste_exec(bContext *C, wmOperator *UNUSED(op)) FRS_paste_active_lineset(&srl->freestyleConfig); + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -806,6 +831,7 @@ static int freestyle_lineset_remove_exec(bContext *C, wmOperator *UNUSED(op)) FRS_delete_active_lineset(&srl->freestyleConfig); + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -838,6 +864,7 @@ static int freestyle_lineset_move_exec(bContext *C, wmOperator *op) else { FRS_move_active_lineset_down(&srl->freestyleConfig); } + DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); return OPERATOR_FINISHED; @@ -879,13 +906,13 @@ static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op) } if (lineset->linestyle) { lineset->linestyle->id.us--; - lineset->linestyle = BKE_copy_linestyle(lineset->linestyle); + lineset->linestyle = BKE_linestyle_copy(lineset->linestyle); } else { - lineset->linestyle = BKE_new_linestyle("LineStyle", NULL); + lineset->linestyle = BKE_linestyle_new("LineStyle", NULL); } - - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -916,11 +943,12 @@ static int freestyle_color_modifier_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (BKE_add_linestyle_color_modifier(lineset->linestyle, type) == NULL) { + if (BKE_linestyle_color_modifier_add(lineset->linestyle, NULL, type) == NULL) { BKE_report(op->reports, RPT_ERROR, "Unknown line color modifier type"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -955,11 +983,12 @@ static int freestyle_alpha_modifier_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (BKE_add_linestyle_alpha_modifier(lineset->linestyle, type) == NULL) { + if (BKE_linestyle_alpha_modifier_add(lineset->linestyle, NULL, type) == NULL) { BKE_report(op->reports, RPT_ERROR, "Unknown alpha transparency modifier type"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -994,11 +1023,12 @@ static int freestyle_thickness_modifier_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (BKE_add_linestyle_thickness_modifier(lineset->linestyle, type) == NULL) { + if (BKE_linestyle_thickness_modifier_add(lineset->linestyle, NULL, type) == NULL) { BKE_report(op->reports, RPT_ERROR, "Unknown line thickness modifier type"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -1033,11 +1063,12 @@ static int freestyle_geometry_modifier_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - if (BKE_add_linestyle_geometry_modifier(lineset->linestyle, type) == NULL) { + if (BKE_linestyle_geometry_modifier_add(lineset->linestyle, NULL, type) == NULL) { BKE_report(op->reports, RPT_ERROR, "Unknown stroke geometry modifier type"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -1088,22 +1119,23 @@ static int freestyle_modifier_remove_exec(bContext *C, wmOperator *op) switch (freestyle_get_modifier_type(&ptr)) { case LS_MODIFIER_TYPE_COLOR: - BKE_remove_linestyle_color_modifier(lineset->linestyle, modifier); + BKE_linestyle_color_modifier_remove(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_ALPHA: - BKE_remove_linestyle_alpha_modifier(lineset->linestyle, modifier); + BKE_linestyle_alpha_modifier_remove(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_THICKNESS: - BKE_remove_linestyle_thickness_modifier(lineset->linestyle, modifier); + BKE_linestyle_thickness_modifier_remove(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_GEOMETRY: - BKE_remove_linestyle_geometry_modifier(lineset->linestyle, modifier); + BKE_linestyle_geometry_modifier_remove(lineset->linestyle, modifier); break; default: BKE_report(op->reports, RPT_ERROR, "The object the data pointer refers to is not a valid modifier"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -1137,22 +1169,23 @@ static int freestyle_modifier_copy_exec(bContext *C, wmOperator *op) switch (freestyle_get_modifier_type(&ptr)) { case LS_MODIFIER_TYPE_COLOR: - BKE_copy_linestyle_color_modifier(lineset->linestyle, modifier); + BKE_linestyle_color_modifier_copy(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_ALPHA: - BKE_copy_linestyle_alpha_modifier(lineset->linestyle, modifier); + BKE_linestyle_alpha_modifier_copy(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_THICKNESS: - BKE_copy_linestyle_thickness_modifier(lineset->linestyle, modifier); + BKE_linestyle_thickness_modifier_copy(lineset->linestyle, modifier); break; case LS_MODIFIER_TYPE_GEOMETRY: - BKE_copy_linestyle_geometry_modifier(lineset->linestyle, modifier); + BKE_linestyle_geometry_modifier_copy(lineset->linestyle, modifier); break; default: BKE_report(op->reports, RPT_ERROR, "The object the data pointer refers to is not a valid modifier"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -1187,22 +1220,23 @@ static int freestyle_modifier_move_exec(bContext *C, wmOperator *op) switch (freestyle_get_modifier_type(&ptr)) { case LS_MODIFIER_TYPE_COLOR: - BKE_move_linestyle_color_modifier(lineset->linestyle, modifier, dir); + BKE_linestyle_color_modifier_move(lineset->linestyle, modifier, dir); break; case LS_MODIFIER_TYPE_ALPHA: - BKE_move_linestyle_alpha_modifier(lineset->linestyle, modifier, dir); + BKE_linestyle_alpha_modifier_move(lineset->linestyle, modifier, dir); break; case LS_MODIFIER_TYPE_THICKNESS: - BKE_move_linestyle_thickness_modifier(lineset->linestyle, modifier, dir); + BKE_linestyle_thickness_modifier_move(lineset->linestyle, modifier, dir); break; case LS_MODIFIER_TYPE_GEOMETRY: - BKE_move_linestyle_geometry_modifier(lineset->linestyle, modifier, dir); + BKE_linestyle_geometry_modifier_move(lineset->linestyle, modifier, dir); break; default: BKE_report(op->reports, RPT_ERROR, "The object the data pointer refers to is not a valid modifier"); return OPERATOR_CANCELLED; } - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + DAG_id_tag_update(&lineset->linestyle->id, 0); + WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); return OPERATOR_FINISHED; } @@ -1231,6 +1265,36 @@ void SCENE_OT_freestyle_modifier_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Direction to move, UP or DOWN"); } +static int freestyle_stroke_material_create_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + FreestyleLineStyle *linestyle = BKE_linestyle_active_from_scene(scene); + + if (!linestyle) { + BKE_report(op->reports, RPT_ERROR, "No active line style in the current scene"); + return OPERATOR_CANCELLED; + } + + FRS_create_stroke_material(bmain, linestyle); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Create Freestyle Stroke Material"; + ot->idname = "SCENE_OT_freestyle_stroke_material_create"; + ot->description = "Create Freestyle stroke material for testing"; + + /* api callbacks */ + ot->exec = freestyle_stroke_material_create_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + #endif /* WITH_FREESTYLE */ static int texture_slot_move_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 94d8d78de1a..df7ca9f11b2 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -45,6 +45,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_icons.h" #include "BKE_main.h" @@ -473,15 +474,22 @@ static void image_changed(Main *bmain, Image *ima) texture_changed(bmain, tex); } -static void scene_changed(Main *bmain, Scene *UNUSED(scene)) +static void scene_changed(Main *bmain, Scene *scene) { Object *ob; Material *ma; /* glsl */ - for (ob = bmain->object.first; ob; ob = ob->id.next) + for (ob = bmain->object.first; ob; ob = ob->id.next) { if (ob->gpulamp.first) GPU_lamp_free(ob); + + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + BKE_texpaint_slots_refresh_object(scene, ob); + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + GPU_drawobject_free(ob->derivedFinal); + } + } for (ma = bmain->mat.first; ma; ma = ma->id.next) if (ma->gpumaterial.first) diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 0beb5737ec7..ab28f5fa675 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -252,7 +252,7 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) } else if (sima->flag & SI_FULLWINDOW) { sima->flag &= ~SI_FULLWINDOW; - ED_screen_full_toggle(C, win, sa); + ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/screen/CMakeLists.txt b/source/blender/editors/screen/CMakeLists.txt index 4ff1767f582..413d40b9f9c 100644 --- a/source/blender/editors/screen/CMakeLists.txt +++ b/source/blender/editors/screen/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -51,4 +52,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_screen "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/screen/SConscript b/source/blender/editors/screen/SConscript index 28a6cbb02e6..f5442c7ea63 100644 --- a/source/blender/editors/screen/SConscript +++ b/source/blender/editors/screen/SConscript @@ -31,7 +31,8 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', @@ -45,7 +46,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs += ' ' + env['BF_PTHREADS_INC'] diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 151764dab6a..e38f3b5bee6 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -63,6 +63,7 @@ #include "BLF_api.h" #include "UI_interface.h" +#include "UI_interface_icons.h" #include "UI_resources.h" #include "UI_view2d.h" @@ -107,10 +108,7 @@ static void region_draw_emboss(ARegion *ar, rcti *scirct) void ED_region_pixelspace(ARegion *ar) { - int width = BLI_rcti_size_x(&ar->winrct) + 1; - int height = BLI_rcti_size_y(&ar->winrct) + 1; - - wmOrtho2(-GLA_PIXEL_OFS, (float)width - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, (float)height - GLA_PIXEL_OFS); + wmOrtho2_region_pixelspace(ar); glLoadIdentity(); } @@ -152,6 +150,45 @@ void ED_area_do_refresh(bContext *C, ScrArea *sa) } /** + * \brief Corner widget use for quitting fullscreen. + */ +static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha) +{ + int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC; + int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC; + + /* adjust the icon distance from the corner */ + x += 36.0f / UI_DPI_FAC; + y += 36.0f / UI_DPI_FAC; + + /* draws from the left bottom corner of the icon */ + x -= UI_DPI_ICON_SIZE; + y -= UI_DPI_ICON_SIZE; + + alpha = min_ff(alpha, 0.75f); + + UI_icon_draw_aspect(x, y, ICON_FULLSCREEN_EXIT, 0.7f / UI_DPI_FAC, alpha); + + /* debug drawing : + * The click_rect is the same as defined in fullscreen_click_rcti_init + * Keep them both in sync */ + + if (G.debug_value == 1) { + rcti click_rect; + float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC; + char alpha_debug = 255 * alpha; + + BLI_rcti_init(&click_rect, x, x + icon_size, y, y + icon_size); + + glColor4ub(255, 0, 0, alpha_debug); + fdrawbox(click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); + glColor4ub(0, 255, 255, alpha_debug); + fdrawline(click_rect.xmin, click_rect.ymin, click_rect.xmax, click_rect.ymax); + fdrawline(click_rect.xmin, click_rect.ymax, click_rect.xmax, click_rect.ymin); + } +} + +/** * \brief Corner widgets use for dragging and splitting the view. */ static void area_draw_azone(short x1, short y1, short x2, short y2) @@ -368,6 +405,9 @@ static void region_draw_azones(ScrArea *sa, ARegion *ar) } } } + else if (az->type == AZONE_FULLSCREEN) { + area_draw_azone_fullscreen(az->x1, az->y1, az->x2, az->y2, az->alpha); + } } } @@ -421,6 +461,8 @@ void ED_region_do_draw(bContext *C, ARegion *ar) /* note; this sets state, so we can use wmOrtho and friends */ wmSubWindowScissorSet(win, ar->swinid, &ar->drawrct, scissor_pad); + + wmOrtho2_region_ui(ar); UI_SetTheme(sa ? sa->spacetype : 0, at->regionid); @@ -457,8 +499,9 @@ void ED_region_do_draw(bContext *C, ARegion *ar) uiFreeInactiveBlocks(C, &ar->uiblocks); - if (sa) + if (sa && (win->screen->state != SCREENFULL)) { region_draw_emboss(ar, &ar->winrct); + } } /* ********************************** @@ -472,7 +515,8 @@ void ED_region_tag_redraw(ARegion *ar) * but python scripts can cause this to happen indirectly */ if (ar && !(ar->do_draw & RGN_DRAWING)) { /* zero region means full region redraw */ - ar->do_draw = RGN_DRAW; + ar->do_draw &= ~RGN_DRAW_PARTIAL; + ar->do_draw |= RGN_DRAW; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); } } @@ -483,12 +527,19 @@ void ED_region_tag_redraw_overlay(ARegion *ar) ar->do_draw_overlay = RGN_DRAW; } +void ED_region_tag_refresh_ui(ARegion *ar) +{ + if (ar) { + ar->do_draw |= RGN_DRAW_REFRESH_UI; + } +} + void ED_region_tag_redraw_partial(ARegion *ar, rcti *rct) { if (ar && !(ar->do_draw & RGN_DRAWING)) { - if (!ar->do_draw) { + if (!(ar->do_draw & RGN_DRAW)) { /* no redraw set yet, set partial region */ - ar->do_draw = RGN_DRAW_PARTIAL; + ar->do_draw |= RGN_DRAW_PARTIAL; ar->drawrct = *rct; } else if (ar->drawrct.xmin != ar->drawrct.xmax) { @@ -560,10 +611,10 @@ static void area_azone_initialize(wmWindow *win, bScreen *screen, ScrArea *sa) { AZone *az; - /* reinitalize entirely, regions add azones too */ + /* reinitalize entirely, regions and fullscreen add azones too */ BLI_freelistN(&sa->actionzones); - if (screen->full != SCREENNORMAL) { + if (screen->state != SCREENNORMAL) { return; } @@ -595,6 +646,26 @@ static void area_azone_initialize(wmWindow *win, bScreen *screen, ScrArea *sa) BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); } +static void fullscreen_azone_initialize(ScrArea *sa, ARegion *ar) +{ + AZone *az; + + if (ar->regiontype != RGN_TYPE_WINDOW) + return; + + az = (AZone *)MEM_callocN(sizeof(AZone), "fullscreen action zone"); + BLI_addtail(&(sa->actionzones), az); + az->type = AZONE_FULLSCREEN; + az->ar = ar; + az->alpha = 0.0f; + + az->x1 = ar->winrct.xmax - (AZONEFADEOUT - 1); + az->y1 = ar->winrct.ymax - (AZONEFADEOUT - 1); + az->x2 = ar->winrct.xmax; + az->y2 = ar->winrct.ymax; + BLI_rcti_init(&az->rect, az->x1, az->x2, az->y1, az->y2); +} + #define AZONEPAD_EDGE (0.1f * U.widget_unit) #define AZONEPAD_ICON (0.45f * U.widget_unit) static void region_azone_edge(AZone *az, ARegion *ar) @@ -823,25 +894,30 @@ static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar) } -static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge) +static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge, const bool is_fullscreen) { AZone *az; + const bool is_hidden = (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) == 0; - az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); - BLI_addtail(&(sa->actionzones), az); - az->type = AZONE_REGION; - az->ar = ar; - az->edge = edge; + if (is_hidden || !is_fullscreen) { + az = (AZone *)MEM_callocN(sizeof(AZone), "actionzone"); + BLI_addtail(&(sa->actionzones), az); + az->type = AZONE_REGION; + az->ar = ar; + az->edge = edge; + } if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) { - if (G.debug_value == 3) - region_azone_icon(sa, az, ar); - else if (G.debug_value == 2) - region_azone_tria(sa, az, ar); - else if (G.debug_value == 1) - region_azone_tab(sa, az, ar); - else - region_azone_tab_plus(sa, az, ar); + if (!is_fullscreen) { + if (G.debug_value == 3) + region_azone_icon(sa, az, ar); + else if (G.debug_value == 2) + region_azone_tria(sa, az, ar); + else if (G.debug_value == 1) + region_azone_tab(sa, az, ar); + else + region_azone_tab_plus(sa, az, ar); + } } else { region_azone_edge(az, ar); @@ -852,18 +928,18 @@ static void region_azone_initialize(ScrArea *sa, ARegion *ar, AZEdge edge) /* *************************************************************** */ -static void region_azone_add(ScrArea *sa, ARegion *ar, int alignment) +static void region_azone_add(ScrArea *sa, ARegion *ar, const int alignment, const bool is_fullscreen) { /* edge code (t b l r) is along which area edge azone will be drawn */ if (alignment == RGN_ALIGN_TOP) - region_azone_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT); + region_azone_initialize(sa, ar, AE_BOTTOM_TO_TOPLEFT, is_fullscreen); else if (alignment == RGN_ALIGN_BOTTOM) - region_azone_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT); + region_azone_initialize(sa, ar, AE_TOP_TO_BOTTOMRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_RIGHT) - region_azone_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT); + region_azone_initialize(sa, ar, AE_LEFT_TO_TOPRIGHT, is_fullscreen); else if (alignment == RGN_ALIGN_LEFT) - region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT); + region_azone_initialize(sa, ar, AE_RIGHT_TO_TOPLEFT, is_fullscreen); } /* dir is direction to check, not the splitting edge direction! */ @@ -883,38 +959,59 @@ static int rct_fits(rcti *rect, char dir, int size) /* function checks if some overlapping region was defined before - on same place */ static void region_overlap_fix(ScrArea *sa, ARegion *ar) { - ARegion *ar1 = ar->prev; - + ARegion *ar1; + const int align = ar->alignment & ~RGN_SPLIT_PREV; + int align1 = 0; + /* find overlapping previous region on same place */ - while (ar1) { - if (ar1->overlap) { - if ((ar1->alignment & RGN_SPLIT_PREV) == 0) - if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) - break; + for (ar1 = ar->prev; ar1; ar1 = ar1->prev) { + if (ar1->overlap && ((ar1->alignment & RGN_SPLIT_PREV) == 0)) { + align1 = ar1->alignment; + if (BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) { + if (align1 != align) { + /* Left overlapping right or vice-versa, forbid this! */ + ar->flag |= RGN_FLAG_TOO_SMALL; + return; + } + /* Else, we have our previous region on same side. */ + break; + } } - ar1 = ar1->prev; } - + /* translate or close */ if (ar1) { - int align1 = ar1->alignment & ~RGN_SPLIT_PREV; - if (align1 == RGN_ALIGN_LEFT) { - if (ar->winrct.xmax + ar1->winx > sa->winx - U.widget_unit) + if (ar->winrct.xmax + ar1->winx > sa->winx - U.widget_unit) { ar->flag |= RGN_FLAG_TOO_SMALL; - else + return; + } + else { BLI_rcti_translate(&ar->winrct, ar1->winx, 0); + } } else if (align1 == RGN_ALIGN_RIGHT) { - if (ar->winrct.xmin - ar1->winx < U.widget_unit) + if (ar->winrct.xmin - ar1->winx < U.widget_unit) { ar->flag |= RGN_FLAG_TOO_SMALL; - else + return; + } + else { BLI_rcti_translate(&ar->winrct, -ar1->winx, 0); + } } } - - + /* At this point, 'ar' is in its final position and still open. + * Make a final check it does not overlap any previous 'other side' region. */ + for (ar1 = ar->prev; ar1; ar1 = ar1->prev) { + if (ar1->overlap && (ar1->alignment & RGN_SPLIT_PREV) == 0) { + if ((ar1->alignment != align) && BLI_rcti_isect(&ar1->winrct, &ar->winrct, NULL)) { + /* Left overlapping right or vice-versa, forbid this! */ + ar->flag |= RGN_FLAG_TOO_SMALL; + return; + } + } + } } /* overlapping regions only in the following restricted cases */ @@ -923,11 +1020,11 @@ static bool region_is_overlap(wmWindow *win, ScrArea *sa, ARegion *ar) if (U.uiflag2 & USER_REGION_OVERLAP) { if (WM_is_draw_triple(win)) { if (ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { - if (ELEM3(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) + if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS)) return 1; } else if (sa->spacetype == SPACE_IMAGE) { - if (ELEM4(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW)) + if (ELEM(ar->regiontype, RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, RGN_TYPE_PREVIEW)) return 1; } } @@ -1160,7 +1257,13 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti * must be minimum '4' */ } else { - region_azone_add(sa, ar, alignment); + if (ELEM(win->screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { + region_azone_add(sa, ar, alignment, false); + } + else { + region_azone_add(sa, ar, alignment, true); + fullscreen_azone_initialize(sa, ar); + } } region_rect_recursive(win, sa, ar->next, remainder, quad); @@ -1226,7 +1329,9 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand /* time space only has this keymap, the others get a boundbox restricted map */ if (sa->spacetype != SPACE_TIME) { ARegion *ar; - static rcti rect = {0, 10000, 0, 30}; /* same local check for all areas */ + /* same local check for all areas */ + static rcti rect = {0, 10000, 0, -1}; + rect.ymax = (30 * UI_DPI_FAC); ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); if (ar) { WM_event_add_keymap_handler_bb(handlers, keymap, &rect, &ar->winrct); @@ -1339,10 +1444,6 @@ void ED_region_init(bContext *C, ARegion *ar) region_subwindow(CTX_wm_window(C), ar); region_update_rect(ar); - - /* UI convention */ - wmOrtho2(-0.01f, ar->winx - 0.01f, -0.01f, ar->winy - 0.01f); - glLoadIdentity(); } /* for quick toggle, can skip fades */ @@ -1379,12 +1480,15 @@ void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free) SpaceType *st; ARegion *ar; const char spacetype = sa_dst->spacetype; + const short flag_copy = HEADER_NO_PULLDOWN; sa_dst->headertype = sa_src->headertype; sa_dst->spacetype = sa_src->spacetype; sa_dst->type = sa_src->type; sa_dst->butspacetype = sa_src->butspacetype; + sa_dst->flag = (sa_dst->flag & ~flag_copy) | (sa_src->flag & flag_copy); + /* area */ if (do_free) { BKE_spacedata_freelist(&sa_dst->spacedata); @@ -1759,9 +1863,7 @@ void ED_region_panels(const bContext *C, ARegion *ar, int vertical, const char * break; } } - - BLI_SMALLSTACK_FREE(pt_stack); - + /* clear */ if (ar->overlap) { /* view should be in pixelspace */ diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index f31d79ff76b..c095dfe7792 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -50,6 +50,8 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" +#include "UI_interface.h" + #ifndef GL_CLAMP_TO_EDGE #define GL_CLAMP_TO_EDGE 0x812F #endif @@ -222,15 +224,15 @@ void fdrawcheckerboard(float x1, float y1, float x2, float y2) glDisable(GL_POLYGON_STIPPLE); } -void sdrawline(short x1, short y1, short x2, short y2) +void sdrawline(int x1, int y1, int x2, int y2) { - short v[2]; + int v[2]; glBegin(GL_LINE_STRIP); v[0] = x1; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); v[0] = x2; v[1] = y2; - glVertex2sv(v); + glVertex2iv(v); glEnd(); } @@ -244,25 +246,25 @@ void sdrawline(short x1, short y1, short x2, short y2) * x1,y1-- x2,y1 */ -static void sdrawtripoints(short x1, short y1, short x2, short y2) +static void sdrawtripoints(int x1, int y1, int x2, int y2) { - short v[2]; + int v[2]; v[0] = x1; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); v[0] = x1; v[1] = y2; - glVertex2sv(v); + glVertex2iv(v); v[0] = x2; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); } -void sdrawtri(short x1, short y1, short x2, short y2) +void sdrawtri(int x1, int y1, int x2, int y2) { glBegin(GL_LINE_STRIP); sdrawtripoints(x1, y1, x2, y2); glEnd(); } -void sdrawtrifill(short x1, short y1, short x2, short y2) +void sdrawtrifill(int x1, int y1, int x2, int y2) { glBegin(GL_TRIANGLES); sdrawtripoints(x1, y1, x2, y2); @@ -270,22 +272,22 @@ void sdrawtrifill(short x1, short y1, short x2, short y2) } #endif -void sdrawbox(short x1, short y1, short x2, short y2) +void sdrawbox(int x1, int y1, int x2, int y2) { - short v[2]; + int v[2]; glBegin(GL_LINE_STRIP); v[0] = x1; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); v[0] = x1; v[1] = y2; - glVertex2sv(v); + glVertex2iv(v); v[0] = x2; v[1] = y2; - glVertex2sv(v); + glVertex2iv(v); v[0] = x2; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); v[0] = x1; v[1] = y1; - glVertex2sv(v); + glVertex2iv(v); glEnd(); } @@ -337,7 +339,7 @@ void sdrawXORline(int x0, int y0, int x1, int y1) void sdrawXORline4(int nr, int x0, int y0, int x1, int y1) { - static short old[4][2][2]; + static int old[4][2][2]; static char flags[4] = {0, 0, 0, 0}; /* with builtin memory, max 4 lines */ @@ -348,8 +350,8 @@ void sdrawXORline4(int nr, int x0, int y0, int x1, int y1) if (nr == -1) { /* flush */ for (nr = 0; nr < 4; nr++) { if (flags[nr]) { - glVertex2sv(old[nr][0]); - glVertex2sv(old[nr][1]); + glVertex2iv(old[nr][0]); + glVertex2iv(old[nr][1]); flags[nr] = 0; } } @@ -357,8 +359,8 @@ void sdrawXORline4(int nr, int x0, int y0, int x1, int y1) else { if (nr >= 0 && nr < 4) { if (flags[nr]) { - glVertex2sv(old[nr][0]); - glVertex2sv(old[nr][1]); + glVertex2iv(old[nr][0]); + glVertex2iv(old[nr][1]); } old[nr][0][0] = x0; @@ -1139,3 +1141,40 @@ void cpack(unsigned int x) (((x) >> 8) & 0xFF), (((x) >> 16) & 0xFF) ); } + +void glaDrawBorderCorners(const rcti *border, float zoomx, float zoomy) +{ + float delta_x = 4.0f * UI_DPI_FAC / zoomx; + float delta_y = 4.0f * UI_DPI_FAC / zoomy; + + delta_x = min_ff(delta_x, border->xmax - border->xmin); + delta_y = min_ff(delta_y, border->ymax - border->ymin); + + /* left bottom corner */ + glBegin(GL_LINE_STRIP); + glVertex2f(border->xmin, border->ymin + delta_y); + glVertex2f(border->xmin, border->ymin); + glVertex2f(border->xmin + delta_x, border->ymin); + glEnd(); + + /* left top corner */ + glBegin(GL_LINE_STRIP); + glVertex2f(border->xmin, border->ymax - delta_y); + glVertex2f(border->xmin, border->ymax); + glVertex2f(border->xmin + delta_x, border->ymax); + glEnd(); + + /* right bottom corner */ + glBegin(GL_LINE_STRIP); + glVertex2f(border->xmax - delta_x, border->ymin); + glVertex2f(border->xmax, border->ymin); + glVertex2f(border->xmax, border->ymin + delta_y); + glEnd(); + + /* right top corner */ + glBegin(GL_LINE_STRIP); + glVertex2f(border->xmax - delta_x, border->ymax); + glVertex2f(border->xmax, border->ymax); + glVertex2f(border->xmax, border->ymax - delta_y); + glEnd(); +} diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index f76f76aacaa..9463a702b76 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -378,7 +378,7 @@ ScrArea *area_split(bScreen *sc, ScrArea *sa, char dir, float fac, int merge) if (split == 0) return NULL; /* note regarding (fac > 0.5f) checks below. - * notmally it shouldn't matter which is used since the copy should match the original + * normally it shouldn't matter which is used since the copy should match the original * however with viewport rendering and python console this isn't the case. - campbell */ if (dir == 'h') { @@ -1009,7 +1009,7 @@ static void scrarea_draw_shape_light(ScrArea *sa, char UNUSED(dir)) glDisable(GL_BLEND); } -static void drawscredge_area_draw(int sizex, int sizey, short x1, short y1, short x2, short y2, short a) +static void drawscredge_area_draw(int sizex, int sizey, int x1, int y1, int x2, int y2, int a) { /* right border area */ if (x2 < sizex - 1) @@ -1058,7 +1058,7 @@ bScreen *ED_screen_duplicate(wmWindow *win, bScreen *sc) { bScreen *newsc; - if (sc->full != SCREENNORMAL) return NULL; /* XXX handle this case! */ + if (sc->state != SCREENNORMAL) return NULL; /* XXX handle this case! */ /* make new empty screen: */ newsc = ED_screen_add(win, sc->scene, sc->id.name + 2); @@ -1485,7 +1485,7 @@ void ED_screen_set(bContext *C, bScreen *sc) return; - if (sc->full) { /* find associated full */ + if (sc->state == SCREENFULL) { /* find associated full */ bScreen *sc1; for (sc1 = bmain->screen.first; sc1; sc1 = sc1->id.next) { ScrArea *sa = sc1->areabase.first; @@ -1586,7 +1586,7 @@ void ED_screen_delete(bContext *C, bScreen *sc) int delete = 1; /* don't allow deleting temp fullscreens for now */ - if (sc->full == SCREENFULL) { + if (ELEM(sc->state, SCREENMAXIMIZED, SCREENFULL)) { return; } @@ -1730,11 +1730,11 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) ScrArea *newsa = NULL; if (!sa || sa->full == NULL) { - newsa = ED_screen_full_toggle(C, win, sa); + newsa = ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); } if (!newsa) { - if (sa->full) { + if (sa->full && (screen->state == SCREENMAXIMIZED)) { /* if this has been called from the temporary info header generated in * temp fullscreen layouts, find the correct fullscreen area to change * to create a new space inside */ @@ -1760,7 +1760,7 @@ void ED_screen_full_prevspace(bContext *C, ScrArea *sa) ED_area_prevspace(C, sa); if (sa->full) - ED_screen_full_toggle(C, win, sa); + ED_screen_state_toggle(C, win, sa, SCREENMAXIMIZED); } /* restore a screen / area back to default operation, after temp fullscreen modes */ @@ -1768,6 +1768,8 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) { wmWindow *win = CTX_wm_window(C); SpaceLink *sl = sa->spacedata.first; + bScreen *screen = CTX_wm_screen(C); + short state = (screen ? screen->state : SCREENMAXIMIZED); /* if fullscreen area has a secondary space (such as a file browser or fullscreen render * overlaid on top of a existing setup) then return to the previous space */ @@ -1785,23 +1787,23 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) ED_screen_full_prevspace(C, sa); } else - ED_screen_full_toggle(C, win, sa); + ED_screen_state_toggle(C, win, sa, state); } else if (sl->spacetype == SPACE_FILE) { ED_screen_full_prevspace(C, sa); } else { - ED_screen_full_toggle(C, win, sa); + ED_screen_state_toggle(C, win, sa, state); } } /* otherwise just tile the area again */ else { - ED_screen_full_toggle(C, win, sa); + ED_screen_state_toggle(C, win, sa, state); } } -/* this function toggles: if area is full then the parent will be restored */ -ScrArea *ED_screen_full_toggle(bContext *C, wmWindow *win, ScrArea *sa) +/* this function toggles: if area is maximized/full then the parent will be restored */ +ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const short state) { bScreen *sc, *oldscreen; ARegion *ar; @@ -1812,23 +1814,20 @@ ScrArea *ED_screen_full_toggle(bContext *C, wmWindow *win, ScrArea *sa) * are no longer in the same screen */ for (ar = sa->regionbase.first; ar; ar = ar->next) uiFreeBlocks(C, &ar->uiblocks); - + /* prevent hanging header prints */ ED_area_headerprint(sa, NULL); } if (sa && sa->full) { + /* restoring back to SCREENNORMAL */ ScrArea *old; - /*short fulltype;*/ /*UNUSED*/ sc = sa->full; /* the old screen to restore */ oldscreen = win->screen; /* the one disappearing */ - /*fulltype = sc->full;*/ - sc->full = 0; + sc->state = SCREENNORMAL; - /* removed: SCREENAUTOPLAY exception here */ - /* find old area */ for (old = sc->areabase.first; old; old = old->next) if (old->full) break; @@ -1838,6 +1837,13 @@ ScrArea *ED_screen_full_toggle(bContext *C, wmWindow *win, ScrArea *sa) return NULL; } + if (state == SCREENFULL) { + /* restore the old side panels/header visibility */ + for (ar = sa->regionbase.first; ar; ar = ar->next) { + ar->flag = ar->flagfullscreen; + } + } + ED_area_data_swap(old, sa); if (sa->flag & AREA_TEMP_INFO) sa->flag &= ~AREA_TEMP_INFO; old->full = NULL; @@ -1853,44 +1859,66 @@ ScrArea *ED_screen_full_toggle(bContext *C, wmWindow *win, ScrArea *sa) } else { + /* change from SCREENNORMAL to new state */ ScrArea *newa; char newname[MAX_ID_NAME - 2]; oldscreen = win->screen; - /* nothing wrong with having only 1 area, as far as I can see... - * is there only 1 area? */ -#if 0 - if (oldscreen->areabase.first == oldscreen->areabase.last) - return NULL; -#endif - - oldscreen->full = SCREENFULL; - BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "full"); + oldscreen->state = state; + BLI_snprintf(newname, sizeof(newname), "%s-%s", oldscreen->id.name + 2, "nonnormal"); sc = ED_screen_add(win, oldscreen->scene, newname); - sc->full = SCREENFULL; // XXX + sc->state = state; /* timer */ sc->animtimer = oldscreen->animtimer; oldscreen->animtimer = NULL; - /* returns the top small area */ - newa = area_split(sc, (ScrArea *)sc->areabase.first, 'h', 0.99f, 1); - ED_area_newspace(C, newa, SPACE_INFO); - /* use random area when we have no active one, e.g. when the * mouse is outside of the window and we open a file browser */ if (!sa) sa = oldscreen->areabase.first; - /* copy area */ - newa = newa->prev; - ED_area_data_swap(newa, sa); - sa->flag |= AREA_TEMP_INFO; + if (state == SCREENMAXIMIZED) { + /* returns the top small area */ + newa = area_split(sc, (ScrArea *)sc->areabase.first, 'h', 0.99f, 1); + ED_area_newspace(C, newa, SPACE_INFO); + + /* copy area */ + newa = newa->prev; + ED_area_data_swap(newa, sa); + sa->flag |= AREA_TEMP_INFO; + + sa->full = oldscreen; + newa->full = oldscreen; + newa->next->full = oldscreen; // XXX + } + else if (state == SCREENFULL) { + newa = (ScrArea *)sc->areabase.first; + + /* copy area */ + ED_area_data_swap(newa, sa); + newa->flag = sa->flag; /* mostly for AREA_FLAG_WASFULLSCREEN */ + + /* temporarily hide the side panels/header */ + for (ar = newa->regionbase.first; ar; ar = ar->next) { + ar->flagfullscreen = ar->flag; + + if (ELEM(ar->regiontype, + RGN_TYPE_UI, + RGN_TYPE_HEADER, + RGN_TYPE_TOOLS)) + { + ar->flag |= RGN_FLAG_HIDDEN; + } + } - sa->full = oldscreen; - newa->full = oldscreen; - newa->next->full = oldscreen; // XXX + sa->full = oldscreen; + newa->full = oldscreen; + } + else { + BLI_assert(false); + } ED_screen_set(C, sc); } diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 9e0421b6e99..79036d3356f 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -36,6 +36,8 @@ struct wmWindow; struct Scene; #define AZONESPOT (0.6f * U.widget_unit) +#define AZONEFADEIN (5.0f * U.widget_unit) /* when azone is totally visible */ +#define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the azone */ /* area.c */ void ED_area_data_copy(ScrArea *sa_dst, ScrArea *sa_src, const bool do_free); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 8b6a1b22a5c..a4a7580c058 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -129,13 +129,31 @@ static int screen_active_editable(bContext *C) { if (ED_operator_screenactive(C)) { /* no full window splitting allowed */ - if (CTX_wm_screen(C)->full != SCREENNORMAL) + if (CTX_wm_screen(C)->state != SCREENNORMAL) return 0; return 1; } return 0; } +static ARegion *screen_find_region_type(bContext *C, int type) +{ + ARegion *ar = CTX_wm_region(C); + + /* find the header region + * - try context first, but upon failing, search all regions in area... + */ + if ((ar == NULL) || (ar->regiontype != type)) { + ScrArea *sa = CTX_wm_area(C); + ar = BKE_area_find_region_type(sa, type); + } + else { + ar = NULL; + } + + return ar; +} + /* when mouse is over area-edge */ int ED_operator_screen_mainwinactive(bContext *C) { @@ -166,7 +184,7 @@ int ED_operator_objectmode(bContext *C) return 0; /* add a check for ob->mode too? */ - if (obact && obact->mode) + if (obact && (obact->mode != OB_MODE_OBJECT)) return 0; return 1; @@ -201,7 +219,7 @@ int ED_operator_animview_active(bContext *C) { if (ED_operator_areaactive(C)) { SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C); - if (sl && (ELEM5(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME))) + if (sl && (ELEM(sl->spacetype, SPACE_SEQ, SPACE_ACTION, SPACE_NLA, SPACE_IPO, SPACE_TIME))) return true; } @@ -267,7 +285,6 @@ int ED_operator_node_editable(bContext *C) return 0; } -/* XXX rename */ int ED_operator_graphedit_active(bContext *C) { return ed_spacetype_test(C, SPACE_IPO); @@ -612,6 +629,24 @@ static int actionzone_area_poll(bContext *C) return 0; } +/* the debug drawing of the click_rect is in area_draw_azone_fullscreen, keep both in sync */ +static void fullscreen_click_rcti_init(rcti *rect, const short x1, const short y1, const short x2, const short y2) +{ + int x = x2 - ((float) x2 - x1) * 0.5f / UI_DPI_FAC; + int y = y2 - ((float) y2 - y1) * 0.5f / UI_DPI_FAC; + float icon_size = UI_DPI_ICON_SIZE + 7 * UI_DPI_FAC; + + /* adjust the icon distance from the corner */ + x += 36.0f / UI_DPI_FAC; + y += 36.0f / UI_DPI_FAC; + + /* draws from the left bottom corner of the icon */ + x -= UI_DPI_ICON_SIZE; + y -= UI_DPI_ICON_SIZE; + + BLI_rcti_init(rect, x, x + icon_size, y, y + icon_size); +} + AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2]) { AZone *az = NULL; @@ -628,6 +663,42 @@ AZone *is_in_area_actionzone(ScrArea *sa, const int xy[2]) else if (az->type == AZONE_REGION) { break; } + else if (az->type == AZONE_FULLSCREEN) { + int mouse_radius, spot_radius, fadein_radius, fadeout_radius; + rcti click_rect; + + fullscreen_click_rcti_init(&click_rect, az->x1, az->y1, az->x2, az->y2); + + if (BLI_rcti_isect_pt_v(&click_rect, xy)) { + az->alpha = 1.0f; + } + else { + mouse_radius = (xy[0] - az->x2) * (xy[0] - az->x2) + (xy[1] - az->y2) * (xy[1] - az->y2); + spot_radius = AZONESPOT * AZONESPOT; + fadein_radius = AZONEFADEIN * AZONEFADEIN; + fadeout_radius = AZONEFADEOUT * AZONEFADEOUT; + + if (mouse_radius < spot_radius) { + az->alpha = 1.0f; + } + else if (mouse_radius < fadein_radius) { + az->alpha = 1.0f; + } + else if (mouse_radius < fadeout_radius) { + az->alpha = 1.0f - ((float)(mouse_radius - fadein_radius)) / ((float)(fadeout_radius - fadein_radius)); + } + else { + az->alpha = 0.0f; + } + + /* fade in/out but no click */ + az = NULL; + } + + /* XXX force redraw to show/hide the action zone */ + ED_area_tag_redraw(sa); + break; + } } } @@ -655,8 +726,11 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) if (type == AZONE_AREA) event.type = EVT_ACTIONZONE_AREA; + else if (type == AZONE_FULLSCREEN) + event.type = EVT_ACTIONZONE_FULLSCREEN; else event.type = EVT_ACTIONZONE_REGION; + event.val = 0; event.customdata = op->customdata; event.customdatafree = true; @@ -681,8 +755,8 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) sad->x = event->x; sad->y = event->y; /* region azone directly reacts on mouse clicks */ - if (sad->az->type == AZONE_REGION) { - actionzone_apply(C, op, AZONE_REGION); + if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) { + actionzone_apply(C, op, sad->az->type); actionzone_exit(op); return OPERATOR_FINISHED; } @@ -1463,7 +1537,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) int dir; /* no full window splitting allowed */ - if (sc->full != SCREENNORMAL) + if (sc->state != SCREENNORMAL) return OPERATOR_CANCELLED; if (event->type == EVT_ACTIONZONE_AREA) { @@ -2232,8 +2306,8 @@ static void SCREEN_OT_marker_jump(wmOperatorType *ot) static bool screen_set_is_ok(bScreen *screen, bScreen *screen_prev) { - return ((screen->winid == 0) && - (screen->full == 0) && + return ((screen->winid == 0) && + (screen->state == SCREENNORMAL) && (screen != screen_prev) && (screen->id.name[2] != '.' || !(U.uiflag & USER_HIDE_DOT))); } @@ -2304,10 +2378,11 @@ static void SCREEN_OT_screen_set(wmOperatorType *ot) /* function to be called outside UI context, or for redo */ -static int screen_full_area_exec(bContext *C, wmOperator *UNUSED(op)) +static int screen_maximize_area_exec(bContext *C, wmOperator *op) { bScreen *screen = CTX_wm_screen(C); ScrArea *sa = NULL; + const bool hide_panels = RNA_boolean_get(op->ptr, "use_hide_panels"); /* search current screen for 'fullscreen' areas */ /* prevents restoring info header, when mouse is over it */ @@ -2315,25 +2390,41 @@ static int screen_full_area_exec(bContext *C, wmOperator *UNUSED(op)) if (sa->full) break; } - if (sa == NULL) sa = CTX_wm_area(C); + if (sa == NULL) { + sa = CTX_wm_area(C); + } - ED_screen_full_toggle(C, CTX_wm_window(C), sa); + if (hide_panels) { + if (!ELEM(screen->state, SCREENNORMAL, SCREENFULL)) { + return OPERATOR_CANCELLED; + } + ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENFULL); + } + else { + if (!ELEM(screen->state, SCREENNORMAL, SCREENMAXIMIZED)) { + return OPERATOR_CANCELLED; + } + ED_screen_state_toggle(C, CTX_wm_window(C), sa, SCREENMAXIMIZED); + } + return OPERATOR_FINISHED; } static void SCREEN_OT_screen_full_area(wmOperatorType *ot) { - ot->name = "Toggle Full Screen"; - ot->description = "Toggle display selected area as fullscreen"; + PropertyRNA *prop; + + ot->name = "Toggle Fullscreen Area"; + ot->description = "Toggle display selected area as fullscreen/maximized"; ot->idname = "SCREEN_OT_screen_full_area"; - ot->exec = screen_full_area_exec; + ot->exec = screen_maximize_area_exec; ot->poll = ED_operator_areaactive; ot->flag = 0; - -} - + prop = RNA_def_boolean(ot->srna, "use_hide_panels", false, "Hide Panels", "Hide all the panels"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} /* ************** join area operator ********************************************** */ @@ -3028,23 +3119,44 @@ static void SCREEN_OT_region_flip(wmOperatorType *ot) ot->flag = 0; } +/* ************** header operator ***************************** */ +static int header_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER); + + if (ar == NULL) { + return OPERATOR_CANCELLED; + } + + ar->flag ^= RGN_FLAG_HIDDEN; + + ED_area_tag_redraw(CTX_wm_area(C)); + + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +static void SCREEN_OT_header(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Header"; + ot->description = "Display header"; + ot->idname = "SCREEN_OT_header"; + + /* api callbacks */ + ot->exec = header_exec; +} + /* ************** header flip operator ***************************** */ /* flip a header region alignment */ static int header_flip_exec(bContext *C, wmOperator *UNUSED(op)) { - ARegion *ar = CTX_wm_region(C); - - /* find the header region - * - try context first, but upon failing, search all regions in area... - */ - if ((ar == NULL) || (ar->regiontype != RGN_TYPE_HEADER)) { - ScrArea *sa = CTX_wm_area(C); - ar = BKE_area_find_region_type(sa, RGN_TYPE_HEADER); + ARegion *ar = screen_find_region_type(C, RGN_TYPE_HEADER); - /* don't do anything if no region */ - if (ar == NULL) - return OPERATOR_CANCELLED; + if (ar == NULL) { + return OPERATOR_CANCELLED; } /* copied from SCREEN_OT_region_flip */ @@ -3221,6 +3333,16 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws) } } + else if (regiontype == RGN_TYPE_CHANNELS) { + switch (spacetype) { + case SPACE_IPO: + case SPACE_ACTION: + case SPACE_NLA: + if (redraws & TIME_ALL_ANIM_WIN) + return 1; + break; + } + } else if (regiontype == RGN_TYPE_UI) { if (spacetype == SPACE_CLIP) { /* Track Preview button is on Properties Editor in SpaceClip, @@ -3936,6 +4058,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_region_scale); WM_operatortype_append(SCREEN_OT_region_flip); WM_operatortype_append(SCREEN_OT_header_flip); + WM_operatortype_append(SCREEN_OT_header); WM_operatortype_append(SCREEN_OT_header_toggle_menus); WM_operatortype_append(SCREEN_OT_header_toolbox); WM_operatortype_append(SCREEN_OT_screen_set); @@ -4038,6 +4161,7 @@ void ED_keymap_screen(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "SCREEN_OT_area_options", RIGHTMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "SCREEN_OT_header", F9KEY, KM_PRESS, KM_ALT, 0); /* Header Editing ------------------------------------------------ */ keymap = WM_keymap_find(keyconf, "Header", 0, 0); @@ -4057,6 +4181,11 @@ void ED_keymap_screen(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", UPARROWKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", DOWNARROWKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", SPACEKEY, KM_PRESS, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", F10KEY, KM_PRESS, KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "use_hide_panels", true); + kmi = WM_keymap_add_item(keymap, "SCREEN_OT_screen_full_area", EVT_ACTIONZONE_FULLSCREEN, 0, 0, 0); + RNA_boolean_set(kmi->ptr, "use_hide_panels", true); + WM_keymap_add_item(keymap, "SCREEN_OT_screenshot", F3KEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "SCREEN_OT_screencast", F3KEY, KM_PRESS, KM_ALT, 0); @@ -4155,7 +4284,8 @@ void ED_keymap_screen(wmKeyConfig *keyconf) /* dropbox for entire window */ lb = WM_dropboxmap_find("Window", 0, 0); WM_dropbox_add(lb, "WM_OT_open_mainfile", open_file_drop_poll, open_file_drop_copy); - + WM_dropbox_add(lb, "UI_OT_drop_color", UI_drop_color_poll, UI_drop_color_copy); + keymap_modal_set(keyconf); } diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index 79ce4f879b7..0fa5f2d9837 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -40,6 +41,7 @@ set(INC_SYS set(SRC paint_cursor.c + paint_curve.c paint_hide.c paint_image.c paint_image_2d.c @@ -63,4 +65,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_sculpt_paint "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/sculpt_paint/SConscript b/source/blender/editors/sculpt_paint/SConscript index b0118fbbf53..233f562fcc7 100644 --- a/source/blender/editors/sculpt_paint/SConscript +++ b/source/blender/editors/sculpt_paint/SConscript @@ -29,11 +29,12 @@ Import ('env') sources = env.Glob('*.c') -defs = [] +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../uvedit', '../../blenfont', diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index b1e4696cd02..e27ef705fad 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -44,6 +44,7 @@ #include "BKE_brush.h" #include "BKE_context.h" +#include "BKE_curve.h" #include "BKE_image.h" #include "BKE_node.h" #include "BKE_paint.h" @@ -58,6 +59,8 @@ #include "ED_view3d.h" +#include "UI_resources.h" + #include "paint_intern.h" /* still needed for sculpt_stroke_get_location, should be * removed eventually (TODO) */ @@ -518,14 +521,15 @@ static int project_brush_radius(ViewContext *vc, } } -static int sculpt_get_brush_geometry(bContext *C, ViewContext *vc, - int x, int y, int *pixel_radius, - float location[3]) +static bool sculpt_get_brush_geometry( + bContext *C, ViewContext *vc, + int x, int y, int *pixel_radius, + float location[3]) { Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); float mouse[2]; - int hit; + bool hit; mouse[0] = x; mouse[1] = y; @@ -756,7 +760,7 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, int x, int y, float zoom, PaintMode mode) { /* color means that primary brush texture is colured and secondary is used for alpha/mask control */ - bool col = ELEM3(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX) ? true : false; + bool col = ELEM(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX) ? true : false; OverlayControlFlags flags = BKE_paint_get_overlay_flags(); /* save lots of GL state * TODO: check on whether all of these are needed? */ @@ -791,6 +795,138 @@ static void paint_draw_alpha_overlay(UnifiedPaintSettings *ups, Brush *brush, glPopAttrib(); } + +BLI_INLINE void draw_tri_point(float *co, float width, bool selected) +{ + float w = width / 2.0f; + if (selected) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + UI_ThemeColor4(TH_PAINT_CURVE_PIVOT); + + glLineWidth(3.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0], co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); + + glColor4f(1.0, 1.0, 1.0, 0.5); + glLineWidth(1.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0], co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); +} + +BLI_INLINE void draw_rect_point(float *co, float width, bool selected) +{ + float w = width / 2.0f; + if (selected) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + UI_ThemeColor4(TH_PAINT_CURVE_HANDLE); + glLineWidth(3.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0] + w, co[1] + w); + glVertex2f(co[0] - w, co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); + + glColor4f(1.0, 1.0, 1.0, 0.5); + glLineWidth(1.0); + + glBegin(GL_LINE_LOOP); + glVertex2f(co[0] + w, co[1] + w); + glVertex2f(co[0] - w, co[1] + w); + glVertex2f(co[0] - w, co[1] - w); + glVertex2f(co[0] + w, co[1] - w); + glEnd(); +} + + +BLI_INLINE void draw_bezier_handle_lines(BezTriple *bez) +{ + short line1[] = {0, 1}; + short line2[] = {1, 2}; + + glVertexPointer(2, GL_FLOAT, 3 * sizeof(float), bez->vec); + glColor4f(0.0, 0.0, 0.0, 0.5); + glLineWidth(3.0); + glDrawArrays(GL_LINE_STRIP, 0, 3); + + glLineWidth(1.0); + if (bez->f1 || bez->f2) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + glColor4f(1.0, 1.0, 1.0, 0.5); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, line1); + if (bez->f3 || bez->f2) + UI_ThemeColor4(TH_VERTEX_SELECT); + else + glColor4f(1.0, 1.0, 1.0, 0.5); + glDrawElements(GL_LINES, 2, GL_UNSIGNED_SHORT, line2); +} + +static void paint_draw_curve_cursor(Brush *brush) +{ + if (brush->paint_curve && brush->paint_curve->points) { + int i; + PaintCurve *pc = brush->paint_curve; + PaintCurvePoint *cp = pc->points; + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glEnableClientState(GL_VERTEX_ARRAY); + + /* draw the bezier handles and the curve segment between the current and next point */ + for (i = 0; i < pc->tot_points - 1; i++, cp++) { + int j; + PaintCurvePoint *cp_next = cp + 1; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + /* use color coding to distinguish handles vs curve segments */ + draw_bezier_handle_lines(&cp->bez); + draw_tri_point(&cp->bez.vec[1][0], 10.0, cp->bez.f2); + draw_rect_point(&cp->bez.vec[0][0], 8.0, cp->bez.f1 || cp->bez.f2); + draw_rect_point(&cp->bez.vec[2][0], 8.0, cp->bez.f3 || cp->bez.f2); + + for (j = 0; j < 2; j++) + BKE_curve_forward_diff_bezier( + cp->bez.vec[1][j], + cp->bez.vec[2][j], + cp_next->bez.vec[0][j], + cp_next->bez.vec[1][j], + data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + + glVertexPointer(2, GL_FLOAT, 0, data); + glLineWidth(3.0); + glColor4f(0.0, 0.0, 0.0, 0.5); + glDrawArrays(GL_LINE_STRIP, 0, PAINT_CURVE_NUM_SEGMENTS + 1); + + glLineWidth(1.0); + glColor4f(0.9, 0.9, 1.0, 0.5); + glDrawArrays(GL_LINE_STRIP, 0, PAINT_CURVE_NUM_SEGMENTS + 1); + } + + /* draw last line segment */ + draw_bezier_handle_lines(&cp->bez); + draw_tri_point(&cp->bez.vec[1][0], 10.0, cp->bez.f2); + draw_rect_point(&cp->bez.vec[0][0], 8.0, cp->bez.f1 || cp->bez.f2); + draw_rect_point(&cp->bez.vec[2][0], 8.0, cp->bez.f3 || cp->bez.f2); + + glLineWidth(1.0); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + glDisableClientState(GL_VERTEX_ARRAY); + } +} + /* Special actions taken when paint cursor goes over mesh */ /* TODO: sculpt only for now */ static void paint_cursor_on_hit(UnifiedPaintSettings *ups, Brush *brush, ViewContext *vc, @@ -848,6 +984,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) zoomx = max_ff(zoomx, zoomy); mode = BKE_paintmode_get_active_from_context(C); + /* skip everything and draw brush here */ + if (brush->flag & BRUSH_CURVE) { + paint_draw_curve_cursor(brush); + return; + } + /* set various defaults */ translation[0] = x; translation[1] = y; @@ -857,8 +999,11 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* 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 */ - if (!ups->stroke_active && (brush->flag & BRUSH_RAKE)) - paint_calculate_rake_rotation(ups, translation); + if (!ups->stroke_active) { + if (brush->flag & BRUSH_RAKE) + /* here, translation contains the mouse coordinates. */ + paint_calculate_rake_rotation(ups, translation); + } /* draw overlay */ paint_draw_alpha_overlay(ups, brush, &vc, x, y, zoomx, mode); @@ -867,7 +1012,8 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) * special mode of drawing will go away */ if ((mode == PAINT_SCULPT) && vc.obact->sculpt) { float location[3]; - int pixel_radius, hit; + int pixel_radius; + bool hit; /* test if brush is over the mesh */ hit = sculpt_get_brush_geometry(C, &vc, x, y, &pixel_radius, location); @@ -878,9 +1024,9 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* check if brush is subtracting, use different color then */ /* TODO: no way currently to know state of pen flip or * invert key modifier without starting a stroke */ - if ((!(brush->flag & BRUSH_INVERTED) ^ + if ((!(ups->draw_inverted) ^ !(brush->flag & BRUSH_DIR_IN)) && - ELEM5(brush->sculpt_tool, SCULPT_TOOL_DRAW, + ELEM(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) { @@ -890,12 +1036,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) /* only do if brush is over the mesh */ if (hit) paint_cursor_on_hit(ups, brush, &vc, location); + } - if (ups->draw_anchored) { - final_radius = ups->anchored_size; - translation[0] = ups->anchored_initial_mouse[0]; - translation[1] = ups->anchored_initial_mouse[1]; - } + if (ups->draw_anchored) { + final_radius = ups->anchored_size; + translation[0] = ups->anchored_initial_mouse[0]; + translation[1] = ups->anchored_initial_mouse[1]; } /* make lines pretty */ diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c new file mode 100644 index 00000000000..8c7c3b102e3 --- /dev/null +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -0,0 +1,787 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/sculpt_paint/paint_curve.c + * \ingroup edsculpt + */ + +#include <string.h> +#include <limits.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_paint.h" + +#include "BLI_math_vector.h" +#include "BLI_string.h" + +#include "ED_paint.h" +#include "ED_view3d.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_view2d.h" + +#include "paint_intern.h" + +#define PAINT_CURVE_SELECT_THRESHOLD 40.0f +#define PAINT_CURVE_POINT_SELECT(pcp, i) (*(&pcp->bez.f1 + i) = SELECT) + + +int paint_curve_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + Paint *p; + RegionView3D *rv3d = CTX_wm_region_view3d(C); + SpaceImage *sima; + + if (rv3d && !(ob && ((ob->mode & OB_MODE_ALL_PAINT) != 0))) + return false; + + sima = CTX_wm_space_image(C); + + if (sima && sima->mode != SI_MODE_PAINT) + return false; + + p = BKE_paint_get_active_from_context(C); + + if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + return true; + } + + return false; +} + +/* Paint Curve Undo*/ + +typedef struct UndoCurve { + struct UndoImageTile *next, *prev; + + PaintCurvePoint *points; /* points of curve */ + int tot_points; + int active_point; + + char idname[MAX_ID_NAME]; /* name instead of pointer*/ +} UndoCurve; + +static void paintcurve_undo_restore(bContext *C, ListBase *lb) +{ + Paint *p = BKE_paint_get_active_from_context(C); + UndoCurve *uc; + PaintCurve *pc = NULL; + + if (p->brush) { + pc = p->brush->paint_curve; + } + + if (!pc) + return; + + uc = (UndoCurve *)lb->first; + + if (strncmp(uc->idname, pc->id.name, BLI_strnlen(uc->idname, sizeof(uc->idname))) == 0) { + SWAP(PaintCurvePoint *, pc->points, uc->points); + SWAP(int, pc->tot_points, uc->tot_points); + SWAP(int, pc->add_index, uc->active_point); + } +} + +static void paintcurve_undo_delete(ListBase *lb) +{ + UndoCurve *uc; + uc = (UndoCurve *)lb->first; + + if (uc->points) + MEM_freeN(uc->points); + uc->points = NULL; +} + + +static void paintcurve_undo_begin(bContext *C, wmOperator *op, PaintCurve *pc) +{ + PaintMode mode = BKE_paintmode_get_active_from_context(C); + ListBase *lb = NULL; + int undo_stack_id; + UndoCurve *uc; + + switch (mode) { + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: + undo_stack_id = UNDO_PAINT_IMAGE; + break; + + case PAINT_SCULPT: + undo_stack_id = UNDO_PAINT_MESH; + break; + + default: + /* do nothing, undo is handled by global */ + return; + } + + + ED_undo_paint_push_begin(undo_stack_id, op->type->name, + paintcurve_undo_restore, paintcurve_undo_delete, NULL); + lb = undo_paint_push_get_list(undo_stack_id); + + uc = MEM_callocN(sizeof(*uc), "Undo_curve"); + + lb->first = uc; + + BLI_strncpy(uc->idname, pc->id.name, sizeof(uc->idname)); + uc->tot_points = pc->tot_points; + uc->active_point = pc->add_index; + uc->points = MEM_dupallocN(pc->points); + + undo_paint_push_count_alloc(undo_stack_id, sizeof(*uc) + sizeof(*pc->points) * pc->tot_points); + + ED_undo_paint_push_end(undo_stack_id); +} +#define SEL_F1 (1 << 0) +#define SEL_F2 (1 << 1) +#define SEL_F3 (1 << 2) + +/* returns 0, 1, or 2 in point according to handle 1, pivot or handle 2 */ +static PaintCurvePoint *paintcurve_point_get_closest(PaintCurve *pc, const float pos[2], bool ignore_pivot, const float threshold, char *point) +{ + PaintCurvePoint *pcp, *closest = NULL; + int i; + float dist, closest_dist = FLT_MAX; + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[0]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F1; + } + } + if (!ignore_pivot) { + dist = len_manhattan_v2v2(pos, pcp->bez.vec[1]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F2; + } + } + } + dist = len_manhattan_v2v2(pos, pcp->bez.vec[2]); + if (dist < threshold) { + if (dist < closest_dist) { + closest = pcp; + closest_dist = dist; + if (point) + *point = SEL_F3; + } + } + } + + return closest; +} + +static int paintcurve_point_co_index(char sel) +{ + char i = 0; + while (sel != 1) { + sel >>= 1; + i++; + } + return i; +} + +/******************* Operators *********************************/ + +static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); + + if (p && p->brush) { + p->brush->paint_curve = BKE_paint_curve_add(bmain, "PaintCurve"); + } + + return OPERATOR_FINISHED; +} + +void PAINTCURVE_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve"; + ot->description = "Add new paint curve"; + ot->idname = "PAINTCURVE_OT_new"; + + /* api callbacks */ + ot->exec = paintcurve_new_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + Main *bmain = CTX_data_main(C); + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + float vec[3] = {loc[0], loc[1], 0.0}; + int add_index; + int i; + + pc = br->paint_curve; + if (!pc) { + br->paint_curve = pc = BKE_paint_curve_add(bmain, "PaintCurve"); + } + + paintcurve_undo_begin(C, op, pc); + + pcp = MEM_mallocN((pc->tot_points + 1) * sizeof(PaintCurvePoint), "PaintCurvePoint"); + add_index = pc->add_index; + + if (pc->points) { + if (add_index > 0) + memcpy(pcp, pc->points, add_index * sizeof(PaintCurvePoint)); + if (add_index < pc->tot_points) + memcpy(pcp + add_index + 1, pc->points + add_index, (pc->tot_points - add_index) * sizeof(PaintCurvePoint)); + + MEM_freeN(pc->points); + } + pc->points = pcp; + pc->tot_points++; + + /* initialize new point */ + memset(&pcp[add_index], 0, sizeof(PaintCurvePoint)); + copy_v3_v3(pcp[add_index].bez.vec[0], vec); + copy_v3_v3(pcp[add_index].bez.vec[1], vec); + copy_v3_v3(pcp[add_index].bez.vec[2], vec); + + /* last step, clear selection from all bezier handles expect the next */ + for (i = 0; i < pc->tot_points; i++) { + pcp[i].bez.f1 = pcp[i].bez.f2 = pcp[i].bez.f3 = 0; + } + pcp[add_index].bez.f3 = SELECT; + pcp[add_index].bez.h2 = HD_ALIGN; + + pc->add_index = add_index + 1; + + WM_paint_cursor_tag_redraw(window, ar); +} + + +static int paintcurve_add_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int loc[2] = {event->mval[0], event->mval[1]}; + paintcurve_point_add(C, op, loc); + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; +} + +static int paintcurve_add_point_exec(bContext *C, wmOperator *op) +{ + int loc[2]; + + if (RNA_struct_property_is_set(op->ptr, "location")) { + RNA_int_get_array(op->ptr, "location", loc); + paintcurve_point_add(C, op, loc); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void PAINTCURVE_OT_add_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve Point"; + ot->description = "Add new paint curve point"; + ot->idname = "PAINTCURVE_OT_add_point"; + + /* api callbacks */ + ot->invoke = paintcurve_add_point_invoke; + ot->exec = paintcurve_add_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, + "Location", "Location of vertex in area space", 0, SHRT_MAX); +} + +static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) +{ + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + PaintCurvePoint *pcp; + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + int i; + int tot_del = 0; + pc = br->paint_curve; + + if (!pc || pc->tot_points == 0) { + return OPERATOR_CANCELLED; + } + + paintcurve_undo_begin(C, op, pc); + +#define DELETE_TAG 2 + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if ((pcp->bez.f1 & SELECT) || (pcp->bez.f2 & SELECT) || (pcp->bez.f3 & SELECT)) { + pcp->bez.f2 |= DELETE_TAG; + tot_del++; + } + } + + if (tot_del > 0) { + int j = 0; + int new_tot = pc->tot_points - tot_del; + PaintCurvePoint *points_new = NULL; + if (new_tot > 0) + points_new = MEM_mallocN(new_tot * sizeof(PaintCurvePoint), "PaintCurvePoint"); + + for (i = 0, pcp = pc->points; i < pc->tot_points; i++, pcp++) { + if (!(pcp->bez.f2 & DELETE_TAG)) { + points_new[j] = pc->points[i]; + + if ((i + 1) == pc->add_index) { + pc->add_index = j + 1; + } + j++; + } + else if ((i + 1) == pc->add_index) { + /* prefer previous point */ + pc->add_index = j; + } + } + MEM_freeN(pc->points); + + pc->points = points_new; + pc->tot_points = new_tot; + } + +#undef DELETE_TAG + + WM_paint_cursor_tag_redraw(window, ar); + + return OPERATOR_FINISHED; +} + + +void PAINTCURVE_OT_delete_point(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Paint Curve Point"; + ot->description = "Add new paint curve point"; + ot->idname = "PAINTCURVE_OT_delete_point"; + + /* api callbacks */ + ot->exec = paintcurve_delete_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; +} + + +static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2], bool toggle, bool extend) +{ + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + Paint *p = BKE_paint_get_active_from_context(C); + Brush *br = p->brush; + PaintCurve *pc; + PaintCurvePoint *pcp; + int i; + const float loc_fl[2] = {UNPACK2(loc)}; + + pc = br->paint_curve; + + if (!pc) + return false; + + paintcurve_undo_begin(C, op, pc); + + pcp = pc->points; + + if (toggle) { + char select = 0; + bool selected = false; + + for (i = 0; i < pc->tot_points; i++) { + if (pcp[i].bez.f1 || pcp[i].bez.f2 || pcp[i].bez.f3) { + selected = true; + break; + } + } + + if (!selected) { + select = SELECT; + } + + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = select; + } + } + else { + PaintCurvePoint *pcp; + char selflag; + + pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); + + if (pcp) { + pc->add_index = (pcp - pc->points) + 1; + + if (selflag == SEL_F2) { + if (extend) + pcp->bez.f2 ^= SELECT; + else + pcp->bez.f2 |= SELECT; + } + else if (selflag == SEL_F1) { + if (extend) + pcp->bez.f1 ^= SELECT; + else + pcp->bez.f1 |= SELECT; + } + else if (selflag == SEL_F3) { + if (extend) + pcp->bez.f3 ^= SELECT; + else + pcp->bez.f3 |= SELECT; + } + } + + /* clear selection for unselected points if not extending and if a point has been selected */ + if (!extend && pcp) { + for (i = 0; i < pc->tot_points; i++) { + pc->points[i].bez.f1 = pc->points[i].bez.f2 = pc->points[i].bez.f3 = 0; + + if ((pc->points + i) == pcp) { + char index = paintcurve_point_co_index(selflag); + PAINT_CURVE_POINT_SELECT(pcp, index); + } + } + } + + if (!pcp) + return false; + } + + WM_paint_cursor_tag_redraw(window, ar); + + return true; +} + + +static int paintcurve_select_point_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int loc[2] = {UNPACK2(event->mval)}; + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + if (paintcurve_point_select(C, op, loc, toggle, extend)) { + RNA_int_set_array(op->ptr, "location", loc); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +static int paintcurve_select_point_exec(bContext *C, wmOperator *op) +{ + int loc[2]; + + if (RNA_struct_property_is_set(op->ptr, "location")) { + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool extend = RNA_boolean_get(op->ptr, "extend"); + RNA_int_get_array(op->ptr, "location", loc); + if (paintcurve_point_select(C, op, loc, toggle, extend)) + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void PAINTCURVE_OT_select(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Paint Curve Point"; + ot->description = "Select a paint curve point"; + ot->idname = "PAINTCURVE_OT_select"; + + /* api callbacks */ + ot->invoke = paintcurve_select_point_invoke; + ot->exec = paintcurve_select_point_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, SHRT_MAX, + "Location", "Location of vertex in area space", 0, SHRT_MAX); + prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "(De)select all"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +typedef struct PointSlideData { + PaintCurvePoint *pcp; + char select; + int initial_loc[2]; + float point_initial_loc[3][2]; + int event; + bool align; +} PointSlideData; + +static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Paint *p = BKE_paint_get_active_from_context(C); + const float loc_fl[2] = {UNPACK2(event->mval)}; + char select; + int i; + bool do_select = RNA_boolean_get(op->ptr, "select"); + bool align = RNA_boolean_get(op->ptr, "align"); + Brush *br = p->brush; + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + + if (!pc) + return OPERATOR_PASS_THROUGH; + + if (do_select) { + pcp = paintcurve_point_get_closest(pc, loc_fl, align, PAINT_CURVE_SELECT_THRESHOLD, &select); + } + else { + pcp = NULL; + /* just find first selected point */ + for (i = 0; i < pc->tot_points; i++) { + if (pc->points[i].bez.f1 || pc->points[i].bez.f2 || pc->points[i].bez.f3) { + pcp = &pc->points[i]; + select = SEL_F3; + break; + } + } + } + + + if (pcp) { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + PointSlideData *psd = MEM_mallocN(sizeof(PointSlideData), "PointSlideData"); + copy_v2_v2_int(psd->initial_loc, event->mval); + psd->event = event->type; + psd->pcp = pcp; + psd->select = paintcurve_point_co_index(select); + for (i = 0; i < 3; i++) { + copy_v2_v2(psd->point_initial_loc[i], pcp->bez.vec[i]); + } + psd->align = align; + op->customdata = psd; + + if (do_select) + paintcurve_undo_begin(C, op, pc); + + /* first, clear all selection from points */ + for (i = 0; i < pc->tot_points; i++) + pc->points[i].bez.f1 = pc->points[i].bez.f3 = pc->points[i].bez.f2 = 0; + + /* only select the active point */ + PAINT_CURVE_POINT_SELECT(pcp, psd->select); + pc->add_index = (pcp - pc->points) + 1; + + WM_event_add_modal_handler(C, op); + WM_paint_cursor_tag_redraw(window, ar); + return OPERATOR_RUNNING_MODAL; + } + + return OPERATOR_PASS_THROUGH; +} + +static int paintcurve_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + PointSlideData *psd = op->customdata; + + if (event->type == psd->event && event->val == KM_RELEASE) { + MEM_freeN(psd); + return OPERATOR_FINISHED; + } + + switch (event->type) { + case MOUSEMOVE: + { + ARegion *ar = CTX_wm_region(C); + wmWindow *window = CTX_wm_window(C); + float diff[2] = {event->mval[0] - psd->initial_loc[0], + event->mval[1] - psd->initial_loc[1]}; + if (psd->select == 1) { + int i; + for (i = 0; i < 3; i++) + add_v2_v2v2(psd->pcp->bez.vec[i], diff, psd->point_initial_loc[i]); + } + else { + add_v2_v2(diff, psd->point_initial_loc[psd->select]); + copy_v2_v2(psd->pcp->bez.vec[psd->select], diff); + + if (psd->align) { + char opposite = (psd->select == 0) ? 2 : 0; + sub_v2_v2v2(diff, psd->pcp->bez.vec[1], psd->pcp->bez.vec[psd->select]); + add_v2_v2v2(psd->pcp->bez.vec[opposite], psd->pcp->bez.vec[1], diff); + } + } + WM_paint_cursor_tag_redraw(window, ar); + break; + } + default: + break; + } + + return OPERATOR_RUNNING_MODAL; +} + + +void PAINTCURVE_OT_slide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Slide Paint Curve Point"; + ot->description = "Select and slide paint curve point"; + ot->idname = "PAINTCURVE_OT_slide"; + + /* api callbacks */ + ot->invoke = paintcurve_slide_invoke; + ot->modal = paintcurve_slide_modal; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "align", false, "Align Handles", "Aligns opposite point handle during transform"); + RNA_def_boolean(ot->srna, "select", true, "Select", "Attempt to select a point handle before transform"); +} + +static int paintcurve_draw_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PaintMode mode = BKE_paintmode_get_active_from_context(C); + const char *name; + + switch (mode) { + case PAINT_TEXTURE_2D: + case PAINT_TEXTURE_PROJECTIVE: + name = "PAINT_OT_image_paint"; + break; + case PAINT_WEIGHT: + name = "PAINT_OT_weight_paint"; + break; + case PAINT_VERTEX: + name = "PAINT_OT_vertex_paint"; + break; + case PAINT_SCULPT: + name = "SCULPT_OT_brush_stroke"; + break; + default: + return OPERATOR_PASS_THROUGH; + } + + return WM_operator_name_call(C, name, WM_OP_INVOKE_DEFAULT, NULL); +} + +void PAINTCURVE_OT_draw(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Draw Curve"; + ot->description = "Draw curve"; + ot->idname = "PAINTCURVE_OT_draw"; + + /* api callbacks */ + ot->exec = paintcurve_draw_exec; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO; +} + +static int paintcurve_cursor_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + PaintMode mode = BKE_paintmode_get_active_from_context(C); + + switch (mode) { + case PAINT_TEXTURE_2D: + { + ARegion *ar = CTX_wm_region(C); + SpaceImage *sima = CTX_wm_space_image(C); + float location[2]; + + if (!sima) + return OPERATOR_CANCELLED; + + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); + copy_v2_v2(sima->cursor, location); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL); + break; + } + default: + ED_view3d_cursor3d_update(C, event->mval); + break; + } + + return OPERATOR_FINISHED; +} + +void PAINTCURVE_OT_cursor(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Cursor"; + ot->description = "Place cursor"; + ot->idname = "PAINTCURVE_OT_cursor"; + + /* api callbacks */ + ot->invoke = paintcurve_cursor_invoke; + ot->poll = paint_curve_poll; + + /* flags */ + ot->flag = 0; +} diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 3d860145f59..f1c91d0fcb5 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -192,14 +192,14 @@ static void partialvis_update_grids(Object *ob, /* skip grid element if not in the effected area */ if (is_effected(area, planes, co, mask)) { /* set or clear the hide flag */ - BLI_BITMAP_MODIFY(gh, y * key.grid_size + x, + BLI_BITMAP_SET(gh, y * key.grid_size + x, action == PARTIALVIS_HIDE); any_changed = true; } /* keep track of whether any elements are still hidden */ - if (BLI_BITMAP_GET(gh, y * key.grid_size + x)) + if (BLI_BITMAP_TEST(gh, y * key.grid_size + x)) any_hidden = true; else any_visible = true; @@ -252,14 +252,14 @@ static void partialvis_update_bmesh_verts(BMesh *bm, } } -static void partialvis_update_bmesh_faces(GSet *faces, PartialVisAction action) +static void partialvis_update_bmesh_faces(GSet *faces) { GSetIterator gs_iter; GSET_ITER (gs_iter, faces) { BMFace *f = BLI_gsetIterator_getKey(&gs_iter); - if ((action == PARTIALVIS_HIDE) && paint_is_bmesh_face_hidden(f)) + if (paint_is_bmesh_face_hidden(f)) BM_elem_flag_enable(f, BM_ELEM_HIDDEN); else BM_elem_flag_disable(f, BM_ELEM_HIDDEN); @@ -301,7 +301,7 @@ static void partialvis_update_bmesh(Object *ob, &any_visible); /* finally loop over node faces and tag the ones that are fully hidden */ - partialvis_update_bmesh_faces(faces, action); + partialvis_update_bmesh_faces(faces); if (any_changed) { BKE_pbvh_node_mark_rebuild_draw(node); @@ -329,7 +329,7 @@ static void clip_planes_from_rect(bContext *C, view3d_set_viewcontext(C, &vc); view3d_get_transformation(vc.ar, vc.rv3d, vc.obact, &mats); ED_view3d_clipping_calc(&bb, clip_planes, &mats, rect); - mul_m4_fl(clip_planes, -1.0f); + negate_m4(clip_planes); } /* If mode is inside, get all PBVH nodes that lie at least partially diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5e2bdcb3c93..021822793ff 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -43,6 +43,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_threads.h" #include "PIL_time.h" @@ -56,12 +57,18 @@ #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" #include "BKE_paint.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" +#include "BKE_colortools.h" #include "BKE_editmesh.h" @@ -69,8 +76,8 @@ #include "ED_image.h" #include "ED_object.h" +#include "ED_paint.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_view3d.h" #include "WM_api.h" @@ -81,6 +88,10 @@ #include "RNA_enum_types.h" #include "GPU_draw.h" +#include "GPU_buffers.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" #include "IMB_colormanagement.h" @@ -102,14 +113,27 @@ typedef struct UndoImageTile { int x, y; + Image *ima; short source, use_float; char gen_type; + bool valid; } UndoImageTile; /* this is a static resource for non-globality, * Maybe it should be exposed as part of the * paint operation, but for now just give a public interface */ static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; +static SpinLock undolock; + +void image_undo_init_locks(void) +{ + BLI_spin_init(&undolock); +} + +void image_undo_end_locks(void) +{ + BLI_spin_end(&undolock); +} ImagePaintPartialRedraw *get_imapaintpartial(void) { @@ -122,26 +146,53 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) } /* UNDO */ +typedef enum { + COPY = 0, + RESTORE = 1, + RESTORE_COPY = 2 +} CopyMode; -static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) +static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + if (mode == COPY) { + /* copy or swap contents of tile->rect and region in ibuf->rect */ + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } } else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - if (restore) + if (mode == RESTORE_COPY) + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + /* swap to the tmpbuf for easy copying */ + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE, tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + + if (mode == RESTORE) { + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + } + } } -void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask) +void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -160,6 +211,8 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi *mask = tile->mask; } + if (validate) + tile->valid = true; return tile->rect.pt; } @@ -170,7 +223,7 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi return NULL; } -void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -179,10 +232,14 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, void *data; /* check if tile is already pushed */ - data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, NULL); - if (data) - return data; - + + /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ + if (!proj) { + data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); + if (data) + return data; + } + if (*tmpibuf == NULL) *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); @@ -191,6 +248,11 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->x = x_tile; tile->y = y_tile; + /* add mask explicitly here */ + if (mask) + *mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, + "UndoImageTile.mask"); + allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); @@ -200,12 +262,23 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->gen_type = ima->gen_type; tile->source = ima->source; tile->use_float = use_float; + tile->valid = true; + tile->ima = ima; - undo_copy_tile(tile, *tmpibuf, ibuf, 0); - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); + if (valid) + *valid = &tile->valid; + undo_copy_tile(tile, *tmpibuf, ibuf, COPY); + + if (proj) + BLI_spin_lock(&undolock); + + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); BLI_addtail(lb, tile); - + + if (proj) + BLI_spin_unlock(&undolock); + return tile->rect.pt; } @@ -222,6 +295,33 @@ void image_undo_remove_masks(void) } } +static void image_undo_restore_runtime(ListBase *lb) +{ + ImBuf *ibuf, *tmpibuf; + UndoImageTile *tile; + + tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, + IB_rectfloat | IB_rect); + + for (tile = lb->first; tile; tile = tile->next) { + Image *ima = tile->ima; + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); + + GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ + if (ibuf->rect_float) + ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + if (ibuf->mipmap[0]) + ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + IMB_freeImBuf(tmpibuf); +} + void ED_image_undo_restore(bContext *C, ListBase *lb) { Main *bmain = CTX_data_main(C); @@ -247,7 +347,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) if (ima && ibuf && strcmp(tile->ibufname, ibuf->name) != 0) { /* current ImBuf filename was changed, probably current frame - * was changed when paiting on image sequence, rather than storing + * was changed when painting on image sequence, rather than storing * full image user (which isn't so obvious, btw) try to find ImBuf with * matched file name in list of already loaded images */ @@ -273,7 +373,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) continue; } - undo_copy_tile(tile, tmpibuf, ibuf, 1); + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); GPU_free_image(ima); /* force OpenGL reload */ if (ibuf->rect_float) @@ -282,6 +382,8 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + DAG_id_tag_update(&ima->id, 0); + BKE_image_release_ibuf(ima, ibuf, NULL); } @@ -296,6 +398,42 @@ void ED_image_undo_free(ListBase *lb) MEM_freeN(tile->rect.pt); } +static void image_undo_end(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; + int deallocsize = 0; + int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; + + /* first dispose of invalid tiles (may happen due to drag dot for instance) */ + for (tile = lb->first; tile;) { + if (!tile->valid) { + UndoImageTile *tmp_tile = tile->next; + deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); + MEM_freeN(tile->rect.pt); + BLI_freelinkN (lb, tile); + tile = tmp_tile; + } + else { + tile = tile->next; + } + } + + /* don't forget to remove the size of deallocated tiles */ + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +} + +static void image_undo_invalidate(void) +{ + UndoImageTile *tile; + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + + for (tile = lb->first; tile; tile = tile->next) + tile->valid = false; +} + /* Imagepaint Partial Redraw & Dirty Region */ void ED_imapaint_clear_partial_redraw(void) @@ -344,7 +482,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int for (ty = tiley; ty <= tileh; ty++) for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty); + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false); ibuf->userflags |= IB_BITMAPDIRTY; @@ -373,6 +511,79 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te } } +/* paint blur kernels. Projective painting enforces use of a 2x2 kernel due to lagging */ +BlurKernel *paint_new_blur_kernel(Brush *br, bool proj) +{ + int i, j; + BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + float radius; + int side; + BlurKernelType type = br->blur_mode; + + if (proj) { + radius = 0.5f; + + side = kernel->side = 2; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = radius; + } + else { + if (br->blur_kernel_radius <= 0) + br->blur_kernel_radius = 1; + + radius = br->blur_kernel_radius; + + side = kernel->side = radius * 2 + 1; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = br->blur_kernel_radius; + } + + switch (type) { + case KERNEL_BOX: + for (i = 0; i < kernel->side_squared; i++) + kernel->wdata[i] = 1.0; + break; + + case KERNEL_GAUSSIAN: + { + /* at 3.0 standard deviations distance, kernel is about zero */ + float standard_dev = radius / 3.0f; + + /* make the necessary adjustment to the value for use in the normal distribution formula */ + standard_dev = -standard_dev * standard_dev * 2; + + for (i = 0; i < side; i++) { + for (j = 0; j < side; j++) { + float idist = radius - i; + float jdist = radius - j; + float value = exp((idist * idist + jdist * jdist) / standard_dev); + + kernel->wdata[i + j * side] = value; + } + } + + break; + } + + default: + printf("unidentified kernel type, aborting\n"); + MEM_freeN(kernel->wdata); + MEM_freeN(kernel); + return NULL; + break; + } + + return kernel; +} + +void paint_delete_blur_kernel(BlurKernel *kernel) +{ + if (kernel->wdata) + MEM_freeN(kernel->wdata); +} + /************************ image paint poll ************************/ static Brush *image_paint_brush(bContext *C) @@ -385,11 +596,12 @@ static Brush *image_paint_brush(bContext *C) static int image_paint_poll(bContext *C) { - Object *obact = CTX_data_active_object(C); + Object *obact; if (!image_paint_brush(C)) return 0; + obact = CTX_data_active_object(C); if ((obact && obact->mode & OB_MODE_TEXTURE_PAINT) && CTX_wm_region_view3d(C)) { return 1; } @@ -432,11 +644,57 @@ typedef struct PaintOperation { void *custom_paint; float prevmouse[2]; + float startmouse[2]; double starttime; + void *cursor; ViewContext vc; } PaintOperation; +bool paint_use_opacity_masking(Brush *brush) +{ + return (brush->flag & BRUSH_AIRBRUSH) || + (brush->flag & BRUSH_DRAG_DOT) || + (brush->flag & BRUSH_ANCHORED) || + (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || + (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) || + (brush->imagepaint_tool == PAINT_TOOL_FILL) || + (brush->flag & BRUSH_USE_GRADIENT) || + (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D)) ? + false : true; +} + +void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, + float pressure, float color[3], struct ColorManagedDisplay *display) +{ + if (invert) + copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br)); + else { + if (br->flag & BRUSH_USE_GRADIENT) { + switch (br->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + do_colorband(br->gradient, pressure, color); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: + { + float coord = fmod(distance / br->gradient_spacing, 1.0); + do_colorband(br->gradient, coord, color); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: + { + do_colorband(br->gradient, distance / br->gradient_spacing, color); + break; + } + } + } + else + copy_v3_v3(color, BKE_brush_color_get(scene, br)); + } + if (color_correction) + IMB_colormanagement_display_to_scene_linear_v3(color, display); +} + void paint_brush_init_tex(Brush *brush) { /* init mtex nodes */ @@ -462,26 +720,56 @@ void paint_brush_exit_tex(Brush *brush) } } +static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +{ + PaintOperation *pop = (PaintOperation *)customdata; + + if (pop) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glLineWidth(4.0); + glColor4ub(0, 0, 0, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(2.0); + glColor4ub(255, 255, 255, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(1.0); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } +} -static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mouse[2]) + +static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); int mode = RNA_enum_get(op->ptr, "mode"); view3d_set_viewcontext(C, &pop->vc); - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); /* initialize from context */ if (CTX_wm_region_view3d(C)) { + Object *ob = OBACT; + bool uvs, mat, tex, stencil; + if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, &stencil)) { + BKE_paint_data_warning(op->reports, uvs, mat, tex, stencil); + MEM_freeN(pop); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + return NULL; + } pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, OBACT, pop->prevmouse, mode); + pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); } else { pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op); + pop->custom_paint = paint_2d_new_stroke(C, op, mode); } if (!pop->custom_paint) { @@ -489,54 +777,75 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mou return NULL; } + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, gradient_draw_line, pop); + } + settings->imapaint.flag |= IMAGEPAINT_DRAWING; ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); - - { - UnifiedPaintSettings *ups = &settings->unified_paint_settings; - ups->stroke_active = true; - } + ED_image_undo_restore, ED_image_undo_free, NULL); return pop; } +/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ +static void paint_stroke_restore(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + image_undo_restore_runtime(lb); + image_undo_invalidate(); +} + static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { PaintOperation *pop = paint_stroke_mode_data(stroke); Scene *scene = CTX_data_scene(C); - Brush *brush = BKE_paint_brush(&scene->toolsettings->imapaint.paint); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startsize = (float)BKE_brush_size_get(scene, brush); float startalpha = BKE_brush_alpha_get(scene, brush); float mouse[2]; float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); int eraser; RNA_float_get_array(itemptr, "mouse", mouse); pressure = RNA_float_get(itemptr, "pressure"); eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = max_ff(1.0f, RNA_float_get(itemptr, "size")); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } if (BKE_brush_use_alpha_pressure(scene, brush)) - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure)); - if (BKE_brush_use_size_pressure(scene, brush)) - BKE_brush_size_set(scene, brush, (int)max_ff(1.0f, startsize * pressure)); + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + else + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + paint_stroke_restore(); + } if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse); + paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); } else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser); + paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); } - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); /* restore brush values */ BKE_brush_alpha_set(scene, brush, startalpha); - BKE_brush_size_set(scene, brush, startsize); } static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) @@ -554,11 +863,41 @@ static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, b static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) { Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; + ToolSettings *toolsettings = scene->toolsettings; PaintOperation *pop = paint_stroke_mode_data(stroke); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + if (pop->mode == PAINT_MODE_2D) { + paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, paint_stroke_flipped(stroke), + 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + else { + if (pop->mode == PAINT_MODE_2D) { + float color[3]; + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, paint_stroke_flipped(stroke), + 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + } if (pop->mode == PAINT_MODE_3D_PROJECT) { paint_proj_stroke_done(pop->custom_paint); } @@ -566,7 +905,11 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) paint_2d_stroke_done(pop->custom_paint); } - ED_undo_paint_push_end(UNDO_PAINT_IMAGE); + if (pop->cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor); + } + + image_undo_end(); /* duplicate warning, see texpaint_init */ #if 0 @@ -576,43 +919,41 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile); #endif MEM_freeN(pop); - - { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->stroke_active = false; - } -} - -static bool paint_stroke_test_start(bContext *UNUSED(C), wmOperator *UNUSED(op), const float UNUSED(mouse[2])) -{ - return true; } - -static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { PaintOperation *pop; - float mouse[2]; - int retval; /* TODO Should avoid putting this here. Instead, last position should be requested * from stroke system. */ - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; if (!(pop = texture_paint_init(C, op, mouse))) { - return OPERATOR_CANCELLED; + return false; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + paint_stroke_set_mode_data(op->customdata, pop); + + return true; +} + + +static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, event->type); - paint_stroke_set_mode_data(op->customdata, pop); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -637,12 +978,10 @@ static int paint_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, 0); - paint_stroke_set_mode_data(op->customdata, pop); - /* frees op->customdata */ paint_stroke_exec(C, op); @@ -651,12 +990,6 @@ static int paint_exec(bContext *C, wmOperator *op) void PAINT_OT_image_paint(wmOperatorType *ot) { - static EnumPropertyItem stroke_mode_items[] = { - {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally"}, - {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, - {0} - }; - /* identifiers */ ot->name = "Image Paint"; ot->idname = "PAINT_OT_image_paint"; @@ -672,11 +1005,7 @@ void PAINT_OT_image_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; - RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, - "Paint Stroke Mode", - "Action taken when a paint stroke is made"); - - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + paint_stroke_operator_properties(ot); } @@ -686,9 +1015,9 @@ int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) if (!rv3d) { SpaceImage *sima = CTX_wm_space_image(C); - ARegion *ar = CTX_wm_region(C); if (sima->mode == SI_MODE_PAINT) { + ARegion *ar = CTX_wm_region(C); ED_space_image_get_zoom(sima, ar, zoomx, zoomy); return 1; @@ -847,17 +1176,39 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) typedef struct { bool show_cursor; short event_type; -} SampleColorData; + float initcolor[3]; + bool sample_palette; +} SampleColorData; + + +static void sample_color_update_header(SampleColorData *data, bContext *C) +{ +#define HEADER_LENGTH 150 + char msg[HEADER_LENGTH]; + ScrArea *sa = CTX_wm_area(C); + + if (sa) { + BLI_snprintf(msg, HEADER_LENGTH, + "Sample color for %s", + !data->sample_palette ? + "Brush. Use Left Click to sample for palette instead" : + "Palette. Use Left Click to sample more colors"); + ED_area_headerprint(sa, msg); + } + +#undef HEADER_LENGTH +} static int sample_color_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); ARegion *ar = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); bool show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); int location[2]; - + bool use_palette; paint->flags &= ~PAINT_SHOW_BRUSH; /* force redraw without cursor */ @@ -865,7 +1216,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) WM_redraw_windows(C); RNA_int_get_array(op->ptr, "location", location); - paint_sample_color(C, ar, location[0], location[1]); + use_palette = RNA_boolean_get(op->ptr, "palette"); + + paint_sample_color(C, ar, location[0], location[1], mode == PAINT_TEXTURE_PROJECTIVE, use_palette); if (show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; @@ -878,7 +1231,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); + PaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); ARegion *ar = CTX_wm_region(C); @@ -886,18 +1241,24 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event data->event_type = event->type; data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); + copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); + data->sample_palette = false; op->customdata = data; paint->flags &= ~PAINT_SHOW_BRUSH; + sample_color_update_header(data, C); + + WM_event_add_modal_handler(C, op); + /* force redraw without cursor */ WM_paint_cursor_tag_redraw(win, ar); WM_redraw_windows(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); - WM_event_add_modal_handler(C, op); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); return OPERATOR_RUNNING_MODAL; @@ -905,17 +1266,27 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); SampleColorData *data = op->customdata; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { + ScrArea *sa = CTX_wm_area(C); + if (data->show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; } + if (data->sample_palette) { + BKE_brush_color_set(scene, brush, data->initcolor); + RNA_boolean_set(op->ptr, "palette", true); + } WM_cursor_modal_restore(CTX_wm_window(C)); MEM_freeN(data); + ED_area_headerprint(sa, NULL); + return OPERATOR_FINISHED; } @@ -924,10 +1295,23 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); break; } + + case LEFTMOUSE: + if (event->val == KM_PRESS) { + ARegion *ar = CTX_wm_region(C); + RNA_int_set_array(op->ptr, "location", event->mval); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, true); + if (!data->sample_palette) { + data->sample_palette = true; + sample_color_update_header(data, C); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); + } + break; } return OPERATOR_RUNNING_MODAL; @@ -956,6 +1340,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot) /* properties */ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "Cursor location in region coordinates", 0, 16384); + RNA_def_boolean(ot->srna, "palette", 0, "Palette", "Add color to palette"); } /******************** texture paint toggle operator ********************/ @@ -979,7 +1364,6 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); const int mode_flag = OB_MODE_TEXTURE_PAINT; const bool is_mode_set = (ob->mode & mode_flag) != 0; - Mesh *me; if (!is_mode_set) { if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { @@ -987,8 +1371,6 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) } } - me = BKE_mesh_from_object(ob); - if (ob->mode & mode_flag) { ob->mode &= ~mode_flag; @@ -999,12 +1381,47 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) toggle_paint_cursor(C, 0); } else { + bScreen *sc; + Main *bmain = CTX_data_main(C); + Image *ima = NULL; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + + /* This has to stay here to regenerate the texture paint + * cache in case we are loading a file */ + BKE_texpaint_slots_refresh_object(scene, ob); + + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + + /* entering paint mode also sets image to editors */ + if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + Material *ma = give_current_material(ob, ob->actcol); /* set the current material active paint slot on image editor */ + + if (ma && ma->texpaintslot) + ima = ma->texpaintslot[ma->paint_active_slot].ima; + } + else if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + ima = imapaint->canvas; + } + + if (ima) { + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + + if (!sima->pin) + ED_space_image_set(sima, scene, scene->obedit, ima); + } + } + } + } + } + ob->mode |= mode_flag; - if (me->mtface == NULL) - me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, - NULL, me->totface); - BKE_paint_init(&scene->toolsettings->imapaint.paint, PAINT_CURSOR_TEXTURE_PAINT); if (U.glreslimit != 0) @@ -1014,7 +1431,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) toggle_paint_cursor(C, 1); } - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + GPU_drawobject_free(ob->derivedFinal); WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); return OPERATOR_FINISHED; @@ -1035,6 +1452,65 @@ void PAINT_OT_texture_paint_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *br = image_paint_brush(C); + if (ups->flag & UNIFIED_PAINT_COLOR) { + swap_v3_v3(ups->rgb, ups->secondary_rgb); + } + else if (br) { + swap_v3_v3(br->rgb, br->secondary_rgb); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); + + return OPERATOR_FINISHED; +} + +static int brush_colors_flip_poll(bContext *C) +{ + if (image_paint_poll(C)) { + Brush *br = image_paint_brush(C); + if (br->imagepaint_tool == PAINT_TOOL_DRAW) + return 1; + } + + return 0; +} + +void PAINT_OT_brush_colors_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Brush Colors Flip"; + ot->idname = "PAINT_OT_brush_colors_flip"; + ot->description = "Toggle foreground and background brush colors"; + + /* api callbacks */ + ot->exec = brush_colors_flip_exec; + ot->poll = brush_colors_flip_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + + ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, + ED_image_undo_restore, ED_image_undo_free, NULL); + + paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); + + DAG_id_tag_update(&ima->id, 0); +} + + static int texture_paint_poll(bContext *C) { if (texture_paint_toggle_poll(C)) diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 667b487d4b1..ea0e30a6635 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -40,14 +40,21 @@ #include "BLI_math.h" +#include "BLI_rect.h" +#include "BLI_math_color_blend.h" +#include "BLI_stack.h" +#include "BLI_bitmap.h" + #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_texture.h" +#include "ED_paint.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -69,24 +76,25 @@ /* Defines and Structs */ typedef struct BrushPainterCache { - int size; /* size override, if 0 uses 2*BKE_brush_size_get(brush) */ - bool use_float; /* need float imbuf? */ bool use_color_correction; /* use color correction for float */ - bool use_masking; /* use masking? */ + bool invert; bool is_texbrush; bool is_maskbrush; - int lastsize; - float lastalpha; - float lastjitter; + int lastdiameter; float last_tex_rotation; float last_mask_rotation; + float last_pressure; ImBuf *ibuf; ImBuf *texibuf; - unsigned short *mask; + unsigned short *curve_mask; + unsigned short *tex_mask; + unsigned short *tex_mask_old; + unsigned int tex_mask_old_w; + unsigned int tex_mask_old_h; } BrushPainterCache; typedef struct BrushPainter { @@ -136,43 +144,42 @@ typedef struct ImagePaintState { int do_facesel; bool need_redraw; + + BlurKernel *blurkernel; } ImagePaintState; -static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush) +static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool invert) { BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); painter->brush = brush; painter->scene = scene; painter->firsttouch = 1; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + painter->cache.invert = invert; return painter; } -static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction, bool use_masking) +static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction) { Brush *brush = painter->brush; if ((painter->cache.use_float != use_float)) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.mask) MEM_freeN(painter->cache.mask); + if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); painter->cache.ibuf = NULL; - painter->cache.mask = NULL; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ - } - - if (painter->cache.use_float != use_float) { - if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - painter->cache.texibuf = NULL; - painter->cache.lastsize = -1; /* force ibuf create in refresh */ + painter->cache.curve_mask = NULL; + painter->cache.tex_mask = NULL; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ } painter->cache.use_float = use_float; painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.use_masking = use_masking; painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; } @@ -181,7 +188,9 @@ static void brush_painter_2d_free(BrushPainter *painter) { if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.mask) MEM_freeN(painter->cache.mask); + if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); MEM_freeN(painter); } @@ -192,41 +201,177 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) texco[2] = 0.0f; } -/* create a mask with the falloff strength and optionally brush alpha */ -static unsigned short *brush_painter_mask_new(BrushPainter *painter, int size) +/* create a mask with the mask texture */ +static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) { Scene *scene = painter->scene; Brush *brush = painter->brush; - bool use_masking = painter->cache.use_masking; - - float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush); - int radius = BKE_brush_size_get(scene, brush); - int xoff = -size * 0.5f + 0.5f; - int yoff = -size * 0.5f + 0.5f; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + float texco[3]; unsigned short *mask, *m; - int x, y; + int x, y, thread = 0; - mask = MEM_callocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); + mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); m = mask; for (y = 0; y < size; y++) { for (x = 0; x < size; x++, m++) { + float res; + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); + *m = (unsigned short)(65535.0f * res); + } + } + + return mask; +} + +/* update rectangular section of the brush image */ +static void brush_painter_mask_imbuf_update( + BrushPainter *painter, unsigned short *tex_mask_old, + int origx, int origy, int w, int h, int xt, int yt, int diameter) +{ + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf tex_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + unsigned short res; + + bool use_texture_old = (tex_mask_old != NULL); + + int x, y, thread = 0; + + unsigned short *tex_mask = painter->cache.tex_mask; + unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture */ + float texco[3]; + + /* handle byte pixel */ + unsigned short *b = tex_mask + (y * diameter + x); + unsigned short *t = tex_mask_cur + (y * diameter + x); + + if (!use_texture_old) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + res = (unsigned short)(65535.0f * BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); + } + + /* read from old texture buffer */ + if (use_texture_old) { + res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + } + + /* write to new texture mask */ + *t = res; + /* write to mask image buffer */ + *b = res; + } + } +} + + +/** + * Update the brush mask image by trying to reuse the cached texture result. + * This can be considerably faster for brushes that change size due to pressure or + * textures that stick to the surface where only part of the pixels are new + */ +static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +{ + BrushPainterCache *cache = &painter->cache; + unsigned short *tex_mask_old; + int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + if (!cache->tex_mask) + cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + + /* create new texture image buffer with coordinates relative to old */ + tex_mask_old = cache->tex_mask_old; + cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + + if (tex_mask_old) { + ImBuf maskibuf; + ImBuf maskibuf_old; + maskibuf.x = maskibuf.y = diameter; + maskibuf_old.x = cache->tex_mask_old_w; + maskibuf_old.y = cache->tex_mask_old_h; + + srcx = srcy = 0; + w = cache->tex_mask_old_w; + h = cache->tex_mask_old_h; + destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); + desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); + + /* hack, use temporary rects so that clipping works */ + IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, diameter); + y1 = min_ii(desty, diameter); + x2 = min_ii(destx + w, diameter); + y2 = min_ii(desty + h, diameter); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + + if (tex_mask_old) + MEM_freeN(tex_mask_old); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); + if ((x2 < diameter) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + if ((x1 < x2) && (0 < y1)) + brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); + if ((x1 < x2) && (y2 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + + /* through with sampling, now update sizes */ + cache->tex_mask_old_w = diameter; + cache->tex_mask_old_h = diameter; +} + +/* create a mask with the falloff strength */ +static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius) +{ + Brush *brush = painter->brush; + + int xoff = -diameter * 0.5f + 0.5f; + int yoff = -diameter * 0.5f + 0.5f; + + unsigned short *mask, *m; + int x, y; + + mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + m = mask; + + for (y = 0; y < diameter; y++) { + for (x = 0; x < diameter; x++, m++) { float xy[2] = {x + xoff, y + yoff}; float len = len_v2(xy); - float strength = alpha; - - strength *= BKE_brush_curve_strength_clamp(brush, len, radius); - *m = (unsigned short)(65535.0f * strength); + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamp(brush, len, radius)); } } return mask; } + /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance) { Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -235,19 +380,11 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; - rctf mask_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; - bool use_masking = painter->cache.use_masking; bool use_color_correction = painter->cache.use_color_correction; bool use_float = painter->cache.use_float; bool is_texbrush = painter->cache.is_texbrush; - bool is_maskbrush = painter->cache.is_maskbrush; - - float alpha = (use_masking) ? 1.0f : BKE_brush_alpha_get(scene, brush); - int radius = BKE_brush_size_get(scene, brush); - int xoff = -size * 0.5f + 0.5f; - int yoff = -size * 0.5f + 0.5f; int x, y, thread = 0; float brush_rgb[3]; @@ -257,11 +394,7 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - copy_v3_v3(brush_rgb, brush->rgb); - - if (use_color_correction) { - IMB_colormanagement_display_to_scene_linear_v3(brush_rgb, display); - } + paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -278,30 +411,17 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size) if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); + mul_v3_v3(rgba, brush_rgb); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); } - mul_v3_v3(rgba, brush_rgb); } else { copy_v3_v3(rgba, brush_rgb); rgba[3] = 1.0f; } - if (is_maskbrush) { - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - } - - /* when not using masking, multiply in falloff and strength */ - if (!use_masking) { - float xy[2] = {x + xoff, y + yoff}; - float len = len_v2(xy); - - rgba[3] *= alpha * BKE_brush_curve_strength_clamp(brush, len, radius); - } - if (use_float) { /* write to float pixel */ float *dstf = ibuf->rect_float + (y * size + x) * 4; @@ -332,14 +452,11 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); rctf tex_mapping = painter->tex_mapping; - rctf mask_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; - bool use_masking = painter->cache.use_masking; bool use_color_correction = painter->cache.use_color_correction; bool use_float = painter->cache.use_float; bool is_texbrush = painter->cache.is_texbrush; - bool is_maskbrush = painter->cache.is_maskbrush; bool use_texture_old = (oldtexibuf != NULL); int x, y, thread = 0; @@ -347,15 +464,10 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, ImBuf *ibuf = painter->cache.ibuf; ImBuf *texibuf = painter->cache.texibuf; - unsigned short *mask = painter->cache.mask; /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - copy_v3_v3(brush_rgb, brush->rgb); - - if (use_color_correction) { - IMB_colormanagement_display_to_scene_linear_v3(brush_rgb, display); - } + paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -363,7 +475,7 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, brush_rgb[2] = 1.0f; } - /* fill pixes */ + /* fill pixels */ for (y = origy; y < h; y++) { for (x = origx; x < w; x++) { /* sample texture and multiply with brush color */ @@ -373,21 +485,16 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, if (is_texbrush) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); BKE_brush_sample_tex_3D(scene, brush, texco, rgba, thread, pool); + mul_v3_v3(rgba, brush_rgb); /* TODO(sergey): Support texture paint color space. */ if (!use_float) { IMB_colormanagement_scene_linear_to_display_v3(rgba, display); } - mul_v3_v3(rgba, brush_rgb); } else { copy_v3_v3(rgba, brush_rgb); rgba[3] = 1.0f; } - - if (is_maskbrush) { - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - rgba[3] *= BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - } } if (use_float) { @@ -404,12 +511,6 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, /* write to new texture buffer */ copy_v4_v4(tf, rgba); - /* if not using masking, multiply in the mask now */ - if (!use_masking) { - unsigned short *m = mask + (y * ibuf->x + x); - rgba[3] *= *m * (1.0f / 65535.0f); - } - /* output premultiplied float image, mf was already premultiplied */ mul_v3_v3fl(bf, rgba, rgba[3]); bf[3] = rgba[3]; @@ -438,12 +539,6 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, t[2] = crgba[2]; t[3] = crgba[3]; - /* if not using masking, multiply in the mask now */ - if (!use_masking) { - unsigned short *m = mask + (y * ibuf->x + x); - crgba[3] = (crgba[3] * (*m)) / 65535; - } - /* write to brush image buffer */ b[0] = crgba[0]; b[1] = crgba[1]; @@ -457,14 +552,11 @@ static void brush_painter_imbuf_update(BrushPainter *painter, ImBuf *oldtexibuf, /* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2]) +static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) { - const Scene *scene = painter->scene; - Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - int diameter = 2 * BKE_brush_size_get(scene, brush); /* create brush image buffer if it didn't exist yet */ imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; @@ -478,10 +570,10 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa if (oldtexibuf) { srcx = srcy = 0; - destx = (int)painter->lastpaintpos[0] - (int)pos[0]; - desty = (int)painter->lastpaintpos[1] - (int)pos[1]; w = oldtexibuf->x; h = oldtexibuf->y; + destx = (int)painter->lastpaintpos[0] - (int)pos[0] + (diameter / 2 - w / 2); + desty = (int)painter->lastpaintpos[1] - (int)pos[1] + (diameter / 2 - h / 2); IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } @@ -514,7 +606,7 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { float invw = 1.0f / (float)s->canvas->x; float invh = 1.0f / (float)s->canvas->y; @@ -522,19 +614,19 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const flo int ipos[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - size / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - size / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); if (mapmode == MTEX_MAP_MODE_STENCIL) { /* map from view coordinates of brush to region coordinates */ UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region(s->v2d, (ipos[0] + size) * invw, (ipos[1] + size) * invh, &xmax, &ymax); + UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ mapping->xmin = xmin; mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)size; - mapping->ymax = (ymax - ymin) / (float)size; + mapping->xmax = (xmax - xmin) / (float)diameter; + mapping->ymax = (ymax - ymin) / (float)diameter; } else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1 */ @@ -545,104 +637,126 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int size, const flo } else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { /* view mapping */ - mapping->xmin = mouse[0] - size * 0.5f + 0.5f; - mapping->ymin = mouse[1] - size * 0.5f + 0.5f; + mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; + mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = -size * 0.5f + 0.5f + (int)pos[0] - (int)startpos[0]; - mapping->ymin = -size * 0.5f + 0.5f + (int)pos[1] - (int)startpos[1]; + mapping->xmin = (int)(-diameter * 0.5) + (int)pos[0] - (int)startpos[0]; + mapping->ymin = (int)(-diameter * 0.5) + (int)pos[1] - (int)startpos[1]; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2]) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * BKE_brush_size_get(scene, brush); - const int size = (cache->size) ? cache->size : diameter; - const float alpha = BKE_brush_alpha_get(scene, brush); - const bool use_masking = painter->cache.use_masking; + const int diameter = 2 * size; bool do_random = false; bool do_partial_update = false; - bool do_view = false; + bool update_color = (brush->flag & BRUSH_USE_GRADIENT) && + ((ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP)) || + (cache->last_pressure != pressure)); float tex_rotation = -brush->mtex.rot; float mask_rotation = -brush->mask_mtex.rot; + painter->pool = BKE_image_pool_new(); + /* determine how can update based on textures used */ if (painter->cache.is_texbrush) { if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - do_view = true; tex_rotation += ups->brush_rotation; } else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) do_random = true; - else + else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) do_partial_update = true; - brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, + brush_painter_2d_tex_mapping(s, diameter, painter->startpaintpos, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } if (painter->cache.is_maskbrush) { + bool renew_maxmask = false; + bool do_partial_update_mask = false; + /* invalidate case for all mapping modes */ if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - do_view = true; mask_rotation += ups->brush_rotation; } - else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - do_random = true; - else - do_partial_update = true; + else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { + renew_maxmask = true; + } + else if (!(brush->flag & BRUSH_ANCHORED)) { + do_partial_update_mask = true; + renew_maxmask = true; + } + /* explicilty disable partial update even if it has been enabled above */ + if (brush->mask_pressure) { + do_partial_update_mask = false; + renew_maxmask = true; + } + + if ((diameter != cache->lastdiameter) || + (mask_rotation != cache->last_mask_rotation) || + renew_maxmask) + { + if (cache->tex_mask) { + MEM_freeN(cache->tex_mask); + cache->tex_mask = NULL; + } - brush_painter_2d_tex_mapping(s, size, painter->startpaintpos, pos, mouse, - brush->mask_mtex.brush_map_mode, &painter->mask_mapping); + brush_painter_2d_tex_mapping(s, diameter, painter->startpaintpos, pos, mouse, + brush->mask_mtex.brush_map_mode, &painter->mask_mapping); + + if (do_partial_update_mask) + brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + else + cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); + cache->last_mask_rotation = mask_rotation; + } } - if (do_view || do_random) - do_partial_update = false; + /* curve mask can only change if the size changes */ + if (diameter != cache->lastdiameter) { + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); + cache->curve_mask = NULL; + } - painter->pool = BKE_image_pool_new(); + cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); + } /* detect if we need to recreate image brush buffer */ - if (diameter != cache->lastsize || - alpha != cache->lastalpha || - brush->jitter != cache->lastjitter || - tex_rotation != cache->last_tex_rotation || - mask_rotation != cache->last_mask_rotation || - do_random) + if ((diameter != cache->lastdiameter) || + (tex_rotation != cache->last_tex_rotation) || + do_random || + update_color) { if (cache->ibuf) { IMB_freeImBuf(cache->ibuf); cache->ibuf = NULL; } - if (cache->mask) { - MEM_freeN(cache->mask); - cache->mask = NULL; - } if (do_partial_update) { - /* do partial update of texture + recreate mask */ - cache->mask = brush_painter_mask_new(painter, size); - brush_painter_imbuf_partial_update(painter, pos); + /* do partial update of texture */ + brush_painter_imbuf_partial_update(painter, pos, diameter); } else { - /* create brush and mask from scratch */ - if (use_masking) - cache->mask = brush_painter_mask_new(painter, size); - cache->ibuf = brush_painter_imbuf_new(painter, size); + /* create brush from scratch */ + cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); } - cache->lastsize = diameter; - cache->lastalpha = alpha; - cache->lastjitter = brush->jitter; + cache->lastdiameter = diameter; cache->last_tex_rotation = tex_rotation; - cache->last_mask_rotation = mask_rotation; + cache->last_pressure = pressure; } else if (do_partial_update) { /* do only partial update of texture */ @@ -650,7 +764,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai int dy = (int)painter->lastpaintpos[1] - (int)pos[1]; if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos); + brush_painter_imbuf_partial_update(painter, pos, diameter); } } @@ -703,7 +817,7 @@ static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus } } -static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus) +static float paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, float *outrgb, short torus, float w) { float inrgb[4]; @@ -716,16 +830,23 @@ static int paint_2d_ibuf_add_if(ImBuf *ibuf, unsigned int x, unsigned int y, flo paint_2d_ibuf_rgb_get(ibuf, x, y, 0, inrgb); } + mul_v4_fl(inrgb, w); add_v4_v4(outrgb, inrgb); - return 1; + return w; } -static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool is_torus) +static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short is_torus) { - int x, y, count, xi, yi, xo, yo; + bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + float threshold = s->brush->sharp_threshold; + int x, y, xi, yi, xo, yo, xk, yk; + float count; int out_off[2], in_off[2], dim[2]; + int diff_pos[2]; float outrgb[4]; + float rgba[4]; + BlurKernel *kernel = s->blurkernel; dim[0] = ibufb->x; dim[1] = ibufb->y; @@ -741,28 +862,52 @@ static void paint_2d_lift_soften(ImBuf *ibuf, ImBuf *ibufb, int *pos, const bool return; } + /* find offset inside mask buffers to sample them */ + sub_v2_v2v2_int(diff_pos, out_off, in_off); + for (y = 0; y < dim[1]; y++) { for (x = 0; x < dim[0]; x++) { /* get input pixel */ xi = in_off[0] + x; yi = in_off[1] + y; - count = 1; - paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, outrgb); + count = 0.0; + paint_2d_ibuf_rgb_get(ibuf, xi, yi, is_torus, rgba); + zero_v4(outrgb); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi - 1, yi + 1, outrgb, is_torus); + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + count += paint_2d_ibuf_add_if(ibuf, xi + xk - kernel->pixel_len, + yi + yk - kernel->pixel_len, outrgb, is_torus, + kernel->wdata[xk + yk * kernel->side]); + } + } - count += paint_2d_ibuf_add_if(ibuf, xi, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi, yi + 1, outrgb, is_torus); + if (count > 0.0f) { + mul_v4_fl(outrgb, 1.0f / (float)count); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi - 1, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi, outrgb, is_torus); - count += paint_2d_ibuf_add_if(ibuf, xi + 1, yi + 1, outrgb, is_torus); + if (sharpen) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(outrgb, rgba, outrgb); - mul_v4_fl(outrgb, 1.0f / (float)count); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + outrgb[0] = outrgb[1] = outrgb[2] = rgb_to_grayscale(outrgb); + if (fabsf(outrgb[0]) > threshold) { + float mask = BKE_brush_alpha_get(s->scene, s->brush); + float alpha = rgba[3]; + rgba[3] = outrgb[3] = mask; + /* add to enhance edges */ + blend_color_add_float(outrgb, rgba, outrgb); + outrgb[3] = alpha; + } + else + copy_v4_v4(outrgb, rgba); + } + } + else + copy_v4_v4(outrgb, rgba); /* write into brush buffer */ xo = out_off[0] + x; yo = out_off[1] + y; @@ -830,10 +975,10 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos) tot = paint_2d_torus_split_region(region, ibufb, ibuf); for (a = 0; a < tot; a++) - IMB_rectblend(ibufb, ibufb, ibuf, NULL, NULL, 0, region[a].destx, region[a].desty, + IMB_rectblend(ibufb, ibufb, ibuf, NULL, NULL, NULL, 0, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, IMB_BLEND_COPY_RGB); + region[a].width, region[a].height, IMB_BLEND_COPY_RGB, false); } static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) @@ -844,10 +989,10 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); - IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, - IMB_BLEND_COPY_ALPHA); - IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, - IMB_BLEND_COPY_RGB); + IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, + IMB_BLEND_COPY_ALPHA, false); + IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, + IMB_BLEND_COPY_RGB, false); return clonebuf; } @@ -858,17 +1003,16 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); } -static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const float lastpos[2], const float pos[2]) +static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); - ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL; + ImBuf *clonebuf = NULL, *frombuf; ImagePaintRegion region[4]; short torus = s->brush->flag & BRUSH_TORUS; short blend = s->blend; const float *offset = s->brush->clone.offset; float liftpos[2]; - float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush); - unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f); + float mask_max = BKE_brush_alpha_get(s->scene, s->brush); int bpos[2], blastpos[2], bliftpos[2]; int a, tot; @@ -876,7 +1020,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f /* lift from canvas */ if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s->canvas, ibufb, bpos, torus); + paint_2d_lift_soften(s, s->canvas, ibufb, bpos, torus); } else if (s->tool == PAINT_TOOL_SMEAR) { if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) @@ -903,9 +1047,6 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); tot = 1; } - - if (s->do_masking) - tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); /* blend into canvas */ for (a = 0; a < tot; a++) { @@ -916,11 +1057,14 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ int tilex, tiley, tilew, tileh, tx, ty; + ImBuf *tmpbuf; imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty, region[a].width, region[a].height, &tilex, &tiley, &tilew, &tileh); - + + tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + for (ty = tiley; ty <= tileh; ty++) { for (tx = tilex; tx <= tilew; tx++) { /* retrieve original pixels + mask from undo buffer */ @@ -929,31 +1073,32 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *maskb, const f int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE; if (s->canvas->rect_float) - tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); else - tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask); + tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask, false); IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, - maskb, mask_max, + curveb, texmaskb, mask_max, region[a].destx, region[a].desty, origx, origy, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); + region[a].width, region[a].height, blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); } } + + IMB_freeImBuf(tmpbuf); } else { /* no masking, composite brush directly onto canvas */ - IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, NULL, 0, + IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, - region[a].width, region[a].height, blend); + region[a].width, region[a].height, blend, false); } } if (clonebuf) IMB_freeImBuf(clonebuf); - if (tmpbuf) IMB_freeImBuf(tmpbuf); return 1; } @@ -1003,10 +1148,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) } /* set masking */ - s->do_masking = (s->brush->flag & BRUSH_AIRBRUSH || - (s->brush->imagepaint_tool == PAINT_TOOL_SMEAR) || - (s->brush->mtex.tex && !ELEM3(s->brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D))) - ? false : true; + s->do_masking = paint_use_opacity_masking(s->brush); return 1; } @@ -1016,11 +1158,15 @@ static void paint_2d_canvas_free(ImagePaintState *s) BKE_image_release_ibuf(s->image, s->canvas, NULL); BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); - if (s->do_masking) - image_undo_remove_masks(); + if (s->blurkernel) { + paint_delete_blur_kernel(s->blurkernel); + MEM_freeN(s->blurkernel); + } + + image_undo_remove_masks(); } -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser) +void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size) { float newuv[2], olduv[2]; ImagePaintState *s = ps; @@ -1063,17 +1209,17 @@ void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], in /* OCIO_TODO: float buffers are now always linear, so always use color correction * this should probably be changed when texture painting color space is supported */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data, s->do_masking); + brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); - brush_painter_2d_refresh_cache(s, painter, newuv, mval); + brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); - if (paint_2d_op(s, painter->cache.ibuf, painter->cache.mask, olduv, newuv)) + if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv)) s->need_redraw = true; BKE_image_release_ibuf(s->image, ibuf, NULL); } -void *paint_2d_new_stroke(bContext *C, wmOperator *op) +void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) { Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; @@ -1102,10 +1248,14 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op) return NULL; } + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + s->blurkernel = paint_new_blur_kernel(brush, false); + } + paint_brush_init_tex(s->brush); /* create painter */ - s->painter = brush_painter_2d_new(scene, s->brush); + s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); return s; } @@ -1134,6 +1284,7 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) /* compositor listener deals with updating */ WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + DAG_id_tag_update(&s->image->id, 0); } else { if (!s->sima || !s->sima->lock) @@ -1153,3 +1304,335 @@ void paint_2d_stroke_done(void *ps) MEM_freeN(s); } + +static void paint_2d_fill_add_pixel_byte( + const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, + const float color[4], float threshold_sq) +{ + int coordinate; + + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; + + coordinate = y_px * ibuf->x + x_px; + + if (!BLI_BITMAP_TEST(touched, coordinate)) { + float color_f[4]; + unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); + rgba_uchar_to_float(color_f, color_b); + + if (compare_len_squared_v3v3(color_f, color, threshold_sq)) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } +} + +static void paint_2d_fill_add_pixel_float( + const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, + const float color[4], float threshold_sq) +{ + int coordinate; + + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; + + coordinate = y_px * ibuf->x + x_px; + + if (!BLI_BITMAP_TEST(touched, coordinate)) { + if (compare_len_squared_v3v3(ibuf->rect_float + 4 * coordinate, color, threshold_sq)) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } +} + +/* this function expects linear space color values */ +void paint_2d_bucket_fill( + const bContext *C, const float color[3], Brush *br, + const float mouse_init[2], + void *ps) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float strength = br ? br->alpha : 1.0f; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + do_float = (ibuf->rect_float != NULL); + /* first check if our image is float. If it is not we should correct the color to + * be in gamma space. strictly speaking this is not correct, but blender does not paint + * byte images in linear space */ + if (!do_float) { + linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); + *(((char *)&color_b) + 3) = strength * 255; + } + else { + copy_v3_v3(color_f, color); + color_f[3] = strength; + } + + if (!mouse_init || !br) { + /* first case, no image UV, fill the whole image */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), color_f); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), (unsigned char *)&color_b); + } + } + } + } + else { + /* second case, start sweeping the neighboring pixels, looking for pixels whose + * value is within the brush fill threshold from the fill color */ + BLI_Stack *stack; + BLI_bitmap *touched; + int coordinate; + int width = ibuf->x; + float image_init[2]; + int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; + float pixel_color[4]; + float threshold_sq = br->fill_threshold * br->fill_threshold; + + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + x_px = image_init[0] * ibuf->x; + y_px = image_init[1] * ibuf->y; + + if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { + BKE_image_release_ibuf(ima, ibuf, NULL); + return; + } + + /* change image invalidation method later */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + stack = BLI_stack_new(sizeof(int), __func__); + touched = BLI_BITMAP_NEW(ibuf->x * ibuf->y, "bucket_fill_bitmap"); + + coordinate = (y_px * ibuf->x + x_px); + + if (do_float) { + copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); + } + else { + int pixel_color_b = *(ibuf->rect + coordinate); + rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); + } + + BLI_stack_push(stack, &coordinate); + BLI_BITMAP_SET(touched, coordinate, true); + + if (do_float) { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_float(ibuf->rect_float + 4 * (coordinate), + ibuf->rect_float + 4 * (coordinate), + color_f, br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_float(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + else { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_byte((unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)&color_b, br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_byte(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + + MEM_freeN(touched); + BLI_stack_free(stack); + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); +} + +void paint_2d_gradient_fill( + const bContext *C, Brush *br, + const float mouse_init[2], const float mouse_final[2], + void *ps) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float image_init[2], image_final[2]; + float tangent[2]; + float line_len_sq_inv, line_len; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + image_final[0] *= ibuf->x; + image_final[1] *= ibuf->y; + + image_init[0] *= ibuf->x; + image_init[1] *= ibuf->y; + + /* some math to get needed gradient variables */ + sub_v2_v2v2(tangent, image_final, image_init); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + do_float = (ibuf->rect_float != NULL); + + /* this will be substituted by something else when selection is available */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + do_colorband(br->gradient, f, color_f); + /* convert to premultiplied */ + mul_v3_fl(color_f, color_f[3]); + color_f[3] *= br->alpha; + IMB_blend_color_float(ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + ibuf->rect_float + 4 * (y_px * ibuf->x + x_px), + color_f, br->blend); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + + do_colorband(br->gradient, f, color_f); + linearrgb_to_srgb_v3_v3(color_f, color_f); + rgba_float_to_uchar((unsigned char *)&color_b, color_f); + ((unsigned char *)&color_b)[3] *= br->alpha; + IMB_blend_color_byte((unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + y_px * ibuf->x + x_px), + (unsigned char *)&color_b, br->blend); + } + } + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); +} diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 492857bd0bb..791c1b3ada1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -48,33 +48,46 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLF_translation.h" + #include "PIL_time.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "DNA_brush_types.h" +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "BKE_camera.h" +#include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_DerivedMesh.h" #include "BKE_idprop.h" #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" #include "BKE_mesh_mapping.h" +#include "BKE_node.h" #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" +#include "BKE_texture.h" #include "UI_view2d.h" +#include "UI_interface.h" +#include "ED_image.h" +#include "ED_mesh.h" +#include "ED_node.h" +#include "ED_paint.h" #include "ED_screen.h" -#include "ED_sculpt.h" #include "ED_uvedit.h" #include "ED_view3d.h" @@ -88,6 +101,7 @@ #include "RNA_enum_types.h" #include "GPU_draw.h" +#include "GPU_buffers.h" #include "IMB_colormanagement.h" @@ -134,9 +148,14 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_FACE_NOSEAM3 (1 << 6) #define PROJ_FACE_NOSEAM4 (1 << 7) +/* face winding */ +#define PROJ_FACE_WINDING_INIT 1 +#define PROJ_FACE_WINDING_CW 2 + #define PROJ_SRC_VIEW 1 #define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 +#define PROJ_SRC_VIEW_FILL 4 #define PROJ_VIEW_DATA_ID "view_data" #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */ @@ -158,6 +177,9 @@ BLI_INLINE unsigned char f_to_char(const float val) /* vert flags */ #define PROJ_VERT_CULL 1 +/* 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 * because 'partRedrawRect' and 'touch' values would not be thread safe */ @@ -165,7 +187,10 @@ typedef struct ProjPaintImage { Image *ima; ImBuf *ibuf; ImagePaintPartialRedraw *partRedrawRect; - void **undoRect; /* only used to build undo tiles after painting */ + volatile void **undoRect; /* only used to build undo tiles during painting */ + unsigned short **maskRect; /* the mask accumulation must happen on canvas, not on space screen bucket. + * Here we store the mask rectangle */ + bool **valid; /* store flag to enforce validation of undo rectangle */ int touch; } ProjPaintImage; @@ -177,9 +202,14 @@ typedef struct ProjPaintState { Scene *scene; int source; /* PROJ_SRC_**** */ + /* the paint color. It can change depending of inverted mode or not */ + float paint_color[3]; + float paint_color_linear[3]; + Brush *brush; short tool, blend, mode; int orig_brush_size; + float brush_size; Object *ob; /* end similarities with ImagePaintState */ @@ -190,10 +220,15 @@ typedef struct ProjPaintState { MVert *dm_mvert; MFace *dm_mface; - MTFace *dm_mtface; - MTFace *dm_mtface_clone; /* other UV map, use for cloning between layers */ + MTFace **dm_mtface; + MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */ MTFace *dm_mtface_stencil; + Image *stencil_ima; + Image *canvas_ima; + Image *clone_ima; + float stencil_value; + /* projection painting only */ MemArena *arena_mt[BLENDER_MAX_THREADS]; /* for multithreading, the first item is sometimes used for non threaded cases too */ LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */ @@ -201,6 +236,7 @@ typedef struct ProjPaintState { unsigned char *bucketFlags; /* store if the bucks have been initialized */ #ifndef PROJ_DEBUG_NOSEAMBLEED char *faceSeamFlags; /* store info about faces, if they are initialized etc*/ + char *faceWindingFlags; /* save the winding of the face in uv space, helps as an extra validation step for seam detection */ float (*faceSeamUVs)[4][2]; /* expanded UVs for faces to use as seams */ LinkNode **vertFaces; /* Only needed for when seam_bleed_px is enabled, use to find UV seams */ #endif @@ -226,6 +262,8 @@ typedef struct ProjPaintState { bool do_layer_clone; bool do_layer_stencil; bool do_layer_stencil_inv; + bool do_stencil_brush; + bool do_material_slots; bool do_occlude; /* Use raytraced occlusion? - ortherwise will paint right through to the back*/ bool do_backfacecull; /* ignore faces with normals pointing away, skips a lot of raycasts if your normals are correctly flipped */ @@ -240,7 +278,6 @@ typedef struct ProjPaintState { bool do_masking; /* use masking during painting. Some operations such as airbrush may disable */ bool is_texbrush; /* only to avoid running */ bool is_maskbrush; /* mask brush is applied before masking */ - bool is_maskbrush_tiled; /* mask brush is applied after masking */ #ifndef PROJ_DEBUG_NOSEAMBLEED float seam_bleed_px; #endif @@ -264,6 +301,10 @@ typedef struct ProjPaintState { /* redraw */ bool need_redraw; + + BlurKernel *blurkernel; + + SpinLock *tile_lock; } ProjPaintState; typedef union pixelPointer { @@ -285,14 +326,16 @@ typedef struct ProjPixel { * Store the max mask value to avoid painting over an area with a lower opacity * with an advantage that we can avoid touching the pixel at all, if the * new mask value is lower then mask_accum */ - unsigned short mask_accum; + unsigned short *mask_accum; /* for various reasons we may want to mask out painting onto this pixel */ unsigned short mask; short x_px, y_px; + /* horrible hack, store tile valid flag pointer here to re-validate tiles used for anchored and drag-dot strokes */ + bool *valid; - PixelStore origColor; + PixelPointer origColor; PixelStore newColor; PixelPointer pixel; @@ -305,33 +348,51 @@ typedef struct ProjPixelClone { PixelStore clonepx; } ProjPixelClone; -/* blur, store surrounding colors */ -#define PROJ_PIXEL_SOFTEN_TOT 4 -/* blur picking offset (in screenspace) */ -#define PROJ_PIXEL_SOFTEN_OFS_PX 1.0f +/* undo tile pushing */ +typedef struct { + SpinLock *lock; + bool masked; + unsigned short tile_width; + ImBuf **tmpibuf; + ProjPaintImage *pjima; +} TileInfo; -static const float proj_pixel_soften_v2[PROJ_PIXEL_SOFTEN_TOT][2] = { - {-PROJ_PIXEL_SOFTEN_OFS_PX, 0.0f}, - { 0.0f, -PROJ_PIXEL_SOFTEN_OFS_PX}, - { 0.0f, PROJ_PIXEL_SOFTEN_OFS_PX}, - { PROJ_PIXEL_SOFTEN_OFS_PX, 0.0f}, -}; /* Finish projection painting structs */ -static Image *project_paint_face_image(const ProjPaintState *ps, MTFace *dm_mtface, int face_index) +static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int face_index) { - Image *ima; + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_active_slot : NULL; +} - if (ps->do_new_shading_nodes) { /* cached BKE_scene_use_new_shading_nodes result */ - MFace *mf = ps->dm_mface + face_index; - ED_object_get_active_image(ps->ob, mf->mat_nr + 1, &ima, NULL, NULL); +static Image *project_paint_face_paint_image(const ProjPaintState *ps, int face_index) +{ + if (ps->do_stencil_brush) { + return ps->stencil_ima; } else { - ima = dm_mtface[face_index].tpage; + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_active_slot : NULL; + return slot ? slot->ima : ps->canvas_ima; } +} - return ima; +static TexPaintSlot *project_paint_face_clone_slot(const ProjPaintState *ps, int face_index) +{ + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + return ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; +} + +static Image *project_paint_face_clone_image(const ProjPaintState *ps, int face_index) +{ + MFace *mf = ps->dm_mface + face_index; + Material *ma = ps->dm->mat[mf->mat_nr]; + TexPaintSlot *slot = ma ? ma->texpaintslot + ma->paint_clone_slot : NULL; + return slot ? slot->ima : ps->clone_ima; } /* fast projection bucket array lookup, use the safe version for bound checking */ @@ -472,8 +533,8 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f static void uvco_to_wrapped_pxco(const float uv[2], int ibuf_x, int ibuf_y, float *x, float *y) { /* use */ - *x = (float)fmodf(uv[0], 1.0f); - *y = (float)fmodf(uv[1], 1.0f); + *x = fmodf(uv[0], 1.0f); + *y = fmodf(uv[1], 1.0f); if (*x < 0.0f) *x += 1.0f; if (*y < 0.0f) *y += 1.0f; @@ -500,7 +561,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], if (face_index == -1) return 0; - tf = ps->dm_mtface + face_index; + tf = *(ps->dm_mtface + face_index); if (side == 0) { interp_v2_v2v2v2(uv, tf->uv[0], tf->uv[1], tf->uv[2], w); @@ -509,8 +570,9 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], interp_v2_v2v2v2(uv, tf->uv[0], tf->uv[2], tf->uv[3], w); } - ima = project_paint_face_image(ps, ps->dm_mtface, face_index); + ima = project_paint_face_paint_image(ps, face_index); ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */ + if (!ibuf) return 0; if (interp) { float x, y; @@ -757,11 +819,11 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve static bool cmp_uv(const float vec2a[2], const float vec2b[2]) { /* if the UV's are not between 0.0 and 1.0 */ - float xa = (float)fmodf(vec2a[0], 1.0f); - float ya = (float)fmodf(vec2a[1], 1.0f); + float xa = fmodf(vec2a[0], 1.0f); + float ya = fmodf(vec2a[1], 1.0f); - float xb = (float)fmodf(vec2b[0], 1.0f); - float yb = (float)fmodf(vec2b[1], 1.0f); + float xb = fmodf(vec2b[0], 1.0f); + float yb = fmodf(vec2b[1], 1.0f); if (xa < 0.0f) xa += 1.0f; if (ya < 0.0f) ya += 1.0f; @@ -835,6 +897,21 @@ static bool pixel_bounds_array(float (*uv)[2], rcti *bounds_px, const int ibuf_x #ifndef PROJ_DEBUG_NOSEAMBLEED +static void project_face_winding_init(const ProjPaintState *ps, const int face_index) +{ + /* detect the winding of faces in uv space */ + MTFace *tf = ps->dm_mtface[face_index]; + float winding = cross_tri_v2(tf->uv[0], tf->uv[1], tf->uv[2]); + + if (ps->dm_mface[face_index].v4) + winding += cross_tri_v2(tf->uv[2], tf->uv[3], tf->uv[0]); + + if (winding > 0) + ps->faceWindingFlags[face_index] |= PROJ_FACE_WINDING_CW; + + ps->faceWindingFlags[face_index] |= PROJ_FACE_WINDING_INIT; +} + /* This function returns 1 if this face has a seam along the 2 face-vert indices * 'orig_i1_fidx' and 'orig_i2_fidx' */ static bool check_seam(const ProjPaintState *ps, @@ -848,7 +925,7 @@ static bool check_seam(const ProjPaintState *ps, MFace *mf; MTFace *tf; const MFace *orig_mf = ps->dm_mface + orig_face; - const MTFace *orig_tf = ps->dm_mtface + orig_face; + const MTFace *orig_tf = ps->dm_mtface[orig_face]; /* vert indices from face vert order indices */ i1 = (*(&orig_mf->v1 + orig_i1_fidx)); @@ -870,23 +947,37 @@ static bool check_seam(const ProjPaintState *ps, /* Only need to check if 'i2_fidx' is valid because we know i1_fidx is the same vert on both faces */ if (i2_fidx != -1) { - Image *tpage = project_paint_face_image(ps, ps->dm_mtface, face_index); - Image *orig_tpage = project_paint_face_image(ps, ps->dm_mtface, orig_face); + Image *tpage = project_paint_face_paint_image(ps, face_index); + Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); BLI_assert(i1_fidx != -1); /* This IS an adjacent face!, now lets check if the UVs are ok */ - tf = ps->dm_mtface + face_index; + tf = ps->dm_mtface[face_index]; /* set up the other face */ *other_face = face_index; - *orig_fidx = (i1_fidx < i2_fidx) ? i1_fidx : i2_fidx; + + /* we check if difference is 1 here, else we might have a case of edge 2-0 or 3-0 for quads */ + *orig_fidx = (i1_fidx < i2_fidx && (i2_fidx - i1_fidx == 1)) ? i1_fidx : i2_fidx; + + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[face_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, face_index); /* first test if they have the same image */ if ((orig_tpage == tpage) && cmp_uv(orig_tf->uv[orig_i1_fidx], tf->uv[i1_fidx]) && - cmp_uv(orig_tf->uv[orig_i2_fidx], tf->uv[i2_fidx]) ) + cmp_uv(orig_tf->uv[orig_i2_fidx], tf->uv[i2_fidx])) { + /* if faces don't have the same winding in uv space, + * they are on the same side so edge is boundary */ + if ((ps->faceWindingFlags[face_index] & PROJ_FACE_WINDING_CW) != + (ps->faceWindingFlags[orig_face] & PROJ_FACE_WINDING_CW)) + { + return 1; + } + // printf("SEAM (NONE)\n"); return 0; @@ -1016,6 +1107,10 @@ static void project_face_seams_init(const ProjPaintState *ps, const int face_ind int fidx1 = is_quad ? 3 : 2; int fidx2 = 0; /* next fidx in the face (0,1,2,3) -> (1,2,3,0) or (0,1,2) -> (1,2,0) for a tri */ + /* initialize face winding if needed */ + if ((ps->faceWindingFlags[face_index] & PROJ_FACE_WINDING_INIT) == 0) + project_face_winding_init(ps, face_index); + do { if ((ps->faceSeamFlags[face_index] & (1 << fidx1 | 16 << fidx1)) == 0) { if (check_seam(ps, face_index, fidx1, fidx2, &other_face, &other_fidx)) { @@ -1132,7 +1227,7 @@ static float project_paint_uvpixel_mask( if (ps->do_layer_stencil) { /* another UV maps image is masking this one's */ ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_image(ps, ps->dm_mtface_stencil, face_index); + Image *other_tpage = ps->stencil_ima; const MTFace *tf_other = ps->dm_mtface_stencil + face_index; if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { @@ -1260,24 +1355,70 @@ static int project_paint_pixel_sizeof(const short tool) } } +static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) +{ + ProjPaintImage *pjIma = tinf->pjima; + int tile_index = tx + ty * tinf->tile_width; + bool generate_tile = false; + + /* double check lock to avoid locking */ + if (UNLIKELY(!pjIma->undoRect[tile_index])) { + if (tinf->lock) + BLI_spin_lock(tinf->lock); + if (LIKELY(!pjIma->undoRect[tile_index])) { + pjIma->undoRect[tile_index] = TILE_PENDING; + generate_tile = true; + } + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + } + + + if (generate_tile) { + volatile void *undorect; + if (tinf->masked) { + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true); + } + else { + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true); + } + + pjIma->ibuf->userflags |= IB_BITMAPDIRTY; + /* tile ready, publish */ + if (tinf->lock) + BLI_spin_lock(tinf->lock); + pjIma->undoRect[tile_index] = undorect; + if (tinf->lock) + BLI_spin_unlock(tinf->lock); + + } + + return tile_index; +} /* run this function when we know a bucket's, face's pixel can be initialized, * return the ProjPixel which is added to 'ps->bucketRect[bucket_index]' */ static ProjPixel *project_paint_uvpixel_init( const ProjPaintState *ps, MemArena *arena, - const ImBuf *ibuf, + const TileInfo *tinf, int x_px, int y_px, const float mask, const int face_index, - const int image_index, const float pixelScreenCo[4], const float world_spaceCo[3], const int side, const float w[3]) { ProjPixel *projPixel; - + int x_tile, y_tile; + int x_round, y_round; + int tile_offset; + /* volatile is important here to ensure pending check is not optimized away by compiler*/ + volatile int tile_index; + + ProjPaintImage *projima = tinf->pjima; + ImBuf *ibuf = projima->ibuf; /* wrap pixel location */ x_px = mod_i(x_px, ibuf->x); @@ -1285,19 +1426,36 @@ static ProjPixel *project_paint_uvpixel_init( BLI_assert(ps->pixel_sizeof == project_paint_pixel_sizeof(ps->tool)); projPixel = (ProjPixel *)BLI_memarena_alloc(arena, ps->pixel_sizeof); + + /* calculate the undo tile offset of the pixel, used to store the original + * pixel color and accumulated mask if any */ + x_tile = x_px >> IMAPAINT_TILE_BITS; + y_tile = y_px >> IMAPAINT_TILE_BITS; + + x_round = x_tile * IMAPAINT_TILE_SIZE; + y_round = y_tile * IMAPAINT_TILE_SIZE; //memset(projPixel, 0, size); + tile_offset = (x_px - x_round) + (y_px - y_round) * IMAPAINT_TILE_SIZE; + tile_index = project_paint_undo_subtiles(tinf, x_tile, y_tile); + + /* other thread may be initializing the tile so wait here */ + while (projima->undoRect[tile_index] == TILE_PENDING) + ; + + BLI_assert(tile_index < (IMAPAINT_TILE_NUMBER(ibuf->x) * IMAPAINT_TILE_NUMBER(ibuf->y))); + BLI_assert(tile_offset < (IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE)); + + projPixel->valid = projima->valid[tile_index]; + if (ibuf->rect_float) { projPixel->pixel.f_pt = ibuf->rect_float + ((x_px + y_px * ibuf->x) * 4); - projPixel->origColor.f[0] = projPixel->pixel.f_pt[0]; - projPixel->origColor.f[1] = projPixel->pixel.f_pt[1]; - projPixel->origColor.f[2] = projPixel->pixel.f_pt[2]; - projPixel->origColor.f[3] = projPixel->pixel.f_pt[3]; + projPixel->origColor.f_pt = (float *)projima->undoRect[tile_index] + 4 * tile_offset; zero_v4(projPixel->newColor.f); } else { - projPixel->pixel.ch_pt = ((unsigned char *)ibuf->rect + ((x_px + y_px * ibuf->x) * 4)); - projPixel->origColor.uint = *projPixel->pixel.uint_pt; + projPixel->pixel.ch_pt = (unsigned char *)(ibuf->rect + (x_px + y_px * ibuf->x)); + projPixel->origColor.uint_pt = (unsigned int *)projima->undoRect[tile_index] + tile_offset; projPixel->newColor.uint = 0; } @@ -1312,7 +1470,10 @@ static ProjPixel *project_paint_uvpixel_init( projPixel->y_px = y_px; projPixel->mask = (unsigned short)(mask * 65535); - projPixel->mask_accum = 0; + if (ps->do_masking) + projPixel->mask_accum = projima->maskRect[tile_index] + tile_offset; + else + projPixel->mask_accum = NULL; /* which bounding box cell are we in?, needed for undo */ projPixel->bb_cell_index = ((int)(((float)x_px / (float)ibuf->x) * PROJ_BOUNDBOX_DIV)) + @@ -1322,8 +1483,8 @@ static ProjPixel *project_paint_uvpixel_init( if (ps->tool == PAINT_TOOL_CLONE) { if (ps->dm_mtface_clone) { ImBuf *ibuf_other; - Image *other_tpage = project_paint_face_image(ps, ps->dm_mtface_clone, face_index); - const MTFace *tf_other = ps->dm_mtface_clone + face_index; + Image *other_tpage = project_paint_face_clone_image(ps, face_index); + const MTFace *tf_other = ps->dm_mtface_clone[face_index]; if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { /* BKE_image_acquire_ibuf - TODO - this may be slow */ @@ -1346,6 +1507,7 @@ static ProjPixel *project_paint_uvpixel_init( project_face_pixel(tf_other, ibuf_other, w, side, NULL, rgba); premul_to_straight_v4(rgba); linearrgb_to_srgb_uchar3(((ProjPixelClone *)projPixel)->clonepx.ch, rgba); + ((ProjPixelClone *)projPixel)->clonepx.ch[3] = rgba[3] * 255; } else { /* char to char */ project_face_pixel(tf_other, ibuf_other, w, side, ((ProjPixelClone *)projPixel)->clonepx.ch, NULL); @@ -1387,7 +1549,8 @@ static ProjPixel *project_paint_uvpixel_init( if (ibuf->rect_float) projPixel->pixel.f_pt[0] = 0; else projPixel->pixel.ch_pt[0] = 0; #endif - projPixel->image_index = image_index; + /* pointer arithmetics */ + projPixel->image_index = projima - ps->projImages; return projPixel; } @@ -2095,15 +2258,24 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot /* One of the most important function for projection painting, since it selects the pixels to be added into each bucket. * initialize pixels from this face where it intersects with the bucket_index, optionally initialize pixels for removing seams */ -static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, const ImBuf *ibuf, const short clamp_u, const short clamp_v) +static void project_paint_face_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const int face_index, const int image_index, rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) { /* Projection vars, to get the 3D locations into screen space */ MemArena *arena = ps->arena_mt[thread_index]; LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; + bool threaded = (ps->thread_tot > 1); + + TileInfo tinf = { + ps->tile_lock, + ps->do_masking, + IMAPAINT_TILE_NUMBER(ibuf->x), + tmpibuf, + ps->projImages + image_index + }; const MFace *mf = ps->dm_mface + face_index; - const MTFace *tf = ps->dm_mtface + face_index; + const MTFace *tf = ps->dm_mtface[face_index]; /* UV/pixel seeking data */ int x; /* Image X-Pixel */ @@ -2147,8 +2319,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i /* Use tf_uv_pxoffset instead of tf->uv so we can offset the UV half a pixel * this is done so we can avoid offsetting all the pixels by 0.5 which causes * problems when wrapping negative coords */ - xhalfpx = (0.5f + (PROJ_GEOM_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; - yhalfpx = (0.5f + (PROJ_GEOM_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; + xhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 3.0f))) / ibuf_xf; + yhalfpx = (0.5f + (PROJ_PIXEL_TOLERANCE * (1.0f / 4.0f))) / ibuf_yf; /* Note about (PROJ_GEOM_TOLERANCE/x) above... * Needed to add this offset since UV coords are often quads aligned to pixels. @@ -2222,6 +2394,10 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i CLAMP(bounds_px.ymax, 0, ibuf->y); } + /* + project_paint_undo_tiles_init(&bounds_px, ps->projImages + image_index, tmpibuf, + tile_width, threaded, ps->do_masking); + */ /* clip face and */ has_isect = 0; @@ -2264,8 +2440,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, - image_index, pixelScreenCo, wco, side, w), + project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, face_index, + pixelScreenCo, wco, side, w), arena ); } @@ -2297,7 +2473,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (ps->seam_bleed_px > 0.0f) { int face_seam_flag; - if (ps->thread_tot > 1) + if (threaded) BLI_lock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ face_seam_flag = ps->faceSeamFlags[face_index]; @@ -2315,7 +2491,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if ((face_seam_flag & (PROJ_FACE_SEAM1 | PROJ_FACE_SEAM2 | PROJ_FACE_SEAM3 | PROJ_FACE_SEAM4)) == 0) { - if (ps->thread_tot > 1) + if (threaded) BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ } @@ -2340,7 +2516,7 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i uv_image_outset(tf_uv_pxoffset, outset_uv, ps->seam_bleed_px, ibuf->x, ibuf->y, mf->v4 != 0); /* ps->faceSeamUVs cant be modified when threading, now this is done we can unlock */ - if (ps->thread_tot > 1) + if (threaded) BLI_unlock_thread(LOCK_CUSTOM1); /* Other threads could be modifying these vars */ vCoSS[0] = ps->screenCoords[mf->v1]; @@ -2484,7 +2660,8 @@ static void project_paint_face_init(const ProjPaintState *ps, const int thread_i if (mask > 0.0f) { BLI_linklist_prepend_arena( bucketPixelNodes, - project_paint_uvpixel_init(ps, arena, ibuf, x, y, mask, face_index, image_index, pixelScreenCo, wco, side, w), + project_paint_uvpixel_init(ps, arena, &tinf, x, y, mask, face_index, + pixelScreenCo, wco, side, w), arena ); } @@ -2553,6 +2730,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index ImBuf *ibuf = NULL; Image *tpage_last = NULL, *tpage; Image *ima = NULL; + ImBuf *tmpibuf = NULL; if (ps->image_tot == 1) { /* Simple loop, no context switching */ @@ -2560,7 +2738,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index ima = ps->projImages[0].ima; for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { - project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init(ps, thread_index, bucket_index, GET_INT_FROM_POINTER(node->link), 0, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); } } else { @@ -2570,7 +2748,7 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index face_index = GET_INT_FROM_POINTER(node->link); /* Image context switching */ - tpage = project_paint_face_image(ps, ps->dm_mtface, face_index); + tpage = project_paint_face_paint_image(ps, face_index); if (tpage_last != tpage) { tpage_last = tpage; @@ -2584,10 +2762,13 @@ static void project_bucket_init(const ProjPaintState *ps, const int thread_index } /* context switching done */ - project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init(ps, thread_index, bucket_index, face_index, image_index, bucket_bounds, ibuf, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); } } + if (tmpibuf) + IMB_freeImBuf(tmpibuf); + ps->bucketFlags[bucket_index] |= PROJ_BUCKET_INIT; } @@ -2735,11 +2916,17 @@ static void project_paint_begin(ProjPaintState *ps) ProjPaintImage *projIma; Image *tpage_last = NULL, *tpage; + TexPaintSlot *slot_last = NULL, *slot = NULL; + TexPaintSlot *slot_last_clone = NULL, *slot_clone; /* Face vars */ MPoly *mpoly_orig; MFace *mf; - MTFace *tf; + MTFace **tf; + MTFace *tf_base; + + MTFace **tf_clone; + MTFace *tf_clone_base = NULL; int a, i; /* generic looping vars */ int image_index = -1, face_index; @@ -2785,7 +2972,7 @@ static void project_paint_begin(ProjPaintState *ps) ps->dm_release = true; } - if (!CustomData_has_layer(&ps->dm->faceData, CD_MTFACE) ) { + if (!CustomData_has_layer(&ps->dm->faceData, CD_MTFACE)) { if (ps->dm_release) ps->dm->release(ps->dm); @@ -2794,13 +2981,15 @@ static void project_paint_begin(ProjPaintState *ps) return; } - ps->dm_mvert = ps->dm->getVertArray(ps->dm); - ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); - ps->dm_mtface = ps->dm->getTessFaceDataArray(ps->dm, CD_MTFACE); + DM_update_materials(ps->dm, ps->ob); ps->dm_totvert = ps->dm->getNumVerts(ps->dm); ps->dm_totface = ps->dm->getNumTessFaces(ps->dm); + ps->dm_mvert = ps->dm->getVertArray(ps->dm); + ps->dm_mface = ps->dm->getTessFaceArray(ps->dm); + ps->dm_mtface = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); + if (ps->do_face_sel) { index_mf_to_mpoly = ps->dm->getTessFaceDataArray(ps->dm, CD_ORIGINDEX); index_mp_to_orig = ps->dm->getPolyDataArray(ps->dm, CD_ORIGINDEX); @@ -2816,32 +3005,36 @@ static void project_paint_begin(ProjPaintState *ps) } /* use clone mtface? */ - - - /* Note, use the original mesh for getting the clone and mask layer index - * this avoids re-generating the derived mesh just to get the new index */ if (ps->do_layer_clone) { - //int layer_num = CustomData_get_clone_layer(&ps->dm->faceData, CD_MTFACE); - int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); - if (layer_num != -1) - ps->dm_mtface_clone = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - - if (ps->dm_mtface_clone == NULL || ps->dm_mtface_clone == ps->dm_mtface) { - ps->do_layer_clone = false; - ps->dm_mtface_clone = NULL; - } + ps->dm_mtface_clone = MEM_mallocN(ps->dm_totface * sizeof(MTFace *), "proj_paint_mtfaces"); } - if (ps->do_layer_stencil) { + if (ps->do_layer_stencil || ps->do_stencil_brush) { //int layer_num = CustomData_get_stencil_layer(&ps->dm->faceData, CD_MTFACE); int layer_num = CustomData_get_stencil_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); if (layer_num != -1) ps->dm_mtface_stencil = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); - if (ps->dm_mtface_stencil == NULL || ps->dm_mtface_stencil == ps->dm_mtface) { - ps->do_layer_stencil = false; - ps->dm_mtface_stencil = NULL; + if (ps->dm_mtface_stencil == NULL) { + /* get active instead */ + ps->dm_mtface_stencil = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); } + + if (ps->do_stencil_brush) + tf_base = ps->dm_mtface_stencil; + } + + if (ps->do_layer_clone) { + int layer_num = CustomData_get_clone_layer(&((Mesh *)ps->ob->data)->pdata, CD_MTEXPOLY); + + if (layer_num != -1) + tf_clone_base = CustomData_get_layer_n(&ps->dm->faceData, CD_MTFACE, layer_num); + + if (tf_clone_base == NULL) { + /* get active instead */ + tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + } + } /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ @@ -3038,6 +3231,7 @@ static void project_paint_begin(ProjPaintState *ps) if (ps->seam_bleed_px > 0.0f) { ps->vertFaces = (LinkNode **)MEM_callocN(sizeof(LinkNode *) * ps->dm_totvert, "paint-vertFaces"); ps->faceSeamFlags = (char *)MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceSeamFlags"); + ps->faceWindingFlags = (char *)MEM_callocN(sizeof(char) * ps->dm_totface, "paint-faceWindindFlags"); ps->faceSeamUVs = MEM_mallocN(sizeof(float) * ps->dm_totface * 8, "paint-faceSeamUVs"); } #endif @@ -3055,6 +3249,13 @@ static void project_paint_begin(ProjPaintState *ps) if (reset_threads) ps->thread_tot = 1; + if (ps->thread_tot > 1) { + ps->tile_lock = MEM_mallocN(sizeof(SpinLock), "projpaint_tile_lock"); + BLI_spin_init(ps->tile_lock); + } + + image_undo_init_locks(); + for (a = 0; a < ps->thread_tot; a++) { ps->arena_mt[a] = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "project paint arena"); } @@ -3116,7 +3317,60 @@ static void project_paint_begin(ProjPaintState *ps) is_face_sel = true; } - if (is_face_sel && (tpage = project_paint_face_image(ps, ps->dm_mtface, face_index))) { + if (!ps->do_stencil_brush) { + slot = project_paint_face_paint_slot(ps, face_index); + /* all faces should have a valid slot, reassert here */ + if (slot == NULL) { + tf_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + tpage = ps->canvas_ima; + } + else { + if (slot != slot_last) { + if (!slot->uvname || !(tf_base = CustomData_get_layer_named(&ps->dm->faceData, CD_MTFACE, slot->uvname))) + tf_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + slot_last = slot; + } + + /* don't allow using the same inage for painting and stencilling */ + if (slot->ima == ps->stencil_ima) + continue; + + tpage = slot->ima; + } + } + else { + tpage = ps->stencil_ima; + } + + *tf = tf_base + face_index; + + if (ps->do_layer_clone) { + if (ps->do_material_slots) { + slot_clone = project_paint_face_clone_slot(ps, face_index); + /* all faces should have a valid slot, reassert here */ + if (ELEM(slot_clone, NULL, slot)) + continue; + } + else if (ps->clone_ima == ps->canvas_ima) + continue; + + tf_clone = ps->dm_mtface_clone + face_index; + + if (ps->do_material_slots) { + if (slot_clone != slot_last_clone) { + if (!slot->uvname || !(tf_clone_base = CustomData_get_layer_named(&ps->dm->faceData, CD_MTFACE, slot_clone->uvname))) + tf_clone_base = CustomData_get_layer(&ps->dm->faceData, CD_MTFACE); + slot_last_clone = slot_clone; + } + } + + *tf_clone = tf_clone_base + face_index; + } + + /* tfbase here should be non-null! */ + BLI_assert (tf_base != NULL); + + if (is_face_sel && tpage) { const float *v1coSS, *v2coSS, *v3coSS, *v4coSS = NULL; v1coSS = ps->screenCoords[mf->v1]; @@ -3214,10 +3468,19 @@ static void project_paint_begin(ProjPaintState *ps) projIma = ps->projImages = (ProjPaintImage *)BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + int size; projIma->ima = node->link; projIma->touch = 0; projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); - projIma->partRedrawRect = BLI_memarena_calloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); + projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + memset(projIma->partRedrawRect, 0, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); + projIma->undoRect = (volatile void **) BLI_memarena_alloc(arena, size); + memset(projIma->undoRect, 0, size); + projIma->maskRect = (unsigned short **) BLI_memarena_alloc(arena, size); + memset(projIma->maskRect, 0, size); + projIma->valid = (bool **) BLI_memarena_alloc(arena, size); + memset(projIma->valid, 0, size); } /* we have built the array, discard the linked list */ @@ -3244,95 +3507,12 @@ static void project_paint_end(ProjPaintState *ps) int a; ProjPaintImage *projIma; - /* build undo data from original pixel colors */ - if (U.uiflag & USER_GLOBALUNDO) { - ProjPixel *projPixel; - ImBuf *tmpibuf = NULL, *tmpibuf_float = NULL; - LinkNode *pixel_node; - void *tilerect; - MemArena *arena = ps->arena_mt[0]; /* threaded arena re-used for non threaded case */ - - int bucket_tot = (ps->buckets_x * ps->buckets_y); /* we could get an X/Y but easier to loop through all possible buckets */ - int bucket_index; - int tile_index; - int x_round, y_round; - int x_tile, y_tile; - int is_float = -1; - - /* context */ - ProjPaintImage *last_projIma; - int last_image_index = -1; - int last_tile_width = 0; - - for (a = 0, last_projIma = ps->projImages; a < ps->image_tot; a++, last_projIma++) { - int size = sizeof(void **) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(last_projIma->ibuf->y); - last_projIma->undoRect = (void **) BLI_memarena_calloc(arena, size); - last_projIma->ibuf->userflags |= IB_BITMAPDIRTY; - } - - for (bucket_index = 0; bucket_index < bucket_tot; bucket_index++) { - /* loop through all pixels */ - for (pixel_node = ps->bucketRect[bucket_index]; pixel_node; pixel_node = pixel_node->next) { - - /* ok we have a pixel, was it modified? */ - projPixel = (ProjPixel *)pixel_node->link; - - if (last_image_index != projPixel->image_index) { - /* set the context */ - last_image_index = projPixel->image_index; - last_projIma = ps->projImages + last_image_index; - last_tile_width = IMAPAINT_TILE_NUMBER(last_projIma->ibuf->x); - is_float = last_projIma->ibuf->rect_float ? 1 : 0; - } - - - if ((is_float == 0 && projPixel->origColor.uint != *projPixel->pixel.uint_pt) || - (is_float == 1 && - (projPixel->origColor.f[0] != projPixel->pixel.f_pt[0] || - projPixel->origColor.f[1] != projPixel->pixel.f_pt[1] || - projPixel->origColor.f[2] != projPixel->pixel.f_pt[2] || - projPixel->origColor.f[3] != projPixel->pixel.f_pt[3])) - ) - { - - x_tile = projPixel->x_px >> IMAPAINT_TILE_BITS; - y_tile = projPixel->y_px >> IMAPAINT_TILE_BITS; - - x_round = x_tile * IMAPAINT_TILE_SIZE; - y_round = y_tile * IMAPAINT_TILE_SIZE; - - tile_index = x_tile + y_tile * last_tile_width; - - if (last_projIma->undoRect[tile_index] == NULL) { - /* add the undo tile from the modified image, then write the original colors back into it */ - tilerect = last_projIma->undoRect[tile_index] = image_undo_push_tile(last_projIma->ima, last_projIma->ibuf, is_float ? (&tmpibuf_float) : (&tmpibuf), x_tile, y_tile); - } - else { - tilerect = last_projIma->undoRect[tile_index]; - } - - /* This is a BIT ODD, but overwrite the undo tiles image info with this pixels original color - * because allocating the tiles along the way slows down painting */ - - if (is_float) { - float *rgba_fp = (float *)tilerect + (((projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE)) * 4; - copy_v4_v4(rgba_fp, projPixel->origColor.f); - } - else { - ((unsigned int *)tilerect)[(projPixel->x_px - x_round) + (projPixel->y_px - y_round) * IMAPAINT_TILE_SIZE] = projPixel->origColor.uint; - } - } - } - } - - if (tmpibuf) IMB_freeImBuf(tmpibuf); - if (tmpibuf_float) IMB_freeImBuf(tmpibuf_float); - } - /* done calculating undo data */ + image_undo_remove_masks(); /* dereference used image buffers */ for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { BKE_image_release_ibuf(projIma->ima, projIma->ibuf, NULL); + DAG_id_tag_update(&projIma->ima->id, 0); } BKE_image_release_ibuf(ps->reproject_image, ps->reproject_ibuf, NULL); @@ -3341,15 +3521,29 @@ static void project_paint_end(ProjPaintState *ps) MEM_freeN(ps->bucketRect); MEM_freeN(ps->bucketFaces); MEM_freeN(ps->bucketFlags); + MEM_freeN(ps->dm_mtface); + if (ps->do_layer_clone) + MEM_freeN(ps->dm_mtface_clone); + if (ps->thread_tot > 1) { + BLI_spin_end(ps->tile_lock); + MEM_freeN((void *)ps->tile_lock); + } + image_undo_end_locks(); #ifndef PROJ_DEBUG_NOSEAMBLEED if (ps->seam_bleed_px > 0.0f) { MEM_freeN(ps->vertFaces); MEM_freeN(ps->faceSeamFlags); + MEM_freeN(ps->faceWindingFlags); MEM_freeN(ps->faceSeamUVs); } #endif + if (ps->blurkernel) { + paint_delete_blur_kernel(ps->blurkernel); + MEM_freeN(ps->blurkernel); + } + if (ps->vertFlags) MEM_freeN(ps->vertFlags); for (a = 0; a < ps->thread_tot; a++) { @@ -3442,7 +3636,7 @@ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) { if (ps->source == PROJ_SRC_VIEW) { float min_brush[2], max_brush[2]; - const float radius = (float)BKE_brush_size_get(ps->scene, ps->brush); + const float radius = ps->brush_size; /* so we don't have a bucket bounds that is way too small to paint into */ // if (radius < 1.0f) radius = 1.0f; // this doesn't work yet :/ @@ -3480,7 +3674,7 @@ static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) static bool project_bucket_iter_next(ProjPaintState *ps, int *bucket_index, rctf *bucket_bounds, const float mval[2]) { - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + const int diameter = 2 * ps->brush_size; if (ps->thread_tot > 1) BLI_lock_thread(LOCK_CUSTOM1); @@ -3542,7 +3736,7 @@ static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, floa clone_rgba[3] = (unsigned char)(clone_pt[3] * mask); if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, clone_rgba, ps->blend); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, clone_rgba, ps->blend); } else { IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, clone_rgba, ps->blend); @@ -3560,7 +3754,7 @@ static void do_projectpaint_clone_f(ProjPaintState *ps, ProjPixel *projPixel, fl mul_v4_v4fl(clone_rgba, clone_pt, mask); if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f, clone_rgba, ps->blend); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, clone_rgba, ps->blend); } else { IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, clone_rgba, ps->blend); @@ -3598,42 +3792,58 @@ static void do_projectpaint_smear_f(ProjPaintState *ps, ProjPixel *projPixel, fl BLI_linklist_prepend_arena(smearPixels_f, (void *)projPixel, smearArena); } -/* do_projectpaint_soften for float & byte - */ -static float inv_pow2(float f) -{ - f = 1.0f - f; - f = f * f; - return 1.0f - f; -} - static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels) { - unsigned int accum_tot = 0; - unsigned int i; - + float accum_tot = 0.0f; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; float *rgba = projPixel->newColor.f; - /* sigh, mask values tend to need to be a _lot_ stronger with blur */ - mask = inv_pow2(mask); - /* rather then painting, accumulate surrounding colors */ zero_v4(rgba); - for (i = 0; i < PROJ_PIXEL_SOFTEN_TOT; i++) { - float co_ofs[2]; - float rgba_tmp[4]; - sub_v2_v2v2(co_ofs, projPixel->projCoSS, proj_pixel_soften_v2[i]); - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - add_v4_v4(rgba, rgba_tmp); - accum_tot++; + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } } } if (LIKELY(accum_tot != 0)) { mul_v4_fl(rgba, 1.0f / (float)accum_tot); - blend_color_interpolate_float(rgba, rgba, projPixel->pixel.f_pt, mask); + + if (ps->mode == BRUSH_STROKE_INVERT) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, projPixel->pixel.f_pt, rgba); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = rgb_to_grayscale(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = projPixel->pixel.f_pt[3]; + projPixel->pixel.f_pt[3] = rgba[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, projPixel->pixel.f_pt, rgba); + rgba[3] = alpha; + } + else + return; + } + else { + blend_color_interpolate_float(rgba, rgba, projPixel->pixel.f_pt, mask); + } + BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); } } @@ -3641,24 +3851,27 @@ static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, f static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, float mask, MemArena *softenArena, LinkNode **softenPixels) { - unsigned int accum_tot = 0; - unsigned int i; - + float accum_tot = 0; + int xk, yk; + BlurKernel *kernel = ps->blurkernel; float rgba[4]; /* convert to byte after */ - /* sigh, mask values tend to need to be a _lot_ stronger with blur */ - mask = inv_pow2(mask); - /* rather then painting, accumulate surrounding colors */ zero_v4(rgba); - for (i = 0; i < PROJ_PIXEL_SOFTEN_TOT; i++) { - float co_ofs[2]; - float rgba_tmp[4]; - sub_v2_v2v2(co_ofs, projPixel->projCoSS, proj_pixel_soften_v2[i]); - if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { - add_v4_v4(rgba, rgba_tmp); - accum_tot++; + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + float rgba_tmp[4]; + float co_ofs[2] = {2.0f * xk - 1.0f, 2.0f * yk - 1.0f}; + + add_v2_v2(co_ofs, projPixel->projCoSS); + + if (project_paint_PickColor(ps, co_ofs, rgba_tmp, NULL, true)) { + float weight = kernel->wdata[xk + yk * kernel->side]; + mul_v4_fl(rgba_tmp, weight); + add_v4_v4(rgba, rgba_tmp); + accum_tot += weight; + } } } @@ -3666,9 +3879,34 @@ static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, flo unsigned char *rgba_ub = projPixel->newColor.ch; mul_v4_fl(rgba, 1.0f / (float)accum_tot); - premul_float_to_straight_uchar(rgba_ub, rgba); - blend_color_interpolate_byte(rgba_ub, rgba_ub, projPixel->pixel.ch_pt, mask); + if (ps->mode == BRUSH_STROKE_INVERT) { + float rgba_pixel[4]; + + straight_uchar_to_premul_float(rgba_pixel, projPixel->pixel.ch_pt); + + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(rgba, rgba_pixel, rgba); + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + rgba[0] = rgba[1] = rgba[2] = rgb_to_grayscale(rgba); + if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { + float alpha = rgba_pixel[3]; + rgba[3] = rgba_pixel[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(rgba, rgba_pixel, rgba); + + rgba[3] = alpha; + premul_float_to_straight_uchar(rgba_ub, rgba); + } + else + return; + } + else { + premul_float_to_straight_uchar(rgba_ub, rgba); + blend_color_interpolate_byte(rgba_ub, rgba_ub, projPixel->pixel.ch_pt, mask); + } BLI_linklist_prepend_arena(softenPixels, (void *)projPixel, softenArena); } } @@ -3678,7 +3916,7 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const float rgb[3]; unsigned char rgba_ub[4]; - copy_v3_v3(rgb, ps->brush->rgb); + copy_v3_v3(rgb, ps->paint_color); if (ps->is_texbrush) { mul_v3_v3(rgb, texrgb); @@ -3690,7 +3928,7 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, const rgba_ub[3] = f_to_char(mask); if (ps->do_masking) { - IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, rgba_ub, ps->blend); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); } else { IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); @@ -3701,7 +3939,7 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, con { float rgba[4]; - srgb_to_linearrgb_v3_v3(rgba, ps->brush->rgb); + copy_v3_v3(rgba, ps->paint_color_linear); if (ps->is_texbrush) mul_v3_v3(rgba, texrgb); @@ -3710,13 +3948,42 @@ static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, con rgba[3] = mask; if (ps->do_masking) { - IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f, rgba, ps->blend); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); + } + else { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); + } +} + +static void do_projectpaint_mask(ProjPaintState *ps, ProjPixel *projPixel, float mask) +{ + unsigned char rgba_ub[4]; + rgba_ub[0] = rgba_ub[1] = rgba_ub[2] = ps->stencil_value * 255.0f; + rgba_ub[3] = f_to_char(mask); + + if (ps->do_masking) { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, rgba_ub, ps->blend); + } + else { + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->pixel.ch_pt, rgba_ub, ps->blend); + } +} + +static void do_projectpaint_mask_f(ProjPaintState *ps, ProjPixel *projPixel, float mask) +{ + float rgba[4]; + rgba[0] = rgba[1] = rgba[2] = ps->stencil_value; + rgba[3] = mask; + + if (ps->do_masking) { + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, rgba, ps->blend); } else { IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->pixel.f_pt, rgba, ps->blend); } } + /* run this for single and multithreaded painting */ static void *do_projectpaint_thread(void *ph_v) { @@ -3750,7 +4017,7 @@ static void *do_projectpaint_thread(void *ph_v) float co[2]; unsigned short mask_short; const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); - const float brush_radius = (float)BKE_brush_size_get(ps->scene, brush); + const float brush_radius = ps->brush_size; const float brush_radius_sq = brush_radius * brush_radius; /* avoid a square root with every dist comparison */ short lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? 0 : brush->flag & BRUSH_LOCK_ALPHA; @@ -3800,32 +4067,108 @@ static void *do_projectpaint_thread(void *ph_v) } /* end copy */ - if (is_floatbuf) { - /* re-project buffer is assumed byte - TODO, allow float */ - bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float newColor_f[4]; - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + /* fill tools */ + if (ps->source == PROJ_SRC_VIEW_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + /* these could probably be cached instead of being done per pixel */ + float tangent[2]; + float line_len_sq_inv, line_len; + float f; + float color_f[4]; + float p[2] = {projPixel->projCoSS[0] - lastpos[0], projPixel->projCoSS[1] - lastpos[1]}; + + sub_v2_v2v2(tangent, pos, lastpos); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + switch (brush->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: + { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + { + f = len_v2(p) / line_len; + break; + } + } + do_colorband(brush->gradient, f, color_f); + color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + + if (is_floatbuf) { + /* convert to premultipied */ + mul_v3_fl(color_f, color_f[3]); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + color_f, ps->blend); + } + else { + linearrgb_to_srgb_v3_v3(color_f, color_f); + rgba_float_to_uchar(projPixel->newColor.ch, color_f); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch, ps->blend); + } + } + else { + if (is_floatbuf) { + float newColor_f[4]; + newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; + copy_v3_v3(newColor_f, ps->paint_color_linear); - straight_uchar_to_premul_float(newColor_f, projPixel->newColor.ch); - IMB_colormanagement_colorspace_to_scene_linear_v4(newColor_f, true, ps->reproject_ibuf->rect_colorspace); - mul_v4_v4fl(newColor_f, newColor_f, mask); + IMB_blend_color_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + newColor_f, ps->blend); + } + else { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] = mask * 255 * brush->alpha; - blend_color_mix_float(projPixel->pixel.f_pt, projPixel->origColor.f, - newColor_f); + rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); + IMB_blend_color_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch, ps->blend); + } + } + + if (lock_alpha) { + if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f_pt[3]; + else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; } + + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; + last_partial_redraw_cell->x1 = min_ii(last_partial_redraw_cell->x1, (int)projPixel->x_px); + last_partial_redraw_cell->y1 = min_ii(last_partial_redraw_cell->y1, (int)projPixel->y_px); + + last_partial_redraw_cell->x2 = max_ii(last_partial_redraw_cell->x2, (int)projPixel->x_px + 1); + last_partial_redraw_cell->y2 = max_ii(last_partial_redraw_cell->y2, (int)projPixel->y_px + 1); } else { - /* re-project buffer is assumed byte - TODO, allow float */ - bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, - projPixel->projCoSS[0], projPixel->projCoSS[1]); - if (projPixel->newColor.ch[3]) { - float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); - projPixel->newColor.ch[3] *= mask; - - blend_color_mix_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch, - projPixel->newColor.ch); + if (is_floatbuf) { + /* re-project buffer is assumed byte - TODO, allow float */ + bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, + projPixel->projCoSS[0], projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float newColor_f[4]; + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + + straight_uchar_to_premul_float(newColor_f, projPixel->newColor.ch); + IMB_colormanagement_colorspace_to_scene_linear_v4(newColor_f, true, ps->reproject_ibuf->rect_colorspace); + mul_v4_v4fl(newColor_f, newColor_f, mask); + + blend_color_mix_float(projPixel->pixel.f_pt, projPixel->origColor.f_pt, + newColor_f); + } + } + else { + /* re-project buffer is assumed byte - TODO, allow float */ + bicubic_interpolation_color(ps->reproject_ibuf, projPixel->newColor.ch, NULL, + projPixel->projCoSS[0], projPixel->projCoSS[1]); + if (projPixel->newColor.ch[3]) { + float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); + projPixel->newColor.ch[3] *= mask; + + blend_color_mix_byte(projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, + projPixel->newColor.ch); + } } } } @@ -3847,7 +4190,7 @@ static void *do_projectpaint_thread(void *ph_v) if (falloff > 0.0f) { float texrgb[3]; - float mask = falloff; + float mask; if (ps->do_masking) { /* masking to keep brush contribution to a pixel limited. note we do not do @@ -3856,20 +4199,24 @@ static void *do_projectpaint_thread(void *ph_v) * * Instead we use a formula that adds up but approaches brush_alpha slowly * and never exceeds it, which gives nice smooth results. */ - float mask_accum = projPixel->mask_accum; + float mask_accum = *projPixel->mask_accum; + float max_mask = brush_alpha * falloff * 65535.0f; if (ps->is_maskbrush) { float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - CLAMP(texmask, 0.0f, 1.0f); - mask = mask_accum + (brush_alpha * texmask * 65535.0f - mask_accum) * mask; - } - else { - mask = mask_accum + (brush_alpha * 65535.0f - mask_accum) * mask; + max_mask *= texmask; } + + if (brush->flag & BRUSH_ACCUMULATE) + mask = mask_accum + max_mask; + else + mask = mask_accum + (max_mask - mask_accum * falloff); + + mask = min_ff(mask, 65535.0f); mask_short = (unsigned short)mask; - if (mask_short > projPixel->mask_accum) { - projPixel->mask_accum = mask_short; + if (mask_short > *projPixel->mask_accum) { + *projPixel->mask_accum = mask_short; mask = mask_short * (1.0f / 65535.0f); } else { @@ -3878,7 +4225,7 @@ static void *do_projectpaint_thread(void *ph_v) } } else { - mask *= brush_alpha; + mask = brush_alpha * falloff; if (ps->is_maskbrush) { float texmask = BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); CLAMP(texmask, 0.0f, 1.0f); @@ -3907,10 +4254,6 @@ static void *do_projectpaint_thread(void *ph_v) mask *= texrgba[3]; } - if (ps->is_maskbrush_tiled) { - mask *= BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); - } - /* extra mask for normal, layer stencil, .. */ mask *= ((float)projPixel->mask) * (1.0f / 65535.0f); @@ -3926,6 +4269,9 @@ static void *do_projectpaint_thread(void *ph_v) } /* end copy */ + /* validate undo tile, since we will modify t*/ + *projPixel->valid = true; + last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; last_partial_redraw_cell->x1 = min_ii(last_partial_redraw_cell->x1, (int)projPixel->x_px); last_partial_redraw_cell->y1 = min_ii(last_partial_redraw_cell->y1, (int)projPixel->y_px); @@ -3949,6 +4295,10 @@ static void *do_projectpaint_thread(void *ph_v) if (is_floatbuf) do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); else do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); break; + case PAINT_TOOL_MASK: + if (is_floatbuf) do_projectpaint_mask_f(ps, projPixel, mask); + else do_projectpaint_mask(ps, projPixel, mask); + break; default: if (is_floatbuf) do_projectpaint_draw_f(ps, projPixel, texrgb, mask); else do_projectpaint_draw(ps, projPixel, texrgb, mask); @@ -3957,8 +4307,8 @@ static void *do_projectpaint_thread(void *ph_v) } if (lock_alpha) { - if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f[3]; - else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch[3]; + if (is_floatbuf) projPixel->pixel.f_pt[3] = projPixel->origColor.f_pt[3]; + else projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; } /* done painting */ @@ -4076,14 +4426,20 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } -void paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const float pos[2]) +void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], const float pos[2], const bool eraser, float pressure, float distance, float size) { ProjPaintState *ps = pps; + Brush *brush = ps->brush; + Scene *scene = ps->scene; int a; + ps->brush_size = size; + ps->blend = brush->blend; + if (eraser) + ps->blend = IMB_BLEND_ERASE_ALPHA; + /* clone gets special treatment here to avoid going through image initialization */ if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { - Scene *scene = ps->scene; View3D *v3d = ps->v3d; float *cursor = ED_view3d_cursor3d_get(scene, v3d); int mval_i[2] = {(int)pos[0], (int)pos[1]}; @@ -4098,6 +4454,25 @@ void paint_proj_stroke(bContext *C, void *pps, const float prev_pos[2], const fl return; } + /* handle gradient and inverted stroke color here */ + if (ps->tool == PAINT_TOOL_DRAW) { + paint_brush_color_get(scene, brush, false, ps->mode == BRUSH_STROKE_INVERT, distance, pressure, ps->paint_color, NULL); + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else if (ps->tool == PAINT_TOOL_FILL) { + copy_v3_v3(ps->paint_color, BKE_brush_color_get(scene, brush)); + srgb_to_linearrgb_v3_v3(ps->paint_color_linear, ps->paint_color); + } + else if (ps->tool == PAINT_TOOL_MASK) { + ps->stencil_value = brush->weight; + + if ((ps->mode == BRUSH_STROKE_INVERT) ^ + ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0)) + { + ps->stencil_value = 1.0f - ps->stencil_value; + } + } + /* continue adding to existing partial redraw rects until redraw */ if (!ps->need_redraw) { for (a = 0; a < ps->image_tot; a++) @@ -4122,30 +4497,24 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int Brush *brush = ps->brush; ps->tool = brush->imagepaint_tool; ps->blend = brush->blend; + /* only check for inversion for the soften tool, elsewhere, a resident brush inversion flag can cause issues */ + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + ps->mode = ((ps->mode == BRUSH_STROKE_INVERT) ^ ((brush->flag & BRUSH_DIR_IN) != 0) ? + BRUSH_STROKE_INVERT : BRUSH_STROKE_NORMAL); + + ps->blurkernel = paint_new_blur_kernel(brush, true); + } /* disable for 3d mapping also because painting on mirrored mesh can create "stripes" */ - ps->do_masking = (brush->flag & BRUSH_AIRBRUSH || - (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || - (brush->mtex.tex && !ELEM3(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D))) - ? false : true; + ps->do_masking = paint_use_opacity_masking(brush); ps->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - ps->is_maskbrush = false; - ps->is_maskbrush_tiled = false; - if (brush->mask_mtex.tex) { - if (ELEM(brush->mask_mtex.brush_map_mode, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_TILED)) { - ps->is_maskbrush_tiled = true; - } - else { - ps->is_maskbrush = true; - } - } + ps->is_maskbrush = (brush->mask_mtex.tex) ? true : false; } else { /* brush may be NULL*/ ps->do_masking = false; ps->is_texbrush = false; ps->is_maskbrush = false; - ps->is_maskbrush_tiled = false; } /* sizeof(ProjPixel), since we alloc this a _lot_ */ @@ -4160,17 +4529,33 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int ps->scene = scene; ps->ob = ob; /* allow override of active object */ + ps->do_material_slots = (settings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); + ps->stencil_ima = settings->imapaint.stencil; + ps->canvas_ima = (!ps->do_material_slots) ? + settings->imapaint.canvas : NULL; + ps->clone_ima = (!ps->do_material_slots) ? + settings->imapaint.clone : NULL; + /* setup projection painting data */ - ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1; - ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1; - ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1; + if (ps->tool != PAINT_TOOL_FILL) { + ps->do_backfacecull = (settings->imapaint.flag & IMAGEPAINT_PROJECT_BACKFACE) ? 0 : 1; + ps->do_occlude = (settings->imapaint.flag & IMAGEPAINT_PROJECT_XRAY) ? 0 : 1; + ps->do_mask_normal = (settings->imapaint.flag & IMAGEPAINT_PROJECT_FLAT) ? 0 : 1; + } + else { + ps->do_backfacecull = ps->do_occlude = ps->do_mask_normal = 0; + } ps->do_new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); /* only cache the value */ if (ps->tool == PAINT_TOOL_CLONE) ps->do_layer_clone = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) ? 1 : 0; - ps->do_layer_stencil = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? 1 : 0; - ps->do_layer_stencil_inv = (settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) ? 1 : 0; + + ps->do_stencil_brush = (ps->brush && ps->brush->imagepaint_tool == PAINT_TOOL_MASK); + /* deactivate stenciling for the stencil brush :) */ + ps->do_layer_stencil = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) && + !(ps->do_stencil_brush) && ps->stencil_ima); + ps->do_layer_stencil_inv = ((settings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0); #ifndef PROJ_DEBUG_NOSEAMBLEED @@ -4198,6 +4583,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int mode) { ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + project_state_init(C, ob, ps, mode); if (ps->tool == PAINT_TOOL_CLONE && mode == BRUSH_STROKE_INVERT) { @@ -4230,6 +4616,10 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m paint_proj_begin_clone(ps, mouse); + /* special full screen draw mode for fill tool */ + if (ps->tool == PAINT_TOOL_FILL) + ps->source = PROJ_SRC_VIEW_FILL; + return ps; } @@ -4278,14 +4668,22 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) int orig_brush_size; IDProperty *idgroup; IDProperty *view_data = NULL; + Object *ob = OBACT; + bool uvs, mat, tex; - project_state_init(C, OBACT, &ps, BRUSH_STROKE_NORMAL); - - if (ps.ob == NULL || ps.ob->type != OB_MESH) { + if (ob == NULL || ob->type != OB_MESH) { BKE_report(op->reports, RPT_ERROR, "No active mesh object"); return OPERATOR_CANCELLED; } + if (!BKE_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { + BKE_paint_data_warning(op->reports, uvs, mat, tex, true); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + return OPERATOR_CANCELLED; + } + + project_state_init(C, ob, &ps, BRUSH_STROKE_NORMAL); + if (image == NULL) { BKE_report(op->reports, RPT_ERROR, "Image could not be found"); return OPERATOR_CANCELLED; @@ -4327,7 +4725,6 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) /* override */ ps.is_texbrush = false; ps.is_maskbrush = false; - ps.is_maskbrush_tiled = false; ps.do_masking = false; orig_brush_size = BKE_brush_size_get(scene, ps.brush); BKE_brush_size_set(scene, ps.brush, 32); /* cover the whole image */ @@ -4337,7 +4734,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) scene->toolsettings->imapaint.flag |= IMAGEPAINT_DRAWING; ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); + ED_image_undo_restore, ED_image_undo_free, NULL); /* allocate and initialize spatial data structures */ project_paint_begin(&ps); @@ -4470,3 +4867,361 @@ void PAINT_OT_image_from_view(wmOperatorType *ot) RNA_def_string_file_name(ot->srna, "filepath", NULL, FILE_MAX, "File Path", "Name of the file"); } + +/********************************************* + * Data generation for projective texturing * + * *******************************************/ + +void BKE_paint_data_warning(struct ReportList *reports, bool uvs, bool mat, bool tex, bool stencil) +{ + BKE_reportf(reports, RPT_WARNING, "Missing%s%s%s%s detected!", + !uvs ? " UVs," : "", + !mat ? " Materials," : "", + !tex ? " Textures," : "", + !stencil ? " Stencil," : "" + ); +} + +/* Make sure that active object has a material, and assign UVs and image layers if they do not exist */ +bool BKE_paint_proj_mesh_data_check(Scene *scene, Object *ob, bool *uvs, bool *mat, bool *tex, bool *stencil) +{ + Mesh *me; + int layernum; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + Brush *br = BKE_paint_brush(&imapaint->paint); + bool hasmat = true; + bool hastex = true; + bool hasstencil = true; + bool hasuvs = true; + + imapaint->missing_data = 0; + + BLI_assert(ob->type == OB_MESH); + + if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + /* no material, add one */ + if (ob->totcol == 0) { + hasmat = false; + hastex = false; + } + else { + /* there may be material slots but they may be empty, check */ + int i; + hasmat = false; + hastex = false; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + + if (ma) { + hasmat = true; + if (!ma->texpaintslot) { + /* refresh here just in case */ + BKE_texpaint_slot_refresh_cache(scene, ma); + + /* if still no slots, we have to add */ + if (ma->texpaintslot) { + hastex = true; + break; + } + } + else { + hastex = true; + break; + } + } + } + } + } + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { + if (imapaint->canvas == NULL) { + hastex = false; + } + } + + me = BKE_mesh_from_object(ob); + layernum = CustomData_number_of_layers(&me->pdata, CD_MTEXPOLY); + + if (layernum == 0) { + hasuvs = false; + } + + /* Make sure we have a stencil to paint on! */ + if (br && br->imagepaint_tool == PAINT_TOOL_MASK) { + imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; + + if (imapaint->stencil == NULL) { + hasstencil = false; + } + } + + if (!hasuvs) imapaint->missing_data |= IMAGEPAINT_MISSING_UVS; + if (!hasmat) imapaint->missing_data |= IMAGEPAINT_MISSING_MATERIAL; + if (!hastex) imapaint->missing_data |= IMAGEPAINT_MISSING_TEX; + if (!hasstencil) imapaint->missing_data |= IMAGEPAINT_MISSING_STENCIL; + + if (uvs) { + *uvs = hasuvs; + } + if (mat) { + *mat = hasmat; + } + if (tex) { + *tex = hastex; + } + if (stencil) { + *stencil = hasstencil; + } + + return hasuvs && hasmat && hastex && hasstencil; +} + +/* Add layer operator */ + +static EnumPropertyItem layer_type_items[] = { + {MAP_COL, "DIFFUSE_COLOR", 0, "Diffuse Color", ""}, + {MAP_REF, "DIFFUSE_INTENSITY", 0, "Diffuse Intensity", ""}, + {MAP_ALPHA, "ALPHA", 0, "Alpha", ""}, + {MAP_TRANSLU, "TRANSLUCENCY", 0, "Translucency", ""}, + {MAP_COLSPEC, "SPECULAR_COLOR", 0, "Specular Color", ""}, + {MAP_SPEC, "SPECULAR_INTENSITY", 0, "Specular Intensity", ""}, + {MAP_HAR, "SPECULAR_HARDNESS", 0, "Specular Hardness", ""}, + {MAP_AMB, "AMBIENT", 0, "Ambient", ""}, + {MAP_EMIT, "EMIT", 0, "Emit", ""}, + {MAP_COLMIR, "MIRROR_COLOR", 0, "Mirror Color", ""}, + {MAP_RAYMIRR, "RAYMIRROR", 0, "Ray Mirror", ""}, + {MAP_NORM, "NORMAL", 0, "Normal", ""}, + {MAP_WARP, "WARP", 0, "Warp", ""}, + {MAP_DISPLACE, "DISPLACE", 0, "Displace", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static Image *proj_paint_image_create(wmOperator *op, Main *bmain) +{ + Image *ima; + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + char imagename[MAX_ID_NAME - 2] = "Material Diffuse Color"; + int width = 1024; + int height = 1024; + bool use_float = false; + short gen_type = IMA_GENTYPE_BLANK; + bool alpha = false; + + if (op) { + width = RNA_int_get(op->ptr, "width"); + height = RNA_int_get(op->ptr, "height"); + use_float = RNA_boolean_get(op->ptr, "float"); + gen_type = RNA_enum_get(op->ptr, "generated_type"); + RNA_float_get_array(op->ptr, "color", color); + alpha = RNA_boolean_get(op->ptr, "alpha"); + RNA_string_get(op->ptr, "name", imagename); + } + ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, + gen_type, color); + + return ima; +} + +static bool proj_paint_add_slot(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + Material *ma; + bool is_bi = BKE_scene_uses_blender_internal(scene); + Image *ima = NULL; + + if (!ob) + return false; + + ma = give_current_material(ob, ob->actcol); + + if (ma) { + Main *bmain = CTX_data_main(C); + + if (!is_bi && BKE_scene_use_new_shading_nodes(scene)) { + bNode *imanode; + bNodeTree *ntree = ma->nodetree; + + if (!ntree) { + ED_node_shader_default(C, &ma->id); + ntree = ma->nodetree; + } + + ma->use_nodes = true; + + /* try to add an image node */ + imanode = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); + + ima = proj_paint_image_create(op, bmain); + imanode->id = &ima->id; + + nodeSetActive(ntree, imanode); + + ntreeUpdateTree(CTX_data_main(C), ntree); + } + else { + MTex *mtex = add_mtex_id(&ma->id, -1); + + /* successful creation of mtex layer, now create set */ + if (mtex) { + int type = MAP_COL; + int type_id = 0; + + if (op) { + int i; + type = RNA_enum_get(op->ptr, "type"); + + for (i = 0; i < ARRAY_SIZE(layer_type_items); i++) { + if (layer_type_items[i].value == type) { + type_id = i; + break; + } + } + } + + mtex->tex = add_texture(bmain, DATA_(layer_type_items[type_id].name)); + mtex->mapto = type; + + if (mtex->tex) { + ima = mtex->tex->ima = proj_paint_image_create(op, bmain); + } + + WM_event_add_notifier(C, NC_TEXTURE | NA_ADDED, mtex->tex); + } + } + + if (ima) { + BKE_texpaint_slot_refresh_cache(scene, ma); + BKE_image_signal(ima, NULL, IMA_SIGNAL_USER_NEW_IMAGE); + WM_event_add_notifier(C, NC_IMAGE | NA_ADDED, ima); + DAG_id_tag_update(&ma->id, 0); + ED_area_tag_redraw(CTX_wm_area(C)); + + return true; + } + } + + return false; +} + +static int texture_paint_add_texture_paint_slot_exec(bContext *C, wmOperator *op) +{ + if (proj_paint_add_slot(C, op)) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + + +static int texture_paint_add_texture_paint_slot_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + char imagename[MAX_ID_NAME - 2]; + Object *ob = CTX_data_active_object(C); + Material *ma = give_current_material(ob, ob->actcol); + int type = RNA_enum_get(op->ptr, "type"); + + if (!ma) { + ma = BKE_material_add(CTX_data_main(C), "Material"); + /* no material found, just assign to first slot */ + assign_material(ob, ma, ob->actcol, BKE_MAT_ASSIGN_USERPREF); + } + + type = RNA_enum_from_value(layer_type_items, type); + + /* get the name of the texture layer type */ + BLI_assert(type != -1); + + /* take the second letter to avoid the ID identifier */ + BLI_snprintf(imagename, FILE_MAX, "%s %s", &ma->id.name[2], layer_type_items[type].name); + + RNA_string_set(op->ptr, "name", imagename); + return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); +} + +#define IMA_DEF_NAME N_("Untitled") + + +void PAINT_OT_add_texture_paint_slot(wmOperatorType *ot) +{ + PropertyRNA *prop; + static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + /* identifiers */ + ot->name = "Add Texture Paint Slot"; + ot->description = "Add a texture paint slot"; + ot->idname = "PAINT_OT_add_texture_paint_slot"; + + /* api callbacks */ + ot->invoke = texture_paint_add_texture_paint_slot_invoke; + ot->exec = texture_paint_add_texture_paint_slot_exec; + ot->poll = ED_operator_region_view3d_active; + + /* flags */ + ot->flag = OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", layer_type_items, 0, "Type", "Merge method to use"); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_string(ot->srna, "name", IMA_DEF_NAME, MAX_ID_NAME - 2, "Name", "Image datablock name"); + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_float_array_default(prop, default_color); + RNA_def_boolean(ot->srna, "alpha", 1, "Alpha", "Create an image with an alpha channel"); + RNA_def_enum(ot->srna, "generated_type", image_generated_type_items, IMA_GENTYPE_BLANK, + "Generated Type", "Fill the image with a grid for UV map testing"); + RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); +} + +static int texture_paint_delete_texture_paint_slot_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + Material *ma; + bool is_bi = BKE_scene_uses_blender_internal(scene); + TexPaintSlot *slot; + + /* not supported for node-based engines */ + if (!ob || !is_bi) + return OPERATOR_CANCELLED; + + ma = give_current_material(ob, ob->actcol); + + if (!ma->texpaintslot || ma->use_nodes) + return OPERATOR_CANCELLED; + + slot = ma->texpaintslot + ma->paint_active_slot; + + if (ma->mtex[slot->index]->tex) + id_us_min(&ma->mtex[slot->index]->tex->id); + MEM_freeN(ma->mtex[slot->index]); + ma->mtex[slot->index] = NULL; + + BKE_texpaint_slot_refresh_cache(scene, ma); + DAG_id_tag_update(&ma->id, 0); + WM_event_add_notifier(C, NC_MATERIAL, CTX_data_scene(C)); + /* we need a notifier for data change since we change the displayed modifier uvs */ + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + return OPERATOR_FINISHED; +} + + +void PAINT_OT_delete_texture_paint_slot(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Texture Paint Slot"; + ot->description = "Delete selected texture paint slot"; + ot->idname = "PAINT_OT_delete_texture_paint_slot"; + + /* api callbacks */ + ot->exec = texture_paint_delete_texture_paint_slot_exec; + ot->poll = ED_operator_region_view3d_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index d8f7a3d8e05..c3b7ec60e71 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -38,7 +38,9 @@ struct bglMats; struct Brush; struct ImagePool; struct ColorSpace; +struct ColorManagedDisplay; struct ListBase; +struct Material; struct Mesh; struct MTex; struct Object; @@ -65,7 +67,7 @@ typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final); typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); -struct PaintStroke *paint_stroke_new(struct bContext *C, +struct PaintStroke *paint_stroke_new(struct bContext *C, struct wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, StrokeRedraw redraw, StrokeDone done, int event_type); @@ -82,8 +84,10 @@ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf); int paint_stroke_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); int paint_stroke_exec(struct bContext *C, struct wmOperator *op); void paint_stroke_cancel(struct bContext *C, struct wmOperator *op); +bool paint_stroke_flipped(struct PaintStroke *stroke); struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); void *paint_stroke_mode_data(struct PaintStroke *stroke); +float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); int paint_poll(struct bContext *C); void paint_cursor_start(struct bContext *C, int (*poll)(struct bContext *C)); @@ -117,7 +121,7 @@ void PAINT_OT_weight_gradient(struct wmOperatorType *ot); void PAINT_OT_vertex_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_vertex_paint(struct wmOperatorType *ot); -unsigned int vpaint_get_current_col(struct VPaint *vp); +unsigned int vpaint_get_current_col(struct Scene *scene, struct VPaint *vp); /* paint_vertex_proj.c */ @@ -144,32 +148,41 @@ typedef struct ImagePaintPartialRedraw { #define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) int image_texture_paint_poll(struct bContext *C); -void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask); -void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile); +void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate); +void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj); void image_undo_remove_masks(void); +void image_undo_init_locks(void); +void image_undo_end_locks(void); + void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); struct ImagePaintPartialRedraw *get_imapaintpartial(void); void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr); void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy); -void *paint_2d_new_stroke(struct bContext *, struct wmOperator *); +void *paint_2d_new_stroke(struct bContext *, struct wmOperator *, int mode); void paint_2d_redraw(const bContext *C, void *ps, bool final); void paint_2d_stroke_done(void *ps); -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], int eraser); +void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size); +void paint_2d_bucket_fill(const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps); +void paint_2d_gradient_fill (const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); void *paint_proj_new_stroke(struct bContext *C, struct Object *ob, const float mouse[2], int mode); -void paint_proj_stroke(struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2]); -void paint_proj_redraw(const bContext *C, void *pps, bool final); +void paint_proj_stroke(const struct bContext *C, void *ps, const float prevmval_i[2], const float mval_i[2], const bool eraser, float pressure, float distance, float size); +void paint_proj_redraw(const struct bContext *C, void *pps, bool final); void paint_proj_stroke_done(void *ps); + +void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, float pressure, float color[3], struct ColorManagedDisplay *display); +bool paint_use_opacity_masking(struct Brush *brush); void paint_brush_init_tex(struct Brush *brush); void paint_brush_exit_tex(struct Brush *brush); void PAINT_OT_grab_clone(struct wmOperatorType *ot); void PAINT_OT_sample_color(struct wmOperatorType *ot); +void PAINT_OT_brush_colors_flip(struct wmOperatorType *ot); void PAINT_OT_texture_paint_toggle(struct wmOperatorType *ot); void PAINT_OT_project_image(struct wmOperatorType *ot); void PAINT_OT_image_from_view(struct wmOperatorType *ot); - -/* new texture painting */ +void PAINT_OT_add_texture_paint_slot(struct wmOperatorType *ot); +void PAINT_OT_delete_texture_paint_slot(struct wmOperatorType *ot); void PAINT_OT_image_paint(struct wmOperatorType *ot); /* uv sculpting */ @@ -202,7 +215,10 @@ float paint_calc_object_space_radius(struct ViewContext *vc, const float center[ float paint_get_tex_pixel(struct MTex *mtex, float u, float v, struct ImagePool *pool, int thread); void paint_get_tex_pixel_col(struct MTex *mtex, float u, float v, float rgba[4], struct ImagePool *pool, int thread, bool convert, struct ColorSpace *colorspace); -void paint_sample_color(const struct bContext *C, struct ARegion *ar, int x, int y); +void paint_sample_color(bContext *C, struct ARegion *ar, int x, int y, bool texpaint_proj, bool palette); + +void paint_stroke_operator_properties(struct wmOperatorType *ot); + void BRUSH_OT_curve_preset(struct wmOperatorType *ot); void PAINT_OT_face_select_linked(struct wmOperatorType *ot); @@ -213,8 +229,10 @@ void PAINT_OT_face_select_reveal(struct wmOperatorType *ot); void PAINT_OT_vert_select_all(struct wmOperatorType *ot); void PAINT_OT_vert_select_ungrouped(struct wmOperatorType *ot); + int vert_paint_poll(struct bContext *C); int mask_paint_poll(struct bContext *C); +int paint_curve_poll(struct bContext *C); int facemask_paint_poll(struct bContext *C); void flip_v3_v3(float out[3], const float in[3], const char symm); @@ -250,10 +268,36 @@ void PAINT_OT_hide_show(struct wmOperatorType *ot); typedef enum { PAINT_MASK_FLOOD_VALUE, + PAINT_MASK_FLOOD_VALUE_INVERSE, PAINT_MASK_INVERT } PaintMaskFloodMode; void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); void PAINT_OT_mask_lasso_gesture(struct wmOperatorType *ot); +/* paint_curve.c */ +void PAINTCURVE_OT_new(struct wmOperatorType *ot); +void PAINTCURVE_OT_add_point(struct wmOperatorType *ot); +void PAINTCURVE_OT_delete_point(struct wmOperatorType *ot); +void PAINTCURVE_OT_select(struct wmOperatorType *ot); +void PAINTCURVE_OT_slide(struct wmOperatorType *ot); +void PAINTCURVE_OT_draw(struct wmOperatorType *ot); +void PAINTCURVE_OT_cursor(struct wmOperatorType *ot); + +/* image painting blur kernel */ +typedef struct { + float *wdata; /* actual kernel */ + int side; /* kernel side */ + int side_squared; /* data side */ + int pixel_len; /* pixels around center that kernel is wide */ +} BlurKernel; + +enum BlurKernelType; +/* can be extended to other blur kernels later */ +BlurKernel *paint_new_blur_kernel(struct Brush *br, bool proj); +void paint_delete_blur_kernel(BlurKernel *); + +/* paint curve defines */ +#define PAINT_CURVE_NUM_SEGMENTS 40 + #endif /* __PAINT_INTERN_H__ */ diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 49b62140fe6..25f22996050 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -67,6 +67,13 @@ #include <stdlib.h> +static EnumPropertyItem mode_items[] = { + {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"}, + {PAINT_MASK_FLOOD_VALUE_INVERSE, "VALUE_INVERSE", 0, "Value Inverted", "Set mask to the level specified by the inverted 'value' property"}, + {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"}, + {0}}; + + static void mask_flood_fill_set_elem(float *elem, PaintMaskFloodMode mode, float value) @@ -75,6 +82,9 @@ static void mask_flood_fill_set_elem(float *elem, case PAINT_MASK_FLOOD_VALUE: (*elem) = value; break; + case PAINT_MASK_FLOOD_VALUE_INVERSE: + (*elem) = 1.0f - value; + break; case PAINT_MASK_INVERT: (*elem) = 1.0f - (*elem); break; @@ -86,32 +96,26 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) ARegion *ar = CTX_wm_region(C); struct Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); - struct MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); PaintMaskFloodMode mode; float value; - DerivedMesh *dm; PBVH *pbvh; PBVHNode **nodes; int totnode, i; + bool multires; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; mode = RNA_enum_get(op->ptr, "mode"); value = RNA_float_get(op->ptr, "value"); - BKE_sculpt_mask_layers_ensure(ob, mmd); - - dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - pbvh = dm->getPBVH(ob, dm); - ob->sculpt->pbvh = pbvh; - - ob->sculpt->show_diffuse_color = sd->flags & SCULPT_SHOW_DIFFUSE; - pbvh_show_diffuse_color_set(pbvh, ob->sculpt->show_diffuse_color); + BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); sculpt_undo_push_begin("Mask flood fill"); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (i = 0; i < totnode; i++) { PBVHVertexIter vi; @@ -122,10 +126,13 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_redraw(nodes[i]); - if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + if (multires) + BKE_pbvh_node_mark_normals_update(nodes[i]); } - + + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + sculpt_undo_push_end(); if (nodes) @@ -133,16 +140,13 @@ static int mask_flood_fill_exec(bContext *C, wmOperator *op) ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; } void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) { - static EnumPropertyItem mode_items[] = { - {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"}, - {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"}, - {0}}; - /* identifiers */ ot->name = "Mask Flood Fill"; ot->idname = "PAINT_OT_mask_flood_fill"; @@ -185,7 +189,7 @@ static void flip_plane(float out[4], const float in[4], const char symm) out[3] = in[3]; } -int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNUSED(extend)) +int ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select, bool UNUSED(extend)) { Sculpt *sd = vc->scene->toolsettings->sculpt; BoundBox bb; @@ -195,10 +199,9 @@ int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNU ARegion *ar = vc->ar; struct Scene *scene = vc->scene; Object *ob = vc->obact; - struct MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); PaintMaskFloodMode mode; float value; - DerivedMesh *dm; + bool multires; PBVH *pbvh; PBVHNode **nodes; int totnode, i, symmpass; @@ -210,16 +213,11 @@ int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNU /* transform the clip planes in object space */ view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, &mats); ED_view3d_clipping_calc(&bb, clip_planes, &mats, rect); - mul_m4_fl(clip_planes, -1.0f); - - BKE_sculpt_mask_layers_ensure(ob, mmd); + negate_m4(clip_planes); - dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); - pbvh = dm->getPBVH(ob, dm); - ob->sculpt->pbvh = pbvh; - - ob->sculpt->show_diffuse_color = sd->flags & SCULPT_SHOW_DIFFUSE; - pbvh_show_diffuse_color_set(pbvh, ob->sculpt->show_diffuse_color); + BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); sculpt_undo_push_begin("Mask box fill"); @@ -238,7 +236,7 @@ int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNU BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (i = 0; i < totnode; i++) { PBVHVertexIter vi; bool any_masked = false; @@ -251,8 +249,8 @@ int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNU sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); BKE_pbvh_node_mark_redraw(nodes[i]); - if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + if (multires) + BKE_pbvh_node_mark_normals_update(nodes[i]); } mask_flood_fill_set_elem(vi.mask, mode, value); } @@ -264,17 +262,22 @@ int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNU } } + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + sculpt_undo_push_end(); ED_region_tag_redraw(ar); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; } typedef struct LassoMaskData { struct ViewContext *vc; float projviewobjmat[4][4]; - bool *px; + BLI_bitmap *px; int width; rcti rect; /* bounding box for scanfilling */ int symmpass; @@ -304,19 +307,19 @@ static bool is_effected_lasso(LassoMaskData *data, float co[3]) scr_co_s[0] -= data->rect.xmin; scr_co_s[1] -= data->rect.ymin; - return data->px[scr_co_s[1] * data->width + scr_co_s[0]]; + return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); } static void mask_lasso_px_cb(int x, int y, void *user_data) { struct LassoMaskData *data = user_data; - data->px[(y * data->width) + x] = true; + BLI_BITMAP_ENABLE(data->px, (y * data->width) + x); } static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) { int mcords_tot; - int (*mcords)[2] = (int (*)[2])WM_gesture_lasso_path_to_array(C, op, &mcords_tot); + const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); if (mcords) { float clip_planes[4][4], clip_planes_final[4][4]; @@ -325,16 +328,15 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) Object *ob; ViewContext vc; LassoMaskData data; + struct Scene *scene = CTX_data_scene(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - struct MultiresModifierData *mmd; - DerivedMesh *dm; PBVH *pbvh; PBVHNode **nodes; int totnode, i, symmpass; - PaintMaskFloodMode mode = PAINT_MASK_FLOOD_VALUE; - bool select = true; /* TODO: see how to implement deselection */ - float value = select ? 1.0 : 0.0; + bool multires; + PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); + float value = RNA_float_get(op->ptr, "value"); /* Calculations of individual vertices are done in 2D screen space to diminish the amount of * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle @@ -347,26 +349,21 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) ob = vc.obact; ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - BLI_lasso_boundbox(&data.rect, (const int (*)[2])mcords, mcords_tot); + BLI_lasso_boundbox(&data.rect, mcords, mcords_tot); data.width = data.rect.xmax - data.rect.xmin; - data.px = MEM_callocN(sizeof(*data.px) * data.width * (data.rect.ymax - data.rect.ymin), "lasso_mask_pixel_buffer"); + data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); fill_poly_v2i_n( data.rect.xmin, data.rect.ymin, data.rect.xmax, data.rect.ymax, - (const int (*)[2])mcords, mcords_tot, + mcords, mcords_tot, mask_lasso_px_cb, &data); ED_view3d_clipping_calc(&bb, clip_planes, &mats, &data.rect); - mul_m4_fl(clip_planes, -1.0f); + negate_m4(clip_planes); - mmd = BKE_sculpt_multires_active(vc.scene, ob); - BKE_sculpt_mask_layers_ensure(ob, mmd); - dm = mesh_get_derived_final(vc.scene, ob, CD_MASK_BAREMESH); - pbvh = dm->getPBVH(ob, dm); - ob->sculpt->pbvh = pbvh; - - ob->sculpt->show_diffuse_color = sd->flags & SCULPT_SHOW_DIFFUSE; - pbvh_show_diffuse_color_set(pbvh, ob->sculpt->show_diffuse_color); + BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true); + pbvh = ob->sculpt->pbvh; + multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); sculpt_undo_push_begin("Mask lasso fill"); @@ -388,7 +385,7 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) /* gather nodes inside lasso's enclosing rectangle (should greatly help with bigger meshes) */ BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (i = 0; i < totnode; i++) { PBVHVertexIter vi; bool any_masked = false; @@ -401,8 +398,8 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); BKE_pbvh_node_mark_redraw(nodes[i]); - if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) - multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + if (multires) + BKE_pbvh_node_mark_normals_update(nodes[i]); } mask_flood_fill_set_elem(vi.mask, mode, value); @@ -415,12 +412,17 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) } } + if (multires) + multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED); + sculpt_undo_push_end(); ED_region_tag_redraw(vc.ar); MEM_freeN((void *)mcords); MEM_freeN(data.px); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + return OPERATOR_FINISHED; } return OPERATOR_PASS_THROUGH; @@ -444,4 +446,8 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath); + + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float(ot->srna, "value", 1.0, 0, 1.0, "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1); } diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 543463cd5f3..dc6be9caa53 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -40,7 +40,7 @@ #include "BKE_paint.h" #include "BKE_main.h" -#include "ED_sculpt.h" +#include "ED_paint.h" #include "ED_screen.h" #include "ED_image.h" #include "UI_resources.h" @@ -149,11 +149,113 @@ static void BRUSH_OT_scale_size(wmOperatorType *ot) RNA_def_float(ot->srna, "scalar", 1, 0, 2, "Scalar", "Factor to scale brush size by", 0, 2); } +/* Palette operators */ + +static int palette_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Main *bmain = CTX_data_main(C); + Palette *palette; + + palette = BKE_palette_add(bmain, "Palette"); + + BKE_paint_palette_set(paint, palette); + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Palette"; + ot->description = "Add new palette"; + ot->idname = "PALETTE_OT_new"; + + /* api callbacks */ + ot->exec = palette_new_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int palette_poll(bContext *C) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + + if (paint && paint->palette != NULL) + return true; + + return false; +} + +static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = paint->brush; + PaintMode mode = BKE_paintmode_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *color = BKE_palette_color_add(palette); + + if (ELEM(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX)) { + copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); + color->value = 0.0; + } + else if (mode == PAINT_WEIGHT) { + zero_v3(color->rgb); + color->value = brush->weight; + } + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_color_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Palette Color"; + ot->description = "Add new color to active palette"; + ot->idname = "PALETTE_OT_color_add"; + + /* api callbacks */ + ot->exec = palette_color_add_exec; + ot->poll = palette_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = paint->palette; + PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); + + BKE_palette_color_remove(palette, color); + + return OPERATOR_FINISHED; +} + +static void PALETTE_OT_color_delete(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Palette Color"; + ot->description = "Remove active color from palette"; + ot->idname = "PALETTE_OT_color_delete"; + + /* api callbacks */ + ot->exec = palette_color_delete_exec; + ot->poll = palette_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + + static int vertex_color_set_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Object *obact = CTX_data_active_object(C); - unsigned int paintcol = vpaint_get_current_col(scene->toolsettings->vpaint); + unsigned int paintcol = vpaint_get_current_col(scene, scene->toolsettings->vpaint); if (ED_vpaint_fill(obact, paintcol)) { ED_region_tag_redraw(CTX_wm_region(C)); // XXX - should redraw all 3D views @@ -332,6 +434,7 @@ static int brush_generic_tool_set(Main *bmain, Paint *paint, const int tool, if (brush) { BKE_paint_brush_set(paint, brush); BKE_paint_invalidate_overlay_all(); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); return OPERATOR_FINISHED; } @@ -564,7 +667,7 @@ static void stencil_set_target(StencilControlData *scd) scd->lenorig = len_v2(mdiff); - scd->init_angle = atan2(mdiff[1], mdiff[0]); + scd->init_angle = atan2f(mdiff[1], mdiff[0]); } static int stencil_control_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -660,7 +763,7 @@ static void stencil_control_calculate(StencilControlData *scd, const int mval[2] { float angle; sub_v2_v2v2(mdiff, mvalf, scd->pos_target); - angle = atan2(mdiff[1], mdiff[0]); + angle = atan2f(mdiff[1], mdiff[0]); angle = scd->init_rot + angle - scd->init_angle; if (angle < 0.0f) angle += (float)(2 * M_PI); @@ -813,7 +916,7 @@ static int stencil_fit_image_aspect_exec(bContext *C, wmOperator *op) stencil_area = br->stencil_dimension[0] * br->stencil_dimension[1]; } - factor = sqrt(stencil_area / orig_area); + factor = sqrtf(stencil_area / orig_area); if (do_mask) { br->mask_stencil_dimension[0] = factor * aspx; @@ -928,8 +1031,37 @@ static void ed_keymap_stencil(wmKeyMap *keymap) /**************************** registration **********************************/ +void ED_operatormacros_paint(void) +{ + wmOperatorType *ot; + wmOperatorTypeMacro *otmacro; + + ot = WM_operatortype_append_macro("PAINTCURVE_OT_add_point_slide", "Add Curve Point and Slide", + "Add new curve point and slide it", OPTYPE_UNDO); + ot->description = "Add new curve point and slide it"; + WM_operatortype_macro_define(ot, "PAINTCURVE_OT_add_point"); + otmacro = WM_operatortype_macro_define(ot, "PAINTCURVE_OT_slide"); + RNA_boolean_set(otmacro->ptr, "align", true); + RNA_boolean_set(otmacro->ptr, "select", false); +} + + void ED_operatortypes_paint(void) { + /* palette */ + WM_operatortype_append(PALETTE_OT_new); + WM_operatortype_append(PALETTE_OT_color_add); + WM_operatortype_append(PALETTE_OT_color_delete); + + /* paint curve */ + WM_operatortype_append(PAINTCURVE_OT_new); + WM_operatortype_append(PAINTCURVE_OT_add_point); + WM_operatortype_append(PAINTCURVE_OT_delete_point); + WM_operatortype_append(PAINTCURVE_OT_select); + WM_operatortype_append(PAINTCURVE_OT_slide); + WM_operatortype_append(PAINTCURVE_OT_draw); + WM_operatortype_append(PAINTCURVE_OT_cursor); + /* brush */ WM_operatortype_append(BRUSH_OT_add); WM_operatortype_append(BRUSH_OT_scale_size); @@ -950,6 +1082,9 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_grab_clone); WM_operatortype_append(PAINT_OT_project_image); WM_operatortype_append(PAINT_OT_image_from_view); + WM_operatortype_append(PAINT_OT_brush_colors_flip); + WM_operatortype_append(PAINT_OT_add_texture_paint_slot); + WM_operatortype_append(PAINT_OT_delete_texture_paint_slot); /* weight */ WM_operatortype_append(PAINT_OT_weight_paint_toggle); @@ -1119,12 +1254,44 @@ static void paint_partial_visibility_keys(wmKeyMap *keymap) RNA_enum_set(kmi->ptr, "area", PARTIALVIS_ALL); } + +static void paint_keymap_curve(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_add_point_slide", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", true); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_slide", ACTIONMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_slide", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "align", true); + kmi = WM_keymap_add_item(keymap, "PAINTCURVE_OT_select", AKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "toggle", true); + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_cursor", ACTIONMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_delete_point", XKEY, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "PAINTCURVE_OT_draw", RETKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "PAINTCURVE_OT_draw", PADENTER, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", EVT_TWEAK_S, KM_ANY, 0, 0); + WM_keymap_add_item(keymap, "TRANSFORM_OT_rotate", RKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "TRANSFORM_OT_resize", SKEY, KM_PRESS, 0, 0); +} + void ED_keymap_paint(wmKeyConfig *keyconf) { wmKeyMap *keymap; wmKeyMapItem *kmi; int i; + keymap = WM_keymap_find(keyconf, "Paint Curve", 0, 0); + keymap->poll = paint_curve_poll; + + paint_keymap_curve(keymap); + /* Sculpt mode */ keymap = WM_keymap_find(keyconf, "Sculpt", 0, 0); keymap->poll = sculpt_mode_poll; @@ -1191,7 +1358,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "create_missing", 1); /* */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.sculpt.brush.stroke_method"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", SKEY, KM_PRESS, KM_SHIFT, 0); @@ -1225,7 +1392,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.texture_angle_source_random"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method"); /* Weight Paint mode */ @@ -1250,7 +1417,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) ed_keymap_stencil(keymap); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.vertex_paint.brush.stroke_method"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", MKEY, KM_PRESS, 0, 0); /* face mask toggle */ @@ -1283,6 +1450,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_image_paint", LEFTMOUSE, KM_PRESS, 0, 0)->ptr, "mode", BRUSH_STROKE_NORMAL); RNA_enum_set(WM_keymap_add_item(keymap, "PAINT_OT_image_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "mode", BRUSH_STROKE_INVERT); + WM_keymap_add_item(keymap, "PAINT_OT_brush_colors_flip", XKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "PAINT_OT_grab_clone", RIGHTMOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "PAINT_OT_sample_color", SKEY, KM_PRESS, 0, 0); @@ -1301,7 +1469,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", RKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.texture_angle_source_random"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", AKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", EKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.image_paint.brush.stroke_method"); /* face-mask mode */ diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 7b9121446f5..9c4701d0a01 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -36,16 +36,19 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_rand.h" +#include "BLI_listbase.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" +#include "DNA_curve_types.h" #include "RNA_access.h" #include "BKE_context.h" #include "BKE_paint.h" #include "BKE_brush.h" +#include "BKE_curve.h" #include "BKE_colortools.h" #include "BKE_image.h" @@ -65,6 +68,12 @@ #include <float.h> #include <math.h> +// #define DEBUG_TIME + +#ifdef DEBUG_TIME +# include "PIL_time_utildefines.h" +#endif + typedef struct PaintSample { float mouse[2]; float pressure; @@ -72,7 +81,7 @@ typedef struct PaintSample { typedef struct PaintStroke { void *mode_data; - void *smooth_stroke_cursor; + void *stroke_cursor; wmTimer *timer; /* Cached values */ @@ -81,6 +90,9 @@ typedef struct PaintStroke { Brush *brush; UnifiedPaintSettings *ups; + /* used for lines and curves */ + ListBase line; + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs * to smooth the stroke */ PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; @@ -88,6 +100,8 @@ typedef struct PaintStroke { int cur_sample; float last_mouse_position[2]; + /* space distance covered so far */ + float stroke_distance; /* Set whether any stroke step has yet occurred * e.g. in sculpt mode, stroke doesn't start until cursor @@ -104,7 +118,7 @@ typedef struct PaintStroke { float cached_size_pressure; /* last pressure will store last pressure value for use in interpolation for space strokes */ float last_pressure; - + int stroke_mode; float zoom_2d; int pen_flip; @@ -116,18 +130,17 @@ typedef struct PaintStroke { StrokeDone done; } PaintStroke; -/*** Cursor ***/ -static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) +/*** Cursors ***/ +static void paint_draw_smooth_cursor(bContext *C, int x, int y, void *customdata) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); PaintStroke *stroke = customdata; - if (stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) { - glColor4ubv(paint->paint_cursor_col); + if (stroke && brush) { glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); - + glColor4ubv(paint->paint_cursor_col); sdrawline(x, y, (int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1]); glDisable(GL_BLEND); @@ -135,35 +148,65 @@ static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata } } -/* if this is a tablet event, return tablet pressure and set *pen_flip - * to 1 if the eraser tool is being used, 0 otherwise */ -static float event_tablet_data(const wmEvent *event, int *pen_flip) +static void paint_draw_line_cursor(bContext *C, int x, int y, void *customdata) { - int erasor = 0; - float pressure = 1; + Paint *paint = BKE_paint_get_active_from_context(C); + PaintStroke *stroke = customdata; - if (event->tablet_data) { - wmTabletData *wmtab = event->tablet_data; + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); - erasor = (wmtab->Active == EVT_TABLET_ERASER); - pressure = (wmtab->Active != EVT_TABLET_NONE) ? wmtab->Pressure : 1; - } + glEnable(GL_LINE_STIPPLE); + glLineStipple(3, 0xAAAA); + + glColor4ub(0, 0, 0, paint->paint_cursor_col[3]); + glLineWidth(3.0); + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); - if (pen_flip) - (*pen_flip) = erasor; + glColor4ub(255, 255, 255, paint->paint_cursor_col[3]); + glLineWidth(1.0); + sdrawline((int)stroke->last_mouse_position[0], (int)stroke->last_mouse_position[1], + x, y); - return pressure; + glDisable(GL_LINE_STIPPLE); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); } +static bool paint_tool_require_location(Brush *brush, PaintMode mode) +{ + switch (mode) { + case PAINT_SCULPT: + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_THUMB)) + { + return false; + } + else { + return true; + } + default: + break; + } + + return true; +} /* Initialize the stroke cache variants from operator properties */ -static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, - struct PaintStroke *stroke, - const float mouse[2], float pressure) +static bool paint_brush_update(bContext *C, + Brush *brush, + PaintMode mode, + struct PaintStroke *stroke, + const float mouse_init[2], + float mouse[2], float pressure, + float location[3]) { Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - + UnifiedPaintSettings *ups = stroke->ups; + bool location_sampled = false; + bool location_success = false; /* XXX: Use pressure value from first brush step for brushes which don't * support strokes (grab, thumb). They depends on initial state and * brush coord/pressure/etc. @@ -176,6 +219,9 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, copy_v2_v2(ups->mask_tex_mouse, mouse); stroke->cached_size_pressure = pressure; + ups->do_linear_conversion = false; + ups->colorspace = NULL; + /* check here if color sampling the main brush should do color conversion. This is done here * to avoid locking up to get the image buffer during sampling */ if (brush->mtex.tex && brush->mtex.tex->type == TEX_IMAGE && brush->mtex.tex->ima) { @@ -224,14 +270,14 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, else { copy_v2_v2(ups->tex_mouse, mouse); } - } - /* take care of mask texture, if any */ - if (brush->mask_mtex.tex) { - if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - BKE_brush_randomize_texture_coordinates(ups, true); - else { - copy_v2_v2(ups->mask_tex_mouse, mouse); + /* take care of mask texture, if any */ + if (brush->mask_mtex.tex) { + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + BKE_brush_randomize_texture_coordinates(ups, true); + else { + copy_v2_v2(ups->mask_tex_mouse, mouse); + } } } @@ -243,18 +289,21 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, const float dx = mouse[0] - stroke->initial_mouse[0]; const float dy = mouse[1] - stroke->initial_mouse[1]; - ups->anchored_size = ups->pixel_radius = sqrt(dx * dx + dy * dy); + ups->anchored_size = ups->pixel_radius = sqrtf(dx * dx + dy * dy); - ups->brush_rotation = atan2(dx, dy) + M_PI; + ups->brush_rotation = atan2f(dx, dy) + M_PI; if (brush->flag & BRUSH_EDGE_TO_EDGE) { - float out[3]; - halfway[0] = dx * 0.5f + stroke->initial_mouse[0]; halfway[1] = dy * 0.5f + stroke->initial_mouse[1]; if (stroke->get_location) { - if (stroke->get_location(C, out, halfway)) { + if (stroke->get_location(C, location, halfway)) { + hit = true; + location_sampled = true; + location_success = true; + } + else if (!paint_tool_require_location(brush, mode)) { hit = true; } } @@ -265,30 +314,69 @@ static void paint_brush_update(bContext *C, Brush *brush, PaintMode mode, if (hit) { copy_v2_v2(ups->anchored_initial_mouse, halfway); copy_v2_v2(ups->tex_mouse, halfway); + copy_v2_v2(ups->mask_tex_mouse, halfway); + copy_v2_v2(mouse, halfway); ups->anchored_size /= 2.0f; ups->pixel_radius /= 2.0f; + stroke->stroke_distance = ups->pixel_radius; } - else + else { copy_v2_v2(ups->anchored_initial_mouse, stroke->initial_mouse); - + copy_v2_v2(mouse, stroke->initial_mouse); + stroke->stroke_distance = ups->pixel_radius; + } + ups->pixel_radius /= stroke->zoom_2d; ups->draw_anchored = true; } else if (brush->flag & BRUSH_RAKE) { - paint_calculate_rake_rotation(ups, mouse); + /* here we are using the initial mouse coordinate because we do not want the rake + * result to depend on jittering */ + if (!stroke->brush_init) + copy_v2_v2(ups->last_rake, mouse_init); + else + paint_calculate_rake_rotation(ups, mouse_init); + } + + if (!location_sampled) { + if (stroke->get_location) { + if (stroke->get_location(C, location, mouse)) + location_success = true; + else if (!paint_tool_require_location(brush, mode)) + location_success = true; + } + else { + zero_v3(location); + location_success = true; + } } + + return location_success; } +static bool paint_stroke_use_jitter(PaintMode mode, Brush *brush, bool invert) +{ + bool use_jitter = (brush->flag & BRUSH_ABSOLUTE_JITTER) ? + (brush->jitter_absolute != 0) : (brush->jitter != 0); + + /* jitter-ed brush gives weird and unpredictable result for this + * kinds of stroke, so manually disable jitter usage (sergey) */ + use_jitter &= (brush->flag & (BRUSH_DRAG_DOT | BRUSH_ANCHORED)) == 0; + use_jitter &= (!ELEM(mode, PAINT_TEXTURE_2D, PAINT_TEXTURE_PROJECTIVE) || + !(invert && brush->imagepaint_tool == PAINT_TOOL_CLONE)); + + + return use_jitter; +} /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float mouse_in[2], float pressure) { Scene *scene = CTX_data_scene(C); - wmWindow *window = CTX_wm_window(C); - ARegion *ar = CTX_wm_region(C); Paint *paint = BKE_paint_get_active_from_context(C); PaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); PaintStroke *stroke = op->customdata; + UnifiedPaintSettings *ups = stroke->ups; float mouse_out[2]; PointerRNA itemptr; float location[3]; @@ -314,9 +402,7 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float copy_v2_v2(stroke->last_mouse_position, mouse_in); stroke->last_pressure = pressure; - paint_brush_update(C, brush, mode, stroke, mouse_in, pressure); - - { + if (paint_stroke_use_jitter(mode, brush, stroke->stroke_mode == BRUSH_STROKE_INVERT)) { float delta[2]; float factor = stroke->zoom_2d; @@ -334,16 +420,17 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float add_v2_v2v2(mouse_out, mouse_in, delta); } } + else { + copy_v2_v2(mouse_out, mouse_in); + } - /* TODO: can remove the if statement once all modes have this */ - if (stroke->get_location) - stroke->get_location(C, location, mouse_out); - else - zero_v3(location); + if (!paint_brush_update(C, brush, mode, stroke, mouse_in, mouse_out, pressure, location)) { + return; + } /* Add to stroke */ RNA_collection_add(op->ptr, "stroke", &itemptr); - + RNA_float_set(&itemptr, "size", ups->pixel_radius); RNA_float_set_array(&itemptr, "location", location); RNA_float_set_array(&itemptr, "mouse", mouse_out); RNA_boolean_set(&itemptr, "pen_flip", stroke->pen_flip); @@ -354,20 +441,12 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float /* don't record this for now, it takes up a lot of memory when doing long * strokes with small brush size, and operators have register disabled */ RNA_collection_clear(op->ptr, "stroke"); - - /* always redraw region if brush is shown */ - if (ar && (paint->flags & PAINT_SHOW_BRUSH)) - WM_paint_cursor_tag_redraw(window, ar); } /* 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) { - output[0] = sample->mouse[0]; - output[1] = sample->mouse[1]; - *outpressure = sample->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; @@ -383,6 +462,11 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outp output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u; *outpressure = sample->pressure * v + stroke->last_pressure * u; } + else { + output[0] = sample->mouse[0]; + output[1] = sample->mouse[1]; + *outpressure = sample->pressure; + } return 1; } @@ -405,6 +489,55 @@ static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, return max_ff(1.0, size_clamp * spacing / 50.0f); } + + +static float paint_stroke_overlapped_curve(Brush *br, float x, float spacing) +{ + int i; + const int n = 100 / spacing; + const float h = spacing / 50.0f; + const float x0 = x - 1; + + float sum; + + sum = 0; + for (i = 0; i < n; i++) { + float xx; + + xx = fabsf(x0 + i * h); + + if (xx < 1.0f) + sum += BKE_brush_curve_strength(br, xx, 1); + } + + return sum; +} + +static float paint_stroke_integrate_overlap(Brush *br, float factor) +{ + int i; + int m; + float g; + float max; + + float spacing = br->spacing * factor; + + if (!(br->flag & BRUSH_SPACE_ATTEN && (br->spacing < 100))) + return 1.0; + + m = 10; + g = 1.0f / m; + max = 0; + for (i = 0; i < m; i++) { + float overlap = paint_stroke_overlapped_curve(br, i * g, spacing); + + if (overlap > max) + max = overlap; + } + + return 1.0f / max; +} + static float paint_space_stroke_spacing_variable(const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length) { if (BKE_brush_use_size_pressure(scene, stroke->brush)) { @@ -436,40 +569,42 @@ static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mou { const Scene *scene = CTX_data_scene(C); PaintStroke *stroke = op->customdata; - PaintMode mode = BKE_paintmode_get_active_from_context(C); + UnifiedPaintSettings *ups = stroke->ups; int cnt = 0; - if (paint_space_stroke_enabled(stroke->brush, mode)) { - float pressure, dpressure; - float mouse[2], dmouse[2]; - float length; + float pressure, dpressure; + float mouse[2], dmouse[2]; + float length; + float no_pressure_spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); - sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); + sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position); - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; - length = normalize_v2(dmouse); + length = normalize_v2(dmouse); - while (length > 0.0f) { - float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length); - - if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; - pressure = stroke->last_pressure + (spacing / length) * dpressure; + while (length > 0.0f) { + float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length); - paint_brush_stroke_add_step(C, op, mouse, pressure); + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing; + pressure = stroke->last_pressure + (spacing / length) * dpressure; - length -= spacing; - pressure = stroke->last_pressure; - dpressure = final_pressure - stroke->last_pressure; + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, spacing / no_pressure_spacing); - cnt++; - } - else { - break; - } + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, pressure); + + length -= spacing; + pressure = stroke->last_pressure; + dpressure = final_pressure - stroke->last_pressure; + + cnt++; + } + else { + break; } } @@ -479,6 +614,7 @@ static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mou /**** Public API ****/ PaintStroke *paint_stroke_new(bContext *C, + wmOperator *op, StrokeGetLocation get_location, StrokeTestStart test_start, StrokeUpdateStep update_step, @@ -489,6 +625,7 @@ PaintStroke *paint_stroke_new(bContext *C, ToolSettings *toolsettings = CTX_data_tool_settings(C); UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; Brush *br = stroke->brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + float zoomx, zoomy; view3d_set_viewcontext(C, &stroke->vc); if (stroke->vc.v3d) @@ -501,6 +638,19 @@ PaintStroke *paint_stroke_new(bContext *C, stroke->done = done; stroke->event_type = event_type; /* for modal, return event */ stroke->ups = ups; + stroke->stroke_mode = RNA_enum_get(op->ptr, "mode"); + + get_imapaint_zoom(C, &zoomx, &zoomy); + stroke->zoom_2d = max_ff(zoomx, zoomy); + + if ((br->flag & BRUSH_CURVE) && + RNA_struct_property_is_set(op->ptr, "mode")) + { + RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); + } + /* initialize here */ + ups->overlap_factor = 1.0; + ups->stroke_active = true; /* initialize here to avoid initialization conflict with threaded strokes */ curvemapping_initialize(br->curve); @@ -513,8 +663,7 @@ PaintStroke *paint_stroke_new(bContext *C, void paint_stroke_data_free(struct wmOperator *op) { BKE_paint_set_overlay_override(0); - MEM_freeN(op->customdata); - op->customdata = NULL; + MEM_SAFE_FREE(op->customdata); } static void stroke_done(struct bContext *C, struct wmOperator *op) @@ -544,8 +693,10 @@ static void stroke_done(struct bContext *C, struct wmOperator *op) stroke->timer); } - if (stroke->smooth_stroke_cursor) - WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor); + if (stroke->stroke_cursor) + WM_paint_cursor_end(CTX_wm_manager(C), stroke->stroke_cursor); + + BLI_freelistN(&stroke->line); paint_stroke_data_free(op); } @@ -558,7 +709,7 @@ bool paint_space_stroke_enabled(Brush *br, PaintMode mode) static bool sculpt_is_grab_tool(Brush *br) { - return ELEM4(br->sculpt_tool, + return ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, @@ -576,6 +727,16 @@ bool paint_supports_dynamic_size(Brush *br, PaintMode mode) if (sculpt_is_grab_tool(br)) return false; break; + + case PAINT_TEXTURE_2D: /* fall through */ + case PAINT_TEXTURE_PROJECTIVE: + if ((br->imagepaint_tool == PAINT_TOOL_FILL) && + (br->flag & BRUSH_USE_GRADIENT)) + { + return false; + } + break; + default: break; } @@ -585,8 +746,7 @@ bool paint_supports_dynamic_size(Brush *br, PaintMode mode) bool paint_supports_smooth_stroke(Brush *br, PaintMode mode) { if (!(br->flag & BRUSH_SMOOTH_STROKE) || - (br->flag & BRUSH_ANCHORED) || - (br->flag & BRUSH_DRAG_DOT)) + (br->flag & (BRUSH_ANCHORED | BRUSH_DRAG_DOT | BRUSH_LINE))) { return false; } @@ -604,8 +764,8 @@ bool paint_supports_smooth_stroke(Brush *br, PaintMode mode) bool paint_supports_texture(PaintMode mode) { - /* ommit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */ - return ELEM4(mode, PAINT_SCULPT, PAINT_VERTEX, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D); + /* omit: PAINT_WEIGHT, PAINT_SCULPT_UV, PAINT_INVALID */ + return ELEM(mode, PAINT_SCULPT, PAINT_VERTEX, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D); } /* return true if the brush size can change during paint (normally used for pressure) */ @@ -693,28 +853,151 @@ static void paint_stroke_sample_average(const PaintStroke *stroke, /*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/ } +/** + * Slightly different version of spacing for line/curve strokes, + * makes sure the dabs stay on the line path. + */ +static void paint_line_strokes_spacing( + bContext *C, wmOperator *op, PaintStroke *stroke, float spacing, float *length_residue, + const float old_pos[2], const float new_pos[2]) +{ + UnifiedPaintSettings *ups = stroke->ups; + + float mouse[2], dmouse[2]; + float length; + + sub_v2_v2v2(dmouse, new_pos, old_pos); + copy_v2_v2(stroke->last_mouse_position, old_pos); + + length = normalize_v2(dmouse); + + BLI_assert(length >= 0.0f); + + if (length == 0.0f) + return; + + while (length > 0.0f) { + float spacing_final = spacing - *length_residue; + length += *length_residue; + *length_residue = 0.0; + + if (length >= spacing) { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; + + ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); + + stroke->stroke_distance += spacing / stroke->zoom_2d; + paint_brush_stroke_add_step(C, op, mouse, 1.0); + + length -= spacing; + spacing_final = spacing; + } + else { + break; + } + } + + *length_residue = length; +} + + +static void paint_stroke_line_end(bContext *C, wmOperator *op, PaintStroke *stroke, float mouse[2]) +{ + Brush *br = stroke->brush; + if (stroke->stroke_started && (br->flag & BRUSH_LINE)) { + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + + paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0); + paint_space_stroke(C, op, mouse, 1.0); + } +} + +static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *stroke) +{ + Brush *br = stroke->brush; + + if (br->flag & BRUSH_CURVE) { + const Scene *scene = CTX_data_scene(C); + const float spacing = paint_space_stroke_spacing(scene, stroke, 1.0f, 1.0f); + PaintCurve *pc = br->paint_curve; + PaintCurvePoint *pcp; + float length_residue = 0.0f; + int i; + + if (!pc) + return true; + +#ifdef DEBUG_TIME + TIMEIT_START(stroke); +#endif + + pcp = pc->points; + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + + for (i = 0; i < pc->tot_points - 1; i++, pcp++) { + int j; + float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + PaintCurvePoint *pcp_next = pcp + 1; + + for (j = 0; j < 2; j++) + BKE_curve_forward_diff_bezier( + pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + data + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + + + for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { + if (!stroke->stroke_started) { + stroke->last_pressure = 1.0; + copy_v2_v2(stroke->last_mouse_position, data + 2 * j); + stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); + + if (stroke->stroke_started) { + paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0); + paint_line_strokes_spacing(C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + else { + paint_line_strokes_spacing(C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1)); + } + } + } + + stroke_done(C, op); + +#ifdef DEBUG_TIME + TIMEIT_END(stroke); +#endif + + return true; + } + + return false; +} + + int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) { Paint *p = BKE_paint_get_active_from_context(C); PaintMode mode = BKE_paintmode_get_active_from_context(C); PaintStroke *stroke = op->customdata; + Brush *br = stroke->brush; PaintSample sample_average; float mouse[2]; bool first_dab = false; bool first_modal = false; - float zoomx, zoomy; bool redraw = false; float pressure; - /* see if tablet affects event */ - pressure = event_tablet_data(event, &stroke->pen_flip); + /* see if tablet affects event. Line, anchored and drag dot strokes do not support pressure */ + pressure = (br->flag & (BRUSH_LINE | BRUSH_ANCHORED | BRUSH_DRAG_DOT)) ? 1.0f : WM_event_tablet_data(event, &stroke->pen_flip, NULL); paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure); paint_stroke_sample_average(stroke, &sample_average); - get_imapaint_zoom(C, &zoomx, &zoomy); - stroke->zoom_2d = max_ff(zoomx, zoomy); - /* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously! * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it * since the 2D deltas are zero -- code in this file needs to be updated to use the @@ -724,8 +1007,12 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* one time initialization */ if (!stroke->stroke_init) { - stroke->smooth_stroke_cursor = - WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke); + if (paint_stroke_curve_end(C, op, stroke)) + return OPERATOR_FINISHED; + + if (paint_supports_smooth_stroke(br, mode)) + stroke->stroke_cursor = + WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_cursor, stroke); stroke->stroke_init = true; first_modal = true; @@ -739,9 +1026,14 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) BLI_assert((stroke->stroke_started & ~1) == 0); /* 0/1 */ if (stroke->stroke_started) { - if (stroke->brush->flag & BRUSH_AIRBRUSH) + if (br->flag & BRUSH_AIRBRUSH) stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); + if (br->flag & BRUSH_LINE) { + stroke->stroke_cursor = + WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_line_cursor, stroke); + } + first_dab = true; } } @@ -757,20 +1049,42 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - if (event->type == stroke->event_type && event->val == KM_RELEASE && !first_modal) { + if (event->type == stroke->event_type && !first_modal) { + if (event->val == KM_RELEASE) { + paint_stroke_line_end (C, op, stroke, sample_average.mouse); + stroke_done(C, op); + return OPERATOR_FINISHED; + } + } + else if (ELEM(event->type, RETKEY, SPACEKEY)) { + paint_stroke_line_end(C, op, stroke, sample_average.mouse); stroke_done(C, op); return OPERATOR_FINISHED; } - else if (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) || - (event->type == TIMER && (event->customdata == stroke->timer)) ) + else if ((br->flag & BRUSH_LINE) && stroke->stroke_started && + (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) + { + if (br->flag & BRUSH_RAKE) { + copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); + paint_calculate_rake_rotation(stroke->ups, sample_average.mouse); + } + } + else if (first_modal || + /* regular dabs */ + (!(br->flag & (BRUSH_AIRBRUSH)) && (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE))) || + /* airbrush */ + ((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer)) { if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) { if (stroke->stroke_started) { - if (paint_space_stroke_enabled(stroke->brush, mode)) { + if (paint_space_stroke_enabled(br, mode)) { if (paint_space_stroke(C, op, mouse, pressure)) redraw = true; } else { + float dmouse[2]; + sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position); + stroke->stroke_distance += len_v2(dmouse); paint_brush_stroke_add_step(C, op, mouse, pressure); redraw = true; } @@ -781,20 +1095,28 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) /* we want the stroke to have the first daub at the start location * instead of waiting till we have moved the space distance */ if (first_dab && - paint_space_stroke_enabled(stroke->brush, mode) && - !(stroke->brush->flag & BRUSH_ANCHORED) && - !(stroke->brush->flag & BRUSH_SMOOTH_STROKE)) + paint_space_stroke_enabled(br, mode) && + !(br->flag & BRUSH_SMOOTH_STROKE)) { - paint_brush_stroke_add_step(C, op, mouse, pressure); + stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0); + paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure); redraw = true; } /* do updates for redraw. if event is inbetween mousemove there are more * coming, so postpone potentially slow redraw updates until all are done */ - if (event->type != INBETWEEN_MOUSEMOVE) + if (event->type != INBETWEEN_MOUSEMOVE) { + wmWindow *window = CTX_wm_window(C); + ARegion *ar = CTX_wm_region(C); + + /* At the very least, invalidate the cursor */ + if (ar && (p->flags & PAINT_SHOW_BRUSH)) + WM_paint_cursor_tag_redraw(window, ar); + if (redraw && stroke->redraw) stroke->redraw(C, stroke, false); - + } + return OPERATOR_RUNNING_MODAL; } @@ -835,6 +1157,16 @@ void *paint_stroke_mode_data(struct PaintStroke *stroke) return stroke->mode_data; } +bool paint_stroke_flipped(struct PaintStroke *stroke) +{ + return stroke->pen_flip; +} + +float paint_stroke_distance_get(struct PaintStroke *stroke) +{ + return stroke->stroke_distance; +} + void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) { stroke->mode_data = mode_data; @@ -848,6 +1180,6 @@ int paint_poll(bContext *C) ARegion *ar = CTX_wm_region(C); return p && ob && BKE_paint_brush(p) && - (sa && sa->spacetype == SPACE_VIEW3D) && + (sa && ELEM(sa->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) && (ar && ar->regiontype == RGN_TYPE_WINDOW); } diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index f3946e30b83..20e3155c01d 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -38,7 +38,7 @@ #include "BKE_context.h" #include "BKE_global.h" -#include "ED_sculpt.h" +#include "ED_paint.h" #include "paint_intern.h" @@ -51,6 +51,7 @@ typedef struct UndoElem { UndoRestoreCb restore; UndoFreeCb free; + UndoCleanupCb cleanup; } UndoElem; typedef struct UndoStack { @@ -78,7 +79,7 @@ static void undo_elem_free(UndoStack *UNUSED(stack), UndoElem *uel) } } -static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free) +static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) { UndoElem *uel; int nr; @@ -98,6 +99,7 @@ static void undo_stack_push_begin(UndoStack *stack, const char *name, UndoRestor stack->current = uel = MEM_callocN(sizeof(UndoElem), "undo file"); uel->restore = restore; uel->free = free; + uel->cleanup = cleanup; BLI_addtail(&stack->elems, uel); /* name can be a dynamic string */ @@ -171,10 +173,38 @@ static void undo_stack_push_end(UndoStack *stack) } } +static void undo_stack_cleanup(UndoStack *stack, bContext *C) +{ + UndoElem *uel = stack->elems.first; + bool stack_reset = false; + + while (uel) { + if (uel->cleanup && uel->cleanup(C, &uel->elems)) { + UndoElem *uel_tmp = uel->next; + if (stack->current == uel) { + stack->current = NULL; + stack_reset = true; + } + undo_elem_free(stack, uel); + BLI_freelinkN(&stack->elems, uel); + uel = uel_tmp; + } + else + uel = uel->next; + } + if (stack_reset) { + stack->current = stack->elems.last; + } + +} + static int undo_stack_step(bContext *C, UndoStack *stack, int step, const char *name) { UndoElem *undo; + /* first cleanup any old undo steps that may belong to invalid data */ + undo_stack_cleanup(stack, C); + if (step == 1) { if (stack->current == NULL) { /* pass */ @@ -223,23 +253,25 @@ static void undo_stack_free(UndoStack *stack) /* Exported Functions */ -void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free) +void ED_undo_paint_push_begin(int type, const char *name, UndoRestoreCb restore, UndoFreeCb free, UndoCleanupCb cleanup) { if (type == UNDO_PAINT_IMAGE) - undo_stack_push_begin(&ImageUndoStack, name, restore, free); + undo_stack_push_begin(&ImageUndoStack, name, restore, free, cleanup); else if (type == UNDO_PAINT_MESH) - undo_stack_push_begin(&MeshUndoStack, name, restore, free); + undo_stack_push_begin(&MeshUndoStack, name, restore, free, cleanup); } ListBase *undo_paint_push_get_list(int type) { if (type == UNDO_PAINT_IMAGE) { - if (ImageUndoStack.current) + if (ImageUndoStack.current) { return &ImageUndoStack.current->elems; + } } else if (type == UNDO_PAINT_MESH) { - if (MeshUndoStack.current) + if (MeshUndoStack.current) { return &MeshUndoStack.current->elems; + } } return NULL; @@ -315,12 +347,17 @@ static char *undo_stack_get_name(UndoStack *stack, int nr, int *active) return NULL; } -const char *ED_undo_paint_get_name(int type, int nr, int *active) +const char *ED_undo_paint_get_name(bContext *C, int type, int nr, int *active) { - if (type == UNDO_PAINT_IMAGE) + + if (type == UNDO_PAINT_IMAGE) { + undo_stack_cleanup(&ImageUndoStack, C); return undo_stack_get_name(&ImageUndoStack, nr, active); - else if (type == UNDO_PAINT_MESH) + } + else if (type == UNDO_PAINT_MESH) { + undo_stack_cleanup(&MeshUndoStack, C); return undo_stack_get_name(&MeshUndoStack, nr, active); + } return NULL; } diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index 642c1dd9529..e03c8a5f106 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -35,23 +35,28 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "DNA_material_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" #include "BLI_math.h" +#include "BLI_math_color.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" #include "BLI_rect.h" #include "BLF_translation.h" +#include "BKE_scene.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_DerivedMesh.h" +#include "BKE_material.h" #include "BKE_image.h" #include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_image.h" #include "RNA_access.h" #include "RNA_define.h" @@ -67,6 +72,10 @@ #include "ED_view3d.h" #include "ED_screen.h" +#include "ED_uvedit.h" + +#include "IMB_imbuf_types.h" +#include "IMB_imbuf.h" #include "BLI_sys_types.h" #include "ED_mesh.h" /* for face mask functions */ @@ -146,7 +155,7 @@ void paint_calc_redraw_planes(float planes[4][4], rect.ymax += 2; ED_view3d_clipping_calc(&bb, planes, &mats, &rect); - mul_m4_fl(planes, -1.0f); + negate_m4(planes); } float paint_calc_object_space_radius(ViewContext *vc, const float center[3], @@ -205,6 +214,192 @@ void paint_get_tex_pixel_col(MTex *mtex, float u, float v, float rgba[4], struct CLAMP(rgba[3], 0.0f, 1.0f); } +void paint_stroke_operator_properties(wmOperatorType *ot) +{ + static EnumPropertyItem stroke_mode_items[] = { + {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally"}, + {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, + {BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke"}, + {0} + }; + + RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + + RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, + "Stroke Mode", + "Action taken when a paint stroke is made"); + +} + +/* 3D Paint */ + +static void imapaint_project(float matrix[4][4], const float co[3], float pco[4]) +{ + copy_v3_v3(pco, co); + pco[3] = 1.0f; + + mul_m4_v4(matrix, pco); +} + +static void imapaint_tri_weights(float matrix[4][4], GLint view[4], + const float v1[3], const float v2[3], const float v3[3], + const float co[2], float w[3]) +{ + float pv1[4], pv2[4], pv3[4], h[3], divw; + float wmat[3][3], invwmat[3][3]; + + /* compute barycentric coordinates */ + + /* project the verts */ + imapaint_project(matrix, v1, pv1); + imapaint_project(matrix, v2, pv2); + imapaint_project(matrix, v3, pv3); + + /* do inverse view mapping, see gluProject man page */ + h[0] = (co[0] - view[0]) * 2.0f / view[2] - 1.0f; + h[1] = (co[1] - view[1]) * 2.0f / view[3] - 1.0f; + h[2] = 1.0f; + + /* solve for (w1,w2,w3)/perspdiv in: + * h * perspdiv = Project * Model * (w1 * v1 + w2 * v2 + w3 * v3) */ + + wmat[0][0] = pv1[0]; wmat[1][0] = pv2[0]; wmat[2][0] = pv3[0]; + wmat[0][1] = pv1[1]; wmat[1][1] = pv2[1]; wmat[2][1] = pv3[1]; + wmat[0][2] = pv1[3]; wmat[1][2] = pv2[3]; wmat[2][2] = pv3[3]; + + invert_m3_m3(invwmat, wmat); + mul_m3_v3(invwmat, h); + + copy_v3_v3(w, h); + + /* w is still divided by perspdiv, make it sum to one */ + divw = w[0] + w[1] + w[2]; + if (divw != 0.0f) { + mul_v3_fl(w, 1.0f / divw); + } +} + +/* compute uv coordinates of mouse in face */ +static void imapaint_pick_uv(Scene *scene, Object *ob, unsigned int faceindex, const int xy[2], float uv[2]) +{ + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + MTFace *tf_base, *tf; + Material *ma; + TexPaintSlot *slot; + int numfaces = dm->getNumTessFaces(dm), a, findex; + float p[2], w[3], absw, minabsw; + MFace mf; + MVert mv[4]; + float matrix[4][4], proj[4][4]; + GLint view[4]; + + /* compute barycentric coordinates */ + + /* double lookup */ + const int *index_mf_to_mpoly = dm->getTessFaceDataArray(dm, CD_ORIGINDEX); + const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); + if (index_mf_to_mpoly == NULL) { + index_mp_to_orig = NULL; + } + + /* get the needed opengl matrices */ + glGetIntegerv(GL_VIEWPORT, view); + glGetFloatv(GL_MODELVIEW_MATRIX, (float *)matrix); + glGetFloatv(GL_PROJECTION_MATRIX, (float *)proj); + view[0] = view[1] = 0; + mul_m4_m4m4(matrix, matrix, ob->obmat); + mul_m4_m4m4(matrix, proj, matrix); + + minabsw = 1e10; + uv[0] = uv[1] = 0.0; + + /* test all faces in the derivedmesh with the original index of the picked face */ + for (a = 0; a < numfaces; a++) { + findex = index_mf_to_mpoly ? DM_origindex_mface_mpoly(index_mf_to_mpoly, index_mp_to_orig, a) : a; + + if (findex == faceindex) { + dm->getTessFace(dm, a, &mf); + + ma = dm->mat[mf.mat_nr]; + slot = &ma->texpaintslot[ma->paint_active_slot]; + + dm->getVert(dm, mf.v1, &mv[0]); + dm->getVert(dm, mf.v2, &mv[1]); + dm->getVert(dm, mf.v3, &mv[2]); + if (mf.v4) + dm->getVert(dm, mf.v4, &mv[3]); + + if (!(slot && slot->uvname && (tf_base = CustomData_get_layer_named(&dm->faceData, CD_MTFACE, slot->uvname)))) + tf_base = CustomData_get_layer(&dm->faceData, CD_MTFACE); + + tf = &tf_base[a]; + + p[0] = xy[0]; + p[1] = xy[1]; + + if (mf.v4) { + /* the triangle with the largest absolute values is the one + * with the most negative weights */ + imapaint_tri_weights(matrix, view, mv[0].co, mv[1].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[0][0] * w[0] + tf->uv[1][0] * w[1] + tf->uv[3][0] * w[2]; + uv[1] = tf->uv[0][1] * w[0] + tf->uv[1][1] * w[1] + tf->uv[3][1] * w[2]; + minabsw = absw; + } + + imapaint_tri_weights(matrix, view, mv[1].co, mv[2].co, mv[3].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[1][0] * w[0] + tf->uv[2][0] * w[1] + tf->uv[3][0] * w[2]; + uv[1] = tf->uv[1][1] * w[0] + tf->uv[2][1] * w[1] + tf->uv[3][1] * w[2]; + minabsw = absw; + } + } + else { + imapaint_tri_weights(matrix, view, mv[0].co, mv[1].co, mv[2].co, p, w); + absw = fabsf(w[0]) + fabsf(w[1]) + fabsf(w[2]); + if (absw < minabsw) { + uv[0] = tf->uv[0][0] * w[0] + tf->uv[1][0] * w[1] + tf->uv[2][0] * w[2]; + uv[1] = tf->uv[0][1] * w[0] + tf->uv[1][1] * w[1] + tf->uv[2][1] * w[2]; + minabsw = absw; + } + } + } + } + + dm->release(dm); +} + +/* returns 0 if not found, otherwise 1 */ +static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int *r_index, unsigned int totface) +{ + if (totface == 0) + return 0; + + /* sample only on the exact position */ + *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); + + if ((*r_index) == 0 || (*r_index) > (unsigned int)totface) { + return 0; + } + + (*r_index)--; + + return 1; +} + + +static Image *imapaint_face_image(DerivedMesh *dm, int face_index) +{ + Image *ima; + MFace *mf = dm->getTessFaceArray(dm) + face_index; + Material *ma = dm->mat[mf->mat_nr]; + ima = ma ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + + return ima; +} + /* Uses symm to selectively flip any axis of a coordinate. */ void flip_v3_v3(float out[3], const float in[3], const char symm) { @@ -223,25 +418,132 @@ void flip_v3_v3(float out[3], const float in[3], const char symm) } /* used for both 3d view and image window */ -void paint_sample_color(const bContext *C, ARegion *ar, int x, int y) /* frontbuf */ +void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_proj, bool use_palette) { + Scene *scene = CTX_data_scene(C); + Paint *paint = BKE_paint_get_active_from_context(C); + Palette *palette = BKE_paint_palette(paint); + PaletteColor *color; Brush *br = BKE_paint_brush(BKE_paint_get_active_from_context(C)); unsigned int col; - const char *cp; + const unsigned char *cp; CLAMP(x, 0, ar->winx); CLAMP(y, 0, ar->winy); - glReadBuffer(GL_FRONT); - glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); - glReadBuffer(GL_BACK); + if (use_palette) { + if (!palette) { + palette = BKE_palette_add(CTX_data_main(C), "Palette"); + BKE_paint_palette_set(paint, palette); + } - cp = (char *)&col; + color = BKE_palette_color_add(palette); + } + + + if (CTX_wm_view3d(C) && texpaint_proj) { + /* first try getting a colour directly from the mesh faces if possible */ + Object *ob = OBACT; + bool sample_success = false; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; + bool use_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL); + + if (ob) { + DerivedMesh *dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); + + ViewContext vc; + const int mval[2] = {x, y}; + unsigned int faceindex; + unsigned int totface = dm->getNumTessFaces(dm); + MTFace *dm_mtface = dm->getTessFaceDataArray(dm, CD_MTFACE); + + DM_update_materials(dm, ob); + + if (dm_mtface) { + view3d_set_viewcontext(C, &vc); + + view3d_operator_needs_opengl(C); + + if (imapaint_pick_face(&vc, mval, &faceindex, totface)) { + Image *image; + + if (use_material) + image = imapaint_face_image(dm, faceindex); + else + image = imapaint->canvas; + + if (image) { + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (ibuf && ibuf->rect) { + float uv[2]; + float u, v; + imapaint_pick_uv(scene, ob, faceindex, mval, uv); + sample_success = true; + + u = fmodf(uv[0], 1.0f); + v = fmodf(uv[1], 1.0f); + + if (u < 0.0f) u += 1.0f; + if (v < 0.0f) v += 1.0f; + + u = u * ibuf->x - 0.5f; + v = v * ibuf->y - 0.5f; + + if (ibuf->rect_float) { + float rgba_f[4]; + bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + straight_to_premul_v4(rgba_f); + if (use_palette) { + linearrgb_to_srgb_v3_v3(color->rgb, rgba_f); + } + else { + linearrgb_to_srgb_v3_v3(rgba_f, rgba_f); + BKE_brush_color_set(scene, br, rgba_f); + } + } + else { + unsigned char rgba[4]; + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (use_palette) { + rgb_uchar_to_float(color->rgb, rgba); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, rgba); + BKE_brush_color_set(scene, br, rgba_f); + } + } + } + + BKE_image_release_ibuf(image, ibuf, NULL); + } + } + } + dm->release(dm); + } + + if (!sample_success) { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + else + return; + } + else { + glReadBuffer(GL_FRONT); + glReadPixels(x + ar->winrct.xmin, y + ar->winrct.ymin, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); + glReadBuffer(GL_BACK); + } + cp = (unsigned char *)&col; - if (br) { - br->rgb[0] = cp[0] / 255.0f; - br->rgb[1] = cp[1] / 255.0f; - br->rgb[2] = cp[2] / 255.0f; + if (use_palette) { + rgb_uchar_to_float(color->rgb, cp); + } + else { + float rgba_f[3]; + rgb_uchar_to_float(rgba_f, cp); + BKE_brush_color_set(scene, br, rgba_f); } } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 969c5a09a82..431dd54b3c0 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -33,6 +33,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_bitmap.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -75,7 +76,6 @@ #include "paint_intern.h" /* own include */ - /* check if we can do partial updates and have them draw realtime * (without rebuilding the 'derivedFinal') */ static bool vertex_paint_use_fast_update_check(Object *ob) @@ -197,11 +197,11 @@ static int *get_indexarray(Mesh *me) return MEM_mallocN(sizeof(int) * (me->totpoly + 1), "vertexpaint"); } -unsigned int vpaint_get_current_col(VPaint *vp) +unsigned int vpaint_get_current_col(Scene *scene, VPaint *vp) { Brush *brush = BKE_paint_brush(&vp->paint); unsigned char col[4]; - rgb_float_to_uchar(col, brush->rgb); + rgb_float_to_uchar(col, BKE_brush_color_get(scene, brush)); col[3] = 255; /* alpha isn't used, could even be removed to speedup paint a little */ return *(unsigned int *)col; } @@ -919,7 +919,7 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co { const float dist_sq = len_squared_v2v2(mval, co_ss); - if (dist_sq <= brush_size_pressure * brush_size_pressure) { + if (dist_sq <= SQUARE(brush_size_pressure)) { Brush *brush = BKE_paint_brush(&vp->paint); const float dist = sqrtf(dist_sq); float factor; @@ -2547,14 +2547,17 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start, wpaint_stroke_update_step, NULL, wpaint_stroke_done, event->type); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -2563,7 +2566,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int wpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new(C, NULL, wpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, wpaint_stroke_test_start, wpaint_stroke_update_step, NULL, wpaint_stroke_done, 0); @@ -2595,8 +2598,8 @@ void PAINT_OT_weight_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + + paint_stroke_operator_properties(ot); } static int weight_paint_set_exec(bContext *C, wmOperator *op) @@ -2778,7 +2781,8 @@ static void vpaint_build_poly_facemap(struct VPaintData *vd, Mesh *me) static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float UNUSED(mouse[2])) { - ToolSettings *ts = CTX_data_tool_settings(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; struct PaintStroke *stroke = op->customdata; VPaint *vp = ts->vpaint; Brush *brush = BKE_paint_brush(&vp->paint); @@ -2810,7 +2814,7 @@ static bool vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const f vpd->vp_handle = ED_vpaint_proj_handle_create(vpd->vc.scene, ob, &vpd->vertexcosnos); vpd->indexar = get_indexarray(me); - vpd->paintcol = vpaint_get_current_col(vp); + vpd->paintcol = vpaint_get_current_col(scene, vp); vpd->is_texbrush = !(brush->vertexpaint_tool == PAINT_BLEND_BLUR) && brush->mtex.tex; @@ -3062,14 +3066,18 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start, vpaint_stroke_update_step, NULL, vpaint_stroke_done, event->type); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -3078,7 +3086,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event) static int vpaint_exec(bContext *C, wmOperator *op) { - op->customdata = paint_stroke_new(C, NULL, vpaint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, vpaint_stroke_test_start, vpaint_stroke_update_step, NULL, vpaint_stroke_done, 0); @@ -3110,7 +3118,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + paint_stroke_operator_properties(ot); } /* ********************** weight from bones operator ******************* */ @@ -3184,6 +3192,8 @@ typedef struct DMGradient_userData { int def_nr; short is_init; DMGradient_vertStore *vert_cache; + /* only for init */ + BLI_bitmap *vert_visit; /* options */ short use_select; @@ -3191,19 +3201,81 @@ typedef struct DMGradient_userData { float weightpaint; } DMGradient_userData; -static void gradientVert__mapFunc(void *userData, int index, const float co[3], - const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +static void gradientVert_update(DMGradient_userData *grad_data, int index) { - DMGradient_userData *grad_data = userData; Mesh *me = grad_data->me; + DMGradient_vertStore *vs = &grad_data->vert_cache[index]; + float alpha; + + if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) { + alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end); + } + else { + BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL); + alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div; + } + /* no need to clamp 'alpha' yet */ - if (grad_data->use_select == false || (me->mvert[index].flag & SELECT)) { + /* adjust weight */ + alpha = BKE_brush_curve_strength_clamp(grad_data->brush, alpha, 1.0f); + + if (alpha != 0.0f) { + MDeformVert *dv = &me->dvert[index]; + MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr); + // dw->weight = alpha; // testing + int tool = grad_data->brush->vertexpaint_tool; + float testw; + + /* init if we just added */ + testw = wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha); + CLAMP(testw, 0.0f, 1.0f); + dw->weight = testw; + } + else { + MDeformVert *dv = &me->dvert[index]; + if (vs->flag & VGRAD_STORE_DW_EXIST) { + /* normally we NULL check, but in this case we know it exists */ + MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); + dw->weight = vs->weight_orig; + } + else { + /* wasn't originally existing, remove */ + MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); + if (dw) { + defvert_remove_group(dv, dw); + } + } + } +} + +static void gradientVertUpdate__mapFunc( + void *userData, int index, const float UNUSED(co[3]), + const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +{ + DMGradient_userData *grad_data = userData; + Mesh *me = grad_data->me; + if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) { DMGradient_vertStore *vs = &grad_data->vert_cache[index]; + if (vs->sco[0] != FLT_MAX) { + gradientVert_update(grad_data, index); + } + } +} - /* run first pass only, could be split into its own mapFunc +static void gradientVertInit__mapFunc( + void *userData, int index, const float co[3], + const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) +{ + DMGradient_userData *grad_data = userData; + Mesh *me = grad_data->me; + + if ((grad_data->use_select == false) || (me->mvert[index].flag & SELECT)) { + /* run first pass only, * the screen coords of the verts need to be cached because * updating the mesh may move them about (entering feedback loop) */ - if (grad_data->is_init) { + + if (BLI_BITMAP_TEST(grad_data->vert_visit, index) == 0) { + DMGradient_vertStore *vs = &grad_data->vert_cache[index]; if (ED_view3d_project_float_object(grad_data->ar, co, vs->sco, V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) @@ -3220,55 +3292,14 @@ static void gradientVert__mapFunc(void *userData, int index, const float co[3], vs->weight_orig = 0.0f; vs->flag = VGRAD_STORE_NOP; } - } - else { - /* no go */ - copy_v2_fl(vs->sco, FLT_MAX); - } - } - /* end init */ - if (vs->sco[0] != FLT_MAX) { - float alpha; + BLI_BITMAP_ENABLE(grad_data->vert_visit, index); - if (grad_data->type == WPAINT_GRADIENT_TYPE_LINEAR) { - alpha = line_point_factor_v2(vs->sco, grad_data->sco_start, grad_data->sco_end); + gradientVert_update(grad_data, index); } else { - BLI_assert(grad_data->type == WPAINT_GRADIENT_TYPE_RADIAL); - alpha = len_v2v2(grad_data->sco_start, vs->sco) * grad_data->sco_line_div; - } - /* no need to clamp 'alpha' yet */ - - /* adjust weight */ - alpha = BKE_brush_curve_strength_clamp(grad_data->brush, alpha, 1.0f); - - if (alpha != 0.0f) { - MDeformVert *dv = &me->dvert[index]; - MDeformWeight *dw = defvert_verify_index(dv, grad_data->def_nr); - // dw->weight = alpha; // testing - int tool = grad_data->brush->vertexpaint_tool; - float testw; - - /* init if we just added */ - testw = wpaint_blend_tool(tool, vs->weight_orig, grad_data->weightpaint, alpha * grad_data->brush->alpha); - CLAMP(testw, 0.0f, 1.0f); - dw->weight = testw; - } - else { - MDeformVert *dv = &me->dvert[index]; - if (vs->flag & VGRAD_STORE_DW_EXIST) { - /* normally we NULL check, but in this case we know it exists */ - MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); - dw->weight = vs->weight_orig; - } - else { - /* wasn't originally existing, remove */ - MDeformWeight *dw = defvert_find_index(dv, grad_data->def_nr); - if (dw) { - defvert_remove_group(dv, dw); - } - } + /* no go */ + copy_v2_fl(vs->sco, FLT_MAX); } } } @@ -3364,6 +3395,7 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) data.def_nr = ob->actdef - 1; data.use_select = (me->editflag & (ME_EDIT_PAINT_FACE_SEL | ME_EDIT_PAINT_VERT_SEL)); data.vert_cache = vert_cache; + data.vert_visit = NULL; data.type = RNA_enum_get(op->ptr, "type"); { @@ -3379,7 +3411,17 @@ static int paint_weight_gradient_exec(bContext *C, wmOperator *op) ED_view3d_init_mats_rv3d(ob, ar->regiondata); - dm->foreachMappedVert(dm, gradientVert__mapFunc, &data, DM_FOREACH_NOP); + if (data.is_init) { + data.vert_visit = BLI_BITMAP_NEW(me->totvert, __func__); + + dm->foreachMappedVert(dm, gradientVertInit__mapFunc, &data, DM_FOREACH_NOP); + + MEM_freeN(data.vert_visit); + data.vert_visit = NULL; + } + else { + dm->foreachMappedVert(dm, gradientVertUpdate__mapFunc, &data, DM_FOREACH_NOP); + } DAG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index c9101fff6e5..0e0012c198f 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -37,6 +37,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" +#include "BLI_dial.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLI_threads.h" @@ -65,6 +66,7 @@ #include "BKE_modifier.h" #include "BKE_multires.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_node.h" #include "BKE_object.h" #include "BKE_subsurf.h" @@ -87,6 +89,9 @@ #include "GPU_buffers.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "bmesh.h" #include "bmesh_tools.h" @@ -111,7 +116,7 @@ static int system_physical_thread_count(void) } #endif /* __APPLE__ */ -void ED_sculpt_get_average_stroke(Object *ob, float stroke[3]) +void ED_sculpt_stroke_get_average(Object *ob, float stroke[3]) { if (ob->sculpt->last_stroke_valid && ob->sculpt->average_stroke_counter > 0) { float fac = 1.0f / ob->sculpt->average_stroke_counter; @@ -177,7 +182,7 @@ typedef struct StrokeCache { float initial_mouse[2]; /* Pre-allocated temporary storage used during smoothing */ - int num_threads, max_threads; + int num_threads, init_num_threads; float (**tmpgrid_co)[3], (**tmprow_co)[3]; float **tmpgrid_mask, **tmprow_mask; @@ -187,8 +192,8 @@ typedef struct StrokeCache { float true_location[3]; float location[3]; - float pen_flip; - float invert; + bool pen_flip; + bool invert; float pressure; float mouse[2]; float bstrength; @@ -222,7 +227,7 @@ typedef struct StrokeCache { float sculpt_normal[3]; float sculpt_normal_symm[3]; - /* Used for wrap texture mode, local_mat gets calculated by + /* Used for area texture mode, local_mat gets calculated by * calc_brush_local_mat() and used in tex_strength(). */ float brush_local_mat[4][4]; @@ -234,12 +239,8 @@ typedef struct StrokeCache { float anchored_location[3]; float vertex_rotation; /* amount to rotate the vertices when using rotate brush */ - float previous_vertex_rotation; /* previous rotation, used to detect if we rotate more than - * PI radians */ - short num_vertex_turns; /* records number of full 2*PI turns */ - float initial_mouse_dir[2]; /* used to calculate initial angle */ - bool init_dir_set; /* detect if we have initialized the initial mouse direction */ - + Dial *dial; + char saved_active_brush_name[MAX_ID_NAME]; char saved_mask_brush_tool; int saved_smooth_size; /* smooth tool copies the size of the current tool */ @@ -267,8 +268,8 @@ typedef struct { /* Original coordinate, normal, and mask */ const float *co; - float mask; const short *no; + float mask; } SculptOrigVertData; @@ -355,19 +356,19 @@ static int sculpt_stroke_dynamic_topology(const SculptSession *ss, !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - (!ELEM6(brush->sculpt_tool, - /* These brushes, as currently coded, cannot - * support dynamic topology */ - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER, + (!ELEM(brush->sculpt_tool, + /* These brushes, as currently coded, cannot + * support dynamic topology */ + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER, - /* These brushes could handle dynamic topology, - * but user feedback indicates it's better not - * to */ - SCULPT_TOOL_SMOOTH, - SCULPT_TOOL_MASK))); + /* These brushes could handle dynamic topology, + * but user feedback indicates it's better not + * to */ + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_MASK))); } /*** paint mesh ***/ @@ -392,7 +393,7 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) * entries might be inserted by sculpt_undo_push_node() into the * GHash used internally by BM_log_original_vert_co() by a * different thread. [#33787] */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP && !ss->bm) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { SculptUndoNode *unode; SculptUndoType type = (brush->sculpt_tool == SCULPT_TOOL_MASK ? @@ -485,8 +486,8 @@ static bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, return 1; } -void sculpt_get_redraw_planes(float planes[4][4], ARegion *ar, - RegionView3D *rv3d, Object *ob) +void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, + RegionView3D *rv3d, Object *ob) { PBVH *pbvh = ob->sculpt->pbvh; /* copy here, original will be used below */ @@ -549,7 +550,7 @@ static bool sculpt_brush_test(SculptBrushTest *test, const float co[3]) if (sculpt_brush_test_clipping(test, co)) { return 0; } - test->dist = sqrt(distsq); + test->dist = sqrtf(distsq); return 1; } else { @@ -659,47 +660,6 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca /* ===== Sculpting ===== * */ - -static float overlapped_curve(Brush *br, float x) -{ - int i; - const int n = 100 / br->spacing; - const float h = br->spacing / 50.0f; - const float x0 = x - 1; - - float sum; - - sum = 0; - for (i = 0; i < n; i++) { - float xx; - - xx = fabsf(x0 + i * h); - - if (xx < 1.0f) - sum += BKE_brush_curve_strength(br, xx, 1); - } - - return sum; -} - -static float integrate_overlap(Brush *br) -{ - int i; - int m = 10; - float g = 1.0f / m; - float max; - - max = 0; - for (i = 0; i < m; i++) { - float overlap = overlapped_curve(br, i * g); - - if (overlap > max) - max = overlap; - } - - return max; -} - static void flip_v3(float v[3], const char symm) { flip_v3_v3(v, v, symm); @@ -772,7 +732,7 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) /* 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(Sculpt *sd, StrokeCache *cache, float feather) +static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather, UnifiedPaintSettings *ups) { const Scene *scene = cache->vc->scene; Brush *brush = BKE_paint_brush(&sd->paint); @@ -784,13 +744,10 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; float pen_flip = cache->pen_flip ? -1 : 1; float invert = cache->invert ? -1 : 1; - float accum = integrate_overlap(brush); + float overlap = ups->overlap_factor; /* spacing is integer percentage of radius, divide by 50 to get * normalized diameter */ - float overlap = (brush->flag & BRUSH_SPACE_ATTEN && - brush->flag & BRUSH_SPACE && - !(brush->flag & BRUSH_ANCHORED) && - (brush->spacing < 100)) ? 1.0f / accum : 1; + float flip = dir * invert * pen_flip; switch (brush->sculpt_tool) { @@ -852,10 +809,10 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) return alpha * pressure * feather; case SCULPT_TOOL_SNAKE_HOOK: - return feather; + return root_alpha * feather; case SCULPT_TOOL_GRAB: - return feather; + return root_alpha * feather; case SCULPT_TOOL_ROTATE: return alpha * pressure * feather; @@ -869,7 +826,6 @@ static float brush_strength(Sculpt *sd, StrokeCache *cache, float feather) static float tex_strength(SculptSession *ss, Brush *br, const float point[3], const float len, - const float sculpt_normal[3], const short vno[3], const float fno[3], const float mask) @@ -940,7 +896,7 @@ static float tex_strength(SculptSession *ss, Brush *br, /* Falloff curve */ avg *= BKE_brush_curve_strength(br, len, cache->radius); - avg *= frontface(br, sculpt_normal, vno, fno); + avg *= frontface(br, cache->view_normal, vno, fno); /* Paint mask */ avg *= 1.0f - mask; @@ -1037,7 +993,7 @@ static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nod zero_v3(an); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1231,17 +1187,16 @@ static int brush_needs_sculpt_normal(const Brush *brush) return ((ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_SNAKE_HOOK) && - ((brush->normal_weight > 0) || - (brush->flag & BRUSH_FRONTFACE))) || - - ELEM7(brush->sculpt_tool, - SCULPT_TOOL_BLOB, - SCULPT_TOOL_CREASE, - SCULPT_TOOL_DRAW, - SCULPT_TOOL_LAYER, - SCULPT_TOOL_NUDGE, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB) || + (brush->normal_weight > 0)) || + + ELEM(brush->sculpt_tool, + SCULPT_TOOL_BLOB, + SCULPT_TOOL_CREASE, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB) || (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)); } @@ -1396,7 +1351,7 @@ static void do_mesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, { if (sculpt_brush_test(&test, vd.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->view_normal, vd.no, vd.fno, + vd.no, vd.fno, smooth_mask ? 0 : (vd.mask ? *vd.mask : 0.0f)); if (smooth_mask) { float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; @@ -1437,7 +1392,7 @@ static void do_bmesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, { if (sculpt_brush_test(&test, vd.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->view_normal, vd.no, vd.fno, + vd.no, vd.fno, smooth_mask ? 0 : *vd.mask); if (smooth_mask) { float val = bmesh_neighbor_average_mask(ss->bm, vd.bm_vert) - *vd.mask; @@ -1565,7 +1520,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no int index; if (gh) { - if (BLI_BITMAP_GET(gh, y * gridsize + x)) + if (BLI_BITMAP_TEST(gh, y * gridsize + x)) continue; } @@ -1589,7 +1544,6 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no if (sculpt_brush_test(&test, co)) { const float strength_mask = (smooth_mask ? 0 : *mask); const float fade = bstrength * tex_strength(ss, brush, co, test.dist, - ss->cache->view_normal, NULL, fno, strength_mask); float n = 1.0f / 16.0f; @@ -1645,7 +1599,7 @@ static void smooth(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, for (iteration = 0; iteration <= count; ++iteration) { float strength = (iteration != count) ? 1.0f : last; -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { switch (type) { case PBVH_GRIDS: @@ -1681,7 +1635,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot int n; /* threaded loop over nodes */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1692,7 +1646,7 @@ static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int tot { if (sculpt_brush_test(&test, vd.co)) { float fade = tex_strength(ss, brush, vd.co, test.dist, - ss->cache->view_normal, vd.no, vd.fno, 0); + vd.no, vd.fno, 0); (*vd.mask) += fade * bstrength; CLAMP(*vd.mask, 0, 1); @@ -1734,7 +1688,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) mul_v3_fl(offset, bstrength); /* threaded loop over nodes */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1748,8 +1702,7 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { if (sculpt_brush_test(&test, vd.co)) { /* offset vertex */ - float fade = tex_strength(ss, brush, vd.co, test.dist, - ss->cache->sculpt_normal_symm, vd.no, + float fade = tex_strength(ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -1790,7 +1743,7 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; /* threaded loop over nodes */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1805,7 +1758,6 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (sculpt_brush_test(&test, vd.co)) { /* offset vertex */ const float fade = tex_strength(ss, brush, vd.co, test.dist, - ss->cache->sculpt_normal_symm, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); float val1[3]; float val2[3]; @@ -1834,7 +1786,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode float bstrength = ss->cache->bstrength; int n; -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1847,8 +1799,7 @@ static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test(&test, vd.co)) { - float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->view_normal, vd.no, + float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); float val[3]; @@ -1882,7 +1833,7 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) add_v3_v3(grab_delta, ss->cache->sculpt_normal_symm); } -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1903,7 +1854,6 @@ static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) const float fade = bstrength * tex_strength(ss, brush, orig_data.co, test.dist, - ss->cache->sculpt_normal_symm, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f); @@ -1931,7 +1881,7 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1945,7 +1895,6 @@ static void do_nudge_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode { if (sculpt_brush_test(&test, vd.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->sculpt_normal_symm, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], cono, fade); @@ -1980,7 +1929,7 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to add_v3_v3(grab_delta, ss->cache->sculpt_normal_symm); } -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -1994,7 +1943,6 @@ static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int to { if (sculpt_brush_test(&test, vd.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->sculpt_normal_symm, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], grab_delta, fade); @@ -2021,7 +1969,7 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2042,7 +1990,6 @@ static void do_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode const float fade = bstrength * tex_strength(ss, brush, orig_data.co, test.dist, - ss->cache->sculpt_normal_symm, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f); @@ -2065,7 +2012,7 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2087,7 +2034,6 @@ static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod const float fade = bstrength * tex_strength(ss, brush, orig_data.co, test.dist, - ss->cache->sculpt_normal_symm, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f); @@ -2119,7 +2065,7 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2143,7 +2089,6 @@ static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode if (sculpt_brush_test(&test, orig_data.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->sculpt_normal_symm, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); float *disp = &layer_disp[vd.i]; float val[3]; @@ -2183,7 +2128,7 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float bstrength = ss->cache->bstrength; int n; -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2197,7 +2142,6 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno { if (sculpt_brush_test(&test, vd.co)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, test.dist, - ss->cache->view_normal, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); float val[3]; @@ -2229,7 +2173,7 @@ static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int to zero_v3(fc); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2336,7 +2280,7 @@ static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, /* for flatten center */ zero_v3(fc); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2588,7 +2532,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno mul_v3_fl(temp, displace); add_v3_v3(fc, temp); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2609,8 +2553,8 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrt(test.dist), - an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); + const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrtf(test.dist), + vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2660,7 +2604,7 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) /* add_v3_v3v3(p, ss->cache->location, an); */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2682,9 +2626,8 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength(ss, brush, vd.co, - sqrt(test.dist), - an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); + const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrtf(test.dist), + vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2762,7 +2705,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t mul_m4_m4m4(tmat, mat, scale); invert_m4_m4(mat, tmat); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2786,7 +2729,7 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t if (plane_trim(ss->cache, brush, val)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, ss->cache->radius * test.dist, - an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); + vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2826,7 +2769,7 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) mul_v3_fl(temp, displace); add_v3_v3(fc, temp); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2849,8 +2792,8 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) if (plane_trim(ss->cache, brush, val)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, - sqrt(test.dist), - an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); + sqrtf(test.dist), + vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2890,7 +2833,7 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod mul_v3_fl(temp, displace); add_v3_v3(fc, temp); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2913,8 +2856,8 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (plane_trim(ss->cache, brush, val)) { const float fade = bstrength * tex_strength(ss, brush, vd.co, - sqrt(test.dist), - an, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); + sqrtf(test.dist), + vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], val, fade); @@ -2944,7 +2887,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl mul_v3_fl(offset, bstrength); /* threaded loop over nodes */ -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; SculptBrushTest test; @@ -2956,8 +2899,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq(&test, vd.co)) { - const float fade = tex_strength(ss, brush, vd.co, sqrt(test.dist), - ss->cache->sculpt_normal_symm, vd.no, + const float fade = tex_strength(ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); mul_v3_v3fl(proxy[vd.i], offset, fade); @@ -3041,11 +2983,11 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush) radius = ss->cache->radius * 1.25f; data.radius_squared = radius * radius; - data.original = ELEM4(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER) ? true : ss->cache->original; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); @@ -3103,18 +3045,18 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush) data.ss = ss; data.sd = sd; data.radius_squared = ss->cache->radius_squared; - data.original = ELEM4(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER) ? true : ss->cache->original; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); /* Only act if some verts are inside the brush area */ if (totnode) { float location[3]; -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { sculpt_undo_push_node(ob, nodes[n], brush->sculpt_tool == SCULPT_TOOL_MASK ? @@ -3244,10 +3186,10 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) ss->cache->supports_gravity) { /* these brushes start from original coordinates */ - const bool use_orco = ELEM3(brush->sculpt_tool, SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); + const bool use_orco = ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; PBVHProxyNode *proxies; @@ -3340,7 +3282,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { PBVHVertexIter vd; @@ -3388,7 +3330,7 @@ static void calc_brushdata_symm(Sculpt *sd, StrokeCache *cache, const char symm, /* XXX This reduces the length of the grab delta if it approaches the line of symmetry * XXX However, a different approach appears to be needed */ #if 0 - if (sd->flags & SCULPT_SYMMETRY_FEATHER) { + if (sd->paint.symmetry_flags & SCULPT_SYMMETRY_FEATHER) { float frac = 1.0f / max_overlap_count(sd); float reduce = (feather - frac) / (1 - frac); @@ -3448,7 +3390,7 @@ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) } static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, - BrushActionFunc action) + BrushActionFunc action, UnifiedPaintSettings *ups) { Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; @@ -3458,7 +3400,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, Object *ob, float feather = calc_symmetry_feather(sd, ss->cache); - cache->bstrength = brush_strength(sd, cache, feather); + cache->bstrength = brush_strength(sd, cache, feather, ups); cache->symmetry = symm; /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ @@ -3582,6 +3524,8 @@ static void sculpt_cache_free(StrokeCache *cache) { if (cache->face_norms) MEM_freeN(cache->face_norms); + if (cache->dial) + MEM_freeN(cache->dial); MEM_freeN(cache); } @@ -3623,6 +3567,12 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss) StrokeCache *cache = ss->cache; #ifdef _OPENMP + +#if defined(__APPLE__) + cache->init_num_threads = BLI_system_thread_count(); +#else + cache->init_num_threads = omp_get_max_threads(); +#endif /* If using OpenMP then create a number of threads two times the * number of processor cores. * Justification: Empirically I've found that two threads per @@ -3631,13 +3581,12 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss) #if defined(__APPLE__) cache->num_threads = system_physical_thread_count(); #else - cache->num_threads = omp_get_num_procs(); + cache->num_threads = 2 * omp_get_num_procs(); #endif } else { cache->num_threads = 1; } - cache->max_threads = omp_get_max_threads(); omp_set_num_threads(cache->num_threads); #else (void)sd; @@ -3670,8 +3619,9 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss) static void sculpt_omp_done(SculptSession *ss) { #ifdef _OPENMP - omp_set_num_threads(ss->cache->max_threads); + omp_set_num_threads(ss->cache->init_num_threads); #endif + if (ss->multires) { int i; @@ -3738,8 +3688,8 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio /* not very nice, but with current events system implementation * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) brush->flag |= BRUSH_INVERTED; - else brush->flag &= ~BRUSH_INVERTED; + if (cache->invert) ups->draw_inverted = true; + else ups->draw_inverted = false; /* Alt-Smooth */ if (cache->alt_smooth) { @@ -3785,7 +3735,7 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio mul_m3_v3(mat, viewDir); normalize_v3_v3(cache->true_view_normal, viewDir); - cache->supports_gravity = (!ELEM3(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && + cache->supports_gravity = (!ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && (sd->gravity_factor > 0.0f)); /* get gravity vector in world space */ if (cache->supports_gravity) { @@ -3843,10 +3793,10 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio cache->original = 1; } - if (ELEM9(brush->sculpt_tool, - SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, - SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, - SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_ROTATE, SCULPT_TOOL_FLATTEN)) + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_DRAW, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, + SCULPT_TOOL_LAYER, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, + SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_ROTATE, SCULPT_TOOL_FLATTEN)) { if (!(brush->flag & BRUSH_ACCUMULATE)) { cache->original = 1; @@ -3855,11 +3805,12 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio cache->first_time = 1; - cache->vertex_rotation = 0; - cache->num_vertex_turns = 0; - cache->previous_vertex_rotation = 0; - cache->init_dir_set = false; - +#define PIXEL_INPUT_THRESHHOLD 5 + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) + cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); + +#undef PIXEL_INPUT_THRESHHOLD + sculpt_omp_start(sd, ss); } @@ -3873,10 +3824,10 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru }; int tool = brush->sculpt_tool; - if (ELEM5(tool, - SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, - SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, - SCULPT_TOOL_THUMB)) + if (ELEM(tool, + SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, + SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB)) { float grab_location[3], imat[4][4], delta[3], loc[3]; @@ -3997,16 +3948,9 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, cache->radius_squared = cache->radius * cache->radius; if (brush->flag & BRUSH_ANCHORED) { + /* true location has been calculated as part of the stroke system already here */ if (brush->flag & BRUSH_EDGE_TO_EDGE) { - float halfway[2]; - float out[3]; - halfway[0] = 0.5f * (cache->mouse[0] + cache->initial_mouse[0]); - halfway[1] = 0.5f * (cache->mouse[1] + cache->initial_mouse[1]); - - if (sculpt_stroke_get_location(C, out, halfway)) { - copy_v3_v3(cache->anchored_location, out); - copy_v3_v3(cache->true_location, cache->anchored_location); - } + RNA_float_get_array(ptr, "location", cache->true_location); } cache->radius = paint_calc_object_space_radius(cache->vc, @@ -4020,48 +3964,7 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, sculpt_update_brush_delta(ups, ob, brush); if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { -#define PIXEL_INPUT_THRESHHOLD 5 - - const float dx = cache->mouse[0] - cache->initial_mouse[0]; - const float dy = cache->mouse[1] - cache->initial_mouse[1]; - - /* only update when we have enough precision, by having the mouse adequately away from center - * may be better to convert to radial representation but square works for small values too*/ - if (fabsf(dx) > PIXEL_INPUT_THRESHHOLD && fabsf(dy) > PIXEL_INPUT_THRESHHOLD) { - float mouse_angle; - float dir[2] = {dx, dy}; - float cosval, sinval; - normalize_v2(dir); - - if (!cache->init_dir_set) { - copy_v2_v2(cache->initial_mouse_dir, dir); - cache->init_dir_set = true; - } - - /* calculate mouse angle between initial and final mouse position */ - cosval = dot_v2v2(dir, cache->initial_mouse_dir); - sinval = cross_v2v2(dir, cache->initial_mouse_dir); - - /* clamp to avoid nans in acos */ - CLAMP(cosval, -1.0f, 1.0f); - mouse_angle = (sinval > 0) ? acosf(cosval) : -acosf(cosval); - - /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. - * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2 */ - if ((mouse_angle * cache->previous_vertex_rotation < 0.0f) && - (fabsf(cache->previous_vertex_rotation) > (float)M_PI_2)) - { - if (cache->previous_vertex_rotation < 0) - cache->num_vertex_turns--; - else - cache->num_vertex_turns++; - } - cache->previous_vertex_rotation = mouse_angle; - - cache->vertex_rotation = -(mouse_angle + 2.0f * (float)M_PI * cache->num_vertex_turns) * cache->bstrength; - -#undef PIXEL_INPUT_THRESHHOLD - } + cache->vertex_rotation = -BLI_dial_angle(cache->dial, cache->mouse) * cache->bstrength; ups->draw_anchored = true; copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); @@ -4103,14 +4006,14 @@ static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob) typedef struct { SculptSession *ss; const float *ray_start, *ray_normal; - int hit; + bool hit; float dist; - int original; + bool original; } SculptRaycastData; typedef struct { const float *ray_start, *ray_normal; - int hit; + bool hit; float dist; float detail; } SculptDetailRaycastData; @@ -4242,7 +4145,7 @@ static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession sculpt_update_tex(scene, sd, ss); } -static int sculpt_brush_stroke_init(bContext *C, wmOperator *op) +static bool sculpt_brush_stroke_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); @@ -4398,10 +4301,10 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st } if (sculpt_stroke_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update); + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); } - do_symmetrical_brush_actions(sd, ob, do_brush_action); + do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); sculpt_combine_proxies(sd, ob); @@ -4451,8 +4354,9 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str /* Finished */ if (ss->cache) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; Brush *brush = BKE_paint_brush(&sd->paint); - brush->flag &= ~BRUSH_INVERTED; + ups->draw_inverted = false; sculpt_stroke_modifiers_check(C, ob); @@ -4473,7 +4377,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str /* update last stroke position */ ob->sculpt->last_stroke_valid = 1; - ED_sculpt_get_average_stroke(ob, ob->sculpt->last_stroke); + ED_sculpt_stroke_get_average(ob, ob->sculpt->last_stroke); sculpt_cache_free(ss->cache); ss->cache = NULL; @@ -4499,11 +4403,6 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); } -#ifdef _OPENMP - if (!(sd->flags & SCULPT_USE_OPENMP)) - omp_set_num_threads(BLI_system_thread_count()); /* set back to original logical corecount */ -#endif - sculpt_brush_exit_tex(sd); } @@ -4516,7 +4415,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent if (!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - stroke = paint_stroke_new(C, sculpt_stroke_get_location, + stroke = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, NULL, sculpt_stroke_done, event->type); @@ -4531,10 +4430,13 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_PASS_THROUGH; } + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -4546,7 +4448,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) if (!sculpt_brush_stroke_init(C, op)) return OPERATOR_CANCELLED; - op->customdata = paint_stroke_new(C, sculpt_stroke_get_location, sculpt_stroke_test_start, + op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, sculpt_stroke_update_step, NULL, sculpt_stroke_done, 0); /* frees op->customdata */ @@ -4577,13 +4479,6 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { - static EnumPropertyItem stroke_mode_items[] = { - {BRUSH_STROKE_NORMAL, "NORMAL", 0, "Normal", "Apply brush normally"}, - {BRUSH_STROKE_INVERT, "INVERT", 0, "Invert", "Invert action of brush for duration of stroke"}, - {BRUSH_STROKE_SMOOTH, "SMOOTH", 0, "Smooth", "Switch brush to smooth mode for duration of stroke"}, - {0} - }; - /* identifiers */ ot->name = "Sculpt"; ot->idname = "SCULPT_OT_brush_stroke"; @@ -4601,15 +4496,11 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot) /* properties */ - RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); - - RNA_def_enum(ot->srna, "mode", stroke_mode_items, BRUSH_STROKE_NORMAL, - "Sculpt Stroke Mode", - "Action taken when a sculpt stroke is made"); + paint_stroke_operator_properties(ot); RNA_def_boolean(ot->srna, "ignore_background_click", 0, "Ignore Background Click", - "Clicks on the background do not start the stroke"); + "Clicks on the background do not start the stroke"); } /**** Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) ****/ @@ -4799,6 +4690,7 @@ void sculpt_dynamic_topology_disable(bContext *C, sculpt_update_after_dynamic_topology_toggle(C); } + static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); @@ -4819,26 +4711,79 @@ static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(o return OPERATOR_FINISHED; } + +static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, bool vdata, bool modifiers) +{ + uiPopupMenu *pup = uiPupMenuBegin(C, IFACE_("Warning!"), ICON_ERROR); + uiLayout *layout = uiPupMenuLayout(pup); + + if (vdata) { + const char *msg_error = TIP_("Vertex Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + if (modifiers) { + const char *msg_error = TIP_("Generative Modifiers Detected!"); + const char *msg = TIP_("Keeping the modifiers will increase polycount when returning to object mode"); + + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } + + uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0); + + uiPupMenuEnd(C, pup); + + return OPERATOR_CANCELLED; +} + + static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Object *ob = CTX_data_active_object(C); Mesh *me = ob->data; SculptSession *ss = ob->sculpt; - const char *msg = TIP_("Dynamic-topology sculpting will not preserve vertex colors, UVs, or other customdata"); if (!ss->bm) { + Scene *scene = CTX_data_scene(C); + ModifierData *md; + VirtualModifierData virtualModifierData; int i; + bool vdata = false; + bool modifiers = false; for (i = 0; i < CD_NUMTYPES; i++) { - if (!ELEM7(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX) && + if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX) && (CustomData_has_layer(&me->vdata, i) || CustomData_has_layer(&me->edata, i) || CustomData_has_layer(&me->fdata, i))) { - /* The mesh has customdata that will be lost, let the user confirm this is OK */ - return WM_operator_confirm_message(C, op, msg); + vdata = true; + break; } } + + md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + ModifierTypeInfo *mti = modifierType_getInfo(md->type); + if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; + + if (mti->type == eModifierTypeType_Constructive) { + modifiers = true; + break; + } + } + + if (vdata || modifiers) { + /* The mesh has customdata that will be lost, let the user confirm this is OK */ + return dyntopo_warning_popup(C, op->type, vdata, modifiers); + } } return sculpt_dynamic_topology_toggle_exec(C, op); @@ -5018,11 +4963,11 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->paint.flags |= PAINT_SHOW_BRUSH; /* Make sure at least dyntopo subdivision is enabled */ - ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE; + ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; } if (!ts->sculpt->detail_size) { - ts->sculpt->detail_size = 30; + ts->sculpt->detail_size = 12; } if (ts->sculpt->constant_detail == 0.0f) @@ -5041,6 +4986,15 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) BKE_sculpt_mask_layers_ensure(ob, mmd); } + if (!(fabsf(ob->size[0] - ob->size[1]) < 1e-4f && fabsf(ob->size[1] - ob->size[2]) < 1e-4f)) { + BKE_report(op->reports, RPT_WARNING, + "Object has non-uniform scale, sculpting may be unpredictable"); + } + else if (is_negative_m4(ob->obmat)) { + BKE_report(op->reports, RPT_WARNING, + "Object has negative scale, sculpting may be unpredictable"); + } + BKE_paint_init(&ts->sculpt->paint, PAINT_CURSOR_SCULPT); paint_cursor_start(C, sculpt_poll_view3d); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index cd79f525d82..a61f571fdf6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -130,4 +130,11 @@ void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]); void sculpt_update_object_bounding_box(struct Object *ob); +/* Setting zero so we can catch bugs in OpenMP/sculpt. */ +#ifdef DEBUG +# define SCULPT_OMP_LIMIT 0 +#else +# define SCULPT_OMP_LIMIT 4 +#endif + #endif diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 57e852db796..91f80a4fc40 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -62,7 +62,8 @@ #include "GPU_buffers.h" -#include "ED_sculpt.h" +#include "ED_paint.h" + #include "bmesh.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -195,9 +196,9 @@ static int sculpt_undo_restore_hidden(bContext *C, DerivedMesh *dm, for (i = 0; i < unode->totvert; i++) { MVert *v = &mvert[unode->index[i]]; - int uval = BLI_BITMAP_GET(unode->vert_hidden, i); + int uval = BLI_BITMAP_TEST(unode->vert_hidden, i); - BLI_BITMAP_MODIFY(unode->vert_hidden, i, + BLI_BITMAP_SET(unode->vert_hidden, i, v->flag & ME_HIDE); if (uval) v->flag |= ME_HIDE; @@ -290,7 +291,7 @@ static void sculpt_undo_bmesh_restore_generic(bContext *C, BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); -#pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP) +#pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (i = 0; i < totnode; i++) { BKE_pbvh_node_mark_redraw(nodes[i]); } @@ -526,9 +527,11 @@ static void sculpt_undo_free(ListBase *lb) } if (unode->mask) MEM_freeN(unode->mask); + if (unode->bm_entry) { BM_log_entry_drop(unode->bm_entry); } + if (unode->bm_enter_totvert) CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); if (unode->bm_enter_totedge) @@ -540,6 +543,23 @@ static void sculpt_undo_free(ListBase *lb) } } +static bool sculpt_undo_cleanup(bContext *C, ListBase *lb) +{ + Object *ob = CTX_data_active_object(C); + SculptUndoNode *unode; + + unode = lb->first; + + if (unode && strcmp(unode->idname, ob->id.name) != 0) { + if (unode->bm_entry) + BM_log_cleanup_entry(unode->bm_entry); + + return true; + } + + return false; +} + SculptUndoNode *sculpt_undo_get_node(PBVHNode *node) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); @@ -681,7 +701,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); for (i = 0; i < allvert; i++) { - BLI_BITMAP_MODIFY(unode->vert_hidden, i, + BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); } } @@ -859,7 +879,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, void sculpt_undo_push_begin(const char *name) { ED_undo_paint_push_begin(UNDO_PAINT_MESH, name, - sculpt_undo_restore, sculpt_undo_free); + sculpt_undo_restore, sculpt_undo_free, sculpt_undo_cleanup); } void sculpt_undo_push_end(void) diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index 797a881ce79..d90eaafa379 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -239,12 +239,14 @@ void ED_space_image_uv_sculpt_update(wmWindowManager *wm, ToolSettings *settings BKE_paint_init(&settings->uvsculpt->paint, PAINT_CURSOR_SCULPT); - WM_paint_cursor_activate(wm, uv_sculpt_brush_poll, - brush_drawcursor_uvsculpt, NULL); + settings->uvsculpt->paint.paint_cursor = WM_paint_cursor_activate(wm, uv_sculpt_brush_poll, + brush_drawcursor_uvsculpt, NULL); } else { - if (settings->uvsculpt) - settings->uvsculpt->paint.flags &= ~PAINT_SHOW_BRUSH; + if (settings->uvsculpt) { + WM_paint_cursor_end(wm, settings->uvsculpt->paint.paint_cursor); + settings->uvsculpt->paint.paint_cursor = NULL; + } } } @@ -271,7 +273,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, Temp_UVData *tmp_uvdata; float diff[2]; int i; - float radius_root = sqrt(radius); + float radius_root = sqrtf(radius); Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); @@ -314,7 +316,7 @@ static void HC_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *sculptdata, if ((dist = dot_v2v2(diff, diff)) <= radius) { UvElement *element; float strength; - strength = alpha * BKE_brush_curve_strength(brush, sqrt(dist), radius_root); + strength = alpha * BKE_brush_curve_strength(brush, sqrtf(dist), radius_root); sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + strength * (tmp_uvdata[i].p[0] - 0.5f * (tmp_uvdata[i].b[0] + tmp_uvdata[i].sum_b[0] / tmp_uvdata[i].ncounter)); sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + strength * (tmp_uvdata[i].p[1] - 0.5f * (tmp_uvdata[i].b[1] + tmp_uvdata[i].sum_b[1] / tmp_uvdata[i].ncounter)); @@ -343,7 +345,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *scul Temp_UVData *tmp_uvdata; float diff[2]; int i; - float radius_root = sqrt(radius); + float radius_root = sqrtf(radius); Brush *brush = BKE_paint_brush(sculptdata->uvsculpt); tmp_uvdata = (Temp_UVData *)MEM_callocN(sculptdata->totalUniqueUvs * sizeof(Temp_UVData), "Temporal data"); @@ -378,7 +380,7 @@ static void laplacian_relaxation_iteration_uv(BMEditMesh *em, UvSculptData *scul if ((dist = dot_v2v2(diff, diff)) <= radius) { UvElement *element; float strength; - strength = alpha * BKE_brush_curve_strength(brush, sqrt(dist), radius_root); + strength = alpha * BKE_brush_curve_strength(brush, sqrtf(dist), radius_root); sculptdata->uv[i].uv[0] = (1.0f - strength) * sculptdata->uv[i].uv[0] + strength * tmp_uvdata[i].p[0]; sculptdata->uv[i].uv[1] = (1.0f - strength) * sculptdata->uv[i].uv[1] + strength * tmp_uvdata[i].p[1]; @@ -432,7 +434,7 @@ static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *e /* We will compare squares to save some computation */ radius = radius * radius; - radius_root = sqrt(radius); + radius_root = sqrtf(radius); /* * Pinch Tool @@ -453,7 +455,7 @@ static void uv_sculpt_stroke_apply(bContext *C, wmOperator *op, const wmEvent *e if ((dist = dot_v2v2(diff, diff)) <= radius) { UvElement *element; float strength; - strength = alpha * BKE_brush_curve_strength(brush, sqrt(dist), radius_root); + strength = alpha * BKE_brush_curve_strength(brush, sqrtf(dist), radius_root); normalize_v2(diff); sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f; @@ -561,7 +563,7 @@ static unsigned int uv_edge_hash(const void *key) BLI_ghashutil_uinthash(edge->uv1)); } -static int uv_edge_compare(const void *a, const void *b) +static bool uv_edge_compare(const void *a, const void *b) { UvEdge *edge1 = (UvEdge *)a; UvEdge *edge2 = (UvEdge *)b; @@ -801,7 +803,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm aspectRatio = width / (float)height; radius /= (width * zoomx); radius = radius * radius; - radius_root = sqrt(radius); + radius_root = sqrtf(radius); /* Allocate selection stack */ data->initial_stroke = MEM_mallocN(sizeof(*data->initial_stroke), "uv_sculpt_initial_stroke"); @@ -827,7 +829,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm diff[1] /= aspectRatio; if ((dist = dot_v2v2(diff, diff)) <= radius) { float strength; - strength = alpha * BKE_brush_curve_strength(brush, sqrt(dist), radius_root); + strength = alpha * BKE_brush_curve_strength(brush, sqrtf(dist), radius_root); data->initial_stroke->initialSelection[counter].uv = i; data->initial_stroke->initialSelection[counter].strength = strength; diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt index e0dc2709cf3..96a1e74c882 100644 --- a/source/blender/editors/space_action/CMakeLists.txt +++ b/source/blender/editors/space_action/CMakeLists.txt @@ -22,10 +22,12 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -42,4 +44,6 @@ set(SRC action_intern.h ) +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_action "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_action/SConscript b/source/blender/editors/space_action/SConscript index 2776bd2989a..2e2081b3c6e 100644 --- a/source/blender/editors/space_action/SConscript +++ b/source/blender/editors/space_action/SConscript @@ -29,16 +29,20 @@ Import ('env') sources = env.Glob('*.c') +defs = env['BF_GL_DEFINITIONS'] + incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] incs = ' '.join(incs) -env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), [], libtype=['core'], priority=[40] ) +env.BlenderLib ( 'bf_editors_space_action', sources, Split(incs), defs, libtype=['core'], priority=[40] ) diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index f15bbe0d2f1..335949e8495 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -139,7 +139,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* free tempolary channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ************************************************************************* */ @@ -219,7 +219,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) if (acf->has_setting(ac, ale, ACHANNEL_SETTING_SELECT)) sel = ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_SELECT); - if (ELEM3(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY)) { + if (ELEM(ac->datatype, ANIMCONT_ACTION, ANIMCONT_DOPESHEET, ANIMCONT_SHAPEKEY)) { switch (ale->type) { case ANIMTYPE_SUMMARY: { @@ -346,7 +346,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) } /* free tempolary channels used for drawing */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* black line marking 'current frame' for Time-Slide transform mode */ if (saction->flag & SACTION_MOVING) { diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 92e727fc2d7..091d3fe56b4 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -299,7 +299,7 @@ static bool get_keyframe_extents(bAnimContext *ac, float *min, float *max, const } /* free memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } else { /* set default range */ @@ -362,10 +362,12 @@ void ACTION_OT_previewrange_set(wmOperatorType *ot) /* ****************** View-All Operator ****************** */ -/* Find the extents of the active channel - * > min: (float) bottom y-extent of channel - * > max: (float) top y-extent of channel - * > returns: success of finding a selected channel +/** + * Find the extents of the active channel + * + * \param[out] min Bottom y-extent of channel + * \param[out] max Top y-extent of channel + * \return Success of finding a selected channel */ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, float *max) { @@ -410,7 +412,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, } /* free all temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return (found != 0); } @@ -536,7 +538,7 @@ static short copy_action_keys(bAnimContext *ac) ok = copy_animedit_keys(ac, &anim_data); /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ok; } @@ -561,9 +563,9 @@ static short paste_action_keys(bAnimContext *ac, /* paste keyframes */ ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode); - + /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ok; } @@ -640,10 +642,7 @@ static int actkeys_paste_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } } - - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - + /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -718,9 +717,12 @@ static void insert_action_keys(bAnimContext *ac, short mode) insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag); else insert_vert_fcurve(fcu, cfra, fcu->curval, 0); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -741,10 +743,7 @@ static int actkeys_insertkey_exec(bContext *C, wmOperator *op) /* insert keyframes */ insert_action_keys(&ac, mode); - - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - + /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); @@ -795,10 +794,12 @@ static void duplicate_action_keys(bAnimContext *ac) ED_masklayer_frames_duplicate((MaskLayer *)ale->data); else BLI_assert(0); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - /* free filtered list */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -813,11 +814,7 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) /* duplicate keyframes */ duplicate_action_keys(&ac); - - /* validate keyframes after editing */ - if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) - ANIM_editkeyframes_refresh(&ac); - + /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); @@ -854,40 +851,49 @@ static bool delete_action_keys(bAnimContext *ac) ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - bool changed = false; - + bool changed_final = false; + /* filter data */ if (ELEM(ac->datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); else filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_FOREDIT /*| ANIMFILTER_CURVESONLY*/ | ANIMFILTER_NODUPLIS); ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - + /* loop through filtered data and delete selected keys */ for (ale = anim_data.first; ale; ale = ale->next) { + bool changed = false; + if (ale->type == ANIMTYPE_GPLAYER) { - changed |= ED_gplayer_frames_delete((bGPDlayer *)ale->data); + changed = ED_gplayer_frames_delete((bGPDlayer *)ale->data); } else if (ale->type == ANIMTYPE_MASKLAYER) { - changed |= ED_masklayer_frames_delete((MaskLayer *)ale->data); + changed = ED_masklayer_frames_delete((MaskLayer *)ale->data); } else { FCurve *fcu = (FCurve *)ale->key_data; AnimData *adt = ale->adt; /* delete selected keyframes only */ - changed |= delete_fcurve_keys(fcu); + changed = delete_fcurve_keys(fcu); /* Only delete curve too if it won't be doing anything anymore */ - if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0)) + if ((fcu->totvert == 0) && (list_has_suitable_fmodifier(&fcu->modifiers, 0, FMI_TYPE_GENERATE_CURVE) == 0)) { ANIM_fcurve_delete_from_animdata(ac, adt, fcu); + ale->key_data = NULL; + } + } + + if (changed) { + ale->update |= ANIM_UPDATE_DEFAULT; + changed_final = true; } } - - /* free filtered list */ - BLI_freelistN(&anim_data); - return changed; + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); + + return changed_final; } /* ------------------- */ @@ -904,10 +910,6 @@ static int actkeys_delete_exec(bContext *C, wmOperator *UNUSED(op)) if (!delete_action_keys(&ac)) return OPERATOR_CANCELLED; - /* validate keyframes after editing */ - if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); @@ -943,11 +945,14 @@ static void clean_action_keys(bAnimContext *ac, float thresh) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and clean curves */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { clean_fcurve((FCurve *)ale->key_data, thresh); - - /* free temp data */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -969,9 +974,6 @@ static int actkeys_clean_exec(bContext *C, wmOperator *op) /* clean keyframes */ clean_action_keys(&ac, thresh); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1011,11 +1013,14 @@ static void sample_action_keys(bAnimContext *ac) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and add keys between selected keyframes on every frame */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { sample_fcurve((FCurve *)ale->key_data); - - /* admin and redraws */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEPS; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1033,9 +1038,6 @@ static int actkeys_sample_exec(bContext *C, wmOperator *UNUSED(op)) /* sample keyframes */ sample_action_keys(&ac); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1118,10 +1120,12 @@ static void setexpo_action_keys(bAnimContext *ac, short mode) } } } + + ale->update |= ANIM_UPDATE_DEFAULT; } - - /* cleanup */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1143,9 +1147,6 @@ static int actkeys_expo_exec(bContext *C, wmOperator *op) /* set handle type */ setexpo_action_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1188,11 +1189,14 @@ static void setipo_action_keys(bAnimContext *ac, short mode) /* loop through setting BezTriple interpolation * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here... */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); - - /* cleanup */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1214,9 +1218,6 @@ static int actkeys_ipo_exec(bContext *C, wmOperator *op) /* set handle type */ setipo_action_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1268,11 +1269,13 @@ static void sethandles_action_keys(bAnimContext *ac, short mode) if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* change type of selected handles */ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + + ale->update |= ANIM_UPDATE_DEFAULT; } } - - /* cleanup */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1294,9 +1297,6 @@ static int actkeys_handletype_exec(bContext *C, wmOperator *op) /* set handle type */ sethandles_action_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1339,11 +1339,14 @@ static void setkeytype_action_keys(bAnimContext *ac, short mode) /* loop through setting BezTriple interpolation * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here... */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, NULL); - - /* cleanup */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEPS | ANIM_UPDATE_HANDLES; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1365,9 +1368,6 @@ static int actkeys_keytype_exec(bContext *C, wmOperator *op) /* set handle type */ setkeytype_action_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1436,7 +1436,7 @@ static int actkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_calc_average, NULL); } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set the new current frame value, based on the average time */ if (ked.i1) { @@ -1525,9 +1525,12 @@ static void snap_action_keys(bAnimContext *ac, short mode) else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); } + + ale->update |= ANIM_UPDATE_DEFAULT; } - - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1547,10 +1550,6 @@ static int actkeys_snap_exec(bContext *C, wmOperator *op) /* snap keyframes */ snap_action_keys(&ac, mode); - /* validate keyframes after editing */ - if (!ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1635,9 +1634,12 @@ static void mirror_action_keys(bAnimContext *ac, short mode) // snap_gplayer_frames(ale->data, mode); else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1661,9 +1663,6 @@ static int actkeys_mirror_exec(bContext *C, wmOperator *op) /* mirror keyframes */ mirror_action_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 93cd94ed892..b99419dec20 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -224,6 +224,9 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) */ WM_keymap_add_item(keymap, "ANIM_OT_channels_editable_toggle", TABKEY, KM_PRESS, 0, 0); + /* find (i.e. a shortcut for setting the name filter) */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0); + /* transform system */ transform_keymap_for_space(keyconf, keymap, SPACE_ACTION); diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index 1e89b304279..ddfca98a119 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -142,7 +142,7 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -294,7 +294,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -442,7 +442,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } @@ -477,7 +477,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) for (ale = anim_data.first; ale; ale = ale->next) ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL); } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); break; case ACTKEYS_COLUMNSEL_CFRA: /* current frame */ @@ -534,7 +534,7 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) /* free elements */ BLI_freelistN(&ked.list); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -612,7 +612,7 @@ static int actkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -675,7 +675,7 @@ static void select_moreless_action_keys(bAnimContext *ac, short mode) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ----------------- */ @@ -838,7 +838,7 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ----------------- */ @@ -975,7 +975,7 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } else { ANIM_animchannel_keyframes_loop(&ked, ac->ads, ale, ok_cb, select_cb, NULL); @@ -1029,7 +1029,7 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se /* free elements */ BLI_freelistN(&ked.list); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* option 4) select all keyframes in same channel */ @@ -1066,7 +1066,7 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s } } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } else { ANIM_animchannel_keyframes_loop(NULL, ac->ads, ale, NULL, select_cb, NULL); @@ -1086,8 +1086,9 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ View2D *v2d = &ac->ar->v2d; bDopeSheet *ads = NULL; int channel_index; - short found = 0; - float selx = 0.0f; + 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 x, y; rctf rectf; @@ -1112,7 +1113,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ if (ale == NULL) { /* channel not found */ printf("Error: animation channel (index = %d) not found in mouse_action_keys()\n", channel_index); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } else { @@ -1179,7 +1180,8 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ * requiring to map each frame once again... */ selx = BKE_nla_tweakedit_remap(adt, ak->cfra, NLATIME_CONVERT_UNMAP); - found = 1; + frame = ak->cfra; + found = true; break; } else if (ak->cfra < rectf.xmin) @@ -1195,7 +1197,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ BLI_dlrbTree_free(&anim_keys); /* free list of channels, since it's not used anymore */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* for replacing selection, firstly need to clear existing selection */ @@ -1258,8 +1260,11 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ if (found) { /* apply selection to keyframes */ if (column) { - /* select all keyframes in the same frame as the one we hit on the active channel */ - actkeys_mselect_column(ac, select_mode, selx); + /* select all keyframes in the same frame as the one we hit on the active channel + * [T41077]: "frame" not "selx" here (i.e. no NLA corrections yet) as the code here + * does that itself again as it needs to work on multiple datablocks + */ + actkeys_mselect_column(ac, select_mode, frame); } else if (same_channel) { /* select all keyframes in the active channel */ diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index 69eeac69e85..7ca8968a705 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -476,7 +476,14 @@ static void action_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa) break; case NC_ANIMATION: switch (wmn->data) { - case ND_KEYFRAME: + case ND_ANIMCHAN: /* set of visible animchannels changed */ + /* NOTE: for now, this should usually just mean that the filters changed + * It may be better if we had a dedicated flag for that though + */ + ED_region_tag_redraw(ar); + break; + + case ND_KEYFRAME: /* new keyframed added -> active action may have changed */ //saction->flag |= SACTION_TEMP_NEEDCHANSYNC; ED_region_tag_redraw(ar); break; diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index 83040a26480..c8431d58bf5 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -53,6 +53,7 @@ #include "ED_mesh.h" #include "ED_node.h" #include "ED_object.h" +#include "ED_paint.h" #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" @@ -60,6 +61,7 @@ #include "ED_space_api.h" #include "ED_sound.h" #include "ED_uvedit.h" +#include "ED_view3d.h" #include "ED_mball.h" #include "ED_logic.h" #include "ED_clip.h" @@ -129,8 +131,17 @@ void ED_spacetypes_init(void) type->operatortypes(); } - /* Macros's must go last since they reference other operators - * maybe we'll need to have them go after python operators too? */ + /* register internal render callbacks */ + ED_render_internal_init(); +} + +void ED_spacemacros_init(void) +{ + const ListBase *spacetypes; + SpaceType *type; + + /* Macros's must go last since they reference other operators. + * We need to have them go after python operators too */ ED_operatormacros_armature(); ED_operatormacros_mesh(); ED_operatormacros_metaball(); @@ -143,6 +154,7 @@ void ED_spacetypes_init(void) ED_operatormacros_curve(); ED_operatormacros_mask(); ED_operatormacros_sequencer(); + ED_operatormacros_paint(); /* register dropboxes (can use macros) */ spacetypes = BKE_spacetypes_list(); @@ -150,9 +162,6 @@ void ED_spacetypes_init(void) if (type->dropboxes) type->dropboxes(); } - - /* register internal render callbacks */ - ED_render_internal_init(); } /* called in wm.c */ diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index 9e045a39a0c..01a099e3701 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -46,6 +48,8 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/editors/space_buttons/SConscript b/source/blender/editors/space_buttons/SConscript index 61f9b6f496e..07fe17e302b 100644 --- a/source/blender/editors/space_buttons/SConscript +++ b/source/blender/editors/space_buttons/SConscript @@ -31,12 +31,14 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../bmesh', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -44,7 +46,8 @@ incs = [ '../../windowmanager', ] incs = ' '.join(incs) -defs = [] + +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index a00aac12fba..524a42ba388 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -55,6 +55,7 @@ #include "BKE_particle.h" #include "BKE_screen.h" #include "BKE_texture.h" +#include "BKE_linestyle.h" #include "RNA_access.h" @@ -153,7 +154,7 @@ static int buttons_context_path_linestyle(ButsContextPath *path) /* if we have a scene, use the lineset's linestyle */ else if (buttons_context_path_scene(path)) { scene = path->ptr[path->len - 1].data; - linestyle = CTX_data_linestyle_from_scene(scene); + linestyle = BKE_linestyle_active_from_scene(scene); if (linestyle) { RNA_id_pointer_create(&linestyle->id, &path->ptr[path->len]); path->len++; @@ -199,7 +200,7 @@ static int buttons_context_path_data(ButsContextPath *path, int type) /* if we already have a data, we're done */ if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) return 1; - else if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM3(type, OB_CURVE, OB_SURF, OB_FONT))) return 1; + else if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) return 1; @@ -229,14 +230,14 @@ static int buttons_context_path_modifier(ButsContextPath *path) if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; - if (ob && ELEM5(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE)) + if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE)) return 1; } return 0; } -static int buttons_context_path_material(ButsContextPath *path, int for_texture) +static int buttons_context_path_material(ButsContextPath *path, bool for_texture, bool new_shading) { Object *ob; PointerRNA *ptr = &path->ptr[path->len - 1]; @@ -257,11 +258,14 @@ static int buttons_context_path_material(ButsContextPath *path, int for_texture) if (for_texture && give_current_material_texture_node(ma)) return 1; - - ma = give_node_material(ma); - if (ma) { - RNA_id_pointer_create(&ma->id, &path->ptr[path->len]); - path->len++; + + if (!new_shading) { + /* Only try to get mat from node in case of old shading system (see T40331). */ + ma = give_node_material(ma); + if (ma) { + RNA_id_pointer_create(&ma->id, &path->ptr[path->len]); + path->len++; + } } return 1; } @@ -411,7 +415,7 @@ static int buttons_context_path_texture(ButsContextPath *path, ButsContextTextur if (GS(id->name) == ID_BR) buttons_context_path_brush(path); else if (GS(id->name) == ID_MA) - buttons_context_path_material(path, 0); + buttons_context_path_material(path, false, true); else if (GS(id->name) == ID_WO) buttons_context_path_world(path); else if (GS(id->name) == ID_LA) @@ -480,7 +484,7 @@ static int buttons_context_path_texture(ButsContextPath *path, ButsContextTextur } } /* try material */ - else if ((path->tex_ctx == SB_TEXC_MATERIAL) && buttons_context_path_material(path, 1)) { + else if ((path->tex_ctx == SB_TEXC_MATERIAL) && buttons_context_path_material(path, true, false)) { ma = path->ptr[path->len - 1].data; if (ma) { @@ -609,7 +613,7 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma found = buttons_context_path_particle(path); break; case BCONTEXT_MATERIAL: - found = buttons_context_path_material(path, 0); + found = buttons_context_path_material(path, false, (sbuts->texuser != NULL)); break; case BCONTEXT_TEXTURE: found = buttons_context_path_texture(path, sbuts->texuser); @@ -634,7 +638,7 @@ static int buttons_shading_context(const bContext *C, int mainb) { Object *ob = CTX_data_active_object(C); - if (ELEM3(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) + if (ELEM(mainb, BCONTEXT_MATERIAL, BCONTEXT_WORLD, BCONTEXT_TEXTURE)) return 1; if (mainb == BCONTEXT_DATA && ob && ELEM(ob->type, OB_LAMP, OB_CAMERA)) return 1; @@ -1116,7 +1120,7 @@ void buttons_context_draw(const bContext *C, uiLayout *layout) name = RNA_struct_name_get_alloc(ptr, namebuf, sizeof(namebuf), NULL); if (name) { - if (!ELEM3(sbuts->mainb, BCONTEXT_RENDER, BCONTEXT_SCENE, BCONTEXT_RENDER_LAYER) && ptr->type == &RNA_Scene) + if (!ELEM(sbuts->mainb, BCONTEXT_RENDER, BCONTEXT_SCENE, BCONTEXT_RENDER_LAYER) && ptr->type == &RNA_Scene) uiItemLDrag(row, ptr, "", icon); /* save some space */ else uiItemLDrag(row, ptr, name, icon); diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index c0837b627b3..b651d684bf6 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -239,6 +239,8 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot) ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; + ot->flag |= OPTYPE_UNDO; + /* properties */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY); @@ -257,6 +259,8 @@ void BUTTONS_OT_directory_browse(wmOperatorType *ot) ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; + ot->flag |= OPTYPE_UNDO; + /* properties */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY); diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index c558d811693..020d477fc39 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -53,8 +53,8 @@ #include "DNA_world_types.h" #include "DNA_linestyle_types.h" - #include "BKE_context.h" +#include "BKE_linestyle.h" #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_node.h" @@ -352,7 +352,7 @@ static void buttons_texture_users_from_context(ListBase *users, const bContext * ob = (scene->basact) ? scene->basact->object : NULL; wrld = scene->world; brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - linestyle = CTX_data_linestyle_from_scene(scene); + linestyle = BKE_linestyle_active_from_scene(scene); } if (ob && ob->type == OB_LAMP && !la) @@ -370,7 +370,7 @@ static void buttons_texture_users_from_context(ListBase *users, const bContext * if (wrld && !limited_mode) buttons_texture_users_find_nodetree(users, &wrld->id, wrld->nodetree, "World"); if (linestyle && !limited_mode) - buttons_texture_users_find_nodetree(users, &linestyle->id, linestyle->nodetree, "LineStyle"); + buttons_texture_users_find_nodetree(users, &linestyle->id, linestyle->nodetree, "Line Style"); if (ob) { ParticleSystem *psys = psys_get_current(ob); @@ -442,7 +442,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceButs *sbuts) set_texture_context(C, sbuts); - if (!(BKE_scene_use_new_shading_nodes(scene) || (sbuts->texture_context == SB_TEXC_OTHER))) { + if (!((sbuts->texture_context == SB_TEXC_OTHER) || BKE_scene_use_new_shading_nodes(scene))) { if (ct) { BLI_freelistN(&ct->users); MEM_freeN(ct); diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 1257dcb5e4c..4d62f528915 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -233,6 +233,10 @@ static void buttons_area_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier * buttons_area_redraw(sa, BCONTEXT_RENDER); buttons_area_redraw(sa, BCONTEXT_RENDER_LAYER); break; + case ND_WORLD: + buttons_area_redraw(sa, BCONTEXT_WORLD); + sbuts->preview = 1; + break; case ND_FRAME: /* any buttons area can have animated properties so redraw all */ ED_area_tag_redraw(sa); @@ -358,7 +362,7 @@ static void buttons_area_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier * case NC_ANIMATION: switch (wmn->data) { case ND_KEYFRAME: - if (wmn->action == NA_EDITED) + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) ED_area_tag_redraw(sa); break; } diff --git a/source/blender/editors/space_clip/CMakeLists.txt b/source/blender/editors/space_clip/CMakeLists.txt index 7689aa28169..4659e612b41 100644 --- a/source/blender/editors/space_clip/CMakeLists.txt +++ b/source/blender/editors/space_clip/CMakeLists.txt @@ -27,11 +27,12 @@ set(INC ../../blenfont ../../blenlib ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../windowmanager - ../../gpu ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -56,6 +57,8 @@ set(SRC clip_intern.h ) +add_definitions(${GL_DEFINITIONS}) + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/editors/space_clip/SConscript b/source/blender/editors/space_clip/SConscript index d4483eda6e3..8fec4ebf0fa 100644 --- a/source/blender/editors/space_clip/SConscript +++ b/source/blender/editors/space_clip/SConscript @@ -28,10 +28,13 @@ Import ('env') sources = env.Glob('*.c') -defs = [] + +defs = env['BF_GL_DEFINITIONS'] + incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index cda46d2a9c2..889613b5d13 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -84,8 +84,8 @@ void ED_clip_buttons_register(ARegionType *art) strcpy(pt->idname, "CLIP_PT_gpencil"); strcpy(pt->label, N_("Grease Pencil")); strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw_header = gpencil_panel_standard_header; - pt->draw = gpencil_panel_standard; + pt->draw_header = ED_gpencil_panel_standard_header; + pt->draw = ED_gpencil_panel_standard; pt->flag |= PNL_DEFAULT_CLOSED; pt->poll = clip_grease_pencil_panel_poll; BLI_addtail(&art->paneltypes, pt); diff --git a/source/blender/editors/space_clip/clip_dopesheet_ops.c b/source/blender/editors/space_clip/clip_dopesheet_ops.c index 573b6bc9276..7ae5eda7139 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_ops.c +++ b/source/blender/editors/space_clip/clip_dopesheet_ops.c @@ -93,6 +93,7 @@ static int dopesheet_select_channel_exec(bContext *C, wmOperator *op) float location[2]; const bool extend = RNA_boolean_get(op->ptr, "extend"); int current_channel_index = 0, channel_index; + const bool show_selected_only = (dopesheet->flag & TRACKING_DOPE_SELECTED_ONLY) != 0; RNA_float_get_array(op->ptr, "location", location); channel_index = -(location[1] - (CHANNEL_FIRST + CHANNEL_HEIGHT_HALF)) / CHANNEL_STEP; @@ -110,6 +111,9 @@ static int dopesheet_select_channel_exec(bContext *C, wmOperator *op) tracking->act_track = track; BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true); } + else if (show_selected_only == false) { + BKE_tracking_track_deselect(track, TRACK_AREA_ALL); + } } else if (!extend) track->flag &= ~TRACK_DOPE_SEL; diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 9d413e81ea8..a35251e71ef 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -1595,21 +1595,7 @@ static void draw_distortion(SpaceClip *sc, ARegion *ar, MovieClip *clip, } } - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - MovieTrackingTrack *track = BKE_tracking_track_get_active(&sc->clip->tracking); - - if (track) { - int framenr = ED_space_clip_get_clip_frame_number(sc); - MovieTrackingMarker *marker = BKE_tracking_marker_get(track, framenr); - - offsx = marker->pos[0]; - offsy = marker->pos[1]; - - gpd = track->gpd; - } - - } - else { + if (sc->gpencil_src != SC_GPENCIL_SRC_TRACK) { gpd = clip->gpd; } @@ -1728,7 +1714,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar) smat[1][1] = 1.0f / height; invert_m4_m4(ismat, smat); - mul_serie_m4(sc->unistabmat, smat, sc->stabmat, ismat, NULL, NULL, NULL, NULL, NULL); + mul_m4_series(sc->unistabmat, smat, sc->stabmat, ismat); } } else if ((sc->flag & SC_MUTE_FOOTAGE) == 0) { @@ -1778,13 +1764,15 @@ void clip_draw_grease_pencil(bContext *C, int onlyv2d) return; if (onlyv2d) { - /* if manual calibration is used then grease pencil data is already - * drawn in draw_distortion */ - if ((sc->flag & SC_MANUAL_CALIBRATION) == 0) { + bool is_track_source = sc->gpencil_src == SC_GPENCIL_SRC_TRACK; + /* if manual calibration is used then grease pencil data + * associated with the clip is already drawn in draw_distortion + */ + if ((sc->flag & SC_MANUAL_CALIBRATION) == 0 || is_track_source) { glPushMatrix(); glMultMatrixf(sc->unistabmat); - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + if (is_track_source) { MovieTrackingTrack *track = BKE_tracking_track_get_active(&sc->clip->tracking); if (track) { @@ -1795,12 +1783,12 @@ void clip_draw_grease_pencil(bContext *C, int onlyv2d) } } - draw_gpencil_2dimage(C); + ED_gpencil_draw_2dimage(C); glPopMatrix(); } } else { - draw_gpencil_view2d(C, 0); + ED_gpencil_draw_view2d(C, 0); } } diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index b7860643e1a..f25f035db32 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -48,7 +48,7 @@ #include "BLI_fileops.h" #include "BLI_math.h" #include "BLI_rect.h" -#include "BLI_threads.h" +#include "BLI_task.h" #include "BKE_global.h" #include "BKE_main.h" @@ -620,11 +620,6 @@ typedef struct PrefetchQueue { float *progress; } PrefetchQueue; -typedef struct PrefetchThread { - MovieClip *clip; - PrefetchQueue *queue; -} PrefetchThread; - /* check whether pre-fetching is allowed */ static bool check_prefetch_break(void) { @@ -757,15 +752,15 @@ static unsigned char *prefetch_thread_next_frame(PrefetchQueue *queue, MovieClip return mem; } -static void *do_prefetch_thread(void *data_v) +static void prefetch_task_func(TaskPool *pool, void *task_data, int UNUSED(threadid)) { - PrefetchThread *data = (PrefetchThread *) data_v; - MovieClip *clip = data->clip; + PrefetchQueue *queue = (PrefetchQueue *)BLI_task_pool_userdata(pool); + MovieClip *clip = (MovieClip *)task_data; unsigned char *mem; size_t size; int current_frame; - while ((mem = prefetch_thread_next_frame(data->queue, data->clip, &size, ¤t_frame))) { + while ((mem = prefetch_thread_next_frame(queue, clip, &size, ¤t_frame))) { ImBuf *ibuf; MovieClipUser user = {0}; int flag = IB_rect | IB_alphamode_detect; @@ -773,17 +768,17 @@ static void *do_prefetch_thread(void *data_v) char *colorspace_name = NULL; user.framenr = current_frame; - user.render_size = data->queue->render_size; - user.render_flag = data->queue->render_flag; + user.render_size = queue->render_size; + user.render_flag = queue->render_flag; /* Proxies are stored in the display space. */ - if (data->queue->render_flag & MCLIP_USE_PROXY) { + if (queue->render_flag & MCLIP_USE_PROXY) { colorspace_name = clip->colorspace_settings.name; } ibuf = IMB_ibImageFromMemory(mem, size, flag, colorspace_name, "prefetch frame"); - result = BKE_movieclip_put_frame_if_possible(data->clip, &user, ibuf); + result = BKE_movieclip_put_frame_if_possible(clip, &user, ibuf); IMB_freeImBuf(ibuf); @@ -791,27 +786,20 @@ static void *do_prefetch_thread(void *data_v) if (!result) { /* no more space in the cache, stop reading frames */ - *data->queue->stop = 1; + *queue->stop = 1; break; } } - - return NULL; } static void start_prefetch_threads(MovieClip *clip, int start_frame, int current_frame, int end_frame, short render_size, short render_flag, short *stop, short *do_update, float *progress) { - ListBase threads; PrefetchQueue queue; - PrefetchThread *handles; - int tot_thread = BLI_system_thread_count(); - int i; - - /* reserve one thread for the interface */ - if (tot_thread > 1) - tot_thread--; + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool; + int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler); /* initialize queue */ BLI_spin_init(&queue.spin); @@ -828,29 +816,18 @@ static void start_prefetch_threads(MovieClip *clip, int start_frame, int current queue.do_update = do_update; queue.progress = progress; - /* fill in thread handles */ - handles = MEM_callocN(sizeof(PrefetchThread) * tot_thread, "prefetch threaded handles"); - - if (tot_thread > 1) - BLI_init_threads(&threads, do_prefetch_thread, tot_thread); - + task_pool = BLI_task_pool_create(task_scheduler, &queue); for (i = 0; i < tot_thread; i++) { - PrefetchThread *handle = &handles[i]; - - handle->clip = clip; - handle->queue = &queue; - - if (tot_thread > 1) - BLI_insert_thread(&threads, handle); + BLI_task_pool_push(task_pool, + prefetch_task_func, + clip, + false, + TASK_PRIORITY_LOW); } + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); - /* run the threads */ - if (tot_thread > 1) - BLI_end_threads(&threads); - else - do_prefetch_thread(handles); - - MEM_freeN(handles); + BLI_spin_end(&queue.spin); } static bool prefetch_movie_frame(MovieClip *clip, int frame, short render_size, diff --git a/source/blender/editors/space_clip/clip_graph_ops.c b/source/blender/editors/space_clip/clip_graph_ops.c index 95f59e79c08..d1e2c770ade 100644 --- a/source/blender/editors/space_clip/clip_graph_ops.c +++ b/source/blender/editors/space_clip/clip_graph_ops.c @@ -104,7 +104,7 @@ typedef struct { int coord; /* coordinate index of found entuty (0 = X-axis, 1 = Y-axis) */ bool has_prev; /* if there's valid coordinate of previous point of curve segment */ - float min_dist, /* minimal distance between mouse and currently found entuty */ + float min_dist_sq, /* minimal distance between mouse and currently found entity */ mouse_co[2], /* mouse coordinate */ prev_co[2], /* coordinate of previeous point of segment */ min_co[2]; /* coordinate of entity with minimal distance */ @@ -121,11 +121,11 @@ static void find_nearest_tracking_segment_cb(void *userdata, MovieTrackingTrack float co[2] = {scene_framenr, val}; if (data->has_prev) { - float d = dist_to_line_segment_v2(data->mouse_co, data->prev_co, co); + float dist_sq = dist_squared_to_line_segment_v2(data->mouse_co, data->prev_co, co); - if (data->track == NULL || d < data->min_dist) { + if (data->track == NULL || dist_sq < data->min_dist_sq) { data->track = track; - data->min_dist = d; + data->min_dist_sq = dist_sq; data->coord = coord; copy_v2_v2(data->min_co, co); } @@ -146,15 +146,15 @@ static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *tr MovieTrackingMarker *marker, int coord, int scene_framenr, float val) { MouseSelectUserData *data = userdata; - float dx = scene_framenr - data->mouse_co[0], dy = val - data->mouse_co[1]; - float d = dx * dx + dy * dy; + float mdiff[2] = {scene_framenr - data->mouse_co[0], val - data->mouse_co[1]}; + float dist_sq = len_squared_v2(mdiff); - if (data->marker == NULL || d < data->min_dist) { + if (data->marker == NULL || dist_sq < data->min_dist_sq) { float co[2] = {scene_framenr, val}; data->track = track; data->marker = marker; - data->min_dist = d; + data->min_dist_sq = dist_sq; data->coord = coord; copy_v2_v2(data->min_co, co); } @@ -164,7 +164,7 @@ static void find_nearest_tracking_knot_cb(void *userdata, MovieTrackingTrack *tr static void mouse_select_init_data(MouseSelectUserData *userdata, const float co[2]) { memset(userdata, 0, sizeof(MouseSelectUserData)); - userdata->min_dist = FLT_MAX; + userdata->min_dist_sq = FLT_MAX; copy_v2_v2(userdata->mouse_co, co); } diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 7ccee73626c..9f7bcae800a 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -49,7 +49,7 @@ #include "BLI_path_util.h" #include "BLI_math.h" #include "BLI_rect.h" -#include "BLI_threads.h" +#include "BLI_task.h" #include "BLI_string.h" #include "BLF_translation.h" @@ -79,6 +79,8 @@ #include "UI_view2d.h" +#include "PIL_time.h" + #include "clip_intern.h" // own include /******************** view navigation utilities *********************/ @@ -486,18 +488,25 @@ typedef struct ViewZoomData { float zoom; int event_type; float location[2]; + wmTimer *timer; + double timer_lastdraw; } ViewZoomData; static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event) { SpaceClip *sc = CTX_wm_space_clip(C); ARegion *ar = CTX_wm_region(C); - ViewZoomData *vpd; op->customdata = vpd = MEM_callocN(sizeof(ViewZoomData), "ClipViewZoomData"); WM_cursor_modal_set(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR); + if (U.viewzoom == USER_ZOOM_CONT) { + /* needs a timer to continue redrawing */ + vpd->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f); + vpd->timer_lastdraw = PIL_check_seconds_timer(); + } + vpd->x = event->x; vpd->y = event->y; vpd->zoom = sc->zoom; @@ -518,6 +527,10 @@ static void view_zoom_exit(bContext *C, wmOperator *op, bool cancel) ED_region_tag_redraw(CTX_wm_region(C)); } + if (vpd->timer) { + WM_event_remove_timer(CTX_wm_manager(C), vpd->timer->win, vpd->timer); + } + WM_cursor_modal_restore(CTX_wm_window(C)); MEM_freeN(op->customdata); } @@ -555,22 +568,61 @@ static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) } } +static void view_zoom_apply(bContext *C, + ViewZoomData *vpd, + wmOperator *op, + const wmEvent *event) +{ + float factor; + + if (U.viewzoom == USER_ZOOM_CONT) { + SpaceClip *sclip = CTX_wm_space_clip(C); + double time = PIL_check_seconds_timer(); + float time_step = (float)(time - vpd->timer_lastdraw); + float fac; + float zfac; + + if (U.uiflag & USER_ZOOM_HORIZ) { + fac = (float)(event->x - vpd->x); + } + else { + fac = (float)(event->y - vpd->y); + } + + if (U.uiflag & USER_ZOOM_INVERT) { + fac = -fac; + } + + zfac = 1.0f + ((fac / 20.0f) * time_step); + vpd->timer_lastdraw = time; + factor = (sclip->zoom * zfac) / vpd->zoom; + } + else { + float delta = event->x - vpd->x + event->y - vpd->y; + + if (U.uiflag & USER_ZOOM_INVERT) { + delta *= -1; + } + + factor = 1.0f + delta / 300.0f; + } + + RNA_float_set(op->ptr, "factor", factor); + sclip_zoom_set(C, vpd->zoom * factor, vpd->location); + ED_region_tag_redraw(CTX_wm_region(C)); +} + static int view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *event) { ViewZoomData *vpd = op->customdata; - float delta, factor; - switch (event->type) { + case TIMER: + if (event->customdata == vpd->timer) { + view_zoom_apply(C, vpd, op, event); + } + break; case MOUSEMOVE: - delta = event->x - vpd->x + event->y - vpd->y; - - if (U.uiflag & USER_ZOOM_INVERT) - delta *= -1; - - factor = 1.0f + delta / 300.0f; - RNA_float_set(op->ptr, "factor", factor); - sclip_zoom_set(C, vpd->zoom * factor, vpd->location); - ED_region_tag_redraw(CTX_wm_region(C)); + view_zoom_apply(C, vpd, op, event); break; default: if (event->type == vpd->event_type && event->val == KM_RELEASE) { @@ -591,6 +643,8 @@ static void view_zoom_cancel(bContext *C, wmOperator *op) void CLIP_OT_view_zoom(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom"; ot->idname = "CLIP_OT_view_zoom"; @@ -607,8 +661,9 @@ void CLIP_OT_view_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; /* properties */ - RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, - "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX); + prop = RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, "Factor", + "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /********************** view zoom in/out operator *********************/ @@ -641,6 +696,8 @@ static int view_zoom_in_invoke(bContext *C, wmOperator *op, const wmEvent *event void CLIP_OT_view_zoom_in(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom In"; ot->idname = "CLIP_OT_view_zoom_in"; @@ -652,8 +709,9 @@ void CLIP_OT_view_zoom_in(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* properties */ - RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", - "Cursor location in screen coordinates", -10.0f, 10.0f); + prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", + "Cursor location in screen coordinates", -10.0f, 10.0f); + RNA_def_property_flag(prop, PROP_HIDDEN); } static int view_zoom_out_exec(bContext *C, wmOperator *op) @@ -684,6 +742,8 @@ static int view_zoom_out_invoke(bContext *C, wmOperator *op, const wmEvent *even void CLIP_OT_view_zoom_out(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom Out"; ot->idname = "CLIP_OT_view_zoom_out"; @@ -695,8 +755,9 @@ void CLIP_OT_view_zoom_out(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* properties */ - RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", - "Cursor location in normalized (0.0-1.0) coordinates", -10.0f, 10.0f); + prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", + "Cursor location in normalized (0.0-1.0) coordinates", -10.0f, 10.0f); + RNA_def_property_flag(prop, PROP_HIDDEN); } /********************** view zoom ratio operator *********************/ @@ -1062,10 +1123,7 @@ typedef struct ProxyQueue { typedef struct ProxyThread { MovieClip *clip; - ProxyQueue *queue; - struct MovieDistortion *distortion; - int *build_sizes, build_count; int *build_undistort_sizes, build_undistort_count; } ProxyThread; @@ -1085,7 +1143,7 @@ static unsigned char *proxy_thread_next_frame(ProxyQueue *queue, MovieClip *clip BKE_movieclip_filename_for_frame(clip, &user, name); - file = open(name, O_BINARY | O_RDONLY, 0); + file = BLI_open(name, O_BINARY | O_RDONLY, 0); if (file < 0) { BLI_spin_unlock(&queue->spin); return NULL; @@ -1121,14 +1179,15 @@ static unsigned char *proxy_thread_next_frame(ProxyQueue *queue, MovieClip *clip return mem; } -static void *do_proxy_thread(void *data_v) +static void proxy_task_func(TaskPool *pool, void *task_data, int UNUSED(threadid)) { - ProxyThread *data = (ProxyThread *) data_v; + ProxyThread *data = (ProxyThread *)task_data; + ProxyQueue *queue = (ProxyQueue *)BLI_task_pool_userdata(pool); unsigned char *mem; size_t size; int cfra; - while ((mem = proxy_thread_next_frame(data->queue, data->clip, &size, &cfra))) { + while ((mem = proxy_thread_next_frame(queue, data->clip, &size, &cfra))) { ImBuf *ibuf; ibuf = IMB_ibImageFromMemory(mem, size, IB_rect | IB_multilayer | IB_alphamode_detect, @@ -1144,8 +1203,6 @@ static void *do_proxy_thread(void *data_v) MEM_freeN(mem); } - - return NULL; } static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count, @@ -1155,12 +1212,18 @@ static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count, ProxyJob *pj = pjv; MovieClip *clip = pj->clip; Scene *scene = pj->scene; + TaskScheduler *task_scheduler = BLI_task_scheduler_get(); + TaskPool *task_pool; int sfra = SFRA, efra = EFRA; ProxyThread *handles; - ListBase threads; - int i, tot_thread = BLI_system_thread_count(); + int i, tot_thread = BLI_task_scheduler_num_threads(task_scheduler); + int width, height; ProxyQueue queue; + if (build_undistort_count) { + BKE_movieclip_get_size(clip, NULL, &width, &height); + } + BLI_spin_init(&queue.spin); queue.cfra = sfra; @@ -1170,16 +1233,13 @@ static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count, queue.do_update = do_update; queue.progress = progress; - handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, "proxy threaded handles"); - - if (tot_thread > 1) - BLI_init_threads(&threads, do_proxy_thread, tot_thread); - + task_pool = BLI_task_pool_create(task_scheduler, &queue); + handles = MEM_callocN(sizeof(ProxyThread) * tot_thread, + "proxy threaded handles"); for (i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; handle->clip = clip; - handle->queue = &queue; handle->build_count = build_count; handle->build_sizes = build_sizes; @@ -1188,29 +1248,29 @@ static void do_sequence_proxy(void *pjv, int *build_sizes, int build_count, handle->build_undistort_sizes = build_undistort_sizes; if (build_undistort_count) { - int width, height; - BKE_movieclip_get_size(clip, NULL, &width, &height); - handle->distortion = BKE_tracking_distortion_new(&clip->tracking, width, height); + handle->distortion = BKE_tracking_distortion_new(&clip->tracking, + width, height); } - if (tot_thread > 1) - BLI_insert_thread(&threads, handle); + BLI_task_pool_push(task_pool, + proxy_task_func, + handle, + false, + TASK_PRIORITY_LOW); } - if (tot_thread > 1) - BLI_end_threads(&threads); - else - do_proxy_thread(handles); - - MEM_freeN(handles); + BLI_task_pool_work_and_wait(task_pool); + BLI_task_pool_free(task_pool); if (build_undistort_count) { for (i = 0; i < tot_thread; i++) { ProxyThread *handle = &handles[i]; - BKE_tracking_distortion_free(handle->distortion); } } + + BLI_spin_end(&queue.spin); + MEM_freeN(handles); } static void proxy_startjob(void *pjv, short *stop, short *do_update, float *progress) diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index d3be25050c8..a797a60f74c 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -754,6 +754,10 @@ static void clip_keymap(struct wmKeyConfig *keyconf) RNA_string_set(kmi->ptr, "data_path", "space_data.pivot_point"); RNA_string_set(kmi->ptr, "value", "INDIVIDUAL_ORIGINS"); + /* Copy-paste */ + WM_keymap_add_item(keymap, "CLIP_OT_copy_tracks", CKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CLIP_OT_paste_tracks", VKEY, KM_PRESS, KM_CTRL, 0); + /* ******** Hotkeys avalaible for preview region only ******** */ keymap = WM_keymap_find(keyconf, "Clip Graph Editor", SPACE_CLIP, 0); @@ -846,7 +850,7 @@ static int clip_context(const bContext *C, const char *member, bContextDataResul static int clip_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) - if (ELEM4(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE, ICON_FILE_BLANK)) /* rule might not work? */ + if (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE, ICON_FILE_BLANK)) /* rule might not work? */ return true; return false; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index ce14471f608..fb6b1a0033c 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -2277,7 +2277,7 @@ static void set_axis(Scene *scene, Object *ob, MovieClip *clip, MovieTrackingOb copy_v3_v3(lmat[3], obmat[3]); invert_m4_m4(ilmat, lmat); - mul_serie_m4(mat, lmat, mat, ilmat, obmat, NULL, NULL, NULL, NULL); + mul_m4_series(mat, lmat, mat, ilmat, obmat); } else { mul_m4_m4m4(mat, obmat, mat); @@ -2996,7 +2996,7 @@ void CLIP_OT_detect_features(wmOperatorType *ot) /* properties */ RNA_def_enum(ot->srna, "placement", placement_items, 0, "Placement", "Placement for detected features"); RNA_def_int(ot->srna, "margin", 16, 0, INT_MAX, "Margin", "Only features further than margin pixels from the image edges are considered", 0, 300); - RNA_def_float(ot->srna, "threshold", 1.0f, 0.0001f, FLT_MAX, "Threshold", "Threshold level to consider feature good enough for tracking", 0.0001f, FLT_MAX); + RNA_def_float(ot->srna, "threshold", 0.5f, 0.0001f, FLT_MAX, "Threshold", "Threshold level to consider feature good enough for tracking", 0.0001f, FLT_MAX); RNA_def_int(ot->srna, "min_distance", 120, 0, INT_MAX, "Distance", "Minimal distance accepted between two features", 0, 300); } @@ -3811,7 +3811,9 @@ static int paste_tracks_exec(bContext *C, wmOperator *UNUSED(op)) MovieClip *clip = ED_space_clip_get_clip(sc); MovieTracking *tracking = &clip->tracking; MovieTrackingObject *object = BKE_tracking_object_get_active(tracking); + ListBase *tracks_base = BKE_tracking_object_get_tracks(tracking, object); + BKE_tracking_tracks_deselect_all(tracks_base); BKE_tracking_clipboard_paste_tracks(tracking, object); WM_event_add_notifier(C, NC_MOVIECLIP | NA_EDITED, clip); diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 4d9c262e1ff..860d9dc6b3c 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -792,7 +792,7 @@ void CLIP_OT_select_circle(wmOperatorType *ot) /* properties */ RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX); RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX); } diff --git a/source/blender/editors/space_console/CMakeLists.txt b/source/blender/editors/space_console/CMakeLists.txt index 241a48c1e2d..ecfb1f0e0df 100644 --- a/source/blender/editors/space_console/CMakeLists.txt +++ b/source/blender/editors/space_console/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -45,4 +47,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_console "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_console/SConscript b/source/blender/editors/space_console/SConscript index 0395a33cd97..87d12fe34bd 100644 --- a/source/blender/editors/space_console/SConscript +++ b/source/blender/editors/space_console/SConscript @@ -28,16 +28,19 @@ Import ('env') sources = env.Glob('*.c') -defs = [] + +defs = env['BF_GL_DEFINITIONS'] incs = [ '../include', - '#/extern/glew/include', '#/intern/guardedalloc', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../../makesdna', '../../makesrna', '../../blenkernel', '../../blenlib', + '../../gpu', '../../windowmanager', '../../blenfont', ] diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index b8743d69762..b44e942527c 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -888,7 +888,7 @@ static int console_copy_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceConsole *sc = CTX_wm_space_console(C); - DynStr *buf_dyn = BLI_dynstr_new(); + DynStr *buf_dyn; char *buf_str; ConsoleLine *cl; @@ -897,14 +897,6 @@ static int console_copy_exec(bContext *C, wmOperator *UNUSED(op)) ConsoleLine cl_dummy = {NULL}; -#if 0 - /* copy whole file */ - for (cl = sc->scrollback.first; cl; cl = cl->next) { - BLI_dynstr_append(buf_dyn, cl->line); - BLI_dynstr_append(buf_dyn, "\n"); - } -#endif - if (sc->sel_start == sc->sel_end) return OPERATOR_CANCELLED; @@ -919,6 +911,7 @@ static int console_copy_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + buf_dyn = BLI_dynstr_new(); offset -= 1; sel[0] = offset - sc->sel_end; sel[1] = offset - sc->sel_start; diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 43313c7dd06..e4a61a8f06e 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -158,6 +158,18 @@ static void console_main_area_init(wmWindowManager *wm, ARegion *ar) WM_event_add_dropbox_handler(&ar->handlers, lb); } +/* same as 'text_cursor' */ +static void console_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) +{ + SpaceText *st = sa->spacedata.first; + int wmcursor = BC_TEXTEDITCURSOR; + + if (st->text && BLI_rcti_isect_pt(&st->txtbar, win->eventstate->x - ar->winrct.xmin, st->txtbar.ymin)) { + wmcursor = CURSOR_STD; + } + + WM_cursor_set(win, wmcursor); +} /* ************* dropboxes ************* */ @@ -396,6 +408,7 @@ void ED_spacetype_console(void) art->init = console_main_area_init; art->draw = console_main_area_draw; + art->cursor = console_cursor; art->listener = console_main_area_listener; diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index 3b2db3ee7bc..fc007a659b4 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -25,11 +25,13 @@ set(INC ../../blenlib ../../blenloader ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -86,6 +88,8 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() diff --git a/source/blender/editors/space_file/SConscript b/source/blender/editors/space_file/SConscript index d42394454eb..85c3e073922 100644 --- a/source/blender/editors/space_file/SConscript +++ b/source/blender/editors/space_file/SConscript @@ -30,12 +30,14 @@ Import ('env') sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../blenloader', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -44,7 +46,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_OPENJPEG']: defs.append('WITH_OPENJPEG') diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index f0a84ef482e..3e099b43a4b 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -416,11 +416,12 @@ static void draw_background(FileLayout *layout, View2D *v2d) int i; int sy; + UI_ThemeColorShade(TH_BACK, -7); + /* alternating flat shade background */ for (i = 0; (i <= layout->rows); i += 2) { sy = (int)v2d->cur.ymax - i * (layout->tile_h + 2 * layout->tile_border_y) - layout->tile_border_y; - UI_ThemeColorShade(TH_BACK, -7); glRectf(v2d->cur.xmin, (float)sy, v2d->cur.xmax, (float)(sy + layout->tile_h + 2 * layout->tile_border_y)); } @@ -428,18 +429,36 @@ static void draw_background(FileLayout *layout, View2D *v2d) static void draw_dividers(FileLayout *layout, View2D *v2d) { + const int step = (layout->tile_w + 2 * layout->tile_border_x); + int v1[2], v2[2]; int sx; + unsigned char col_hi[3], col_lo[3]; + + UI_GetThemeColorShade3ubv(TH_BACK, 30, col_hi); + UI_GetThemeColorShade3ubv(TH_BACK, -30, col_lo); + + v1[1] = v2d->cur.ymax - layout->tile_border_y; + v2[1] = v2d->cur.ymin; + + glBegin(GL_LINES); /* vertical column dividers */ sx = (int)v2d->tot.xmin; while (sx < v2d->cur.xmax) { - sx += (layout->tile_w + 2 * layout->tile_border_x); - - UI_ThemeColorShade(TH_BACK, 30); - sdrawline(sx + 1, (short)(v2d->cur.ymax - layout->tile_border_y), sx + 1, (short)v2d->cur.ymin); - UI_ThemeColorShade(TH_BACK, -30); - sdrawline(sx, (short)(v2d->cur.ymax - layout->tile_border_y), sx, (short)v2d->cur.ymin); + sx += step; + + glColor3ubv(col_lo); + v1[0] = v2[0] = sx; + glVertex2iv(v1); + glVertex2iv(v2); + + glColor3ubv(col_hi); + v1[0] = v2[0] = sx + 1; + glVertex2iv(v1); + glVertex2iv(v2); } + + glEnd(); } void file_draw_list(const bContext *C, ARegion *ar) diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 1d3013bd8b4..27d6fabba4e 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1277,8 +1277,9 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN const char *lastdir = folderlist_peeklastdir(sfile->folders_prev); /* if not, ask to create it and enter if confirmed */ + wmOperatorType *ot = WM_operatortype_find("FILE_OT_directory_new", false); PointerRNA ptr; - WM_operator_properties_create(&ptr, "FILE_OT_directory_new"); + WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "directory", sfile->params->dir); RNA_boolean_set(&ptr, "open", true); @@ -1286,7 +1287,7 @@ void file_directory_enter_handle(bContext *C, void *UNUSED(arg_unused), void *UN BLI_strncpy(sfile->params->dir, lastdir, sizeof(sfile->params->dir)); - WM_operator_name_call(C, "FILE_OT_directory_new", WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); WM_operator_properties_free(&ptr); } diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 69789569912..8fad17e1210 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -57,12 +57,13 @@ static void file_panel_cb(bContext *C, void *arg_entry, void *UNUSED(arg_v)) { + wmOperatorType *ot = WM_operatortype_find("FILE_OT_select_bookmark", false); PointerRNA ptr; const char *entry = (char *)arg_entry; - WM_operator_properties_create(&ptr, "FILE_OT_select_bookmark"); + WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "dir", entry); - WM_operator_name_call(C, "FILE_OT_select_bookmark", WM_OP_INVOKE_REGION_WIN, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_REGION_WIN, &ptr); WM_operator_properties_free(&ptr); } diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index e8371d7666b..afe3f29e9bc 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -281,14 +281,28 @@ int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar) { int numfiles; + /* Values in pixels. + * + * - *_item: size of each (row|col), (including padding) + * - *_view: (x|y) size of the view. + * - *_over: extra pixels, to take into account, when the fit isnt exact + * (needed since you may see the end of the previous column and the beginning of the next). + * + * Could be more clever and take scrolling into account, + * but for now don't bother. + */ if (layout->flag & FILE_LAYOUT_HOR) { - int width = (int)(BLI_rctf_size_x(&ar->v2d.cur) - 2 * layout->tile_border_x); - numfiles = (int)((float)width / (float)layout->tile_w + 0.5f); + const int x_item = layout->tile_w + (2 * layout->tile_border_x); + const int x_view = (int)(BLI_rctf_size_x(&ar->v2d.cur)); + const int x_over = x_item - (x_view % x_item); + numfiles = (int)((float)(x_view + x_over) / (float)(x_item)); return numfiles * layout->rows; } else { - int height = (int)(BLI_rctf_size_y(&ar->v2d.cur) - 2 * layout->tile_border_y); - numfiles = (int)((float)height / (float)layout->tile_h + 0.5f); + const int y_item = layout->tile_h + (2 * layout->tile_border_y); + const int y_view = (int)(BLI_rctf_size_y(&ar->v2d.cur)); + const int y_over = y_item - (y_view % y_item); + numfiles = (int)((float)(y_view + y_over) / (float)(y_item)); return numfiles * layout->columns; } } @@ -656,7 +670,7 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) } else { char path[FILE_MAX]; - struct stat status; + BLI_stat_t status; BLI_join_dirfile(path, sizeof(path), dirname, de->d_name); diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 6284caf1456..c117b8c2d3e 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -441,9 +441,14 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle); - if (!CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingASCII)) + if (pathString == NULL || !CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingASCII)) continue; - fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, FS_INSERT_SORTED); + + /* Exclude "all my files" as it makes no sense in blender fileselector */ + /* Exclude "airdrop" if wlan not active as it would show "" ) */ + if (!strstr(line, "myDocuments.cannedSearch") && (*line != '\0')) { + fsmenu_insert_entry(fsmenu, FS_CATEGORY_SYSTEM_BOOKMARKS, line, NULL); + } CFRelease(pathString); CFRelease(cfURL); diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 3a493c0338c..d5be04cff20 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -422,7 +422,7 @@ static void file_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, 0, 0); kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "fill", true); diff --git a/source/blender/editors/space_graph/CMakeLists.txt b/source/blender/editors/space_graph/CMakeLists.txt index d3fb87204fb..0a29810ff3d 100644 --- a/source/blender/editors/space_graph/CMakeLists.txt +++ b/source/blender/editors/space_graph/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -56,4 +58,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_graph "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_graph/SConscript b/source/blender/editors/space_graph/SConscript index 8ddeb0ccfe8..35e09749743 100644 --- a/source/blender/editors/space_graph/SConscript +++ b/source/blender/editors/space_graph/SConscript @@ -31,19 +31,21 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/audaspace/intern', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index 8193008098e..b59030d3c12 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -47,6 +47,7 @@ #include "BLF_translation.h" #include "BKE_context.h" +#include "BKE_curve.h" #include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_main.h" @@ -226,15 +227,13 @@ static short get_active_fcurve_keyframe_edit(FCurve *fcu, BezTriple **bezt, BezT } /* update callback for active keyframe properties - base updates stuff */ -static void graphedit_activekey_update_cb(bContext *C, void *fcu_ptr, void *UNUSED(bezt_ptr)) +static void graphedit_activekey_update_cb(bContext *UNUSED(C), void *fcu_ptr, void *UNUSED(bezt_ptr)) { - SpaceIpo *sipo = CTX_wm_space_graph(C); - const short use_handle = !(sipo->flag & SIPO_NOHANDLES); FCurve *fcu = (FCurve *)fcu_ptr; /* make sure F-Curve and its handles are still valid after this editing */ sort_time_fcurve(fcu); - testhandles_fcurve(fcu, use_handle); + calchandles_fcurve(fcu); } /* update callback for active keyframe properties - handle-editing wrapper */ @@ -250,6 +249,9 @@ static void graphedit_activekey_handles_cb(bContext *C, void *fcu_ptr, void *bez bezt->h1 = HD_ALIGN; bezt->h2 = HD_ALIGN; } + else { + BKE_nurb_bezt_handle_test(bezt, true); + } /* now call standard updates */ graphedit_activekey_update_cb(C, fcu_ptr, bezt_ptr); @@ -259,6 +261,24 @@ static void graphedit_activekey_handles_cb(bContext *C, void *fcu_ptr, void *bez * NOTE: we cannot just do graphedit_activekey_handles_cb() due to "order of computation" * weirdness (see calchandleNurb_intern() and T39911) */ +static void graphedit_activekey_left_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr) +{ + BezTriple *bezt = (BezTriple *)bezt_ptr; + + const char f1 = bezt->f1; + const char f3 = bezt->f3; + + bezt->f1 |= SELECT; + bezt->f3 &= ~SELECT; + + /* perform normal updates NOW */ + graphedit_activekey_handles_cb(C, fcu_ptr, bezt_ptr); + + /* restore selection state so that no-one notices this hack */ + bezt->f1 = f1; + bezt->f3 = f3; +} + static void graphedit_activekey_right_handle_coord_cb(bContext *C, void *fcu_ptr, void *bezt_ptr) { BezTriple *bezt = (BezTriple *)bezt_ptr; @@ -270,8 +290,8 @@ static void graphedit_activekey_right_handle_coord_cb(bContext *C, void *fcu_ptr /* temporarily make it so that only the right handle is selected, so that updates go correctly * (i.e. it now acts as if we've just transforming the vert when it is selected by itself) */ - bezt->f1 = 0; - bezt->f3 = 1; + bezt->f1 &= ~SELECT; + bezt->f3 |= SELECT; /* perform normal updates NOW */ graphedit_activekey_handles_cb(C, fcu_ptr, bezt_ptr); @@ -362,11 +382,11 @@ static void graph_panel_key_properties(const bContext *C, Panel *pa) but = uiDefButR(block, NUM, B_REDR, "X:", 0, 0, UI_UNIT_X, UI_UNIT_Y, &bezt_ptr, "handle_left", 0, 0, 0, -1, -1, NULL); - uiButSetFunc(but, graphedit_activekey_handles_cb, fcu, bezt); + uiButSetFunc(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); but = uiDefButR(block, NUM, B_REDR, "Y:", 0, 0, UI_UNIT_X, UI_UNIT_Y, &bezt_ptr, "handle_left", 1, 0, 0, -1, -1, NULL); - uiButSetFunc(but, graphedit_activekey_handles_cb, fcu, bezt); + uiButSetFunc(but, graphedit_activekey_left_handle_coord_cb, fcu, bezt); uiButSetUnitType(but, unit); /* XXX: with label? */ @@ -540,7 +560,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Bone 1:")); - if (dtar->id && ob1->pose) { + if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); @@ -551,7 +571,7 @@ static void graph_panel_driverVar__rotDiff(uiLayout *layout, ID *id, DriverVar * uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Bone 2:")); - if (dtar2->id && ob2->pose) { + if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); @@ -578,7 +598,7 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone 1:")); - if (dtar->id && ob1->pose) { + if (dtar->id && GS(dtar->id->name) == ID_OB && ob1->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob1->pose, &tar_ptr); @@ -592,7 +612,7 @@ static void graph_panel_driverVar__locDiff(uiLayout *layout, ID *id, DriverVar * uiLayoutSetRedAlert(col, (dtar2->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar2_ptr, "id", "id_type", IFACE_("Ob/Bone 2:")); - if (dtar2->id && ob2->pose) { + if (dtar2->id && GS(dtar2->id->name) == ID_OB && ob2->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar2->id, &RNA_Pose, ob2->pose, &tar_ptr); @@ -619,7 +639,7 @@ static void graph_panel_driverVar__transChan(uiLayout *layout, ID *id, DriverVar uiLayoutSetRedAlert(col, (dtar->flag & DTAR_FLAG_INVALID)); /* XXX: per field... */ uiTemplateAnyID(col, &dtar_ptr, "id", "id_type", IFACE_("Ob/Bone:")); - if (dtar->id && ob->pose) { + if (dtar->id && GS(dtar->id->name) == ID_OB && ob->pose) { PointerRNA tar_ptr; RNA_pointer_create(dtar->id, &RNA_Pose, ob->pose, &tar_ptr); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index 83f87ebc174..ed7cfe7da99 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -476,11 +476,12 @@ static void draw_fcurve_samples(SpaceIpo *sipo, ARegion *ar, FCurve *fcu) static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, View2DGrid *grid) { ChannelDriver *driver; - float samplefreq, ctime; + float samplefreq; float stime, etime; float unitFac; float dx, dy; short mapping_flag = ANIM_get_normalization_flags(ac); + int i, n; /* when opening a blend file on a different sized screen or while dragging the toolbar this can happen * best just bail out in this case */ @@ -524,10 +525,12 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d * the displayed values appear correctly in the viewport */ glBegin(GL_LINE_STRIP); - - for (ctime = stime; ctime <= etime; ctime += samplefreq) + + for (i = 0, n = (etime - stime) / samplefreq + 0.5f; i < n; ++i) { + float ctime = stime + i * samplefreq; glVertex2f(ctime, evaluate_fcurve(fcu, ctime) * unitFac); - + } + glEnd(); /* restore driver */ @@ -1054,7 +1057,7 @@ void graph_draw_curves(bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGrid } /* free list of curves */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ************************************************************************* */ @@ -1139,5 +1142,5 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) } /* free tempolary channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 364dd1d0344..c8298927f7d 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -151,7 +151,7 @@ void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, floa } /* free memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } else { /* set default range */ @@ -369,7 +369,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) } /* admin and redraws */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -505,9 +505,12 @@ static void insert_graph_keys(bAnimContext *ac, short mode) insert_keyframe(reports, ale->id, NULL, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, flag); else insert_vert_fcurve(fcu, cfra, fcu->curval, 0); + + ale->update |= ANIM_UPDATE_DEFAULT; } - BLI_freelistN(&anim_data); + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -527,9 +530,6 @@ static int graphkeys_insertkey_exec(bContext *C, wmOperator *op) /* insert keyframes */ insert_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); @@ -581,6 +581,8 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op) * keyframes if these will be visible after doing so... */ if (fcurve_is_keyframable(fcu)) { + ListBase anim_data; + short mapping_flag = ANIM_get_normalization_flags(&ac); /* get frame and value from props */ @@ -596,6 +598,13 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op) /* insert keyframe on the specified frame + value */ insert_vert_fcurve(fcu, frame, val, 0); + + ale->update |= ANIM_UPDATE_DEPS; + + BLI_listbase_clear(&anim_data); + BLI_addtail(&anim_data, ale); + + ANIM_animdata_update(&ac, &anim_data); } else { /* warn about why this can't happen */ @@ -684,7 +693,7 @@ static short copy_graph_keys(bAnimContext *ac) ok = copy_animedit_keys(ac, &anim_data); /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ok; } @@ -708,9 +717,9 @@ static short paste_graph_keys(bAnimContext *ac, /* paste keyframes */ ok = paste_animedit_keys(ac, &anim_data, offset_mode, merge_mode); - + /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ok; } @@ -771,9 +780,6 @@ static int graphkeys_paste_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -815,10 +821,12 @@ static void duplicate_graph_keys(bAnimContext *ac) /* loop through filtered data and delete selected keys */ for (ale = anim_data.first; ale; ale = ale->next) { duplicate_fcurve_keys((FCurve *)ale->key_data); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - /* free filtered list */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -834,9 +842,6 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) /* duplicate keyframes */ duplicate_graph_keys(&ac); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); @@ -876,7 +881,7 @@ static bool delete_graph_keys(bAnimContext *ac) ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - bool changed = false; + bool changed_final = false; /* filter data */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_NODUPLIS); @@ -886,9 +891,15 @@ static bool delete_graph_keys(bAnimContext *ac) for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->key_data; AnimData *adt = ale->adt; + bool changed; /* delete selected keyframes only */ - changed |= delete_fcurve_keys(fcu); + changed = delete_fcurve_keys(fcu); + + if (changed) { + ale->update |= ANIM_UPDATE_DEFAULT; + changed_final = true; + } /* Only delete curve too if it won't be doing anything anymore */ if ((fcu->totvert == 0) && @@ -896,13 +907,14 @@ static bool delete_graph_keys(bAnimContext *ac) (fcu->driver == NULL)) { ANIM_fcurve_delete_from_animdata(ac, adt, fcu); + ale->key_data = NULL; } } - - /* free filtered list */ - BLI_freelistN(&anim_data); - return changed; + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); + + return changed_final; } /* ------------------- */ @@ -919,9 +931,6 @@ static int graphkeys_delete_exec(bContext *C, wmOperator *UNUSED(op)) if (!delete_graph_keys(&ac)) return OPERATOR_CANCELLED; - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); @@ -957,11 +966,14 @@ static void clean_graph_keys(bAnimContext *ac, float thresh) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and clean curves */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { clean_fcurve((FCurve *)ale->key_data, thresh); - - /* free temp data */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -981,9 +993,6 @@ static int graphkeys_clean_exec(bContext *C, wmOperator *op) /* clean keyframes */ clean_graph_keys(&ac, thresh); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1036,10 +1045,12 @@ static void bake_graph_curves(bAnimContext *ac, int start, int end) /* restore driver */ fcu->driver = driver; + + ale->update |= ANIM_UPDATE_DEPS; } - - /* admin and redraws */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1063,9 +1074,6 @@ static int graphkeys_bake_exec(bContext *C, wmOperator *UNUSED(op)) /* bake keyframes */ bake_graph_curves(&ac, start, end); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ // NOTE: some distinction between order/number of keyframes and type should be made? WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1178,16 +1186,16 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) /* sample the sound */ fcurve_store_samples(fcu, &sbi, start, end, fcurve_samplingcb_sound); + + ale->update |= ANIM_UPDATE_DEFAULT; } /* free sample data */ free(sbi.samples); - /* admin and redraws */ - BLI_freelistN(&anim_data); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); + ANIM_animdata_update(&ac, &anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that 'keyframes' have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1275,11 +1283,14 @@ static void sample_graph_keys(bAnimContext *ac) ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); /* loop through filtered data and add keys between selected keyframes on every frame */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { sample_fcurve((FCurve *)ale->key_data); - - /* admin and redraws */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEPS; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1295,9 +1306,6 @@ static int graphkeys_sample_exec(bContext *C, wmOperator *UNUSED(op)) /* sample keyframes */ sample_graph_keys(&ac); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1357,6 +1365,8 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode) if (mode >= 0) { /* just set mode setting */ fcu->extend = mode; + + ale->update |= ANIM_UPDATE_HANDLES; } else { /* shortcuts for managing Cycles F-Modifiers to make it easier to toggle cyclic animation @@ -1381,10 +1391,12 @@ static void setexpo_graph_keys(bAnimContext *ac, short mode) } } } + + ale->update |= ANIM_UPDATE_DEPS; } - - /* cleanup */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1404,9 +1416,6 @@ static int graphkeys_expo_exec(bContext *C, wmOperator *op) /* set handle type */ setexpo_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1449,11 +1458,14 @@ static void setipo_graph_keys(bAnimContext *ac, short mode) /* loop through setting BezTriple interpolation * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here... */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); - - /* cleanup */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1473,9 +1485,6 @@ static int graphkeys_ipo_exec(bContext *C, wmOperator *op) /* set handle type */ setipo_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1517,11 +1526,14 @@ static void seteasing_graph_keys(bAnimContext *ac, short mode) /* loop through setting BezTriple easing * Note: we do not supply KeyframeEditData to the looper yet. Currently that's not necessary here... */ - for (ale = anim_data.first; ale; ale = ale->next) + for (ale = anim_data.first; ale; ale = ale->next) { ANIM_fcurve_keyframes_loop(NULL, ale->key_data, NULL, set_cb, calchandles_fcurve); - - /* cleanup */ - BLI_freelistN(&anim_data); + + ale->update |= ANIM_UPDATE_DEFAULT_NOHANDLES; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } static int graphkeys_easing_exec(bContext *C, wmOperator *op) @@ -1539,9 +1551,6 @@ static int graphkeys_easing_exec(bContext *C, wmOperator *op) /* set handle type */ seteasing_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1593,11 +1602,13 @@ static void sethandles_graph_keys(bAnimContext *ac, short mode) if (ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, sel_cb, NULL)) { /* change type of selected handles */ ANIM_fcurve_keyframes_loop(NULL, fcu, NULL, edit_cb, calchandles_fcurve); + + ale->update |= ANIM_UPDATE_DEFAULT; } } - - /* cleanup */ - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -1616,9 +1627,6 @@ static int graphkeys_handletype_exec(bContext *C, wmOperator *op) /* set handle type */ sethandles_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframe properties have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME_PROP, NULL); @@ -1699,7 +1707,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) */ if (strstr(fcu->rna_path, "rotation_euler") == NULL) continue; - else if (ELEM3(fcu->array_index, 0, 1, 2) == 0) { + else if (ELEM(fcu->array_index, 0, 1, 2) == 0) { BKE_reportf(op->reports, RPT_WARNING, "Euler Rotation F-Curve has invalid index (ID='%s', Path='%s', Index=%d)", (ale->id) ? ale->id->name : TIP_("<No ID>"), fcu->rna_path, fcu->array_index); @@ -1724,10 +1732,12 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) euf->rna_path = fcu->rna_path; /* this should be safe, since we're only using it for a short time */ euf->fcurves[fcu->array_index] = fcu; } + + ale->update |= ANIM_UPDATE_DEFAULT; } - BLI_freelistN(&anim_data); - + if (groups == 0) { + ANIM_animdata_freelist(&anim_data); BKE_report(op->reports, RPT_WARNING, "No Euler Rotation F-Curves to fix up"); return OPERATOR_CANCELLED; } @@ -1740,7 +1750,7 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) /* sanity check: ensure that there are enough F-Curves to work on in this group */ /* TODO: also enforce assumption that there be a full set of keyframes at each position by ensuring that totvert counts are same? */ - if (ELEM3(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) { + if (ELEM(NULL, euf->fcurves[0], euf->fcurves[1], euf->fcurves[2])) { /* report which components are missing */ BKE_reportf(op->reports, RPT_WARNING, "Missing %s%s%s component(s) of euler rotation for ID='%s' and RNA-Path='%s'", @@ -1786,6 +1796,9 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) } BLI_freelistN(&eulers); + ANIM_animdata_update(&ac, &anim_data); + ANIM_animdata_freelist(&anim_data); + /* updates + finishing warnings */ if (failed == groups) { BKE_report(op->reports, RPT_ERROR, @@ -1801,9 +1814,6 @@ static int graphkeys_euler_filter_exec(bContext *C, wmOperator *op) "consecutive XYZ order and selected"); } - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -1882,7 +1892,7 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) ked.i2 += current_ked.i2; } - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set the new current frame and cursor values, based on the average time and value */ if (ked.i1) { @@ -1984,9 +1994,12 @@ static void snap_graph_keys(bAnimContext *ac, short mode) } else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -2006,9 +2019,6 @@ static int graphkeys_snap_exec(bContext *C, wmOperator *op) /* snap keyframes */ snap_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -2111,9 +2121,12 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) } else ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, edit_cb, calchandles_fcurve); + + ale->update |= ANIM_UPDATE_DEFAULT; } - - BLI_freelistN(&anim_data); + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -2133,9 +2146,6 @@ static int graphkeys_mirror_exec(bContext *C, wmOperator *op) /* mirror keyframes */ mirror_graph_keys(&ac, mode); - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -2185,11 +2195,12 @@ static int graphkeys_smooth_exec(bContext *C, wmOperator *UNUSED(op)) * Snap->Flatten Handles anyway. */ smooth_fcurve(ale->key_data); + + ale->update |= ANIM_UPDATE_DEFAULT; } - BLI_freelistN(&anim_data); - - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); + + ANIM_animdata_update(&ac, &anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); @@ -2217,39 +2228,33 @@ void GRAPH_OT_smooth(wmOperatorType *ot) /* ******************** Add F-Modifier Operator *********************** */ -/* present a special customised popup menu for this, with some filtering */ -static int graph_fmodifier_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static EnumPropertyItem *graph_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { - wmOperatorType *ot = WM_operatortype_find("GRAPH_OT_fmodifier_add", 1); - uiPopupMenu *pup; - uiLayout *layout; - int i; - - pup = uiPupMenuBegin(C, IFACE_("Add F-Curve Modifier"), ICON_NONE); - layout = uiPupMenuLayout(pup); - + EnumPropertyItem *item = NULL; + int totitem = 0; + int i = 0; + + if (C == NULL) { + return fmodifier_type_items; + } + /* start from 1 to skip the 'Invalid' modifier type */ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); - PointerRNA props_ptr; - + int index; + /* check if modifier is valid for this context */ if (fmi == NULL) continue; - - /* create operator menu item with relevant properties filled in */ - props_ptr = uiItemFullO_ptr(layout, ot, IFACE_(fmi->name), ICON_NONE, - NULL, WM_OP_EXEC_REGION_WIN, UI_ITEM_O_RETURN_PROPS); - /* the only thing that gets set from the menu is the type of F-Modifier to add */ - RNA_enum_set(&props_ptr, "type", i); - /* the following properties are just repeats of existing ones... */ - RNA_boolean_set(&props_ptr, "only_active", RNA_boolean_get(op->ptr, "only_active")); + + index = RNA_enum_from_value(fmodifier_type_items, fmi->type); + RNA_enum_item_add(&item, &totitem, &fmodifier_type_items[index]); } - uiItemS(layout); - - uiPupMenuEnd(C, pup); - - return OPERATOR_CANCELLED; + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; } static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) @@ -2289,11 +2294,12 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Modifier could not be added (see console for details)"); break; } + + ale->update |= ANIM_UPDATE_DEPS; } - BLI_freelistN(&anim_data); - - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); + + ANIM_animdata_update(&ac, &anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ // FIXME: this really isn't the best description for it... @@ -2304,13 +2310,15 @@ static int graph_fmodifier_add_exec(bContext *C, wmOperator *op) void GRAPH_OT_fmodifier_add(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Add F-Curve Modifier"; ot->idname = "GRAPH_OT_fmodifier_add"; ot->description = "Add F-Modifiers to the selected F-Curves"; /* api callbacks */ - ot->invoke = graph_fmodifier_add_invoke; + ot->invoke = WM_menu_invoke; ot->exec = graph_fmodifier_add_exec; ot->poll = graphop_selected_fcurve_poll; @@ -2318,7 +2326,10 @@ void GRAPH_OT_fmodifier_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* id-props */ - ot->prop = RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", ""); + prop = RNA_def_enum(ot->srna, "type", fmodifier_type_items, 0, "Type", ""); + RNA_def_enum_funcs(prop, graph_fmodifier_itemf); + ot->prop = prop; + RNA_def_boolean(ot->srna, "only_active", 1, "Only Active", "Only add F-Modifier to active F-Curve"); } @@ -2398,19 +2409,26 @@ static int graph_fmodifier_paste_exec(bContext *C, wmOperator *op) /* paste modifiers */ for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->data; + int tot; /* TODO: do we want to replace existing modifiers? add user pref for that! */ - ok += ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0); + tot = ANIM_fmodifiers_paste_from_buf(&fcu->modifiers, 0); + + if (tot) { + ale->update |= ANIM_UPDATE_DEPS; + } + + ok += tot; } - - /* clean up */ - BLI_freelistN(&anim_data); + + if (ok) { + ANIM_animdata_update(&ac, &anim_data); + } + ANIM_animdata_freelist(&anim_data); /* successful or not? */ if (ok) { - /* validate keyframes after editing */ - ANIM_editkeyframes_refresh(&ac); - + /* set notifier that keyframes have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index cfd82b67289..62b6b59df29 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -167,6 +167,7 @@ static int graphview_cursor_modal(bContext *C, wmOperator *op, const wmEvent *ev case LEFTMOUSE: case RIGHTMOUSE: + case MIDDLEMOUSE: /* we check for either mouse-button to end, as checking for ACTIONMOUSE (which is used to init * the modal op) doesn't work for some reason */ @@ -441,8 +442,12 @@ void graphedit_keymap(wmKeyConfig *keyconf) /* keymap for all regions */ keymap = WM_keymap_find(keyconf, "Graph Editor Generic", SPACE_IPO, 0); WM_keymap_add_item(keymap, "GRAPH_OT_properties", NKEY, KM_PRESS, 0, 0); + /* extrapolation works on channels, not keys */ WM_keymap_add_item(keymap, "GRAPH_OT_extrapolation_type", EKEY, KM_PRESS, KM_SHIFT, 0); + + /* find (i.e. a shortcut for setting the name filter) */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0); /* channels */ /* Channels are not directly handled by the Graph Editor module, but are inherited from the Animation module. diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 82eddfbe4a9..378139accbc 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -141,7 +141,7 @@ static void deselect_graph_keys(bAnimContext *ac, short test, short sel, short d } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -311,7 +311,7 @@ static void borderselect_graphkeys( } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -548,7 +548,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } @@ -576,7 +576,7 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode) for (ale = anim_data.first; ale; ale = ale->next) ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, bezt_to_cfraelem, NULL); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); break; case GRAPHKEYS_COLUMNSEL_CFRA: /* current frame */ @@ -622,7 +622,7 @@ static void columnselect_graph_keys(bAnimContext *ac, short mode) /* free elements */ BLI_freelistN(&ked.list); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -700,7 +700,7 @@ static int graphkeys_select_linked_exec(bContext *C, wmOperator *UNUSED(op)) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that keyframe selection has changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); @@ -764,7 +764,7 @@ static void select_moreless_graph_keys(bAnimContext *ac, short mode) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ----------------- */ @@ -899,7 +899,7 @@ static void graphkeys_select_leftright(bAnimContext *ac, short leftright, short } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ----------------- */ @@ -1147,7 +1147,7 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L } /* free channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* helper for find_nearest_fcurve_vert() - get the best match to use */ @@ -1400,7 +1400,7 @@ static void graphkeys_mselect_column(bAnimContext *ac, const int mval[2], short /* free elements */ MEM_freeN(nvi); BLI_freelistN(&ked.list); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c index 07a582f7556..e9c8ae95acd 100644 --- a/source/blender/editors/space_graph/graph_utils.c +++ b/source/blender/editors/space_graph/graph_utils.c @@ -78,7 +78,7 @@ bAnimListElem *get_active_fcurve_channel(bAnimContext *ac) /* remove first item from list, then free the rest of the list and return the stored one */ BLI_remlink(&anim_data, ale); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return ale; } @@ -135,7 +135,7 @@ int graphop_visible_keyframes_poll(bContext *C) } /* cleanup and return findings */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return found; } @@ -185,7 +185,7 @@ int graphop_editable_keyframes_poll(bContext *C) } /* cleanup and return findings */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return found; } @@ -251,7 +251,7 @@ int graphop_selected_fcurve_poll(bContext *C) return 0; /* cleanup and return findings */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return 1; } diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index bfc1bcbcba0..6dba706b241 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -607,7 +607,7 @@ static void graph_refresh(const bContext *C, ScrArea *sa) } /* free temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } } diff --git a/source/blender/editors/space_image/CMakeLists.txt b/source/blender/editors/space_image/CMakeLists.txt index 62ac3c2d985..30bfc2fbdfd 100644 --- a/source/blender/editors/space_image/CMakeLists.txt +++ b/source/blender/editors/space_image/CMakeLists.txt @@ -23,14 +23,15 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib - ../../imbuf ../../bmesh + ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc - ../../gpu + ../../../../intern/glew-mx ) set(INC_SYS @@ -71,4 +72,6 @@ if(WITH_IMAGE_CINEON) add_definitions(-DWITH_CINEON) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_image "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_image/SConscript b/source/blender/editors/space_image/SConscript index d878a726b55..e983db431b4 100644 --- a/source/blender/editors/space_image/SConscript +++ b/source/blender/editors/space_image/SConscript @@ -31,12 +31,14 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../bmesh', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -46,7 +48,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index c2b9d92d00a..154437ab53a 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -284,10 +284,10 @@ static void image_panel_preview(ScrArea *sa, short cntrl) // IMAGE_HANDLER_PRE /* ********************* callbacks for standard image buttons *************** */ -static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void *render_slot_p) +static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void *image_p) { uiBlock *block = uiLayoutGetBlock(layout); - short *render_slot = render_slot_p; + Image *image = image_p; int slot; uiDefBut(block, LABEL, 0, IFACE_("Slot"), @@ -296,10 +296,15 @@ static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void * slot = IMA_MAX_RENDER_SLOT; while (slot--) { - char str[32]; - BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), slot + 1); + char str[64]; + if (image->render_slots[slot].name[0] != '\0') { + BLI_strncpy(str, image->render_slots[slot].name, sizeof(str)); + } + else { + BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), slot + 1); + } uiDefButS(block, BUTM, B_NOP, str, 0, 0, - UI_UNIT_X * 5, UI_UNIT_X, render_slot, (float) slot, 0.0, 0, -1, ""); + UI_UNIT_X * 5, UI_UNIT_X, &image->render_slot, (float) slot, 0.0, 0, -1, ""); } } @@ -320,13 +325,21 @@ static void ui_imageuser_layer_menu(bContext *UNUSED(C), uiLayout *layout, void { void **rnd_data = rnd_pt; uiBlock *block = uiLayoutGetBlock(layout); - RenderResult *rr = rnd_data[0]; + Image *image = rnd_data[0]; ImageUser *iuser = rnd_data[1]; + Scene *scene = iuser->scene; + RenderResult *rr; RenderLayer *rl; RenderLayer rl_fake = {NULL}; const char *fake_name; int nr; + /* may have been freed since drawing */ + rr = BKE_image_acquire_renderresult(scene, image); + if (UNLIKELY(rr == NULL)) { + return; + } + uiBlockSetCurLayout(block, layout); uiLayoutColumn(layout, false); @@ -355,6 +368,8 @@ final: } BLI_assert(nr == -1); + + BKE_image_release_renderresult(scene, image); } static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) @@ -369,17 +384,28 @@ static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *ptrpair_p) { - void **ptrpair = ptrpair_p; + void **rnd_data = ptrpair_p; uiBlock *block = uiLayoutGetBlock(layout); - // RenderResult *rr = ptrpair[0]; - ImageUser *iuser = ptrpair[1]; - /* rl==NULL means composite result */ - RenderLayer *rl = ptrpair[2]; + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + /* (rpass_index == -1) means composite result */ + const int rpass_index = GET_INT_FROM_POINTER(rnd_data[2]); + Scene *scene = iuser->scene; + RenderResult *rr; + RenderLayer *rl; RenderPass rpass_fake = {NULL}; RenderPass *rpass; const char *fake_name; int nr; + /* may have been freed since drawing */ + rr = BKE_image_acquire_renderresult(scene, image); + if (UNLIKELY(rr == NULL)) { + return; + } + + rl = BLI_findlink(&rr->layers, rpass_index); + uiBlockSetCurLayout(block, layout); uiLayoutColumn(layout, false); @@ -410,6 +436,8 @@ final: } BLI_assert(nr == -1); + + BKE_image_release_renderresult(scene, image); } /* 5 layer button callbacks... */ @@ -492,7 +520,7 @@ static void image_user_change(bContext *C, void *iuser_v, void *unused) } #endif -static void uiblock_layer_pass_buttons(uiLayout *layout, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) +static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) { static void *rnd_pt[3]; /* XXX, workaround */ uiBlock *block = uiLayoutGetBlock(layout); @@ -509,26 +537,33 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, RenderResult *rr, Image wmenu2 = (3 * w) / 5; wmenu3 = (3 * w) / 6; - rnd_pt[0] = rr; + rnd_pt[0] = image; rnd_pt[1] = iuser; rnd_pt[2] = NULL; /* menu buts */ if (render_slot) { char str[64]; - BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), *render_slot + 1); - but = uiDefMenuBut(block, ui_imageuser_slot_menu, render_slot, str, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select Slot")); + if (image->render_slots[*render_slot].name[0] != '\0') { + BLI_strncpy(str, image->render_slots[*render_slot].name, sizeof(str)); + } + else { + BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), *render_slot + 1); + } + but = uiDefMenuBut(block, ui_imageuser_slot_menu, image, str, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select Slot")); uiButSetFunc(but, image_multi_cb, rr, iuser); uiButSetMenuFromPulldown(but); } if (rr) { RenderPass *rpass; + int rpass_index; /* layer */ fake_name = ui_imageuser_layer_fake_name(rr); - rl = BLI_findlink(&rr->layers, iuser->layer - (fake_name ? 1 : 0)); - rnd_pt[2] = rl; + rpass_index = iuser->layer - (fake_name ? 1 : 0); + rl = BLI_findlink(&rr->layers, rpass_index); + rnd_pt[2] = SET_INT_IN_POINTER(rpass_index); 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")); @@ -547,7 +582,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, RenderResult *rr, Image } } -static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, RenderResult *rr, ImageUser *iuser, short *render_slot) +static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, short *render_slot) { uiBlock *block = uiLayoutGetBlock(layout); uiLayout *row; @@ -569,7 +604,7 @@ static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, RenderResult *rr, but = uiDefIconBut(block, BUT, 0, ICON_TRIA_RIGHT, 0, 0, 0.90f * UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, TIP_("Next Layer")); uiButSetFunc(but, image_multi_inclay_cb, rr, iuser); - uiblock_layer_pass_buttons(row, rr, iuser, 230 * dpi_fac, render_slot); + uiblock_layer_pass_buttons(row, image, rr, iuser, 230 * dpi_fac, render_slot); /* decrease, increase arrows */ but = uiDefIconBut(block, BUT, 0, ICON_TRIA_LEFT, 0, 0, 0.85f * UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, TIP_("Previous Pass")); @@ -691,7 +726,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char /* use BKE_image_acquire_renderresult so we get the correct slot in the menu */ rr = BKE_image_acquire_renderresult(scene, ima); - uiblock_layer_pass_arrow_buttons(layout, rr, iuser, &ima->render_slot); + uiblock_layer_pass_arrow_buttons(layout, ima, rr, iuser, &ima->render_slot); BKE_image_release_renderresult(scene, ima); } } @@ -724,7 +759,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char /* multilayer? */ if (ima->type == IMA_TYPE_MULTILAYER && ima->rr) { - uiblock_layer_pass_arrow_buttons(layout, ima->rr, iuser, NULL); + uiblock_layer_pass_arrow_buttons(layout, ima, ima->rr, iuser, NULL); } else if (ima->source != IMA_SRC_GENERATED) { if (compact == 0) { @@ -809,6 +844,10 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemR(col, &imaptr, "use_generated_float", 0, NULL, ICON_NONE); uiItemR(split, &imaptr, "generated_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + if (ima->gen_type == IMA_GENTYPE_BLANK) { + uiItemR(layout, &imaptr, "generated_color", 0, NULL, ICON_NONE); + } } } @@ -843,14 +882,14 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man uiItemR(sub, imfptr, "color_mode", UI_ITEM_R_EXPAND, IFACE_("Color"), ICON_NONE); /* only display depth setting if multiple depths can be used */ - if ((ELEM7(depth_ok, - R_IMF_CHAN_DEPTH_1, - R_IMF_CHAN_DEPTH_8, - R_IMF_CHAN_DEPTH_10, - R_IMF_CHAN_DEPTH_12, - R_IMF_CHAN_DEPTH_16, - R_IMF_CHAN_DEPTH_24, - R_IMF_CHAN_DEPTH_32)) == 0) + if ((ELEM(depth_ok, + R_IMF_CHAN_DEPTH_1, + R_IMF_CHAN_DEPTH_8, + R_IMF_CHAN_DEPTH_10, + R_IMF_CHAN_DEPTH_12, + R_IMF_CHAN_DEPTH_16, + R_IMF_CHAN_DEPTH_24, + R_IMF_CHAN_DEPTH_32)) == 0) { row = uiLayoutRow(col, false); @@ -933,7 +972,7 @@ void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser /* use BKE_image_acquire_renderresult so we get the correct slot in the menu */ rr = BKE_image_acquire_renderresult(scene, ima); - uiblock_layer_pass_buttons(layout, rr, iuser, 160 * dpi_fac, (ima->type == IMA_TYPE_R_RESULT) ? &ima->render_slot : NULL); + uiblock_layer_pass_buttons(layout, ima, rr, iuser, 160 * dpi_fac, (ima->type == IMA_TYPE_R_RESULT) ? &ima->render_slot : NULL); BKE_image_release_renderresult(scene, ima); } } @@ -947,8 +986,8 @@ void image_buttons_register(ARegionType *art) strcpy(pt->idname, "IMAGE_PT_gpencil"); strcpy(pt->label, N_("Grease Pencil")); strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw_header = gpencil_panel_standard_header; - pt->draw = gpencil_panel_standard; + pt->draw_header = ED_gpencil_panel_standard_header; + pt->draw = ED_gpencil_panel_standard; BLI_strncpy(pt->category, category, BLI_strlen_utf8(category)); BLI_addtail(&art->paneltypes, pt); } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 016143f640b..f41e237beb4 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -83,6 +83,7 @@ static void draw_render_info(Scene *scene, Image *ima, ARegion *ar, float zoomx, { RenderResult *rr; Render *re = RE_GetRender(scene->id.name); + RenderData *rd = RE_engine_get_render_data(re); rr = BKE_image_acquire_renderresult(scene, ima); @@ -110,48 +111,16 @@ static void draw_render_info(Scene *scene, Image *ima, ARegion *ar, float zoomx, glTranslatef(x, y, 0.0f); glScalef(zoomx, zoomy, 1.0f); - if (scene->r.mode & R_BORDER) { - glTranslatef((int)(-scene->r.border.xmin * scene->r.xsch * scene->r.size / 100.0f), - (int)(-scene->r.border.ymin * scene->r.ysch * scene->r.size / 100.0f), + if (rd->mode & R_BORDER) { + glTranslatef((int)(-rd->border.xmin * rd->xsch * rd->size / 100.0f), + (int)(-rd->border.ymin * rd->ysch * rd->size / 100.0f), 0.0f); } UI_ThemeColor(TH_FACE_SELECT); for (i = 0, tile = tiles; i < total_tiles; i++, tile++) { - float delta_x = 4.0f * UI_DPI_FAC / zoomx; - float delta_y = 4.0f * UI_DPI_FAC / zoomy; - - delta_x = min_ff(delta_x, tile->xmax - tile->xmin); - delta_y = min_ff(delta_y, tile->ymax - tile->ymin); - - /* left bottom corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(tile->xmin, tile->ymin + delta_y); - glVertex2f(tile->xmin, tile->ymin); - glVertex2f(tile->xmin + delta_x, tile->ymin); - glEnd(); - - /* left top corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(tile->xmin, tile->ymax - delta_y); - glVertex2f(tile->xmin, tile->ymax); - glVertex2f(tile->xmin + delta_x, tile->ymax); - glEnd(); - - /* right bottom corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(tile->xmax - delta_x, tile->ymin); - glVertex2f(tile->xmax, tile->ymin); - glVertex2f(tile->xmax, tile->ymin + delta_y); - glEnd(); - - /* right top corner */ - glBegin(GL_LINE_STRIP); - glVertex2f(tile->xmax - delta_x, tile->ymax); - glVertex2f(tile->xmax, tile->ymax); - glVertex2f(tile->xmax, tile->ymax - delta_y); - glEnd(); + glaDrawBorderCorners(tile, zoomx, zoomy); } MEM_freeN(tiles); @@ -644,14 +613,14 @@ void draw_image_grease_pencil(bContext *C, bool onlyv2d) /* draw in View2D space? */ if (onlyv2d) { /* draw grease-pencil ('image' strokes) */ - draw_gpencil_2dimage(C); + ED_gpencil_draw_2dimage(C); } else { /* assume that UI_view2d_restore(C) has been called... */ //SpaceImage *sima = (SpaceImage *)CTX_wm_space_data(C); /* draw grease-pencil ('screen' strokes) */ - draw_gpencil_view2d(C, 0); + ED_gpencil_draw_view2d(C, 0); } } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index a2f7d9e7d6c..757059ebc29 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -28,6 +28,7 @@ * \ingroup spimage */ +#include "DNA_brush_types.h" #include "DNA_mask_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -297,7 +298,7 @@ bool ED_space_image_show_render(SpaceImage *sima) bool ED_space_image_show_paint(SpaceImage *sima) { if (ED_space_image_show_render(sima)) - return 0; + return false; return (sima->mode == SI_MODE_PAINT); } @@ -305,36 +306,18 @@ bool ED_space_image_show_paint(SpaceImage *sima) bool ED_space_image_show_uvedit(SpaceImage *sima, Object *obedit) { if (sima && (ED_space_image_show_render(sima) || ED_space_image_show_paint(sima))) - return 0; + return false; if (obedit && obedit->type == OB_MESH) { struct BMEditMesh *em = BKE_editmesh_from_object(obedit); - int ret; + bool ret; ret = EDBM_mtexpoly_check(em); return ret; } - return 0; -} - -bool ED_space_image_show_uvshadow(SpaceImage *sima, Object *obedit) -{ - if (ED_space_image_show_render(sima)) - return 0; - - if (ED_space_image_show_paint(sima)) - if (obedit && obedit->type == OB_MESH) { - struct BMEditMesh *em = BKE_editmesh_from_object(obedit); - int ret; - - ret = EDBM_mtexpoly_check(em); - - return ret; - } - - return 0; + return false; } /* matches clip function */ @@ -361,6 +344,21 @@ int ED_space_image_maskedit_poll(bContext *C) return false; } +bool ED_space_image_paint_curve(const bContext *C) +{ + SpaceImage *sima = CTX_wm_space_image(C); + + if (sima && sima->mode == SI_MODE_PAINT) { + Brush *br = CTX_data_tool_settings(C)->imapaint.paint.brush; + + if (br && (br->flag & BRUSH_CURVE)) + return true; + } + + return false; +} + + int ED_space_image_maskedit_mask_poll(bContext *C) { if (ED_space_image_maskedit_poll(C)) { diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 4d391874f9b..aecc43f4fdf 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -71,6 +71,7 @@ void IMAGE_OT_view_ndof(struct wmOperatorType *ot); void IMAGE_OT_new(struct wmOperatorType *ot); void IMAGE_OT_open(struct wmOperatorType *ot); +void IMAGE_OT_unlink(struct wmOperatorType *ot); void IMAGE_OT_match_movie_length(struct wmOperatorType *ot); void IMAGE_OT_replace(struct wmOperatorType *ot); void IMAGE_OT_reload(struct wmOperatorType *ot); @@ -90,6 +91,10 @@ void IMAGE_OT_curves_point_set(struct wmOperatorType *ot); void IMAGE_OT_change_frame(struct wmOperatorType *ot); +void IMAGE_OT_read_renderlayers(struct wmOperatorType *ot); +void IMAGE_OT_render_border(struct wmOperatorType *ot); +void IMAGE_OT_clear_render_border(struct wmOperatorType *ot); + /* image_panels.c */ struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree); void image_buttons_register(struct ARegionType *art); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 57be15f23fb..df556f94f28 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -48,17 +48,21 @@ #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_DerivedMesh.h" #include "BKE_icons.h" #include "BKE_image.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_packedFile.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_sound.h" #include "GPU_draw.h" +#include "GPU_buffers.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -72,6 +76,7 @@ #include "RNA_enum_types.h" #include "ED_image.h" +#include "ED_paint.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -88,7 +93,6 @@ #include "PIL_time.h" #include "image_intern.h" -#include "ED_sculpt.h" /******************** view navigation utilities *********************/ @@ -564,6 +568,8 @@ static void image_view_zoom_cancel(bContext *C, wmOperator *op) void IMAGE_OT_view_zoom(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom"; ot->idname = "IMAGE_OT_view_zoom"; @@ -580,8 +586,9 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_LOCK_BYPASS; /* properties */ - RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, - "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX); + prop = RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, "Factor", + "Zoom factor, values higher than 1.0 zoom in, lower values zoom out", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } /********************** NDOF operator *********************/ @@ -799,6 +806,8 @@ static int image_view_zoom_in_invoke(bContext *C, wmOperator *op, const wmEvent void IMAGE_OT_view_zoom_in(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom In"; ot->idname = "IMAGE_OT_view_zoom_in"; @@ -813,7 +822,9 @@ void IMAGE_OT_view_zoom_in(wmOperatorType *ot) ot->flag = OPTYPE_LOCK_BYPASS; /* properties */ - RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f); + prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, + "Location", "Cursor location in screen coordinates", -10.0f, 10.0f); + RNA_def_property_flag(prop, PROP_HIDDEN); } static int image_view_zoom_out_exec(bContext *C, wmOperator *op) @@ -844,6 +855,8 @@ static int image_view_zoom_out_invoke(bContext *C, wmOperator *op, const wmEvent void IMAGE_OT_view_zoom_out(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Zoom Out"; ot->idname = "IMAGE_OT_view_zoom_out"; @@ -858,7 +871,9 @@ void IMAGE_OT_view_zoom_out(wmOperatorType *ot) ot->flag = OPTYPE_LOCK_BYPASS; /* properties */ - RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f); + prop = RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, + "Location", "Cursor location in screen coordinates", -10.0f, 10.0f); + RNA_def_property_flag(prop, PROP_HIDDEN); } /********************** view zoom ratio operator *********************/ @@ -942,10 +957,10 @@ static void image_open_cancel(bContext *UNUSED(C), wmOperator *op) } /** - * @brief Get a list of frames from the list of image files matching the first file name sequence pattern - * @param ptr [in] the RNA pointer containing the "directory" entry and "files" collection - * @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 + * \brief Get a list of frames from the list of image files matching the first file name sequence pattern + * \param ptr [in] the RNA pointer containing the "directory" entry and "files" collection + * \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) { @@ -989,10 +1004,10 @@ static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *p RNA_END } -static int image_cmp_frame(void *a, void *b) +static int image_cmp_frame(const void *a, const void *b) { - ImageFrame *frame_a = (ImageFrame *)a; - ImageFrame *frame_b = (ImageFrame *)b; + const ImageFrame *frame_a = a; + const ImageFrame *frame_b = b; if (frame_a->framenr < frame_b->framenr) return -1; if (frame_a->framenr > frame_b->framenr) return 1; @@ -1000,10 +1015,10 @@ static int image_cmp_frame(void *a, void *b) } /** - * @brief Return the start (offset) and the length of the sequence of continuous frames in the list of frames - * @param frames [in] the list of frame numbers, as a side-effect the list is sorted - * @param ofs [out] offest, the first frame number in the sequence - * @return the number of continuos frames in the sequence + * \brief Return the start (offset) and the length of the sequence of continuous frames in the list of frames + * \param frames [in] the list of frame numbers, as a side-effect the list is sorted + * \param ofs [out] offest, the first frame number in the sequence + * \return the number of contiguous frames in the sequence */ static int image_sequence_get_len(ListBase *frames, int *ofs) { @@ -1040,7 +1055,11 @@ static int image_open_exec(bContext *C, wmOperator *op) const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); - if (RNA_struct_property_is_set(op->ptr, "files") && RNA_struct_property_is_set(op->ptr, "directory")) { + RNA_string_get(op->ptr, "filepath", path); + + if (!IMB_isanim(path) && RNA_struct_property_is_set(op->ptr, "files") && + RNA_struct_property_is_set(op->ptr, "directory")) + { ListBase frames; BLI_listbase_clear(&frames); @@ -1048,9 +1067,6 @@ static int image_open_exec(bContext *C, wmOperator *op) frame_seq_len = image_sequence_get_len(&frames, &frame_ofs); BLI_freelistN(&frames); } - else { - RNA_string_get(op->ptr, "filepath", path); - } errno = 0; @@ -1381,7 +1397,7 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, /* sanitize all settings */ /* unlikely but just in case */ - if (ELEM3(simopts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { + if (ELEM(simopts->im_format.planes, R_IMF_PLANES_BW, R_IMF_PLANES_RGB, R_IMF_PLANES_RGBA) == 0) { simopts->im_format.planes = R_IMF_PLANES_RGBA; } @@ -1483,9 +1499,12 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI } else { /* TODO, better solution, if a 24bit image is painted onto it may contain alpha */ - if (ibuf->userflags & IB_BITMAPDIRTY) { /* it has been painted onto */ + if ((simopts->im_format.planes == R_IMF_PLANES_RGBA) && + /* it has been painted onto */ + (ibuf->userflags & IB_BITMAPDIRTY)) + { /* checks each pixel, not ideal */ - ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? 32 : 24; + ibuf->planes = BKE_imbuf_alpha_test(ibuf) ? R_IMF_PLANES_RGBA : R_IMF_PLANES_RGB; } } @@ -1865,6 +1884,7 @@ static int image_reload_exec(bContext *C, wmOperator *UNUSED(op)) // XXX other users? BKE_image_signal(ima, (sima) ? &sima->iuser : NULL, IMA_SIGNAL_RELOAD); + DAG_id_tag_update(&ima->id, 0); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -1888,6 +1908,12 @@ void IMAGE_OT_reload(wmOperatorType *ot) /********************** new image operator *********************/ #define IMA_DEF_NAME N_("Untitled") +enum { + GEN_CONTEXT_NONE = 0, + GEN_CONTEXT_PAINT_CANVAS = 1, + GEN_CONTEXT_PAINT_STENCIL = 2 +}; + static int image_new_exec(bContext *C, wmOperator *op) { SpaceImage *sima; @@ -1901,6 +1927,7 @@ static int image_new_exec(bContext *C, wmOperator *op) char *name = _name; float color[4]; int width, height, floatbuf, gen_type, alpha; + int gen_context; /* retrieve state */ sima = CTX_wm_space_image(C); @@ -1920,7 +1947,8 @@ static int image_new_exec(bContext *C, wmOperator *op) gen_type = RNA_enum_get(op->ptr, "generated_type"); RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); - + gen_context = RNA_enum_get(op->ptr, "gen_context"); + if (!alpha) color[3] = 1.0f; @@ -1944,6 +1972,40 @@ static int image_new_exec(bContext *C, wmOperator *op) else if (sima) { ED_space_image_set(sima, scene, obedit, ima); } + else if (gen_context == GEN_CONTEXT_PAINT_CANVAS) { + bScreen *sc; + Object *ob = CTX_data_active_object(C); + + GPU_drawobject_free(ob->derivedFinal); + if (scene->toolsettings->imapaint.canvas) + id_us_min(&scene->toolsettings->imapaint.canvas->id); + scene->toolsettings->imapaint.canvas = ima; + + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + + if (!sima->pin) + ED_space_image_set(sima, scene, scene->obedit, ima); + } + } + } + } + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } + else if (gen_context == GEN_CONTEXT_PAINT_STENCIL) { + Object *ob = CTX_data_active_object(C); + if (scene->toolsettings->imapaint.stencil) + id_us_min(&scene->toolsettings->imapaint.stencil->id); + scene->toolsettings->imapaint.stencil = ima; + BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); + } else { Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data; if (tex && tex->type == TEX_IMAGE) { @@ -1974,6 +2036,13 @@ void IMAGE_OT_new(wmOperatorType *ot) { PropertyRNA *prop; static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + static EnumPropertyItem gen_context_items[] = { + {GEN_CONTEXT_NONE, "NONE", 0, "None", ""}, + {GEN_CONTEXT_PAINT_CANVAS, "PAINT_CANVAS", 0, "Paint Canvas", ""}, + {GEN_CONTEXT_PAINT_STENCIL, "PAINT_STENCIL", 0, "Paint Stencil", ""}, + {0, NULL, 0, NULL, NULL} + }; /* identifiers */ ot->name = "New Image"; @@ -2000,6 +2069,9 @@ void IMAGE_OT_new(wmOperatorType *ot) RNA_def_enum(ot->srna, "generated_type", image_generated_type_items, IMA_GENTYPE_BLANK, "Generated Type", "Fill the image with a grid for UV map testing"); RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); + prop = RNA_def_enum(ot->srna, "gen_context", gen_context_items, 0, "Gen Context", "Generation context"); + RNA_def_property_flag(prop, PROP_HIDDEN); + } #undef IMA_DEF_NAME @@ -2034,7 +2106,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) if (support_undo) { ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); + ED_image_undo_restore, ED_image_undo_free, NULL); /* not strictly needed, because we only imapaint_dirty_region to invalidate all tiles * but better do this right in case someone copies this for a tool that uses partial redraw better */ ED_imapaint_clear_partial_redraw(); @@ -2458,11 +2530,12 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event int point = RNA_enum_get(op->ptr, "point"); if (point == 1) { - curvemapping_set_black_white(curve_mapping, NULL, info->colfp); + curvemapping_set_black_white(curve_mapping, NULL, info->linearcol); } else if (point == 0) { - curvemapping_set_black_white(curve_mapping, info->colfp, NULL); + curvemapping_set_black_white(curve_mapping, info->linearcol, NULL); } + WM_event_add_notifier(C, NC_WINDOW, NULL); } } @@ -2996,3 +3069,120 @@ void IMAGE_OT_change_frame(wmOperatorType *ot) /* rna */ RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); } + +/* Reload cached render results... */ +/* goes over all scenes, reads render layers */ +static int image_read_renderlayers_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima; + + ima = BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result"); + if (sima->image == NULL) { + ED_space_image_set(sima, scene, NULL, ima); + } + + RE_ReadRenderResult(scene, scene); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + return OPERATOR_FINISHED; +} + +void IMAGE_OT_read_renderlayers(wmOperatorType *ot) +{ + ot->name = "Read Render Layers"; + ot->idname = "IMAGE_OT_read_renderlayers"; + ot->description = "Read all the current scene's render layers from cache, as needed"; + + ot->poll = space_image_main_area_poll; + ot->exec = image_read_renderlayers_exec; + + /* flags */ + ot->flag = 0; +} + +/* ********************* Render border operator ****************** */ + +static int render_border_exec(bContext *C, wmOperator *op) +{ + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + rctf border; + + /* get rectangle from operator */ + WM_operator_properties_border_to_rctf(op, &border); + UI_view2d_region_to_view_rctf(&ar->v2d, &border, &border); + + /* actually set border */ + CLAMP(border.xmin, 0.0f, 1.0f); + CLAMP(border.ymin, 0.0f, 1.0f); + CLAMP(border.xmax, 0.0f, 1.0f); + CLAMP(border.ymax, 0.0f, 1.0f); + scene->r.border = border; + + /* 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)) + { + scene->r.mode &= ~R_BORDER; + } + else { + scene->r.mode |= R_BORDER; + } + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + return OPERATOR_FINISHED; + +} + +void IMAGE_OT_render_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Render Border"; + ot->description = "Set the boundaries of the border render and enable border render"; + ot->idname = "IMAGE_OT_render_border"; + + /* api callbacks */ + ot->invoke = WM_border_select_invoke; + ot->exec = render_border_exec; + ot->modal = WM_border_select_modal; + ot->cancel = WM_border_select_cancel; + ot->poll = image_cycle_render_slot_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* rna */ + WM_operator_properties_border(ot); +} + +/* ********************* Clear render border operator ****************** */ + +static int clear_render_border_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + scene->r.mode &= ~R_BORDER; + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, NULL); + BLI_rctf_init(&scene->r.border, 0.0f, 1.0f, 0.0f, 1.0f); + return OPERATOR_FINISHED; + +} + +void IMAGE_OT_clear_render_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Render Border"; + ot->description = "Clear the boundaries of the border render and disable border render"; + ot->idname = "IMAGE_OT_clear_render_border"; + + /* api callbacks */ + ot->exec = clear_render_border_exec; + ot->poll = image_cycle_render_slot_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index f60705ca52a..9cfd2e645f9 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -255,6 +255,10 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_toolshelf); WM_operatortype_append(IMAGE_OT_change_frame); + + WM_operatortype_append(IMAGE_OT_read_renderlayers); + WM_operatortype_append(IMAGE_OT_render_border); + WM_operatortype_append(IMAGE_OT_clear_render_border); } static void image_keymap(struct wmKeyConfig *keyconf) @@ -266,6 +270,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "IMAGE_OT_new", NKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "IMAGE_OT_open", OKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "IMAGE_OT_reload", RKEY, KM_PRESS, KM_ALT, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_read_renderlayers", RKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "IMAGE_OT_save", SKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "IMAGE_OT_save_as", F3KEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "IMAGE_OT_properties", NKEY, KM_PRESS, 0, 0); @@ -322,9 +327,9 @@ static void image_keymap(struct wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "toggle", true); /* fast switch to render slots */ - for (i = 0; i < MAX2(IMA_MAX_RENDER_SLOT, 9); i++) { + for (i = 0; i < MIN2(IMA_MAX_RENDER_SLOT, 9); i++) { kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_int", ONEKEY + i, KM_PRESS, 0, 0); - RNA_string_set(kmi->ptr, "data_path", "space_data.image.render_slot"); + RNA_string_set(kmi->ptr, "data_path", "space_data.image.render_slots.active_index"); RNA_int_set(kmi->ptr, "value", i); } @@ -340,13 +345,17 @@ static void image_keymap(struct wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", PERIODKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "space_data.pivot_point"); RNA_string_set(kmi->ptr, "value", "CURSOR"); + + /* render border */ + WM_keymap_add_item(keymap, "IMAGE_OT_render_border", BKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_clear_render_border", BKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); } /* dropboxes */ static int image_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { if (drag->type == WM_DRAG_PATH) - if (ELEM3(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_BLANK)) /* rule might not work? */ + if (ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_BLANK)) /* rule might not work? */ return 1; return 0; } @@ -529,6 +538,19 @@ static void image_listener(bScreen *sc, ScrArea *sa, wmNotifier *wmn) break; } + case NC_ID: + { + if (wmn->action == NA_RENAME) { + ED_area_tag_redraw(sa); + } + break; + } + case NC_WM: + if (wmn->data == ND_UNDO) { + ED_area_tag_redraw(sa); + ED_area_tag_refresh(sa); + } + break; } } @@ -623,6 +645,12 @@ static void image_main_area_init(wmWindowManager *wm, ARegion *ar) WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); /* image paint polls for mode */ + keymap = WM_keymap_find(wm->defaultconf, "Curve", 0, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + keymap = WM_keymap_find(wm->defaultconf, "Paint Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Image Paint", 0, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); @@ -647,6 +675,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) Object *obact = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Mask *mask = NULL; + bool curve = false; Scene *scene = CTX_data_scene(C); View2D *v2d = &ar->v2d; //View2DScrollers *scrollers; @@ -692,6 +721,9 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) else if (sima->mode == SI_MODE_MASK) { mask = ED_space_image_get_mask(sima); } + else if (ED_space_image_paint_curve(C)) { + curve = true; + } ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW); @@ -743,6 +775,11 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) draw_image_cursor(ar, sima->cursor); UI_view2d_view_restore(C); } + else if (curve) { + UI_view2d_view_ortho(v2d); + draw_image_cursor(ar, sima->cursor); + UI_view2d_view_restore(C); + } draw_image_cache(C, ar); diff --git a/source/blender/editors/space_info/CMakeLists.txt b/source/blender/editors/space_info/CMakeLists.txt index 94b40a9f40a..46ea251de03 100644 --- a/source/blender/editors/space_info/CMakeLists.txt +++ b/source/blender/editors/space_info/CMakeLists.txt @@ -26,10 +26,12 @@ set(INC ../../blenloader ../../imbuf ../../bmesh + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -52,4 +54,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_info "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_info/SConscript b/source/blender/editors/space_info/SConscript index 2a1419984c6..378940805da 100644 --- a/source/blender/editors/space_info/SConscript +++ b/source/blender/editors/space_info/SConscript @@ -31,13 +31,15 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../blenloader', '../../bmesh', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -45,7 +47,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 8664ebf30b7..a0dfb285a1c 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -57,6 +57,7 @@ #include "ED_armature.h" #define MAX_INFO_LEN 512 +#define MAX_INFO_NUM_LEN 16 typedef struct SceneStats { int totvert, totvertsel; @@ -65,11 +66,22 @@ typedef struct SceneStats { int totbone, totbonesel; int totobj, totobjsel; int totlamp, totlampsel; - int tottri, totmesh; + int tottri; char infostr[MAX_INFO_LEN]; } SceneStats; +typedef struct SceneStatsFmt { + /* Totals */ + char totvert[MAX_INFO_NUM_LEN], totvertsel[MAX_INFO_NUM_LEN]; + char totface[MAX_INFO_NUM_LEN], totfacesel[MAX_INFO_NUM_LEN]; + char totedge[MAX_INFO_NUM_LEN], totedgesel[MAX_INFO_NUM_LEN]; + char totbone[MAX_INFO_NUM_LEN], totbonesel[MAX_INFO_NUM_LEN]; + char totobj[MAX_INFO_NUM_LEN], totobjsel[MAX_INFO_NUM_LEN]; + char totlamp[MAX_INFO_NUM_LEN], totlampsel[MAX_INFO_NUM_LEN]; + char tottri[MAX_INFO_NUM_LEN]; +} SceneStatsFmt; + static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) { switch (ob->type) { @@ -79,8 +91,6 @@ static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) DerivedMesh *dm = ob->derivedFinal; int totvert, totedge, totface, totloop; - stats->totmesh += totob; - if (dm) { totvert = dm->getNumVerts(dm); totedge = dm->getNumEdges(dm); @@ -367,6 +377,7 @@ static void stats_string(Scene *scene) { #define MAX_INFO_MEM_LEN 64 SceneStats *stats = scene->stats; + SceneStatsFmt stats_fmt; Object *ob = (scene->basact) ? scene->basact->object : NULL; uintptr_t mem_in_use, mmap_in_use; char memstr[MAX_INFO_MEM_LEN]; @@ -376,6 +387,34 @@ static void stats_string(Scene *scene) mem_in_use = MEM_get_memory_in_use(); mmap_in_use = MEM_get_mapped_memory_in_use(); + + /* Generate formatted numbers */ +#define SCENE_STATS_FMT_INT(_id) \ + BLI_str_format_int_grouped(stats_fmt._id, stats->_id) + + SCENE_STATS_FMT_INT(totvert); + SCENE_STATS_FMT_INT(totvertsel); + + SCENE_STATS_FMT_INT(totedge); + SCENE_STATS_FMT_INT(totedgesel); + + SCENE_STATS_FMT_INT(totface); + SCENE_STATS_FMT_INT(totfacesel); + + SCENE_STATS_FMT_INT(totbone); + SCENE_STATS_FMT_INT(totbonesel); + + SCENE_STATS_FMT_INT(totobj); + SCENE_STATS_FMT_INT(totobjsel); + + SCENE_STATS_FMT_INT(totlamp); + SCENE_STATS_FMT_INT(totlampsel); + + SCENE_STATS_FMT_INT(tottri); + +#undef SCENE_STATS_FMT_INT + + /* get memory statistics */ s = memstr; ofs += BLI_snprintf(s + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_(" | Mem:%.2fM"), @@ -394,32 +433,36 @@ static void stats_string(Scene *scene) if (scene->obedit->type == OB_MESH) { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, - IFACE_("Verts:%d/%d | Edges:%d/%d | Faces:%d/%d | Tris:%d"), - stats->totvertsel, stats->totvert, stats->totedgesel, stats->totedge, - stats->totfacesel, stats->totface, stats->tottri); + IFACE_("Verts:%s/%s | Edges:%s/%s | Faces:%s/%s | Tris:%s"), + stats_fmt.totvertsel, stats_fmt.totvert, stats_fmt.totedgesel, stats_fmt.totedge, + stats_fmt.totfacesel, stats_fmt.totface, stats_fmt.tottri); } else if (scene->obedit->type == OB_ARMATURE) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d/%d | Bones:%d/%d"), stats->totvertsel, - stats->totvert, stats->totbonesel, stats->totbone); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s/%s | Bones:%s/%s"), stats_fmt.totvertsel, + stats_fmt.totvert, stats_fmt.totbonesel, stats_fmt.totbone); } else { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d/%d"), stats->totvertsel, stats->totvert); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s/%s"), stats_fmt.totvertsel, + stats_fmt.totvert); } ofs += BLI_strncpy_rlen(s + ofs, memstr, MAX_INFO_LEN - ofs); } else if (ob && (ob->mode & OB_MODE_POSE)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%d/%d %s"), - stats->totbonesel, stats->totbone, memstr); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s"), + stats_fmt.totbonesel, stats_fmt.totbone, memstr); } else if (stats_is_object_dynamic_topology_sculpt(ob)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%d | Tris:%d"), stats->totvert, stats->tottri); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s"), stats_fmt.totvert, + stats_fmt.tottri); } else { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, - IFACE_("Verts:%d | Faces:%d | Tris:%d | Objects:%d/%d | Lamps:%d/%d%s"), stats->totvert, - stats->totface, stats->tottri, stats->totobjsel, stats->totobj, stats->totlampsel, - stats->totlamp, memstr); + IFACE_("Verts:%s | Faces:%s | Tris:%s | Objects:%s/%s | Lamps:%s/%s%s"), + stats_fmt.totvert, stats_fmt.totface, + stats_fmt.tottri, stats_fmt.totobjsel, + stats_fmt.totobj, stats_fmt.totlampsel, + stats_fmt.totlamp, memstr); } if (ob) diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index c09c684ac7a..33333e4c992 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -290,7 +290,7 @@ int textview_draw(TextViewContext *tvc, const int draw, int mval[2], void **mous cdc.lheight = tvc->lheight; cdc.lofs = -BLF_descender(mono); /* note, scroll bar must be already subtracted () */ - cdc.console_width = (tvc->winx - (CONSOLE_DRAW_MARGIN * 2) ) / cdc.cwidth; + cdc.console_width = (tvc->winx - (CONSOLE_DRAW_MARGIN * 2)) / cdc.cwidth; /* avoid divide by zero on small windows */ if (cdc.console_width < 1) cdc.console_width = 1; diff --git a/source/blender/editors/space_logic/CMakeLists.txt b/source/blender/editors/space_logic/CMakeLists.txt index af2e8476511..19525c87923 100644 --- a/source/blender/editors/space_logic/CMakeLists.txt +++ b/source/blender/editors/space_logic/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -47,6 +49,8 @@ if(WITH_GAMEENGINE) add_definitions(-DWITH_GAMEENGINE) endif() +add_definitions(${GL_DEFINITIONS}) + if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() diff --git a/source/blender/editors/space_logic/SConscript b/source/blender/editors/space_logic/SConscript index ae8e929a684..067073fab22 100644 --- a/source/blender/editors/space_logic/SConscript +++ b/source/blender/editors/space_logic/SConscript @@ -31,12 +31,14 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../interface', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -44,7 +46,7 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_GAMEENGINE']: defs.append('WITH_GAMEENGINE') diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 4b533292a95..dd152022762 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -519,6 +519,8 @@ static const char *actuator_name(int type) return N_("Armature"); case ACT_STEERING: return N_("Steering"); + case ACT_MOUSE: + return N_("Mouse"); } return N_("Unknown"); } @@ -1142,15 +1144,30 @@ static void draw_sensor_message(uiLayout *layout, PointerRNA *ptr) uiItemR(layout, ptr, "subject", 0, NULL, ICON_NONE); } -static void draw_sensor_mouse(uiLayout *layout, PointerRNA *ptr) +static void draw_sensor_mouse(uiLayout *layout, PointerRNA *ptr, bContext *C) { - uiLayout *split; + uiLayout *split, *split2; + PointerRNA main_ptr; split = uiLayoutSplit(layout, 0.8f, false); uiItemR(split, ptr, "mouse_event", 0, NULL, ICON_NONE); - if (RNA_enum_get(ptr, "mouse_event") == BL_SENS_MOUSE_MOUSEOVER_ANY) + if (RNA_enum_get(ptr, "mouse_event") == BL_SENS_MOUSE_MOUSEOVER_ANY) { uiItemR(split, ptr, "use_pulse", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); + + split = uiLayoutSplit(layout, 0.3f, false); + uiItemR(split, ptr, "use_material", 0, "", ICON_NONE); + + split2 = uiLayoutSplit(split, 0.7f, false); + if (RNA_enum_get(ptr, "use_material") == SENS_RAY_PROPERTY) { + uiItemR(split2, ptr, "property", 0, "", ICON_NONE); + } + else { + RNA_main_pointer_create(CTX_data_main(C), &main_ptr); + uiItemPointerR(split2, ptr, "material", &main_ptr, "materials", "", ICON_MATERIAL_DATA); + } + uiItemR(split2, ptr, "use_x_ray", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); + } } static void draw_sensor_near(uiLayout *layout, PointerRNA *ptr) @@ -1182,9 +1199,9 @@ static void draw_sensor_property(uiLayout *layout, PointerRNA *ptr) uiItemR(row, ptr, "value_max", 0, NULL, ICON_NONE); break; case SENS_PROP_EQUAL: - uiItemR(layout, ptr, "value", 0, NULL, ICON_NONE); - break; case SENS_PROP_NEQUAL: + case SENS_PROP_LESSTHAN: + case SENS_PROP_GREATERTHAN: uiItemR(layout, ptr, "value", 0, NULL, ICON_NONE); break; case SENS_PROP_CHANGED: @@ -1271,7 +1288,7 @@ static void draw_brick_sensor(uiLayout *layout, PointerRNA *ptr, bContext *C) draw_sensor_message(box, ptr); break; case SENS_MOUSE: - draw_sensor_mouse(box, ptr); + draw_sensor_mouse(box, ptr, C); break; case SENS_NEAR: draw_sensor_near(box, ptr); @@ -1718,6 +1735,12 @@ static void draw_actuator_edit_object(uiLayout *layout, PointerRNA *ptr) sub = uiLayoutSplit(split, 0.7f, false); uiItemR(sub, ptr, "time", 0, NULL, ICON_NONE); uiItemR(sub, ptr, "use_3d_tracking", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "up_axis", 0, NULL, ICON_NONE); + + row = uiLayoutRow(layout, false); + uiItemR(row, ptr, "track_axis", 0, NULL, ICON_NONE); break; case ACT_EDOB_DYNAMICS: if (ob->type != OB_MESH) { @@ -1809,7 +1832,7 @@ static void draw_actuator_motion(uiLayout *layout, PointerRNA *ptr) uiItemR(row, ptr, "offset_rotation", 0, NULL, ICON_NONE); uiItemR(split, ptr, "use_local_rotation", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); - if (ELEM3(physics_type, OB_BODY_TYPE_DYNAMIC, OB_BODY_TYPE_RIGID, OB_BODY_TYPE_SOFT)) { + if (ELEM(physics_type, OB_BODY_TYPE_DYNAMIC, OB_BODY_TYPE_RIGID, OB_BODY_TYPE_SOFT)) { uiItemL(layout, IFACE_("Dynamic Object Settings:"), ICON_NONE); split = uiLayoutSplit(layout, 0.9, false); row = uiLayoutRow(split, false); @@ -1931,6 +1954,7 @@ static void draw_actuator_property(uiLayout *layout, PointerRNA *ptr) switch (RNA_enum_get(ptr, "mode")) { case ACT_PROP_TOGGLE: + case ACT_PROP_LEVEL: break; case ACT_PROP_ADD: uiItemR(layout, ptr, "value", 0, NULL, ICON_NONE); @@ -2177,6 +2201,68 @@ static void draw_actuator_steering(uiLayout *layout, PointerRNA *ptr) } } +static void draw_actuator_mouse(uiLayout *layout, PointerRNA *ptr) +{ + uiLayout *row, *col, *subcol, *split, *subsplit; + + uiItemR(layout, ptr, "mode", 0, NULL, 0); + + switch (RNA_enum_get(ptr, "mode")) { + case ACT_MOUSE_VISIBILITY: + row = uiLayoutRow(layout, 0); + uiItemR(row, ptr, "visible", UI_ITEM_R_TOGGLE, NULL, 0); + break; + + case ACT_MOUSE_LOOK: + /* X axis */ + row = uiLayoutRow(layout, 0); + col = uiLayoutColumn(row, 1); + + uiItemR(col, ptr, "use_axis_x", UI_ITEM_R_TOGGLE, NULL, 0); + + subcol = uiLayoutColumn(col, 1); + uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_axis_x")==1); + uiItemR(subcol, ptr, "sensitivity_x", 0, NULL, 0); + uiItemR(subcol, ptr, "threshold_x", 0, NULL, 0); + + uiItemR(subcol, ptr, "min_x", 0, NULL, 0); + uiItemR(subcol, ptr, "max_x", 0, NULL, 0); + + uiItemR(subcol, ptr, "object_axis_x", 0, NULL, 0); + + /* Y Axis */ + col = uiLayoutColumn(row, 1); + + uiItemR(col, ptr, "use_axis_y", UI_ITEM_R_TOGGLE, NULL, 0); + + subcol = uiLayoutColumn(col, 1); + uiLayoutSetActive(subcol, RNA_boolean_get(ptr, "use_axis_y")==1); + uiItemR(subcol, ptr, "sensitivity_y", 0, NULL, 0); + uiItemR(subcol, ptr, "threshold_y", 0, NULL, 0); + + uiItemR(subcol, ptr, "min_y", 0, NULL, 0); + uiItemR(subcol, ptr, "max_y", 0, NULL, 0); + + uiItemR(subcol, ptr, "object_axis_y", 0, NULL, 0); + + /* Lower options */ + row = uiLayoutRow(layout, 0); + split = uiLayoutSplit(row, 0.5, 0); + + subsplit = uiLayoutSplit(split, 0.5, 1); + uiLayoutSetActive(subsplit, RNA_boolean_get(ptr, "use_axis_x")==1); + uiItemR(subsplit, ptr, "local_x", UI_ITEM_R_TOGGLE, NULL, 0); + uiItemR(subsplit, ptr, "reset_x", UI_ITEM_R_TOGGLE, NULL, 0); + + subsplit = uiLayoutSplit(split, 0.5, 1); + uiLayoutSetActive(subsplit, RNA_boolean_get(ptr, "use_axis_y")==1); + uiItemR(subsplit, ptr, "local_y", UI_ITEM_R_TOGGLE, NULL, 0); + uiItemR(subsplit, ptr, "reset_y", UI_ITEM_R_TOGGLE, NULL, 0); + + break; + } +} + static void draw_brick_actuator(uiLayout *layout, PointerRNA *ptr, bContext *C) { uiLayout *box; @@ -2241,6 +2327,10 @@ static void draw_brick_actuator(uiLayout *layout, PointerRNA *ptr, bContext *C) break; case ACT_STEERING: draw_actuator_steering(box, ptr); + break; + case ACT_MOUSE: + draw_actuator_mouse(box, ptr); + break; } } diff --git a/source/blender/editors/space_nla/CMakeLists.txt b/source/blender/editors/space_nla/CMakeLists.txt index a74fc3191b7..ab0dfa30121 100644 --- a/source/blender/editors/space_nla/CMakeLists.txt +++ b/source/blender/editors/space_nla/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -49,4 +51,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_nla "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_nla/SConscript b/source/blender/editors/space_nla/SConscript index a00337e0b58..9e743d918b9 100644 --- a/source/blender/editors/space_nla/SConscript +++ b/source/blender/editors/space_nla/SConscript @@ -31,18 +31,20 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 808bd622c0d..1090106d79f 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -174,7 +174,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return (found != 0); } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 16572f1790b..fbb4d273626 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -50,6 +50,7 @@ #include "ED_anim_api.h" #include "ED_keyframes_edit.h" +#include "ED_object.h" #include "ED_screen.h" #include "RNA_access.h" @@ -73,7 +74,7 @@ * --> Most channels are now selection only... */ -static int mouse_nla_channels(bAnimContext *ac, float x, int channel_index, short selectmode) +static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channel_index, short selectmode) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; @@ -94,7 +95,7 @@ static int mouse_nla_channels(bAnimContext *ac, float x, int channel_index, shor if (G.debug & G_DEBUG) printf("Error: animation channel (index = %d) not found in mouse_anim_channels()\n", channel_index); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return 0; } @@ -154,6 +155,9 @@ static int mouse_nla_channels(bAnimContext *ac, float x, int channel_index, shor if (adt) adt->flag |= ADT_UI_SELECTED; } + /* change active object - regardless of whether it is now selected [T37883] */ + ED_base_object_activate(C, base); /* adds notifier */ + if ((adt) && (adt->flag & ADT_UI_SELECTED)) adt->flag |= ADT_UI_ACTIVE; @@ -322,7 +326,7 @@ static int mouse_nla_channels(bAnimContext *ac, float x, int channel_index, shor } /* free channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* return the notifier-flags set */ return notifierFlags; @@ -366,7 +370,7 @@ static int nlachannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmEv UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index); /* handle mouse-click in the relevant channel then */ - notifierFlags = mouse_nla_channels(&ac, x, channel_index, selectmode); + notifierFlags = mouse_nla_channels(C, &ac, x, channel_index, selectmode); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | notifierFlags, NULL); @@ -416,8 +420,8 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) /* active animdata block */ if (nla_panel_context(C, &adt_ptr, NULL, NULL) == 0 || (adt_ptr.data == NULL)) { - BKE_report(op->reports, RPT_ERROR, "No active AnimData block to use. " - "Select a datablock expander first or set the appropriate flags on an AnimData block"); + BKE_report(op->reports, RPT_ERROR, "No active AnimData block to use " + "(select a datablock expander first or set the appropriate flags on an AnimData block)"); return OPERATOR_CANCELLED; } else { @@ -437,13 +441,13 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) /* get channel from index */ ale = BLI_findlink(&anim_data, channel_index); if (ale == NULL) { - BKE_reportf(op->reports, RPT_ERROR, "No animation channel found at index = %d", channel_index); - BLI_freelistN(&anim_data); + BKE_reportf(op->reports, RPT_ERROR, "No animation channel found at index %d", channel_index); + ANIM_animdata_freelist(&anim_data); return OPERATOR_CANCELLED; } else if (ale->type != ANIMTYPE_NLAACTION) { - BKE_reportf(op->reports, RPT_ERROR, "Animation channel at index = %d is not a NLA 'Active Action' channel", channel_index); - BLI_freelistN(&anim_data); + BKE_reportf(op->reports, RPT_ERROR, "Animation channel at index %d is not a NLA 'Active Action' channel", channel_index); + ANIM_animdata_freelist(&anim_data); return OPERATOR_CANCELLED; } @@ -451,7 +455,7 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) adt = ale->adt; /* we don't need anything here anymore, so free it all */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* double-check that we are free to push down here... */ @@ -460,7 +464,8 @@ static int nlachannels_pushdown_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } else if (nlaedit_is_tweakmode_on(&ac)) { - BKE_report(op->reports, RPT_WARNING, "Cannot push down actions while tweaking a strip's action. Exit tweakmode first"); + BKE_report(op->reports, RPT_WARNING, + "Cannot push down actions while tweaking a strip's action, exit tweak mode first"); return OPERATOR_CANCELLED; } else if (adt->action == NULL) { @@ -538,7 +543,7 @@ bool nlaedit_add_tracks_existing(bAnimContext *ac, bool above_sel) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return added; } @@ -571,7 +576,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac) } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return added; } @@ -665,7 +670,7 @@ static int nlaedit_delete_tracks_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 943112624cd..ac8dca6e83a 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -613,7 +613,7 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *ar) } /* free tempolary channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* *********************************************** */ @@ -704,7 +704,7 @@ void draw_nla_channel_list(bContext *C, bAnimContext *ac, ARegion *ar) } /* free temporary channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* *********************************************** */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index 93d0f7527ef..c787177a62a 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -93,7 +93,7 @@ void ED_nla_postop_refresh(bAnimContext *ac) } /* free temp memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* *********************************************** */ @@ -136,7 +136,7 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* if we managed to enter tweakmode on at least one AnimData block, * set the flag for this in the active scene and send notifiers @@ -200,7 +200,7 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* if we managed to enter tweakmode on at least one AnimData block, * set the flag for this in the active scene and send notifiers @@ -293,7 +293,7 @@ static void get_nlastrip_extents(bAnimContext *ac, float *min, float *max, const } /* free memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* set default range if nothing happened */ @@ -356,10 +356,12 @@ void NLA_OT_previewrange_set(wmOperatorType *ot) /* ****************** View-All Operator ****************** */ -/* Find the extents of the active channel - * > min: (float) bottom y-extent of channel - * > max: (float) top y-extent of channel - * > returns: success of finding a selected channel +/** + * Find the extents of the active channel + * + * \param[out] min Bottom y-extent of channel + * \param[out] max Top y-extent of channel + * \return Success of finding a selected channel */ static bool nla_channels_get_selected_extents(bAnimContext *ac, float *min, float *max) { @@ -406,7 +408,7 @@ static bool nla_channels_get_selected_extents(bAnimContext *ac, float *min, floa } /* free all temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return (found != 0); } @@ -602,7 +604,7 @@ static int nlaedit_add_actionclip_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -723,7 +725,7 @@ static int nlaedit_add_transition_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* was anything added? */ if (done) { @@ -815,7 +817,7 @@ static int nlaedit_add_sound_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -880,7 +882,7 @@ static int nlaedit_add_meta_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -932,7 +934,7 @@ static int nlaedit_remove_meta_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -1019,7 +1021,7 @@ static int nlaedit_duplicate_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); if (done) { /* refresh auto strip properties */ @@ -1111,7 +1113,7 @@ static int nlaedit_delete_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1256,7 +1258,7 @@ static int nlaedit_split_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1309,7 +1311,7 @@ static int nlaedit_bake_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1375,7 +1377,7 @@ static int nlaedit_toggle_mute_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -1529,7 +1531,7 @@ static int nlaedit_swap_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1603,7 +1605,7 @@ static int nlaedit_move_up_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1677,7 +1679,7 @@ static int nlaedit_move_down_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -1756,7 +1758,7 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -1832,7 +1834,7 @@ static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -1928,7 +1930,7 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -1988,7 +1990,7 @@ static int nlaedit_clear_scale_exec(bContext *C, wmOperator *UNUSED(op)) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -2129,7 +2131,7 @@ static int nlaedit_snap_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* refresh auto strip properties */ ED_nla_postop_refresh(&ac); @@ -2252,7 +2254,7 @@ static int nla_fmodifier_add_exec(bContext *C, wmOperator *op) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* set notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_NLA | NA_EDITED, NULL); @@ -2375,7 +2377,7 @@ static int nla_fmodifier_paste_exec(bContext *C, wmOperator *op) } /* clean up */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* successful or not? */ if (ok) { diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index 295a7ab2e04..5e1381db696 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -323,6 +323,9 @@ void nla_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "NLA_OT_tweakmode_enter", TABKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "NLA_OT_tweakmode_exit", TABKEY, KM_PRESS, 0, 0); + /* find (i.e. a shortcut for setting the name filter) */ + WM_keymap_add_item(keymap, "ANIM_OT_channels_find", FKEY, KM_PRESS, KM_CTRL, 0); + /* channels ---------------------------------------------------------- */ /* Channels are not directly handled by the NLA Editor module, but are inherited from the Animation module. * Most of the relevant operations, keymaps, drawing, etc. can therefore all be found in that module instead, as there diff --git a/source/blender/editors/space_nla/nla_select.c b/source/blender/editors/space_nla/nla_select.c index 3e7a6f4578c..8261397c940 100644 --- a/source/blender/editors/space_nla/nla_select.c +++ b/source/blender/editors/space_nla/nla_select.c @@ -153,7 +153,7 @@ static void deselect_nla_strips(bAnimContext *ac, short test, short sel) } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -268,7 +268,7 @@ static void borderselect_nla_strips(bAnimContext *ac, rcti rect, short mode, sho } /* cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -412,7 +412,7 @@ static void nlaedit_select_leftright(bContext *C, bAnimContext *ac, short leftri } /* Cleanup */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ------------------- */ @@ -465,9 +465,9 @@ static int nlaedit_select_leftright_invoke(bContext *C, wmOperator *op, const wm /* determine which side of the current frame mouse is on */ x = UI_view2d_region_to_view_x(v2d, event->mval[0]); if (x < CFRA) - RNA_int_set(op->ptr, "mode", NLAEDIT_LRSEL_LEFT); + RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_LEFT); else - RNA_int_set(op->ptr, "mode", NLAEDIT_LRSEL_RIGHT); + RNA_enum_set(op->ptr, "mode", NLAEDIT_LRSEL_RIGHT); } /* perform selection */ @@ -537,7 +537,7 @@ static void mouse_nla_strips(bContext *C, bAnimContext *ac, const int mval[2], s if (ale == NULL) { /* channel not found */ printf("Error: animation channel (index = %d) not found in mouse_nla_strips()\n", channel_index); - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } else { @@ -556,7 +556,7 @@ static void mouse_nla_strips(bContext *C, bAnimContext *ac, const int mval[2], s BLI_remlink(&anim_data, ale); /* free list of channels, since it's not used anymore */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* if currently in tweakmode, exit tweakmode before changing selection states diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 6b0460ce5cd..34887f8388d 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../windowmanager ../../compositor ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -64,4 +65,6 @@ if(WITH_COMPOSITOR) add_definitions(-DWITH_COMPOSITOR) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_node "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_node/SConscript b/source/blender/editors/space_node/SConscript index 94756b96035..435a78f1b7e 100644 --- a/source/blender/editors/space_node/SConscript +++ b/source/blender/editors/space_node/SConscript @@ -31,7 +31,8 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', @@ -47,7 +48,8 @@ incs = [ ] incs = ' '.join(incs) -defs = [] +defs = env['BF_GL_DEFINITIONS'] + cf = [] if env['OURPLATFORM'] in ('win32-vc', 'win64-vc'): #cf.append('/WX') diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 8ef1d0bef99..ffc82128181 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -181,7 +181,7 @@ static void node_buts_time(uiLayout *layout, bContext *UNUSED(C), PointerRNA *pt } #endif - uiTemplateCurveMapping(layout, ptr, "curve", 's', 0, 0); + uiTemplateCurveMapping(layout, ptr, "curve", 's', false, false, false); row = uiLayoutRow(layout, true); uiItemR(row, ptr, "frame_start", 0, IFACE_("Sta"), ICON_NONE); @@ -195,7 +195,7 @@ static void node_buts_colorramp(uiLayout *layout, bContext *UNUSED(C), PointerRN static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiTemplateCurveMapping(layout, ptr, "mapping", 'v', 0, 0); + uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false); } #define SAMPLE_FLT_ISNONE FLT_MAX @@ -223,7 +223,7 @@ static void node_buts_curvecol(uiLayout *layout, bContext *UNUSED(C), PointerRNA cumap->flag &= ~CUMA_DRAW_SAMPLE; } - uiTemplateCurveMapping(layout, ptr, "mapping", 'c', 0, 0); + uiTemplateCurveMapping(layout, ptr, "mapping", 'c', false, false, false); } static void node_buts_normal(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -605,7 +605,7 @@ static void node_draw_reroute(const bContext *C, ARegion *ar, SpaceNode *UNUSED( static int node_tweak_area_reroute(bNode *node, int x, int y) { /* square of tweak radius */ - static const float tweak_radius_sq = 576; /* 24 * 24 */ + const float tweak_radius_sq = SQUARE(24); bNodeSocket *sock = node->inputs.first; float dx = sock->locx - x; @@ -885,6 +885,11 @@ static void node_shader_buts_uvmap(uiLayout *layout, bContext *C, PointerRNA *pt } } +static void node_shader_buts_uvalongstroke(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "use_tips", 0, NULL, 0); +} + static void node_shader_buts_normal_map(uiLayout *layout, bContext *C, PointerRNA *ptr) { uiItemR(layout, ptr, "space", 0, "", 0); @@ -930,32 +935,30 @@ static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); } +static void node_shader_buts_anisotropic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); +} + static void node_shader_buts_subsurface(uiLayout *layout, bContext *C, PointerRNA *ptr) { - /* SSS does not work on GPU yet */ + /* SSS only enabled in Experimental Kernel */ PointerRNA scene = CTX_data_pointer_get(C, "scene"); if (scene.data) { PointerRNA cscene = RNA_pointer_get(&scene, "cycles"); - if (cscene.data && (RNA_enum_get(&cscene, "device") == 1 && U.compute_device_type != 0)) - uiItemL(layout, IFACE_("SSS not supported on GPU"), ICON_ERROR); + if (cscene.data && + ((U.compute_device_type != USER_COMPUTE_DEVICE_NONE) && + (RNA_enum_get(&cscene, "device") == 1) && + (RNA_enum_get(&cscene, "feature_set") == 0))) + { + uiItemL(layout, IFACE_("Only enabled in experimental GPU kernel"), ICON_ERROR); + } } uiItemR(layout, ptr, "falloff", 0, "", ICON_NONE); } -static void node_shader_buts_volume(uiLayout *layout, bContext *C, PointerRNA *UNUSED(ptr)) -{ - /* Volume does not work on GPU yet */ - PointerRNA scene = CTX_data_pointer_get(C, "scene"); - if (scene.data) { - PointerRNA cscene = RNA_pointer_get(&scene, "cycles"); - - if (cscene.data && (RNA_enum_get(&cscene, "device") == 1 && U.compute_device_type != 0)) - uiItemL(layout, IFACE_("Volumes not supported on GPU"), ICON_ERROR); - } -} - static void node_shader_buts_toon(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "component", 0, "", ICON_NONE); @@ -995,6 +998,16 @@ static void node_shader_buts_script_ex(uiLayout *layout, bContext *C, PointerRNA #endif } +static void node_buts_output_linestyle(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *row, *col; + + col = uiLayoutColumn(layout, false); + row = uiLayoutRow(col, true); + uiItemR(row, ptr, "blend_type", 0, "", ICON_NONE); + uiItemR(col, ptr, "use_clamp", 0, NULL, ICON_NONE); +} + /* only once called */ static void node_shader_set_butfunc(bNodeType *ntype) { @@ -1096,15 +1109,12 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_BSDF_REFRACTION: ntype->draw_buttons = node_shader_buts_glossy; break; + case SH_NODE_BSDF_ANISOTROPIC: + ntype->draw_buttons = node_shader_buts_anisotropic; + break; case SH_NODE_SUBSURFACE_SCATTERING: ntype->draw_buttons = node_shader_buts_subsurface; break; - case SH_NODE_VOLUME_SCATTER: - ntype->draw_buttons = node_shader_buts_volume; - break; - case SH_NODE_VOLUME_ABSORPTION: - ntype->draw_buttons = node_shader_buts_volume; - break; case SH_NODE_BSDF_TOON: ntype->draw_buttons = node_shader_buts_toon; break; @@ -1118,6 +1128,12 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_UVMAP: ntype->draw_buttons = node_shader_buts_uvmap; break; + case SH_NODE_UVALONGSTROKE: + ntype->draw_buttons = node_shader_buts_uvalongstroke; + break; + case SH_NODE_OUTPUT_LINESTYLE: + ntype->draw_buttons = node_buts_output_linestyle; + break; } } @@ -1833,7 +1849,7 @@ static void node_composit_buts_huecorrect(uiLayout *layout, bContext *UNUSED(C), cumap->flag &= ~CUMA_DRAW_SAMPLE; } - uiTemplateCurveMapping(layout, ptr, "mapping", 'h', 0, 0); + uiTemplateCurveMapping(layout, ptr, "mapping", 'h', false, false, false); } static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2299,6 +2315,12 @@ static void node_composit_buts_cornerpin(uiLayout *UNUSED(layout), bContext *UNU { } +static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "source", UI_ITEM_R_EXPAND, "", ICON_NONE); + uiItemR(layout, ptr, "ray_length", UI_ITEM_R_SLIDER, NULL, ICON_NONE); +} + /* only once called */ static void node_composit_set_butfunc(bNodeType *ntype) { @@ -2523,6 +2545,9 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_CORNERPIN: ntype->draw_buttons = node_composit_buts_cornerpin; break; + case CMP_NODE_SUNBEAMS: + ntype->draw_buttons = node_composit_buts_sunbeams; + break; } } @@ -3031,7 +3056,8 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b glaDefine2DArea(&ar->winrct); /* ortho at pixel level curarea */ - wmOrtho2(-GLA_PIXEL_OFS, ar->winx - GLA_PIXEL_OFS, -GLA_PIXEL_OFS, ar->winy - GLA_PIXEL_OFS); + /* almost #wmOrtho2_region_pixelspace, but no +1 px */ + wmOrtho2_pixelspace(ar->winx, ar->winy); x = (ar->winx - snode->zoom * ibuf->x) / 2 + snode->xof; y = (ar->winy - snode->zoom * ibuf->y) / 2 + snode->yof; @@ -3100,7 +3126,7 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b IMB_display_buffer_release(cache_handle); } - /** @note draw selected info on backdrop */ + /** \note draw selected info on backdrop */ if (snode->edittree) { bNode *node = snode->edittree->nodes.first; rctf *viewer_border = &snode->nodetree->viewer_border; @@ -3114,20 +3140,17 @@ void draw_nodespace_back_pix(const bContext *C, ARegion *ar, SpaceNode *snode, b } if ((snode->nodetree->flag & NTREE_VIEWER_BORDER) && - viewer_border->xmin < viewer_border->xmax && - viewer_border->ymin < viewer_border->ymax) + viewer_border->xmin < viewer_border->xmax && + viewer_border->ymin < viewer_border->ymax) { - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - setlinestyle(3); - cpack(0x4040FF); - - glRectf(x + snode->zoom * viewer_border->xmin * ibuf->x, - y + snode->zoom * viewer_border->ymin * ibuf->y, - x + snode->zoom * viewer_border->xmax * ibuf->x, - y + snode->zoom * viewer_border->ymax * ibuf->y); - - setlinestyle(0); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + rcti pixel_border; + UI_ThemeColor(TH_ACTIVE); + BLI_rcti_init(&pixel_border, + x + snode->zoom * viewer_border->xmin * ibuf->x, + x + snode->zoom * viewer_border->xmax * ibuf->x, + y + snode->zoom * viewer_border->ymin * ibuf->y, + y + snode->zoom * viewer_border->ymax * ibuf->y); + glaDrawBorderCorners(&pixel_border, 1.0f, 1.0f); } } @@ -3421,7 +3444,7 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) { bool do_shaded = false; bool do_triple = false; - int th_col1 = TH_HEADER, th_col2 = TH_HEADER, th_col3 = TH_WIRE; + int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; if (link->fromsock == NULL && link->tosock == NULL) return; diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index ebff840dc9f..58d94a28226 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -202,8 +202,8 @@ void node_buttons_register(ARegionType *art) strcpy(pt->idname, "NODE_PT_gpencil"); strcpy(pt->label, N_("Grease Pencil")); strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw_header = gpencil_panel_standard_header; - pt->draw = gpencil_panel_standard; + pt->draw_header = ED_gpencil_panel_standard_header; + pt->draw = ED_gpencil_panel_standard; pt->poll = active_nodetree_poll; BLI_addtail(&art->paneltypes, pt); } diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index b3cf5ce0c81..7b5ec38f4c6 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -138,6 +138,10 @@ void ED_node_tag_update_id(ID *id) DAG_id_tag_update(id, 0); WM_main_add_notifier(NC_TEXTURE | ND_NODES, id); } + else if (id == &ntree->id) { + /* node groups */ + DAG_id_tag_update(id, 0); + } } void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree) @@ -1341,7 +1345,7 @@ void drawnodespace(const bContext *C, ARegion *ar) if (snode->flag & SNODE_SHOW_GPENCIL) { /* draw grease-pencil ('canvas' strokes) */ - draw_gpencil_view2d(C, true); + ED_gpencil_draw_view2d(C, true); } } else { @@ -1360,7 +1364,7 @@ void drawnodespace(const bContext *C, ARegion *ar) if (snode->treepath.last) { if (snode->flag & SNODE_SHOW_GPENCIL) { /* draw grease-pencil (screen strokes, and also paintbuffer) */ - draw_gpencil_view2d(C, false); + ED_gpencil_draw_view2d(C, false); } } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index fc0c82faba8..ca13d87d632 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -640,11 +640,11 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) /* tree specific activate calls */ if (ntree->type == NTREE_SHADER) { /* when we select a material, active texture is cleared, for buttons */ - if (node->id && ELEM3(GS(node->id->name), ID_MA, ID_LA, ID_WO)) + if (node->id && ELEM(GS(node->id->name), ID_MA, ID_LA, ID_WO)) nodeClearActiveID(ntree, ID_TE); - if (ELEM4(node->type, SH_NODE_OUTPUT, SH_NODE_OUTPUT_MATERIAL, - SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LAMP)) + if (ELEM(node->type, SH_NODE_OUTPUT, SH_NODE_OUTPUT_MATERIAL, + SH_NODE_OUTPUT_WORLD, SH_NODE_OUTPUT_LAMP, SH_NODE_OUTPUT_LINESTYLE)) { bNode *tnode; @@ -695,7 +695,12 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) for (scene = bmain->scene.first; scene; scene = scene->id.next) { if (scene->nodetree && scene->use_nodes && ntreeHasTree(scene->nodetree, ntree)) { if (node->id == NULL || node->id == (ID *)scene) { + int num_layers = BLI_countlist(&scene->r.layers); scene->r.actlay = node->custom1; + /* Clamp the value, because it might have come from a different + * scene which could have more render layers than new one. + */ + scene->r.actlay = min_ff(scene->r.actlay, num_layers - 1); } } } @@ -2477,14 +2482,6 @@ static int viewer_border_exec(bContext *C, wmOperator *op) btree->flag &= ~NTREE_VIEWER_BORDER; } else { - if (ibuf->rect) - memset(ibuf->rect, 0, 4 * ibuf->x * ibuf->y); - - if (ibuf->rect_float) - memset(ibuf->rect_float, 0, 4 * ibuf->x * ibuf->y * sizeof(float)); - - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - btree->flag |= NTREE_VIEWER_BORDER; } @@ -2521,3 +2518,30 @@ void NODE_OT_viewer_border(wmOperatorType *ot) /* properties */ WM_operator_properties_gesture_border(ot, true); } + +static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->nodetree; + + btree->flag &= ~NTREE_VIEWER_BORDER; + snode_notify(C, snode); + WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, NULL); + + return OPERATOR_FINISHED; +} + +void NODE_OT_clear_viewer_border(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Viewer Border"; + ot->description = "Clear the boundaries for viewer operations"; + ot->idname = "NODE_OT_clear_viewer_border"; + + /* api callbacks */ + ot->exec = clear_viewer_border_exec; + ot->poll = composite_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index 39aba921f72..96cc7fb4407 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -200,7 +200,7 @@ static int node_group_ungroup(bNodeTree *ntree, bNode *gnode) * - ngroup (i.e. the source NodeTree) is left unscathed * - temp copy. don't change ID usercount */ - wgroup = ntreeCopyTree_ex(ngroup, false); + wgroup = ntreeCopyTree_ex(ngroup, G.main, false); /* Add the nodes into the ntree */ for (node = wgroup->nodes.first; node; node = nextnode) { diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index fa20aeb8624..9598ff190c0 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -107,8 +107,6 @@ void node_socket_select(struct bNode *node, struct bNodeSocket *sock); void node_socket_deselect(struct bNode *node, struct bNodeSocket *sock, const bool deselect_node); void node_deselect_all_input_sockets(struct SpaceNode *snode, const bool deselect_nodes); void node_deselect_all_output_sockets(struct SpaceNode *snode, const bool deselect_nodes); -int node_select_same_type(struct SpaceNode *snode); -int node_select_same_type_np(struct SpaceNode *snode, int dir); void node_select_single(struct bContext *C, struct bNode *node); void NODE_OT_select(struct wmOperatorType *ot); @@ -118,7 +116,7 @@ void NODE_OT_select_linked_from(struct wmOperatorType *ot); void NODE_OT_select_border(struct wmOperatorType *ot); void NODE_OT_select_circle(struct wmOperatorType *ot); void NODE_OT_select_lasso(struct wmOperatorType *ot); -void NODE_OT_select_same_type(struct wmOperatorType *ot); +void NODE_OT_select_grouped(struct wmOperatorType *ot); void NODE_OT_select_same_type_step(struct wmOperatorType *ot); void NODE_OT_find_node(struct wmOperatorType *ot); @@ -217,6 +215,7 @@ void NODE_OT_tree_socket_move(struct wmOperatorType *ot); void NODE_OT_shader_script_update(struct wmOperatorType *ot); void NODE_OT_viewer_border(struct wmOperatorType *ot); +void NODE_OT_clear_viewer_border(struct wmOperatorType *ot); extern const char *node_context_dir[]; diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index ac541ef6a28..7dcbeae4627 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -58,7 +58,7 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_select_border); WM_operatortype_append(NODE_OT_select_circle); WM_operatortype_append(NODE_OT_select_lasso); - WM_operatortype_append(NODE_OT_select_same_type); + WM_operatortype_append(NODE_OT_select_grouped); WM_operatortype_append(NODE_OT_select_same_type_step); WM_operatortype_append(NODE_OT_find_node); @@ -122,6 +122,7 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_shader_script_update); WM_operatortype_append(NODE_OT_viewer_border); + WM_operatortype_append(NODE_OT_clear_viewer_border); WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_remove); @@ -292,7 +293,10 @@ void node_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "NODE_OT_select_linked_to", LKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "NODE_OT_select_linked_from", LKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "NODE_OT_select_same_type", GKEY, KM_PRESS, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "NODE_OT_select_grouped", GKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "extend", false); + kmi = WM_keymap_add_item(keymap, "NODE_OT_select_grouped", GKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "extend", true); kmi = WM_keymap_add_item(keymap, "NODE_OT_select_same_type_step", RIGHTBRACKETKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "prev", false); @@ -321,6 +325,7 @@ void node_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "NODE_OT_clipboard_paste", VKEY, KM_PRESS, KM_OSKEY, 0); #endif WM_keymap_add_item(keymap, "NODE_OT_viewer_border", BKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "NODE_OT_clear_viewer_border", BKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); transform_keymap_for_space(keyconf, keymap, SPACE_NODE); } diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 6a0e1f35d24..973ce56857c 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -67,12 +67,12 @@ typedef struct bNodeListItem { struct bNode *node; } bNodeListItem; -static int sort_nodes_locx(void *a, void *b) +static int sort_nodes_locx(const void *a, const void *b) { - bNodeListItem *nli1 = (bNodeListItem *)a; - bNodeListItem *nli2 = (bNodeListItem *)b; - bNode *node1 = nli1->node; - bNode *node2 = nli2->node; + const bNodeListItem *nli1 = a; + const bNodeListItem *nli2 = b; + const bNode *node1 = nli1->node; + const bNode *node2 = nli2->node; if (node1->locx > node2->locx) return 1; @@ -461,7 +461,7 @@ static bNodeSocket *node_find_linkable_socket(bNodeTree *ntree, bNode *node, bNo sock = cur->next ? cur->next : first; /* wrap around the list end */ while (sock != cur) { - if (node_link_socket_match(sock, cur)) { + if (!nodeSocketIsHidden(sock) && node_link_socket_match(sock, cur)) { int link_count = node_count_links(ntree, sock); /* take +1 into account since we would add a new link */ if (link_count + 1 <= sock->limit) @@ -490,7 +490,6 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link, bool use_ if (new_from && new_from != from) { /* redirect existing link */ tlink->fromsock = new_from; - new_from->flag &= ~SOCK_HIDDEN; } else if (!new_from) { /* no possible replacement, remove tlink */ @@ -504,7 +503,6 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link, bool use_ if (new_to && new_to != to) { /* redirect existing link */ tlink->tosock = new_to; - new_to->flag &= ~SOCK_HIDDEN; } else if (!new_to) { /* no possible replacement, remove tlink */ diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 7371a3ef070..de580f612a0 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -32,10 +32,12 @@ #include "DNA_node_types.h" +#include "BLI_utildefines.h" #include "BLI_rect.h" #include "BLI_lasso.h" +#include "BLI_math.h" #include "BLI_string.h" -#include "BLI_utildefines.h" +#include "BLI_string_utf8.h" #include "BKE_context.h" #include "BKE_main.h" @@ -205,79 +207,156 @@ void node_deselect_all_output_sockets(SpaceNode *snode, const bool deselect_node } } -/* return 1 if we need redraw otherwise zero. */ -int node_select_same_type(SpaceNode *snode) +/* Return true if we need redraw, otherwise false. */ + +static bool node_select_grouped_type(SpaceNode *snode, bNode *node_act) { - bNode *nac, *p; - int redraw; + bNode *node; + bool changed = false; - /* search for the active node. */ - for (nac = snode->edittree->nodes.first; nac; nac = nac->next) { - if (nac->flag & SELECT) - break; + for (node = snode->edittree->nodes.first; node; node = node->next) { + if ((node->flag & SELECT) == 0) { + if (node->type == node_act->type) { + nodeSetSelected(node, true); + changed = true; + } + } } - /* no active node, return. */ - if (!nac) - return(0); + return changed; +} - redraw = 0; - for (p = snode->edittree->nodes.first; p; p = p->next) { - if (p->type != nac->type && p->flag & SELECT) { - /* if it's selected but different type, unselect */ - redraw = 1; - nodeSetSelected(p, false); - } - else if (p->type == nac->type && (!(p->flag & SELECT))) { - /* if it's the same type and is not selected, select! */ - redraw = 1; - nodeSetSelected(p, true); +static bool node_select_grouped_color(SpaceNode *snode, bNode *node_act) +{ + bNode *node; + bool changed = false; + + for (node = snode->edittree->nodes.first; node; node = node->next) { + if ((node->flag & SELECT) == 0) { + if (compare_v3v3(node->color, node_act->color, 0.005f)) { + nodeSetSelected(node, true); + changed = true; + } } } - return(redraw); + + return changed; } -/* return 1 if we need redraw, otherwise zero. - * dir can be 0 == next or 0 != prev. - */ -int node_select_same_type_np(SpaceNode *snode, int dir) +static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bool from_right) { - bNode *nac, *p, *tnode; + bNode *node; + bool changed = false; + const unsigned int delims[] = {'.', '-', '_', '\0'}; + size_t pref_len_act, pref_len_curr; + char *sep, *suf_act, *suf_curr; - /* search the active one. */ - for (nac = snode->edittree->nodes.first; nac; nac = nac->next) { - if (nac->flag & SELECT) - break; + pref_len_act = BLI_str_partition_ex_utf8(node_act->name, delims, &sep, &suf_act, from_right); + + /* Note: in case we are searching for suffix, and found none, use whole name as suffix. */ + if (from_right && !(sep && suf_act)) { + pref_len_act = 0; + suf_act = node_act->name; } - /* no active node, return. */ - if (!nac) - return(0); + for (node = snode->edittree->nodes.first; node; node = node->next) { + if (node->flag & SELECT) { + continue; + } + pref_len_curr = BLI_str_partition_ex_utf8(node->name, delims, &sep, &suf_curr, from_right); - if (dir == 0) - p = nac->next; - else - p = nac->prev; + /* Same as with active node name! */ + if (from_right && !(sep && suf_curr)) { + pref_len_curr = 0; + suf_curr = node->name; + } - while (p) { - /* Now search the next with the same type. */ - if (p->type == nac->type) - break; + if ((from_right && STREQ(suf_act, suf_curr)) || + (!from_right && (pref_len_act == pref_len_curr) && STREQLEN(node_act->name, node->name, pref_len_act))) + { + nodeSetSelected(node, true); + changed = true; + } + } - if (dir == 0) - p = p->next; - else - p = p->prev; + return changed; +} + +enum { + NODE_SELECT_GROUPED_TYPE = 0, + NODE_SELECT_GROUPED_COLOR = 1, + NODE_SELECT_GROUPED_PREFIX = 2, + NODE_SELECT_GROUPED_SUFIX = 3, +}; + +static int node_select_grouped_exec(bContext *C, wmOperator *op) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node_act = nodeGetActive(snode->edittree); + bNode *node; + bool changed = false; + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const int type = RNA_enum_get(op->ptr, "type"); + + if (!extend) { + for (node = snode->edittree->nodes.first; node; node = node->next) { + nodeSetSelected(node, false); + } } + nodeSetSelected(node_act, true); - if (p) { - for (tnode = snode->edittree->nodes.first; tnode; tnode = tnode->next) - if (tnode != p) - nodeSetSelected(tnode, false); - nodeSetSelected(p, true); - return(1); + switch (type) { + case NODE_SELECT_GROUPED_TYPE: + changed = node_select_grouped_type(snode, node_act); + break; + case NODE_SELECT_GROUPED_COLOR: + changed = node_select_grouped_color(snode, node_act); + break; + case NODE_SELECT_GROUPED_PREFIX: + changed = node_select_grouped_name(snode, node_act, false); + break; + case NODE_SELECT_GROUPED_SUFIX: + changed = node_select_grouped_name(snode, node_act, true); + break; + default: + break; } - return(0); + + if (changed) { + ED_node_sort(snode->edittree); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void NODE_OT_select_grouped(wmOperatorType *ot) +{ + static EnumPropertyItem prop_select_grouped_types[] = { + {NODE_SELECT_GROUPED_TYPE, "TYPE", 0, "Type", ""}, + {NODE_SELECT_GROUPED_COLOR, "COLOR", 0, "Color", ""}, + {NODE_SELECT_GROUPED_PREFIX, "PREFIX", 0, "Prefix", ""}, + {NODE_SELECT_GROUPED_SUFIX, "SUFFIX", 0, "Suffix", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Select Grouped"; + ot->description = "Select nodes with similar properties"; + ot->idname = "NODE_OT_select_grouped"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = node_select_grouped_exec; + ot->poll = ED_operator_node_active; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection instead of deselecting everything first"); + ot->prop = RNA_def_enum(ot->srna, "type", prop_select_grouped_types, 0, "Type", ""); } void node_select_single(bContext *C, bNode *node) @@ -563,7 +642,7 @@ void NODE_OT_select_circle(wmOperatorType *ot) /* rna */ RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX); RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX); } @@ -787,40 +866,6 @@ void NODE_OT_select_linked_from(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****** Select Same Type ****** */ - -static int node_select_same_type_exec(bContext *C, wmOperator *UNUSED(op)) -{ - SpaceNode *snode = CTX_wm_space_node(C); - - node_select_same_type(snode); - - ED_node_sort(snode->edittree); - - WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); - return OPERATOR_FINISHED; -} - -void NODE_OT_select_same_type(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Select Same Type"; - ot->description = "Select all the nodes of the same type"; - ot->idname = "NODE_OT_select_same_type"; - - /* api callbacks */ - ot->exec = node_select_same_type_exec; - ot->poll = ED_operator_node_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ****** Select The Next/Prev Node Of The Same Type ****** */ - -/* ************************** */ - - static int node_select_same_type_step_exec(bContext *C, wmOperator *op) { SpaceNode *snode = CTX_wm_space_node(C); @@ -971,7 +1016,6 @@ static uiBlock *node_find_menu(bContext *C, ARegion *ar, void *arg_op) uiDefBut(block, LABEL, 0, "", 10, 10 - uiSearchBoxHeight(), uiSearchBoxWidth(), uiSearchBoxHeight(), NULL, 0, 0, 0, 0, NULL); uiPopupBoundsBlock(block, 6, 0, -UI_UNIT_Y); /* move it downwards, mouse over button */ - uiEndBlock(C, block); // uiButActiveOnly(C, ar, block, but); XXX using this here makes Blender hang - investigate wm_event_init_from_window(win, &event); diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 44639b1295c..99a481e5d82 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -511,6 +511,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_ bNodeSocket *sock = arg->sock; bNodeTreeType *ntreetype = arg->ntree->typeinfo; + uiBlockSetFlag(block, UI_BLOCK_NO_FLIP); uiBlockSetCurLayout(block, layout); split = uiLayoutSplit(layout, 0.0f, false); diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index ddfbe3bebf2..ccaeae34927 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -420,6 +420,9 @@ static void node_area_listener(bScreen *sc, ScrArea *sa, wmNotifier *wmn) } } break; + case ND_LAYER_CONTENT: + ED_area_tag_refresh(sa); + break; } break; @@ -501,6 +504,17 @@ static void node_area_listener(bScreen *sc, ScrArea *sa, wmNotifier *wmn) } } break; + + case NC_LINESTYLE: + if (ED_node_is_shader(snode) && shader_type == SNODE_SHADER_LINESTYLE) { + ED_area_tag_refresh(sa); + } + break; + case NC_WM: + if (wmn->data == ND_UNDO) { + ED_area_tag_refresh(sa); + } + break; } } @@ -740,6 +754,7 @@ static void node_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegi case NC_TEXTURE: case NC_WORLD: case NC_NODE: + case NC_LINESTYLE: ED_region_tag_redraw(ar); break; case NC_OBJECT: diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 4db47b75502..b716f0671bd 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenlib ../../blenfont ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -50,4 +52,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_outliner "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_outliner/SConscript b/source/blender/editors/space_outliner/SConscript index db8041f0235..cc02c9555a9 100644 --- a/source/blender/editors/space_outliner/SConscript +++ b/source/blender/editors/space_outliner/SConscript @@ -28,15 +28,18 @@ Import ('env') sources = env.Glob('*.c') -defs = [] + +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 1f728b380d6..e033f781f62 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -29,6 +29,7 @@ * \ingroup spoutliner */ +#include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" @@ -46,6 +47,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_depsgraph.h" +#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" @@ -55,6 +57,7 @@ #include "BKE_object.h" #include "ED_armature.h" +#include "ED_keyframing.h" #include "ED_object.h" #include "ED_screen.h" @@ -168,7 +171,7 @@ static void restrictbutton_recursive_bone(bContext *C, bArmature *arm, Bone *bon } static void restrictbutton_recursive_child(bContext *C, Scene *scene, Object *ob_parent, char flag, - bool state, bool deselect) + bool state, bool deselect, const char *rnapropname) { Main *bmain = CTX_data_main(C); Object *ob; @@ -183,6 +186,33 @@ static void restrictbutton_recursive_child(bContext *C, Scene *scene, Object *ob else { ob->restrictflag &= ~flag; } + + if (rnapropname) { + PointerRNA ptr; + PropertyRNA *prop; + ID *id; + bAction *action; + FCurve *fcu; + bool driven; + + RNA_id_pointer_create(&ob->id, &ptr); + prop = RNA_struct_find_property(&ptr, rnapropname); + fcu = rna_get_fcurve_context_ui(C, &ptr, prop, 0, &action, &driven); + + if (fcu && !driven) { + id = ptr.id.data; + if (autokeyframe_cfra_can_key(scene, id)) { + ReportList *reports = CTX_wm_reports(C); + short flag = ANIM_get_keyframing_flags(scene, 1); + + fcu->flag &= ~FCURVE_SELECTED; + insert_keyframe(reports, id, action, ((fcu->grp) ? (fcu->grp->name) : (NULL)), + fcu->rna_path, fcu->array_index, CFRA, flag); + /* Assuming this is not necessary here, since 'ancestor' object button will do it anyway. */ + /* WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); */ + } + } + } } } } @@ -203,7 +233,7 @@ static void restrictbutton_view_cb(bContext *C, void *poin, void *poin2) if (CTX_wm_window(C)->eventstate->ctrl) { restrictbutton_recursive_child(C, scene, ob, OB_RESTRICT_VIEW, - (ob->restrictflag & OB_RESTRICT_VIEW) != 0, true); + (ob->restrictflag & OB_RESTRICT_VIEW) != 0, true, "hide"); } WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -226,7 +256,7 @@ static void restrictbutton_sel_cb(bContext *C, void *poin, void *poin2) if (CTX_wm_window(C)->eventstate->ctrl) { restrictbutton_recursive_child(C, scene, ob, OB_RESTRICT_SELECT, - (ob->restrictflag & OB_RESTRICT_SELECT) != 0, true); + (ob->restrictflag & OB_RESTRICT_SELECT) != 0, true, NULL); } WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -239,7 +269,7 @@ static void restrictbutton_rend_cb(bContext *C, void *poin, void *poin2) if (CTX_wm_window(C)->eventstate->ctrl) { restrictbutton_recursive_child(C, (Scene *)poin, ob, OB_RESTRICT_RENDER, - (ob->restrictflag & OB_RESTRICT_RENDER) != 0, false); + (ob->restrictflag & OB_RESTRICT_RENDER) != 0, false, "hide_render"); } WM_event_add_notifier(C, NC_SCENE | ND_OB_RENDER, poin); @@ -638,7 +668,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, Scene *scene, ARegion *ar uiButSetFlag(bt, UI_BUT_DRAG_LOCK); layflag++; /* is lay_xor */ - if (ELEM8(passflag, SCE_PASS_SPEC, SCE_PASS_SHADOW, SCE_PASS_AO, SCE_PASS_REFLECT, SCE_PASS_REFRACT, + if (ELEM(passflag, SCE_PASS_SPEC, SCE_PASS_SHADOW, SCE_PASS_AO, SCE_PASS_REFLECT, SCE_PASS_REFRACT, SCE_PASS_INDIRECT, SCE_PASS_EMIT, SCE_PASS_ENVIRONMENT)) { bt = uiDefIconButBitI(block, TOG, passflag, 0, (*layflag & passflag) ? ICON_DOT : ICON_BLANK1, diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 17e1e032bbf..ef621407abd 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -215,15 +215,15 @@ void OUTLINER_OT_item_openclose(wmOperatorType *ot) static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem, ReportList *reports) { /* can't rename rna datablocks entries or listbases */ - if (ELEM4(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) { + if (ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM, TSE_ID_BASE)) { /* do nothing */; } - else if (ELEM10(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, - TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) + else if (ELEM(tselem->type, TSE_ANIM_DATA, TSE_NLA, TSE_DEFGROUP_BASE, TSE_CONSTRAINT_BASE, TSE_MODIFIER_BASE, + TSE_DRIVER_BASE, TSE_POSE_BASE, TSE_POSEGRP_BASE, TSE_R_LAYER_BASE, TSE_R_PASS)) { BKE_report(reports, RPT_WARNING, "Cannot edit builtin name"); } - else if (ELEM3(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { + 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) { @@ -1654,7 +1654,7 @@ static int outliner_parenting_poll(bContext *C) SpaceOops *soops = CTX_wm_space_outliner(C); if (soops) { - return ELEM4(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS); + return ELEM(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS); } return false; diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index d7521edd57a..6f5bf712d55 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -948,7 +948,7 @@ static bool do_outliner_item_activate(bContext *C, Scene *scene, ARegion *ar, Sp WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); } - else if (ELEM5(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) { + else if (ELEM(te->idcode, ID_ME, ID_CU, ID_MB, ID_LT, ID_AR)) { WM_operator_name_call(C, "OBJECT_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); } else { // rest of types diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 8f5e2ad3693..d09ed1a100e 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -459,6 +459,13 @@ void outliner_do_object_operation(bContext *C, Scene *scene_act, SpaceOops *soop /* ******************************************** */ +static void clear_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), + TreeStoreElem *tselem, void *UNUSED(arg)) +{ + BKE_free_animdata(tselem->id); +} + + static void unlinkact_animdata_cb(int UNUSED(event), TreeElement *UNUSED(te), TreeStoreElem *tselem, void *UNUSED(arg)) { @@ -1076,6 +1083,8 @@ void OUTLINER_OT_action_set(wmOperatorType *ot) typedef enum eOutliner_AnimDataOps { OUTLINER_ANIMOP_INVALID = 0, + OUTLINER_ANIMOP_CLEAR_ADT, + OUTLINER_ANIMOP_SET_ACT, OUTLINER_ANIMOP_CLEAR_ACT, @@ -1087,6 +1096,7 @@ typedef enum eOutliner_AnimDataOps { } eOutliner_AnimDataOps; static EnumPropertyItem prop_animdata_op_types[] = { + {OUTLINER_ANIMOP_CLEAR_ADT, "CLEAR_ANIMDATA", 0, "Clear Animation Data", "Remove this animation data container"}, {OUTLINER_ANIMOP_SET_ACT, "SET_ACT", 0, "Set Action", ""}, {OUTLINER_ANIMOP_CLEAR_ACT, "CLEAR_ACT", 0, "Unlink Action", ""}, {OUTLINER_ANIMOP_REFRESH_DRV, "REFRESH_DRIVERS", 0, "Refresh Drivers", ""}, @@ -1115,6 +1125,14 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) /* perform the core operation */ switch (event) { + case OUTLINER_ANIMOP_CLEAR_ADT: + /* Remove Animation Data - this may remove the active action, in some cases... */ + outliner_do_data_operation(soops, datalevel, event, &soops->tree, clear_animdata_cb, NULL); + + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + ED_undo_push(C, "Clear Animation Data"); + break; + case OUTLINER_ANIMOP_SET_ACT: /* delegate once again... */ WM_operator_name_call(C, "OUTLINER_OT_action_set", WM_OP_INVOKE_REGION_WIN, NULL); @@ -1321,7 +1339,7 @@ static int do_outliner_operation_event(bContext *C, Scene *scene, ARegion *ar, S else if (datalevel == TSE_DRIVER_BASE) { /* do nothing... no special ops needed yet */ } - else if (ELEM3(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) { + else if (ELEM(datalevel, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)) { /*WM_operator_name_call(C, "OUTLINER_OT_renderdata_operation", WM_OP_INVOKE_REGION_WIN, NULL)*/ } else { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 520cd9a544d..5801dd126e3 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -821,7 +821,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i TreeStoreElem *tselem; ID *id = idv; - if (ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { + if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { id = ((PointerRNA *)idv)->id.data; if (!id) id = ((PointerRNA *)idv)->data; } @@ -847,10 +847,10 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i te->parent = parent; te->index = index; // for data arays - if (ELEM3(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { + if (ELEM(type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) { /* pass */ } - else if (ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { + else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { /* pass */ } else if (type == TSE_ANIM_DATA) { @@ -985,7 +985,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i te->directdata = seq; te->name = seq->strip->stripdata->name; } - else if (ELEM3(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { + else if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { PointerRNA pptr, propptr, *ptr = (PointerRNA *)idv; PropertyRNA *prop, *iterprop; PropertyType proptype; @@ -1062,7 +1062,7 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i else if (tot) te->flag |= TE_LAZY_CLOSED; } - else if (ELEM3(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { + else if (ELEM(proptype, PROP_BOOLEAN, PROP_INT, PROP_FLOAT)) { tot = RNA_property_array_length(ptr, prop); if (TSELEM_OPEN(tselem, soops)) { diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index e25e67a9f3e..504d9628d98 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -143,7 +143,7 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); - if (!ELEM4(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS)) { + if (!ELEM(soops->outlinevis, SO_ALL_SCENES, SO_CUR_SCENE, SO_VISIBLE, SO_GROUPS)) { return false; } @@ -156,7 +156,7 @@ static int outliner_parent_clear_poll(bContext *C, wmDrag *drag, const wmEvent * switch (te->idcode) { case ID_SCE: - return (ELEM3(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)); + return (ELEM(tselem->type, TSE_R_LAYER_BASE, TSE_R_LAYER, TSE_R_PASS)); case ID_OB: return (ELEM(tselem->type, TSE_MODIFIER_BASE, TSE_CONSTRAINT_BASE)); /* Other codes to ignore? */ @@ -345,6 +345,7 @@ static void outliner_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa) case NC_GEOM: switch (wmn->data) { case ND_VERTEX_GROUP: + case ND_DATA: ED_region_tag_redraw(ar); break; } diff --git a/source/blender/editors/space_script/CMakeLists.txt b/source/blender/editors/space_script/CMakeLists.txt index 26c4183f7df..2eb31576c57 100644 --- a/source/blender/editors/space_script/CMakeLists.txt +++ b/source/blender/editors/space_script/CMakeLists.txt @@ -22,10 +22,12 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -47,4 +49,6 @@ if(WITH_PYTHON) add_definitions(-DWITH_PYTHON) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_script "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_script/SConscript b/source/blender/editors/space_script/SConscript index acb0bf9f20b..e749780a4aa 100644 --- a/source/blender/editors/space_script/SConscript +++ b/source/blender/editors/space_script/SConscript @@ -31,10 +31,12 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', @@ -42,7 +44,7 @@ incs = [ '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_PYTHON']: defs.append('WITH_PYTHON') diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index db5729a5762..4cf9c0c95c2 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenkernel ../../blenlib ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -60,4 +62,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_sequencer "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_sequencer/SConscript b/source/blender/editors/space_sequencer/SConscript index 5aae30244a7..954f8abff69 100644 --- a/source/blender/editors/space_sequencer/SConscript +++ b/source/blender/editors/space_sequencer/SConscript @@ -31,19 +31,21 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/audaspace/intern', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc', 'win64-mingw'): incs.append(env['BF_PTHREADS_INC']) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 8fe9dbe3eb9..345988c1d5c 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -226,6 +226,32 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, wmOperator *op) } } +/** + * Apply generic operator options. + */ +static void sequencer_add_apply_overlap(bContext *C, wmOperator *op, Sequence *seq) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, false); + + if (RNA_boolean_get(op->ptr, "overlap") == false) { + if (BKE_sequence_test_overlap(ed->seqbasep, seq)) { + BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); + } + } +} + +static void sequencer_add_apply_replace_sel(bContext *C, wmOperator *op, Sequence *seq) +{ + Scene *scene = CTX_data_scene(C); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + BKE_sequencer_active_set(scene, seq); + seq->flag |= SELECT; + } +} + /* add scene operator */ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) { @@ -268,15 +294,8 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) BKE_sequence_calc_disp(scene, seq); BKE_sequencer_sort(scene); - if (RNA_boolean_get(op->ptr, "replace_sel")) { - ED_sequencer_deselect_all(scene); - BKE_sequencer_active_set(scene, seq); - seq->flag |= SELECT; - } - - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); - } + sequencer_add_apply_replace_sel(C, op, seq); + sequencer_add_apply_overlap(C, op, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -363,15 +382,8 @@ static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) BKE_sequence_calc_disp(scene, seq); BKE_sequencer_sort(scene); - if (RNA_boolean_get(op->ptr, "replace_sel")) { - ED_sequencer_deselect_all(scene); - BKE_sequencer_active_set(scene, seq); - seq->flag |= SELECT; - } - - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); - } + sequencer_add_apply_replace_sel(C, op, seq); + sequencer_add_apply_overlap(C, op, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -455,15 +467,8 @@ static int sequencer_add_mask_strip_exec(bContext *C, wmOperator *op) BKE_sequence_calc_disp(scene, seq); BKE_sequencer_sort(scene); - if (RNA_boolean_get(op->ptr, "replace_sel")) { - ED_sequencer_deselect_all(scene); - BKE_sequencer_active_set(scene, seq); - seq->flag |= SELECT; - } - - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); - } + sequencer_add_apply_replace_sel(C, op, seq); + sequencer_add_apply_overlap(C, op, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -513,9 +518,7 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad Scene *scene = CTX_data_scene(C); /* only for sound */ Editing *ed = BKE_sequencer_editing_get(scene, true); SeqLoadInfo seq_load; - Sequence *seq; int tot_files; - const bool overlap = RNA_boolean_get(op->ptr, "overlap"); seq_load_operator_info(&seq_load, op); @@ -536,6 +539,8 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad RNA_BEGIN (op->ptr, itemptr, "files") { + Sequence *seq; + RNA_string_get(&itemptr, "name", file_only); BLI_join_dirfile(seq_load.path, sizeof(seq_load.path), dir_only, file_only); @@ -544,21 +549,23 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad seq = seq_load_func(C, ed->seqbasep, &seq_load); if (seq) { - if (overlap == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) - BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); + sequencer_add_apply_overlap(C, op, seq); + if (seq_load.seq_sound) { + sequencer_add_apply_overlap(C, op, seq_load.seq_sound); } } } RNA_END; } else { + Sequence *seq; + /* single file */ seq = seq_load_func(C, ed->seqbasep, &seq_load); if (seq) { - if (overlap == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) - BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); + sequencer_add_apply_overlap(C, op, seq); + if (seq_load.seq_sound) { + sequencer_add_apply_overlap(C, op, seq_load.seq_sound); } } } @@ -728,10 +735,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) /* last active name */ BLI_strncpy(ed->act_imagedir, strip->dir, sizeof(ed->act_imagedir)); - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) - BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); - } + sequencer_add_apply_overlap(C, op, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -859,9 +863,8 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) } } - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (BKE_sequence_test_overlap(ed->seqbasep, seq)) BKE_sequence_base_shuffle(ed->seqbasep, seq, scene); - } + sequencer_add_apply_replace_sel(C, op, seq); + sequencer_add_apply_overlap(C, op, seq); BKE_sequencer_update_changed_seq_and_deps(scene, seq, 1, 1); /* runs BKE_sequence_calc */ @@ -870,12 +873,6 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) * it was NOT called in blender 2.4x, but wont hurt */ BKE_sequencer_sort(scene); - if (RNA_boolean_get(op->ptr, "replace_sel")) { - ED_sequencer_deselect_all(scene); - BKE_sequencer_active_set(scene, seq); - seq->flag |= SELECT; - } - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 36589984c78..d75eeca2c67 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -68,8 +68,8 @@ void sequencer_buttons_register(ARegionType *art) strcpy(pt->idname, "SEQUENCER_PT_gpencil"); strcpy(pt->label, N_("Grease Pencil")); strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw_header = gpencil_panel_standard_header; - pt->draw = gpencil_panel_standard; + pt->draw_header = ED_gpencil_panel_standard_header; + pt->draw = ED_gpencil_panel_standard; pt->poll = sequencer_grease_pencil_panel_poll; BLI_addtail(&art->paneltypes, pt); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index e2830fca4bb..f9ca713ad2d 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -142,19 +142,21 @@ static void get_seq_color3ubv(Scene *curscene, Sequence *seq, unsigned char col[ case SEQ_TYPE_GLOW: case SEQ_TYPE_MULTICAM: case SEQ_TYPE_ADJUSTMENT: + case SEQ_TYPE_GAUSSIAN_BLUR: UI_GetThemeColor3ubv(TH_SEQ_EFFECT, col); /* slightly offset hue to distinguish different effects */ - if (seq->type == SEQ_TYPE_ADD) rgb_byte_set_hue_float_offset(col, 0.04); - else if (seq->type == SEQ_TYPE_SUB) rgb_byte_set_hue_float_offset(col, 0.08); - else if (seq->type == SEQ_TYPE_MUL) rgb_byte_set_hue_float_offset(col, 0.12); - else if (seq->type == SEQ_TYPE_ALPHAOVER) rgb_byte_set_hue_float_offset(col, 0.16); - else if (seq->type == SEQ_TYPE_ALPHAUNDER) rgb_byte_set_hue_float_offset(col, 0.20); - else if (seq->type == SEQ_TYPE_OVERDROP) rgb_byte_set_hue_float_offset(col, 0.24); - else if (seq->type == SEQ_TYPE_GLOW) rgb_byte_set_hue_float_offset(col, 0.28); - else if (seq->type == SEQ_TYPE_TRANSFORM) rgb_byte_set_hue_float_offset(col, 0.36); - else if (seq->type == SEQ_TYPE_MULTICAM) rgb_byte_set_hue_float_offset(col, 0.32); - else if (seq->type == SEQ_TYPE_ADJUSTMENT) rgb_byte_set_hue_float_offset(col, 0.40); + if (seq->type == SEQ_TYPE_ADD) rgb_byte_set_hue_float_offset(col, 0.04); + else if (seq->type == SEQ_TYPE_SUB) rgb_byte_set_hue_float_offset(col, 0.08); + else if (seq->type == SEQ_TYPE_MUL) rgb_byte_set_hue_float_offset(col, 0.12); + else if (seq->type == SEQ_TYPE_ALPHAOVER) rgb_byte_set_hue_float_offset(col, 0.16); + else if (seq->type == SEQ_TYPE_ALPHAUNDER) rgb_byte_set_hue_float_offset(col, 0.20); + else if (seq->type == SEQ_TYPE_OVERDROP) rgb_byte_set_hue_float_offset(col, 0.24); + else if (seq->type == SEQ_TYPE_GLOW) rgb_byte_set_hue_float_offset(col, 0.28); + else if (seq->type == SEQ_TYPE_TRANSFORM) rgb_byte_set_hue_float_offset(col, 0.36); + else if (seq->type == SEQ_TYPE_MULTICAM) rgb_byte_set_hue_float_offset(col, 0.32); + else if (seq->type == SEQ_TYPE_ADJUSTMENT) rgb_byte_set_hue_float_offset(col, 0.40); + else if (seq->type == SEQ_TYPE_GAUSSIAN_BLUR) rgb_byte_set_hue_float_offset(col, 0.42); break; case SEQ_TYPE_COLOR: @@ -833,6 +835,9 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int short is_break = G.is_break; render_size = sseq->render_size; + if (render_size == 99) { + render_size = 100; + } if (render_size == 0) { render_size = scene->r.size; } @@ -1233,7 +1238,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq if (sseq->flag & SEQ_SHOW_GPENCIL) { if (is_imbuf) { /* draw grease-pencil (image aligned) */ - draw_gpencil_2dimage(C); + ED_gpencil_draw_2dimage(C); } } @@ -1246,7 +1251,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq if (sseq->flag & SEQ_SHOW_GPENCIL) { if (is_imbuf) { /* draw grease-pencil (screen aligned) */ - draw_gpencil_view2d(C, 0); + ED_gpencil_draw_view2d(C, 0); } } @@ -1387,16 +1392,20 @@ static void draw_seq_strips(const bContext *C, Editing *ed, ARegion *ar) } static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) -{ +{ + const Editing *ed = BKE_sequencer_editing_get(scene, false); + const int frame_sta = PSFRA; + const int frame_end = PEFRA + 1; + glEnable(GL_BLEND); /* draw darkened area outside of active timeline * frame range used is preview range or scene range */ UI_ThemeColorShadeAlpha(TH_BACK, -25, -100); - if (PSFRA < PEFRA + 1) { - glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax); - glRectf((float)(PEFRA + 1), v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); + if (frame_sta < frame_end) { + glRectf(v2d->cur.xmin, v2d->cur.ymin, (float)frame_sta, v2d->cur.ymax); + glRectf((float)frame_end, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); } else { glRectf(v2d->cur.xmin, v2d->cur.ymin, v2d->cur.xmax, v2d->cur.ymax); @@ -1404,9 +1413,21 @@ static void seq_draw_sfra_efra(Scene *scene, View2D *v2d) UI_ThemeColorShade(TH_BACK, -60); /* thin lines where the actual frames are */ - fdrawline((float)PSFRA, v2d->cur.ymin, (float)PSFRA, v2d->cur.ymax); - fdrawline((float)(PEFRA + 1), v2d->cur.ymin, (float)(PEFRA + 1), v2d->cur.ymax); - + fdrawline(frame_sta, v2d->cur.ymin, frame_sta, v2d->cur.ymax); + fdrawline(frame_end, v2d->cur.ymin, frame_end, v2d->cur.ymax); + + if (ed && !BLI_listbase_is_empty(&ed->metastack)) { + MetaStack *ms = ed->metastack.last; + + glColor4ub(255, 255, 255, 8); + glRectf(ms->disp_range[0], v2d->cur.ymin, ms->disp_range[1], v2d->cur.ymax); + + UI_ThemeColorShade(TH_BACK, -40); + + fdrawline(ms->disp_range[0], v2d->cur.ymin, ms->disp_range[0], v2d->cur.ymax); + fdrawline(ms->disp_range[1], v2d->cur.ymin, ms->disp_range[1], v2d->cur.ymax); + } + glDisable(GL_BLEND); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 50efa881b39..01259bb688f 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -86,6 +86,7 @@ EnumPropertyItem sequencer_prop_effect_types[] = { {SEQ_TYPE_SPEED, "SPEED", 0, "Speed", "Color effect strip type"}, {SEQ_TYPE_MULTICAM, "MULTICAM", 0, "Multicam Selector", ""}, {SEQ_TYPE_ADJUSTMENT, "ADJUSTMENT", 0, "Adjustment Layer", ""}, + {SEQ_TYPE_GAUSSIAN_BLUR, "GAUSSIAN_BLUR", 0, "Gaussian Blur", ""}, {0, NULL, 0, NULL, NULL} }; @@ -1230,6 +1231,358 @@ void SEQUENCER_OT_snap(struct wmOperatorType *ot) RNA_def_int(ot->srna, "frame", 0, INT_MIN, INT_MAX, "Frame", "Frame where selected strips will be snapped", INT_MIN, INT_MAX); } + +typedef struct TrimData { + int init_mouse[2]; + float init_mouseloc[2]; + TransSeq *ts; + Sequence **seq_array; + bool *trim; + int num_seq; + bool slow; + int slow_offset; /* offset at the point where offset was turned on */ +} TrimData; + +static void transseq_backup(TransSeq *ts, Sequence *seq) +{ + ts->start = seq->start; + ts->machine = seq->machine; + ts->startstill = seq->startstill; + ts->endstill = seq->endstill; + ts->startdisp = seq->startdisp; + ts->enddisp = seq->enddisp; + ts->startofs = seq->startofs; + ts->endofs = seq->endofs; + ts->anim_startofs = seq->anim_startofs; + ts->anim_endofs = seq->anim_endofs; + ts->len = seq->len; +} + + +static void transseq_restore(TransSeq *ts, Sequence *seq) +{ + seq->start = ts->start; + seq->machine = ts->machine; + seq->startstill = ts->startstill; + seq->endstill = ts->endstill; + seq->startdisp = ts->startdisp; + seq->enddisp = ts->enddisp; + seq->startofs = ts->startofs; + seq->endofs = ts->endofs; + seq->anim_startofs = ts->anim_startofs; + seq->anim_endofs = ts->anim_endofs; + seq->len = ts->len; +} + +static int trim_add_sequences_rec(ListBase *seqbasep, Sequence **seq_array, bool *trim, int offset, bool first_level) +{ + Sequence *seq; + int num_items = 0; + + for (seq = seqbasep->first; seq; seq = seq->next) { + if (!first_level || (!(seq->type & SEQ_TYPE_EFFECT) && (seq->flag & SELECT))) { + seq_array[offset + num_items] = seq; + trim[offset + num_items] = first_level; + num_items++; + + if (seq->type == SEQ_TYPE_META) { + /* trim the sub-sequences */ + num_items += trim_add_sequences_rec(&seq->seqbase, seq_array, trim, num_items + offset, false); + } + else if (seq->type & SEQ_TYPE_EFFECT) { + trim[offset + num_items] = false; + } + } + } + + return num_items; +} + +static int trim_count_sequences_rec(ListBase *seqbasep, bool first_level) +{ + Sequence *seq; + int trimmed_sequences = 0; + + for (seq = seqbasep->first; seq; seq = seq->next) { + if (!first_level || (!(seq->type & SEQ_TYPE_EFFECT) && (seq->flag & SELECT))) { + trimmed_sequences++; + + if (seq->type == SEQ_TYPE_META) { + /* trim the sub-sequences */ + trimmed_sequences += trim_count_sequences_rec(&seq->seqbase, false); + } + } + } + + return trimmed_sequences; +} + +static int sequencer_trim_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + TrimData *data; + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, false); + float mouseloc[2]; + int num_seq, i; + View2D *v2d = UI_view2d_fromcontext(C); + + /* first recursively cound the trimmed elements */ + num_seq = trim_count_sequences_rec(ed->seqbasep, true); + + if (num_seq == 0) + return OPERATOR_CANCELLED; + + data = op->customdata = MEM_mallocN(sizeof(TrimData), "trimdata"); + data->ts = MEM_mallocN(num_seq * sizeof(TransSeq), "trimdata_transform"); + data->seq_array = MEM_mallocN(num_seq * sizeof(Sequence *), "trimdata_sequences"); + data->trim = MEM_mallocN(num_seq * sizeof(bool), "trimdata_trim"); + data->num_seq = num_seq; + + trim_add_sequences_rec(ed->seqbasep, data->seq_array, data->trim, 0, true); + + for (i = 0; i < num_seq; i++) { + transseq_backup(data->ts + i, data->seq_array[i]); + } + + UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &mouseloc[0], &mouseloc[1]); + + copy_v2_v2_int(data->init_mouse, event->mval); + copy_v2_v2(data->init_mouseloc, mouseloc); + + data->slow = false; + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static bool sequencer_trim_recursively(Scene *scene, TrimData *data, int offset) +{ + + /* only data types supported for now */ + if (offset != 0) { + Editing *ed = BKE_sequencer_editing_get(scene, false); + int i; + + /* we iterate in reverse so metastrips are iterated after their children */ + for (i = data->num_seq - 1; i >= 0; i--) { + Sequence *seq = data->seq_array[i]; + int endframe; + /* we have the offset, do the terrible math */ + + /* first, do the offset */ + seq->start = data->ts[i].start + offset; + + if (data->trim[i]) { + /* find the endframe */ + endframe = seq->start + seq->len; + + /* now compute the terrible offsets */ + if (endframe > seq->enddisp) { + seq->endstill = 0; + seq->endofs = endframe - seq->enddisp; + } + else if (endframe <= seq->enddisp) { + seq->endstill = seq->enddisp - endframe; + seq->endofs = 0; + } + + if (seq->start > seq->startdisp) { + seq->startstill = seq->start - seq->startdisp; + seq->startofs = 0; + } + else if (seq->start <= seq->startdisp) { + seq->startstill = 0; + seq->startofs = seq->startdisp - seq->start; + } + } + else { + /* if no real trim, don't change the data, rather transform the strips themselves */ + seq->startdisp = data->ts[i].startdisp + offset; + seq->enddisp = data->ts[i].enddisp + offset; + } + + /* effects are only added if we they are in a metastrip. In this case, dependent strips will just be transformed and we can skip calculating for effects + * This way we can avoid an extra loop just for effects*/ + if (!(seq->type & SEQ_TYPE_EFFECT)) + BKE_sequence_calc(scene, seq); + } + BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); + + return true; + } + + return false; +} + +static int sequencer_trim_exec(bContext *C, wmOperator *op) +{ + TrimData *data; + Scene *scene = CTX_data_scene(C); + Editing *ed = BKE_sequencer_editing_get(scene, false); + int num_seq, i; + int offset = RNA_int_get(op->ptr, "offset"); + bool success = false; + + /* first recursively cound the trimmed elements */ + num_seq = trim_count_sequences_rec(ed->seqbasep, true); + + if (num_seq == 0) + return OPERATOR_CANCELLED; + + data = op->customdata = MEM_mallocN(sizeof(TrimData), "trimdata"); + data->ts = MEM_mallocN(num_seq * sizeof(TransSeq), "trimdata_transform"); + data->seq_array = MEM_mallocN(num_seq * sizeof(Sequence *), "trimdata_sequences"); + data->trim = MEM_mallocN(num_seq * sizeof(bool), "trimdata_trim"); + data->num_seq = num_seq; + + trim_add_sequences_rec(ed->seqbasep, data->seq_array, data->trim, 0, true); + + for (i = 0; i < num_seq; i++) { + transseq_backup(data->ts + i, data->seq_array[i]); + } + + success = sequencer_trim_recursively(scene, data, offset); + + MEM_freeN(data->seq_array); + MEM_freeN(data->trim); + MEM_freeN(data->ts); + MEM_freeN(data); + + if (success) return OPERATOR_FINISHED; + else return OPERATOR_CANCELLED; +} + +static int sequencer_trim_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Scene *scene = CTX_data_scene(C); + TrimData *data = (TrimData *)op->customdata; + ScrArea *sa = CTX_wm_area(C); + + switch (event->type) { + case MOUSEMOVE: + { + float mouseloc[2]; + int offset; + int mouse_x; + View2D *v2d = UI_view2d_fromcontext(C); + + if (data->slow) { + mouse_x = event->mval[0] - data->slow_offset; + mouse_x *= 0.1f; + mouse_x += data->slow_offset; + } + else { + mouse_x = event->mval[0]; + } + + + /* choose the side based on which side of the playhead the mouse is on */ + UI_view2d_region_to_view(v2d, mouse_x, 0, &mouseloc[0], &mouseloc[1]); + offset = mouseloc[0] - data->init_mouseloc[0]; + + RNA_int_set(op->ptr, "offset", offset); + + if (sa) { +#define HEADER_LENGTH 40 + char msg[HEADER_LENGTH]; + BLI_snprintf(msg, HEADER_LENGTH, "Trim offset: %d", offset); +#undef HEADER_LENGTH + ED_area_headerprint(sa, msg); + } + + if (sequencer_trim_recursively(scene, data, offset)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + } + break; + } + + case LEFTMOUSE: + { + MEM_freeN(data->seq_array); + MEM_freeN(data->trim); + MEM_freeN(data->ts); + MEM_freeN(data); + op->customdata = NULL; + if (sa) { + ED_area_headerprint(sa, NULL); + } + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; + } + + case ESCKEY: + case RIGHTMOUSE: + { + int i; + Editing *ed = BKE_sequencer_editing_get(scene, false); + + for (i = 0; i < data->num_seq; i++) { + transseq_restore(data->ts + i, data->seq_array[i]); + } + + for (i = 0; i < data->num_seq; i++) { + Sequence *seq = data->seq_array[i]; + BKE_sequence_reload_new_file(scene, seq, false); + BKE_sequence_calc(scene, seq); + } + + MEM_freeN(data->seq_array); + MEM_freeN(data->ts); + MEM_freeN(data->trim); + MEM_freeN(data); + op->customdata = NULL; + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); + + if (sa) { + ED_area_headerprint(sa, NULL); + } + + return OPERATOR_CANCELLED; + } + + case RIGHTSHIFTKEY: + case LEFTSHIFTKEY: + if (event->val == KM_PRESS) { + data->slow = true; + data->slow_offset = event->mval[0]; + } + else if (event->val == KM_RELEASE) { + data->slow = false; + } + break; + + default: + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +void SEQUENCER_OT_trim(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Trim Strips"; + ot->idname = "SEQUENCER_OT_trim"; + ot->description = "Trim the contents of the active strip"; + + /* api callbacks */ + ot->invoke = sequencer_trim_invoke; + ot->modal = sequencer_trim_modal; + ot->exec = sequencer_trim_exec; + ot->poll = sequencer_edit_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_int(ot->srna, "offset", 0, INT32_MIN, INT32_MAX, "Offset", "Offset to the data of the strip", + INT32_MIN, INT32_MAX); +} + + /* mute operator */ static int sequencer_mute_exec(bContext *C, wmOperator *op) { @@ -1918,9 +2271,12 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) strip_new = seq_new->strip; strip_new->us = 1; - /* new stripdata */ - se_new = strip_new->stripdata; + /* new stripdata (only one element now!) */ + /* Note this assume all elements (images) have the same dimension, since we only copy the name here. */ + se_new = MEM_reallocN(strip_new->stripdata, sizeof(*se_new)); BLI_strncpy(se_new->name, se->name, sizeof(se_new->name)); + strip_new->stripdata = se_new; + BKE_sequence_calc(scene, seq_new); if (step > 1) { @@ -1989,6 +2345,7 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) BLI_addtail(&ed->metastack, ms); ms->parseq = last_seq; ms->oldbasep = ed->seqbasep; + copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp); ed->seqbasep = &last_seq->seqbase; @@ -2008,12 +2365,25 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) ed->seqbasep = ms->oldbasep; + /* for old files, update from meta */ + if (ms->disp_range[0] == ms->disp_range[1]) { + copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp); + } + /* recalc all: the meta can have effects connected to it */ for (seq = ed->seqbasep->first; seq; seq = seq->next) BKE_sequence_calc(scene, seq); + /* 2.73+, keeping endpoings is important! + * moving them around means you can't usefully use metas in a complex edit */ +#if 1 + BKE_sequence_tx_set_final_left(ms->parseq, ms->disp_range[0]); + BKE_sequence_tx_set_final_right(ms->parseq, ms->disp_range[1]); + BKE_sequence_calc(scene, ms->parseq); +#else if (BKE_sequence_test_overlap(ed->seqbasep, ms->parseq)) BKE_sequence_base_shuffle(ed->seqbasep, ms->parseq, scene); +#endif BKE_sequencer_active_set(scene, ms->parseq); @@ -2055,8 +2425,8 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) Sequence *seq, *seqm, *next, *last_seq = BKE_sequencer_active_get(scene); int channel_max = 1; - if (BKE_sequence_base_isolated_sel_check(ed->seqbasep) == false) { - BKE_report(op->reports, RPT_ERROR, "Please select all related strips"); + if (BKE_sequence_base_isolated_sel_check(ed->seqbasep, false) == false) { + BKE_report(op->reports, RPT_ERROR, "Please select more than one or all related strips"); return OPERATOR_CANCELLED; } @@ -2071,6 +2441,7 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) while (seq) { next = seq->next; if (seq != seqm && (seq->flag & SELECT)) { + BKE_sequence_invalidate_cache(scene, seq); channel_max = max_ii(seq->machine, channel_max); BLI_remlink(ed->seqbasep, seq); BLI_addtail(&seqm->seqbase, seq); @@ -2132,6 +2503,10 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) if (last_seq == NULL || last_seq->type != SEQ_TYPE_META) return OPERATOR_CANCELLED; + for (seq = last_seq->seqbase.first; seq != NULL; seq = seq->next) { + BKE_sequence_invalidate_cache(scene, seq); + } + BLI_movelisttolist(ed->seqbasep, &last_seq->seqbase); BLI_listbase_clear(&last_seq->seqbase); @@ -2436,73 +2811,59 @@ static int find_next_prev_edit(Scene *scene, int cfra, const bool do_skip_mute, const bool do_center) { Editing *ed = BKE_sequencer_editing_get(scene, false); - Sequence *seq, *best_seq = NULL, *frame_seq = NULL; + Sequence *seq; - int dist, best_dist; + int dist, best_dist, best_frame = cfra; + int seq_frames[2], seq_frames_tot; + best_dist = MAXFRAME * 2; if (ed == NULL) return cfra; for (seq = ed->seqbasep->first; seq; seq = seq->next) { - int seq_frame; + int i; if (do_skip_mute && (seq->flag & SEQ_MUTE)) { continue; } if (do_center) { - seq_frame = (seq->startdisp + seq->enddisp) / 2; + seq_frames[0] = (seq->startdisp + seq->enddisp) / 2; + seq_frames_tot = 1; } else { - seq_frame = seq->startdisp; - } + seq_frames[0] = seq->startdisp; + seq_frames[1] = seq->enddisp; - dist = MAXFRAME * 2; - - switch (side) { - case SEQ_SIDE_LEFT: - if (seq_frame < cfra) { - dist = cfra - seq_frame; - } - break; - case SEQ_SIDE_RIGHT: - if (seq_frame > cfra) { - dist = seq_frame - cfra; - } - else if (seq_frame == cfra) { - frame_seq = seq; - } - break; + seq_frames_tot = 2; } - if (dist < best_dist) { - best_dist = dist; - best_seq = seq; - } - } + for (i = 0; i < seq_frames_tot; i++) { + const int seq_frame = seq_frames[i]; - /* if no sequence to the right is found and the - * frame is on the start of the last sequence, - * move to the end of the last sequence */ - if (frame_seq) { - if (do_center) { - cfra = (frame_seq->startdisp + frame_seq->enddisp) / 2; - } - else { - cfra = frame_seq->enddisp; - } - } + dist = MAXFRAME * 2; - if (best_seq) { - if (do_center) { - cfra = (best_seq->startdisp + best_seq->enddisp) / 2; - } - else { - cfra = best_seq->startdisp; + switch (side) { + case SEQ_SIDE_LEFT: + if (seq_frame < cfra) { + dist = cfra - seq_frame; + } + break; + case SEQ_SIDE_RIGHT: + if (seq_frame > cfra) { + dist = seq_frame - cfra; + } + break; + } + + if (dist < best_dist) { + best_frame = seq_frame; + best_dist = dist; + } } } - return cfra; + return best_frame; } static bool strip_jump_internal(Scene *scene, @@ -2758,7 +3119,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op) BKE_sequencer_free_clipboard(); - if (BKE_sequence_base_isolated_sel_check(ed->seqbasep) == false) { + if (BKE_sequence_base_isolated_sel_check(ed->seqbasep, true) == false) { BKE_report(op->reports, RPT_ERROR, "Please select all related strips"); return OPERATOR_CANCELLED; } @@ -2795,9 +3156,7 @@ static int sequencer_copy_exec(bContext *C, wmOperator *op) } /* duplicate pointers */ - for (seq = seqbase_clipboard.first; seq; seq = seq->next) { - BKE_sequence_clipboard_pointers_store(seq); - } + BKE_sequencer_base_clipboard_pointers_store(&seqbase_clipboard); } return OPERATOR_FINISHED; @@ -2841,9 +3200,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *UNUSED(op)) } } - for (iseq = nseqbase.first; iseq; iseq = iseq->next) { - BKE_sequence_clipboard_pointers_restore(iseq, bmain); - } + BKE_sequencer_base_clipboard_pointers_restore(&nseqbase, bmain); for (iseq = nseqbase.first; iseq; iseq = iseq->next) { BKE_sequence_sound_init(scene, iseq); diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 60fc300da1f..4fada72d0d9 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -84,6 +84,7 @@ struct wmOperatorType; struct wmKeyConfig; void SEQUENCER_OT_cut(struct wmOperatorType *ot); +void SEQUENCER_OT_trim(struct wmOperatorType *ot); void SEQUENCER_OT_mute(struct wmOperatorType *ot); void SEQUENCER_OT_unmute(struct wmOperatorType *ot); void SEQUENCER_OT_lock(struct wmOperatorType *ot); @@ -165,6 +166,13 @@ enum { SEQ_UNSELECTED }; +enum { + SEQ_SELECT_LR_NONE = 0, + SEQ_SELECT_LR_MOUSE, + SEQ_SELECT_LR_LEFT, + SEQ_SELECT_LR_RIGHT +}; + /* defines used internally */ #define SCE_MARKERS 0 // XXX - dummy diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index e69a02aa3df..dff8f69509e 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -50,6 +50,7 @@ void sequencer_operatortypes(void) { /* sequencer_edit.c */ WM_operatortype_append(SEQUENCER_OT_cut); + WM_operatortype_append(SEQUENCER_OT_trim); WM_operatortype_append(SEQUENCER_OT_mute); WM_operatortype_append(SEQUENCER_OT_unmute); WM_operatortype_append(SEQUENCER_OT_lock); @@ -237,12 +238,12 @@ void sequencer_keymap(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "linked_handle", false); - RNA_boolean_set(kmi->ptr, "left_right", false); + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_NONE); RNA_boolean_set(kmi->ptr, "linked_time", false); kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "linked_handle", false); - RNA_boolean_set(kmi->ptr, "left_right", false); + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_NONE); RNA_boolean_set(kmi->ptr, "linked_time", false); @@ -275,27 +276,27 @@ void sequencer_keymap(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "linked_handle", true); - RNA_boolean_set(kmi->ptr, "left_right", false); + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_NONE); RNA_boolean_set(kmi->ptr, "linked_time", false); kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "linked_handle", true); - RNA_boolean_set(kmi->ptr, "left_right", false); + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_NONE); RNA_boolean_set(kmi->ptr, "linked_time", false); /* match action editor */ kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "extend", false); RNA_boolean_set(kmi->ptr, "linked_handle", false); - RNA_boolean_set(kmi->ptr, "left_right", true); /* grr, these conflict - only use left_right if not over an active seq */ + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_MOUSE); /* grr, these conflict - only use left_right if not over an active seq */ RNA_boolean_set(kmi->ptr, "linked_time", true); /* adjusted since 2.4 */ kmi = WM_keymap_add_item(keymap, "SEQUENCER_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "linked_handle", false); - RNA_boolean_set(kmi->ptr, "left_right", false); + RNA_enum_set(kmi->ptr, "left_right", SEQ_SELECT_LR_NONE); RNA_boolean_set(kmi->ptr, "linked_time", true); WM_keymap_add_item(keymap, "SEQUENCER_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0); @@ -316,6 +317,8 @@ void sequencer_keymap(wmKeyConfig *keyconf) WM_keymap_add_menu(keymap, "SEQUENCER_MT_change", CKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "SEQUENCER_OT_trim", TKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_int", OKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "scene.sequence_editor.overlay_frame"); RNA_int_set(kmi->ptr, "value", 0); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 9826ef10902..933daf4adee 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -314,7 +314,7 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e const bool extend = RNA_boolean_get(op->ptr, "extend"); const bool linked_handle = RNA_boolean_get(op->ptr, "linked_handle"); const bool linked_time = RNA_boolean_get(op->ptr, "linked_time"); - bool left_right = RNA_boolean_get(op->ptr, "left_right"); + int left_right = RNA_enum_get(op->ptr, "left_right"); Sequence *seq, *neighbor, *act_orig; int hand, sel_side; @@ -328,8 +328,8 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e seq = find_nearest_seq(scene, v2d, &hand, event->mval); // XXX - not nice, Ctrl+RMB needs to do left_right only when not over a strip - if (seq && linked_time && left_right) - left_right = false; + if (seq && linked_time && (left_right == SEQ_SELECT_LR_MOUSE)) + left_right = SEQ_SELECT_LR_NONE; if (marker) { @@ -348,12 +348,24 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e } } - else if (left_right) { + else if (left_right != SEQ_SELECT_LR_NONE) { /* use different logic for this */ float x; ED_sequencer_deselect_all(scene); - x = UI_view2d_region_to_view_x(v2d, event->mval[0]); - + + switch (left_right) { + case SEQ_SELECT_LR_MOUSE: + x = UI_view2d_region_to_view_x(v2d, event->mval[0]); + break; + + case SEQ_SELECT_LR_LEFT: + x = CFRA - 1; + break; + case SEQ_SELECT_LR_RIGHT: + x = CFRA + 1; + break; + } + SEQP_BEGIN (ed, seq) { if (x < CFRA) { @@ -522,6 +534,14 @@ static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *e void SEQUENCER_OT_select(wmOperatorType *ot) { + static EnumPropertyItem sequencer_select_left_right_types[] = { + {SEQ_SELECT_LR_NONE, "NONE", 0, "None", "Don't do left-right selection"}, + {SEQ_SELECT_LR_MOUSE, "MOUSE", 0, "Mouse", "Use mouse position for selection"}, + {SEQ_SELECT_LR_LEFT, "LEFT", 0, "Left", "Select left"}, + {SEQ_SELECT_LR_RIGHT, "RIGHT", 0, "Right", "Select right"}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Activate/Select"; ot->idname = "SEQUENCER_OT_select"; @@ -538,7 +558,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection"); RNA_def_boolean(ot->srna, "linked_handle", 0, "Linked Handle", "Select handles next to the active strip"); /* for animation this is an enum but atm having an enum isn't useful for us */ - RNA_def_boolean(ot->srna, "left_right", 0, "Left/Right", "Select based on the current frame side the cursor is on"); + RNA_def_enum(ot->srna, "left_right", sequencer_select_left_right_types, 0, "Left/Right", "Select based on the current frame side the cursor is on"); RNA_def_boolean(ot->srna, "linked_time", 0, "Linked Time", "Select other strips at the same time"); } @@ -912,7 +932,7 @@ static EnumPropertyItem sequencer_prop_select_grouped_types[] = { #define SEQ_IS_EFFECT(_seq) ((_seq->type & SEQ_TYPE_EFFECT) != 0) -#define SEQ_USE_DATA(_seq) (ELEM3(_seq->type, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP, SEQ_TYPE_MASK) || SEQ_HAS_PATH(_seq)) +#define SEQ_USE_DATA(_seq) (ELEM(_seq->type, SEQ_TYPE_SCENE, SEQ_TYPE_MOVIECLIP, SEQ_TYPE_MASK) || SEQ_HAS_PATH(_seq)) static bool select_grouped_type(Editing *ed, Sequence *actseq) { @@ -1035,7 +1055,7 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq) SEQP_BEGIN (ed, seq) { - if (ELEM3(actseq, seq->seq1, seq->seq2, seq->seq3)) { + if (ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index a94b73802b2..c0cfaed7867 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -279,24 +279,40 @@ static void sequencer_refresh(const bContext *C, ScrArea *sa) } break; case SEQ_VIEW_SEQUENCE_PREVIEW: - if (ar_main && (ar_main->flag & RGN_FLAG_HIDDEN)) { - ar_main->flag &= ~RGN_FLAG_HIDDEN; - ar_main->v2d.flag &= ~V2D_IS_INITIALISED; - view_changed = true; - } - if (ar_preview && (ar_preview->flag & RGN_FLAG_HIDDEN)) { - ar_preview->flag &= ~RGN_FLAG_HIDDEN; - ar_preview->v2d.flag &= ~V2D_IS_INITIALISED; - ar_preview->v2d.cur = ar_preview->v2d.tot; - view_changed = true; - } - if (ar_main && ar_main->alignment != RGN_ALIGN_NONE) { - ar_main->alignment = RGN_ALIGN_NONE; - view_changed = true; - } - if (ar_preview && ar_preview->alignment != RGN_ALIGN_TOP) { - ar_preview->alignment = RGN_ALIGN_TOP; - view_changed = true; + if (ar_main && ar_preview) { + /* Get available height (without DPI correction). */ + const float height = (sa->winy - ED_area_headersize()) / UI_DPI_FAC; + + /* We reuse hidden area's size, allows to find same layout as before if we just switch + * between one 'full window' view and the combined one. This gets lost if we switch to both + * 'full window' views before, though... Better than nothing. */ + if (ar_main->flag & RGN_FLAG_HIDDEN) { + ar_main->flag &= ~RGN_FLAG_HIDDEN; + ar_main->v2d.flag &= ~V2D_IS_INITIALISED; + ar_preview->sizey = (int)(height - ar_main->sizey); + view_changed = true; + } + if (ar_preview->flag & RGN_FLAG_HIDDEN) { + ar_preview->flag &= ~RGN_FLAG_HIDDEN; + ar_preview->v2d.flag &= ~V2D_IS_INITIALISED; + ar_preview->v2d.cur = ar_preview->v2d.tot; + ar_main->sizey = (int)(height - ar_preview->sizey); + view_changed = true; + } + if (ar_main->alignment != RGN_ALIGN_NONE) { + ar_main->alignment = RGN_ALIGN_NONE; + view_changed = true; + } + if (ar_preview->alignment != RGN_ALIGN_TOP) { + ar_preview->alignment = RGN_ALIGN_TOP; + view_changed = true; + } + /* Final check that both preview and main height are reasonable! */ + if (ar_preview->sizey < 10 || ar_main->sizey < 10 || ar_preview->sizey + ar_main->sizey > height) { + ar_preview->sizey = (int)(height * 0.4f + 0.5f); + ar_main->sizey = (int)(height - ar_preview->sizey); + view_changed = true; + } } break; } @@ -339,40 +355,6 @@ static void sequencer_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn } } -/* *********************** sequencer (main) region ************************ */ -/* add handlers, stuff you only do once or on area/region changes */ -static void sequencer_main_area_init(wmWindowManager *wm, ARegion *ar) -{ - wmKeyMap *keymap; - ListBase *lb; - - UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy); - -// keymap = WM_keymap_find(wm->defaultconf, "Mask Editing", 0, 0); -// WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); - - keymap = WM_keymap_find(wm->defaultconf, "SequencerCommon", SPACE_SEQ, 0); - WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); - - /* own keymap */ - keymap = WM_keymap_find(wm->defaultconf, "Sequencer", SPACE_SEQ, 0); - WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); - - /* add drop boxes */ - lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); - - WM_event_add_dropbox_handler(&ar->handlers, lb); - -} - -static void sequencer_main_area_draw(const bContext *C, ARegion *ar) -{ -// ScrArea *sa = CTX_wm_area(C); - - /* NLE - strip editing timeline interface */ - draw_timeline_seq(C, ar); -} - /* ************* dropboxes ************* */ static int image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -396,7 +378,7 @@ static int movie_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) int hand; if (drag->type == WM_DRAG_PATH) - if (ELEM3(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) /* rule might not work? */ + if (ELEM(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) /* rule might not work? */ if (find_nearest_seq(scene, &ar->v2d, &hand, event->mval) == NULL) return 1; return 0; @@ -439,7 +421,7 @@ static void sequencer_drop_copy(wmDrag *drag, wmDropBox *drop) static void sequencer_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); - + WM_dropbox_add(lb, "SEQUENCER_OT_image_strip_add", image_drop_poll, sequencer_drop_copy); WM_dropbox_add(lb, "SEQUENCER_OT_movie_strip_add", movie_drop_poll, sequencer_drop_copy); WM_dropbox_add(lb, "SEQUENCER_OT_sound_strip_add", sound_drop_poll, sequencer_drop_copy); @@ -469,16 +451,37 @@ static int sequencer_context(const bContext *C, const char *member, bContextData return false; } - +/* *********************** sequencer (main) region ************************ */ /* add handlers, stuff you only do once or on area/region changes */ -static void sequencer_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar) +static void sequencer_main_area_init(wmWindowManager *wm, ARegion *ar) { - ED_region_header_init(ar); + wmKeyMap *keymap; + ListBase *lb; + + UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy); + +#if 0 + keymap = WM_keymap_find(wm->defaultconf, "Mask Editing", 0, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); +#endif + + keymap = WM_keymap_find(wm->defaultconf, "SequencerCommon", SPACE_SEQ, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + /* own keymap */ + keymap = WM_keymap_find(wm->defaultconf, "Sequencer", SPACE_SEQ, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + + /* add drop boxes */ + lb = WM_dropboxmap_find("Sequencer", SPACE_SEQ, RGN_TYPE_WINDOW); + + WM_event_add_dropbox_handler(&ar->handlers, lb); } -static void sequencer_header_area_draw(const bContext *C, ARegion *ar) +static void sequencer_main_area_draw(const bContext *C, ARegion *ar) { - ED_region_header(C, ar); + /* NLE - strip editing timeline interface */ + draw_timeline_seq(C, ar); } static void sequencer_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa), ARegion *ar, wmNotifier *wmn) @@ -512,15 +515,29 @@ static void sequencer_main_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa } } +/* *********************** header region ************************ */ +/* add handlers, stuff you only do once or on area/region changes */ +static void sequencer_header_area_init(wmWindowManager *UNUSED(wm), ARegion *ar) +{ + ED_region_header_init(ar); +} + +static void sequencer_header_area_draw(const bContext *C, ARegion *ar) +{ + ED_region_header(C, ar); +} + /* *********************** preview region ************************ */ static void sequencer_preview_area_init(wmWindowManager *wm, ARegion *ar) { wmKeyMap *keymap; UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_CUSTOM, ar->winx, ar->winy); - -// keymap = WM_keymap_find(wm->defaultconf, "Mask Editing", 0, 0); -// WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); + +#if 0 + keymap = WM_keymap_find(wm->defaultconf, "Mask Editing", 0, 0); + WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); +#endif keymap = WM_keymap_find(wm->defaultconf, "SequencerCommon", SPACE_SEQ, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); @@ -593,7 +610,6 @@ static void sequencer_preview_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED break; } break; - case NC_MASK: if (wmn->action == NA_EDITED) { ED_region_tag_redraw(ar); @@ -654,10 +670,10 @@ void ED_spacetype_sequencer(void) { SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype sequencer"); ARegionType *art; - + st->spaceid = SPACE_SEQ; strncpy(st->name, "Sequencer", BKE_ST_MAXNAME); - + st->new = sequencer_new; st->free = sequencer_free; st->init = sequencer_init; @@ -682,13 +698,12 @@ void ED_spacetype_sequencer(void) /* preview */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); art->regionid = RGN_TYPE_PREVIEW; - art->prefsizey = 240; // XXX art->init = sequencer_preview_area_init; art->draw = sequencer_preview_area_draw; art->listener = sequencer_preview_area_listener; art->keymapflag = ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_GPENCIL; BLI_addhead(&st->regiontypes, art); - + /* regions: listview/buttons */ art = MEM_callocN(sizeof(ARegionType), "spacetype sequencer region"); art->regionid = RGN_TYPE_UI; @@ -698,7 +713,7 @@ void ED_spacetype_sequencer(void) art->init = sequencer_buttons_area_init; art->draw = sequencer_buttons_area_draw; BLI_addhead(&st->regiontypes, art); - + sequencer_buttons_register(art); /* regions: header */ @@ -706,13 +721,13 @@ void ED_spacetype_sequencer(void) art->regionid = RGN_TYPE_HEADER; art->prefsizey = HEADERY; art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; - + art->init = sequencer_header_area_init; art->draw = sequencer_header_area_draw; art->listener = sequencer_main_area_listener; - + BLI_addhead(&st->regiontypes, art); - + BKE_spacetype_register(st); /* set the sequencer callback when not in background mode */ diff --git a/source/blender/editors/space_text/CMakeLists.txt b/source/blender/editors/space_text/CMakeLists.txt index bfeeb93372a..5367efbf84b 100644 --- a/source/blender/editors/space_text/CMakeLists.txt +++ b/source/blender/editors/space_text/CMakeLists.txt @@ -23,10 +23,12 @@ set(INC ../../blenfont ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -48,6 +50,8 @@ set(SRC text_intern.h ) +add_definitions(${GL_DEFINITIONS}) + if(WITH_PYTHON) list(APPEND INC ../../python diff --git a/source/blender/editors/space_text/SConscript b/source/blender/editors/space_text/SConscript index 3f617f95c58..8ee9bd745dd 100644 --- a/source/blender/editors/space_text/SConscript +++ b/source/blender/editors/space_text/SConscript @@ -28,14 +28,18 @@ Import ('env') sources = env.Glob('*.c') -defs = [] + +defs = env['BF_GL_DEFINITIONS'] + incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', + '../../gpu', '../../imbuf', '../../makesdna', '../../makesrna', diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 6e49569314f..1e710b88cad 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -462,10 +462,13 @@ static void text_cursor(wmWindow *win, ScrArea *sa, ARegion *ar) static int text_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *UNUSED(event)) { - if (drag->type == WM_DRAG_PATH) - if (ELEM(drag->icon, ICON_FILE_SCRIPT, ICON_FILE_BLANK)) /* rule might not work? */ - return 1; - return 0; + if (drag->type == WM_DRAG_PATH) { + /* rule might not work? */ + if (ELEM(drag->icon, ICON_FILE_SCRIPT, ICON_FILE_TEXT, ICON_FILE_BLANK)) { + return true; + } + } + return false; } static void text_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -484,7 +487,13 @@ static int text_drop_paste_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent static void text_drop_paste(wmDrag *drag, wmDropBox *drop) { - RNA_string_set(drop->ptr, "text", ((ID *)drag->poin)->name + 2); + char *text; + ID *id = drag->poin; + + /* copy drag path to properties */ + text = RNA_path_full_ID_py(id); + RNA_string_set(drop->ptr, "text", text); + MEM_freeN(text); } /* this region dropbox definition */ diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 6a98fd802f9..af827d6dc5a 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -373,9 +373,11 @@ static const char *txt_utf8_forward_columns(const char *str, int columns, int *p static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w, const char *format, int skip) { + const bool use_syntax = (st->showsyntax && format); FlattenString fs; int basex, lines; int i, wrap, end, max, columns, padding; /* column */ + /* warning, only valid when 'use_syntax' is set */ int a, fstart, fpos; /* utf8 chars */ int mi, ma, mstart, mend; /* mem */ char fmt_prev = 0xff; @@ -397,6 +399,10 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w /* skip hidden part of line */ if (skip) { skip--; + if (use_syntax) { + /* currently fpos only used when formatting */ + fpos += BLI_strnlen_utf8(str + mstart, mend - mstart); + } fstart = fpos; mstart = mend; mend = txt_utf8_forward_columns(str + mend, max, &padding) - str; end = (wrap += max - padding); @@ -405,7 +411,7 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w /* Draw the visible portion of text on the overshot line */ for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) { - if (st->showsyntax && format) { + if (use_syntax) { if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]); } x += text_font_draw_character_utf8(st, x, y, str + ma); @@ -427,7 +433,7 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w /* Draw the remaining text */ for (a = fstart, ma = mstart; str[ma] && y > 0; a++, ma += BLI_str_utf8_size_safe(str + ma)) { - if (st->showsyntax && format) { + if (use_syntax) { if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]); } @@ -441,6 +447,7 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x, int y, const char *format) { + const bool use_syntax = (st->showsyntax && format); FlattenString fs; int columns, size, n, w = 0, padding, amount = 0; const char *in = NULL; @@ -473,7 +480,7 @@ static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x, x += st->cwidth * padding; - if (st->showsyntax && format) { + if (use_syntax) { int a, str_shift = 0; char fmt_prev = 0xff; @@ -1460,7 +1467,7 @@ void text_scroll_to_cursor(SpaceText *st, ScrArea *sa) ARegion *ar = NULL; int i, x, winx = 0; - if (ELEM3(NULL, st, st->text, st->text->curl)) return; + if (ELEM(NULL, st, st->text, st->text->curl)) return; text = st->text; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index 0263b6cd912..852edaac7dc 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -458,7 +458,7 @@ static void txt_write_file(Text *text, ReportList *reports) { FILE *fp; TextLine *tmp; - struct stat st; + BLI_stat_t st; char filepath[FILE_MAX]; BLI_strncpy(filepath, text->name, FILE_MAX); @@ -473,7 +473,9 @@ static void txt_write_file(Text *text, ReportList *reports) for (tmp = text->lines.first; tmp; tmp = tmp->next) { fputs(tmp->line, fp); - fputc('\n', fp); + if (tmp->next) { + fputc('\n', fp); + } } fclose(fp); @@ -1825,11 +1827,17 @@ static int text_move_cursor(bContext *C, int type, bool select) switch (type) { case LINE_BEGIN: + if (!select) { + txt_sel_clear(text); + } if (st && st->wordwrap && ar) txt_wrap_move_bol(st, ar, select); else txt_move_bol(text, select); break; case LINE_END: + if (!select) { + txt_sel_clear(text); + } if (st && st->wordwrap && ar) txt_wrap_move_eol(st, ar, select); else txt_move_eol(text, select); break; diff --git a/source/blender/editors/space_time/CMakeLists.txt b/source/blender/editors/space_time/CMakeLists.txt index b42ae3ab725..90af405eaa8 100644 --- a/source/blender/editors/space_time/CMakeLists.txt +++ b/source/blender/editors/space_time/CMakeLists.txt @@ -22,10 +22,12 @@ set(INC ../include ../../blenkernel ../../blenlib + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -39,4 +41,6 @@ set(SRC time_intern.h ) +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_space_time "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/space_time/SConscript b/source/blender/editors/space_time/SConscript index bf1b918f4f1..5a9e9a4cb42 100644 --- a/source/blender/editors/space_time/SConscript +++ b/source/blender/editors/space_time/SConscript @@ -31,15 +31,17 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenkernel', '../../blenlib', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] env.BlenderLib('bf_editors_space_time', sources, incs, defs, libtype=['core'], priority=[65]) diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c index 2fd4b0bc21d..88c57d45b79 100644 --- a/source/blender/editors/space_time/space_time.c +++ b/source/blender/editors/space_time/space_time.c @@ -405,6 +405,7 @@ static void time_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn) case NC_OBJECT: { switch (wmn->data) { + case ND_BONE_SELECT: case ND_BONE_ACTIVE: case ND_POINTCACHE: case ND_MODIFIER: diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index 97c328dbac2..320267a4a7c 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC ../../render/extern/include ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ../../../../intern/smoke/extern ) @@ -76,7 +77,7 @@ if(WITH_GAMEENGINE) add_definitions(-DWITH_GAMEENGINE) endif() -add_definitions(-DGLEW_STATIC) +add_definitions(${GL_DEFINITIONS}) if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) diff --git a/source/blender/editors/space_view3d/SConscript b/source/blender/editors/space_view3d/SConscript index e6658ab3c49..a21da940906 100644 --- a/source/blender/editors/space_view3d/SConscript +++ b/source/blender/editors/space_view3d/SConscript @@ -28,11 +28,13 @@ Import ('env') sources = env.Glob('*.c') -defs = [ 'GLEW_STATIC' ] + +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/smoke/extern', '#/source/gameengine/BlenderRoutines', '../include', diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c index 245cdadc7a2..651c9389341 100644 --- a/source/blender/editors/space_view3d/drawarmature.c +++ b/source/blender/editors/space_view3d/drawarmature.c @@ -67,6 +67,7 @@ #include "view3d_intern.h" +#include "GPU_select.h" /* *************** Armature Drawing - Coloring API ***************************** */ @@ -93,7 +94,7 @@ static void set_pchan_colorset(Object *ob, bPoseChannel *pchan) short color_index = 0; /* sanity check */ - if (ELEM4(NULL, ob, arm, pose, pchan)) { + if (ELEM(NULL, ob, arm, pose, pchan)) { bcolor = NULL; return; } @@ -466,10 +467,10 @@ static const unsigned int bone_octahedral_solid_tris[8][3] = { /* aligned with bone_octahedral_solid_tris */ static const float bone_octahedral_solid_normals[8][3] = { - { 0.70710683f, -0.70710683f, 0.00000000f}, - {-0.00000000f, -0.70710683f, -0.70710683f}, - {-0.70710683f, -0.70710683f, 0.00000000f}, - { 0.00000000f, -0.70710683f, 0.70710683f}, + { M_SQRT1_2, -M_SQRT1_2, 0.00000000f}, + {-0.00000000f, -M_SQRT1_2, -M_SQRT1_2}, + {-M_SQRT1_2, -M_SQRT1_2, 0.00000000f}, + { 0.00000000f, -M_SQRT1_2, M_SQRT1_2}, { 0.99388373f, 0.11043154f, -0.00000000f}, { 0.00000000f, 0.11043154f, -0.99388373f}, {-0.99388373f, 0.11043154f, 0.00000000f}, @@ -551,7 +552,7 @@ static void draw_bone_points(const short dt, int armflag, unsigned int boneflag, /* Draw root point if we are not connected */ if ((boneflag & BONE_CONNECTED) == 0) { if (id != -1) - glLoadName(id | BONESEL_ROOT); + GPU_select_load_id(id | BONESEL_ROOT); if (dt <= OB_WIRE) { if (armflag & ARM_EDITMODE) { @@ -574,7 +575,7 @@ static void draw_bone_points(const short dt, int armflag, unsigned int boneflag, /* Draw tip point */ if (id != -1) - glLoadName(id | BONESEL_TIP); + GPU_select_load_id(id | BONESEL_TIP); if (dt <= OB_WIRE) { if (armflag & ARM_EDITMODE) { @@ -787,7 +788,7 @@ static void draw_sphere_bone_wire(float smat[4][4], float imat[4][4], /* Draw root point if we are not connected */ if ((boneflag & BONE_CONNECTED) == 0) { if (id != -1) - glLoadName(id | BONESEL_ROOT); + GPU_select_load_id(id | BONESEL_ROOT); drawcircball(GL_LINE_LOOP, headvec, head, imat); } @@ -799,7 +800,7 @@ static void draw_sphere_bone_wire(float smat[4][4], float imat[4][4], } if (id != -1) - glLoadName(id | BONESEL_TIP); + GPU_select_load_id(id | BONESEL_TIP); drawcircball(GL_LINE_LOOP, tailvec, tail, imat); @@ -830,7 +831,7 @@ static void draw_sphere_bone_wire(float smat[4][4], float imat[4][4], cross_v3_v3v3(norvect, vec, imat[2]); if (id != -1) - glLoadName(id | BONESEL_BONE); + GPU_select_load_id(id | BONESEL_BONE); glBegin(GL_LINES); @@ -907,7 +908,7 @@ static void draw_sphere_bone(const short dt, int armflag, int boneflag, short co /* Draw root point if we are not connected */ if ((boneflag & BONE_CONNECTED) == 0) { if (id != -1) - glLoadName(id | BONESEL_ROOT); + GPU_select_load_id(id | BONESEL_ROOT); gluSphere(qobj, head, 16, 10); } @@ -918,7 +919,7 @@ static void draw_sphere_bone(const short dt, int armflag, int boneflag, short co } if (id != -1) - glLoadName(id | BONESEL_TIP); + GPU_select_load_id(id | BONESEL_TIP); glTranslatef(0.0f, 0.0f, length); gluSphere(qobj, tail, 16, 10); @@ -939,7 +940,7 @@ static void draw_sphere_bone(const short dt, int armflag, int boneflag, short co if (length > (head + tail)) { if (id != -1) - glLoadName(id | BONESEL_BONE); + GPU_select_load_id(id | BONESEL_BONE); glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffset(-1.0f, -1.0f); @@ -1009,7 +1010,7 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned /* Draw root point if we are not connected */ if ((boneflag & BONE_CONNECTED) == 0) { if (G.f & G_PICKSEL) { /* no bitmap in selection mode, crashes 3d cards... */ - glLoadName(id | BONESEL_ROOT); + GPU_select_load_id(id | BONESEL_ROOT); glBegin(GL_POINTS); glVertex3f(0.0f, 0.0f, 0.0f); glEnd(); @@ -1021,7 +1022,7 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned } if (id != -1) - glLoadName((GLuint) id | BONESEL_BONE); + GPU_select_load_id((GLuint) id | BONESEL_BONE); glBegin(GL_LINES); glVertex3f(0.0f, 0.0f, 0.0f); @@ -1031,7 +1032,7 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned /* tip */ if (G.f & G_PICKSEL) { /* no bitmap in selection mode, crashes 3d cards... */ - glLoadName(id | BONESEL_TIP); + GPU_select_load_id(id | BONESEL_TIP); glBegin(GL_POINTS); glVertex3f(0.0f, 1.0f, 0.0f); glEnd(); @@ -1043,7 +1044,7 @@ static void draw_line_bone(int armflag, int boneflag, short constflag, unsigned /* further we send no names */ if (id != -1) - glLoadName(id & 0xFFFF); /* object tag, for bordersel optim */ + GPU_select_load_id(id & 0xFFFF); /* object tag, for bordersel optim */ if (armflag & ARM_POSEMODE) set_pchan_glColor(PCHAN_COLOR_LINEBONE, boneflag, constflag); @@ -1161,7 +1162,7 @@ static void draw_b_bone(const short dt, int armflag, int boneflag, short constfl } if (id != -1) { - glLoadName((GLuint) id | BONESEL_BONE); + GPU_select_load_id((GLuint) id | BONESEL_BONE); } /* set up solid drawing */ @@ -1266,13 +1267,13 @@ static void draw_wire_bone(const short dt, int armflag, int boneflag, short cons /* this chunk not in object mode */ if (armflag & (ARM_EDITMODE | ARM_POSEMODE)) { if (id != -1) - glLoadName((GLuint) id | BONESEL_BONE); + GPU_select_load_id((GLuint) id | BONESEL_BONE); draw_wire_bone_segments(pchan, bbones, length, segments); /* further we send no names */ if (id != -1) - glLoadName(id & 0xFFFF); /* object tag, for bordersel optim */ + GPU_select_load_id(id & 0xFFFF); /* object tag, for bordersel optim */ } /* colors for modes */ @@ -1315,7 +1316,7 @@ static void draw_bone(const short dt, int armflag, int boneflag, short constflag /* now draw the bone itself */ if (id != -1) { - glLoadName((GLuint) id | BONESEL_BONE); + GPU_select_load_id((GLuint) id | BONESEL_BONE); } /* wire? */ @@ -1370,7 +1371,7 @@ static void draw_custom_bone(Scene *scene, View3D *v3d, RegionView3D *rv3d, Obje } if (id != -1) { - glLoadName((GLuint) id | BONESEL_BONE); + GPU_select_load_id((GLuint) id | BONESEL_BONE); } draw_object_instance(scene, v3d, rv3d, ob, dt, armflag & ARM_POSEMODE); @@ -1812,12 +1813,12 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, index += 0x10000; /* pose bones count in higher 2 bytes only */ } - /* very very confusing... but in object mode, solid draw, we cannot do glLoadName yet, + /* very very confusing... but in object mode, solid draw, we cannot do GPU_select_load_id yet, * stick bones and/or wire custom-shapes are drawn in next loop */ - if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE) == 0 && (draw_wire == false)) { + if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE) == 0 && (draw_wire == false) && index != -1) { /* object tag, for bordersel optim */ - glLoadName(index & 0xFFFF); + GPU_select_load_id(index & 0xFFFF); index = -1; } } @@ -1881,9 +1882,9 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, index += 0x10000; /* pose bones count in higher 2 bytes only */ } /* stick or wire bones have not been drawn yet so don't clear object selection in this case */ - if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE) == 0 && draw_wire) { + if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE) == 0 && draw_wire && index != -1) { /* object tag, for bordersel optim */ - glLoadName(index & 0xFFFF); + GPU_select_load_id(index & 0xFFFF); index = -1; } } @@ -1926,7 +1927,7 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, */ if ((do_dashed & 2) && ((bone->flag & BONE_CONNECTED) == 0)) { if (arm->flag & ARM_POSEMODE) { - glLoadName(index & 0xFFFF); /* object tag, for bordersel optim */ + GPU_select_load_id(index & 0xFFFF); /* object tag, for bordersel optim */ UI_ThemeColor(TH_WIRE); } setlinestyle(3); @@ -1946,7 +1947,7 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, if (constflag & PCHAN_HAS_TARGET) glColor3ub(200, 120, 0); else glColor3ub(200, 200, 50); /* add theme! */ - glLoadName(index & 0xFFFF); + GPU_select_load_id(index & 0xFFFF); pchan_draw_IK_root_lines(pchan, !(do_dashed & 2)); } } @@ -1954,7 +1955,7 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, if (bone->flag & BONE_SELECTED) { glColor3ub(150, 200, 50); /* add theme! */ - glLoadName(index & 0xFFFF); + GPU_select_load_id(index & 0xFFFF); pchan_draw_IK_root_lines(pchan, !(do_dashed & 2)); } } @@ -2020,7 +2021,7 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, /* draw DoFs */ if (arm->flag & ARM_POSEMODE) { - if (((base->flag & OB_FROMDUPLI) == 0)) { + if (((base->flag & OB_FROMDUPLI) == 0) && ((v3d->flag & V3D_HIDE_HELPLINES) == 0)) { draw_pose_dofs(ob); } } @@ -2087,6 +2088,10 @@ static void draw_pose_bones(Scene *scene, View3D *v3d, ARegion *ar, Base *base, if (v3d->zbuf) glEnable(GL_DEPTH_TEST); } } + + if (index != -1) { + GPU_select_load_id(-1); + } } /* in editmode, we don't store the bone matrix... */ @@ -2174,7 +2179,7 @@ static void draw_ebones(View3D *v3d, ARegion *ar, Object *ob, const short dt) /* if wire over solid, set offset */ index = -1; - glLoadName(-1); + GPU_select_load_id(-1); if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) { if (G.f & G_PICKSEL) index = 0; @@ -2223,7 +2228,7 @@ static void draw_ebones(View3D *v3d, ARegion *ar, Object *ob, const short dt) /* offset to parent */ if (eBone->parent) { UI_ThemeColor(TH_WIRE_EDIT); - glLoadName(-1); /* -1 here is OK! */ + GPU_select_load_id(-1); /* -1 here is OK! */ setlinestyle(3); glBegin(GL_LINES); @@ -2240,7 +2245,7 @@ static void draw_ebones(View3D *v3d, ARegion *ar, Object *ob, const short dt) /* restore */ if (index != -1) { - glLoadName(-1); + GPU_select_load_id(-1); } if (ELEM(arm->drawtype, ARM_LINE, ARM_WIRE)) { diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 03f2fa86ecb..fa9ba23e454 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -113,8 +113,8 @@ static BLI_bitmap *get_tface_mesh_marked_edge_info(Mesh *me) ml = me->mloop + mp->loopstart; for (j = 0; j < mp->totloop; j++, ml++) { - BLI_BITMAP_SET(bitmap_edge_flags, edge_vis_index(ml->e)); - if (select_set) BLI_BITMAP_SET(bitmap_edge_flags, edge_sel_index(ml->e)); + BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_vis_index(ml->e)); + if (select_set) BLI_BITMAP_ENABLE(bitmap_edge_flags, edge_sel_index(ml->e)); } } } @@ -129,12 +129,12 @@ static DMDrawOption draw_mesh_face_select__setHiddenOpts(void *userData, int ind Mesh *me = data->me; if (me->drawflag & ME_DRAWEDGES) { - if ((me->drawflag & ME_HIDDENEDGES) || (BLI_BITMAP_GET(data->edge_flags, edge_vis_index(index)))) + if ((me->drawflag & ME_HIDDENEDGES) || (BLI_BITMAP_TEST(data->edge_flags, edge_vis_index(index)))) return DM_DRAW_OPTION_NORMAL; else return DM_DRAW_OPTION_SKIP; } - else if (BLI_BITMAP_GET(data->edge_flags, edge_sel_index(index))) + else if (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index))) return DM_DRAW_OPTION_NORMAL; else return DM_DRAW_OPTION_SKIP; @@ -143,7 +143,7 @@ static DMDrawOption draw_mesh_face_select__setHiddenOpts(void *userData, int ind static DMDrawOption draw_mesh_face_select__setSelectOpts(void *userData, int index) { drawMeshFaceSelect_userData *data = userData; - return (BLI_BITMAP_GET(data->edge_flags, edge_sel_index(index))) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP; + return (BLI_BITMAP_TEST(data->edge_flags, edge_sel_index(index))) ? DM_DRAW_OPTION_NORMAL : DM_DRAW_OPTION_SKIP; } /* draws unselected */ @@ -212,12 +212,16 @@ static Material *give_current_material_or_def(Object *ob, int matnr) static struct TextureDrawState { Object *ob; + Image *stencil; /* texture painting stencil */ + Image *canvas; /* texture painting canvas, for image mode */ bool use_game_mat; int is_lit, is_tex; int color_profile; bool use_backface_culling; unsigned char obcol[4]; -} Gtexdraw = {NULL, false, 0, 0, 0, false, {0, 0, 0, 0}}; + bool is_texpaint; + bool texpaint_material; /* use material slots for texture painting */ +} Gtexdraw = {NULL, NULL, NULL, false, 0, 0, 0, false, {0, 0, 0, 0}, false, false}; static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material *ma, struct TextureDrawState gtexdraw) { @@ -229,23 +233,31 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * static int c_lit; static int c_has_texface; - Object *litob = NULL; /* to get mode to turn off mipmap in painting mode */ int backculled = 1; int alphablend = GPU_BLEND_SOLID; int textured = 0; int lit = 0; int has_texface = texface != NULL; bool need_set_tpage = false; + bool texpaint = ((gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) != 0); + + Image *ima = NULL; + + if (ma != NULL) { + if (ma->mode & MA_TRANSP) { + alphablend = GPU_BLEND_ALPHA; + } + } if (clearcache) { c_textured = c_lit = c_backculled = -1; memset(&c_texface, 0, sizeof(MTFace)); c_badtex = false; c_has_texface = -1; + c_ma = NULL; } else { textured = gtexdraw.is_tex; - litob = gtexdraw.ob; } /* convert number of lights into boolean */ @@ -260,14 +272,19 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * } } - if (texface) { + if (texface && !texpaint) { textured = textured && (texface->tpage); /* no material, render alpha if texture has depth=32 */ if (!ma && BKE_image_has_alpha(texface->tpage)) alphablend = GPU_BLEND_ALPHA; } - + else if (texpaint) { + if (gtexdraw.texpaint_material) + ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + else + ima = gtexdraw.canvas; + } else textured = 0; @@ -281,11 +298,51 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * /* need to re-set tpage if textured flag changed or existsment of texface changed.. */ need_set_tpage = textured != c_textured || has_texface != c_has_texface; /* ..or if settings inside texface were changed (if texface was used) */ - need_set_tpage |= texface && memcmp(&c_texface, texface, sizeof(c_texface)); + need_set_tpage |= (texpaint && c_ma != ma) || (texface && memcmp(&c_texface, texface, sizeof(c_texface))); if (need_set_tpage) { if (textured) { - c_badtex = !GPU_set_tpage(texface, !(litob->mode & OB_MODE_TEXTURE_PAINT), alphablend); + if (texpaint) { + c_badtex = false; + if (GPU_verify_image(ima, NULL, 0, 1, 0, false)) { + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_TEXTURE); + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glBindTexture(GL_TEXTURE_2D, ima->bindcode); + glActiveTexture(GL_TEXTURE0); + } + else { + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glActiveTexture(GL_TEXTURE0); + + c_badtex = true; + GPU_clear_tpage(true); + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + } + } + else { + c_badtex = !GPU_set_tpage(texface, !texpaint, alphablend); + } } else { GPU_set_tpage(NULL, 0, 0); @@ -319,6 +376,7 @@ static bool set_draw_settings_cached(int clearcache, MTFace *texface, Material * glDisable(GL_COLOR_MATERIAL); } c_lit = lit; + c_ma = ma; } return c_badtex; @@ -329,6 +387,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O unsigned char obcol[4]; bool is_tex, solidtex; Mesh *me = ob->data; + ImagePaintSettings *imapaint = &scene->toolsettings->imapaint; /* XXX scene->obedit warning */ @@ -358,8 +417,52 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O else is_tex = false; Gtexdraw.ob = ob; + Gtexdraw.stencil = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL) ? imapaint->stencil : NULL; + Gtexdraw.is_texpaint = (ob->mode == OB_MODE_TEXTURE_PAINT); + Gtexdraw.texpaint_material = (imapaint->mode == IMAGEPAINT_MODE_MATERIAL); + Gtexdraw.canvas = (Gtexdraw.texpaint_material) ? NULL : imapaint->canvas; Gtexdraw.is_tex = is_tex; + /* naughty multitexturing hacks to quickly support stencil + shading + alpha blending + * in new texpaint code. The better solution here would be to support GLSL */ + if (Gtexdraw.is_texpaint) { + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_PRIMARY_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_ALPHA); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_REPLACE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + + /* load the stencil texture here */ + if (Gtexdraw.stencil != NULL) { + glActiveTexture(GL_TEXTURE2); + if (GPU_verify_image(Gtexdraw.stencil, NULL, false, false, false, false)) { + float col[4] = {imapaint->stencil_col[0], imapaint->stencil_col[1], imapaint->stencil_col[2], 1.0f}; + glEnable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, GL_INTERPOLATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_RGB, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_RGB, GL_CONSTANT); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC2_RGB, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC0_ALPHA, GL_PREVIOUS); + glTexEnvi(GL_TEXTURE_ENV, GL_SRC1_ALPHA, GL_TEXTURE); + glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, col); + if ((imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) == 0) { + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_ONE_MINUS_SRC_COLOR); + } + else { + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + } + } + } + glActiveTexture(GL_TEXTURE0); + } + Gtexdraw.color_profile = BKE_scene_check_color_management_enabled(scene); Gtexdraw.use_game_mat = (RE_engines_find(scene->r.engine)->flag & RE_GAME) != 0; Gtexdraw.use_backface_culling = (v3d->flag2 & V3D_BACKFACE_CULLING) != 0; @@ -373,8 +476,31 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O static void draw_textured_end(void) { - /* switch off textures */ - GPU_set_tpage(NULL, 0, 0); + if (Gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) { + glActiveTexture(GL_TEXTURE1); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBindTexture(GL_TEXTURE_2D, 0); + + if (Gtexdraw.stencil != NULL) { + glActiveTexture(GL_TEXTURE2); + glDisable(GL_TEXTURE_2D); + glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND2_RGB, GL_SRC_COLOR); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBindTexture(GL_TEXTURE_2D, 0); + } + glActiveTexture(GL_TEXTURE0); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + /* manual reset, since we don't use tpage */ + glBindTexture(GL_TEXTURE_2D, 0); + /* force switch off textures */ + GPU_clear_tpage(true); + } + else { + /* switch off textures */ + GPU_set_tpage(NULL, 0, 0); + } glShadeModel(GL_FLAT); glDisable(GL_CULL_FACE); @@ -450,7 +576,7 @@ static DMDrawOption draw_tface__set_draw(MTFace *tface, const bool UNUSED(has_mc if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return 0; - if (tface) + if (tface || Gtexdraw.is_texpaint) set_draw_settings_cached(0, tface, ma, Gtexdraw); /* always use color from mcol, as set in update_tface_color_layer */ @@ -764,7 +890,8 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d Object *ob, DerivedMesh *dm, const int draw_flags) { Mesh *me = ob->data; - + DMDrawFlag uvflag = DM_DRAW_USE_ACTIVE_UV; + /* correct for negative scale */ if (ob->transflag & OB_NEG_SCALE) glFrontFace(GL_CW); else glFrontFace(GL_CCW); @@ -774,6 +901,10 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d glColor4f(1.0f, 1.0f, 1.0f, 1.0f); + if (ob->mode & OB_MODE_TEXTURE_PAINT) { + uvflag = DM_DRAW_USE_TEXPAINT_UV; + } + if (ob->mode & OB_MODE_EDIT) { drawEMTFMapped_userData data; @@ -783,7 +914,7 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d data.mf = DM_get_tessface_data_layer(dm, CD_MFACE); data.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); - dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, compareDrawOptionsEm, &data); + dm->drawMappedFacesTex(dm, draw_em_tf_mapped__set_draw, compareDrawOptionsEm, &data, 0); } else if (draw_flags & DRAW_FACE_SELECT) { if (ob->mode & OB_MODE_WEIGHT_PAINT) @@ -795,15 +926,15 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d userData.mf = DM_get_tessface_data_layer(dm, CD_MFACE); userData.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); userData.me = me; - dm->drawMappedFacesTex(dm, me->mpoly ? draw_tface_mapped__set_draw : NULL, compareDrawOptions, &userData); + dm->drawMappedFacesTex(dm, me->mpoly ? draw_tface_mapped__set_draw : NULL, compareDrawOptions, &userData, uvflag); } } else { if (GPU_buffer_legacy(dm)) { if (draw_flags & DRAW_MODIFIERS_PREVIEW) - dm->drawFacesTex(dm, draw_mcol__set_draw_legacy, NULL, NULL); + dm->drawFacesTex(dm, draw_mcol__set_draw_legacy, NULL, NULL, uvflag); else - dm->drawFacesTex(dm, draw_tface__set_draw_legacy, NULL, NULL); + dm->drawFacesTex(dm, draw_tface__set_draw_legacy, NULL, NULL, uvflag); } else { drawTFace_userData userData; @@ -814,7 +945,7 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d userData.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); userData.me = NULL; - dm->drawFacesTex(dm, draw_tface__set_draw, compareDrawOptions, &userData); + dm->drawFacesTex(dm, draw_tface__set_draw, compareDrawOptions, &userData, uvflag); } } @@ -864,7 +995,7 @@ static void tex_mat_set_texture_cb(void *userData, int mat_nr, void *attribs) int texture_set = 0; /* draw image texture if we find one */ - if (ED_object_get_active_image(data->ob, mat_nr, &ima, &iuser, &node)) { + if (ED_object_get_active_image(data->ob, mat_nr, &ima, &iuser, &node, NULL)) { /* get openl texture */ int mipmap = 1; int bindcode = (ima) ? GPU_verify_image(ima, iuser, 0, 0, mipmap, false) : 0; @@ -947,7 +1078,11 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, DerivedMesh *dm, const int draw_flags) { /* if not cycles, or preview-modifiers, or drawing matcaps */ - if ((!BKE_scene_use_new_shading_nodes(scene)) || (draw_flags & DRAW_MODIFIERS_PREVIEW) || (v3d->flag2 & V3D_SHOW_SOLID_MATCAP)) { + if ((draw_flags & DRAW_MODIFIERS_PREVIEW) || + (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) || + (BKE_scene_use_new_shading_nodes(scene) == false) || + ((ob->mode & OB_MODE_TEXTURE_PAINT) && ELEM(v3d->drawtype, OB_TEXTURE, OB_SOLID))) + { draw_mesh_textured_old(scene, v3d, rv3d, ob, dm, draw_flags); return; } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 7f56b4a9822..db04d3ccd66 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -86,6 +86,7 @@ #include "GPU_draw.h" #include "GPU_extensions.h" +#include "GPU_select.h" #include "ED_mesh.h" #include "ED_particle.h" @@ -109,7 +110,7 @@ * * Ideally we don't want to evaluate objects from drawing, * but it'll require some major sequencer re-design. So - * for now just fallback to legacy behaior with calling + * for now just fallback to legacy behavior with calling * display ist creating from draw(). */ #define SEQUENCER_DAG_WORKAROUND @@ -145,6 +146,24 @@ typedef struct drawDMEdgesSel_userData { BMEdge *eed_act; } drawDMEdgesSel_userData; +typedef struct drawDMEdgesSelInterp_userData { + BMesh *bm; + + unsigned char *baseCol, *selCol; + unsigned char *lastCol; +} drawDMEdgesSelInterp_userData; + +typedef struct drawDMEdgesWeightInterp_userData { + BMesh *bm; + + int cd_dvert_offset; + int defgroup_tot; + int vgroup_index; + char weight_user; + float alert_color[3]; + +} drawDMEdgesWeightInterp_userData; + typedef struct drawDMFacesSel_userData { #ifdef WITH_FREESTYLE unsigned char *cols[4]; @@ -168,16 +187,26 @@ typedef struct drawDMNormal_userData { float imat[3][3]; } drawDMNormal_userData; -typedef struct bbsObmodeMeshVerts_userData { - void *offset; +typedef struct drawMVertOffset_userData { MVert *mvert; -} bbsObmodeMeshVerts_userData; + int offset; +} drawMVertOffset_userData; typedef struct drawDMLayer_userData { BMesh *bm; int cd_layer_offset; } drawDMLayer_userData; +typedef struct drawBMOffset_userData { + BMesh *bm; + int offset; +} drawBMOffset_userData; + +typedef struct drawBMSelect_userData { + BMesh *bm; + bool select; +} drawBMSelect_userData; + static void draw_bounding_volume(Object *ob, char type); static void drawcube_size(float size); @@ -273,7 +302,8 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt) if (BKE_scene_use_new_shading_nodes(scene)) return false; - return (scene->gm.matmode == GAME_MAT_GLSL) && (dt > OB_SOLID); + return ((scene->gm.matmode == GAME_MAT_GLSL && v3d->drawtype == OB_TEXTURE) || + (v3d->drawtype == OB_MATERIAL)) && (dt > OB_SOLID); } static bool check_alpha_pass(Base *base) @@ -756,8 +786,10 @@ typedef struct ViewCachedString { short sco[2]; short xoffs; short flag; - int str_len, pad; + int str_len; + /* str is allocated past the end */ + char str[0]; } ViewCachedString; /* one arena for all 3 string lists */ @@ -782,7 +814,6 @@ void view3d_cached_text_draw_add(const float co[3], const unsigned char col[4]) { int alloc_len = str_len + 1; - /* TODO, replace with more efficient malloc, perhaps memarena per draw? */ ViewCachedString *vos; BLI_assert(str_len == strlen(str)); @@ -802,7 +833,7 @@ void view3d_cached_text_draw_add(const float co[3], vos->str_len = str_len; /* allocate past the end */ - memcpy(vos + 1, str, alloc_len); + memcpy(vos->str, str, alloc_len); } void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, float mat[4][4]) @@ -850,7 +881,8 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); - ED_region_pixelspace(ar); + wmOrtho2_region_ui(ar); + glLoadIdentity(); if (depth_write) { if (v3d->zbuf) glDisable(GL_DEPTH_TEST); @@ -861,8 +893,6 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo for (vos = g_v3d_strings[g_v3d_string_level]; vos; vos = vos->next) { if (vos->sco[0] != IS_CLIPPED) { - const char *str = (char *)(vos + 1); - if (col_pack_prev != vos->col.pack) { glColor3ubv(vos->col.ub); col_pack_prev = vos->col.pack; @@ -871,10 +901,10 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo ((vos->flag & V3D_CACHE_TEXT_ASCII) ? BLF_draw_default_ascii : BLF_draw_default - )( (float)vos->sco[0] + vos->xoffs, - (float)vos->sco[1], + )((float)(vos->sco[0] + vos->xoffs), + (float)(vos->sco[1]), (depth_write) ? 0.0f : 2.0f, - str, + vos->str, vos->str_len); } } @@ -1408,8 +1438,10 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, drawshadbuflimits(la, ob->obmat); } - UI_GetThemeColor4ubv(TH_LAMP, col); - glColor4ubv(col); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + UI_GetThemeColor4ubv(TH_LAMP, col); + glColor4ubv(col); + } glEnable(GL_BLEND); @@ -1448,7 +1480,9 @@ static void draw_limit_line(float sta, float end, const short dflag, unsigned in if (!(dflag & DRAW_PICKING)) { glPointSize(3.0); glBegin(GL_POINTS); - cpack(col); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + cpack(col); + } glVertex3f(0.0, 0.0, -sta); glVertex3f(0.0, 0.0, -end); glEnd(); @@ -1555,7 +1589,7 @@ static void draw_viewport_object_reconstruction(Scene *scene, Base *base, View3D continue; if (dflag & DRAW_PICKING) - glLoadName(base->selcol + (tracknr << 16)); + GPU_select_load_id(base->selcol + (tracknr << 16)); glPushMatrix(); glTranslatef(track->bundle_pos[0], track->bundle_pos[1], track->bundle_pos[2]); @@ -1706,7 +1740,7 @@ static void draw_viewport_reconstruction(Scene *scene, Base *base, View3D *v3d, } if (dflag & DRAW_PICKING) - glLoadName(base->selcol); + GPU_select_load_id(base->selcol); } /* flag similar to draw_object() */ @@ -1942,7 +1976,29 @@ static void drawlattice__point(Lattice *lt, DispList *dl, int u, int v, int w, i #ifdef SEQUENCER_DAG_WORKAROUND static void ensure_curve_cache(Scene *scene, Object *object) { - if (object->curve_cache == NULL) { + bool need_recalc = object->curve_cache == NULL; + /* Render thread might have freed the curve cache if the + * object is not visible. If the object is also used for + * particles duplication, then render thread might have + * also created curve_cache with only bevel and path + * filled in. + * + * So check for curve_cache != NULL is not fully correct + * here, we also need to check whether display list is + * empty or not. + * + * The trick below tries to optimie calls to displist + * creation for cases curve is empty. Meaning, if the curve + * is empty (without splies) bevel list would also be empty. + * And the thing is, render thread always leaves bevel list + * in a proper state. So if bevel list is here and display + * list is not we need to make display list. + */ + if (need_recalc == false) { + need_recalc = object->curve_cache->disp.first == NULL && + object->curve_cache->bev.first != NULL; + } + if (need_recalc) { switch (object->type) { case OB_CURVE: case OB_SURF: @@ -2141,21 +2197,21 @@ static void draw_dm_face_normals(BMEditMesh *em, Scene *scene, Object *ob, Deriv static void draw_dm_face_centers__mapFunc(void *userData, int index, const float cent[3], const float UNUSED(no[3])) { - BMFace *efa = BM_face_at_index(((void **)userData)[0], index); - const char sel = *(((char **)userData)[1]); + drawBMSelect_userData *data = userData; + BMFace *efa = BM_face_at_index(data->bm, index); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && - (BM_elem_flag_test(efa, BM_ELEM_SELECT) == sel)) + (BM_elem_flag_test(efa, BM_ELEM_SELECT) == data->select)) { bglVertex3fv(cent); } } -static void draw_dm_face_centers(BMEditMesh *em, DerivedMesh *dm, char sel) +static void draw_dm_face_centers(BMEditMesh *em, DerivedMesh *dm, bool select) { - void *ptrs[2] = {em->bm, &sel}; + drawBMSelect_userData data = {em->bm, select}; bglBegin(GL_POINTS); - dm->foreachMappedFaceCenter(dm, draw_dm_face_centers__mapFunc, ptrs, DM_FOREACH_NOP); + dm->foreachMappedFaceCenter(dm, draw_dm_face_centers__mapFunc, &data, DM_FOREACH_NOP); bglEnd(); } @@ -2338,29 +2394,155 @@ static void draw_dm_edges(BMEditMesh *em, DerivedMesh *dm) /* Draw edges with color interpolated based on selection */ static DMDrawOption draw_dm_edges_sel_interp__setDrawOptions(void *userData, int index) { - if (BM_elem_flag_test(BM_edge_at_index(((void **)userData)[0], index), BM_ELEM_HIDDEN)) + drawDMEdgesSelInterp_userData *data = userData; + if (BM_elem_flag_test(BM_edge_at_index(data->bm, index), BM_ELEM_HIDDEN)) return DM_DRAW_OPTION_SKIP; else return DM_DRAW_OPTION_NORMAL; } static void draw_dm_edges_sel_interp__setDrawInterpOptions(void *userData, int index, float t) { - BMEdge *eed = BM_edge_at_index(((void **)userData)[0], index); + drawDMEdgesSelInterp_userData *data = userData; + BMEdge *eed = BM_edge_at_index(data->bm, index); unsigned char **cols = userData; - unsigned char *col0 = cols[(BM_elem_flag_test(eed->v1, BM_ELEM_SELECT)) ? 2 : 1]; - unsigned char *col1 = cols[(BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) ? 2 : 1]; + unsigned int col0_id = (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT)) ? 2 : 1; + unsigned int col1_id = (BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)) ? 2 : 1; + unsigned char *col0 = cols[col0_id]; + unsigned char *col1 = cols[col1_id]; + unsigned char *col_pt; + + if (col0_id == col1_id) { + col_pt = col0; + } + else if (t == 0.0f) { + col_pt = col0; + } + else if (t == 1.0f) { + col_pt = col1; + } + else { + unsigned char col_blend[4]; + interp_v4_v4v4_uchar(col_blend, col0, col1, t); + glColor4ubv(col_blend); + data->lastCol = NULL; + return; + } - glColor4ub(col0[0] + (col1[0] - col0[0]) * t, - col0[1] + (col1[1] - col0[1]) * t, - col0[2] + (col1[2] - col0[2]) * t, - col0[3] + (col1[3] - col0[3]) * t); + if (data->lastCol != col_pt) { + data->lastCol = col_pt; + glColor4ubv(col_pt); + } } static void draw_dm_edges_sel_interp(BMEditMesh *em, DerivedMesh *dm, unsigned char *baseCol, unsigned char *selCol) { - void *cols[3] = {em->bm, baseCol, selCol}; + drawDMEdgesSelInterp_userData data; + data.bm = em->bm; + data.baseCol = baseCol; + data.selCol = selCol; + data.lastCol = NULL; + + dm->drawMappedEdgesInterp(dm, draw_dm_edges_sel_interp__setDrawOptions, draw_dm_edges_sel_interp__setDrawInterpOptions, &data); +} + +static void bm_color_from_weight(float col[3], BMVert *vert, drawDMEdgesWeightInterp_userData *data) +{ + MDeformVert *dvert = BM_ELEM_CD_GET_VOID_P(vert, data->cd_dvert_offset); + float weight = defvert_find_weight(dvert, data->vgroup_index); + + if ((weight == 0.0f) && + ((data->weight_user == OB_DRAW_GROUPUSER_ACTIVE) || + ((data->weight_user == OB_DRAW_GROUPUSER_ALL) && defvert_is_weight_zero(dvert, data->defgroup_tot)))) + { + copy_v3_v3(col, data->alert_color); + } + else { + weight_to_rgb(col, weight); + } +} + +static void draw_dm_edges_nop_interp__setDrawInterpOptions(void *UNUSED(userData), int UNUSED(index), float UNUSED(t)) +{ + /* pass */ +} + +static void draw_dm_edges_weight_interp__setDrawInterpOptions(void *userData, int index, float t) +{ + drawDMEdgesWeightInterp_userData *data = userData; + BMEdge *eed = BM_edge_at_index(data->bm, index); + float col[3]; + + if (t == 0.0f) { + bm_color_from_weight(col, eed->v1, data); + } + else if (t == 1.0f) { + bm_color_from_weight(col, eed->v2, data); + } + else { + float col_v1[3]; + float col_v2[3]; + + bm_color_from_weight(col_v1, eed->v1, data); + bm_color_from_weight(col_v2, eed->v2, data); + interp_v3_v3v3(col, col_v1, col_v2, t); + } + + glColor3fv(col); +} + +static void draw_dm_edges_weight_interp(BMEditMesh *em, DerivedMesh *dm, const char weight_user) +{ + drawDMEdgesWeightInterp_userData data; + Object *ob = em->ob; + + data.bm = em->bm; + data.cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT); + data.defgroup_tot = BLI_countlist(&ob->defbase); + data.vgroup_index = ob->actdef - 1; + data.weight_user = weight_user; + UI_GetThemeColor3fv(TH_VERTEX_UNREFERENCED, data.alert_color); + + if ((data.vgroup_index != -1) && (data.cd_dvert_offset != -1)) { + glEnable(GL_BLEND); + dm->drawMappedEdgesInterp( + dm, + draw_dm_edges_sel_interp__setDrawOptions, + draw_dm_edges_weight_interp__setDrawInterpOptions, + &data); + glDisable(GL_BLEND); + } + else { + float col[3]; + + if (data.weight_user == OB_DRAW_GROUPUSER_NONE) { + weight_to_rgb(col, 0.0f); + } + else { + copy_v3_v3(col, data.alert_color); + } + glColor3fv(col); + + dm->drawMappedEdgesInterp( + dm, + draw_dm_edges_sel_interp__setDrawOptions, + draw_dm_edges_nop_interp__setDrawInterpOptions, + &data); + } + +} + +static bool draw_dm_edges_weight_check(Mesh *me, View3D *v3d) +{ + if (me->drawflag & ME_DRAWEIGHT) { + if ((v3d->drawtype == OB_WIRE) || + (v3d->flag2 & V3D_SOLID_MATCAP) || + ((v3d->flag2 & V3D_OCCLUDE_WIRE) && (v3d->drawtype > OB_WIRE))) + { + return true; + } + } - dm->drawMappedEdgesInterp(dm, draw_dm_edges_sel_interp__setDrawOptions, draw_dm_edges_sel_interp__setDrawInterpOptions, cols); + return false; } /* Draw only seam edges */ @@ -2775,9 +2957,16 @@ static void draw_em_fancy_edges(BMEditMesh *em, Scene *scene, View3D *v3d, draw_dm_edges_sel(em, cageDM, wireCol, selCol, actCol, eed_act); } else if ((me->drawflag & ME_DRAWEDGES) || (ts->selectmode & SCE_SELECT_EDGE)) { - if (cageDM->drawMappedEdgesInterp && (ts->selectmode & SCE_SELECT_VERTEX)) { + if (cageDM->drawMappedEdgesInterp && + ((ts->selectmode & SCE_SELECT_VERTEX) || (me->drawflag & ME_DRAWEIGHT))) + { glShadeModel(GL_SMOOTH); - draw_dm_edges_sel_interp(em, cageDM, wireCol, selCol); + if (draw_dm_edges_weight_check(me, v3d)) { + draw_dm_edges_weight_interp(em, cageDM, ts->weightuser); + } + else { + draw_dm_edges_sel_interp(em, cageDM, wireCol, selCol); + } glShadeModel(GL_FLAT); } else { @@ -3424,7 +3613,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, draw_dm_vert_normals(em, scene, ob, cageDM); } if (me->drawflag & ME_DRAW_LNORMALS) { - UI_ThemeColor(TH_VNORMAL); + UI_ThemeColor(TH_LNORMAL); draw_dm_loop_normals(em, scene, ob, cageDM); } @@ -3564,7 +3753,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D if (ob->sculpt->partial_redraw) { if (ar->do_draw & RGN_DRAW_PARTIAL) { - sculpt_get_redraw_planes(planes, ar, rv3d, ob); + ED_sculpt_redraw_planes_get(planes, ar, rv3d, ob); fpl = planes; ob->sculpt->partial_redraw = 0; } @@ -3662,7 +3851,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D if (ob->sculpt->partial_redraw) { if (ar->do_draw & RGN_DRAW_PARTIAL) { - sculpt_get_redraw_planes(planes, ar, rv3d, ob); + ED_sculpt_redraw_planes_get(planes, ar, rv3d, ob); fpl = planes; ob->sculpt->partial_redraw = 0; } @@ -3812,12 +4001,14 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 else { /* ob->bb was set by derived mesh system, do NULL check just to be sure */ if (me->totpoly <= 4 || (!ob->bb || ED_view3d_boundbox_clip(rv3d, ob->bb))) { - const bool glsl = draw_glsl_material(scene, ob, v3d, dt); - const bool check_alpha = check_alpha_pass(base); + if (dt > OB_WIRE) { + const bool glsl = draw_glsl_material(scene, ob, v3d, dt); - if (dt == OB_SOLID || glsl) { - GPU_begin_object_materials(v3d, rv3d, scene, ob, glsl, - (check_alpha) ? &do_alpha_after : NULL); + if (dt == OB_SOLID || glsl) { + const bool check_alpha = check_alpha_pass(base); + GPU_begin_object_materials(v3d, rv3d, scene, ob, glsl, + (check_alpha) ? &do_alpha_after : NULL); + } } draw_mesh_fancy(scene, ar, v3d, rv3d, base, dt, ob_wire_col, dflag); @@ -3854,11 +4045,12 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 /* ************** DRAW DISPLIST ****************** */ -static bool draw_index_wire = true; -static bool index3_nors_incr = true; -/* returns 1 when nothing was drawn */ -static bool drawDispListwire(ListBase *dlbase) +/** + * \param dl_type_mask Only draw types matching this mask. + * \return true when nothing was drawn + */ +static bool drawDispListwire_ex(ListBase *dlbase, unsigned int dl_type_mask) { DispList *dl; int parts, nr; @@ -3870,8 +4062,13 @@ static bool drawDispListwire(ListBase *dlbase) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (dl = dlbase->first; dl; dl = dl->next) { - if (dl->parts == 0 || dl->nr == 0) + if (dl->parts == 0 || dl->nr == 0) { continue; + } + + if ((dl_type_mask & (1 << dl->type)) == 0) { + continue; + } data = dl->verts; @@ -3930,17 +4127,13 @@ static bool drawDispListwire(ListBase *dlbase) break; case DL_INDEX3: - if (draw_index_wire) { - glVertexPointer(3, GL_FLOAT, 0, dl->verts); - glDrawElements(GL_TRIANGLES, 3 * dl->parts, GL_UNSIGNED_INT, dl->index); - } + glVertexPointer(3, GL_FLOAT, 0, dl->verts); + glDrawElements(GL_TRIANGLES, 3 * dl->parts, GL_UNSIGNED_INT, dl->index); break; case DL_INDEX4: - if (draw_index_wire) { - glVertexPointer(3, GL_FLOAT, 0, dl->verts); - glDrawElements(GL_QUADS, 4 * dl->parts, GL_UNSIGNED_INT, dl->index); - } + glVertexPointer(3, GL_FLOAT, 0, dl->verts); + glDrawElements(GL_QUADS, 4 * dl->parts, GL_UNSIGNED_INT, dl->index); break; } } @@ -3951,6 +4144,20 @@ static bool drawDispListwire(ListBase *dlbase) return false; } +static bool drawDispListwire(ListBase *dlbase, const short ob_type) +{ + unsigned int dl_mask = 0xffffffff; + + /* skip fill-faces for curves & fonts */ + if (ELEM(ob_type, OB_FONT, OB_CURVE)) { + dl_mask &= ~((1 << DL_INDEX3) | (1 << DL_INDEX4)); + } + + return drawDispListwire_ex(dlbase, dl_mask); +} + +static bool index3_nors_incr = true; + static void drawDispListsolid(ListBase *lb, Object *ob, const short dflag, const unsigned char ob_wire_col[4], const bool use_glsl) { @@ -4143,6 +4350,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 lb = &ob->curve_cache->disp; if (solid) { + const bool has_faces = BKE_displist_has_faces(lb); dl = lb->first; if (dl == NULL) { return true; @@ -4151,13 +4359,19 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 if (dl->nors == NULL) BKE_displist_normals_add(lb); index3_nors_incr = false; - if (BKE_displist_has_faces(lb) == false) { - if (!render_only) { - draw_index_wire = false; - drawDispListwire(lb); - draw_index_wire = true; + if (!render_only) { + /* when we have faces, only draw loose-wire */ + if (has_faces) { + drawDispListwire_ex(lb, (1 << DL_SEGM)); + } + else { + drawDispListwire(lb, ob->type); } } + + if (has_faces == false) { + /* pass */ + } else { if (draw_glsl_material(scene, ob, v3d, dt)) { GPU_begin_object_materials(v3d, rv3d, scene, ob, 1, NULL); @@ -4171,20 +4385,14 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 } if (cu->editnurb && cu->bevobj == NULL && cu->taperobj == NULL && cu->ext1 == 0.0f && cu->ext2 == 0.0f) { cpack(0); - draw_index_wire = false; - drawDispListwire(lb); - draw_index_wire = true; + drawDispListwire(lb, ob->type); } } index3_nors_incr = true; } else { if (!render_only || (render_only && BKE_displist_has_faces(lb))) { - int retval; - draw_index_wire = false; - retval = drawDispListwire(lb); - draw_index_wire = true; - return retval; + return drawDispListwire(lb, ob->type); } } break; @@ -4212,7 +4420,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 } } else { - return drawDispListwire(lb); + return drawDispListwire(lb, ob->type); } break; case OB_MBALL: @@ -4237,8 +4445,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3 } } else { - /* MetaBalls use DL_INDEX4 type of DispList */ - return drawDispListwire(lb); + return drawDispListwire(lb, ob->type); } } break; @@ -4493,7 +4700,8 @@ static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d, /* 6. draw the arrays */ /* 7. clean up */ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv3d, - Base *base, ParticleSystem *psys, int ob_dt) + Base *base, ParticleSystem *psys, + const char ob_dt, const short dflag) { Object *ob = base->object; ParticleEditSettings *pset = PE_settings(scene); @@ -4571,7 +4779,9 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv copy_v3_v3(ma_col, &ma->r); } - glColor3ubv(tcol); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + glColor3ubv(tcol); + } timestep = psys_get_timestep(&sim); @@ -4661,7 +4871,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv normalize_v3(imat[1]); } - if (ELEM3(draw_as, PART_DRAW_DOT, PART_DRAW_CROSS, PART_DRAW_LINE) && + if (ELEM(draw_as, PART_DRAW_DOT, PART_DRAW_CROSS, PART_DRAW_LINE) && (part->draw_col > PART_DRAW_COL_MAT)) { create_cdata = 1; @@ -4906,8 +5116,10 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (1) { //ob_dt > OB_WIRE) { glEnableClientState(GL_NORMAL_ARRAY); - if (part->draw_col == PART_DRAW_COL_MAT) - glEnableClientState(GL_COLOR_ARRAY); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + if (part->draw_col == PART_DRAW_COL_MAT) + glEnableClientState(GL_COLOR_ARRAY); + } glEnable(GL_LIGHTING); glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); @@ -4937,8 +5149,11 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (1) { //ob_dt > OB_WIRE) { glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel); - if (part->draw_col == PART_DRAW_COL_MAT) - glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + if (part->draw_col == PART_DRAW_COL_MAT) { + glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + } + } } glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1); @@ -4953,8 +5168,11 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (1) { //ob_dt > OB_WIRE) { glNormalPointer(GL_FLOAT, sizeof(ParticleCacheKey), path->vel); - if (part->draw_col == PART_DRAW_COL_MAT) - glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + if (part->draw_col == PART_DRAW_COL_MAT) { + glColorPointer(3, GL_FLOAT, sizeof(ParticleCacheKey), path->col); + } + } } glDrawArrays(GL_LINE_STRIP, 0, path->steps + 1); @@ -4964,7 +5182,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv /* restore & clean up */ if (1) { //ob_dt > OB_WIRE) { if (part->draw_col == PART_DRAW_COL_MAT) - glDisable(GL_COLOR_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); glDisable(GL_COLOR_MATERIAL); } @@ -4998,21 +5216,24 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv else glDisableClientState(GL_VERTEX_ARRAY); - if (select) { - UI_ThemeColor(TH_ACTIVE); - - if (part->draw_size) - glPointSize(part->draw_size + 2); - else - glPointSize(4.0); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + if (select) { + UI_ThemeColor(TH_ACTIVE); - glLineWidth(3.0); + if (part->draw_size) + glPointSize(part->draw_size + 2); + else + glPointSize(4.0); + + glLineWidth(3.0); + + draw_particle_arrays(draw_as, totpoint, ob_dt, 1); + } - draw_particle_arrays(draw_as, totpoint, ob_dt, 1); + /* restore from select */ + glColor3fv(ma_col); } - /* restore from select */ - glColor3fv(ma_col); glPointSize(part->draw_size ? part->draw_size : 2.0); glLineWidth(1.0); @@ -5029,9 +5250,11 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv glDisable(GL_LIGHTING); } - if (pdd->cdata) { - glEnableClientState(GL_COLOR_ARRAY); - glColorPointer(3, GL_FLOAT, 0, pdd->cdata); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + if (pdd->cdata) { + glEnableClientState(GL_COLOR_ARRAY); + glColorPointer(3, GL_FLOAT, 0, pdd->cdata); + } } draw_particle_arrays(draw_as, totpoint, ob_dt, 0); @@ -5041,8 +5264,10 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv } if (pdd && pdd->vedata) { - glDisableClientState(GL_COLOR_ARRAY); - cpack(0xC0C0C0); + if ((dflag & DRAW_CONSTCOLOR) == 0) { + glDisableClientState(GL_COLOR_ARRAY); + cpack(0xC0C0C0); + } glVertexPointer(3, GL_FLOAT, 0, pdd->vedata); @@ -5658,7 +5883,7 @@ static void editnurb_draw_active_nurbs(Nurb *nu) glLineWidth(1); } -static void draw_editnurb(Object *ob, Nurb *nurb, int sel) +static void draw_editnurb_splines(Object *ob, Nurb *nurb, const bool sel) { Nurb *nu; BPoint *bp, *bp1; @@ -5774,8 +5999,9 @@ static void draw_editnurb(Object *ob, Nurb *nurb, int sel) } } -static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, Nurb *nurb, - const char dt, const short dflag, const unsigned char ob_wire_col[4]) +static void draw_editnurb( + Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, Nurb *nurb, + const char dt, const short dflag, const unsigned char ob_wire_col[4]) { ToolSettings *ts = scene->toolsettings; Object *ob = base->object; @@ -5793,6 +6019,11 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col); + /* for shadows only show solid faces */ + if (v3d->flag2 & V3D_RENDER_SHADOW) { + return; + } + if (v3d->zbuf) glDepthFunc(GL_ALWAYS); /* first non-selected and active handles */ @@ -5805,8 +6036,8 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, } index++; } - draw_editnurb(ob, nurb, 0); - draw_editnurb(ob, nurb, 1); + draw_editnurb_splines(ob, nurb, false); + draw_editnurb_splines(ob, nurb, true); /* selected handles */ for (nu = nurb; nu; nu = nu->next) { if (nu->type == CU_BEZIER && (cu->drawflag & CU_HIDE_HANDLES) == 0) @@ -5816,13 +6047,13 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, if (v3d->zbuf) glDepthFunc(GL_LEQUAL); + glColor3ubv(wire_col); + /* direction vectors for 3d curve paths * when at its lowest, don't render normals */ if ((cu->flag & CU_3D) && (ts->normalsize > 0.0015f) && (cu->drawflag & CU_HIDE_NORMALS) == 0) { - - UI_ThemeColor(TH_WIRE_EDIT); for (bl = ob->curve_cache->bev.first, nu = nurb; nu && bl; bl = bl->next, nu = nu->next) { - BevPoint *bevp = (BevPoint *)(bl + 1); + BevPoint *bevp = bl->bevpoints; int nr = bl->nr; int skip = nu->resolu / 16; @@ -5868,6 +6099,149 @@ static void drawnurb(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, if (v3d->zbuf) glDepthFunc(GL_LEQUAL); } +static void draw_editfont_textcurs(RegionView3D *rv3d, float textcurs[4][2]) +{ + cpack(0); + ED_view3d_polygon_offset(rv3d, -1.0); + set_inverted_drawing(1); + glBegin(GL_QUADS); + glVertex2fv(textcurs[0]); + glVertex2fv(textcurs[1]); + glVertex2fv(textcurs[2]); + glVertex2fv(textcurs[3]); + glEnd(); + set_inverted_drawing(0); + ED_view3d_polygon_offset(rv3d, 0.0); +} + +static void draw_editfont(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, + const char dt, const short dflag, const unsigned char ob_wire_col[4]) +{ + Object *ob = base->object; + Curve *cu = ob->data; + EditFont *ef = cu->editfont; + float vec1[3], vec2[3]; + int i, selstart, selend; + + draw_editfont_textcurs(rv3d, ef->textcurs); + + if (cu->flag & CU_FAST) { + cpack(0xFFFFFF); + set_inverted_drawing(1); + drawDispList(scene, v3d, rv3d, base, OB_WIRE, dflag, ob_wire_col); + set_inverted_drawing(0); + } + else { + drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col); + } + + if (cu->linewidth != 0.0f) { + UI_ThemeColor(TH_WIRE_EDIT); + copy_v3_v3(vec1, ob->orig); + copy_v3_v3(vec2, ob->orig); + vec1[0] += cu->linewidth; + vec2[0] += cu->linewidth; + vec1[1] += cu->linedist * cu->fsize; + vec2[1] -= cu->lines * cu->linedist * cu->fsize; + setlinestyle(3); + glBegin(GL_LINE_STRIP); + glVertex2fv(vec1); + glVertex2fv(vec2); + glEnd(); + setlinestyle(0); + } + + setlinestyle(3); + for (i = 0; i < cu->totbox; i++) { + if (cu->tb[i].w != 0.0f) { + UI_ThemeColor(i == (cu->actbox - 1) ? TH_ACTIVE : TH_WIRE); + vec1[0] = cu->xof + cu->tb[i].x; + vec1[1] = cu->yof + cu->tb[i].y + cu->fsize; + vec1[2] = 0.001; + glBegin(GL_LINE_STRIP); + glVertex3fv(vec1); + vec1[0] += cu->tb[i].w; + glVertex3fv(vec1); + vec1[1] -= cu->tb[i].h; + glVertex3fv(vec1); + vec1[0] -= cu->tb[i].w; + glVertex3fv(vec1); + vec1[1] += cu->tb[i].h; + glVertex3fv(vec1); + glEnd(); + } + } + setlinestyle(0); + + + if (BKE_vfont_select_get(ob, &selstart, &selend) && ef->selboxes) { + const int seltot = selend - selstart; + float selboxw; + + cpack(0xffffff); + set_inverted_drawing(1); + for (i = 0; i <= seltot; i++) { + EditFontSelBox *sb = &ef->selboxes[i]; + float tvec[3]; + + if (i != seltot) { + if (ef->selboxes[i + 1].y == sb->y) + selboxw = ef->selboxes[i + 1].x - sb->x; + else + selboxw = sb->w; + } + else { + selboxw = sb->w; + } + + /* fill in xy below */ + tvec[2] = 0.001; + + glBegin(GL_QUADS); + + if (sb->rot == 0.0f) { + copy_v2_fl2(tvec, sb->x, sb->y); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, sb->x + selboxw, sb->y); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, sb->x + selboxw, sb->y + sb->h); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, sb->x, sb->y + sb->h); + glVertex3fv(tvec); + } + else { + float mat[2][2]; + + angle_to_mat2(mat, sb->rot); + + copy_v2_fl2(tvec, sb->x, sb->y); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, selboxw, 0.0f); + mul_m2v2(mat, tvec); + add_v2_v2(tvec, &sb->x); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, selboxw, sb->h); + mul_m2v2(mat, tvec); + add_v2_v2(tvec, &sb->x); + glVertex3fv(tvec); + + copy_v2_fl2(tvec, 0.0f, sb->h); + mul_m2v2(mat, tvec); + add_v2_v2(tvec, &sb->x); + glVertex3fv(tvec); + } + + glEnd(); + } + set_inverted_drawing(0); + } +} + /* draw a sphere for use as an empty drawtype */ static void draw_empty_sphere(float size) { @@ -5924,21 +6298,6 @@ static void draw_empty_cone(float size) gluDeleteQuadric(qobj); } -static void draw_textcurs(RegionView3D *rv3d, float textcurs[4][2]) -{ - cpack(0); - ED_view3d_polygon_offset(rv3d, -1.0); - set_inverted_drawing(1); - glBegin(GL_QUADS); - glVertex2fv(textcurs[0]); - glVertex2fv(textcurs[1]); - glVertex2fv(textcurs[2]); - glVertex2fv(textcurs[3]); - glEnd(); - set_inverted_drawing(0); - ED_view3d_polygon_offset(rv3d, 0.0); -} - static void drawspiral(const float cent[3], float rad, float tmat[4][4], int start) { float vec[3], vx[3], vy[3]; @@ -6129,7 +6488,7 @@ static bool drawmball(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, if (G.f & G_PICKSEL) { ml->selcol1 = code; - glLoadName(code++); + GPU_select_load_id(code++); } } drawcircball(GL_LINE_LOOP, &(ml->x), ml->rad, imat); @@ -6143,7 +6502,7 @@ static bool drawmball(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, if (G.f & G_PICKSEL) { ml->selcol2 = code; - glLoadName(code++); + GPU_select_load_id(code++); } drawcircball(GL_LINE_LOOP, &(ml->x), ml->rad * atanf(ml->s) / (float)M_PI_2, imat); } @@ -6349,28 +6708,6 @@ static void draw_box(float vec[8][3]) glEnd(); } -/* uses boundbox, function used by Ketsji */ -#if 0 -static void get_local_bounds(Object *ob, float center[3], float size[3]) -{ - BoundBox *bb = BKE_object_boundbox_get(ob); - - if (bb == NULL) { - zero_v3(center); - copy_v3_v3(size, ob->size); - } - else { - size[0] = 0.5 * fabsf(bb->vec[0][0] - bb->vec[4][0]); - size[1] = 0.5 * fabsf(bb->vec[0][1] - bb->vec[2][1]); - size[2] = 0.5 * fabsf(bb->vec[0][2] - bb->vec[1][2]); - - center[0] = (bb->vec[0][0] + bb->vec[4][0]) / 2.0; - center[1] = (bb->vec[0][1] + bb->vec[2][1]) / 2.0; - center[2] = (bb->vec[0][2] + bb->vec[1][2]) / 2.0; - } -} -#endif - static void draw_bb_quadric(BoundBox *bb, char type, bool around_origin) { float size[3], cent[3]; @@ -6378,17 +6715,13 @@ static void draw_bb_quadric(BoundBox *bb, char type, bool around_origin) gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); - size[0] = 0.5f * fabsf(bb->vec[0][0] - bb->vec[4][0]); - size[1] = 0.5f * fabsf(bb->vec[0][1] - bb->vec[2][1]); - size[2] = 0.5f * fabsf(bb->vec[0][2] - bb->vec[1][2]); + BKE_boundbox_calc_size_aabb(bb, size); if (around_origin) { zero_v3(cent); } else { - cent[0] = 0.5f * (bb->vec[0][0] + bb->vec[4][0]); - cent[1] = 0.5f * (bb->vec[0][1] + bb->vec[2][1]); - cent[2] = 0.5f * (bb->vec[0][2] + bb->vec[1][2]); + BKE_boundbox_calc_center_aabb(bb, cent); } glPushMatrix(); @@ -6432,7 +6765,7 @@ static void draw_bounding_volume(Object *ob, char type) if (ob->type == OB_MESH) { bb = BKE_mesh_boundbox_get(ob); } - else if (ELEM3(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { bb = BKE_curve_boundbox_get(ob); } else if (ob->type == OB_MBALL) { @@ -6457,9 +6790,7 @@ static void draw_bounding_volume(Object *ob, char type) if (type == OB_BOUND_BOX) { float vec[8][3], size[3]; - size[0] = 0.5f * fabsf(bb->vec[0][0] - bb->vec[4][0]); - size[1] = 0.5f * fabsf(bb->vec[0][1] - bb->vec[2][1]); - size[2] = 0.5f * fabsf(bb->vec[0][2] - bb->vec[1][2]); + BKE_boundbox_calc_size_aabb(bb, size); vec[0][0] = vec[1][0] = vec[2][0] = vec[3][0] = -size[0]; vec[4][0] = vec[5][0] = vec[6][0] = vec[7][0] = +size[0]; @@ -6489,7 +6820,7 @@ static void drawtexspace(Object *ob) if (ob->type == OB_MESH) { BKE_mesh_texspace_get(ob->data, loc, NULL, size); } - else if (ELEM3(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF, OB_FONT)) { BKE_curve_texspace_get(ob->data, loc, NULL, size); } else if (ob->type == OB_MBALL) { @@ -6527,7 +6858,7 @@ static void drawObjectSelect(Scene *scene, View3D *v3d, ARegion *ar, Base *base, glLineWidth(UI_GetThemeValuef(TH_OUTLINE_WIDTH) * 2.0f); glDepthMask(0); - if (ELEM3(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { DerivedMesh *dm = ob->derivedFinal; bool has_faces = false; @@ -6545,20 +6876,19 @@ static void drawObjectSelect(Scene *scene, View3D *v3d, ARegion *ar, Base *base, } if (has_faces && ED_view3d_boundbox_clip(rv3d, ob->bb)) { - draw_index_wire = false; if (dm) { draw_mesh_object_outline(v3d, ob, dm); } else { - drawDispListwire(&ob->curve_cache->disp); + drawDispListwire(&ob->curve_cache->disp, ob->type); } - draw_index_wire = true; } } else if (ob->type == OB_MBALL) { if (BKE_mball_is_basis(ob)) { - if ((base->flag & OB_FROMDUPLI) == 0) - drawDispListwire(&ob->curve_cache->disp); + if ((base->flag & OB_FROMDUPLI) == 0) { + drawDispListwire(&ob->curve_cache->disp, ob->type); + } } } else if (ob->type == OB_ARMATURE) { @@ -6572,7 +6902,7 @@ static void drawObjectSelect(Scene *scene, View3D *v3d, ARegion *ar, Base *base, static void draw_wire_extra(Scene *scene, RegionView3D *rv3d, Object *ob, const unsigned char ob_wire_col[4]) { - if (ELEM4(ob->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL)) { + if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL)) { if (scene->obedit == ob) { UI_ThemeColor(TH_WIRE_EDIT); @@ -6584,25 +6914,20 @@ static void draw_wire_extra(Scene *scene, RegionView3D *rv3d, Object *ob, const ED_view3d_polygon_offset(rv3d, 1.0); glDepthMask(0); /* disable write in zbuffer, selected edge wires show better */ - if (ELEM3(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { + if (ELEM(ob->type, OB_FONT, OB_CURVE, OB_SURF)) { if (ED_view3d_boundbox_clip(rv3d, ob->bb)) { - if (ob->type == OB_CURVE) - draw_index_wire = false; if (ob->derivedFinal) { drawCurveDMWired(ob); } else { - drawDispListwire(&ob->curve_cache->disp); + drawDispListwire(&ob->curve_cache->disp, ob->type); } - - if (ob->type == OB_CURVE) - draw_index_wire = true; } } else if (ob->type == OB_MBALL) { if (BKE_mball_is_basis(ob)) { - drawDispListwire(&ob->curve_cache->disp); + drawDispListwire(&ob->curve_cache->disp, ob->type); } } @@ -6780,9 +7105,7 @@ static void draw_rigidbody_shape(Object *ob) switch (ob->rigidbody_object->shape) { case RB_SHAPE_BOX: - size[0] = 0.5f * fabsf(bb->vec[0][0] - bb->vec[4][0]); - size[1] = 0.5f * fabsf(bb->vec[0][1] - bb->vec[2][1]); - size[2] = 0.5f * fabsf(bb->vec[0][2] - bb->vec[1][2]); + BKE_boundbox_calc_size_aabb(bb, size); vec[0][0] = vec[1][0] = vec[2][0] = vec[3][0] = -size[0]; vec[4][0] = vec[5][0] = vec[6][0] = vec[7][0] = +size[0]; @@ -6818,32 +7141,32 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short Object *ob = base->object; Curve *cu; RegionView3D *rv3d = ar->regiondata; - float vec1[3], vec2[3]; unsigned int col = 0; unsigned char _ob_wire_col[4]; /* dont initialize this */ const unsigned char *ob_wire_col = NULL; /* dont initialize this, use NULL crashes as a way to find invalid use */ - int i, selstart, selend, empty_object = 0; short dtx; char dt; - bool zbufoff = false, is_paint = false; + bool zbufoff = false, is_paint = false, empty_object = false; const bool is_obact = (ob == OBACT); const bool render_override = (v3d->flag2 & V3D_RENDER_OVERRIDE) != 0; const bool is_picking = (G.f & G_PICKSEL) != 0; + const bool has_particles = (ob->particlesystem.first != NULL); bool particle_skip_object = false; /* Draw particles but not their emitter object. */ - /* only once set now, will be removed too, should become a global standard */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - if (ob != scene->obedit) { - if (ob->restrictflag & OB_RESTRICT_VIEW) { - return; - } - else if ((ob->restrictflag & OB_RESTRICT_RENDER) && render_override) { + if (ob->restrictflag & OB_RESTRICT_VIEW) return; + + if (render_override) { + if (ob->restrictflag & OB_RESTRICT_RENDER) + return; + + if (!has_particles && (ob->transflag & (OB_DUPLI & ~OB_DUPLIFRAMES))) + return; } } - if (ob->particlesystem.first) { + if (has_particles) { /* XXX particles are not safe for simultaneous threaded render */ if (G.is_rendering) { return; @@ -6883,7 +7206,13 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } } + + /* -------------------------------------------------------------------- */ /* no return after this point, otherwise leaks */ + + /* only once set now, will be removed too, should become a global standard */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + view3d_cached_text_draw_begin(); /* draw motion paths (in view space) */ @@ -6943,7 +7272,11 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } /* matcap check - only when not painting color */ - if ((v3d->flag2 & V3D_SOLID_MATCAP) && (dt == OB_SOLID) && (is_paint == false && is_picking == false)) { + if ((v3d->flag2 & V3D_SOLID_MATCAP) && + (dt == OB_SOLID) && + (is_paint == false && is_picking == false) && + ((v3d->flag2 & V3D_RENDER_SHADOW) == 0)) + { draw_object_matcap_check(v3d, ob); } @@ -6979,125 +7312,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short case OB_FONT: cu = ob->data; if (cu->editfont) { - EditFont *ef = cu->editfont; - - draw_textcurs(rv3d, ef->textcurs); - - if (cu->flag & CU_FAST) { - cpack(0xFFFFFF); - set_inverted_drawing(1); - drawDispList(scene, v3d, rv3d, base, OB_WIRE, dflag, ob_wire_col); - set_inverted_drawing(0); - } - else { - drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col); - } - - if (cu->linewidth != 0.0f) { - UI_ThemeColor(TH_WIRE_EDIT); - copy_v3_v3(vec1, ob->orig); - copy_v3_v3(vec2, ob->orig); - vec1[0] += cu->linewidth; - vec2[0] += cu->linewidth; - vec1[1] += cu->linedist * cu->fsize; - vec2[1] -= cu->lines * cu->linedist * cu->fsize; - setlinestyle(3); - glBegin(GL_LINE_STRIP); - glVertex2fv(vec1); - glVertex2fv(vec2); - glEnd(); - setlinestyle(0); - } - - setlinestyle(3); - for (i = 0; i < cu->totbox; i++) { - if (cu->tb[i].w != 0.0f) { - UI_ThemeColor(i == (cu->actbox - 1) ? TH_ACTIVE : TH_WIRE); - vec1[0] = (cu->xof * cu->fsize) + cu->tb[i].x; - vec1[1] = (cu->yof * cu->fsize) + cu->tb[i].y + cu->fsize; - vec1[2] = 0.001; - glBegin(GL_LINE_STRIP); - glVertex3fv(vec1); - vec1[0] += cu->tb[i].w; - glVertex3fv(vec1); - vec1[1] -= cu->tb[i].h; - glVertex3fv(vec1); - vec1[0] -= cu->tb[i].w; - glVertex3fv(vec1); - vec1[1] += cu->tb[i].h; - glVertex3fv(vec1); - glEnd(); - } - } - setlinestyle(0); - - - if (BKE_vfont_select_get(ob, &selstart, &selend) && ef->selboxes) { - const int seltot = selend - selstart; - float selboxw; - - cpack(0xffffff); - set_inverted_drawing(1); - for (i = 0; i <= seltot; i++) { - EditFontSelBox *sb = &ef->selboxes[i]; - float tvec[3]; - - if (i != seltot) { - if (ef->selboxes[i + 1].y == sb->y) - selboxw = ef->selboxes[i + 1].x - sb->x; - else - selboxw = sb->w; - } - else { - selboxw = sb->w; - } - - /* fill in xy below */ - tvec[2] = 0.001; - - glBegin(GL_QUADS); - - if (sb->rot == 0.0f) { - copy_v2_fl2(tvec, sb->x, sb->y); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, sb->x + selboxw, sb->y); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, sb->x + selboxw, sb->y + sb->h); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, sb->x, sb->y + sb->h); - glVertex3fv(tvec); - } - else { - float mat[2][2]; - - angle_to_mat2(mat, sb->rot); - - copy_v2_fl2(tvec, sb->x, sb->y); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, selboxw, 0.0f); - mul_m2v2(mat, tvec); - add_v2_v2(tvec, &sb->x); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, selboxw, sb->h); - mul_m2v2(mat, tvec); - add_v2_v2(tvec, &sb->x); - glVertex3fv(tvec); - - copy_v2_fl2(tvec, 0.0f, sb->h); - mul_m2v2(mat, tvec); - add_v2_v2(tvec, &sb->x); - glVertex3fv(tvec); - } - - glEnd(); - } - set_inverted_drawing(0); - } + draw_editfont(scene, v3d, rv3d, base, dt, dflag, ob_wire_col); } else if (dt == OB_BOUNDBOX) { if ((render_override && v3d->drawtype >= OB_WIRE) == 0) { @@ -7118,7 +7333,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (cu->editnurb) { ListBase *nurbs = BKE_curve_editNurbs_get(cu); - drawnurb(scene, v3d, rv3d, base, nurbs->first, dt, dflag, ob_wire_col); + draw_editnurb(scene, v3d, rv3d, base, nurbs->first, dt, dflag, ob_wire_col); } else if (dt == OB_BOUNDBOX) { if ((render_override && (v3d->drawtype >= OB_WIRE)) == 0) { @@ -7249,7 +7464,12 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short { ParticleSystem *psys; - if (col || (ob->flag & SELECT)) cpack(0xFFFFFF); /* for visibility, also while wpaint */ + if ((dflag & DRAW_CONSTCOLOR) == 0) { + /* for visibility, also while wpaint */ + if (col || (ob->flag & SELECT)) { + cpack(0xFFFFFF); + } + } //glDepthMask(GL_FALSE); glLoadMatrixf(rv3d->viewmat); @@ -7264,7 +7484,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short draw_update_ptcache_edit(scene, ob, edit); } - draw_new_particle_system(scene, v3d, rv3d, base, psys, dt); + draw_new_particle_system(scene, v3d, rv3d, base, psys, dt, dflag); } invert_m4_m4(ob->imat, ob->obmat); view3d_cached_text_draw_end(v3d, ar, 0, NULL); @@ -7292,7 +7512,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } /* draw code for smoke */ - if ((md = modifiers_findByType(ob, eModifierType_Smoke))) { + if ((md = modifiers_findByType(ob, eModifierType_Smoke)) && (modifier_isEnabled(scene, md, eModifierMode_Realtime))) { SmokeModifierData *smd = (SmokeModifierData *)md; // draw collision objects @@ -7638,8 +7858,6 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } } - free_old_images(); - ED_view3d_clear_mats_rv3d(rv3d); } @@ -7648,23 +7866,22 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short static void bbs_obmode_mesh_verts__mapFunc(void *userData, int index, const float co[3], const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) { - bbsObmodeMeshVerts_userData *data = userData; + drawMVertOffset_userData *data = userData; MVert *mv = &data->mvert[index]; - int offset = (intptr_t) data->offset; if (!(mv->flag & ME_HIDE)) { - WM_framebuffer_index_set(offset + index); + WM_framebuffer_index_set(data->offset + index); bglVertex3fv(co); } } static void bbs_obmode_mesh_verts(Object *ob, DerivedMesh *dm, int offset) { - bbsObmodeMeshVerts_userData data; + drawMVertOffset_userData data; Mesh *me = ob->data; MVert *mvert = me->mvert; data.mvert = mvert; - data.offset = (void *)(intptr_t) offset; + data.offset = offset; glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE)); bglBegin(GL_POINTS); dm->foreachMappedVert(dm, bbs_obmode_mesh_verts__mapFunc, &data, DM_FOREACH_NOP); @@ -7675,34 +7892,31 @@ static void bbs_obmode_mesh_verts(Object *ob, DerivedMesh *dm, int offset) static void bbs_mesh_verts__mapFunc(void *userData, int index, const float co[3], const float UNUSED(no_f[3]), const short UNUSED(no_s[3])) { - void **ptrs = userData; - int offset = (intptr_t) ptrs[0]; - BMVert *eve = BM_vert_at_index(ptrs[1], index); + drawBMOffset_userData *data = userData; + BMVert *eve = BM_vert_at_index(data->bm, index); if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - WM_framebuffer_index_set(offset + index); + WM_framebuffer_index_set(data->offset + index); bglVertex3fv(co); } } static void bbs_mesh_verts(BMEditMesh *em, DerivedMesh *dm, int offset) { - void *ptrs[2] = {(void *)(intptr_t) offset, em->bm}; - + drawBMOffset_userData data = {em->bm, offset}; glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE)); bglBegin(GL_POINTS); - dm->foreachMappedVert(dm, bbs_mesh_verts__mapFunc, ptrs, DM_FOREACH_NOP); + dm->foreachMappedVert(dm, bbs_mesh_verts__mapFunc, &data, DM_FOREACH_NOP); bglEnd(); glPointSize(1.0); } static DMDrawOption bbs_mesh_wire__setDrawOptions(void *userData, int index) { - void **ptrs = userData; - int offset = (intptr_t) ptrs[0]; - BMEdge *eed = BM_edge_at_index(ptrs[1], index); + drawBMOffset_userData *data = userData; + BMEdge *eed = BM_edge_at_index(data->bm, index); if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - WM_framebuffer_index_set(offset + index); + WM_framebuffer_index_set(data->offset + index); return DM_DRAW_OPTION_NORMAL; } else { @@ -7711,18 +7925,31 @@ static DMDrawOption bbs_mesh_wire__setDrawOptions(void *userData, int index) } static void bbs_mesh_wire(BMEditMesh *em, DerivedMesh *dm, int offset) { - void *ptrs[2] = {(void *)(intptr_t) offset, em->bm}; - dm->drawMappedEdges(dm, bbs_mesh_wire__setDrawOptions, ptrs); + drawBMOffset_userData data = {em->bm, offset}; + dm->drawMappedEdges(dm, bbs_mesh_wire__setDrawOptions, &data); } -static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int index) +/** + * dont set #WM_framebuffer_index_set. just use to mask other + */ +static DMDrawOption bbs_mesh_mask__setSolidDrawOptions(void *userData, int index) { - BMFace *efa = BM_face_at_index(((void **)userData)[0], index); + BMFace *efa = BM_face_at_index(userData, index); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - if (((void **)userData)[1]) { - WM_framebuffer_index_set(index + 1); - } + return DM_DRAW_OPTION_NORMAL; + } + else { + return DM_DRAW_OPTION_SKIP; + } +} + +static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int index) +{ + BMFace *efa = BM_face_at_index(userData, index); + + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + WM_framebuffer_index_set(index + 1); return DM_DRAW_OPTION_NORMAL; } else { @@ -7732,7 +7959,7 @@ static DMDrawOption bbs_mesh_solid__setSolidDrawOptions(void *userData, int inde static void bbs_mesh_solid__drawCenter(void *userData, int index, const float cent[3], const float UNUSED(no[3])) { - BMFace *efa = BM_face_at_index(((void **)userData)[0], index); + BMFace *efa = BM_face_at_index(userData, index); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { WM_framebuffer_index_set(index + 1); @@ -7743,26 +7970,24 @@ static void bbs_mesh_solid__drawCenter(void *userData, int index, const float ce /* two options, facecolors or black */ static void bbs_mesh_solid_EM(BMEditMesh *em, Scene *scene, View3D *v3d, - Object *ob, DerivedMesh *dm, int facecol) + Object *ob, DerivedMesh *dm, bool use_faceselect) { - void *ptrs[2] = {em->bm, NULL}; //second one being null means to draw black cpack(0); - if (facecol) { - ptrs[1] = (void *)(intptr_t) 1; - dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, ptrs, 0); + if (use_faceselect) { + dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, em->bm, 0); if (check_ob_drawface_dot(scene, v3d, ob->dt)) { glPointSize(UI_GetThemeValuef(TH_FACEDOT_SIZE)); bglBegin(GL_POINTS); - dm->foreachMappedFaceCenter(dm, bbs_mesh_solid__drawCenter, ptrs, DM_FOREACH_NOP); + dm->foreachMappedFaceCenter(dm, bbs_mesh_solid__drawCenter, em->bm, DM_FOREACH_NOP); bglEnd(); } } else { - dm->drawMappedFaces(dm, bbs_mesh_solid__setSolidDrawOptions, GPU_enable_material, NULL, ptrs, 0); + dm->drawMappedFaces(dm, bbs_mesh_mask__setSolidDrawOptions, GPU_enable_material, NULL, em->bm, 0); } } @@ -7851,7 +8076,7 @@ void draw_object_backbufsel(Scene *scene, View3D *v3d, RegionView3D *rv3d, Objec DM_update_materials(dm, ob); - bbs_mesh_solid_EM(em, scene, v3d, ob, dm, ts->selectmode & SCE_SELECT_FACE); + bbs_mesh_solid_EM(em, scene, v3d, ob, dm, (ts->selectmode & SCE_SELECT_FACE) != 0); if (ts->selectmode & SCE_SELECT_FACE) bm_solidoffs = 1 + em->bm->totface; else diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 4d958a50857..b3accb431ee 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -43,7 +43,9 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_depsgraph.h" #include "BKE_icons.h" +#include "BKE_library.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_scene.h" @@ -285,7 +287,7 @@ static void view3d_stop_render_preview(wmWindowManager *wm, ARegion *ar) } } -void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *sa) +void ED_view3d_shade_update(Main *bmain, Scene *scene, View3D *v3d, ScrArea *sa) { wmWindowManager *wm = bmain->wm.first; @@ -297,6 +299,10 @@ void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *sa) view3d_stop_render_preview(wm, ar); } } + else if (scene->obedit != NULL && scene->obedit->type == OB_MESH) { + /* Tag mesh to load edit data. */ + DAG_id_tag_update(scene->obedit->data, 0); + } } /* ******************** default callbacks for view3d space ***************** */ @@ -389,7 +395,16 @@ static SpaceLink *view3d_new(const bContext *C) static void view3d_free(SpaceLink *sl) { View3D *vd = (View3D *) sl; + BGpic *bgpic; + for (bgpic = vd->bgpicbase.first; bgpic; bgpic = bgpic->next) { + if (bgpic->source == V3D_BGPIC_IMAGE) { + id_us_min((ID *)bgpic->ima); + } + else if (bgpic->source == V3D_BGPIC_MOVIE) { + id_us_min((ID *)bgpic->clip); + } + } BLI_freelistN(&vd->bgpicbase); if (vd->localvd) MEM_freeN(vd->localvd); @@ -416,11 +431,10 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) { View3D *v3do = (View3D *)sl; View3D *v3dn = MEM_dupallocN(sl); + BGpic *bgpic; /* clear or remove stuff from old */ - -// XXX BIF_view3d_previewrender_free(v3do); - + if (v3dn->localvd) { v3dn->localvd = NULL; v3dn->properties_storage = NULL; @@ -433,8 +447,16 @@ static SpaceLink *view3d_duplicate(SpaceLink *sl) /* copy or clear inside new stuff */ v3dn->defmaterial = NULL; - + BLI_duplicatelist(&v3dn->bgpicbase, &v3do->bgpicbase); + for (bgpic = v3dn->bgpicbase.first; bgpic; bgpic = bgpic->next) { + if (bgpic->source == V3D_BGPIC_IMAGE) { + id_us_plus((ID *)bgpic->ima); + } + else if (bgpic->source == V3D_BGPIC_MOVIE) { + id_us_plus((ID *)bgpic->clip); + } + } v3dn->properties_storage = NULL; @@ -464,6 +486,12 @@ static void view3d_main_area_init(wmWindowManager *wm, ARegion *ar) keymap = WM_keymap_find(wm->defaultconf, "Object Mode", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Paint Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + + keymap = WM_keymap_find(wm->defaultconf, "Curve", 0, 0); + WM_event_add_keymap_handler(&ar->handlers, keymap); + keymap = WM_keymap_find(wm->defaultconf, "Image Paint", 0, 0); WM_event_add_keymap_handler(&ar->handlers, keymap); @@ -593,8 +621,11 @@ static int view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, const wmEvent * Base *base = ED_view3d_give_base_under_cursor(C, event->mval); /* either holding and ctrl and no object, or dropping to empty */ - if ((event->ctrl && !base) || (base && base->object->type == OB_EMPTY)) + if (((base == NULL) && event->ctrl) || + ((base != NULL) && base->object->type == OB_EMPTY)) + { return view3d_ima_drop_poll(C, drag, event); + } return 0; } @@ -651,7 +682,7 @@ static void view3d_dropboxes(void) WM_dropbox_add(lb, "MESH_OT_drop_named_image", view3d_ima_mesh_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "OBJECT_OT_drop_named_image", view3d_ima_empty_drop_poll, view3d_id_path_drop_copy); WM_dropbox_add(lb, "VIEW3D_OT_background_image_add", view3d_ima_bg_drop_poll, view3d_id_path_drop_copy); - WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy); + WM_dropbox_add(lb, "OBJECT_OT_group_instance_add", view3d_group_drop_poll, view3d_group_drop_copy); } @@ -665,10 +696,6 @@ static void view3d_main_area_free(ARegion *ar) if (rv3d->localvd) MEM_freeN(rv3d->localvd); if (rv3d->clipbb) MEM_freeN(rv3d->clipbb); - if (rv3d->ri) { - // XXX BIF_view3d_previewrender_free(rv3d); - } - if (rv3d->render_engine) RE_engine_free(rv3d->render_engine); @@ -702,7 +729,6 @@ static void *view3d_main_area_duplicate(void *poin) new->depths = NULL; new->gpuoffscreen = NULL; - new->ri = NULL; new->render_engine = NULL; new->sms = NULL; new->smooth_timer = NULL; @@ -757,7 +783,7 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN break; case ND_NLA: case ND_KEYFRAME: - if (wmn->action == NA_EDITED) + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) ED_region_tag_redraw(ar); break; case ND_ANIMCHAN: @@ -838,14 +864,18 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN switch (wmn->data) { case ND_SHADING: case ND_NODES: + { + Object *ob = OBACT; if ((v3d->drawtype == OB_MATERIAL) || + (ob && (ob->mode == OB_MODE_TEXTURE_PAINT)) || (v3d->drawtype == OB_TEXTURE && - (scene->gm.matmode == GAME_MAT_GLSL || - BKE_scene_use_new_shading_nodes(scene)))) + (scene->gm.matmode == GAME_MAT_GLSL || + BKE_scene_use_new_shading_nodes(scene)))) { ED_region_tag_redraw(ar); } break; + } case ND_SHADING_DRAW: case ND_SHADING_LINKS: ED_region_tag_redraw(ar); @@ -883,7 +913,7 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN ED_region_tag_redraw(ar); break; case NC_MOVIECLIP: - if (wmn->data == ND_DISPLAY) + if (wmn->data == ND_DISPLAY || wmn->action == NA_EDITED) ED_region_tag_redraw(ar); break; case NC_SPACE: @@ -968,6 +998,7 @@ static void view3d_header_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa) case ND_LAYER: case ND_TOOLSETTINGS: case ND_LAYER_CONTENT: + case ND_RENDER_OPTIONS: ED_region_tag_redraw(ar); break; } @@ -1007,7 +1038,7 @@ static void view3d_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa break; case ND_NLA: case ND_KEYFRAME: - if (wmn->action == NA_EDITED) + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) ED_region_tag_redraw(ar); break; } @@ -1076,6 +1107,11 @@ static void view3d_buttons_area_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa if (wmn->data == ND_DATA || wmn->action == NA_EDITED) ED_region_tag_redraw(ar); break; + case NC_IMAGE: + /* Update for the image layers in texture paint. */ + if (wmn->action == NA_EDITED) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c index dfbae90655c..6a43357179e 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -82,7 +82,7 @@ #define B_REDR 2 #define B_OBJECTPANELMEDIAN 1008 -#define NBR_TRANSFORM_PROPERTIES 7 +#define NBR_TRANSFORM_PROPERTIES 8 /* temporary struct for storing transform properties */ typedef struct { @@ -124,38 +124,84 @@ static float compute_scale_factor(const float ve_median, const float median) } } +/* Apply helpers. + * Note: In case we only have one element, copy directly the value instead of applying the diff or scale factor. + * Avoids some glitches when going e.g. from 3 to 0.0001 (see T37327). + */ +static void apply_raw_diff(float *val, const int tot, const float ve_median, const float median) +{ + *val = (tot == 1) ? ve_median : (*val + median); +} + +static void apply_raw_diff_v3(float val[3], const int tot, const float ve_median[3], const float median[3]) +{ + if (tot == 1) { + copy_v3_v3(val, ve_median); + } + else { + add_v3_v3(val, median); + } +} + +static void apply_scale_factor(float *val, const int tot, const float ve_median, const float median, const float sca) +{ + if (tot == 1 || ve_median == median) { + *val = ve_median; + } + else { + *val *= sca; + } +} + +static void apply_scale_factor_clamp(float *val, const int tot, const float ve_median, const float sca) +{ + if (tot == 1) { + *val = ve_median; + CLAMP(*val, 0.0f, 1.0f); + } + else if (ELEM(sca, 0.0f, 1.0f)) { + *val = sca; + } + else { + *val = (sca > 0.0f) ? (*val * sca) : (1.0f + ((1.0f - *val) * sca)); + CLAMP(*val, 0.0f, 1.0f); + } +} + /* is used for both read and write... */ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float lim) { /* Get rid of those ugly magic numbers, even in a single func they become confusing! */ /* Location, common to all. */ -/* XXX Those two *must* remain contiguous (used as array)! */ -#define LOC_X 0 -#define LOC_Y 1 -#define LOC_Z 2 +/* Next three *must* remain contiguous (used as array)! */ +#define LOC_X 0 +#define LOC_Y 1 +#define LOC_Z 2 /* Meshes... */ -#define M_CREASE 3 -#define M_WEIGHT 4 -/* XXX Those two *must* remain contiguous (used as array)! */ -#define M_SKIN_X 5 -#define M_SKIN_Y 6 +#define M_BV_WEIGHT 3 +/* Next two *must* remain contiguous (used as array)! */ +#define M_SKIN_X 4 +#define M_SKIN_Y 5 +#define M_BE_WEIGHT 6 +#define M_CREASE 7 /* Curves... */ -#define C_BWEIGHT 3 -#define C_WEIGHT 4 -#define C_RADIUS 5 -#define C_TILT 6 +#define C_BWEIGHT 3 +#define C_WEIGHT 4 +#define C_RADIUS 5 +#define C_TILT 6 /*Lattice... */ -#define L_WEIGHT 4 +#define L_WEIGHT 4 uiBlock *block = (layout) ? uiLayoutAbsoluteBlock(layout) : NULL; TransformProperties *tfp; float median[NBR_TRANSFORM_PROPERTIES], ve_median[NBR_TRANSFORM_PROPERTIES]; - int tot, totedgedata, totcurvedata, totlattdata, totskinradius, totcurvebweight; + int tot, totedgedata, totcurvedata, totlattdata, totcurvebweight; bool has_meshdata = false; + bool has_skinradius = false; PointerRNA data_ptr; fill_vn_fl(median, NBR_TRANSFORM_PROPERTIES, 0.0f); - tot = totedgedata = totcurvedata = totlattdata = totskinradius = totcurvebweight = 0; + tot = totedgedata = totcurvedata = totlattdata = totcurvebweight = 0; /* make sure we got storage */ if (v3d->properties_storage == NULL) @@ -170,37 +216,37 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float BMEdge *eed; BMIter iter; - const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); const int cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + has_skinradius = (cd_vert_skin_offset != -1); + if (bm->totvertsel) { BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { tot++; add_v3_v3(&median[LOC_X], eve->co); - /* TODO cd_vert_bweight_offset */ - (void)cd_vert_bweight_offset; + if (cd_vert_bweight_offset != -1) { + median[M_BV_WEIGHT] += BM_ELEM_CD_GET_FLOAT(eve, cd_vert_bweight_offset); + } - if (cd_vert_skin_offset != -1) { + if (has_skinradius) { MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset); add_v2_v2(&median[M_SKIN_X], vs->radius); /* Third val not used currently. */ - totskinradius++; } } } } - if ((cd_edge_bweight_offset != -1) || - (cd_edge_crease_offset != -1)) - { + if ((cd_edge_bweight_offset != -1) || (cd_edge_crease_offset != -1)) { if (bm->totedgesel) { BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { if (cd_edge_bweight_offset != -1) { - median[M_WEIGHT] += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_bweight_offset); + median[M_BE_WEIGHT] += BM_ELEM_CD_GET_FLOAT(eed, cd_edge_bweight_offset); } if (cd_edge_crease_offset != -1) { @@ -216,7 +262,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float totedgedata = bm->totedgesel; } - has_meshdata = (totedgedata || totskinradius); + has_meshdata = (tot || totedgedata); } else if (ob->type == OB_CURVE || ob->type == OB_SURF) { Curve *cu = ob->data; @@ -326,22 +372,27 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float if (has_meshdata) { if (totedgedata) { median[M_CREASE] /= (float)totedgedata; - median[M_WEIGHT] /= (float)totedgedata; + median[M_BE_WEIGHT] /= (float)totedgedata; } - if (totskinradius) { - median[M_SKIN_X] /= (float)totskinradius; - median[M_SKIN_Y] /= (float)totskinradius; + if (tot) { + median[M_BV_WEIGHT] /= (float)tot; + if (has_skinradius) { + median[M_SKIN_X] /= (float)tot; + median[M_SKIN_Y] /= (float)tot; + } } } else if (totcurvedata) { + if (totcurvebweight) { + median[C_BWEIGHT] /= (float)totcurvebweight; + } median[C_WEIGHT] /= (float)totcurvedata; median[C_RADIUS] /= (float)totcurvedata; median[C_TILT] /= (float)totcurvedata; - if (totcurvebweight) - median[C_BWEIGHT] /= (float)totcurvebweight; } - else if (totlattdata) + else if (totlattdata) { median[L_WEIGHT] /= (float)totlattdata; + } if (block) { /* buttons */ uiBut *but; @@ -393,27 +444,40 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float /* Meshes... */ if (has_meshdata) { - if (totedgedata) { + if (tot) { + uiDefBut(block, LABEL, 0, tot == 1 ? IFACE_("Vertex Data:") : IFACE_("Vertices Data:"), + 0, yi -= buth + but_margin, 200, buth, NULL, 0.0, 0.0, 0, 0, ""); /* customdata layer added on demand */ uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, - totedgedata == 1 ? IFACE_("Crease:") : IFACE_("Mean Crease:"), + tot == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"), 0, yi -= buth + but_margin, 200, buth, - &(tfp->ve_median[M_CREASE]), 0.0, 1.0, 1, 2, TIP_("Weight used by SubSurf modifier")); - /* customdata layer added on demand */ - uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, - totedgedata == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"), - 0, yi -= buth + but_margin, 200, buth, - &(tfp->ve_median[M_WEIGHT]), 0.0, 1.0, 1, 2, TIP_("Weight used by Bevel modifier")); + &(tfp->ve_median[M_BV_WEIGHT]), 0.0, 1.0, 1, 2, TIP_("Vertex weight used by Bevel modifier")); } - if (totskinradius) { + if (has_skinradius) { + uiBlockBeginAlign(block); uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, - totskinradius == 1 ? IFACE_("Radius X:") : IFACE_("Mean Radius X:"), + tot == 1 ? IFACE_("Radius X:") : IFACE_("Mean Radius X:"), 0, yi -= buth + but_margin, 200, buth, &(tfp->ve_median[M_SKIN_X]), 0.0, 100.0, 1, 3, TIP_("X radius used by Skin modifier")); uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, - totskinradius == 1 ? IFACE_("Radius Y:") : IFACE_("Mean Radius Y:"), + tot == 1 ? IFACE_("Radius Y:") : IFACE_("Mean Radius Y:"), 0, yi -= buth + but_margin, 200, buth, &(tfp->ve_median[M_SKIN_Y]), 0.0, 100.0, 1, 3, TIP_("Y radius used by Skin modifier")); + uiBlockEndAlign(block); + } + if (totedgedata) { + uiDefBut(block, LABEL, 0, totedgedata == 1 ? IFACE_("Edge Data:") : IFACE_("Edges Data:"), + 0, yi -= buth + but_margin, 200, buth, NULL, 0.0, 0.0, 0, 0, ""); + /* customdata layer added on demand */ + uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, + totedgedata == 1 ? IFACE_("Bevel Weight:") : IFACE_("Mean Bevel Weight:"), + 0, yi -= buth + but_margin, 200, buth, + &(tfp->ve_median[M_BE_WEIGHT]), 0.0, 1.0, 1, 2, TIP_("Edge weight used by Bevel modifier")); + /* customdata layer added on demand */ + uiDefButF(block, NUM, B_OBJECTPANELMEDIAN, + totedgedata == 1 ? IFACE_("Crease:") : IFACE_("Mean Crease:"), + 0, yi -= buth + but_margin, 200, buth, + &(tfp->ve_median[M_CREASE]), 0.0, 1.0, 1, 2, TIP_("Weight used by SubSurf modifier")); } } /* Curve... */ @@ -450,11 +514,10 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float } uiBlockEndAlign(block); - uiBlockEndAlign(block); - } else { /* apply */ int i; + bool apply_vcos; memcpy(ve_median, tfp->ve_median, sizeof(tfp->ve_median)); @@ -467,155 +530,130 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float while (i--) median[i] = ve_median[i] - median[i]; - if (ob->type == OB_MESH) { + /* Note with a single element selected, we always do. */ + apply_vcos = (tot == 1) || (len_squared_v3(&median[LOC_X]) != 0.0f); + + if ((ob->type == OB_MESH) && + (apply_vcos || median[M_BV_WEIGHT] || median[M_SKIN_X] || median[M_SKIN_Y] || + median[M_BE_WEIGHT] || median[M_CREASE])) + { Mesh *me = ob->data; BMEditMesh *em = me->edit_btmesh; BMesh *bm = em->bm; BMIter iter; + BMVert *eve; + BMEdge *eed; - if (tot == 1 || len_v3(&median[LOC_X]) != 0.0f) { - BMVert *eve; + int cd_vert_bweight_offset = -1; + int cd_vert_skin_offset = -1; + int cd_edge_bweight_offset = -1; + int cd_edge_crease_offset = -1; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (tot == 1) { - /* In case we only have one element selected, copy directly the value instead of applying - * the diff. Avoids some glitches when going e.g. from 3 to 0.0001 (see [#37327]). - */ - copy_v3_v3(eve->co, &ve_median[LOC_X]); - } - else { - add_v3_v3(eve->co, &median[LOC_X]); - } - } - } + float scale_bv_weight = 1.0f; + float scale_skin_x = 1.0f; + float scale_skin_y = 1.0f; + float scale_be_weight = 1.0f; + float scale_crease = 1.0f; - EDBM_mesh_normals_update(em); - } + /* Vertices */ - if (median[M_CREASE] != 0.0f) { - const int cd_edge_crease_offset = (BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE), - CustomData_get_offset(&bm->edata, CD_CREASE)); - const float sca = compute_scale_factor(ve_median[M_CREASE], median[M_CREASE]); - BMEdge *eed; + if (apply_vcos || median[M_BV_WEIGHT] || median[M_SKIN_X] || median[M_SKIN_Y]) { + if (median[M_BV_WEIGHT]) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_VERT_BWEIGHT); + cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); + BLI_assert(cd_vert_bweight_offset != -1); - if (ELEM(sca, 0.0f, 1.0f)) { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - BM_ELEM_CD_SET_FLOAT(eed, cd_edge_crease_offset, sca); - } - } - } - else if (sca > 0.0f) { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float *crease = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_crease_offset); - *crease *= sca; - CLAMP(*crease, 0.0f, 1.0f); - } - } + scale_bv_weight = compute_scale_factor(ve_median[M_BV_WEIGHT], median[M_BV_WEIGHT]); } - else { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float *crease = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_crease_offset); - *crease = 1.0f + ((1.0f - *crease) * sca); - CLAMP(*crease, 0.0f, 1.0f); - } - } - } - } - if (median[M_WEIGHT] != 0.0f) { - const int cd_edge_bweight_offset = (BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT), - CustomData_get_offset(&bm->edata, CD_BWEIGHT)); - const float sca = compute_scale_factor(ve_median[M_WEIGHT], median[M_WEIGHT]); - BMEdge *eed; + if (median[M_SKIN_X]) { + cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); + BLI_assert(cd_vert_skin_offset != -1); - BLI_assert(cd_edge_bweight_offset != -1); - - if (ELEM(sca, 0.0f, 1.0f)) { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - float *bweight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset); - *bweight = sca; - } + if (ve_median[M_SKIN_X] != median[M_SKIN_X]) { + scale_skin_x = ve_median[M_SKIN_X] / (ve_median[M_SKIN_X] - median[M_SKIN_X]); } } - else if (sca > 0.0f) { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float *bweight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset); - *bweight *= sca; - CLAMP(*bweight, 0.0f, 1.0f); - } + if (median[M_SKIN_Y]) { + if (cd_vert_skin_offset == -1) { + cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); + BLI_assert(cd_vert_skin_offset != -1); } - } - else { - BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT) && !BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - float *bweight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset); - *bweight = 1.0f + ((1.0f - *bweight) * sca); - CLAMP(*bweight, 0.0f, 1.0f); - } + + if (ve_median[M_SKIN_Y] != median[M_SKIN_Y]) { + scale_skin_y = ve_median[M_SKIN_Y] / (ve_median[M_SKIN_Y] - median[M_SKIN_Y]); } } - } - - if (median[M_SKIN_X] != 0.0f) { - const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); - /* That one is not clamped to [0.0, 1.0]. */ - float sca = ve_median[M_SKIN_X]; - BMVert *eve; - BLI_assert(cd_vert_skin_offset != -1); + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (apply_vcos) { + apply_raw_diff_v3(eve->co, tot, &ve_median[LOC_X], &median[LOC_X]); + } - if (ve_median[M_SKIN_X] - median[M_SKIN_X] == 0.0f) { - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset); - vs->radius[0] = sca; + if (cd_vert_bweight_offset != -1) { + float *bweight = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset); + apply_scale_factor_clamp(bweight, tot, ve_median[M_BV_WEIGHT], scale_bv_weight); } - } - } - else { - sca /= (ve_median[M_SKIN_X] - median[M_SKIN_X]); - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + + if (cd_vert_skin_offset != -1) { MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset); - vs->radius[0] *= sca; + + /* That one is not clamped to [0.0, 1.0]. */ + if (median[M_SKIN_X] != 0.0f) { + apply_scale_factor(&vs->radius[0], tot, ve_median[M_SKIN_X], median[M_SKIN_X], + scale_skin_x); + } + if (median[M_SKIN_Y] != 0.0f) { + apply_scale_factor(&vs->radius[1], tot, ve_median[M_SKIN_Y], median[M_SKIN_Y], + scale_skin_y); + } } } } } - if (median[M_SKIN_Y] != 0.0f) { - const int cd_vert_skin_offset = CustomData_get_offset(&bm->vdata, CD_MVERT_SKIN); - /* That one is not clamped to [0.0, 1.0]. */ - float sca = ve_median[M_SKIN_Y]; - BMVert *eve; - BLI_assert(cd_vert_skin_offset != -1); + if (apply_vcos) { + EDBM_mesh_normals_update(em); + } + + /* Edges */ - if (ve_median[M_SKIN_Y] - median[M_SKIN_Y] == 0.0f) { - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset); - vs->radius[1] = sca; - } - } + if (median[M_BE_WEIGHT] || median[M_CREASE]) { + if (median[M_BE_WEIGHT]) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_BWEIGHT); + cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); + BLI_assert(cd_edge_bweight_offset != -1); + + scale_be_weight = compute_scale_factor(ve_median[M_BE_WEIGHT], median[M_BE_WEIGHT]); } - else { - sca /= (ve_median[M_SKIN_Y] - median[M_SKIN_Y]); - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, cd_vert_skin_offset); - vs->radius[1] *= sca; + + if (median[M_CREASE]) { + BM_mesh_cd_flag_ensure(bm, me, ME_CDFLAG_EDGE_CREASE); + cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + BLI_assert(cd_edge_crease_offset != -1); + + scale_crease = compute_scale_factor(ve_median[M_CREASE], median[M_CREASE]); + } + + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + if (median[M_BE_WEIGHT] != 0.0f) { + float *bweight = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_bweight_offset); + apply_scale_factor_clamp(bweight, tot, ve_median[M_BE_WEIGHT], scale_be_weight); + } + + if (median[M_CREASE] != 0.0f) { + float *crease = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_crease_offset); + apply_scale_factor_clamp(crease, tot, ve_median[M_CREASE], scale_crease); } } } } } - else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + else if (ELEM(ob->type, OB_CURVE, OB_SURF) && + (apply_vcos || median[C_BWEIGHT] || median[C_WEIGHT] || median[C_RADIUS] || median[C_TILT])) + { Curve *cu = ob->data; Nurb *nu; BPoint *bp; @@ -629,44 +667,31 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float if (nu->type == CU_BEZIER) { for (a = nu->pntsu, bezt = nu->bezt; a--; bezt++) { if (bezt->f2 & SELECT) { - /* Here we always have to use the diff... :/ - * Cannot avoid some glitches when going e.g. from 3 to 0.0001 (see [#37327]), - * unless we use doubles. - */ - add_v3_v3(bezt->vec[0], &median[LOC_X]); - add_v3_v3(bezt->vec[1], &median[LOC_X]); - add_v3_v3(bezt->vec[2], &median[LOC_X]); - - if (median[C_WEIGHT] != 0.0f) { - if (ELEM(scale_w, 0.0f, 1.0f)) { - bezt->weight = scale_w; - } - else { - bezt->weight = scale_w > 0.0f ? bezt->weight * scale_w : - 1.0f + ((1.0f - bezt->weight) * scale_w); - CLAMP(bezt->weight, 0.0f, 1.0f); - } + if (apply_vcos) { + /* Here we always have to use the diff... :/ + * Cannot avoid some glitches when going e.g. from 3 to 0.0001 (see T37327), + * unless we use doubles. + */ + add_v3_v3(bezt->vec[0], &median[LOC_X]); + add_v3_v3(bezt->vec[1], &median[LOC_X]); + add_v3_v3(bezt->vec[2], &median[LOC_X]); + } + if (median[C_WEIGHT]) { + apply_scale_factor_clamp(&bezt->weight, tot, ve_median[C_WEIGHT], scale_w); + } + if (median[C_RADIUS]) { + apply_raw_diff(&bezt->radius, tot, ve_median[C_RADIUS], median[C_RADIUS]); + } + if (median[C_TILT]) { + apply_raw_diff(&bezt->alfa, tot, ve_median[C_TILT], median[C_TILT]); } - - bezt->radius += median[C_RADIUS]; - bezt->alfa += median[C_TILT]; } - else { + else if (apply_vcos) { /* Handles can only have their coordinates changed here. */ if (bezt->f1 & SELECT) { - if (tot == 1) { - copy_v3_v3(bezt->vec[0], &ve_median[LOC_X]); - } - else { - add_v3_v3(bezt->vec[0], &median[LOC_X]); - } + apply_raw_diff_v3(bezt->vec[0], tot, &ve_median[LOC_X], &median[LOC_X]); } if (bezt->f3 & SELECT) { - if (tot == 1) { - copy_v3_v3(bezt->vec[2], &ve_median[LOC_X]); - } - else { - add_v3_v3(bezt->vec[2], &median[LOC_X]); - } + apply_raw_diff_v3(bezt->vec[2], tot, &ve_median[LOC_X], &median[LOC_X]); } } } @@ -674,28 +699,20 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float else { for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a--; bp++) { if (bp->f1 & SELECT) { - if (tot == 1) { - copy_v3_v3(bp->vec, &ve_median[LOC_X]); - bp->vec[3] = ve_median[C_BWEIGHT]; - bp->radius = ve_median[C_RADIUS]; - bp->alfa = ve_median[C_TILT]; + if (apply_vcos) { + apply_raw_diff_v3(bp->vec, tot, &ve_median[LOC_X], &median[LOC_X]); } - else { - add_v3_v3(bp->vec, &median[LOC_X]); - bp->vec[3] += median[C_BWEIGHT]; - bp->radius += median[C_RADIUS]; - bp->alfa += median[C_TILT]; + if (median[C_BWEIGHT]) { + apply_raw_diff(&bp->vec[3], tot, ve_median[C_BWEIGHT], median[C_BWEIGHT]); } - - if (median[C_WEIGHT] != 0.0f) { - if (ELEM(scale_w, 0.0f, 1.0f)) { - bp->weight = scale_w; - } - else { - bp->weight = scale_w > 0.0f ? bp->weight * scale_w : - 1.0f + ((1.0f - bp->weight) * scale_w); - CLAMP(bp->weight, 0.0f, 1.0f); - } + if (median[C_WEIGHT]) { + apply_scale_factor_clamp(&bp->weight, tot, ve_median[C_WEIGHT], scale_w); + } + if (median[C_RADIUS]) { + apply_raw_diff(&bp->radius, tot, ve_median[C_RADIUS], median[C_RADIUS]); + } + if (median[C_TILT]) { + apply_raw_diff(&bp->alfa, tot, ve_median[C_TILT], median[C_TILT]); } } } @@ -706,7 +723,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float nu = nu->next; } } - else if (ob->type == OB_LATTICE) { + else if ((ob->type == OB_LATTICE) && (apply_vcos || median[L_WEIGHT])) { Lattice *lt = ob->data; BPoint *bp; int a; @@ -716,22 +733,11 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float bp = lt->editlatt->latt->def; while (a--) { if (bp->f1 & SELECT) { - if (tot == 1) { - copy_v3_v3(bp->vec, &ve_median[LOC_X]); - } - else { - add_v3_v3(bp->vec, &median[LOC_X]); + if (apply_vcos) { + apply_raw_diff_v3(bp->vec, tot, &ve_median[LOC_X], &median[LOC_X]); } - - if (median[L_WEIGHT] != 0.0f) { - if (ELEM(scale_w, 0.0f, 1.0f)) { - bp->weight = scale_w; - } - else { - bp->weight = scale_w > 0.0f ? bp->weight * scale_w : - 1.0f + ((1.0f - bp->weight) * scale_w); - CLAMP(bp->weight, 0.0f, 1.0f); - } + if (median[L_WEIGHT]) { + apply_scale_factor_clamp(&bp->weight, tot, ve_median[L_WEIGHT], scale_w); } } bp++; @@ -747,10 +753,11 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float #undef LOC_Y #undef LOC_Z /* Meshes (and lattice)... */ -#undef M_CREASE -#undef M_WEIGHT +#undef M_BV_WEIGHT #undef M_SKIN_X #undef M_SKIN_Y +#undef M_BE_WEIGHT +#undef M_CREASE /* Curves... */ #undef C_BWEIGHT #undef C_WEIGHT @@ -1182,8 +1189,8 @@ void view3d_buttons_register(ARegionType *art) strcpy(pt->idname, "VIEW3D_PT_gpencil"); strcpy(pt->label, N_("Grease Pencil")); /* XXX C panels are not available through RNA (bpy.types)! */ strcpy(pt->translation_context, BLF_I18NCONTEXT_DEFAULT_BPYRNA); - pt->draw_header = gpencil_panel_standard_header; - pt->draw = gpencil_panel_standard; + pt->draw_header = ED_gpencil_panel_standard_header; + pt->draw = ED_gpencil_panel_standard; BLI_addtail(&art->paneltypes, pt); pt = MEM_callocN(sizeof(PanelType), "spacetype view3d panel vgroup"); diff --git a/source/blender/editors/space_view3d/view3d_camera_control.c b/source/blender/editors/space_view3d/view3d_camera_control.c index 230df49f386..ee0f3da18b4 100644 --- a/source/blender/editors/space_view3d/view3d_camera_control.c +++ b/source/blender/editors/space_view3d/view3d_camera_control.c @@ -31,9 +31,9 @@ * * Typical view-control usage: * - * - aquire a view-control (#ED_view3d_control_aquire). + * - acquire a view-control (#ED_view3d_control_acquire). * - modify ``rv3d->ofs``, ``rv3d->viewquat``. - * - update the view data (#ED_view3d_control_aquire) - within a loop which draws the viewport. + * - update the view data (#ED_view3d_control_acquire) - within a loop which draws the viewport. * - finish and release the view-control (#ED_view3d_control_release), * either keeping the current view or restoring the initial view. * @@ -138,7 +138,7 @@ Object *ED_view3d_cameracontrol_object_get(View3DCameraControl *vctrl) * Creates a #View3DControl handle and sets up * the view for first-person style navigation. */ -struct View3DCameraControl *ED_view3d_cameracontrol_aquire( +struct View3DCameraControl *ED_view3d_cameracontrol_acquire( Scene *scene, View3D *v3d, RegionView3D *rv3d, const bool use_parent_root) { @@ -272,28 +272,8 @@ void ED_view3d_cameracontrol_update( } /* record the motion */ - if (use_autokey && autokeyframe_cfra_can_key(scene, id_key)) { - ListBase dsources = {NULL, NULL}; - - /* add data-source override for the camera object */ - ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); - - /* insert keyframes - * 1) on the first frame - * 2) on each subsequent frame - * TODO: need to check in future that frame changed before doing this - */ - if (do_rotate) { - struct KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - } - if (do_translate) { - struct KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID); - ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); - } - - /* free temp data */ - BLI_freelistN(&dsources); + if (use_autokey) { + ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate); } } @@ -302,7 +282,7 @@ void ED_view3d_cameracontrol_update( * Release view control. * * \param restore Sets the view state to the values that were set - * before #ED_view3d_control_aquire was called. + * before #ED_view3d_control_acquire was called. */ void ED_view3d_cameracontrol_release( View3DCameraControl *vctrl, diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 9ce6bf58a0a..61bfb0176ef 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -37,6 +37,7 @@ #include "DNA_customdata_types.h" #include "DNA_object_types.h" #include "DNA_group_types.h" +#include "DNA_mesh_types.h" #include "DNA_key_types.h" #include "DNA_lamp_types.h" #include "DNA_scene_types.h" @@ -880,7 +881,7 @@ static void draw_selected_name(Scene *scene, Object *ob, rcti *rect) } } } - else if (ELEM3(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) { + else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVE)) { Key *key = NULL; KeyBlock *kb = NULL; @@ -1090,6 +1091,9 @@ static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) /* passepartout, specified in camera edit buttons */ if (ca && (ca->flag & CAM_SHOWPASSEPARTOUT) && ca->passepartalpha > 0.000001f) { + const float winx = (ar->winx + 1); + const float winy = (ar->winy + 1); + if (ca->passepartalpha == 1.0f) { glColor3f(0, 0, 0); } @@ -1099,11 +1103,11 @@ static void drawviewborder(Scene *scene, ARegion *ar, View3D *v3d) glColor4f(0, 0, 0, ca->passepartalpha); } if (x1i > 0.0f) - glRectf(0.0, (float)ar->winy, x1i, 0.0); - if (x2i < (float)ar->winx) - glRectf(x2i, (float)ar->winy, (float)ar->winx, 0.0); - if (y2i < (float)ar->winy) - glRectf(x1i, (float)ar->winy, x2i, y2i); + glRectf(0.0, winy, x1i, 0.0); + if (x2i < winx) + glRectf(x2i, winy, winx, 0.0); + if (y2i < winy) + glRectf(x1i, winy, x2i, y2i); if (y2i > 0.0f) glRectf(x1i, y1i, x2i, 0.0); @@ -1286,6 +1290,12 @@ static void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d) { /* do nothing */ } + /* texture paint mode sampling */ + else if (base && (base->object->mode & OB_MODE_TEXTURE_PAINT) && + (v3d->drawtype > OB_WIRE)) + { + /* do nothing */ + } else if ((base && (base->object->mode & OB_MODE_PARTICLE_EDIT)) && v3d->drawtype > OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)) { @@ -1578,6 +1588,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, int fg_flag = do_foreground ? V3D_BGPIC_FOREGROUND : 0; for (bgpic = v3d->bgpicbase.first; bgpic; bgpic = bgpic->next) { + bgpic->iuser.scene = scene; /* Needed for render results. */ if ((bgpic->flag & V3D_BGPIC_FOREGROUND) != fg_flag) continue; @@ -1591,9 +1602,10 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, float x1, y1, x2, y2; ImBuf *ibuf = NULL, *freeibuf, *releaseibuf; + void *lock; - Image *ima; - MovieClip *clip; + Image *ima = NULL; + MovieClip *clip = NULL; /* disable individual images */ if ((bgpic->flag & V3D_BGPIC_DISABLED)) @@ -1610,16 +1622,14 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ibuf = NULL; /* frame is out of range, dont show */ } else { - ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, NULL); + ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock); releaseibuf = ibuf; } image_aspect[0] = ima->aspx; - image_aspect[1] = ima->aspx; + image_aspect[1] = ima->aspy; } else if (bgpic->source == V3D_BGPIC_MOVIE) { - clip = NULL; - /* TODO: skip drawing when out of frame range (as image sequences do above) */ if (bgpic->flag & V3D_BGPIC_CAMERACLIP) { @@ -1657,7 +1667,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, if (freeibuf) IMB_freeImBuf(freeibuf); if (releaseibuf) - BKE_image_release_ibuf(ima, releaseibuf, NULL); + BKE_image_release_ibuf(ima, releaseibuf, lock); continue; } @@ -1756,7 +1766,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, if (freeibuf) IMB_freeImBuf(freeibuf); if (releaseibuf) - BKE_image_release_ibuf(ima, releaseibuf, NULL); + BKE_image_release_ibuf(ima, releaseibuf, lock); continue; } @@ -1823,7 +1833,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, if (freeibuf) IMB_freeImBuf(freeibuf); if (releaseibuf) - BKE_image_release_ibuf(ima, releaseibuf, NULL); + BKE_image_release_ibuf(ima, releaseibuf, lock); } } } @@ -1961,7 +1971,9 @@ static DupliObject *dupli_step(DupliObject *dob) return dob; } -static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Base *base, int color) +static void draw_dupli_objects_color( + Scene *scene, ARegion *ar, View3D *v3d, Base *base, + const short dflag, const int color) { RegionView3D *rv3d = ar->regiondata; ListBase *lb; @@ -1970,18 +1982,30 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas Base tbase = {NULL}; BoundBox bb, *bb_tmp; /* use a copy because draw_object, calls clear_mesh_caches */ GLuint displist = 0; - short transflag, use_displist = -1; /* -1 is initialize */ + unsigned char color_rgb[3]; + const short dflag_dupli = dflag | DRAW_CONSTCOLOR; + short transflag; + bool use_displist = false; /* -1 is initialize */ char dt; + bool testbb = false; short dtx; DupliApplyData *apply_data; if (base->object->restrictflag & OB_RESTRICT_VIEW) return; - + if ((base->object->restrictflag & OB_RESTRICT_RENDER) && (v3d->flag2 & V3D_RENDER_OVERRIDE)) return; + + if (dflag & DRAW_CONSTCOLOR) { + BLI_assert(color == TH_UNDEFINED); + } + else { + UI_GetThemeColorBlend3ubv(color, TH_BACK, 0.5f, color_rgb); + } + tbase.flag = OB_FROMDUPLI | base->flag; lb = object_duplilist(G.main->eval_ctx, scene, base->object); // BLI_sortlist(lb, dupli_ob_sort); /* might be nice to have if we have a dupli list with mixed objects. */ - apply_data = duplilist_apply_matrix(lb); + apply_data = duplilist_apply(base->object, lb); dob = dupli_step(lb->first); if (dob) dob_next = dupli_step(dob->next); @@ -2018,68 +2042,77 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas tbase.object->transflag |= OB_NEG_SCALE; else tbase.object->transflag &= ~OB_NEG_SCALE; - - UI_ThemeColorBlend(color, TH_BACK, 0.5); - + + /* should move outside the loop but possible color is set in draw_object still */ + if ((dflag & DRAW_CONSTCOLOR) == 0) { + glColor3ubv(color_rgb); + } + /* generate displist, test for new object */ if (dob_prev && dob_prev->ob != dob->ob) { if (use_displist == true) glDeleteLists(displist, 1); - - use_displist = -1; + + use_displist = false; + } + + if ((bb_tmp = BKE_object_boundbox_get(dob->ob))) { + bb = *bb_tmp; /* must make a copy */ + testbb = true; } - /* generate displist */ - if (use_displist == -1) { - - /* note, since this was added, its checked (dob->type == OB_DUPLIGROUP) - * however this is very slow, it was probably needed for the NLA - * offset feature (used in group-duplicate.blend but no longer works in 2.5) - * so for now it should be ok to - campbell */ - - if ( /* if this is the last no need to make a displist */ - (dob_next == NULL || dob_next->ob != dob->ob) || - /* lamp drawing messes with matrices, could be handled smarter... but this works */ - (dob->ob->type == OB_LAMP) || - (dob->type == OB_DUPLIGROUP && dob->animated) || - !(bb_tmp = BKE_object_boundbox_get(dob->ob)) || - draw_glsl_material(scene, dob->ob, v3d, dt) || - check_object_draw_texture(scene, v3d, dt) || - (base->object == OBACT && v3d->flag2 & V3D_SOLID_MATCAP)) - { - // printf("draw_dupli_objects_color: skipping displist for %s\n", dob->ob->id.name + 2); - use_displist = false; - } - else { - // printf("draw_dupli_objects_color: using displist for %s\n", dob->ob->id.name + 2); - bb = *bb_tmp; /* must make a copy */ - - /* disable boundbox check for list creation */ - BKE_object_boundbox_flag(dob->ob, BOUNDBOX_DISABLED, 1); - /* need this for next part of code */ - unit_m4(dob->ob->obmat); /* obmat gets restored */ - - displist = glGenLists(1); - glNewList(displist, GL_COMPILE); - draw_object(scene, ar, v3d, &tbase, DRAW_CONSTCOLOR); - glEndList(); - - use_displist = true; - BKE_object_boundbox_flag(dob->ob, BOUNDBOX_DISABLED, 0); + if (!testbb || ED_view3d_boundbox_clip_ex(rv3d, &bb, dob->mat)) { + /* generate displist */ + if (use_displist == false) { + + /* note, since this was added, its checked (dob->type == OB_DUPLIGROUP) + * however this is very slow, it was probably needed for the NLA + * offset feature (used in group-duplicate.blend but no longer works in 2.5) + * so for now it should be ok to - campbell */ + + if ( /* if this is the last no need to make a displist */ + (dob_next == NULL || dob_next->ob != dob->ob) || + /* lamp drawing messes with matrices, could be handled smarter... but this works */ + (dob->ob->type == OB_LAMP) || + (dob->type == OB_DUPLIGROUP && dob->animated) || + !bb_tmp || + draw_glsl_material(scene, dob->ob, v3d, dt) || + check_object_draw_texture(scene, v3d, dt) || + (v3d->flag2 & V3D_SOLID_MATCAP) != 0) + { + // printf("draw_dupli_objects_color: skipping displist for %s\n", dob->ob->id.name + 2); + use_displist = false; + } + else { + // printf("draw_dupli_objects_color: using displist for %s\n", dob->ob->id.name + 2); + + /* disable boundbox check for list creation */ + BKE_object_boundbox_flag(dob->ob, BOUNDBOX_DISABLED, 1); + /* need this for next part of code */ + unit_m4(dob->ob->obmat); /* obmat gets restored */ + + displist = glGenLists(1); + glNewList(displist, GL_COMPILE); + draw_object(scene, ar, v3d, &tbase, dflag_dupli); + glEndList(); + + use_displist = true; + BKE_object_boundbox_flag(dob->ob, BOUNDBOX_DISABLED, 0); + } } - } - if (use_displist) { - if (ED_view3d_boundbox_clip_ex(rv3d, &bb, dob->mat)) { + + if (use_displist) { + glPushMatrix(); glMultMatrixf(dob->mat); glCallList(displist); - glLoadMatrixf(rv3d->viewmat); + glPopMatrix(); + } + else { + copy_m4_m4(dob->ob->obmat, dob->mat); + draw_object(scene, ar, v3d, &tbase, dflag_dupli); } } - else { - copy_m4_m4(dob->ob->obmat, dob->mat); - draw_object(scene, ar, v3d, &tbase, DRAW_CONSTCOLOR); - } - + tbase.object->dt = dt; tbase.object->dtx = dtx; tbase.object->transflag = transflag; @@ -2087,7 +2120,7 @@ static void draw_dupli_objects_color(Scene *scene, ARegion *ar, View3D *v3d, Bas } if (apply_data) { - duplilist_restore_matrix(lb, apply_data); + duplilist_restore(lb, apply_data); duplilist_free_apply_data(apply_data); } @@ -2107,7 +2140,7 @@ static void draw_dupli_objects(Scene *scene, ARegion *ar, View3D *v3d, Base *bas if (base->object->dup_group && base->object->dup_group->id.us < 1) color = TH_REDALERT; - draw_dupli_objects_color(scene, ar, v3d, base, color); + draw_dupli_objects_color(scene, ar, v3d, base, 0, color); } /* XXX warning, not using gpu offscreen here */ @@ -2221,7 +2254,7 @@ float view3d_depth_near(ViewDepths *d) return far == far_real ? FLT_MAX : far; } -void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d) +void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d) { short zbuf = v3d->zbuf; RegionView3D *rv3d = ar->regiondata; @@ -2241,14 +2274,14 @@ void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d) glEnable(GL_DEPTH_TEST); if (v3d->flag2 & V3D_SHOW_GPENCIL) { - draw_gpencil_view3d(scene, v3d, ar, true); + ED_gpencil_draw_view3d(scene, v3d, ar, true); } v3d->zbuf = zbuf; } -void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), bool alphaoverride) +void ED_view3d_draw_depth(Scene *scene, ARegion *ar, View3D *v3d, bool alphaoverride) { RegionView3D *rv3d = ar->regiondata; Base *base; @@ -2256,6 +2289,8 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo short flag = v3d->flag; float glalphaclip = U.glalphaclip; int obcenter_dia = U.obcenter_dia; + /* no need for color when drawing depth buffer */ + const short dflag_depth = DRAW_CONSTCOLOR; /* temp set drawtype to solid */ /* Setting these temporarily is not nice */ @@ -2287,11 +2322,9 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo Scene *sce_iter; for (SETLOOPER(scene->set, sce_iter, base)) { if (v3d->lay & base->lay) { - if (func == NULL || func(base)) { - draw_object(scene, ar, v3d, base, 0); - if (base->object->transflag & OB_DUPLI) { - draw_dupli_objects_color(scene, ar, v3d, base, TH_WIRE); - } + draw_object(scene, ar, v3d, base, 0); + if (base->object->transflag & OB_DUPLI) { + draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED); } } } @@ -2299,13 +2332,11 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo for (base = scene->base.first; base; base = base->next) { if (v3d->lay & base->lay) { - if (func == NULL || func(base)) { - /* dupli drawing */ - if (base->object->transflag & OB_DUPLI) { - draw_dupli_objects(scene, ar, v3d, base); - } - draw_object(scene, ar, v3d, base, 0); + /* dupli drawing */ + if (base->object->transflag & OB_DUPLI) { + draw_dupli_objects_color(scene, ar, v3d, base, dflag_depth, TH_UNDEFINED); } + draw_object(scene, ar, v3d, base, dflag_depth); } } @@ -2327,7 +2358,7 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo glDepthFunc(GL_ALWAYS); /* always write into the depth bufer, overwriting front z values */ for (v3da = v3d->afterdraw_xray.first; v3da; v3da = next) { next = v3da->next; - draw_object(scene, ar, v3d, v3da->base, 0); + draw_object(scene, ar, v3d, v3da->base, dflag_depth); } glDepthFunc(GL_LEQUAL); /* Now write the depth buffer normally */ } @@ -2337,7 +2368,7 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo v3d->transp = true; for (v3da = v3d->afterdraw_transp.first; v3da; v3da = next) { next = v3da->next; - draw_object(scene, ar, v3d, v3da->base, 0); + draw_object(scene, ar, v3d, v3da->base, dflag_depth); BLI_remlink(&v3d->afterdraw_transp, v3da); MEM_freeN(v3da); } @@ -2346,7 +2377,7 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo v3d->transp = false; for (v3da = v3d->afterdraw_xray.first; v3da; v3da = next) { next = v3da->next; - draw_object(scene, ar, v3d, v3da->base, 0); + draw_object(scene, ar, v3d, v3da->base, dflag_depth); BLI_remlink(&v3d->afterdraw_xray, v3da); MEM_freeN(v3da); } @@ -2355,7 +2386,7 @@ void draw_depth(Scene *scene, ARegion *ar, View3D *v3d, int (*func)(void *), boo v3d->transp = true; for (v3da = v3d->afterdraw_xraytransp.first; v3da; v3da = next) { next = v3da->next; - draw_object(scene, ar, v3d, v3da->base, 0); + draw_object(scene, ar, v3d, v3da->base, dflag_depth); BLI_remlink(&v3d->afterdraw_xraytransp, v3da); MEM_freeN(v3da); } @@ -2416,7 +2447,7 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) Scene *sce_iter; Base *base; Object *ob; - SceneRenderLayer *srl = BLI_findlink(&scene->r.layers, scene->r.actlay); + SceneRenderLayer *srl = v3d->scenelock ? BLI_findlink(&scene->r.layers, scene->r.actlay) : NULL; BLI_listbase_clear(&shadows); @@ -2453,7 +2484,7 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) v3d->drawtype = OB_SOLID; v3d->lay &= GPU_lamp_shadow_layer(shadow->lamp); - v3d->flag2 &= ~V3D_SOLID_TEX; + v3d->flag2 &= ~(V3D_SOLID_TEX | V3D_SHOW_SOLID_MATCAP); v3d->flag2 |= V3D_RENDER_OVERRIDE | V3D_RENDER_SHADOW; GPU_lamp_shadow_buffer_bind(shadow->lamp, viewmat, &winsize, winmat); @@ -2495,8 +2526,11 @@ CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) mask |= CD_MASK_ORCO; } else { - if (scene->gm.matmode == GAME_MAT_GLSL) + if ((scene->gm.matmode == GAME_MAT_GLSL && v3d->drawtype == OB_TEXTURE) || + (v3d->drawtype == OB_MATERIAL)) + { mask |= CD_MASK_ORCO; + } } } @@ -2591,12 +2625,25 @@ static void view3d_draw_objects( /* set zbuffer after we draw clipping region */ if (v3d->drawtype > OB_WIRE) { v3d->zbuf = true; - glEnable(GL_DEPTH_TEST); } else { v3d->zbuf = false; } + /* special case (depth for wire color) */ + if (v3d->drawtype <= OB_WIRE) { + if (scene->obedit && scene->obedit->type == OB_MESH) { + Mesh *me = scene->obedit->data; + if (me->drawflag & ME_DRAWEIGHT) { + v3d->zbuf = true; + } + } + } + + if (v3d->zbuf) { + glEnable(GL_DEPTH_TEST); + } + if (!draw_offscreen) { /* needs to be done always, gridview is adjusted in drawgrid() now, but only for ortho views. */ rv3d->gridview = v3d->grid; @@ -2633,14 +2680,15 @@ static void view3d_draw_objects( /* draw set first */ if (scene->set) { + const short dflag = DRAW_CONSTCOLOR | DRAW_SCENESET; Scene *sce_iter; for (SETLOOPER(scene->set, sce_iter, base)) { if (v3d->lay & base->lay) { UI_ThemeColorBlend(TH_WIRE, TH_BACK, 0.6f); - draw_object(scene, ar, v3d, base, DRAW_CONSTCOLOR | DRAW_SCENESET); + draw_object(scene, ar, v3d, base, dflag); if (base->object->transflag & OB_DUPLI) { - draw_dupli_objects_color(scene, ar, v3d, base, TH_WIRE); + draw_dupli_objects_color(scene, ar, v3d, base, dflag, TH_UNDEFINED); } } } @@ -2697,7 +2745,7 @@ static void view3d_draw_objects( if (v3d->flag2 & V3D_SHOW_GPENCIL) { /* must be before xray draw which clears the depth buffer */ if (v3d->zbuf) glDisable(GL_DEPTH_TEST); - draw_gpencil_view3d(scene, v3d, ar, true); + ED_gpencil_draw_view3d(scene, v3d, ar, true); if (v3d->zbuf) glEnable(GL_DEPTH_TEST); } @@ -2727,6 +2775,10 @@ static void view3d_draw_objects( v3d->zbuf = false; glDisable(GL_DEPTH_TEST); } + + if ((v3d->flag2 & V3D_RENDER_SHADOW) == 0) { + GPU_free_images_old(); + } } static void view3d_main_area_setup_view(Scene *scene, View3D *v3d, ARegion *ar, float viewmat[4][4], float winmat[4][4]) @@ -2813,7 +2865,7 @@ void ED_view3d_draw_offscreen(Scene *scene, View3D *v3d, ARegion *ar, int winx, if (v3d->flag2 & V3D_SHOW_GPENCIL) { /* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */ - draw_gpencil_view3d(scene, v3d, ar, false); + ED_gpencil_draw_view3d(scene, v3d, ar, false); } /* freeing the images again here could be done after the operator runs, leaving for now */ @@ -2954,10 +3006,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w } -/* NOTE: the info that this uses is updated in ED_refresh_viewport_fps(), - * which currently gets called during SCREEN_OT_animation_step. +/** + * \note The info that this uses is updated in #ED_refresh_viewport_fps, + * which currently gets called during #SCREEN_OT_animation_step. */ -void ED_scene_draw_fps(Scene *scene, rcti *rect) +void ED_scene_draw_fps(Scene *scene, const rcti *rect) { ScreenFrameRateInfo *fpsi = scene->fps_info; float fps; @@ -3106,7 +3159,7 @@ static bool view3d_main_area_draw_engine(const bContext *C, Scene *scene, glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (v3d->flag & V3D_DISPBGPICS) - view3d_draw_bgpic(scene, ar, v3d, false, true); + view3d_draw_bgpic_test(scene, ar, v3d, false, true); else fdrawcheckerboard(0, 0, ar->winx, ar->winy); @@ -3115,7 +3168,7 @@ static bool view3d_main_area_draw_engine(const bContext *C, Scene *scene, type->view_draw(rv3d->render_engine, C); if (v3d->flag & V3D_DISPBGPICS) - view3d_draw_bgpic(scene, ar, v3d, true, true); + view3d_draw_bgpic_test(scene, ar, v3d, true, true); if (clip_border) { /* restore scissor as it was before */ @@ -3428,7 +3481,7 @@ static void view3d_main_area_draw_info(const bContext *C, Scene *scene, if (v3d->flag2 & V3D_SHOW_GPENCIL) { /* draw grease-pencil stuff - needed to get paint-buffer shown too (since it's 2D) */ - draw_gpencil_view3d(scene, v3d, ar, false); + ED_gpencil_draw_view3d(scene, v3d, ar, false); } if ((v3d->flag2 & V3D_RENDER_OVERRIDE) == 0) { diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 6099ef49149..d3bd59cacc8 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -71,12 +71,14 @@ #include "ED_armature.h" #include "ED_particle.h" +#include "ED_keyframing.h" #include "ED_screen.h" #include "ED_transform.h" #include "ED_mesh.h" #include "ED_view3d.h" #include "ED_sculpt.h" +#include "UI_resources.h" #include "PIL_time.h" /* smoothview */ @@ -183,6 +185,72 @@ bool ED_view3d_camera_lock_sync(View3D *v3d, RegionView3D *rv3d) } } +bool ED_view3d_camera_autokey( + Scene *scene, ID *id_key, + struct bContext *C, const bool do_rotate, const bool do_translate) +{ + if (autokeyframe_cfra_can_key(scene, id_key)) { + ListBase dsources = {NULL, NULL}; + + /* add data-source override for the camera object */ + ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL); + + /* insert keyframes + * 1) on the first frame + * 2) on each subsequent frame + * TODO: need to check in future that frame changed before doing this + */ + if (do_rotate) { + struct KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_ROTATION_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + } + if (do_translate) { + struct KeyingSet *ks = ANIM_builtin_keyingset_get_named(NULL, ANIM_KS_LOCATION_ID); + ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA); + } + + /* free temp data */ + BLI_freelistN(&dsources); + + return true; + } + else { + return false; + } +} + +/** + * Call after modifying a locked view. + * + * \note Not every view edit currently auto-keys (numpad for eg), + * this is complicated because of smoothview. + */ +bool ED_view3d_camera_lock_autokey( + View3D *v3d, RegionView3D *rv3d, + struct bContext *C, const bool do_rotate, const bool do_translate) +{ + /* similar to ED_view3d_cameracontrol_update */ + if (ED_view3d_camera_lock_check(v3d, rv3d)) { + Scene *scene = CTX_data_scene(C); + ID *id_key; + Object *root_parent; + if ((U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0 && (root_parent = v3d->camera->parent)) { + while (root_parent->parent) { + root_parent = root_parent->parent; + } + id_key = &root_parent->id; + } + else { + id_key = &v3d->camera->id; + } + + return ED_view3d_camera_autokey(scene, id_key, C, do_rotate, do_translate); + } + else { + return false; + } +} + /** * For viewport operators that exit camera persp. * @@ -240,12 +308,12 @@ static void view3d_boxview_clip(ScrArea *sa) } for (val = 0; val < 8; val++) { - if (ELEM4(val, 0, 3, 4, 7)) + if (ELEM(val, 0, 3, 4, 7)) bb->vec[val][0] = -x1 - ofs[0]; else bb->vec[val][0] = x1 - ofs[0]; - if (ELEM4(val, 0, 1, 4, 5)) + if (ELEM(val, 0, 1, 4, 5)) bb->vec[val][1] = -y1 - ofs[1]; else bb->vec[val][1] = y1 - ofs[1]; @@ -398,11 +466,13 @@ void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip) * properties are always being edited, weak */ viewlock = rv3d->viewlock; - if ((viewlock & RV3D_LOCKED) == 0) + if ((viewlock & RV3D_LOCKED) == 0) { + do_clip = (viewlock & RV3D_BOXCLIP) != 0; viewlock = 0; - else if ((viewlock & RV3D_BOXVIEW) == 0) { - viewlock &= ~RV3D_BOXCLIP; + } + else if ((viewlock & RV3D_BOXVIEW) == 0 && (viewlock & RV3D_BOXCLIP) != 0) { do_clip = true; + viewlock &= ~RV3D_BOXCLIP; } for (; ar; ar = ar->prev) { @@ -558,7 +628,7 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3]) */ if (ob->mode & OB_MODE_SCULPT) { float stroke[3]; - ED_sculpt_get_average_stroke(ob, stroke); + ED_sculpt_stroke_get_average(ob, stroke); copy_v3_v3(lastofs, stroke); } else { @@ -581,6 +651,39 @@ static bool view3d_orbit_calc_center(bContext *C, float r_dyn_ofs[3]) is_set = true; } + else if (ob == NULL || ob->mode == OB_MODE_OBJECT) { + /* object mode use boundbox centers */ + View3D *v3d = CTX_wm_view3d(C); + Base *base; + unsigned int tot = 0; + float select_center[3]; + + zero_v3(select_center); + for (base = FIRSTBASE; base; base = base->next) { + if (TESTBASE(v3d, base)) { + /* use the boundbox if we can */ + Object *ob = base->object; + + if (ob->bb && !(ob->bb->flag & BOUNDBOX_DIRTY)) { + float cent[3]; + + BKE_boundbox_calc_center_aabb(ob->bb, cent); + + mul_m4_v3(ob->obmat, cent); + add_v3_v3(select_center, cent); + } + else { + add_v3_v3(select_center, ob->obmat[3]); + } + tot++; + } + } + if (tot) { + mul_v3_fl(select_center, 1.0f / (float)tot); + copy_v3_v3(lastofs, select_center); + is_set = true; + } + } else { /* If there's no selection, lastofs is unmodified and last value since static */ is_set = calculateTransformCenter(C, V3D_CENTROID, lastofs, NULL); @@ -934,8 +1037,8 @@ static void viewrotate_apply(ViewOpsData *vod, int x, int y) * - dragged. */ phi = si * (float)(M_PI / 2.0); - q1[0] = cos(phi); - mul_v3_fl(q1 + 1, sin(phi)); + q1[0] = cosf(phi); + mul_v3_fl(q1 + 1, sinf(phi)); mul_qt_qtqt(vod->viewquat, q1, vod->oldquat); viewrotate_apply_dyn_ofs(vod, vod->viewquat); @@ -1052,7 +1155,9 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) viewrotate_apply(vod, event->x, event->y); } else if (event_code == VIEW_CONFIRM) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, true); ED_view3d_depth_tag_update(vod->rv3d); + viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -1067,7 +1172,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) * * shared with NDOF. */ -static void view3d_ensure_persp(struct View3D *v3d, ARegion *ar) +static bool view3d_ensure_persp(struct View3D *v3d, ARegion *ar) { RegionView3D *rv3d = ar->regiondata; const bool autopersp = (U.uiflag & USER_AUTOPERSP) != 0; @@ -1075,7 +1180,7 @@ static void view3d_ensure_persp(struct View3D *v3d, ARegion *ar) BLI_assert((rv3d->viewlock & RV3D_LOCKED) == 0); if (ED_view3d_camera_lock_check(v3d, rv3d)) - return; + return false; if (rv3d->persp != RV3D_PERSP) { if (rv3d->persp == RV3D_CAMOB) { @@ -1086,7 +1191,10 @@ static void view3d_ensure_persp(struct View3D *v3d, ARegion *ar) else if (autopersp && RV3D_VIEW_IS_AXIS(rv3d->view)) { rv3d->persp = RV3D_PERSP; } + return true; } + + return false; } static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) @@ -1105,8 +1213,14 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* switch from camera view when: */ - view3d_ensure_persp(vod->v3d, vod->ar); - + if (view3d_ensure_persp(vod->v3d, vod->ar)) { + /* If we're switching from camera view to the perspective one, + * need to tag viewport update, so camera vuew and borders + * are properly updated. + */ + ED_region_tag_redraw(vod->ar); + } + if (event->type == MOUSEPAN) { /* Rotate direction we keep always same */ if (U.uiflag2 & USER_TRACKPAD_NATURAL) @@ -1334,7 +1448,7 @@ static void view3d_ndof_orbit(const struct wmNDOFMotionData *ndof, ScrArea *sa, /* Perform the up/down rotation */ angle = ndof->dt * rot[0]; quat[0] = cosf(angle); - mul_v3_v3fl(quat + 1, xvec, sin(angle)); + mul_v3_v3fl(quat + 1, xvec, sinf(angle)); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, quat); /* Perform the orbital rotation */ @@ -1860,6 +1974,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) viewmove_apply(vod, event->x, event->y); } else if (event_code == VIEW_CONFIRM) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -2000,9 +2115,12 @@ static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzo { float zfac = 1.0; bool use_cam_zoom; + float dist_range[2]; use_cam_zoom = (vod->rv3d->persp == RV3D_CAMOB) && !(vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)); + ED_view3d_dist_range_get(vod->v3d, dist_range); + if (use_cam_zoom) { float delta; delta = (xy[0] - vod->origx + xy[1] - vod->origy) / 10.0f; @@ -2069,16 +2187,19 @@ static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzo } if (!use_cam_zoom) { - if (zfac != 1.0f && zfac * vod->rv3d->dist > 0.001f * vod->grid && - zfac * vod->rv3d->dist < 10.0f * vod->far) - { - view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy); + if (zfac != 1.0f) { + const float zfac_min = dist_range[0] / vod->rv3d->dist; + const float zfac_max = dist_range[1] / vod->rv3d->dist; + CLAMP(zfac, zfac_min, zfac_max); + + if (zfac != 1.0f) { + view_zoom_mouseloc(vod->ar, zfac, vod->oldx, vod->oldy); + } } } /* these limits were in old code too */ - if (vod->rv3d->dist < 0.001f * vod->grid) vod->rv3d->dist = 0.001f * vod->grid; - if (vod->rv3d->dist > 10.0f * vod->far) vod->rv3d->dist = 10.0f * vod->far; + CLAMP(vod->rv3d->dist, dist_range[0], dist_range[1]); if (vod->rv3d->viewlock & RV3D_BOXVIEW) view3d_boxview_sync(vod->sa, vod->ar); @@ -2125,6 +2246,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) viewzoom_apply(vod, &event->x, U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0); } else if (event_code == VIEW_CONFIRM) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -2141,6 +2263,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) ScrArea *sa; ARegion *ar; bool use_cam_zoom; + float dist_range[2]; const int delta = RNA_int_get(op->ptr, "delta"); int mx, my; @@ -2164,13 +2287,15 @@ static int viewzoom_exec(bContext *C, wmOperator *op) use_cam_zoom = (rv3d->persp == RV3D_CAMOB) && !(rv3d->is_persp && ED_view3d_camera_lock_check(v3d, rv3d)); + ED_view3d_dist_range_get(v3d, dist_range); + if (delta < 0) { /* this min and max is also in viewmove() */ if (use_cam_zoom) { rv3d->camzoom -= 10.0f; if (rv3d->camzoom < RV3D_CAMZOOM_MIN) rv3d->camzoom = RV3D_CAMZOOM_MIN; } - else if (rv3d->dist < 10.0f * v3d->far) { + else if (rv3d->dist < dist_range[1]) { view_zoom_mouseloc(ar, 1.2f, mx, my); } } @@ -2179,7 +2304,7 @@ static int viewzoom_exec(bContext *C, wmOperator *op) rv3d->camzoom += 10.0f; if (rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom = RV3D_CAMZOOM_MAX; } - else if (rv3d->dist > 0.001f * v3d->grid) { + else if (rv3d->dist > dist_range[0]) { view_zoom_mouseloc(ar, 0.83333f, mx, my); } } @@ -2292,6 +2417,8 @@ static void viewzoom_cancel(bContext *C, wmOperator *op) void VIEW3D_OT_zoom(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Zoom View"; ot->description = "Zoom in/out in the view"; @@ -2308,8 +2435,10 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX); - RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX); + prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + prop = RNA_def_int(ot->srna, "my", 0, 0, INT_MAX, "Zoom Position Y", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); } @@ -2385,6 +2514,7 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) viewdolly_apply(vod, event->x, event->y, (U.uiflag & USER_ZOOM_INVERT) != 0); } else if (event_code == VIEW_CONFIRM) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, false, true); ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -3171,6 +3301,8 @@ static int render_border_exec(bContext *C, wmOperator *op) void VIEW3D_OT_render_border(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Set Render Border"; ot->description = "Set the boundaries of the border render and enable border render"; @@ -3190,7 +3322,8 @@ void VIEW3D_OT_render_border(wmOperatorType *ot) /* rna */ WM_operator_properties_border(ot); - RNA_def_boolean(ot->srna, "camera_only", 0, "Camera Only", "Set render border for camera view and final render only"); + prop = RNA_def_boolean(ot->srna, "camera_only", 0, "Camera Only", "Set render border for camera view and final render only"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /* ********************* Clear render border operator ****************** */ @@ -3254,7 +3387,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) /* Zooms in on a border drawn by the user */ rcti rect; float dvec[3], vb[2], xscale, yscale; - float dist_range_min; + float dist_range[2]; /* SMOOTHVIEW */ float new_dist; @@ -3274,9 +3407,11 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) /* check if zooming in/out view */ gesture_mode = RNA_int_get(op->ptr, "gesture_mode"); + ED_view3d_dist_range_get(v3d, dist_range); + /* Get Z Depths, needed for perspective, nice for ortho */ bgl_get_mats(&mats); - draw_depth(scene, ar, v3d, NULL, true); + ED_view3d_draw_depth(scene, ar, v3d, true); { /* avoid allocating the whole depth buffer */ @@ -3288,7 +3423,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) /* find the closest Z pixel */ depth_close = view3d_depth_near(&depth_temp); - MEM_freeN(depth_temp.depths); + MEM_SAFE_FREE(depth_temp.depths); } cent[0] = (((double)rect.xmin) + ((double)rect.xmax)) / 2; @@ -3322,8 +3457,9 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) new_ofs[2] = -p[2]; new_dist = len_v3(dvec); - dist_range_min = v3d->near * 1.5f; + /* ignore dist_range min */ + dist_range[0] = v3d->near * 1.5f; } else { /* othographic */ /* find the current window width and height */ @@ -3365,9 +3501,6 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) xscale = (BLI_rcti_size_x(&rect) / vb[0]); yscale = (BLI_rcti_size_y(&rect) / vb[1]); new_dist *= max_ff(xscale, yscale); - - /* zoom in as required, or as far as we can go */ - dist_range_min = 0.001f * v3d->grid; } if (gesture_mode == GESTURE_MODAL_OUT) { @@ -3377,9 +3510,7 @@ static int view3d_zoom_border_exec(bContext *C, wmOperator *op) } /* clamp after because we may have been zooming out */ - if (new_dist < dist_range_min) { - new_dist = dist_range_min; - } + CLAMP(new_dist, dist_range[0], dist_range[1]); ED_view3d_smooth_view(C, v3d, ar, NULL, NULL, new_ofs, NULL, &new_dist, NULL, @@ -3473,13 +3604,13 @@ void VIEW3D_OT_zoom_camera_1_to_1(wmOperatorType *ot) /* ********************* Changing view operator ****************** */ static EnumPropertyItem prop_view_items[] = { + {RV3D_VIEW_LEFT, "LEFT", ICON_TRIA_LEFT, "Left", "View From the Left"}, + {RV3D_VIEW_RIGHT, "RIGHT", ICON_TRIA_RIGHT, "Right", "View From the Right"}, + {RV3D_VIEW_BOTTOM, "BOTTOM", ICON_TRIA_DOWN, "Bottom", "View From the Bottom"}, + {RV3D_VIEW_TOP, "TOP", ICON_TRIA_UP, "Top", "View From the Top"}, {RV3D_VIEW_FRONT, "FRONT", 0, "Front", "View From the Front"}, {RV3D_VIEW_BACK, "BACK", 0, "Back", "View From the Back"}, - {RV3D_VIEW_LEFT, "LEFT", 0, "Left", "View From the Left"}, - {RV3D_VIEW_RIGHT, "RIGHT", 0, "Right", "View From the Right"}, - {RV3D_VIEW_TOP, "TOP", 0, "Top", "View From the Top"}, - {RV3D_VIEW_BOTTOM, "BOTTOM", 0, "Bottom", "View From the Bottom"}, - {RV3D_VIEW_CAMERA, "CAMERA", 0, "Camera", "View From the Active Camera"}, + {RV3D_VIEW_CAMERA, "CAMERA", ICON_CAMERA_DATA, "Camera", "View From the Active Camera"}, {0, NULL, 0, NULL, NULL} }; @@ -3867,6 +3998,7 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) viewroll_apply(vod, event->x, event->y); } else if (event_code == VIEW_CONFIRM) { + ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false); ED_view3d_depth_tag_update(vod->rv3d); viewops_data_free(C, op); @@ -3876,6 +4008,14 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } +static EnumPropertyItem prop_view_roll_items[] = { + {0, "ROLLANGLE", 0, "Roll Angle", "Roll the view using an angle value"}, + {V3D_VIEW_STEPLEFT, "ROLLLEFT", 0, "Roll Left", "Roll the view around to the Left"}, + {V3D_VIEW_STEPRIGHT, "ROLLTRIGHT", 0, "Roll Right", "Roll the view around to the Right"}, + {0, NULL, 0, NULL, NULL} +}; + + static int viewroll_exec(bContext *C, wmOperator *op) { View3D *v3d; @@ -3893,12 +4033,17 @@ static int viewroll_exec(bContext *C, wmOperator *op) rv3d = ar->regiondata; if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { - const float angle = RNA_float_get(op->ptr, "angle"); + int type = RNA_enum_get(op->ptr, "type"); + float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF((float)U.pad_rot_angle); float mousevec[3]; float quat_new[4]; const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + if (type == V3D_VIEW_STEPLEFT) { + angle = -angle; + } + normalize_v3_v3(mousevec, rv3d->viewinv[2]); negate_v3(mousevec); view_roll_angle(ar, quat_new, rv3d->viewquat, mousevec, angle); @@ -3920,7 +4065,9 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ViewOpsData *vod; - if (RNA_struct_property_is_set(op->ptr, "angle")) { + bool use_angle = RNA_enum_get(op->ptr, "type") != 0; + + if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) { viewroll_exec(C, op); } else { @@ -3958,6 +4105,8 @@ static void viewroll_cancel(bContext *C, wmOperator *op) void VIEW3D_OT_view_roll(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "View Roll"; ot->description = "Roll the view"; @@ -3974,7 +4123,10 @@ void VIEW3D_OT_view_roll(wmOperatorType *ot) ot->flag = 0; /* properties */ - ot->prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); + ot->prop = prop = RNA_def_float(ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "type", prop_view_roll_items, 0, "Roll Angle Source", "How roll angle is calculated"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } static EnumPropertyItem prop_view_pan_items[] = { @@ -4148,8 +4300,7 @@ static int background_image_add_invoke(bContext *C, wmOperator *op, const wmEven if (ima) { bgpic->ima = ima; - if (ima->id.us == 0) id_us_plus(&ima->id); - else id_lib_extern(&ima->id); + id_us_plus(&ima->id); if (!(v3d->flag & V3D_DISPBGPICS)) v3d->flag |= V3D_DISPBGPICS; @@ -4191,7 +4342,15 @@ static int background_image_remove_exec(bContext *C, wmOperator *op) BGpic *bgpic_rem = BLI_findlink(&v3d->bgpicbase, index); if (bgpic_rem) { + if (bgpic_rem->source == V3D_BGPIC_IMAGE) { + id_us_min((ID *)bgpic_rem->ima); + } + else if (bgpic_rem->source == V3D_BGPIC_MOVIE) { + id_us_min((ID *)bgpic_rem->clip); + } + ED_view3D_background_image_remove(v3d, bgpic_rem); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); return OPERATOR_FINISHED; } @@ -4221,12 +4380,16 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /* ********************* set clipping operator ****************** */ -static void calc_clipping_plane(float clip[6][4], const BoundBox *clipbb) +static void calc_clipping_plane(float clip[6][4], const BoundBox *clipbb, const bool is_flip) { int val; for (val = 0; val < 4; val++) { normal_tri_v3(clip[val], clipbb->vec[val], clipbb->vec[val == 3 ? 0 : val + 1], clipbb->vec[val + 4]); + if (UNLIKELY(is_flip)) { + negate_v3(clip[val]); + } + clip[val][3] = -dot_v3v3(clip[val], clipbb->vec[val]); } } @@ -4243,7 +4406,7 @@ static void calc_local_clipping(float clip_local[6][4], BoundBox *clipbb, float mul_v3_m4v3(clipbb_local.vec[i], imat, clipbb->vec[i]); } - calc_clipping_plane(clip_local, &clipbb_local); + calc_clipping_plane(clip_local, &clipbb_local, is_negative_m4(mat)); } void ED_view3d_clipping_local(RegionView3D *rv3d, float mat[4][4]) @@ -4355,20 +4518,25 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2]) } } -static int view3d_cursor3d_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +void ED_view3d_cursor3d_update(bContext *C, const int mval[2]) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); float *fp = ED_view3d_cursor3d_get(scene, v3d); - ED_view3d_cursor3d_position(C, fp, event->mval); - + ED_view3d_cursor3d_position(C, fp, mval); + if (v3d && v3d->localvd) WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); else WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); - - return OPERATOR_FINISHED; +} + +static int view3d_cursor3d_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ED_view3d_cursor3d_update(C, event->mval); + + return OPERATOR_FINISHED; } void VIEW3D_OT_cursor3d(wmOperatorType *ot) @@ -4496,7 +4664,7 @@ static float view_autodist_depth_margin(ARegion *ar, const int mval[2], int marg view3d_update_depths_rect(ar, &depth_temp, &rect); depth_close = view3d_depth_near(&depth_temp); - if (depth_temp.depths) MEM_freeN(depth_temp.depths); + MEM_SAFE_FREE(depth_temp.depths); return depth_close; } @@ -4511,7 +4679,7 @@ bool ED_view3d_autodist(Scene *scene, ARegion *ar, View3D *v3d, /* Get Z Depths, needed for perspective, nice for ortho */ bgl_get_mats(&mats); - draw_depth(scene, ar, v3d, NULL, alphaoverride); + ED_view3d_draw_depth(scene, ar, v3d, alphaoverride); depth_close = view_autodist_depth_margin(ar, mval, 4); @@ -4543,10 +4711,10 @@ void ED_view3d_autodist_init(Scene *scene, ARegion *ar, View3D *v3d, int mode) /* Get Z Depths, needed for perspective, nice for ortho */ switch (mode) { case 0: - draw_depth(scene, ar, v3d, NULL, true); + ED_view3d_draw_depth(scene, ar, v3d, true); break; case 1: - draw_depth_gpencil(scene, ar, v3d); + ED_view3d_draw_depth_gpencil(scene, ar, v3d); break; } } diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index 6afe0ef896f..da77c4f75f7 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -385,7 +385,7 @@ static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent fly->zlock = FLY_AXISLOCK_STATE_IDLE; } - fly->v3d_camera_control = ED_view3d_cameracontrol_aquire( + fly->v3d_camera_control = ED_view3d_cameracontrol_acquire( fly->scene, fly->v3d, fly->rv3d, (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0); diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index e3d0e87066b..a88724a1cdd 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -32,6 +32,7 @@ #include <stdio.h> #include <stdlib.h> +#include "DNA_brush_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -45,6 +46,7 @@ #include "BKE_depsgraph.h" #include "BKE_main.h" #include "BKE_modifier.h" +#include "BKE_paint.h" #include "BKE_screen.h" #include "BKE_editmesh.h" @@ -308,7 +310,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) /* mode */ if (ob) { modeselect = ob->mode; - is_paint = ELEM4(ob->mode, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT); + is_paint = ELEM(ob->mode, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT); } else { modeselect = OB_MODE_OBJECT; @@ -336,8 +338,7 @@ void uiTemplateHeader3D(uiLayout *layout, struct bContext *C) uiItemR(layout, &v3dptr, "viewport_shade", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); if (obedit == NULL && is_paint) { - - if (ob->mode & OB_MODE_WEIGHT_PAINT) { + if (ob->mode & OB_MODE_ALL_PAINT) { /* Only for Weight Paint. makes no sense in other paint modes. */ row = uiLayoutRow(layout, true); uiItemR(row, &v3dptr, "pivot_point", UI_ITEM_R_ICON_ONLY, "", ICON_NONE); diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 2c6fc1cfe02..84ac4f7d02d 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -177,8 +177,8 @@ void draw_mesh_paint(View3D *v3d, RegionView3D *rv3d, /* view3d_draw.c */ void view3d_main_area_draw(const struct bContext *C, struct ARegion *ar); -void draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, int (*func)(void *), bool alphaoverride); -void draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d); +void ED_view3d_draw_depth(Scene *scene, struct ARegion *ar, View3D *v3d, bool alphaoverride); +void ED_view3d_draw_depth_gpencil(Scene *scene, ARegion *ar, View3D *v3d); void ED_view3d_after_add(ListBase *lb, Base *base, const short dflag); void circf(float x, float y, float rad); @@ -218,7 +218,7 @@ void ED_view3d_smooth_view( const float *ofs, const float *quat, const float *dist, const float *lens, const int smooth_viewtx); -void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect); +void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect); void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d); void fly_modal_keymap(struct wmKeyConfig *keyconf); @@ -233,7 +233,7 @@ void VIEW3D_OT_properties(struct wmOperatorType *ot); void view3d_buttons_register(struct ARegionType *art); /* view3d_camera_control.c */ -struct View3DCameraControl *ED_view3d_cameracontrol_aquire( +struct View3DCameraControl *ED_view3d_cameracontrol_acquire( Scene *scene, View3D *v3d, RegionView3D *rv3d, const bool use_parent_root); void ED_view3d_cameracontrol_update( @@ -285,7 +285,7 @@ void draw_smoke_heat(struct SmokeDomainSettings *domain, struct Object *ob); /* workaround for trivial but noticeable camera bug caused by imprecision * between view border calculation in 2D/3D space, workaround for bug [#28037]. - * without this deifne we get the old behavior which is to try and align them + * without this define we get the old behavior which is to try and align them * both which _mostly_ works fine, but when the camera moves beyond ~1000 in * any direction it starts to fail */ #define VIEW3D_CAMERA_BORDER_HACK diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index a8128ba7ae8..6a505959820 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -77,7 +77,7 @@ static int view3d_copybuffer_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - BLI_make_file_string("/", str, BLI_temporary_dir(), "copybuffer.blend"); + BLI_make_file_string("/", str, BLI_temp_dir_base(), "copybuffer.blend"); BKE_copybuffer_save(str, op->reports); BKE_report(op->reports, RPT_INFO, "Copied selected objects to buffer"); @@ -102,7 +102,7 @@ static int view3d_pastebuffer_exec(bContext *C, wmOperator *op) { char str[FILE_MAX]; - BLI_make_file_string("/", str, BLI_temporary_dir(), "copybuffer.blend"); + BLI_make_file_string("/", str, BLI_temp_dir_base(), "copybuffer.blend"); if (BKE_copybuffer_paste(C, str, op->reports)) { WM_event_add_notifier(C, NC_WINDOW, NULL); @@ -296,8 +296,8 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_pan", PAD4, KM_PRESS, KM_CTRL, 0)->ptr, "type", V3D_VIEW_PANLEFT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_pan", PAD6, KM_PRESS, KM_CTRL, 0)->ptr, "type", V3D_VIEW_PANRIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_pan", PAD8, KM_PRESS, KM_CTRL, 0)->ptr, "type", V3D_VIEW_PANUP); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", PAD4, KM_PRESS, KM_SHIFT, 0)->ptr, "angle", M_PI / -12); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", PAD6, KM_PRESS, KM_SHIFT, 0)->ptr, "angle", M_PI / 12); + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", PAD4, KM_PRESS, KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPLEFT); + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", PAD6, KM_PRESS, KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_pan", WHEELUPMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "type", V3D_VIEW_PANRIGHT); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_pan", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "type", V3D_VIEW_PANLEFT); @@ -309,8 +309,8 @@ void view3d_keymap(wmKeyConfig *keyconf) RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELUPMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPUP); RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_orbit", WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0)->ptr, "type", V3D_VIEW_STEPDOWN); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELUPMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "angle", M_PI / -12); - RNA_float_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "angle", M_PI / 12); + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELUPMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPLEFT); + RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_roll", WHEELDOWNMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0)->ptr, "type", V3D_VIEW_STEPRIGHT); /* active aligned, replaces '*' key in 2.4x */ kmi = WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD1, KM_PRESS, KM_SHIFT, 0); diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 6ee1750b85d..75c1d9dcd22 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -277,7 +277,7 @@ eV3DProjStatus ED_view3d_project_float_object(const ARegion *ar, const float co[ * *************************************************** */ /** - * Caculate a depth value from \a co, use with #ED_view3d_win_to_delta + * Calculate a depth value from \a co, use with #ED_view3d_win_to_delta */ float ED_view3d_calc_zfac(const RegionView3D *rv3d, const float co[3], bool *r_flip) { @@ -315,22 +315,27 @@ static void view3d_win_to_ray_segment(const ARegion *ar, View3D *v3d, const floa if (rv3d->is_persp) { copy_v3_v3(r_ray_co, rv3d->viewinv[3]); - - start_offset = v3d->near; - end_offset = v3d->far; } else { - float vec[4]; - vec[0] = 2.0f * mval[0] / ar->winx - 1; - vec[1] = 2.0f * mval[1] / ar->winy - 1; - vec[2] = 0.0f; - vec[3] = 1.0f; + 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_m4_v4(rv3d->persinv, vec); - copy_v3_v3(r_ray_co, vec); + mul_project_m4_v3(rv3d->persinv, r_ray_co); + } - start_offset = -1000.0f; - end_offset = 1000.0f; + if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) { + end_offset = v3d->far / 2.0f; + start_offset = -end_offset; + } + else { + ED_view3d_clip_range_get(v3d, rv3d, &start_offset, &end_offset, false); } if (r_ray_start) { @@ -388,7 +393,7 @@ bool ED_view3d_win_to_ray_ex(const ARegion *ar, View3D *v3d, const float mval[2] * \param ar The region (used for the window width and height). * \param v3d The 3d viewport (used for near clipping value). * \param mval The area relative 2d location (such as event->mval, converted into float[2]). - * \param r_ray_co The world-space point where the ray intersects the window plane. + * \param r_ray_start The world-space point where the ray intersects the window plane. * \param r_ray_normal The normalized world-space direction of towards mval. * \param do_clip Optionally clip the start of the ray by the view clipping planes. * \return success, false if the ray is totally clipped. diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 9d0a70ea9d0..652f44ea95a 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -122,12 +122,10 @@ static bool ED_view3d_snap_ray(bContext *C, float r_co[3], bool ret; Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); struct Object *obedit = CTX_data_edit_object(C); /* try snap edge, then face if it fails */ - ret = snapObjectsRayEx(scene, NULL, v3d, ar, obedit, SCE_SNAP_MODE_FACE, + ret = snapObjectsRayEx(scene, NULL, NULL, NULL, obedit, SCE_SNAP_MODE_FACE, NULL, NULL, ray_start, ray_normal, &ray_dist, NULL, &dist_px, r_co, r_no_dummy, SNAP_ALL); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d18eab6da48..9d5240feb43 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -1147,11 +1147,12 @@ static Base *object_mouse_select_menu(bContext *C, ViewContext *vc, unsigned int } { + wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false); PointerRNA ptr; - WM_operator_properties_create(&ptr, "VIEW3D_OT_select_menu"); + WM_operator_properties_create_ptr(&ptr, ot); RNA_boolean_set(&ptr, "toggle", toggle); - WM_operator_name_call(C, "VIEW3D_OT_select_menu", WM_OP_INVOKE_DEFAULT, &ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); WM_operator_properties_free(&ptr); } @@ -1180,28 +1181,45 @@ static short selectbuffer_ret_hits_15(unsigned int *UNUSED(buffer), const short static short selectbuffer_ret_hits_9(unsigned int *buffer, const short hits15, const short hits9) { const int offs = 4 * hits15; - memcpy(buffer, buffer + offs, 4 * offs); + memcpy(buffer, buffer + offs, 4 * hits9 * sizeof(unsigned int)); return hits9; } static short selectbuffer_ret_hits_5(unsigned int *buffer, const short hits15, const short hits9, const short hits5) { const int offs = 4 * hits15 + 4 * hits9; - memcpy(buffer, buffer + offs, 4 * offs); + memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(unsigned int)); return hits5; } /* we want a select buffer with bones, if there are... */ /* so check three selection levels and compare */ -static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buffer, const int mval[2]) +static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buffer, const int mval[2], bool *p_do_nearest, bool enumerate) { rcti rect; int offs; short hits15, hits9 = 0, hits5 = 0; bool has_bones15 = false, has_bones9 = false, has_bones5 = false; - + static int last_mval[2] = {-100, -100}; + bool do_nearest = false; + View3D *v3d = vc->v3d; + + /* define if we use solid nearest select or not */ + if (v3d->drawtype > OB_WIRE) { + do_nearest = true; + if (len_manhattan_v2v2_int(mval, last_mval) < 3) { + do_nearest = false; + } + } + copy_v2_v2_int(last_mval, mval); + + if (p_do_nearest) + *p_do_nearest = do_nearest; + + do_nearest = do_nearest && !enumerate; + BLI_rcti_init(&rect, mval[0] - 14, mval[0] + 14, mval[1] - 14, mval[1] + 14); - hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect); + hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, do_nearest); if (hits15 == 1) { return selectbuffer_ret_hits_15(buffer, hits15); } @@ -1210,7 +1228,7 @@ static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buff offs = 4 * hits15; BLI_rcti_init(&rect, mval[0] - 9, mval[0] + 9, mval[1] - 9, mval[1] + 9); - hits9 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect); + hits9 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, do_nearest); if (hits9 == 1) { return selectbuffer_ret_hits_9(buffer, hits15, hits9); } @@ -1219,7 +1237,7 @@ static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buff offs += 4 * hits9; BLI_rcti_init(&rect, mval[0] - 5, mval[0] + 5, mval[1] - 5, mval[1] + 5); - hits5 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect); + hits5 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect, do_nearest); if (hits5 == 1) { return selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5); } @@ -1241,25 +1259,13 @@ static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buff } /* returns basact */ -static Base *mouse_select_eval_buffer(ViewContext *vc, unsigned int *buffer, int hits, const int mval[2], - Base *startbase, bool has_bones) +static Base *mouse_select_eval_buffer(ViewContext *vc, unsigned int *buffer, int hits, + Base *startbase, bool has_bones, bool do_nearest) { Scene *scene = vc->scene; View3D *v3d = vc->v3d; Base *base, *basact = NULL; - static int lastmval[2] = {-100, -100}; int a; - bool do_nearest = false; - - /* define if we use solid nearest select or not */ - if (v3d->drawtype > OB_WIRE) { - do_nearest = true; - if (ABS(mval[0] - lastmval[0]) < 3 && ABS(mval[1] - lastmval[1]) < 3) { - if (!has_bones) /* hrms, if theres bones we always do nearest */ - do_nearest = false; - } - } - lastmval[0] = mval[0]; lastmval[1] = mval[1]; if (do_nearest) { unsigned int min = 0xFFFFFFFF; @@ -1342,16 +1348,17 @@ Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2]) Base *basact = NULL; unsigned int buffer[4 * MAXPICKBUF]; int hits; + bool do_nearest; /* setup view context for argument to callbacks */ view3d_operator_needs_opengl(C); view3d_set_viewcontext(C, &vc); - hits = mixed_bones_object_selectbuffer(&vc, buffer, mval); + hits = mixed_bones_object_selectbuffer(&vc, buffer, mval, &do_nearest, false); if (hits > 0) { const bool has_bones = selectbuffer_has_bones(buffer, hits); - basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, vc.scene->base.first, has_bones); + basact = mouse_select_eval_buffer(&vc, buffer, hits, vc.scene->base.first, has_bones, do_nearest); } return basact; @@ -1438,10 +1445,11 @@ static bool mouse_select(bContext *C, const int mval[2], } else { unsigned int buffer[4 * MAXPICKBUF]; + bool do_nearest; /* if objects have posemode set, the bones are in the same selection buffer */ - hits = mixed_bones_object_selectbuffer(&vc, buffer, mval); + hits = mixed_bones_object_selectbuffer(&vc, buffer, mval, &do_nearest, enumerate); if (hits > 0) { /* note: bundles are handling in the same way as bones */ @@ -1452,7 +1460,7 @@ static bool mouse_select(bContext *C, const int mval[2], basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, toggle); } else { - basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, startbase, has_bones); + basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest); } if (has_bones && basact) { @@ -1510,11 +1518,11 @@ static bool mouse_select(bContext *C, const int mval[2], if (!changed) { /* fallback to regular object selection if no new bundles were selected, * allows to select object parented to reconstruction object */ - basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, startbase, 0); + basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest); } } } - else if (ED_do_pose_selectbuffer(scene, basact, buffer, hits, extend, deselect, toggle) ) { + else if (ED_do_pose_selectbuffer(scene, basact, buffer, hits, extend, deselect, toggle, do_nearest)) { /* then bone is found */ /* we make the armature selected: @@ -1872,7 +1880,7 @@ static int do_meta_box_select(ViewContext *vc, rcti *rect, bool select, bool ext unsigned int buffer[4 * MAXPICKBUF]; short hits; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect); + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect, false); if (extend == false && select) BKE_mball_deselect_all(mb); @@ -1906,7 +1914,7 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool unsigned int buffer[4 * MAXPICKBUF]; short hits; - hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect); + hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect, false); /* clear flag we use to detect point was affected */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) @@ -1964,7 +1972,7 @@ static int do_armature_box_select(ViewContext *vc, rcti *rect, bool select, bool ED_armature_sync_selection(arm->edbo); - return OPERATOR_CANCELLED; + return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, bool select, bool extend) @@ -2000,7 +2008,7 @@ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, b /* selection buffer now has bones potentially too, so we add MAXPICKBUF */ vbuffer = MEM_mallocN(4 * (totobj + MAXPICKBUF) * sizeof(unsigned int), "selection buffer"); - hits = view3d_opengl_select(vc, vbuffer, 4 * (totobj + MAXPICKBUF), rect); + hits = view3d_opengl_select(vc, vbuffer, 4 * (totobj + MAXPICKBUF), rect, false); /* * LOGIC NOTES (theeth): * The buffer and ListBase have the same relative order, which makes the selection @@ -2128,7 +2136,7 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) } else { /* no editmode, unified for bones and objects */ if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { - ret = do_sculpt_mask_box_select(&vc, &rect, select, extend); + ret = ED_sculpt_mask_box_select(C, &vc, &rect, select, extend); } else if (vc.obact && BKE_paint_select_face_test(vc.obact)) { ret = do_paintface_box_select(&vc, &rect, select, extend); @@ -2841,6 +2849,6 @@ void VIEW3D_OT_select_circle(wmOperatorType *ot) RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX); RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX); } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index ebdcc757acc..858d001a381 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -56,6 +56,7 @@ #include "BIF_glutil.h" #include "GPU_draw.h" +#include "GPU_select.h" #include "WM_api.h" #include "WM_types.h" @@ -273,7 +274,7 @@ void ED_view3d_smooth_view_ex( rv3d->rflag |= RV3D_NAVIGATING; /* not essential but in some cases the caller will tag the area for redraw, - * and in that case we can get a ficker of the 'org' user view but we want to see 'src' */ + * and in that case we can get a flicker of the 'org' user view but we want to see 'src' */ view3d_smooth_view_state_restore(&sms.src, v3d, rv3d); /* keep track of running timer! */ @@ -723,6 +724,13 @@ void ED_view3d_depth_tag_update(RegionView3D *rv3d) rv3d->depths->damaged = true; } +void ED_view3d_dist_range_get(struct View3D *v3d, + float r_dist_range[2]) +{ + r_dist_range[0] = v3d->grid * 0.001f; + r_dist_range[1] = v3d->far * 10.0f; +} + /* copies logic of get_view3d_viewplane(), keep in sync */ bool ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *r_clipsta, float *r_clipend, const bool use_ortho_factor) @@ -782,13 +790,13 @@ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist) } /** - * \param rect, optional for picking (can be NULL). + * \param rect optional for picking (can be NULL). */ -void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect) +void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect) { RegionView3D *rv3d = ar->regiondata; rctf viewplane; - float clipsta, clipend, x1, y1, x2, y2; + float clipsta, clipend; bool is_ortho; is_ortho = ED_view3d_viewplane_get(v3d, rv3d, ar->winx, ar->winy, &viewplane, &clipsta, &clipend, NULL); @@ -800,28 +808,20 @@ void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect) clipsta, clipend); #endif - x1 = viewplane.xmin; - y1 = viewplane.ymin; - x2 = viewplane.xmax; - y2 = viewplane.ymax; - if (rect) { /* picking */ - rect->xmin /= (float)ar->winx; - rect->xmin = x1 + rect->xmin * (x2 - x1); - rect->ymin /= (float)ar->winy; - rect->ymin = y1 + rect->ymin * (y2 - y1); - rect->xmax /= (float)ar->winx; - rect->xmax = x1 + rect->xmax * (x2 - x1); - rect->ymax /= (float)ar->winy; - rect->ymax = y1 + rect->ymax * (y2 - y1); - - if (is_ortho) wmOrtho(rect->xmin, rect->xmax, rect->ymin, rect->ymax, -clipend, clipend); - else wmFrustum(rect->xmin, rect->xmax, rect->ymin, rect->ymax, clipsta, clipend); + rctf r; + r.xmin = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmin / (float)ar->winx)); + r.ymin = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymin / (float)ar->winy)); + r.xmax = viewplane.xmin + (BLI_rctf_size_x(&viewplane) * (rect->xmax / (float)ar->winx)); + r.ymax = viewplane.ymin + (BLI_rctf_size_y(&viewplane) * (rect->ymax / (float)ar->winy)); + viewplane = r; + } + if (is_ortho) { + wmOrtho(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend); } else { - if (is_ortho) wmOrtho(x1, x2, y1, y2, clipsta, clipend); - else wmFrustum(x1, x2, y1, y2, clipsta, clipend); + wmFrustum(viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend); } /* update matrix in 3d view region */ @@ -963,6 +963,78 @@ void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) } } +static void view3d_select_loop(ViewContext *vc, Scene *scene, View3D *v3d, ARegion *ar, bool use_obedit_skip) +{ + short code = 1; + char dt; + short dtx; + + if (vc->obedit && vc->obedit->type == OB_MBALL) { + draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR); + } + else if ((vc->obedit && vc->obedit->type == OB_ARMATURE)) { + /* if not drawing sketch, draw bones */ + if (!BDR_drawSketchNames(vc)) { + draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR); + } + } + else { + Base *base; + + v3d->xray = true; /* otherwise it postpones drawing */ + for (base = scene->base.first; base; base = base->next) { + if (base->lay & v3d->lay) { + + if ((base->object->restrictflag & OB_RESTRICT_SELECT) || + (use_obedit_skip && (scene->obedit->data == base->object->data))) + { + base->selcol = 0; + } + else { + base->selcol = code; + + if (GPU_select_load_id(code)) { + draw_object(scene, ar, v3d, base, DRAW_PICKING | DRAW_CONSTCOLOR); + + /* we draw duplicators for selection too */ + if ((base->object->transflag & OB_DUPLI)) { + ListBase *lb; + DupliObject *dob; + Base tbase; + + tbase.flag = OB_FROMDUPLI; + lb = object_duplilist(G.main->eval_ctx, scene, base->object); + + for (dob = lb->first; dob; dob = dob->next) { + float omat[4][4]; + + tbase.object = dob->ob; + copy_m4_m4(omat, dob->ob->obmat); + copy_m4_m4(dob->ob->obmat, dob->mat); + + /* extra service: draw the duplicator in drawtype of parent */ + /* MIN2 for the drawtype to allow bounding box objects in groups for lods */ + dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt); + dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx; + + draw_object(scene, ar, v3d, &tbase, DRAW_PICKING | DRAW_CONSTCOLOR); + + tbase.object->dt = dt; + tbase.object->dtx = dtx; + + copy_m4_m4(dob->ob->obmat, omat); + } + free_object_duplilist(lb); + } + } + code++; + } + } + } + v3d->xray = false; /* restore */ + } +} + /** * \warning be sure to account for a negative return value * This is an error, "Too many objects in select buffer" @@ -970,17 +1042,16 @@ void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) * * \note (vc->obedit == NULL) can be set to explicitly skip edit-object selection. */ -short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, rcti *input) +short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int bufsize, const rcti *input, bool do_nearest) { Scene *scene = vc->scene; View3D *v3d = vc->v3d; ARegion *ar = vc->ar; rctf rect; - short code, hits; - char dt; - short dtx; + short hits; const bool use_obedit_skip = (scene->obedit != NULL) && (vc->obedit == NULL); - + const bool do_passes = do_nearest && GPU_select_query_check_active(); + G.f |= G_PICKSEL; /* case not a border select */ @@ -1005,78 +1076,24 @@ short view3d_opengl_select(ViewContext *vc, unsigned int *buffer, unsigned int b if (vc->rv3d->rflag & RV3D_CLIPPING) ED_view3d_clipping_set(vc->rv3d); - glSelectBuffer(bufsize, (GLuint *)buffer); - glRenderMode(GL_SELECT); - glInitNames(); /* these two calls whatfor? It doesnt work otherwise */ - glPushName(-1); - code = 1; + if (do_passes) + GPU_select_begin(buffer, bufsize, &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, bufsize, &rect, GPU_SELECT_ALL, 0); + + view3d_select_loop(vc, scene, v3d, ar, use_obedit_skip); + + hits = GPU_select_end(); - if (vc->obedit && vc->obedit->type == OB_MBALL) { - draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR); - } - else if ((vc->obedit && vc->obedit->type == OB_ARMATURE)) { - /* if not drawing sketch, draw bones */ - if (!BDR_drawSketchNames(vc)) { - draw_object(scene, ar, v3d, BASACT, DRAW_PICKING | DRAW_CONSTCOLOR); - } - } - else { - Base *base; - - v3d->xray = true; /* otherwise it postpones drawing */ - for (base = scene->base.first; base; base = base->next) { - if (base->lay & v3d->lay) { - - if ((base->object->restrictflag & OB_RESTRICT_SELECT) || - (use_obedit_skip && (scene->obedit->data == base->object->data))) - { - base->selcol = 0; - } - else { - base->selcol = code; - glLoadName(code); - draw_object(scene, ar, v3d, base, DRAW_PICKING | DRAW_CONSTCOLOR); - - /* we draw duplicators for selection too */ - if ((base->object->transflag & OB_DUPLI)) { - ListBase *lb; - DupliObject *dob; - Base tbase; - - tbase.flag = OB_FROMDUPLI; - lb = object_duplilist(G.main->eval_ctx, scene, base->object); - - for (dob = lb->first; dob; dob = dob->next) { - float omat[4][4]; - - tbase.object = dob->ob; - copy_m4_m4(omat, dob->ob->obmat); - copy_m4_m4(dob->ob->obmat, dob->mat); - - /* extra service: draw the duplicator in drawtype of parent */ - /* MIN2 for the drawtype to allow bounding box objects in groups for lods */ - dt = tbase.object->dt; tbase.object->dt = MIN2(tbase.object->dt, base->object->dt); - dtx = tbase.object->dtx; tbase.object->dtx = base->object->dtx; - - draw_object(scene, ar, v3d, &tbase, DRAW_PICKING | DRAW_CONSTCOLOR); - - tbase.object->dt = dt; - tbase.object->dtx = dtx; - - copy_m4_m4(dob->ob->obmat, omat); - } - free_object_duplilist(lb); - } - code++; - } - } - } - v3d->xray = false; /* restore */ + /* second pass, to get the closest object to camera */ + if (do_passes) { + GPU_select_begin(buffer, bufsize, &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + + view3d_select_loop(vc, scene, v3d, ar, use_obedit_skip); + + GPU_select_end(); } - - glPopName(); /* see above (pushname) */ - hits = glRenderMode(GL_RENDER); - + G.f &= ~G_PICKSEL; view3d_winmatrix_set(ar, v3d, NULL); mul_m4_m4m4(vc->rv3d->persmat, vc->rv3d->winmat, vc->rv3d->viewmat); @@ -1293,7 +1310,7 @@ static bool view3d_localview_init( return ok; } -static void restore_localviewdata(wmWindowManager *wm, wmWindow *win, Main *bmain, ScrArea *sa, const int smooth_viewtx) +static void restore_localviewdata(wmWindowManager *wm, wmWindow *win, Main *bmain, Scene *scene, ScrArea *sa, const int smooth_viewtx) { const bool free = true; ARegion *ar; @@ -1343,7 +1360,7 @@ static void restore_localviewdata(wmWindowManager *wm, wmWindow *win, Main *bmai } } - ED_view3d_shade_update(bmain, v3d, sa); + ED_view3d_shade_update(bmain, scene, v3d, sa); } } } @@ -1360,7 +1377,7 @@ static bool view3d_localview_exit( locallay = v3d->lay & 0xFF000000; - restore_localviewdata(wm, win, bmain, sa, smooth_viewtx); + restore_localviewdata(wm, win, bmain, scene, sa, smooth_viewtx); /* for when in other window the layers have changed */ if (v3d->scenelock) v3d->lay = scene->lay; diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 1c3e223f3ed..c54948b23c6 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -60,10 +60,8 @@ #include "view3d_intern.h" /* own include */ -#define EARTH_GRAVITY 9.80668f /* m/s2 */ - /* prototypes */ -static float getVelocityZeroTime(float velocity); +static float getVelocityZeroTime(const float gravity, const float velocity); /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ enum { @@ -280,7 +278,8 @@ typedef struct WalkInfo { bool is_reversed; /* gravity system */ - eWalkGravityState gravity; + eWalkGravityState gravity_state; + float gravity; /* height to use in walk mode */ float view_height; @@ -360,11 +359,11 @@ static void walk_navigation_mode_set(bContext *C, WalkInfo *walk, eWalkMethod mo { if (mode == WALK_MODE_FREE) { walk->navigation_mode = WALK_MODE_FREE; - walk->gravity = WALK_GRAVITY_STATE_OFF; + walk->gravity_state = WALK_GRAVITY_STATE_OFF; } else { /* WALK_MODE_GRAVITY */ walk->navigation_mode = WALK_MODE_GRAVITY; - walk->gravity = WALK_GRAVITY_STATE_START; + walk->gravity_state = WALK_GRAVITY_STATE_START; } walk_update_header(C, walk); @@ -510,7 +509,14 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->speed = U.walk_navigation.walk_speed; walk->speed_factor = U.walk_navigation.walk_speed_factor; - walk->gravity = WALK_GRAVITY_STATE_OFF; + walk->gravity_state = WALK_GRAVITY_STATE_OFF; + + if ((walk->scene->physics_settings.flag & PHYS_GLOBAL_GRAVITY)) { + walk->gravity = fabsf(walk->scene->physics_settings.gravity[2]); + } + else { + walk->gravity = 9.80668f; /* m/s2 */ + } walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0); @@ -532,7 +538,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->rflag |= RV3D_NAVIGATING; - walk->v3d_camera_control = ED_view3d_cameracontrol_aquire( + walk->v3d_camera_control = ED_view3d_cameracontrol_acquire( walk->scene, walk->v3d, walk->rv3d, (U.uiflag & USER_CAM_LOCK_NO_PARENT) == 0); @@ -755,10 +761,10 @@ static void walkEvent(bContext *C, wmOperator *UNUSED(op), WalkInfo *walk, const #define JUMP_SPEED_MIN 1.0f #define JUMP_TIME_MAX 0.2f /* s */ -#define JUMP_SPEED_MAX sqrtf(2.0f * EARTH_GRAVITY * walk->jump_height) +#define JUMP_SPEED_MAX sqrtf(2.0f * walk->gravity * walk->jump_height) case WALK_MODAL_JUMP_STOP: - if (walk->gravity == WALK_GRAVITY_STATE_JUMP) { + if (walk->gravity_state == WALK_GRAVITY_STATE_JUMP) { float t; /* delta time */ @@ -769,21 +775,21 @@ static void walkEvent(bContext *C, wmOperator *UNUSED(op), WalkInfo *walk, const walk->speed_jump = JUMP_SPEED_MIN + t * (JUMP_SPEED_MAX - JUMP_SPEED_MIN) / JUMP_TIME_MAX; /* when jumping, duration is how long it takes before we start going down */ - walk->teleport.duration = getVelocityZeroTime(walk->speed_jump); + walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump); /* no more increase of jump speed */ - walk->gravity = WALK_GRAVITY_STATE_ON; + walk->gravity_state = WALK_GRAVITY_STATE_ON; } break; case WALK_MODAL_JUMP: if ((walk->navigation_mode == WALK_MODE_GRAVITY) && - (walk->gravity == WALK_GRAVITY_STATE_OFF) && + (walk->gravity_state == WALK_GRAVITY_STATE_OFF) && (walk->teleport.state == WALK_TELEPORT_STATE_OFF)) { /* no need to check for ground, * walk->gravity wouldn't be off * if we were over a hole */ - walk->gravity = WALK_GRAVITY_STATE_JUMP; + walk->gravity_state = WALK_GRAVITY_STATE_JUMP; walk->speed_jump = JUMP_SPEED_MAX; walk->teleport.initial_time = PIL_check_seconds_timer(); @@ -793,7 +799,7 @@ static void walkEvent(bContext *C, wmOperator *UNUSED(op), WalkInfo *walk, const copy_v2_v2(walk->teleport.direction, walk->dvec_prev); /* when jumping, duration is how long it takes before we start going down */ - walk->teleport.duration = getVelocityZeroTime(walk->speed_jump); + walk->teleport.duration = getVelocityZeroTime(walk->gravity, walk->speed_jump); } break; @@ -853,14 +859,14 @@ static void walkMoveCamera(bContext *C, WalkInfo *walk, ED_view3d_cameracontrol_update(walk->v3d_camera_control, true, C, do_rotate, do_translate); } -static float getFreeFallDistance(const float time) +static float getFreeFallDistance(const float gravity, const float time) { - return EARTH_GRAVITY * (time * time) * 0.5f; + return gravity * (time * time) * 0.5f; } -static float getVelocityZeroTime(float velocity) +static float getVelocityZeroTime(const float gravity, const float velocity) { - return velocity / EARTH_GRAVITY; + return velocity / gravity; } static int walkApply(bContext *C, WalkInfo *walk) @@ -910,7 +916,7 @@ static int walkApply(bContext *C, WalkInfo *walk) if ((walk->active_directions) || moffset[0] || moffset[1] || walk->teleport.state == WALK_TELEPORT_STATE_ON || - walk->gravity != WALK_GRAVITY_STATE_OFF) + walk->gravity_state != WALK_GRAVITY_STATE_OFF) { float dvec_tmp[3]; @@ -955,7 +961,7 @@ static int walkApply(bContext *C, WalkInfo *walk) /* clamp the angle limits */ /* it ranges from 90.0f to -90.0f */ - angle = -asin(rv3d->viewmat[2][2]); + angle = -asinf(rv3d->viewmat[2][2]); if (angle > WALK_TOP_LIMIT && y > 0.0f) y = 0.0f; @@ -1000,7 +1006,7 @@ static int walkApply(bContext *C, WalkInfo *walk) /* WASD - 'move' translation code */ if ((walk->active_directions) && - (walk->gravity == WALK_GRAVITY_STATE_OFF)) + (walk->gravity_state == WALK_GRAVITY_STATE_OFF)) { short direction; @@ -1076,7 +1082,7 @@ static int walkApply(bContext *C, WalkInfo *walk) /* stick to the floor */ if (walk->navigation_mode == WALK_MODE_GRAVITY && - ELEM(walk->gravity, + ELEM(walk->gravity_state, WALK_GRAVITY_STATE_OFF, WALK_GRAVITY_STATE_START)) { @@ -1101,13 +1107,13 @@ static int walkApply(bContext *C, WalkInfo *walk) dvec[2] -= difference; /* in case we switched from FREE to GRAVITY too close to the ground */ - if (walk->gravity == WALK_GRAVITY_STATE_START) - walk->gravity = WALK_GRAVITY_STATE_OFF; + if (walk->gravity_state == WALK_GRAVITY_STATE_START) + walk->gravity_state = WALK_GRAVITY_STATE_OFF; } else { /* hijack the teleport variables */ walk->teleport.initial_time = PIL_check_seconds_timer(); - walk->gravity = WALK_GRAVITY_STATE_ON; + walk->gravity_state = WALK_GRAVITY_STATE_ON; walk->teleport.duration = 0.0f; copy_v3_v3(walk->teleport.origin, walk->rv3d->viewinv[3]); @@ -1117,7 +1123,7 @@ static int walkApply(bContext *C, WalkInfo *walk) } /* Falling or jumping) */ - if (ELEM(walk->gravity, WALK_GRAVITY_STATE_ON, WALK_GRAVITY_STATE_JUMP)) { + if (ELEM(walk->gravity_state, WALK_GRAVITY_STATE_ON, WALK_GRAVITY_STATE_JUMP)) { float t; float z_cur, z_new; bool ret; @@ -1130,7 +1136,7 @@ static int walkApply(bContext *C, WalkInfo *walk) copy_v2_v2(dvec, walk->teleport.direction); z_cur = walk->rv3d->viewinv[3][2]; - z_new = walk->teleport.origin[2] - getFreeFallDistance(t) * walk->grid; + z_new = walk->teleport.origin[2] - getFreeFallDistance(walk->gravity, t) * walk->grid; /* jump */ z_new += t * walk->speed_jump * walk->grid; @@ -1148,7 +1154,7 @@ static int walkApply(bContext *C, WalkInfo *walk) if (difference > 0.0f) { /* quit falling, lands at "view_height" from the floor */ dvec[2] -= difference; - walk->gravity = WALK_GRAVITY_STATE_OFF; + walk->gravity_state = WALK_GRAVITY_STATE_OFF; walk->speed_jump = 0.0f; } else { @@ -1334,5 +1340,3 @@ void VIEW3D_OT_walk(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_BLOCKING; } - -#undef EARTH_GRAVITY diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 7765dd511b4..0c360474b78 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -24,11 +24,13 @@ set(INC ../../blenkernel ../../blenlib ../../bmesh + ../../gpu ../../ikplugin ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -53,4 +55,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/transform/SConscript b/source/blender/editors/transform/SConscript index f3c8c13647a..e249856b747 100644 --- a/source/blender/editors/transform/SConscript +++ b/source/blender/editors/transform/SConscript @@ -31,19 +31,21 @@ sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../bmesh', + '../../gpu', '../../ikplugin', '../../makesdna', '../../makesrna', '../../windowmanager', ] -defs = [] +defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index b88c388c257..24499688835 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -58,6 +58,7 @@ #include "BKE_particle.h" #include "BKE_unit.h" #include "BKE_mask.h" +#include "BKE_report.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -86,6 +87,9 @@ #include "transform.h" +/* Disabling, since when you type you know what you are doing, and being able to set it to zero is handy. */ +// #define USE_NUM_NO_ZERO + #define MAX_INFO_LEN 256 static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg); @@ -190,9 +194,9 @@ static bool transdata_check_local_center(TransInfo *t, short around) { return ((around == V3D_LOCAL) && ( (t->flag & (T_OBJECT | T_POSE)) || - (t->obedit && ELEM4(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || + (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || (t->spacetype == SPACE_IPO) || - (t->options & (CTX_MOVIECLIP | CTX_MASK))) + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE))) ); } @@ -263,17 +267,28 @@ static void convertViewVec2D_mask(View2D *v2d, float r_vec[3], int dx, int dy) void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) { if ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) { - const float mval_f[2] = {(float)dx, (float)dy}; - ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac); + if (t->options & CTX_PAINT_CURVE) { + r_vec[0] = dx; + r_vec[1] = dy; + } + else { + const float mval_f[2] = {(float)dx, (float)dy}; + ED_view3d_win_to_delta(t->ar, mval_f, r_vec, t->zfac); + } } else if (t->spacetype == SPACE_IMAGE) { float aspx, aspy; if (t->options & CTX_MASK) { - convertViewVec2D_mask(t->view, r_vec, dx, dy); ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy); } + else if (t->options & CTX_PAINT_CURVE) { + r_vec[0] = dx; + r_vec[1] = dy; + + aspx = aspy = 1.0; + } else { convertViewVec2D(t->view, r_vec, dx, dy); ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); @@ -334,7 +349,6 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr SpaceImage *sima = t->sa->spacedata.first; if (t->options & CTX_MASK) { - /* not working quite right, TODO (see below too) */ float aspx, aspy; float v[2]; @@ -347,14 +361,15 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v); - v[0] = v[0] / aspx; - v[1] = v[1] / aspy; - ED_image_point_pos__reverse(sima, t->ar, v, v); adr[0] = v[0]; adr[1] = v[1]; } + else if (t->options & CTX_PAINT_CURVE) { + adr[0] = vec[0]; + adr[1] = vec[1]; + } else { float aspx, aspy, v[2]; @@ -405,7 +420,6 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr MovieClip *clip = ED_space_clip_get_clip(sc); if (clip) { - /* not working quite right, TODO (see above too) */ float aspx, aspy; float v[2]; @@ -418,9 +432,6 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v); - v[0] = v[0] / aspx; - v[1] = v[1] / aspy; - ED_clip_point_stable_pos__reverse(sc, t->ar, v, v); adr[0] = v[0]; @@ -460,7 +471,11 @@ void projectFloatViewEx(TransInfo *t, const float vec[3], float adr[2], const eV switch (t->spacetype) { case SPACE_VIEW3D: { - if (t->ar->regiontype == RGN_TYPE_WINDOW) { + if (t->options & CTX_PAINT_CURVE) { + adr[0] = vec[0]; + adr[1] = vec[1]; + } + else if (t->ar->regiontype == RGN_TYPE_WINDOW) { /* allow points behind the view [#33643] */ if (ED_view3d_project_float_global(t->ar, vec, adr, flag) != V3D_PROJ_RET_OK) { /* XXX, 2.64 and prior did this, weak! */ @@ -488,7 +503,7 @@ void projectFloatView(TransInfo *t, const float vec[3], float adr[2]) void applyAspectRatio(TransInfo *t, float vec[2]) { - if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { + if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) { SpaceImage *sima = t->sa->spacedata.first; float aspx, aspy; @@ -565,17 +580,23 @@ void removeAspectRatio(TransInfo *t, float vec[2]) static void viewRedrawForce(const bContext *C, TransInfo *t) { if (t->spacetype == SPACE_VIEW3D) { - /* Do we need more refined tags? */ - if (t->flag & T_POSE) - WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); - else - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + if (t->options & CTX_PAINT_CURVE) { + wmWindow *window = CTX_wm_window(C); + WM_paint_cursor_tag_redraw(window, t->ar); + } + else { + /* Do we need more refined tags? */ + if (t->flag & T_POSE) + WM_event_add_notifier(C, NC_OBJECT | ND_POSE, NULL); + else + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - /* for realtime animation record - send notifiers recognised by animation editors */ - // XXX: is this notifier a lame duck? - if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); - + /* for realtime animation record - send notifiers recognised by animation editors */ + // XXX: is this notifier a lame duck? + if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); + + } } else if (t->spacetype == SPACE_ACTION) { //SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first; @@ -601,6 +622,10 @@ static void viewRedrawForce(const bContext *C, TransInfo *t) WM_event_add_notifier(C, NC_MASK | NA_EDITED, mask); } + else if (t->options & CTX_PAINT_CURVE) { + wmWindow *window = CTX_wm_window(C); + WM_paint_cursor_tag_redraw(window, t->ar); + } else { // XXX how to deal with lock? SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; @@ -654,7 +679,7 @@ static void viewRedrawPost(bContext *C, TransInfo *t) allqueue(REDRAWIMAGE, 0); allqueue(REDRAWVIEW3D, 0); } - else if (ELEM3(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) { + else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_IPO)) { allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWNLA, 0); @@ -977,7 +1002,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_TRANSLATE: /* only switch when... */ - if (ELEM5(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { resetTransModal(t); resetTransRestrictions(t); restoreTransObjects(t); @@ -994,11 +1019,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) } else { if (t->obedit && t->obedit->type == OB_MESH) { - if ((t->mode == TFM_TRANSLATION) && - (t->spacetype == SPACE_VIEW3D) && - /* prevents accidental select-tweak, gkey. see: T40102 */ - (ISMOUSE(t->launch_event) == 0)) - { + if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) { resetTransModal(t); resetTransRestrictions(t); restoreTransObjects(t); @@ -1037,7 +1058,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) case TFM_MODAL_ROTATE: /* only switch when... */ if (!(t->options & CTX_TEXTURE) && !(t->options & (CTX_MOVIECLIP | CTX_MASK))) { - if (ELEM6(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { resetTransModal(t); resetTransRestrictions(t); @@ -1057,7 +1078,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_RESIZE: /* only switch when... */ - if (ELEM5(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { /* Scale isn't normally very useful after extrude along normals, see T39756 */ if ((t->con.mode & CON_APPLY) && (t->con.orientation == V3D_MANIP_NORMAL)) { @@ -1320,7 +1341,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case GKEY: /* only switch when... */ - if (ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL) ) { + if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { resetTransModal(t); resetTransRestrictions(t); restoreTransObjects(t); @@ -1332,7 +1353,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case SKEY: /* only switch when... */ - if (ELEM3(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL) ) { + if (ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION, TFM_TRACKBALL)) { resetTransModal(t); resetTransRestrictions(t); restoreTransObjects(t); @@ -1345,7 +1366,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) case RKEY: /* only switch when... */ if (!(t->options & CTX_TEXTURE)) { - if (ELEM4(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION) ) { + if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL, TFM_TRANSLATION)) { resetTransModal(t); resetTransRestrictions(t); @@ -1730,7 +1751,7 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) { float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1]; float angle = atan2f(dy, dx); - float dist = sqrtf(dx * dx + dy * dy); + float dist = hypotf(dx, dy); float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f); float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f); UI_ThemeColor(TH_VIEW_OVERLAY); @@ -1819,24 +1840,26 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); - xco = rect.xmax - (int)printable_size[0] - 10; - yco = rect.ymax - (int)printable_size[1] - 10; + xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + yco = (rect.ymax - U.widget_unit); /* warning text (to clarify meaning of overlays) * - original color was red to match the icon, but that clashes badly with a less nasty border */ UI_ThemeColorShade(TH_TEXT_HI, -50); #ifdef WITH_INTERNATIONAL - BLF_draw_default(xco, ar->winy - 17, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #else - BLF_draw_default_ascii(xco, ar->winy - 17, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #endif /* autokey recording icon... */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); - xco -= (ICON_DEFAULT_WIDTH + 2); + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; + UI_icon_draw(xco, yco, ICON_REC); glDisable(GL_BLEND); @@ -2032,7 +2055,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve initTransInfo(C, t, op, event); if (t->spacetype == SPACE_VIEW3D) { - //calc_manipulator_stats(curarea); initTransformOrientation(C, t); t->draw_handle_apply = ED_region_draw_cb_activate(t->ar->type, drawTransformApply, t, REGION_DRAW_PRE_VIEW); @@ -2077,7 +2099,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve * moded are available from manipulator and doing such check could * lead to keymap conflicts for other modes (see #31584) */ - if (ELEM3(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) { + if (ELEM(mode, TFM_TRANSLATION, TFM_ROTATION, TFM_RESIZE)) { wmKeyMapItem *kmi; for (kmi = t->keymap->items.first; kmi; kmi = kmi->next) { @@ -2853,7 +2875,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN * 2]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bend Angle: %s Radius: %s Alt, Clamp %s"), &c[0], &c[NUM_STR_REP_LEN], @@ -3021,7 +3043,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shear: %s %s"), c, t->proptext); } @@ -3097,9 +3119,11 @@ static void initResize(TransInfo *t) t->num.flag |= NUM_AFFECT_ALL; if (!t->obedit) { t->flag |= T_NO_ZERO; +#ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; t->num.val_flag[2] |= NUM_NO_ZERO; +#endif } t->idx_max = 2; @@ -3120,7 +3144,7 @@ static void headerResize(TransInfo *t, float vec[3], char str[MAX_INFO_LEN]) char tvec[NUM_STR_REP_LEN * 3]; size_t ofs = 0; if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); } else { BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", vec[0]); @@ -3389,9 +3413,11 @@ static void initSkinResize(TransInfo *t) t->num.flag |= NUM_AFFECT_ALL; if (!t->obedit) { t->flag |= T_NO_ZERO; +#ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; t->num.val_flag[2] |= NUM_NO_ZERO; +#endif } t->idx_max = 2; @@ -3532,7 +3558,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("To Sphere: %s %s"), c, t->proptext); } @@ -3604,8 +3630,15 @@ static void initRotation(TransInfo *t) if (t->flag & T_2D_EDIT) t->flag |= T_NO_CONSTRAINT; - negate_v3_v3(t->axis, t->viewinv[2]); - normalize_v3(t->axis); + if (t->options & CTX_PAINT_CURVE) { + t->axis[0] = 0.0; + t->axis[1] = 0.0; + t->axis[2] = -1.0; + } + else { + negate_v3_v3(t->axis, t->viewinv[2]); + normalize_v3(t->axis); + } copy_v3_v3(t->axis_orig, t->axis); } @@ -3639,7 +3672,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short if (td->flag & TD_USEQUAT) { - mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(fmat, td->smtx, mat, td->mtx); mat3_to_quat(quat, fmat); // Actual transform if (td->ext->quat) { @@ -3709,7 +3742,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short if ((t->flag & T_V3D_ALIGN) == 0) { /* align mode doesn't rotate objects itself */ /* euler or quaternion/axis-angle? */ if (td->ext->rotOrder == ROT_MODE_QUAT) { - mul_serie_m3(fmat, td->ext->r_mtx, mat, td->ext->r_smtx, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(fmat, td->ext->r_smtx, mat, td->ext->r_mtx); mat3_to_quat(quat, fmat); /* Actual transform */ @@ -3724,7 +3757,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle); - mul_serie_m3(fmat, td->ext->r_mtx, mat, td->ext->r_smtx, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(fmat, td->ext->r_smtx, mat, td->ext->r_mtx); mat3_to_quat(quat, fmat); /* Actual transform */ mul_qt_qtqt(tquat, quat, iquat); @@ -3781,7 +3814,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short if ((td->ext->rotOrder == ROT_MODE_QUAT) || (td->flag & TD_USEQUAT)) { /* can be called for texture space translate for example, then opt out */ if (td->ext->quat) { - mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(fmat, td->smtx, mat, td->mtx); mat3_to_quat(quat, fmat); // Actual transform mul_qt_qtqt(td->ext->quat, quat, td->ext->iquat); @@ -3795,7 +3828,7 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short axis_angle_to_quat(iquat, td->ext->irotAxis, td->ext->irotAngle); - mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(fmat, td->smtx, mat, td->mtx); mat3_to_quat(quat, fmat); // Actual transform mul_qt_qtqt(tquat, quat, iquat); @@ -3889,7 +3922,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Rot: %s %s %s"), &c[0], t->con.text, t->proptext); } @@ -3993,7 +4026,7 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN * 2]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Trackball: %s %s %s"), &c[0], &c[NUM_STR_REP_LEN], t->proptext); @@ -4034,6 +4067,9 @@ static void initTranslation(TransInfo *t) { if (t->spacetype == SPACE_ACTION) { /* this space uses time translate */ + BKE_report(t->reports, RPT_ERROR, + "Use 'Time_Translate' transform mode instead of 'Translation' mode " + "for translating keyframes in Dope Sheet Editor"); t->state = TRANS_CANCEL; } @@ -4095,7 +4131,7 @@ static void headerTranslation(TransInfo *t, float vec[3], char str[MAX_INFO_LEN] float dist; if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); dist = len_v3(t->num.val); } else { @@ -4343,7 +4379,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Shrink/Fatten:"), MAX_INFO_LEN - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, " %s", c); } else { @@ -4438,7 +4474,7 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Tilt: %s° %s"), &c[0], t->proptext); @@ -4492,7 +4528,9 @@ static void initCurveShrinkFatten(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; t->flag |= T_NO_ZERO; +#ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; +#endif t->flag |= T_NO_CONSTRAINT; } @@ -4514,7 +4552,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shrink/Fatten: %s"), c); } else { @@ -4567,7 +4605,9 @@ static void initMaskShrinkFatten(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; t->flag |= T_NO_ZERO; +#ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; +#endif t->flag |= T_NO_CONSTRAINT; } @@ -4590,7 +4630,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Feather Shrink/Fatten: %s"), c); } else { @@ -4683,7 +4723,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Push/Pull: %s%s %s"), c, t->con.text, t->proptext); } @@ -4742,7 +4782,7 @@ static void initBevelWeight(TransInfo *t) t->mode = TFM_BWEIGHT; t->transform = applyBevelWeight; - initMouseInputMode(t, &t->mouse, INPUT_SPRING); + initMouseInputMode(t, &t->mouse, INPUT_SPRING_DELTA); t->idx_max = 0; t->num.idx_max = 0; @@ -4766,7 +4806,6 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) weight = t->values[0]; - weight -= 1.0f; if (weight > 1.0f) weight = 1.0f; snapGridIncrement(t, &weight); @@ -4777,7 +4816,7 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); if (weight >= 0.0f) BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Bevel Weight: +%s %s"), c, t->proptext); @@ -4821,7 +4860,7 @@ static void initCrease(TransInfo *t) t->mode = TFM_CREASE; t->transform = applyCrease; - initMouseInputMode(t, &t->mouse, INPUT_SPRING); + initMouseInputMode(t, &t->mouse, INPUT_SPRING_DELTA); t->idx_max = 0; t->num.idx_max = 0; @@ -4845,7 +4884,6 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) crease = t->values[0]; - crease -= 1.0f; if (crease > 1.0f) crease = 1.0f; snapGridIncrement(t, &crease); @@ -4856,7 +4894,7 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); if (crease >= 0.0f) BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Crease: +%s %s"), c, t->proptext); @@ -4926,7 +4964,7 @@ static void headerBoneSize(TransInfo *t, float vec[3], char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); } else { BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", vec[0]); @@ -5063,7 +5101,7 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Envelope: %s"), c); } else { @@ -5436,7 +5474,17 @@ static bool createEdgeSlideVerts(TransInfo *t) while (1) { float vec_a[3], vec_b[3]; BMLoop *l_a, *l_b; + BMLoop *l_a_prev, *l_b_prev; BMVert *v_first; + /* If this succeeds call get_next_loop() + * which calculates the direction to slide based on clever checks. + * + * otherwise we simply use 'e_dir' as an edge-rail. + * (which is better when the attached edge is a boundary, see: T40422) + */ +#define EDGESLIDE_VERT_IS_INNER(v, e_dir) \ + ((BM_edge_is_boundary(e_dir) == false) && \ + (BM_vert_edge_count_nonwire(v) == 2)) v = NULL; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -5457,7 +5505,6 @@ static bool createEdgeSlideVerts(TransInfo *t) e = v->e; /*first, rewind*/ - numsel = 0; do { e = get_other_edge(v, e); if (!e) { @@ -5465,8 +5512,6 @@ static bool createEdgeSlideVerts(TransInfo *t) break; } - numsel += 1; - if (!BM_elem_flag_test(BM_edge_other_vert(e, v), BM_ELEM_TAG)) break; @@ -5486,10 +5531,12 @@ static bool createEdgeSlideVerts(TransInfo *t) } else { BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v); - if (BM_vert_edge_count_nonwire(v) == 2) + if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { get_next_loop(v, l_a, e, l_tmp->e, vec_a); - else + } + else { sub_v3_v3v3(vec_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co); + } } } @@ -5501,17 +5548,21 @@ static bool createEdgeSlideVerts(TransInfo *t) } else { BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v); - if (BM_vert_edge_count_nonwire(v) == 2) + if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { get_next_loop(v, l_b, e, l_tmp->e, vec_b); - else + } + else { sub_v3_v3v3(vec_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co); - + } } } else { l_b = NULL; } + l_a_prev = NULL; + l_b_prev = NULL; + /*iterate over the loop*/ v_first = v; do { @@ -5529,14 +5580,14 @@ static bool createEdgeSlideVerts(TransInfo *t) copy_v3_v3(sv->v_co_orig, v->co); sv->loop_nr = loop_nr; - if (l_a) { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v); + if (l_a || l_a_prev) { + BMLoop *l_tmp = BM_loop_other_edge_loop(l_a ? l_a : l_a_prev, v); sv->v_a = BM_edge_other_vert(l_tmp->e, v); copy_v3_v3(sv->dir_a, vec_a); } - if (l_b) { - BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v); + if (l_b || l_b_prev) { + BMLoop *l_tmp = BM_loop_other_edge_loop(l_b ? l_b : l_b_prev, v); sv->v_b = BM_edge_other_vert(l_tmp->e, v); copy_v3_v3(sv->dir_b, vec_b); } @@ -5558,7 +5609,7 @@ static bool createEdgeSlideVerts(TransInfo *t) if (l_a) { BMLoop *l_tmp = BM_loop_other_edge_loop(l_a, v); sv->v_a = BM_edge_other_vert(l_tmp->e, v); - if (BM_vert_edge_count_nonwire(v) == 2) { + if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_a); } else { @@ -5569,7 +5620,7 @@ static bool createEdgeSlideVerts(TransInfo *t) if (l_b) { BMLoop *l_tmp = BM_loop_other_edge_loop(l_b, v); sv->v_b = BM_edge_other_vert(l_tmp->e, v); - if (BM_vert_edge_count_nonwire(v) == 2) { + if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_b); } else { @@ -5585,29 +5636,54 @@ static bool createEdgeSlideVerts(TransInfo *t) l_a_ok_prev = (l_a != NULL); l_b_ok_prev = (l_b != NULL); - l_a = l_a ? get_next_loop(v, l_a, e_prev, e, vec_a) : NULL; - l_b = l_b ? get_next_loop(v, l_b, e_prev, e, vec_b) : NULL; - - /* find the opposite loop if it was missing previously */ - if (l_a == NULL && l_b && (l_b->radial_next != l_b)) l_a = l_b->radial_next; - else if (l_b == NULL && l_a && (l_a->radial_next != l_a)) l_b = l_a->radial_next; - - /* if there are non-contiguous faces, we can still recover the loops of the new edges faces */ - /* note!, the behavior in this case means edges may move in opposite directions, - * this could be made to work more usefully. */ - if (!(l_a && l_b) && (e->l != NULL)) { - if (l_a_ok_prev) { - l_a = e->l; - if (l_a->radial_next != l_a) { - l_b = l_a->radial_next; - } + l_a_prev = l_a; + l_b_prev = l_b; + + if (l_a) { + l_a = get_next_loop(v, l_a, e_prev, e, vec_a); + } + else { + zero_v3(vec_a); + } + + if (l_b) { + l_b = get_next_loop(v, l_b, e_prev, e, vec_b); + } + else { + zero_v3(vec_b); + } + + + if (l_a && l_b) { + /* pass */ + } + else { + if (l_a || l_b) { + /* find the opposite loop if it was missing previously */ + if (l_a == NULL && l_b && (l_b->radial_next != l_b)) l_a = l_b->radial_next; + else if (l_b == NULL && l_a && (l_a->radial_next != l_a)) l_b = l_a->radial_next; } - else if (l_b_ok_prev) { - l_b = e->l; - if (l_b->radial_next != l_b) { - l_a = l_b->radial_next; + else if (e->l != NULL) { + /* if there are non-contiguous faces, we can still recover the loops of the new edges faces */ + /* note!, the behavior in this case means edges may move in opposite directions, + * this could be made to work more usefully. */ + + if (l_a_ok_prev) { + l_a = e->l; + l_b = (l_a->radial_next != l_a) ? l_a->radial_next : NULL; + } + else if (l_b_ok_prev) { + l_b = e->l; + l_a = (l_b->radial_next != l_b) ? l_b->radial_next : NULL; } } + + if (!l_a_ok_prev && l_a) { + get_next_loop(v, l_a, e, e_prev, vec_a); + } + if (!l_b_ok_prev && l_b) { + get_next_loop(v, l_b, e, e_prev, vec_b); + } } BM_elem_flag_disable(v, BM_ELEM_TAG); @@ -5615,6 +5691,8 @@ static bool createEdgeSlideVerts(TransInfo *t) } while ((e != v_first->e) && (l_a || l_b)); loop_nr++; + +#undef EDGESLIDE_VERT_IS_INNER } /* use for visibility checks */ @@ -6198,7 +6276,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); if (is_proportional) { BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Edge Slide: %s (E)ven: %s"), @@ -6721,7 +6799,7 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Vert Slide: "), MAX_INFO_LEN - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); ofs += BLI_strncpy_rlen(str + ofs, &c[0], MAX_INFO_LEN - ofs); } else { @@ -6788,7 +6866,7 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Roll: %s"), &c[0]); } @@ -6862,7 +6940,7 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c); + outputNumInput(&(t->num), c, &t->scene->unit); if (time >= 0.0f) BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Time: +%s %s"), c, t->proptext); @@ -7073,7 +7151,7 @@ static void headerSeqSlide(TransInfo *t, float val[2], char str[MAX_INFO_LEN]) size_t ofs = 0; if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); } else { BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.0f, %.0f", val[0], val[1]); @@ -7299,7 +7377,7 @@ static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) /* if numeric input is active, use results from that, otherwise apply snapping to result */ if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); } else { const Scene *scene = t->scene; @@ -7316,13 +7394,17 @@ static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) /* second step */ val = floorf((double)val / secf + 0.5); } - else { - /* nearest frame/second/marker */ + else if (autosnap == SACTSNAP_SECOND) { + /* nearest second */ val = (float)((double)val / secf); } if (autosnap == SACTSNAP_FRAME) BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 (%.4f)", (int)val, val); + else if (autosnap == SACTSNAP_SECOND) + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%d.00 sec (%.4f)", (int)val, val); + else if (autosnap == SACTSNAP_TSTEP) + BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", val); else BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val); } @@ -7459,7 +7541,7 @@ static void headerTimeSlide(TransInfo *t, float sval, char str[MAX_INFO_LEN]) char tvec[NUM_STR_REP_LEN * 3]; if (hasNumInput(&t->num)) { - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); } else { float minx = *((float *)(t->customData)); @@ -7607,7 +7689,7 @@ static void headerTimeScale(TransInfo *t, char str[MAX_INFO_LEN]) char tvec[NUM_STR_REP_LEN * 3]; if (hasNumInput(&t->num)) - outputNumInput(&(t->num), tvec); + outputNumInput(&(t->num), tvec, &t->scene->unit); else BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", t->values[0]); @@ -7673,17 +7755,11 @@ static void applyTimeScale(TransInfo *t, const int UNUSED(mval[2])) /* TODO, move to: transform_queries.c */ -bool checkUseLocalCenter_GraphEdit(TransInfo *t) -{ - return ((t->around == V3D_LOCAL) && - !ELEM4(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE)); -} - bool checkUseAxisMatrix(TransInfo *t) { /* currently only checks for editmode */ if (t->flag & T_EDIT) { - if ((t->around == V3D_LOCAL) && (ELEM4(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { + if ((t->around == V3D_LOCAL) && (ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { /* not all editmode supports axis-matrix */ return true; } diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f34d2050853..67d55639528 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -61,6 +61,7 @@ struct wmEvent; struct wmTimer; struct ARegion; struct ReportList; +struct EditBone; /* transinfo->redraw */ typedef enum { @@ -129,15 +130,15 @@ typedef struct TransDataExtension { // float drotAxis[3]; /* Initial object drotAxis, TODO: not yet implemented */ float dquat[4]; /* Initial object dquat */ float dscale[3]; /* Initial object dscale */ - float *rot; /* Rotation of the data to transform (Faculative) */ + float *rot; /* Rotation of the data to transform */ float irot[3]; /* Initial rotation */ - float *quat; /* Rotation quaternion of the data to transform (Faculative) */ + float *quat; /* Rotation quaternion of the data to transform */ float iquat[4]; /* Initial rotation quaternion */ - float *rotAngle; /* Rotation angle of the data to transform (Faculative) */ + float *rotAngle; /* Rotation angle of the data to transform */ float irotAngle; /* Initial rotation angle */ - float *rotAxis; /* Rotation axis of the data to transform (Faculative) */ + float *rotAxis; /* Rotation axis of the data to transform */ float irotAxis[4]; /* Initial rotation axis */ - float *size; /* Size of the data to transform (Faculative) */ + float *size; /* Size of the data to transform */ float isize[3]; /* Initial size */ float obmat[4][4]; /* Object matrix */ float l_smtx[3][3]; /* use instead of td->smtx, It is the same but without the 'bone->bone_mat', see TD_PBONE_LOCAL_MTX_C */ @@ -251,6 +252,17 @@ typedef struct VertSlideData { int curr_sv_index; } VertSlideData; +typedef struct BoneInitData { + struct EditBone *bone; + float tail[3]; + float rad_tail; + float roll; + float head[3]; + float dist; + float xwidth; + float zwidth; +} BoneInitData; + typedef struct TransData { float dist; /* Distance needed to affect element (for Proportionnal Editing) */ float rdist; /* Distance to the nearest element (for Proportionnal Editing) */ @@ -520,10 +532,11 @@ void flushTransNodes(TransInfo *t); void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); +void flushTransPaintCurve(TransInfo *t); +void restoreBones(TransInfo *t); /*********************** exported from transform_manipulator.c ********** */ bool gimbal_axis(struct Object *ob, float gmat[3][3]); /* return 0 when no gimbal for selection */ -int calc_manipulator_stats(const struct bContext *C); /*********************** TransData Creation and General Handling *********** */ void createTransData(struct bContext *C, TransInfo *t); @@ -600,6 +613,7 @@ typedef enum { INPUT_VECTOR, INPUT_SPRING, INPUT_SPRING_FLIP, + INPUT_SPRING_DELTA, INPUT_ANGLE, INPUT_ANGLE_SPRING, INPUT_TRACKBALL, @@ -608,7 +622,7 @@ typedef enum { INPUT_VERTICAL_RATIO, INPUT_VERTICAL_ABSOLUTE, INPUT_CUSTOM_RATIO, - INPUT_CUSTOM_RATIO_FLIP + INPUT_CUSTOM_RATIO_FLIP, } MouseInputMode; void initMouseInput(TransInfo *t, MouseInput *mi, const float center[2], const int mval[2]); @@ -679,7 +693,6 @@ void freeVertSlideVerts(TransInfo *t); /* TODO. transform_queries.c */ -bool checkUseLocalCenter_GraphEdit(TransInfo *t); bool checkUseAxisMatrix(TransInfo *t); #endif diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 79f266df607..d8f17315c01 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -663,7 +663,7 @@ void drawConstraint(TransInfo *t) { TransCon *tc = &(t->con); - if (!ELEM3(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) return; if (!(tc->mode & CON_APPLY)) return; @@ -756,6 +756,9 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) /* untested - mask aspect is TODO */ ED_space_image_get_aspect(t->sa->spacedata.first, &aspx, &aspy); } + else if (t->options & CTX_PAINT_CURVE) { + aspx = aspy = 1.0; + } else { ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 949ee79a3dc..16c4a87b763 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -33,6 +33,7 @@ #include <math.h> #include "DNA_anim_types.h" +#include "DNA_brush_types.h" #include "DNA_armature_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -80,6 +81,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_particle.h" +#include "BKE_paint.h" #include "BKE_pointcache.h" #include "BKE_report.h" #include "BKE_rigidbody.h" @@ -124,8 +126,8 @@ static void transform_around_single_fallback(TransInfo *t) { if ((t->total == 1) && - (ELEM3(t->around, V3D_CENTER, V3D_CENTROID, V3D_ACTIVE)) && - (ELEM3(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL))) + (ELEM(t->around, V3D_CENTER, V3D_CENTROID, V3D_ACTIVE)) && + (ELEM(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL))) { t->around = V3D_LOCAL; } @@ -247,7 +249,7 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } dist_sq = len_squared_v3(vec); - if ((tob->rdist == -1.0f) || (dist_sq < (tob->rdist * tob->rdist))) { + if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { tob->rdist = sqrtf(dist_sq); } } @@ -282,7 +284,7 @@ static void createTransTexspace(TransInfo *t) } id = ob->data; - if (id == NULL || !ELEM3(GS(id->name), ID_ME, ID_CU, ID_MB)) { + if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) { BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform"); t->total = 0; return; @@ -584,12 +586,12 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr if (constraints_list_needinv(t, &pchan->constraints)) { copy_m3_m4(tmat, pchan->constinv); invert_m3_m3(cmat, tmat); - mul_serie_m3(td->mtx, pmat, omat, cmat, NULL, NULL, NULL, NULL, NULL); - mul_serie_m3(td->ext->r_mtx, rpmat, omat, cmat, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(td->mtx, cmat, omat, pmat); + mul_m3_series(td->ext->r_mtx, cmat, omat, rpmat); } else { - mul_serie_m3(td->mtx, pmat, omat, NULL, NULL, NULL, NULL, NULL, NULL); - mul_serie_m3(td->ext->r_mtx, rpmat, omat, NULL, NULL, NULL, NULL, NULL, NULL); + mul_m3_series(td->mtx, omat, pmat); + mul_m3_series(td->ext->r_mtx, omat, rpmat); } invert_m3_m3(td->ext->r_smtx, td->ext->r_mtx); } @@ -1052,18 +1054,64 @@ static void createTransPose(TransInfo *t, Object *ob) if (ik_on) transform_autoik_update(t, 0); } -/* ********************* armature ************** */ +void restoreBones(TransInfo *t) +{ + bArmature *arm = t->obedit->data; + BoneInitData *bid = t->customData; + EditBone *ebo; + + while (bid->bone) { + ebo = bid->bone; + + ebo->dist = bid->dist; + ebo->rad_tail = bid->rad_tail; + ebo->roll = bid->roll; + ebo->xwidth = bid->xwidth; + ebo->zwidth = bid->zwidth; + copy_v3_v3(ebo->head, bid->head); + copy_v3_v3(ebo->tail, bid->tail); + + if (arm->flag & ARM_MIRROR_EDIT) { + EditBone *ebo_child; + + /* Also move connected ebo_child, in case ebo_child's name aren't mirrored properly */ + for (ebo_child = arm->edbo->first; ebo_child; ebo_child = ebo_child->next) { + if ((ebo_child->flag & BONE_CONNECTED) && (ebo_child->parent == ebo)) { + copy_v3_v3(ebo_child->head, ebo->tail); + ebo_child->rad_head = ebo->rad_tail; + } + } + /* Also move connected parent, in case parent's name isn't mirrored properly */ + if ((ebo->flag & BONE_CONNECTED) && ebo->parent) { + EditBone *parent = ebo->parent; + copy_v3_v3(parent->tail, ebo->head); + parent->rad_tail = ebo->rad_head; + } + } + + bid++; + } +} + + +/* ********************* armature ************** */ static void createTransArmatureVerts(TransInfo *t) { - EditBone *ebo; + EditBone *ebo, *eboflip; bArmature *arm = t->obedit->data; ListBase *edbo = arm->edbo; - TransData *td; + TransData *td, *td_old; float mtx[3][3], smtx[3][3], bonemat[3][3]; + bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0); + int total_mirrored = 0, i; + int oldtot; + BoneInitData *bid; t->total = 0; for (ebo = edbo->first; ebo; ebo = ebo->next) { + oldtot = t->total; + if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { if (t->mode == TFM_BONESIZE) { if (ebo->flag & BONE_SELECTED) @@ -1080,6 +1128,12 @@ static void createTransArmatureVerts(TransInfo *t) t->total++; } } + + if (mirror && (oldtot < t->total)) { + eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo); + if (eboflip) + total_mirrored++; + } } if (!t->total) return; @@ -1091,7 +1145,15 @@ static void createTransArmatureVerts(TransInfo *t) td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransEditBone"); + if (mirror) { + t->customData = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData"); + t->flag |= T_FREE_CUSTOMDATA; + } + + i = 0; + for (ebo = edbo->first; ebo; ebo = ebo->next) { + td_old = td; ebo->oldlength = ebo->length; // length==0.0 on extrude, used for scaling radius of bone points if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { @@ -1223,6 +1285,26 @@ static void createTransArmatureVerts(TransInfo *t) } } } + + if (mirror && (td_old != td)) { + eboflip = ED_armature_bone_get_mirrored(arm->edbo, ebo); + if (eboflip) { + bid[i].bone = eboflip; + bid[i].dist = eboflip->dist; + bid[i].rad_tail = eboflip->rad_tail; + bid[i].roll = eboflip->roll; + bid[i].xwidth = eboflip->xwidth; + bid[i].zwidth = eboflip->zwidth; + copy_v3_v3(bid[i].head, eboflip->head); + copy_v3_v3(bid[i].tail, eboflip->tail); + i++; + } + } + } + + if (mirror) { + /* trick to terminate iteration */ + bid[total_mirrored].bone = NULL; } } @@ -1650,7 +1732,9 @@ static void createTransLatticeVerts(TransInfo *t) if (bp->f1 & SELECT) { td->flag = TD_SELECTED; } - else td->flag = 0; + else { + td->flag = 0; + } copy_m3_m3(td->smtx, smtx); copy_m3_m3(td->mtx, mtx); @@ -1889,6 +1973,7 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float dists[i] = 0.0f; } } + bm->elem_index_dirty &= ~BM_VERT; } do { @@ -1898,37 +1983,45 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float memcpy(dists_prev, dists, sizeof(float) * bm->totvert); while ((v = BLI_LINKSTACK_POP(queue))) { - BMIter iter; - BMEdge *e; - BMLoop *l; + /* quick checks */ + bool has_edges = (v->e != NULL); + bool has_faces = false; /* connected edge-verts */ - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) { - BMVert *v_other = BM_edge_other_vert(e, v); - if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { - if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { - BM_elem_flag_enable(v_other, BM_ELEM_TAG); - BLI_LINKSTACK_PUSH(queue_next, v_other); + if (has_edges) { + BMIter iter; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { + has_faces |= (BM_edge_is_wire(e) == false); + + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) { + BMVert *v_other = BM_edge_other_vert(e, v); + if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { + if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { + BM_elem_flag_enable(v_other, BM_ELEM_TAG); + BLI_LINKSTACK_PUSH(queue_next, v_other); + } } } } } - /* connected face-verts (excluding adjacent verts) */ - BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { - if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len > 3)) { - BMLoop *l_end = l->prev; - l = l->next->next; - do { - BMVert *v_other = l->v; + /* imaginary edge diagonally across quad */ + if (has_faces) { + BMIter iter; + BMLoop *l; + + BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) { + if ((BM_elem_flag_test(l->f, BM_ELEM_HIDDEN) == 0) && (l->f->len == 4)) { + BMVert *v_other = l->next->next->v; if (bmesh_test_dist_add(v, v_other, dists, dists_prev, mtx)) { if (BM_elem_flag_test(v_other, BM_ELEM_TAG) == 0) { BM_elem_flag_enable(v_other, BM_ELEM_TAG); BLI_LINKSTACK_PUSH(queue_next, v_other); } } - } while ((l = l->next) != l_end); + } } } } @@ -2300,8 +2393,7 @@ static void createTransEditVerts(TransInfo *t) quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]); if (defmats) - mul_serie_m3(mat, mtx, qmat, defmats[a], - NULL, NULL, NULL, NULL, NULL); + mul_m3_series(mat, defmats[a], qmat, mtx); else mul_m3_m3m3(mat, mtx, qmat); } @@ -2622,7 +2714,7 @@ static void createTransUVs(bContext *C, TransInfo *t) if (propconnected) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - BLI_BITMAP_SET(island_enabled, element->island); + BLI_BITMAP_ENABLE(island_enabled, element->island); } } @@ -2663,7 +2755,7 @@ static void createTransUVs(bContext *C, TransInfo *t) if (propconnected) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - if (!BLI_BITMAP_GET(island_enabled, element->island)) { + if (!BLI_BITMAP_TEST(island_enabled, element->island)) { count_rejected++; continue; } @@ -2702,8 +2794,8 @@ void flushTransUVs(TransInfo *t) td->loc2d[1] = td->loc[1] * invy; if ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)) { - td->loc2d[0] = (float)floor(width * td->loc2d[0] + 0.5f) / width; - td->loc2d[1] = (float)floor(height * td->loc2d[1] + 0.5f) / height; + td->loc2d[0] = floorf(width * td->loc2d[0] + 0.5f) / width; + td->loc2d[1] = floorf(height * td->loc2d[1] + 0.5f) / height; } } } @@ -2861,7 +2953,7 @@ static void createTransNlaData(bContext *C, TransInfo *t) } /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } @@ -2994,15 +3086,15 @@ static void createTransNlaData(bContext *C, TransInfo *t) } /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ********************* ACTION EDITOR ****************** */ -static int gpf_cmp_frame(void *thunk, void *a, void *b) +static int gpf_cmp_frame(void *thunk, const void *a, const void *b) { - bGPDframe *frame_a = a; - bGPDframe *frame_b = b; + const bGPDframe *frame_a = a; + const bGPDframe *frame_b = b; if (frame_a->framenum < frame_b->framenum) return -1; if (frame_a->framenum > frame_b->framenum) return 1; @@ -3016,10 +3108,10 @@ static int gpf_cmp_frame(void *thunk, void *a, void *b) return 0; } -static int masklay_shape_cmp_frame(void *thunk, void *a, void *b) +static int masklay_shape_cmp_frame(void *thunk, const void *a, const void *b) { - MaskLayerShape *frame_a = a; - MaskLayerShape *frame_b = b; + const MaskLayerShape *frame_a = a; + const MaskLayerShape *frame_b = b; if (frame_a->frame < frame_b->frame) return -1; if (frame_a->frame > frame_b->frame) return 1; @@ -3183,7 +3275,7 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) } /* free temp data */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ----------------------------- */ @@ -3458,7 +3550,7 @@ static void createTransActionData(bContext *C, TransInfo *t) /* stop if trying to build list if nothing selected */ if (count == 0) { /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } @@ -3547,17 +3639,22 @@ static void createTransActionData(bContext *C, TransInfo *t) } /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* ********************* GRAPH EDITOR ************************* */ +typedef struct TransDataGraph { + float unit_scale; +} TransDataGraph; + /* Helper function for createTransGraphEditData, which is responsible for associating * source data with transform data */ -static void bezt_to_transdata(TransData *td, TransData2D *td2d, AnimData *adt, BezTriple *bezt, - int bi, short selected, short ishandle, short intvals, - float mtx[3][3], float smtx[3][3]) +static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph *tdg, + AnimData *adt, BezTriple *bezt, + int bi, bool selected, bool ishandle, bool intvals, + float mtx[3][3], float smtx[3][3], float unit_scale) { float *loc = bezt->vec[bi]; const float *cent = bezt->vec[1]; @@ -3571,41 +3668,39 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, AnimData *adt, B if (adt) { td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP); - td2d->loc[1] = loc[1]; + td2d->loc[1] = loc[1] * unit_scale; td2d->loc[2] = 0.0f; td2d->loc2d = loc; td->loc = td2d->loc; td->center[0] = BKE_nla_tweakedit_remap(adt, cent[0], NLATIME_CONVERT_MAP); - td->center[1] = cent[1]; + td->center[1] = cent[1] * unit_scale; td->center[2] = 0.0f; copy_v3_v3(td->iloc, td->loc); } else { td2d->loc[0] = loc[0]; - td2d->loc[1] = loc[1]; + td2d->loc[1] = loc[1] * unit_scale; td2d->loc[2] = 0.0f; td2d->loc2d = loc; td->loc = td2d->loc; copy_v3_v3(td->center, cent); + td->center[1] *= unit_scale; copy_v3_v3(td->iloc, td->loc); } - if (td->flag & TD_MOVEHANDLE1) { + if (!ishandle) { td2d->h1 = bezt->vec[0]; - copy_v2_v2(td2d->ih1, td2d->h1); - } - else - td2d->h1 = NULL; - - if (td->flag & TD_MOVEHANDLE2) { td2d->h2 = bezt->vec[2]; + copy_v2_v2(td2d->ih1, td2d->h1); copy_v2_v2(td2d->ih2, td2d->h2); } - else + else { + td2d->h1 = NULL; td2d->h2 = NULL; + } memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; @@ -3630,6 +3725,18 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, AnimData *adt, B /* copy space-conversion matrices for dealing with non-uniform scales */ copy_m3_m3(td->mtx, mtx); copy_m3_m3(td->smtx, smtx); + + tdg->unit_scale = unit_scale; +} + +static bool graph_edit_is_translation_mode(TransInfo *t) +{ + return ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE); +} + +static bool graph_edit_use_local_center(TransInfo *t) +{ + return (t->around == V3D_LOCAL) && !graph_edit_is_translation_mode(t); } static void createTransGraphEditData(bContext *C, TransInfo *t) @@ -3641,6 +3748,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) TransData *td = NULL; TransData2D *td2d = NULL; + TransDataGraph *tdg = NULL; bAnimContext ac; ListBase anim_data = {NULL, NULL}; @@ -3651,8 +3759,9 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) int count = 0, i; float cfra; float mtx[3][3], smtx[3][3]; + const bool is_translation_mode = graph_edit_is_translation_mode(t); const bool use_handle = !(sipo->flag & SIPO_NOHANDLES); - const bool use_local_center = checkUseLocalCenter_GraphEdit(t); + const bool use_local_center = graph_edit_use_local_center(t); short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS; /* determine what type of data we are operating on */ @@ -3699,11 +3808,11 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const char sel2 = bezt->f2 & SELECT; - const char sel1 = use_handle ? bezt->f1 & SELECT : sel2; - const char sel3 = use_handle ? bezt->f3 & SELECT : sel2; + const bool sel2 = bezt->f2 & SELECT; + const bool sel1 = use_handle ? bezt->f1 & SELECT : sel2; + const bool sel3 = use_handle ? bezt->f3 & SELECT : sel2; - if (ELEM4(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE)) { + if (is_translation_mode) { /* for 'normal' pivots - just include anything that is selected. * this works a bit differently in translation modes */ if (sel2) { @@ -3714,9 +3823,9 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (sel3) count++; } } - else if (sipo->around == V3D_LOCAL) { - /* for local-pivot we only need to count the number of selected handles only, so that centerpoints don't - * don't get moved wrong + else if (use_local_center) { + /* for local-pivot we only need to count the number of selected handles only, + * so that centerpoints don't get moved wrong */ if (bezt->ipo == BEZT_IPO_BEZ) { if (sel1) count++; @@ -3738,7 +3847,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) /* stop if trying to build list if nothing selected */ if (count == 0) { /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); return; } @@ -3748,9 +3857,12 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) t->data = MEM_callocN(t->total * sizeof(TransData), "TransData (Graph Editor)"); /* for each 2d vert a 3d vector is allocated, so that they can be treated just as if they were 3d verts */ t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D (Graph Editor)"); + t->customData = MEM_callocN(t->total * sizeof(TransDataGraph), "TransDataGraph"); + t->flag |= T_FREE_CUSTOMDATA; td = t->data; td2d = t->data2d; + tdg = t->customData; /* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */ unit_m3(mtx); @@ -3775,9 +3887,8 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - short intvals = (fcu->flag & FCURVE_INT_VALUES); + bool intvals = (fcu->flag & FCURVE_INT_VALUES); float unit_scale; - float scaled_mtx[3][3], scaled_smtx[3][3]; /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) @@ -3793,17 +3904,12 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag); - copy_m3_m3(scaled_mtx, mtx); - copy_m3_m3(scaled_smtx, smtx); - mul_v3_fl(scaled_mtx[1], unit_scale); - mul_v3_fl(scaled_smtx[1], 1.0f / unit_scale); - /* only include BezTriples whose 'keyframe' occurs on the same side of the current frame as mouse (if applicable) */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { - const char sel2 = bezt->f2 & SELECT; - const char sel1 = use_handle ? bezt->f1 & SELECT : sel2; - const char sel3 = use_handle ? bezt->f3 & SELECT : sel2; + const bool sel2 = bezt->f2 & SELECT; + const bool sel1 = use_handle ? bezt->f1 & SELECT : sel2; + const bool sel3 = use_handle ? bezt->f3 & SELECT : sel2; TransDataCurveHandleFlags *hdata = NULL; /* short h1=1, h2=1; */ /* UNUSED */ @@ -3811,10 +3917,10 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) /* only include handles if selected, irrespective of the interpolation modes. * also, only treat handles specially if the center point isn't selected. */ - if (!ELEM4(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE) || !(sel2)) { + if (!is_translation_mode || !(sel2)) { if (sel1) { hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, adt, bezt, 0, 1, 1, intvals, scaled_mtx, scaled_smtx); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale); } else { /* h1 = 0; */ /* UNUSED */ @@ -3823,7 +3929,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (sel3) { if (hdata == NULL) hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, adt, bezt, 2, 1, 1, intvals, scaled_mtx, scaled_smtx); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale); } else { /* h2 = 0; */ /* UNUSED */ @@ -3831,10 +3937,9 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) } /* only include main vert if selected */ - if (sel2 && (use_local_center == false)) { - + if (sel2 && !use_local_center) { /* move handles relative to center */ - if (ELEM4(t->mode, TFM_TRANSLATION, TFM_TIME_TRANSLATE, TFM_TIME_SLIDE, TFM_TIME_DUPLICATE)) { + if (is_translation_mode) { if (sel1) td->flag |= TD_MOVEHANDLE1; if (sel3) td->flag |= TD_MOVEHANDLE2; } @@ -3844,8 +3949,8 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (hdata == NULL) hdata = initTransDataCurveHandles(td, bezt); } - - bezt_to_transdata(td++, td2d++, adt, bezt, 1, 1, 0, intvals, scaled_mtx, scaled_smtx); + + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale); } /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...): @@ -3870,7 +3975,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) } /* cleanup temp list */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } @@ -4083,14 +4188,19 @@ void flushTransGraphData(TransInfo *t) SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; TransData *td; TransData2D *td2d; + TransDataGraph *tdg; Scene *scene = t->scene; double secf = FPS; int a; /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = t->data, td2d = t->data2d, tdg = t->customData; + a < t->total; + a++, td++, td2d++, tdg++) + { AnimData *adt = (AnimData *)td->extra; /* pointers to relevant AnimData blocks are stored in the td->extra pointers */ - + float inv_unit_scale = 1.0f / tdg->unit_scale; + /* handle snapping for time values * - we should still be in NLA-mapping timespace * - only apply to keyframes (but never to handles) @@ -4147,16 +4257,16 @@ void flushTransGraphData(TransInfo *t) if (td->flag & TD_INTVALUES) td2d->loc2d[1] = floorf(td2d->loc[1] + 0.5f); else - td2d->loc2d[1] = td2d->loc[1]; + td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale; if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; - td2d->h1[1] = td2d->ih1[1] + td->loc[1] - td->iloc[1]; + td2d->h1[1] = td2d->ih1[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; } if ((td->flag & TD_MOVEHANDLE2) && td2d->h2) { td2d->h2[0] = td2d->ih2[0] + td->loc[0] - td->iloc[0]; - td2d->h2[1] = td2d->ih2[1] + td->loc[1] - td->iloc[1]; + td2d->h2[1] = td2d->ih2[1] + (td->loc[1] - td->iloc[1]) * inv_unit_scale; } } } @@ -4681,18 +4791,24 @@ static bool constraints_list_needinv(TransInfo *t, ListBase *list) if ((con->flag & CONSTRAINT_DISABLE) == 0 && (con->enforce != 0.0f)) { /* (affirmative) returns for specific constraints here... */ /* constraints that require this regardless */ - if (ELEM5(con->type, - CONSTRAINT_TYPE_CHILDOF, - CONSTRAINT_TYPE_FOLLOWPATH, - CONSTRAINT_TYPE_CLAMPTO, - CONSTRAINT_TYPE_OBJECTSOLVER, - CONSTRAINT_TYPE_FOLLOWTRACK)) + if (ELEM(con->type, + CONSTRAINT_TYPE_FOLLOWPATH, + CONSTRAINT_TYPE_CLAMPTO, + CONSTRAINT_TYPE_OBJECTSOLVER, + CONSTRAINT_TYPE_FOLLOWTRACK)) { return true; } /* constraints that require this only under special conditions */ - if (con->type == CONSTRAINT_TYPE_ROTLIKE) { + if (con->type == CONSTRAINT_TYPE_CHILDOF) { + /* ChildOf constraint only works when using all location components, see T42256. */ + bChildOfConstraint *data = (bChildOfConstraint *)con->data; + + if ((data->flag & CHILDOF_LOCX) && (data->flag & CHILDOF_LOCY) && (data->flag & CHILDOF_LOCZ)) + return true; + } + else if (con->type == CONSTRAINT_TYPE_ROTLIKE) { /* CopyRot constraint only does this when rotating, and offset is on */ bRotateLikeConstraint *data = (bRotateLikeConstraint *)con->data; @@ -5532,7 +5648,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) } /* free temp memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } else if (ac.datatype == ANIMCONT_ACTION) { // TODO: just integrate into the above... /* Depending on the lock status, draw necessary views */ @@ -5664,7 +5780,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) } /* free temp memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* Make sure all F-Curves are set correctly, but not if transform was @@ -5701,7 +5817,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) } /* free temp memory */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); /* perform after-transfrom validation */ ED_nla_postop_refresh(&ac); @@ -5762,6 +5878,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) DAG_id_tag_update(&ob->id, OB_RECALC_DATA); } + else if (t->options & CTX_PAINT_CURVE) { + /* pass */ + } else if ((t->scene->basact) && (ob = t->scene->basact->object) && (ob->mode & OB_MODE_PARTICLE_EDIT) && @@ -6627,6 +6746,8 @@ static void MaskHandleToTransData(MaskSplinePoint *point, eMaskWhichHandle which td->flag = 0; td->loc = td2d->loc; mul_v2_m3v2(td->center, parent_matrix, bezt->vec[1]); + td->center[0] *= asp[0]; + td->center[1] *= asp[1]; copy_v3_v3(td->iloc, td->loc); memset(td->axismtx, 0, sizeof(td->axismtx)); @@ -6688,6 +6809,8 @@ static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, td->flag = 0; td->loc = td2d->loc; mul_v2_m3v2(td->center, parent_matrix, bezt->vec[1]); + td->center[0] *= asp[0]; + td->center[1] *= asp[1]; copy_v3_v3(td->iloc, td->loc); memset(td->axismtx, 0, sizeof(td->axismtx)); @@ -6937,6 +7060,173 @@ void flushTransMasking(TransInfo *t) } } +typedef struct TransDataPaintCurve { + PaintCurvePoint *pcp; /* initial curve point */ + char id; +} TransDataPaintCurve; + + +#define PC_IS_ANY_SEL(pc) (((pc)->bez.f1 | (pc)->bez.f2 | (pc)->bez.f3) & SELECT) + +static void PaintCurveConvertHandle(PaintCurvePoint *pcp, int id, TransData2D *td2d, TransDataPaintCurve *tdpc, TransData *td) +{ + BezTriple *bezt = &pcp->bez; + copy_v2_v2(td2d->loc, bezt->vec[id]); + td2d->loc[2] = 0.0f; + td2d->loc2d = bezt->vec[id]; + + td->flag = 0; + td->loc = td2d->loc; + copy_v3_v3(td->center, bezt->vec[1]); + copy_v3_v3(td->iloc, td->loc); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext = NULL; + td->val = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdpc->id = id; + tdpc->pcp = pcp; +} + +static void PaintCurvePointToTransData(PaintCurvePoint *pcp, TransData *td, TransData2D *td2d, TransDataPaintCurve *tdpc) +{ + BezTriple *bezt = &pcp->bez; + + if (pcp->bez.f2 == SELECT) { + int i; + for (i = 0; i < 3; i++) { + copy_v2_v2(td2d->loc, bezt->vec[i]); + td2d->loc[2] = 0.0f; + td2d->loc2d = bezt->vec[i]; + + td->flag = 0; + td->loc = td2d->loc; + copy_v3_v3(td->center, bezt->vec[1]); + copy_v3_v3(td->iloc, td->loc); + + memset(td->axismtx, 0, sizeof(td->axismtx)); + td->axismtx[2][2] = 1.0f; + + td->ext = NULL; + td->val = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + unit_m3(td->mtx); + unit_m3(td->smtx); + + tdpc->id = i; + tdpc->pcp = pcp; + + td++; + td2d++; + tdpc++; + } + } + else { + if (bezt->f3 & SELECT) { + PaintCurveConvertHandle(pcp, 2, td2d, tdpc, td); + td2d++; + tdpc++; + td++; + } + + if (bezt->f1 & SELECT) { + PaintCurveConvertHandle(pcp, 0, td2d, tdpc, td); + } + } +} + +static void createTransPaintCurveVerts(bContext *C, TransInfo *t) +{ + Paint *paint = BKE_paint_get_active_from_context(C); + PaintCurve *pc; + PaintCurvePoint *pcp; + Brush *br; + TransData *td = NULL; + TransData2D *td2d = NULL; + TransDataPaintCurve *tdpc = NULL; + int i; + int total = 0; + + t->total = 0; + + if (!paint || !paint->brush || !paint->brush->paint_curve) + return; + + br = paint->brush; + pc = br->paint_curve; + + for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { + if (PC_IS_ANY_SEL(pcp)) { + if (pcp->bez.f2 & SELECT) { + total += 3; + continue; + } + else { + if (pcp->bez.f1 & SELECT) + total++; + if (pcp->bez.f3 & SELECT) + total++; + } + } + } + + if (!total) + return; + + t->total = total; + td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D"); + td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransData"); + tdpc = t->customData = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); + t->flag |= T_FREE_CUSTOMDATA; + + for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { + if (PC_IS_ANY_SEL(pcp)) { + PaintCurvePointToTransData (pcp, td, td2d, tdpc); + + if (pcp->bez.f2 & SELECT) { + td += 3; + td2d += 3; + tdpc += 3; + } + else { + if (pcp->bez.f1 & SELECT) { + td++; + td2d++; + tdpc++; + } + if (pcp->bez.f3 & SELECT) { + td++; + td2d++; + tdpc++; + } + } + } + } +} + + +void flushTransPaintCurve(TransInfo *t) +{ + int i; + TransData2D *td2d = t->data2d; + TransDataPaintCurve *tdpc = (TransDataPaintCurve *)t->customData; + + for (i = 0; i < t->total; i++, tdpc++, td2d++) { + PaintCurvePoint *pcp = tdpc->pcp; + copy_v2_v2(pcp->bez.vec[tdpc->id], td2d->loc); + } +} + + void createTransData(bContext *C, TransInfo *t) { Scene *scene = t->scene; @@ -6969,6 +7259,10 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } + else if (t->options & CTX_PAINT_CURVE) { + if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) + createTransPaintCurveVerts(C, t); + } else if (t->obedit) { createTransUVs(C, t); if (t->data && (t->flag & T_PROP_EDIT)) { @@ -7075,7 +7369,7 @@ void createTransData(bContext *C, TransInfo *t) // XXX active-layer checking isn't done as that should probably be checked through context instead createTransPose(t, ob); } - else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT)) { + else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { /* important that ob_armature can be set even when its not selected [#23412] * lines below just check is also visible */ Object *ob_armature = modifiers_isDeformedByArmature(ob); @@ -7100,12 +7394,11 @@ void createTransData(bContext *C, TransInfo *t) sort_trans_data_dist(t); } } - else if (ob && (ob->mode & (OB_MODE_ALL_PAINT))) { - /* sculpt mode and project paint have own undo stack - * transform ops redo clears sculpt/project undo stack. - * - * Could use 'OB_MODE_ALL_PAINT' since there are key conflicts, - * transform + paint isn't well supported. */ + else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { + if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { + t->flag |= T_POINTS | T_2D_EDIT; + createTransPaintCurveVerts(C, t); + } } else { createTransObject(C, t); diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index f813db04205..2f035949edc 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -38,6 +38,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_lattice_types.h" #include "DNA_screen_types.h" #include "DNA_sequence_types.h" @@ -74,6 +75,7 @@ #include "BKE_lattice.h" #include "BKE_nla.h" #include "BKE_context.h" +#include "BKE_paint.h" #include "BKE_sequencer.h" #include "BKE_editmesh.h" #include "BKE_tracking.h" @@ -98,6 +100,7 @@ #include "WM_api.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "transform.h" @@ -262,7 +265,7 @@ static void animrecord_check_state(Scene *scene, ID *id, wmTimer *animtimer) ScreenAnimData *sad = (animtimer) ? animtimer->customdata : NULL; /* sanity checks */ - if (ELEM3(NULL, scene, id, sad)) + if (ELEM(NULL, scene, id, sad)) return; /* check if we need a new strip if: @@ -363,7 +366,7 @@ static void recalcData_actedit(TransInfo *t) } /* now free temp channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } } /* helper for recalcData() - for Graph Editor transforms */ @@ -423,7 +426,7 @@ static void recalcData_graphedit(TransInfo *t) if (dosort) remake_graph_transdata(t, &anim_data); /* now free temp channels */ - BLI_freelistN(&anim_data); + ANIM_animdata_freelist(&anim_data); } /* helper for recalcData() - for NLA Editor transforms */ @@ -653,6 +656,9 @@ static void recalcData_image(TransInfo *t) if (t->options & CTX_MASK) { recalcData_mask_common(t); } + else if (t->options & CTX_PAINT_CURVE) { + flushTransPaintCurve(t); + } else if (t->obedit && t->obedit->type == OB_MESH) { SpaceImage *sima = t->sa->spacedata.first; @@ -773,7 +779,7 @@ static void recalcData_objects(TransInfo *t) else if (t->obedit->type == OB_ARMATURE) { /* no recalc flag, does pose */ bArmature *arm = t->obedit->data; ListBase *edbo = arm->edbo; - EditBone *ebo; + EditBone *ebo, *ebo_parent; TransData *td = t->data; int i; @@ -783,17 +789,18 @@ static void recalcData_objects(TransInfo *t) /* Ensure all bones are correctly adjusted */ for (ebo = edbo->first; ebo; ebo = ebo->next) { + ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL; - if ((ebo->flag & BONE_CONNECTED) && ebo->parent) { + if (ebo_parent) { /* If this bone has a parent tip that has been moved */ - if (ebo->parent->flag & BONE_TIPSEL) { - copy_v3_v3(ebo->head, ebo->parent->tail); - if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo->parent->rad_tail; + if (ebo_parent->flag & BONE_TIPSEL) { + copy_v3_v3(ebo->head, ebo_parent->tail); + if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo_parent->rad_tail; } /* If this bone has a parent tip that has NOT been moved */ else { - copy_v3_v3(ebo->parent->tail, ebo->head); - if (t->mode == TFM_BONE_ENVELOPE) ebo->parent->rad_tail = ebo->rad_head; + copy_v3_v3(ebo_parent->tail, ebo->head); + if (t->mode == TFM_BONE_ENVELOPE) ebo_parent->rad_tail = ebo->rad_head; } } @@ -817,7 +824,7 @@ static void recalcData_objects(TransInfo *t) } } - if (!ELEM3(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONESIZE)) { + if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONESIZE)) { /* fix roll */ for (i = 0; i < t->total; i++, td++) { if (td->extra) { @@ -847,9 +854,12 @@ static void recalcData_objects(TransInfo *t) } } - if (arm->flag & ARM_MIRROR_EDIT) - transform_armature_mirror_update(t->obedit); - + if (arm->flag & ARM_MIRROR_EDIT) { + if (t->state != TRANS_CANCEL) + transform_armature_mirror_update(t->obedit); + else + restoreBones(t); + } } else { if (t->state != TRANS_CANCEL) { @@ -962,6 +972,9 @@ void recalcData(TransInfo *t) else if (t->options & CTX_EDGE) { recalcData_objects(t); } + else if (t->options & CTX_PAINT_CURVE) { + flushTransPaintCurve(t); + } else if (t->spacetype == SPACE_IMAGE) { recalcData_image(t); } @@ -1070,6 +1083,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve ARegion *ar = CTX_wm_region(C); ScrArea *sa = CTX_wm_area(C); Object *obedit = CTX_data_edit_object(C); + Object *ob = CTX_data_active_object(C); PropertyRNA *prop; t->scene = sce; @@ -1190,11 +1204,18 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* exceptional case */ if (t->around == V3D_LOCAL && (t->settings->selectmode & SCE_SELECT_FACE)) { - if (ELEM3(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { + if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { t->options |= CTX_NO_PET; } } + if (ob && ob->mode & OB_MODE_ALL_PAINT) { + Paint *p = BKE_paint_get_active_from_context(C); + if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { + t->options |= CTX_PAINT_CURVE; + } + } + /* initialize UV transform from */ if (op && ((prop = RNA_struct_find_property(op->ptr, "correct_uv")))) { if (RNA_property_is_set(op->ptr, prop)) { @@ -1223,9 +1244,13 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (sima->mode == SI_MODE_MASK) { t->options |= CTX_MASK; } - else { - /* image not in uv edit, nor in mask mode, can happen for some tools */ + else if (sima->mode == SI_MODE_PAINT) { + Paint *p = &sce->toolsettings->imapaint.paint; + if (p->brush && (p->brush->flag & BRUSH_CURVE)) { + t->options |= CTX_PAINT_CURVE; + } } + /* image not in uv edit, nor in mask mode, can happen for some tools */ } else if (t->spacetype == SPACE_NODE) { // XXX for now, get View2D from the active region @@ -1406,7 +1431,7 @@ void postTrans(bContext *C, TransInfo *t) } if (t->spacetype == SPACE_IMAGE) { - if (t->options & CTX_MASK) { + if (t->options & (CTX_MASK | CTX_PAINT_CURVE)) { /* pass */ } else { @@ -1536,6 +1561,13 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) invert_m3_m3(imat, mat); mul_m3_v3(imat, r_center); } + else if (t->options & CTX_PAINT_CURVE) { + if (ED_view3d_project_float_global(t->ar, cursor, r_center, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { + r_center[0] = t->ar->winx / 2.0f; + r_center[1] = t->ar->winy / 2.0f; + } + r_center[2] = 0.0f; + } } void calculateCenterCursor2D(TransInfo *t, float r_center[2]) @@ -1567,19 +1599,14 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) if (cursor) { if (t->options & CTX_MASK) { float co[2]; - float frame_size[2]; if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; - ED_space_image_get_size_fl(sima, frame_size); - BKE_mask_coord_from_frame(co, cursor, frame_size); - ED_space_image_get_aspect(sima, &aspx, &aspy); + BKE_mask_coord_from_image(sima->image, &sima->iuser, co, cursor); } else if (t->spacetype == SPACE_CLIP) { SpaceClip *space_clip = (SpaceClip *) t->sa->spacedata.first; - ED_space_clip_get_size_fl(space_clip, frame_size); - BKE_mask_coord_from_frame(co, cursor, frame_size); - ED_space_clip_get_aspect(space_clip, &aspx, &aspy); + BKE_mask_coord_from_movieclip(space_clip->clip, &space_clip->user, co, cursor); } else { BLI_assert(!"Shall not happen"); @@ -1588,6 +1615,12 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) r_center[0] = co[0] * aspx; r_center[1] = co[1] * aspy; } + else if (t->options & CTX_PAINT_CURVE) { + if (t->spacetype == SPACE_IMAGE) { + r_center[0] = UI_view2d_view_to_region_x(&t->ar->v2d, cursor[0]); + r_center[1] = UI_view2d_view_to_region_y(&t->ar->v2d, cursor[1]); + } + } else { r_center[0] = cursor[0] * aspx; r_center[1] = cursor[1] * aspy; @@ -1619,8 +1652,9 @@ void calculateCenterMedian(TransInfo *t, float r_center[3]) } } } - if (i) - mul_v3_fl(partial, 1.0f / total); + if (total) { + mul_v3_fl(partial, 1.0f / (float)total); + } copy_v3_v3(r_center, partial); } @@ -1722,6 +1756,14 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) } } } + else if (t->options & CTX_PAINT_CURVE) { + Paint *p = BKE_paint_get_active(t->scene); + Brush *br = p->brush; + PaintCurve *pc = br->paint_curve; + copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]); + r_center[2] = 0.0f; + ok = true; + } else { /* object mode */ Scene *scene = t->scene; diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index 70b565859f3..61b2deabe12 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -64,18 +64,18 @@ static void InputSpring(TransInfo *UNUSED(t), MouseInput *mi, const int mval[2], /* calculate ratio for shiftkey pos, and for total, and blend these for precision */ dx = (float)(mi->center[0] - mi->precision_mval[0]); dy = (float)(mi->center[1] - mi->precision_mval[1]); - ratio = sqrtf(dx * dx + dy * dy); + ratio = hypotf(dx, dy); dx = (float)(mi->center[0] - mval[0]); dy = (float)(mi->center[1] - mval[1]); - precise_ratio = sqrtf(dx * dx + dy * dy); + precise_ratio = hypotf(dx, dy); ratio = (ratio + (precise_ratio - ratio) / 10.0f) / mi->factor; } else { dx = (float)(mi->center[0] - mval[0]); dy = (float)(mi->center[1] - mval[1]); - ratio = sqrtf(dx * dx + dy * dy) / mi->factor; + ratio = hypotf(dx, dy) / mi->factor; } output[0] = ratio; @@ -94,6 +94,12 @@ static void InputSpringFlip(TransInfo *t, MouseInput *mi, const int mval[2], flo } } +static void InputSpringDelta(TransInfo *t, MouseInput *mi, const int mval[2], float output[3]) +{ + InputSpring(t, mi, mval, output); + output[0] -= 1.0f; +} + static void InputTrackBall(TransInfo *UNUSED(t), MouseInput *mi, const int mval[2], float output[3]) { @@ -189,7 +195,7 @@ static void InputCustomRatioFlip(TransInfo *UNUSED(t), MouseInput *mi, const int dx = data[2] - data[0]; dy = data[3] - data[1]; - length = sqrt(dx * dx + dy * dy); + length = hypot(dx, dy); if (mi->precision) { /* deal with Shift key by adding motion / 10 to motion before shift press */ @@ -333,6 +339,11 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) mi->apply = InputSpringFlip; t->helpline = HLP_SPRING; break; + case INPUT_SPRING_DELTA: + calcSpringFactor(mi); + mi->apply = InputSpringDelta; + t->helpline = HLP_SPRING; + break; case INPUT_ANGLE: mi->data = MEM_callocN(sizeof(double), "angle accumulator"); mi->apply = InputAngle; diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index 125975eb32b..37a6d50e149 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -72,6 +72,8 @@ /* local module include */ #include "transform.h" +#include "GPU_select.h" + /* return codes for select, and drawing flags */ #define MAN_TRANS_X (1 << 0) @@ -260,7 +262,7 @@ bool gimbal_axis(Object *ob, float gmat[3][3]) /* centroid, boundbox, of selection */ /* returns total items selected */ -int calc_manipulator_stats(const bContext *C) +static int calc_manipulator_stats(const bContext *C) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); @@ -858,8 +860,8 @@ static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int co /* axes */ if (flagx) { if (is_picksel) { - if (flagx & MAN_SCALE_X) glLoadName(MAN_SCALE_X); - else if (flagx & MAN_TRANS_X) glLoadName(MAN_TRANS_X); + if (flagx & MAN_SCALE_X) GPU_select_load_id(MAN_SCALE_X); + else if (flagx & MAN_TRANS_X) GPU_select_load_id(MAN_TRANS_X); } else { manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); @@ -873,8 +875,8 @@ static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int co case 1: if (flagy) { if (is_picksel) { - if (flagy & MAN_SCALE_Y) glLoadName(MAN_SCALE_Y); - else if (flagy & MAN_TRANS_Y) glLoadName(MAN_TRANS_Y); + if (flagy & MAN_SCALE_Y) GPU_select_load_id(MAN_SCALE_Y); + else if (flagy & MAN_TRANS_Y) GPU_select_load_id(MAN_TRANS_Y); } else { manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); @@ -888,8 +890,8 @@ static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int co case 2: if (flagz) { if (is_picksel) { - if (flagz & MAN_SCALE_Z) glLoadName(MAN_SCALE_Z); - else if (flagz & MAN_TRANS_Z) glLoadName(MAN_TRANS_Z); + if (flagz & MAN_SCALE_Z) GPU_select_load_id(MAN_SCALE_Z); + else if (flagz & MAN_TRANS_Z) GPU_select_load_id(MAN_TRANS_Z); } else { manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); @@ -975,7 +977,7 @@ static void draw_manipulator_rotate( /* Screen aligned trackball rot circle */ if (drawflags & MAN_ROT_T) { - if (is_picksel) glLoadName(MAN_ROT_T); + if (is_picksel) GPU_select_load_id(MAN_ROT_T); else UI_ThemeColor(TH_TRANSFORM); drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); @@ -983,7 +985,7 @@ static void draw_manipulator_rotate( /* Screen aligned view rot circle */ if (drawflags & MAN_ROT_V) { - if (is_picksel) glLoadName(MAN_ROT_V); + if (is_picksel) GPU_select_load_id(MAN_ROT_V); else UI_ThemeColor(TH_TRANSFORM); drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); @@ -1062,7 +1064,7 @@ static void draw_manipulator_rotate( /* Z circle */ if (drawflags & MAN_ROT_Z) { preOrthoFront(ortho, matt, 2); - if (is_picksel) glLoadName(MAN_ROT_Z); + if (is_picksel) GPU_select_load_id(MAN_ROT_Z); else manipulator_setcolor(v3d, 'Z', colcode, 255); drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); postOrtho(ortho); @@ -1070,7 +1072,7 @@ static void draw_manipulator_rotate( /* X circle */ if (drawflags & MAN_ROT_X) { preOrthoFront(ortho, matt, 0); - if (is_picksel) glLoadName(MAN_ROT_X); + if (is_picksel) GPU_select_load_id(MAN_ROT_X); else manipulator_setcolor(v3d, 'X', colcode, 255); glRotatef(90.0, 0.0, 1.0, 0.0); drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); @@ -1080,7 +1082,7 @@ static void draw_manipulator_rotate( /* Y circle */ if (drawflags & MAN_ROT_Y) { preOrthoFront(ortho, matt, 1); - if (is_picksel) glLoadName(MAN_ROT_Y); + if (is_picksel) GPU_select_load_id(MAN_ROT_Y); else manipulator_setcolor(v3d, 'Y', colcode, 255); glRotatef(-90.0, 1.0, 0.0, 0.0); drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); @@ -1097,7 +1099,7 @@ static void draw_manipulator_rotate( /* Z circle */ if (drawflags & MAN_ROT_Z) { preOrthoFront(ortho, rv3d->twmat, 2); - if (is_picksel) glLoadName(MAN_ROT_Z); + if (is_picksel) GPU_select_load_id(MAN_ROT_Z); else manipulator_setcolor(v3d, 'Z', colcode, 255); partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); postOrtho(ortho); @@ -1105,7 +1107,7 @@ static void draw_manipulator_rotate( /* X circle */ if (drawflags & MAN_ROT_X) { preOrthoFront(ortho, rv3d->twmat, 0); - if (is_picksel) glLoadName(MAN_ROT_X); + if (is_picksel) GPU_select_load_id(MAN_ROT_X); else manipulator_setcolor(v3d, 'X', colcode, 255); glRotatef(90.0, 0.0, 1.0, 0.0); partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); @@ -1115,7 +1117,7 @@ static void draw_manipulator_rotate( /* Y circle */ if (drawflags & MAN_ROT_Y) { preOrthoFront(ortho, rv3d->twmat, 1); - if (is_picksel) glLoadName(MAN_ROT_Y); + if (is_picksel) GPU_select_load_id(MAN_ROT_Y); else manipulator_setcolor(v3d, 'Y', colcode, 255); glRotatef(-90.0, 1.0, 0.0, 0.0); partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); @@ -1132,7 +1134,7 @@ static void draw_manipulator_rotate( if (drawflags & MAN_ROT_Z) { preOrthoFront(ortho, rv3d->twmat, 2); glPushMatrix(); - if (is_picksel) glLoadName(MAN_ROT_Z); + if (is_picksel) GPU_select_load_id(MAN_ROT_Z); else manipulator_setcolor(v3d, 'Z', colcode, 255); partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); @@ -1145,7 +1147,7 @@ static void draw_manipulator_rotate( if (drawflags & MAN_ROT_Y) { preOrthoFront(ortho, rv3d->twmat, 1); glPushMatrix(); - if (is_picksel) glLoadName(MAN_ROT_Y); + if (is_picksel) GPU_select_load_id(MAN_ROT_Y); else manipulator_setcolor(v3d, 'Y', colcode, 255); glRotatef(90.0, 1.0, 0.0, 0.0); @@ -1160,7 +1162,7 @@ static void draw_manipulator_rotate( if (drawflags & MAN_ROT_X) { preOrthoFront(ortho, rv3d->twmat, 0); glPushMatrix(); - if (is_picksel) glLoadName(MAN_ROT_X); + if (is_picksel) GPU_select_load_id(MAN_ROT_X); else manipulator_setcolor(v3d, 'X', colcode, 255); glRotatef(-90.0, 0.0, 1.0, 0.0); @@ -1263,7 +1265,7 @@ static void draw_manipulator_scale( int shift = 0; // XXX /* center circle, do not add to selection when shift is pressed (planar constraint) */ - if (is_picksel && shift == 0) glLoadName(MAN_SCALE_C); + if (is_picksel && shift == 0) GPU_select_load_id(MAN_SCALE_C); else manipulator_setcolor(v3d, 'C', colcode, 255); glPushMatrix(); @@ -1304,7 +1306,7 @@ static void draw_manipulator_scale( case 0: /* X cube */ if (drawflags & MAN_SCALE_X) { glTranslatef(dz, 0.0, 0.0); - if (is_picksel) glLoadName(MAN_SCALE_X); + if (is_picksel) GPU_select_load_id(MAN_SCALE_X); else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); drawsolidcube(cusize); glTranslatef(-dz, 0.0, 0.0); @@ -1313,7 +1315,7 @@ static void draw_manipulator_scale( case 1: /* Y cube */ if (drawflags & MAN_SCALE_Y) { glTranslatef(0.0, dz, 0.0); - if (is_picksel) glLoadName(MAN_SCALE_Y); + if (is_picksel) GPU_select_load_id(MAN_SCALE_Y); else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); drawsolidcube(cusize); glTranslatef(0.0, -dz, 0.0); @@ -1322,7 +1324,7 @@ static void draw_manipulator_scale( case 2: /* Z cube */ if (drawflags & MAN_SCALE_Z) { glTranslatef(0.0, 0.0, dz); - if (is_picksel) glLoadName(MAN_SCALE_Z); + if (is_picksel) GPU_select_load_id(MAN_SCALE_Z); else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); drawsolidcube(cusize); glTranslatef(0.0, 0.0, -dz); @@ -1337,7 +1339,7 @@ static void draw_manipulator_scale( if (shift) { glTranslatef(0.0, -dz, 0.0); - glLoadName(MAN_SCALE_C); + GPU_select_load_id(MAN_SCALE_C); glBegin(GL_POINTS); glVertex3f(0.0, 0.0, 0.0); glEnd(); @@ -1399,7 +1401,7 @@ static void draw_manipulator_translate( glDisable(GL_DEPTH_TEST); /* center circle, do not add to selection when shift is pressed (planar constraint) */ - if (is_picksel && shift == 0) glLoadName(MAN_TRANS_C); + if (is_picksel && shift == 0) GPU_select_load_id(MAN_TRANS_C); else manipulator_setcolor(v3d, 'C', colcode, 255); glPushMatrix(); @@ -1412,7 +1414,7 @@ static void draw_manipulator_translate( glMultMatrixf(rv3d->twmat); /* axis */ - glLoadName(-1); + GPU_select_load_id(-1); // translate drawn as last, only axis when no combo with scale, or for ghosting if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) { @@ -1435,7 +1437,7 @@ static void draw_manipulator_translate( case 0: /* Z Cone */ if (drawflags & MAN_TRANS_Z) { glTranslatef(0.0, 0.0, dz); - if (is_picksel) glLoadName(MAN_TRANS_Z); + if (is_picksel) GPU_select_load_id(MAN_TRANS_Z); else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); draw_cone(qobj, cylen, cywid); glTranslatef(0.0, 0.0, -dz); @@ -1444,7 +1446,7 @@ static void draw_manipulator_translate( case 1: /* X Cone */ if (drawflags & MAN_TRANS_X) { glTranslatef(dz, 0.0, 0.0); - if (is_picksel) glLoadName(MAN_TRANS_X); + if (is_picksel) GPU_select_load_id(MAN_TRANS_X); else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); glRotatef(90.0, 0.0, 1.0, 0.0); draw_cone(qobj, cylen, cywid); @@ -1455,7 +1457,7 @@ static void draw_manipulator_translate( case 2: /* Y Cone */ if (drawflags & MAN_TRANS_Y) { glTranslatef(0.0, dz, 0.0); - if (is_picksel) glLoadName(MAN_TRANS_Y); + if (is_picksel) GPU_select_load_id(MAN_TRANS_Y); else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); glRotatef(-90.0, 1.0, 0.0, 0.0); draw_cone(qobj, cylen, cywid); @@ -1503,7 +1505,7 @@ static void draw_manipulator_rotate_cyl( unit_m4(unitmat); - if (is_picksel) glLoadName(MAN_ROT_V); + if (is_picksel) GPU_select_load_id(MAN_ROT_V); UI_ThemeColor(TH_TRANSFORM); drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); @@ -1556,7 +1558,7 @@ static void draw_manipulator_rotate_cyl( case 0: /* X cylinder */ if (drawflags & MAN_ROT_X) { glTranslatef(1.0, 0.0, 0.0); - if (is_picksel) glLoadName(MAN_ROT_X); + if (is_picksel) GPU_select_load_id(MAN_ROT_X); glRotatef(90.0, 0.0, 1.0, 0.0); manipulator_setcolor(v3d, 'X', colcode, 255); draw_cylinder(qobj, cylen, cywid); @@ -1567,7 +1569,7 @@ static void draw_manipulator_rotate_cyl( case 1: /* Y cylinder */ if (drawflags & MAN_ROT_Y) { glTranslatef(0.0, 1.0, 0.0); - if (is_picksel) glLoadName(MAN_ROT_Y); + if (is_picksel) GPU_select_load_id(MAN_ROT_Y); glRotatef(-90.0, 1.0, 0.0, 0.0); manipulator_setcolor(v3d, 'Y', colcode, 255); draw_cylinder(qobj, cylen, cywid); @@ -1578,7 +1580,7 @@ static void draw_manipulator_rotate_cyl( case 2: /* Z cylinder */ if (drawflags & MAN_ROT_Z) { glTranslatef(0.0, 0.0, 1.0); - if (is_picksel) glLoadName(MAN_ROT_Z); + if (is_picksel) GPU_select_load_id(MAN_ROT_Z); manipulator_setcolor(v3d, 'Z', colcode, 255); draw_cylinder(qobj, cylen, cywid); glTranslatef(0.0, 0.0, -1.0); @@ -1690,10 +1692,11 @@ static int manipulator_selectbuf(ScrArea *sa, ARegion *ar, const int mval[2], fl { View3D *v3d = sa->spacedata.first; RegionView3D *rv3d = ar->regiondata; - rctf rect; + rctf rect, selrect; GLuint buffer[64]; // max 4 items per select, so large enuf short hits; const bool is_picksel = true; + const bool do_passes = GPU_select_query_check_active(); /* XXX check a bit later on this... (ton) */ extern void view3d_winmatrix_set(ARegion *ar, View3D *v3d, rctf *rect); @@ -1708,13 +1711,15 @@ static int manipulator_selectbuf(ScrArea *sa, ARegion *ar, const int mval[2], fl rect.ymin = mval[1] - hotspot; rect.ymax = mval[1] + hotspot; + selrect = rect; + view3d_winmatrix_set(ar, v3d, &rect); mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); - glSelectBuffer(64, buffer); - glRenderMode(GL_SELECT); - glInitNames(); /* these two calls whatfor? It doesn't work otherwise */ - glPushName(-2); + if (do_passes) + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_FIRST_PASS, 0); + else + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_ALL, 0); /* do the drawing */ if (v3d->twtype & V3D_MANIP_ROTATE) { @@ -1726,8 +1731,23 @@ static int manipulator_selectbuf(ScrArea *sa, ARegion *ar, const int mval[2], fl if (v3d->twtype & V3D_MANIP_TRANSLATE) draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - glPopName(); - hits = glRenderMode(GL_RENDER); + hits = GPU_select_end(); + + if (do_passes) { + GPU_select_begin(buffer, 64, &selrect, GPU_SELECT_NEAREST_SECOND_PASS, hits); + + /* do the drawing */ + if (v3d->twtype & V3D_MANIP_ROTATE) { + if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); + else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, false, is_picksel); + } + if (v3d->twtype & V3D_MANIP_SCALE) + draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); + if (v3d->twtype & V3D_MANIP_TRANSLATE) + draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); + + GPU_select_end(); + } view3d_winmatrix_set(ar, v3d, NULL); mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat); @@ -1866,11 +1886,14 @@ int BIF_do_manipulator(bContext *C, const struct wmEvent *event, wmOperator *op) else if (drawflags == MAN_ROT_T) { /* trackball need special case, init is different */ /* Do not pass op->ptr!!! trackball has no "constraint" properties! * See [#34621], it's a miracle it did not cause more problems!!! */ - /* However, we need to copy the "release_confirm" property... */ + /* However, we need to copy the "release_confirm" property, but only if defined, see T41112. */ PointerRNA props_ptr; + PropertyRNA *prop; wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_trackball", true); WM_operator_properties_create_ptr(&props_ptr, ot); - RNA_boolean_set(&props_ptr, "release_confirm", RNA_boolean_get(op->ptr, "release_confirm")); + if ((prop = RNA_struct_find_property(op->ptr, "release_confirm")) && RNA_property_is_set(op->ptr, prop)) { + RNA_property_boolean_set(&props_ptr, prop, RNA_property_boolean_get(op->ptr, prop)); + } WM_operator_name_call(C, ot->idname, WM_OP_INVOKE_DEFAULT, &props_ptr); //wm_operator_invoke(C, WM_operatortype_find(ot->idname, 0), event, NULL, NULL, false); WM_operator_properties_free(&props_ptr); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index f27ea4793fe..81e065ee33a 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -26,6 +26,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -49,6 +50,8 @@ #include "UI_resources.h" #include "ED_screen.h" +/* for USE_LOOPSLIDE_HACK only */ +#include "ED_mesh.h" #include "transform.h" @@ -391,6 +394,15 @@ static int transform_modal(bContext *C, wmOperator *op, const wmEvent *event) exit_code = transformEvent(t, event); t->context = NULL; + /* XXX, workaround: active needs to be calculated before transforming, + * since we're not reading from 'td->center' in this case. see: T40241 */ + if (t->tsnap.target == SCE_SNAP_TARGET_ACTIVE) { + /* In camera view, tsnap callback is not set (see initSnappingMode() in transfrom_snap.c, and T40348). */ + if (t->tsnap.targetSnap && ((t->tsnap.status & TARGET_INIT) == 0)) { + t->tsnap.targetSnap(t); + } + } + transformApply(C, t); exit_code |= transformEnd(C, t); @@ -872,6 +884,27 @@ static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) Transform_Properties(ot, P_SNAP); } +static int edge_bevelweight_exec(bContext *C, wmOperator *op) +{ + Mesh *me = (Mesh *)CTX_data_edit_object(C)->data; + + /* auto-enable bevel edge weight drawing, then chain to common transform code */ + me->drawflag |= ME_DRAWBWEIGHTS; + + return transform_exec(C, op); +} + +static int edge_bevelweight_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Mesh *me = (Mesh *)CTX_data_edit_object(C)->data; + + /* auto-enable bevel edge weight drawing, then chain to common transform code */ + me->drawflag |= ME_DRAWBWEIGHTS; + + return transform_invoke(C, op, event); +} + + static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot) { /* identifiers */ @@ -881,8 +914,8 @@ static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; /* api callbacks */ - ot->invoke = transform_invoke; - ot->exec = transform_exec; + ot->invoke = edge_bevelweight_invoke; + ot->exec = edge_bevelweight_exec; ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index ba90926df3b..c7d63da8281 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -509,6 +509,87 @@ void initTransformOrientation(bContext *C, TransInfo *t) } } +/** + * utility function - get first n, selected vert/edge/faces + */ +static unsigned int bm_mesh_elems_select_get_n__internal( + BMesh *bm, BMElem **elems, const unsigned int n, + const BMIterType itype, const char htype) +{ + BMIter iter; + BMElem *ele; + unsigned int i; + + BLI_assert(ELEM(htype, BM_VERT, BM_EDGE, BM_FACE)); + BLI_assert(ELEM(itype, BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH)); + + if (!BLI_listbase_is_empty(&bm->selected)) { + /* quick check */ + BMEditSelection *ese; + i = 0; + for (ese = bm->selected.last; ese; ese = ese->prev) { + /* shouldn't need this check */ + if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) { + + /* only use contiguous selection */ + if (ese->htype != htype) { + i = 0; + break; + } + + elems[i++] = ese->ele; + if (n == i) { + break; + } + } + else { + BLI_assert(0); + } + } + + if (i == 0) { + /* pass */ + } + else { + return i; + } + } + + i = 0; + BM_ITER_MESH (ele, &iter, bm, itype) { + BLI_assert(ele->head.htype == htype); + if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { + elems[i++] = ele; + if (n == i) { + break; + } + } + } + + return i; +} + +static unsigned int bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n) +{ + return bm_mesh_elems_select_get_n__internal( + bm, (BMElem **)elems, min_ii(n, bm->totvertsel), + BM_VERTS_OF_MESH, BM_VERT); +} +static unsigned int bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const unsigned int n) +{ + return bm_mesh_elems_select_get_n__internal( + bm, (BMElem **)elems, min_ii(n, bm->totedgesel), + BM_EDGES_OF_MESH, BM_EDGE); +} +#if 0 +static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n) +{ + return bm_mesh_elems_select_get_n__internal( + bm, (BMElem **)elems, min_ii(n, bm->totfacesel), + BM_FACES_OF_MESH, BM_FACE); +} +#endif + int getTransformOrientation(const bContext *C, float normal[3], float plane[3], const bool activeOnly) { Scene *scene = CTX_data_scene(C); @@ -534,7 +615,6 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], if (ob->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ob); - BMVert *eve; BMEditSelection ese; float vec[3] = {0, 0, 0}; @@ -571,97 +651,78 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], result = ORIENTATION_FACE; } else if (em->bm->totvertsel == 3) { - BMVert *v1 = NULL, *v2 = NULL, *v3 = NULL; - BMIter iter; - - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (v1 == NULL) { - v1 = eve; - } - else if (v2 == NULL) { - v2 = eve; - } - else { - float no_test[3]; + BMVert *v_tri[3]; - float tan_a[3], tan_b[3], tan_c[3]; - float len_a, len_b, len_c; - const float *tan_best; + if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) { + BMEdge *e = NULL; + float no_test[3]; + normal_tri_v3(normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); - v3 = eve; - sub_v3_v3v3(tan_a, v2->co, v1->co); - sub_v3_v3v3(tan_b, v3->co, v2->co); - sub_v3_v3v3(tan_c, v1->co, v3->co); - cross_v3_v3v3(normal, tan_b, tan_a); + /* check if the normal is pointing opposite to vert normals */ + no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0]; + no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1]; + no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2]; + if (dot_v3v3(no_test, normal) < 0.0f) { + negate_v3(normal); + } - /* check if the normal is pointing opposite to vert normals */ - no_test[0] = v1->no[0] + v2->no[0] + v3->no[0]; - no_test[1] = v1->no[1] + v2->no[1] + v3->no[1]; - no_test[2] = v1->no[2] + v2->no[2] + v3->no[2]; - if (dot_v3v3(no_test, normal) < 0.0f) { - negate_v3(normal); + if (em->bm->totedgesel >= 1) { + /* find an edge thats apart of v_tri (no need to search all edges) */ + float e_length; + int j; + + for (j = 0; j < 3; j++) { + BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]); + if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) { + const float e_test_length = BM_edge_calc_length_squared(e_test); + if ((e == NULL) || (e_length < e_test_length)) { + e = e_test; + e_length = e_test_length; + } } - - /* always give the plane to the 2 most distant verts */ - len_a = len_squared_v3(tan_a); - len_b = len_squared_v3(tan_b); - len_c = len_squared_v3(tan_c); - - tan_best = MAX3_PAIR(len_a, len_b, len_c, - tan_a, tan_b, tan_c); - - copy_v3_v3(plane, tan_best); - - break; } } - } - /* if there's an edge available, use that for the tangent */ - if (em->bm->totedgesel >= 1) { - BMEdge *eed = NULL; - - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - sub_v3_v3v3(plane, eed->v2->co, eed->v1->co); - break; + if (e) { + BMVert *v_pair[2]; + if (BM_edge_is_boundary(e)) { + BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]); } + else { + v_pair[0] = e->v1; + v_pair[1] = e->v2; + } + sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co); + } + else { + BM_vert_tri_calc_plane(v_tri, plane); } } + else { + BLI_assert(0); + } result = ORIENTATION_FACE; } else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) { - BMVert *v1 = NULL, *v2 = NULL; - BMIter iter; + BMVert *v_pair[2] = {NULL, NULL}; + BMEdge *eed = NULL; if (em->bm->totedgesel == 1) { - BMEdge *eed = NULL; - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - v1 = eed->v1; - v2 = eed->v2; - } + if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) { + v_pair[0] = eed->v1; + v_pair[1] = eed->v2; } } else { - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (v1 == NULL) { - v1 = eve; - } - else { - v2 = eve; - break; - } - } - } + BLI_assert(em->bm->totvertsel == 2); + bm_mesh_verts_select_get_n(em->bm, v_pair, 2); } /* should never fail */ - if (LIKELY(v1 && v2)) { + if (LIKELY(v_pair[0] && v_pair[1])) { + bool v_pair_swap = false; /* Logic explained: * * - Edges and vert-pairs treated the same way. @@ -672,13 +733,23 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], * which point the Z axis along the normal, however in both cases Z is the dominant axis. */ - /* be deterministic where possible and ensure v1 is active */ - if (BM_mesh_active_vert_get(em->bm) == v2) { - SWAP(BMVert *, v1, v2); + /* be deterministic where possible and ensure v_pair[0] is active */ + if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) { + v_pair_swap = true; + } + else if (eed && BM_edge_is_boundary(eed)) { + /* pradictable direction for boundary edges */ + if (eed->l->v != v_pair[0]) { + v_pair_swap = true; + } + } + + if (v_pair_swap) { + SWAP(BMVert *, v_pair[0], v_pair[1]); } - add_v3_v3v3(plane, v1->no, v2->no); - sub_v3_v3v3(normal, v1->co, v2->co); + add_v3_v3v3(plane, v_pair[0]->no, v_pair[1]->no); + sub_v3_v3v3(normal, v_pair[0]->co, v_pair[1]->co); /* flip the plane normal so we point outwards */ negate_v3(plane); } @@ -686,24 +757,57 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], result = ORIENTATION_EDGE; } else if (em->bm->totvertsel == 1) { - BMIter iter; + BMVert *v = NULL; + + if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) { + copy_v3_v3(normal, v->no); + + if (BM_vert_is_edge_pair(v)) { + bool v_pair_swap = false; + BMEdge *e_pair[2] = {v->e, BM_DISK_EDGE_NEXT(v->e, v)}; + BMVert *v_pair[2] = {BM_edge_other_vert(e_pair[0], v), BM_edge_other_vert(e_pair[1], v)}; + float dir_pair[2][3]; + + if (BM_edge_is_boundary(e_pair[0])) { + if (e_pair[0]->l->v != v) { + v_pair_swap = true; + } + } + else { + if (BM_edge_calc_length_squared(e_pair[0]) < BM_edge_calc_length_squared(e_pair[1])) { + v_pair_swap = true; + } + } - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - copy_v3_v3(normal, eve->no); - break; + if (v_pair_swap) { + SWAP(BMVert *, v_pair[0], v_pair[1]); + } + + sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co); + sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co); + normalize_v3(dir_pair[0]); + normalize_v3(dir_pair[1]); + + add_v3_v3v3(plane, dir_pair[0], dir_pair[1]); } } - result = ORIENTATION_VERT; + + if (is_zero_v3(plane)) { + result = ORIENTATION_VERT; + } + else { + result = ORIENTATION_EDGE; + } } else if (em->bm->totvertsel > 3) { BMIter iter; + BMVert *v; zero_v3(normal); - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - add_v3_v3(normal, eve->no); + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + add_v3_v3(normal, v->no); } } normalize_v3(normal); @@ -756,7 +860,7 @@ int getTransformOrientation(const bContext *C, float normal[3], float plane[3], if (flag) { float tvec[3]; if ((v3d->around == V3D_LOCAL) || - ELEM3(flag, SEL_F2, SEL_F1 | SEL_F3, SEL_F1 | SEL_F2 | SEL_F3)) + ELEM(flag, SEL_F2, SEL_F1 | SEL_F3, SEL_F1 | SEL_F2 | SEL_F3)) { BKE_nurb_bezt_calc_normal(nu, bezt, tvec); add_v3_v3(normal, tvec); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 9afc12a5270..8a3e8f19db4 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -117,8 +117,8 @@ int BIF_snappingSupported(Object *obedit) { int status = 0; - if (obedit == NULL || ELEM5(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) /* only support object mesh, armature, curves */ - { + /* only support object mesh, armature, curves */ + if (obedit == NULL || ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) { status = 1; } @@ -337,6 +337,46 @@ void applyProject(TransInfo *t) mul_m3_v3(td->smtx, tvec); add_v3_v3(td->loc, tvec); + + if (t->tsnap.align && (t->flag & T_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float axis[3]; + float mat[3][3]; + float angle; + float totmat[3][3], smat[3][3]; + float eul[3], fmat[3][3], quat[4]; + float obmat[3][3]; + + /* In pose mode, we want to align normals with Y axis of bones... */ + original_normal = td->axismtx[2]; + + cross_v3_v3v3(axis, original_normal, no); + angle = saacos(dot_v3v3(original_normal, no)); + + axis_angle_to_quat(quat, axis, angle); + + quat_to_mat3(mat, quat); + + mul_m3_m3m3(totmat, mat, td->mtx); + mul_m3_m3m3(smat, td->smtx, totmat); + + /* calculate the total rotatation in eulers */ + add_v3_v3v3(eul, td->ext->irot, td->ext->drot); /* we have to correct for delta rot */ + eulO_to_mat3(obmat, eul, td->ext->rotOrder); + /* mat = transform, obmat = object rotation */ + mul_m3_m3m3(fmat, smat, obmat); + + mat3_to_compatible_eulO(eul, td->ext->rot, td->ext->rotOrder, fmat); + + /* correct back for delta rot */ + sub_v3_v3v3(eul, eul, td->ext->drot); + + /* and apply */ + copy_v3_v3(td->ext->rot, eul); + + /* TODO support constraints for rotation too? see ElementRotation */ + } } } @@ -505,7 +545,7 @@ static void initSnappingMode(TransInfo *t) /* Edit mode */ if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit != NULL && ELEM5(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs + (obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs { /* Exclude editmesh if using proportional edit */ if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) { @@ -588,7 +628,7 @@ void initSnapping(TransInfo *t, wmOperator *op) } /* use scene defaults only when transform is modal */ else if (t->flag & T_MODAL) { - if (ELEM3(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { + if (ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { if (ts->snap_flag & SCE_SNAP) { t->modifiers |= MOD_SNAP; } @@ -658,7 +698,8 @@ static void setSnappingCallback(TransInfo *t) void addSnapPoint(TransInfo *t) { - if (t->tsnap.status & POINT_INIT) { + /* Currently only 3D viewport works for snapping points. */ + if (t->tsnap.status & POINT_INIT && t->spacetype == SPACE_VIEW3D) { TransSnapPoint *p = MEM_callocN(sizeof(TransSnapPoint), "SnapPoint"); t->tsnap.selectedPoint = p; @@ -831,9 +872,9 @@ static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) cross_v3_v3v3(tmp, start, end); if (dot_v3v3(tmp, axis) < 0.0f) - angle = -acos(dot_v3v3(start, end)); + angle = -acosf(dot_v3v3(start, end)); else - angle = acos(dot_v3v3(start, end)); + angle = acosf(dot_v3v3(start, end)); } else { float mtx[3][3]; @@ -843,7 +884,7 @@ static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) mul_m3_v3(mtx, end); mul_m3_v3(mtx, start); - angle = atan2(start[1], start[0]) - atan2(end[1], end[0]); + angle = atan2f(start[1], start[0]) - atan2f(end[1], end[0]); } if (angle > (float)M_PI) { @@ -1489,13 +1530,14 @@ static bool snapCurve(short snap_mode, ARegion *ar, Object *ob, Curve *cu, float static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMesh *dm, BMEditMesh *em, float obmat[4][4], const float ray_start[3], const float ray_normal[3], const float ray_origin[3], - const float mval[2], float r_loc[3], float r_no[3], float *r_dist_px, float *r_depth) + const float mval[2], float r_loc[3], float r_no[3], float *r_dist_px, float *r_depth, bool do_bb) { bool retval = false; + const bool do_ray_start_correction = (snap_mode == SCE_SNAP_MODE_FACE && ar && + !((RegionView3D *)ar->regiondata)->is_persp); int totvert = dm->getNumVerts(dm); if (totvert > 0) { - BoundBox *bb; float imat[4][4]; float timat[3][3]; /* transpose inverse matrix for normals */ float ray_start_local[3], ray_normal_local[3], local_scale, len_diff = TRANSFORM_DIST_MAX_RAY; @@ -1513,9 +1555,33 @@ static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMes /* local scale in normal direction */ local_scale = normalize_v3(ray_normal_local); - bb = BKE_object_boundbox_get(ob); - if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { - return retval; + if (do_bb) { + BoundBox *bb = BKE_object_boundbox_get(ob); + if (!BKE_boundbox_ray_hit_check(bb, ray_start_local, ray_normal_local, &len_diff)) { + return retval; + } + } + else if (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. + */ + BVHTreeFromMesh treeData; + BVHTreeNearest nearest; + len_diff = 0.0f; /* In case BVHTree would fail for some reason... */ + + treeData.em_evil = em; + bvhtree_from_mesh_faces(&treeData, dm, 0.0f, 2, 6); + if (treeData.tree != NULL) { + 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) { + len_diff = sqrtf(nearest.dist_sq); + } + } + free_bvhtree_from_mesh(&treeData); } switch (snap_mode) { @@ -1528,7 +1594,7 @@ static bool snapDerivedMesh(short snap_mode, ARegion *ar, Object *ob, DerivedMes * 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 (ar && !((RegionView3D *)ar->regiondata)->is_persp) { + if (do_ray_start_correction) { float ray_org_local[3]; copy_v3_v3(ray_org_local, ray_origin); @@ -1813,17 +1879,19 @@ static bool snapObject(Scene *scene, short snap_mode, ARegion *ar, Object *ob, f if (ob->type == OB_MESH) { BMEditMesh *em; DerivedMesh *dm; + bool do_bb = true; if (use_obedit) { em = BKE_editmesh_from_object(ob); dm = editbmesh_get_derived_cage(scene, ob, em, CD_MASK_BAREMESH); + do_bb = false; } else { em = NULL; dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH); } - retval = snapDerivedMesh(snap_mode, ar, ob, dm, em, obmat, ray_start, ray_normal, ray_origin, mval, r_loc, r_no, r_dist_px, r_depth); + retval = snapDerivedMesh(snap_mode, ar, ob, dm, em, obmat, ray_start, ray_normal, ray_origin, mval, r_loc, r_no, r_dist_px, r_depth, do_bb); dm->release(dm); } @@ -1968,10 +2036,10 @@ bool snapObjectsRayEx(Scene *scene, Base *base_act, View3D *v3d, ARegion *ar, Ob /******************** PEELING *********************************/ -static int cmpPeel(void *arg1, void *arg2) +static int cmpPeel(const void *arg1, const void *arg2) { - DepthPeel *p1 = arg1; - DepthPeel *p2 = arg2; + const DepthPeel *p1 = arg1; + const DepthPeel *p2 = arg2; int val = 0; if (p1->depth < p2->depth) { @@ -2328,7 +2396,7 @@ bool snapNodesContext(bContext *C, const int mval[2], float *r_dist_px, float r_ /*================================================================*/ -static void applyGridIncrement(TransInfo *t, float *val, int max_index, float fac[3], GearsType action); +static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action); void snapGridIncrementAction(TransInfo *t, float *val, GearsType action) @@ -2361,7 +2429,7 @@ void snapGridIncrement(TransInfo *t, float *val) } -static void applyGridIncrement(TransInfo *t, float *val, int max_index, float fac[3], GearsType action) +static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action) { int i; float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3) @@ -2380,12 +2448,15 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, float fa if (t->options & CTX_MASK) { ED_space_image_get_aspect(t->sa->spacedata.first, asp, asp + 1); } + else if (t->options & CTX_PAINT_CURVE) { + asp[0] = asp[1] = 1.0; + } else { ED_space_image_get_uv_aspect(t->sa->spacedata.first, asp, asp + 1); } } for (i = 0; i <= max_index; i++) { - val[i] = fac[action] * asp[i] * (float)floor(val[i] / (fac[action] * asp[i]) + 0.5f); + val[i] = fac[action] * asp[i] * floorf(val[i] / (fac[action] * asp[i]) + 0.5f); } } diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 3c051586282..f4189a18da4 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenkernel ../../blenlib ../../bmesh + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -67,6 +69,7 @@ set(SRC ../include/ED_node.h ../include/ED_numinput.h ../include/ED_object.h + ../include/ED_paint.h ../include/ED_particle.h ../include/ED_physics.h ../include/ED_render.h @@ -90,7 +93,7 @@ set(SRC ../include/UI_view2d.h ) -add_definitions(-DGLEW_STATIC) +add_definitions(${GL_DEFINITIONS}) if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) diff --git a/source/blender/editors/util/SConscript b/source/blender/editors/util/SConscript index 0876fe48c91..d695a848f4e 100644 --- a/source/blender/editors/util/SConscript +++ b/source/blender/editors/util/SConscript @@ -28,16 +28,19 @@ Import ('env') sources = env.Glob('*.c') -defs = [ 'GLEW_STATIC' ] + +defs = env['BF_GL_DEFINITIONS'] incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '../include', '../../blenfont', '../../blenkernel', '../../blenlib', '../../bmesh', + '../../gpu', '../../makesdna', '../../makesrna', '../../windowmanager', diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index 1fa1e5bdc49..104b628c25a 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -21,7 +21,7 @@ * ***** END GPL LICENSE BLOCK ***** */ -/** \file blender/editors/space_view3d/ed_transverts.c +/** \file blender/editors/util/ed_transverts.c * \ingroup edutil */ @@ -192,7 +192,7 @@ static void set_mapped_co(void *vuserdata, int index, const float co[3], bool ED_transverts_check_obedit(Object *obedit) { - return (ELEM6(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL)); + return (ELEM(obedit->type, OB_ARMATURE, OB_LATTICE, OB_MESH, OB_SURF, OB_CURVE, OB_MBALL)); } void ED_transverts_create_from_obedit(TransVertStore *tvs, Object *obedit, const int mode) diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 2580836cad9..ec0471da8d3 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -41,7 +41,9 @@ #include "DNA_scene_types.h" #include "DNA_packedFile_types.h" -#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_path_util.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -59,7 +61,7 @@ #include "ED_image.h" #include "ED_mesh.h" #include "ED_object.h" -#include "ED_sculpt.h" +#include "ED_paint.h" #include "ED_space_api.h" #include "ED_util.h" @@ -82,14 +84,20 @@ void ED_editors_init(bContext *C) Object *ob, *obact = (sce && sce->basact) ? sce->basact->object : NULL; ID *data; + /* This is called during initialization, so we don't want to store any reports */ + ReportList *reports = CTX_wm_reports(C); + int reports_flag_prev = reports->flag & ~RPT_STORE; + + SWAP(int, reports->flag, reports_flag_prev); + /* toggle on modes for objects that were saved with these enabled. for * e.g. linked objects we have to ensure that they are actually the * active object in this scene. */ for (ob = bmain->object.first; ob; ob = ob->id.next) { int mode = ob->mode; - if (mode && (mode != OB_MODE_POSE)) { - ob->mode = 0; + if (!ELEM(mode, OB_MODE_OBJECT, OB_MODE_POSE)) { + ob->mode = OB_MODE_OBJECT; data = ob->data; if (ob == obact && !ob->id.lib && !(data && data->lib)) @@ -101,6 +109,8 @@ void ED_editors_init(bContext *C) if (sce) { ED_space_image_paint_update(wm, sce->toolsettings); } + + SWAP(int, reports->flag, reports_flag_prev); } /* frees all editmode stuff */ @@ -143,29 +153,39 @@ void ED_editors_exit(bContext *C) /* flush any temp data from object editing to DNA before writing files, * rendering, copying, etc. */ -void ED_editors_flush_edits(const bContext *C, bool for_render) +bool ED_editors_flush_edits(const bContext *C, bool for_render) { - Object *obact = CTX_data_active_object(C); - Object *obedit = CTX_data_edit_object(C); - - /* get editmode results */ - if (obedit) - ED_object_editmode_load(obedit); - - if (obact && (obact->mode & OB_MODE_SCULPT)) { - /* flush multires changes (for sculpt) */ - multires_force_update(obact); + bool has_edited = false; + Object *ob; + Main *bmain = CTX_data_main(C); - if (for_render) { - /* flush changes from dynamic topology sculpt */ - BKE_sculptsession_bm_to_me_for_render(obact); - } - else { - /* Set reorder=false so that saving the file doesn't reorder + /* loop through all data to find edit mode or object mode, because during + * exiting we might not have a context for edit object and multiple sculpt + * objects can exist at the same time */ + for (ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->mode & OB_MODE_SCULPT) { + /* flush multires changes (for sculpt) */ + multires_force_update(ob); + has_edited = true; + + if (for_render) { + /* flush changes from dynamic topology sculpt */ + BKE_sculptsession_bm_to_me_for_render(ob); + } + else { + /* Set reorder=false so that saving the file doesn't reorder * the BMesh's elements */ - BKE_sculptsession_bm_to_me(obact, false); + BKE_sculptsession_bm_to_me(ob, false); + } + } + else if (ob->mode & OB_MODE_EDIT) { + /* get editmode results */ + has_edited = true; + ED_object_editmode_load(ob); } } + + return has_edited; } /* ***** XXX: functions are using old blender names, cleanup later ***** */ diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index ee391af185d..a154f12a786 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -33,6 +33,7 @@ #include "BLI_string_cursor_utf8.h" #include "BKE_context.h" +#include "BKE_scene.h" #include "BKE_unit.h" #include "DNA_scene_types.h" @@ -87,15 +88,18 @@ void initNumInput(NumInput *n) } /* str must be NUM_STR_REP_LEN * (idx_max + 1) length. */ -void outputNumInput(NumInput *n, char *str) +void outputNumInput(NumInput *n, char *str, UnitSettings *unit_settings) { - short i, j; + short j; const int ln = NUM_STR_REP_LEN; int prec = 2; /* draw-only, and avoids too much issues with radian->degrees conversion. */ for (j = 0; j <= n->idx_max; j++) { /* if AFFECTALL and no number typed and cursor not on number, use first number */ - i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j; + const short i = (n->flag & NUM_AFFECT_ALL && n->idx != j && !(n->val_flag[j] & NUM_EDITED)) ? 0 : j; + + /* Use scale_length if needed! */ + const float fac = (float)BKE_scene_unit_scale(unit_settings, n->unit_type[j], 1.0); if (n->val_flag[i] & NUM_EDITED) { /* Get the best precision, allows us to draw '10.0001' as '10' instead! */ @@ -118,7 +122,7 @@ void outputNumInput(NumInput *n, char *str) BLI_strncpy(val, "Invalid", sizeof(val)); } else { - bUnit_AsString(val, sizeof(val), (double)n->val[i], prec, + bUnit_AsString(val, sizeof(val), (double)(n->val[i] * fac), prec, n->unit_sys, n->unit_type[i], true, false); } @@ -186,15 +190,15 @@ bool applyNumInput(NumInput *n, float *vec) if (n->val_flag[i] & NUM_NO_NEGATIVE && val < 0.0f) { val = 0.0f; } - if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) { - val = 0.0001f; - } if (n->val_flag[i] & NUM_NO_FRACTION && val != floorf(val)) { val = floorf(val + 0.5f); if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) { val = 1.0f; } } + else if (n->val_flag[i] & NUM_NO_ZERO && val == 0.0f) { + val = 0.0001f; + } } vec[j] = val; } @@ -256,7 +260,6 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) short idx = n->idx, idx_max = n->idx_max; short dir = STRCUR_DIR_NEXT, mode = STRCUR_JUMP_NONE; int cur; - double val; switch (event->type) { case EVT_MODAL_MAP: @@ -440,6 +443,13 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) } } + /* Up to this point, if we have a ctrl modifier, skip. + * This allows to still access most of modals' shortcuts even in numinput mode. + */ + if (!updated && event->ctrl) { + return false; + } + if ((!utf8_buf || !utf8_buf[0]) && ascii[0]) { /* Fallback to ascii. */ utf8_buf = ascii; @@ -467,16 +477,21 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) /* At this point, our value has changed, try to interpret it with python (if str is not empty!). */ if (n->str[0]) { #ifdef WITH_PYTHON + Scene *sce = CTX_data_scene(C); + double val; char str_unit_convert[NUM_STR_REP_LEN * 6]; /* Should be more than enough! */ const char *default_unit = NULL; + /* Use scale_length if needed! */ + const float fac = (float)BKE_scene_unit_scale(&sce->unit, n->unit_type[idx], 1.0); + /* Make radian default unit when needed. */ if (n->unit_use_radians && n->unit_type[idx] == B_UNIT_ROTATION) default_unit = "r"; BLI_strncpy(str_unit_convert, n->str, sizeof(str_unit_convert)); - bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, 1.0, + bUnit_ReplaceString(str_unit_convert, sizeof(str_unit_convert), default_unit, fac, n->unit_sys, n->unit_type[idx]); /* Note: with angles, we always get values as radians here... */ @@ -489,6 +504,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) } #else /* Very unlikely, but does not harm... */ n->val[idx] = (float)atof(n->str); + (void)C; #endif /* WITH_PYTHON */ if (n->val_flag[idx] & NUM_NEGATE) { diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index a4f2c36f250..189a938e3d8 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -58,7 +58,7 @@ #include "ED_object.h" #include "ED_render.h" #include "ED_screen.h" -#include "ED_sculpt.h" +#include "ED_paint.h" #include "ED_util.h" #include "ED_text.h" @@ -105,6 +105,9 @@ void ED_undo_push(bContext *C, const char *str) PE_undo_push(CTX_data_scene(C), str); } + else if (obact && obact->mode & OB_MODE_SCULPT) { + /* do nothing for now */ + } else { BKE_write_undo(C, str); } @@ -210,6 +213,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) } WM_event_add_notifier(C, NC_WINDOW, NULL); + WM_event_add_notifier(C, NC_WM | ND_UNDO, NULL); if (win) { win->addmousemove = true; @@ -382,7 +386,13 @@ int ED_undo_operator_repeat(bContext *C, struct wmOperator *op) ED_undo_pop_op(C, op); if (op->type->check) { - op->type->check(C, op); /* ignore return value since its running again anyway */ + if (op->type->check(C, op)) { + /* check for popup and re-layout buttons */ + ARegion *ar_menu = CTX_wm_menu(C); + if (ar_menu) { + ED_region_tag_refresh_ui(ar_menu); + } + } } retval = WM_operator_repeat(C, op); @@ -489,7 +499,7 @@ static EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem) name = undo_editmode_get_name(C, i, &active); } else if (undosys == UNDOSYSTEM_IMAPAINT) { - name = ED_undo_paint_get_name(UNDO_PAINT_IMAGE, i, &active); + name = ED_undo_paint_get_name(C, UNDO_PAINT_IMAGE, i, &active); } else { name = BKE_undo_get_name(i, &active); diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt index 45edbde7482..74ba1672485 100644 --- a/source/blender/editors/uvedit/CMakeLists.txt +++ b/source/blender/editors/uvedit/CMakeLists.txt @@ -24,10 +24,12 @@ set(INC ../../blenlib ../../blenfont ../../bmesh + ../../gpu ../../makesdna ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../../../intern/glew-mx ) set(INC_SYS @@ -57,4 +59,6 @@ if(WITH_OPENNL) ) endif() +add_definitions(${GL_DEFINITIONS}) + blender_add_lib(bf_editor_uvedit "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/uvedit/SConscript b/source/blender/editors/uvedit/SConscript index 413503191cd..7153d25188b 100644 --- a/source/blender/editors/uvedit/SConscript +++ b/source/blender/editors/uvedit/SConscript @@ -27,13 +27,14 @@ Import ('env') -defs = [] +defs = env['BF_GL_DEFINITIONS'] sources = env.Glob('*.c') incs = [ '#/intern/guardedalloc', - '#/extern/glew/include', + env['BF_GLEW_INC'], + '#/intern/glew-mx', '#/intern/opennl/extern', '../include', '../../blenfont', diff --git a/source/blender/editors/uvedit/uvedit_buttons.c b/source/blender/editors/uvedit/uvedit_buttons.c index 78e3811a5fc..6816e785c1f 100644 --- a/source/blender/editors/uvedit/uvedit_buttons.c +++ b/source/blender/editors/uvedit/uvedit_buttons.c @@ -135,6 +135,7 @@ static void uvedit_vertex_buttons(const bContext *C, uiBlock *block) BMEditMesh *em; float center[2]; int imx, imy, step, digits; + float width = 8 * UI_UNIT_X; ED_space_image_get_size(sima, &imx, &imy); @@ -158,9 +159,9 @@ static void uvedit_vertex_buttons(const bContext *C, uiBlock *block) } uiBlockBeginAlign(block); - uiDefButF(block, NUM, B_UVEDIT_VERTEX, IFACE_("X:"), 10, 10, 145, 19, &uvedit_old_center[0], + uiDefButF(block, NUM, B_UVEDIT_VERTEX, IFACE_("X:"), 0, 0, width, UI_UNIT_Y, &uvedit_old_center[0], -10 * imx, 10.0 * imx, step, digits, ""); - uiDefButF(block, NUM, B_UVEDIT_VERTEX, IFACE_("Y:"), 165, 10, 145, 19, &uvedit_old_center[1], + uiDefButF(block, NUM, B_UVEDIT_VERTEX, IFACE_("Y:"), width, 0, width, UI_UNIT_Y, &uvedit_old_center[1], -10 * imy, 10.0 * imy, step, digits, ""); uiBlockEndAlign(block); } diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index ce4b97e7108..83fd7e6505d 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -33,6 +33,7 @@ #include <stdlib.h> #include <string.h> +#include "DNA_material_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -42,11 +43,15 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLI_alloca.h" +#include "BLI_buffer.h" +#include "BLI_bitmap.h" #include "BKE_DerivedMesh.h" #include "BKE_editmesh.h" +#include "BKE_material.h" -#include "BLI_buffer.h" +#include "BKE_scene.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -64,6 +69,7 @@ /* use editmesh tessface */ #define USE_EDBM_LOOPTRIS +static void draw_uvs_lineloop_bmface(BMFace *efa, const int cd_loop_uv_offset); void draw_image_cursor(ARegion *ar, const float cursor[2]) { @@ -130,9 +136,7 @@ static void draw_uvs_shadow(Object *obedit) BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MLoopUV *luv; + BMIter iter; const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); @@ -140,12 +144,7 @@ static void draw_uvs_shadow(Object *obedit) UI_ThemeColor(TH_UV_SHADOW); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); } } @@ -354,7 +353,113 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe BLI_buffer_free(&tf_uvorig_buf); } -static void draw_uvs_other(Scene *scene, Object *obedit, Image *curimage) +static void draw_uvs_lineloop_bmface(BMFace *efa, const int cd_loop_uv_offset) +{ + BMIter liter; + BMLoop *l; + MLoopUV *luv; + + glBegin(GL_LINE_LOOP); + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + glVertex2fv(luv->uv); + } + glEnd(); +} + +static void draw_uvs_lineloop_mpoly(Mesh *me, MPoly *mpoly) +{ + MLoopUV *mloopuv; + int i; + + glBegin(GL_LINE_LOOP); + mloopuv = &me->mloopuv[mpoly->loopstart]; + for (i = mpoly->totloop; i != 0; i--, mloopuv++) { + glVertex2fv(mloopuv->uv); + } + glEnd(); +} + +static void draw_uvs_other_mesh_texface(Object *ob, const Image *curimage) +{ + Mesh *me = ob->data; + MPoly *mpoly = me->mpoly; + MTexPoly *mtpoly = me->mtpoly; + int a; + + if (me->mloopuv == NULL) { + return; + } + + for (a = me->totpoly; a != 0; a--, mpoly++, mtpoly++) { + if (mtpoly->tpage != curimage) { + continue; + } + + draw_uvs_lineloop_mpoly(me, mpoly); + } +} +static void draw_uvs_other_mesh_new_shading(Object *ob, const Image *curimage) +{ + Mesh *me = ob->data; + MPoly *mpoly = me->mpoly; + int a; + BLI_bitmap *mat_test_array; + bool ok = false; + int totcol = 0; + + if (me->mloopuv == NULL) { + return; + } + + if (curimage && ob->totcol == 0) { + return; + } + + totcol = max_ii(ob->totcol, 1); + mat_test_array = BLI_BITMAP_NEW_ALLOCA(totcol); + + for (a = 0; a < totcol; a++) { + Image *image; + + /* if no materials, assume a default material with no image */ + if (ob->totcol) + ED_object_get_active_image(ob, a + 1, &image, NULL, NULL, NULL); + else + image = NULL; + + if (image == curimage) { + BLI_BITMAP_ENABLE(mat_test_array, a); + ok = true; + } + } + + if (ok == false) { + return; + } + + for (a = me->totpoly; a != 0; a--, mpoly++) { + const int mat_nr = mpoly->mat_nr; + if ((mat_nr >= totcol) || + (BLI_BITMAP_TEST(mat_test_array, mat_nr)) == 0) + { + continue; + } + + draw_uvs_lineloop_mpoly(me, mpoly); + } +} +static void draw_uvs_other_mesh(Object *ob, const Image *curimage, const bool new_shading_nodes) +{ + if (new_shading_nodes) { + draw_uvs_other_mesh_new_shading(ob, curimage); + } + else { + draw_uvs_other_mesh_texface(ob, curimage); + } +} + +static void draw_uvs_other(Scene *scene, Object *obedit, const Image *curimage, const bool new_shading_nodes) { Base *base; @@ -367,57 +472,47 @@ static void draw_uvs_other(Scene *scene, Object *obedit, Image *curimage) if (!(base->lay & scene->lay)) continue; if (ob->restrictflag & OB_RESTRICT_VIEW) continue; - if ((ob->type == OB_MESH) && (ob != obedit)) { - Mesh *me = ob->data; - - if (me->mtpoly) { - MPoly *mpoly = me->mpoly; - MTexPoly *mtpoly = me->mtpoly; - MLoopUV *mloopuv; - int a, b; - - for (a = me->totpoly; a > 0; a--, mtpoly++, mpoly++) { - if (mtpoly->tpage == curimage) { - glBegin(GL_LINE_LOOP); - - mloopuv = me->mloopuv + mpoly->loopstart; - for (b = 0; b < mpoly->totloop; b++, mloopuv++) { - glVertex2fv(mloopuv->uv); - } - glEnd(); - } - } - } + if ((ob->type == OB_MESH) && (ob != obedit) && ((Mesh *)ob->data)->mloopuv) { + draw_uvs_other_mesh(ob, curimage, new_shading_nodes); } } } static void draw_uvs_texpaint(SpaceImage *sima, Scene *scene, Object *ob) { - Mesh *me = ob->data; + const bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); Image *curimage = ED_space_image(sima); + Mesh *me = ob->data; + Material *ma; - if (sima->flag & SI_DRAW_OTHER) - draw_uvs_other(scene, ob, curimage); + if (sima->flag & SI_DRAW_OTHER) { + draw_uvs_other(scene, ob, curimage, new_shading_nodes); + } UI_ThemeColor(TH_UV_SHADOW); + ma = give_current_material(ob, ob->actcol); + if (me->mtpoly) { MPoly *mpoly = me->mpoly; - MTexPoly *tface = me->mtpoly; - MLoopUV *mloopuv; + MLoopUV *mloopuv, *mloopuv_base; int a, b; + if (!(ma && ma->texpaintslot && ma->texpaintslot[ma->paint_active_slot].uvname && + (mloopuv = CustomData_get_layer_named(&me->ldata, CD_MLOOPUV, ma->texpaintslot[ma->paint_active_slot].uvname)))) + { + mloopuv = me->mloopuv; + } - for (a = me->totpoly; a > 0; a--, tface++, mpoly++) { - if (tface->tpage == curimage) { - glBegin(GL_LINE_LOOP); + mloopuv_base = mloopuv; - mloopuv = me->mloopuv + mpoly->loopstart; - for (b = 0; b < mpoly->totloop; b++, mloopuv++) { - glVertex2fv(mloopuv->uv); - } - glEnd(); + for (a = me->totpoly; a > 0; a--, mpoly++) { + glBegin(GL_LINE_LOOP); + + mloopuv = mloopuv_base + mpoly->loopstart; + for (b = 0; b < mpoly->totloop; b++, mloopuv++) { + glVertex2fv(mloopuv->uv); } + glEnd(); } } } @@ -442,6 +537,7 @@ static void draw_uvs_looptri(BMEditMesh *em, unsigned int *r_loop_index, const i /* draws uv's in the image space */ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) { + const bool new_shading_nodes = BKE_scene_use_new_shading_nodes(scene); ToolSettings *ts; Mesh *me = obedit->data; BMEditMesh *em = me->edit_btmesh; @@ -477,9 +573,21 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) /* draw other uvs */ if (sima->flag & SI_DRAW_OTHER) { - Image *curimage = (activetf) ? activetf->tpage : ima; + Image *curimage; + + if (new_shading_nodes) { + if (efa_act) { + ED_object_get_active_image(obedit, efa_act->mat_nr + 1, &curimage, NULL, NULL, NULL); + } + else { + curimage = ima; + } + } + else { + curimage = (activetf) ? activetf->tpage : ima; + } - draw_uvs_other(scene, obedit, curimage); + draw_uvs_other(scene, obedit, curimage, new_shading_nodes); } /* 1. draw shadow mesh */ @@ -636,22 +744,12 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (tf) { cpack(0x111111); - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); setlinestyle(2); cpack(0x909090); - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); setlinestyle(0); } @@ -666,12 +764,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); } break; case SI_UVDT_OUTLINE: @@ -682,12 +775,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); } glLineWidth(1); @@ -745,12 +833,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; - glBegin(GL_LINE_LOOP); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); - } - glEnd(); + draw_uvs_lineloop_bmface(efa, cd_loop_uv_offset); } } @@ -863,14 +946,30 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) glPointSize(1.0); } + +static void draw_uv_shadows_get(SpaceImage *sima, Object *ob, Object *obedit, bool *show_shadow, bool *show_texpaint) +{ + *show_shadow = *show_texpaint = false; + + if (ED_space_image_show_render(sima) || (sima->flag & SI_NO_DRAW_TEXPAINT)) + return; + + if ((sima->mode == SI_MODE_PAINT) && obedit && obedit->type == OB_MESH) { + struct BMEditMesh *em = BKE_editmesh_from_object(obedit); + + *show_shadow = EDBM_mtexpoly_check(em); + } + + *show_texpaint = (ob && ob->type == OB_MESH && ob->mode == OB_MODE_TEXTURE_PAINT); +} + void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit, Object *obact) { ToolSettings *toolsettings = scene->toolsettings; - int show_uvedit, show_uvshadow, show_texpaint_uvshadow; + bool show_uvedit, show_uvshadow, show_texpaint_uvshadow; - show_texpaint_uvshadow = (obact && obact->type == OB_MESH && obact->mode == OB_MODE_TEXTURE_PAINT); show_uvedit = ED_space_image_show_uvedit(sima, obedit); - show_uvshadow = ED_space_image_show_uvshadow(sima, obedit); + draw_uv_shadows_get(sima, obact, obedit, &show_uvshadow, &show_texpaint_uvshadow); if (show_uvedit || show_uvshadow || show_texpaint_uvshadow) { if (show_uvshadow) diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 5169cc73052..4b341547370 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -137,21 +137,24 @@ static bool is_image_texture_node(bNode *node) } bool ED_object_get_active_image(Object *ob, int mat_nr, - Image **r_ima, ImageUser **r_iuser, bNode **r_node) + Image **r_ima, ImageUser **r_iuser, bNode **r_node, bNodeTree **r_ntree) { Material *ma = give_current_material(ob, mat_nr); - bNode *node = (ma && ma->use_nodes) ? nodeGetActiveTexture(ma->nodetree) : NULL; + bNodeTree *ntree = (ma && ma->use_nodes) ? ma->nodetree : NULL; + bNode *node = (ntree) ? nodeGetActiveTexture(ntree) : NULL; if (node && is_image_texture_node(node)) { if (r_ima) *r_ima = (Image *)node->id; if (r_iuser) *r_iuser = NULL; if (r_node) *r_node = node; + if (r_ntree) *r_ntree = ntree; return true; } if (r_ima) *r_ima = NULL; if (r_iuser) *r_iuser = NULL; if (r_node) *r_node = node; + if (r_ntree) *r_ntree = ntree; return false; } @@ -3026,14 +3029,15 @@ static void UV_OT_circle_select(wmOperatorType *ot) /* properties */ RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX); RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX); - RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX); + RNA_def_int(ot->srna, "radius", 1, 1, INT_MAX, "Radius", "", 1, INT_MAX); RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Gesture Mode", "", INT_MIN, INT_MAX); } /* ******************** lasso select operator **************** */ -static bool do_lasso_select_mesh_uv(bContext *C, const int mcords[][2], short moves, const bool select) +static bool do_lasso_select_mesh_uv(bContext *C, const int mcords[][2], short moves, + const bool select, const bool extend) { SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); @@ -3060,6 +3064,10 @@ static bool do_lasso_select_mesh_uv(bContext *C, const int mcords[][2], short mo BLI_lasso_boundbox(&rect, mcords, moves); + if (!extend && select) { + uv_select_all_perform(scene, ima, em, SEL_DESELECT); + } + if (use_face_center) { /* Face Center Sel */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); @@ -3122,11 +3130,12 @@ static int uv_lasso_select_exec(bContext *C, wmOperator *op) const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot); if (mcords) { - bool select; + bool select, extend; bool changed; select = !RNA_boolean_get(op->ptr, "deselect"); - changed = do_lasso_select_mesh_uv(C, mcords, mcords_tot, select); + extend = RNA_boolean_get(op->ptr, "extend"); + changed = do_lasso_select_mesh_uv(C, mcords, mcords_tot, select, extend); MEM_freeN((void *)mcords); @@ -3827,7 +3836,8 @@ static void UV_OT_reveal(wmOperatorType *ot) static int uv_set_2d_cursor_poll(bContext *C) { return ED_operator_uvedit_space_image(C) || - ED_space_image_maskedit_poll(C); + ED_space_image_maskedit_poll(C) || + ED_space_image_paint_curve(C); } static int uv_set_2d_cursor_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 5f22a201600..79f53e1d971 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -372,7 +372,7 @@ static float p_vec_angle(float *v1, float *v2, float *v3) else if (dot >= 1.0f) return 0.0f; else - return (float)acos(dot); + return acosf(dot); } static float p_vec2_angle(float *v1, float *v2, float *v3) @@ -433,7 +433,7 @@ static float p_edge_length(PEdge *e) d[1] = v2->co[1] - v1->co[1]; d[2] = v2->co[2] - v1->co[2]; - return sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); + return sqrtf(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]); } static float p_edge_uv_length(PEdge *e) @@ -444,7 +444,7 @@ static float p_edge_uv_length(PEdge *e) d[0] = v2->uv[0] - v1->uv[0]; d[1] = v2->uv[1] - v1->uv[1]; - return sqrt(d[0] * d[0] + d[1] * d[1]); + return sqrtf(d[0] * d[0] + d[1] * d[1]); } static void p_chart_uv_bbox(PChart *chart, float minv[2], float maxv[2]) @@ -2353,8 +2353,8 @@ static void p_abf_compute_sines(PAbfSystem *sys) float *sine = sys->sine, *cosine = sys->cosine, *alpha = sys->alpha; for (i = 0; i < sys->nangles; i++, sine++, cosine++, alpha++) { - *sine = sin(*alpha); - *cosine = cos(*alpha); + *sine = sinf(*alpha); + *cosine = cosf(*alpha); } } @@ -3163,9 +3163,9 @@ static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart) SWAP(PVert *, v2, v3); } - sina1 = sin(a1); - sina2 = sin(a2); - sina3 = sin(a3); + sina1 = sinf(a1); + sina2 = sinf(a2); + sina3 = sinf(a3); sinmax = max_fff(sina1, sina2, sina3); @@ -3314,7 +3314,7 @@ static float p_face_stretch(PFace *f) a = dot_v3v3(Ps, Ps); c = dot_v3v3(Pt, Pt); - T = sqrt(0.5f * (a + c)); + T = sqrtf(0.5f * (a + c)); if (f->flag & PFACE_FILLED) T *= 0.2f; @@ -3630,8 +3630,8 @@ static float p_chart_minimum_area_angle(PChart *chart) static void p_chart_rotate_minimum_area(PChart *chart) { float angle = p_chart_minimum_area_angle(chart); - float sine = sin(angle); - float cosine = cos(angle); + float sine = sinf(angle); + float cosine = cosf(angle); PVert *v; for (v = chart->verts; v; v = v->nextlink) { @@ -4045,7 +4045,7 @@ static void p_smooth(PChart *chart) diff[0] = p[0] - oldp[0]; diff[1] = p[1] - oldp[1]; - length = sqrt(diff[0] * diff[0] + diff[1] * diff[1]); + length = len_v2(diff); d = max_ff(d, length); moved += length; } @@ -4559,7 +4559,7 @@ void param_pack(ParamHandle *handle, float margin, bool do_rotate) box->index = i; /* warning this index skips PCHART_NOPACK boxes */ if (margin > 0.0f) - area += sqrt(box->w * box->h); + area += sqrtf(box->w * box->h); } if (margin > 0.0f) { @@ -4661,7 +4661,7 @@ void param_average(ParamHandle *handle) /* Move center to 0,0 */ p_chart_uv_translate(chart, trans); - p_chart_uv_scale(chart, sqrt(fac / tot_fac)); + p_chart_uv_scale(chart, sqrtf(fac / tot_fac)); /* Move to original center */ trans[0] = -trans[0]; diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index ba4b65f4ed4..fcd5267fd44 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1360,7 +1360,7 @@ static unsigned int uv_edge_hash(const void *key) BLI_ghashutil_uinthash(edge->uv1); } -static int uv_edge_compare(const void *a, const void *b) +static bool uv_edge_compare(const void *a, const void *b) { UvEdge *edge1 = (UvEdge *)a; UvEdge *edge2 = (UvEdge *)b; diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 21e7bb00204..335d8e6589e 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -201,7 +201,7 @@ void uvedit_get_aspect(Scene *scene, Object *ob, BMEditMesh *em, float *aspx, fl if (efa) { if (BKE_scene_use_new_shading_nodes(scene)) { - ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL); + ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL); } else { MTexPoly *tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); @@ -938,7 +938,7 @@ static void uv_map_rotation_matrix(float result[4][4], RegionView3D *rv3d, Objec rotup[0][0] = 1.0f / radius; /* calculate transforms*/ - mul_serie_m4(result, rotup, rotside, viewmatrix, rotobj, NULL, NULL, NULL, NULL); + mul_m4_series(result, rotup, rotside, viewmatrix, rotobj); } static void uv_map_transform(bContext *C, wmOperator *op, float center[3], float rotmat[4][4]) |