diff options
Diffstat (limited to 'source/blender/editors')
294 files changed, 18291 insertions, 9867 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 37c40052275..ebd05d8b16b 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -110,7 +110,7 @@ static void acf_generic_root_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUS /* backdrop for top-level widgets (Scene and Object only) */ static void acf_generic_root_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; short expanded = ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_EXPAND) != 0; short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; @@ -136,7 +136,7 @@ static void acf_generic_dataexpand_color(bAnimContext *UNUSED(ac), bAnimListElem /* backdrop for data expanders under top-level Scene/Object */ static void acf_generic_dataexpand_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; float color[3]; @@ -177,7 +177,7 @@ static bool acf_show_channel_colors(bAnimContext *ac) /* get backdrop color for generic channels */ static void acf_generic_channel_color(bAnimContext *ac, bAnimListElem *ale, float r_color[3]) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); bActionGroup *grp = NULL; short indent = (acf->get_indent_level) ? acf->get_indent_level(ac, ale) : 0; bool showGroupColors = acf_show_channel_colors(ac); @@ -217,7 +217,7 @@ static void acf_generic_channel_color(bAnimContext *ac, bAnimListElem *ale, floa /* backdrop for generic channels */ static void acf_generic_channel_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; float color[3]; @@ -269,7 +269,7 @@ static short acf_generic_indention_flexible(bAnimContext *UNUSED(ac), bAnimListE /* basic offset for channels derived from indention */ static short acf_generic_basic_offset(bAnimContext *ac, bAnimListElem *ale) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); if (acf && acf->get_indent_level) return acf->get_indent_level(ac, ale) * INDENT_STEP_SIZE; @@ -409,7 +409,7 @@ static void acf_summary_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(al /* backdrop for summary widget */ static void acf_summary_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; float color[3]; @@ -790,7 +790,7 @@ static void acf_group_color(bAnimContext *ac, bAnimListElem *ale, float r_color[ /* backdrop for group widget */ static void acf_group_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; short expanded = ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_EXPAND) != 0; short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; @@ -1017,6 +1017,149 @@ static bAnimChannelType ACF_FCURVE = acf_fcurve_setting_ptr /* pointer for setting */ }; +/* NLA Control FCurves Expander ----------------------- */ + +/* get backdrop color for nla controls widget */ +static void acf_nla_controls_color(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), float r_color[3]) +{ + // TODO: give this its own theme setting? + UI_GetThemeColorShade3fv(TH_GROUP, 55, r_color); +} + +/* backdrop for nla controls expander widget */ +static void acf_nla_controls_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) +{ + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + View2D *v2d = &ac->ar->v2d; + short expanded = ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_EXPAND) != 0; + short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; + float color[3]; + + /* set backdrop drawing color */ + acf->get_backdrop_color(ac, ale, color); + glColor3fv(color); + + /* rounded corners on LHS only - top only when expanded, but bottom too when collapsed */ + UI_draw_roundbox_corner_set(expanded ? UI_CNR_TOP_LEFT : (UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT)); + UI_draw_roundbox_gl_mode(GL_POLYGON, offset, yminc, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymaxc, 5); +} + +/* name for nla controls expander entries */ +static void acf_nla_controls_name(bAnimListElem *UNUSED(ale), char *name) +{ + BLI_strncpy(name, IFACE_("NLA Strip Controls"), ANIM_CHAN_NAME_SIZE); +} + +/* check if some setting exists for this channel */ +static bool acf_nla_controls_setting_valid(bAnimContext *UNUSED(ac), bAnimListElem *UNUSED(ale), eAnimChannel_Settings setting) +{ + /* for now, all settings are supported, though some are only conditionally */ + switch (setting) { + /* supported */ + case ACHANNEL_SETTING_EXPAND: + return true; + + // TOOD: selected? + + default: /* unsupported */ + return false; + } +} + +/* get the appropriate flag(s) for the setting when it is valid */ +static int acf_nla_controls_setting_flag(bAnimContext *UNUSED(ac), eAnimChannel_Settings setting, bool *neg) +{ + /* clear extra return data first */ + *neg = false; + + switch (setting) { + case ACHANNEL_SETTING_EXPAND: /* expanded */ + *neg = true; + return ADT_NLA_SKEYS_COLLAPSED; + + default: + /* this shouldn't happen */ + return 0; + } +} + +/* get pointer to the setting */ +static void *acf_nla_controls_setting_ptr(bAnimListElem *ale, eAnimChannel_Settings UNUSED(setting), short *type) +{ + AnimData *adt = (AnimData *)ale->data; + + /* all flags are just in adt->flag for now... */ + return GET_ACF_FLAG_PTR(adt->flag, type); +} + +static int acf_nla_controls_icon(bAnimListElem *UNUSED(ale)) +{ + return ICON_NLA; +} + +/* NLA Control FCurves Expander type define */ +static bAnimChannelType ACF_NLACONTROLS = +{ + "NLA Controls Expander", /* type name */ + ACHANNEL_ROLE_EXPANDER, /* role */ + + acf_nla_controls_color, /* backdrop color */ + acf_nla_controls_backdrop, /* backdrop */ + acf_generic_indention_0, /* indent level */ + acf_generic_group_offset, /* offset */ + + acf_nla_controls_name, /* name */ + NULL, /* name prop */ + acf_nla_controls_icon, /* icon */ + + acf_nla_controls_setting_valid, /* has setting */ + acf_nla_controls_setting_flag, /* flag for setting */ + acf_nla_controls_setting_ptr /* pointer for setting */ +}; + + +/* NLA Control F-Curve -------------------------------- */ + +/* name for nla control fcurve entries */ +static void acf_nla_curve_name(bAnimListElem *ale, char *name) +{ + NlaStrip *strip = ale->owner; + FCurve *fcu = ale->data; + PropertyRNA *prop; + + /* try to get RNA property that this shortened path (relative to the strip) refers to */ + prop = RNA_struct_type_find_property(&RNA_NlaStrip, fcu->rna_path); + if (prop) { + /* "name" of this strip displays the UI identifier + the name of the NlaStrip */ + BLI_snprintf(name, 256, "%s (%s)", RNA_property_ui_name(prop), strip->name); + } + else { + /* unknown property... */ + BLI_snprintf(name, 256, "%s[%d]", fcu->rna_path, fcu->array_index); + } +} + + +/* NLA Control F-Curve type define */ +static bAnimChannelType ACF_NLACURVE = +{ + "NLA Control F-Curve", /* type name */ + ACHANNEL_ROLE_CHANNEL, /* role */ + + acf_generic_channel_color, /* backdrop color */ + acf_generic_channel_backdrop, /* backdrop */ + acf_generic_indention_1, /* indent level */ + acf_generic_group_offset, /* offset */ + + acf_nla_curve_name, /* name */ + acf_fcurve_name_prop, /* name prop */ + NULL, /* icon */ + + acf_fcurve_setting_valid, /* has setting */ + acf_fcurve_setting_flag, /* flag for setting */ + acf_fcurve_setting_ptr /* pointer for setting */ +}; + /* Object Action Expander ------------------------------------------- */ // TODO: just get this from RNA? @@ -3063,7 +3206,7 @@ static void acf_nlaaction_color(bAnimContext *UNUSED(ac), bAnimListElem *ale, fl /* backdrop for nla action channel */ static void acf_nlaaction_backdrop(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; AnimData *adt = ale->adt; short offset = (acf->get_offset) ? acf->get_offset(ac, ale) : 0; @@ -3222,6 +3365,9 @@ static void ANIM_init_channel_typeinfo_data(void) animchannelTypeInfo[type++] = &ACF_GROUP; /* Group */ animchannelTypeInfo[type++] = &ACF_FCURVE; /* F-Curve */ + animchannelTypeInfo[type++] = &ACF_NLACONTROLS; /* NLA Control FCurve Expander */ + animchannelTypeInfo[type++] = &ACF_NLACURVE; /* NLA Control FCurve Channel */ + animchannelTypeInfo[type++] = &ACF_FILLACTD; /* Object Action Expander */ animchannelTypeInfo[type++] = &ACF_FILLDRIVERS; /* Drivers Expander */ @@ -3256,7 +3402,7 @@ static void ANIM_init_channel_typeinfo_data(void) } /* Get type info from given channel type */ -bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale) +const bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale) { /* santiy checks */ if (ale == NULL) @@ -3277,7 +3423,7 @@ bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale) /* Print debug info string for the given channel */ void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); /* print indents */ for (; indent_level > 0; indent_level--) @@ -3309,7 +3455,7 @@ void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level) */ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); /* 1) check that the setting exists for the current context */ if ((acf) && (!acf->has_setting || acf->has_setting(ac, ale, setting))) { @@ -3382,7 +3528,7 @@ short ANIM_channel_setting_get(bAnimContext *ac, bAnimListElem *ale, eAnimChanne */ void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel_Settings setting, eAnimChannels_SetFlag mode) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); /* 1) check that the setting exists for the current context */ if ((acf) && (!acf->has_setting || acf->has_setting(ac, ale, setting))) { @@ -3430,10 +3576,26 @@ void ANIM_channel_setting_set(bAnimContext *ac, bAnimListElem *ale, eAnimChannel // width of rename textboxes #define RENAME_TEXT_WIDTH (5 * U.widget_unit) + +/* Helper - Check if a channel needs renaming */ +static bool achannel_is_being_renamed(const bAnimContext *ac, const bAnimChannelType *acf, size_t channel_index) +{ + if (acf->name_prop && ac->ads) { + /* if rename index matches, this channel is being renamed */ + if (ac->ads->renameIndex == channel_index + 1) { + return true; + } + } + + /* not being renamed */ + return false; +} + + /* Draw the given channel */ -void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc) +void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; short selected, offset; float y, ymid, ytext; @@ -3491,7 +3653,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float if (ac->sl) { if ((ac->spacetype == SPACE_IPO) && acf->has_setting(ac, ale, ACHANNEL_SETTING_VISIBLE)) { /* for F-Curves, draw color-preview of curve behind checkbox */ - if (ale->type == ANIMTYPE_FCURVE) { + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { FCurve *fcu = (FCurve *)ale->data; /* F-Curve channels need to have a special 'color code' box drawn, which is colored with whatever @@ -3518,8 +3680,8 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float } /* step 5) draw name ............................................... */ - /* TODO: when renaming, we might not want to draw this, especially if name happens to be longer than channel */ - if (acf->name) { + /* Don't draw this if renaming... */ + if (acf->name && !achannel_is_being_renamed(ac, acf, channel_index)) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; char name[ANIM_CHAN_NAME_SIZE]; /* hopefully this will be enough! */ @@ -3537,7 +3699,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float UI_fontstyle_draw_simple(fstyle, offset, ytext, name); /* draw red underline if channel is disabled */ - if ((ale->type == ANIMTYPE_FCURVE) && (ale->flag & FCURVE_DISABLED)) { + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE) && (ale->flag & FCURVE_DISABLED)) { /* FIXME: replace hardcoded color here, and check on extents! */ glColor3f(1.0f, 0.0f, 0.0f); glLineWidth(2.0); @@ -3604,7 +3766,7 @@ void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float * (only only F-Curves really can support them for now) * - slider should start before the toggles (if they're visible) to keep a clean line down the side */ - if ((draw_sliders) && ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_SHAPEKEY)) { + if ((draw_sliders) && ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE, ANIMTYPE_SHAPEKEY)) { /* adjust offset */ offset += SLIDER_WIDTH; } @@ -3771,8 +3933,46 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi MEM_freeN(rna_path); } +/* callback for NLA Control Curve widget sliders - insert keyframes */ +static void achannel_setting_slider_nla_curve_cb(bContext *C, void *UNUSED(id_poin), void *fcu_poin) +{ + /* ID *id = (ID *)id_poin; */ + FCurve *fcu = (FCurve *)fcu_poin; + + PointerRNA ptr; + PropertyRNA *prop; + int index; + + ReportList *reports = CTX_wm_reports(C); + Scene *scene = CTX_data_scene(C); + short flag = 0; + bool done = false; + float cfra; + + /* get current frame - *no* NLA mapping should be done */ + cfra = (float)CFRA; + + /* get flags for keyframing */ + flag = ANIM_get_keyframing_flags(scene, 1); + + /* get pointer and property from the slider - this should all match up with the NlaStrip required... */ + UI_context_active_but_prop_get(C, &ptr, &prop, &index); + + if (fcu && prop) { + /* set the special 'replace' flag if on a keyframe */ + if (fcurve_frame_has_keyframe(fcu, cfra, 0)) + flag |= INSERTKEY_REPLACE; + + /* insert a keyframe for this F-Curve */ + done = insert_keyframe_direct(reports, ptr, prop, fcu, cfra, flag); + + if (done) + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } +} + /* Draw a widget for some setting */ -static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChannelType *acf, +static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, const bAnimChannelType *acf, uiBlock *block, int xpos, int ypos, int setting) { short ptrsize, butType; @@ -3793,7 +3993,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann //icon = ((enabled) ? ICON_VISIBLE_IPO_ON : ICON_VISIBLE_IPO_OFF); icon = ICON_VISIBLE_IPO_OFF; - if (ale->type == ANIMTYPE_FCURVE) + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) tooltip = TIP_("F-Curve is visible in Graph Editor for editing"); else tooltip = TIP_("Channels are visible in Graph Editor for editing"); @@ -3828,7 +4028,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann //icon = ((enabled) ? ICON_MUTE_IPO_ON : ICON_MUTE_IPO_OFF); icon = ICON_MUTE_IPO_OFF; - if (ale->type == ANIMTYPE_FCURVE) { + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { tooltip = TIP_("Does F-Curve contribute to result"); } else if ((ac) && (ac->spacetype == SPACE_NLA) && (ale->type != ANIMTYPE_NLATRACK)) { @@ -3912,7 +4112,7 @@ static void draw_setting_widget(bAnimContext *ac, bAnimListElem *ale, bAnimChann /* Draw UI widgets the given channel */ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListElem *ale, uiBlock *block, float yminc, float ymaxc, size_t channel_index) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; float y, ymid /*, ytext*/; short offset; @@ -3991,36 +4191,32 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle } /* step 4) draw text - check if renaming widget is in use... */ - if (acf->name_prop && ac->ads) { - float channel_height = ymaxc - yminc; + if (achannel_is_being_renamed(ac, acf, channel_index)) { + PointerRNA ptr = {{NULL}}; + PropertyRNA *prop = NULL; - /* if rename index matches, add widget for this */ - if (ac->ads->renameIndex == channel_index + 1) { - PointerRNA ptr = {{NULL}}; - PropertyRNA *prop = NULL; + /* draw renaming widget if we can get RNA pointer for it + * NOTE: property may only be available in some cases, even if we have + * a callback available (e.g. broken F-Curve rename) + */ + if (acf->name_prop(ale, &ptr, &prop)) { + const float channel_height = ymaxc - yminc; + uiBut *but; - /* draw renaming widget if we can get RNA pointer for it - * NOTE: property may only be available in some cases, even if we have - * a callback available (e.g. broken F-Curve rename) - */ - if (acf->name_prop(ale, &ptr, &prop)) { - uiBut *but; - - UI_block_emboss_set(block, UI_EMBOSS); - - but = uiDefButR(block, UI_BTYPE_TEXT, 1, "", offset + 3, yminc, RENAME_TEXT_WIDTH, channel_height, - &ptr, RNA_property_identifier(prop), -1, 0, 0, -1, -1, NULL); - - /* copy what outliner does here, see outliner_buttons */ - if (UI_but_active_only(C, ac->ar, block, but) == false) { - ac->ads->renameIndex = 0; - - /* send notifiers */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_RENAME, NULL); - } + UI_block_emboss_set(block, UI_EMBOSS); + + but = uiDefButR(block, UI_BTYPE_TEXT, 1, "", offset + 3, yminc, RENAME_TEXT_WIDTH, channel_height, + &ptr, RNA_property_identifier(prop), -1, 0, 0, -1, -1, NULL); + + /* copy what outliner does here, see outliner_buttons */ + if (UI_but_active_only(C, ac->ar, block, but) == false) { + ac->ads->renameIndex = 0; - UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* send notifiers */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_RENAME, NULL); } + + UI_block_emboss_set(block, UI_EMBOSS_NONE); } } @@ -4097,7 +4293,7 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle * and wouldn't be able to auto-keyframe... * - slider should start before the toggles (if they're visible) to keep a clean line down the side */ - if ((draw_sliders) && ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_SHAPEKEY)) { + if ((draw_sliders) && ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE, ANIMTYPE_SHAPEKEY)) { /* adjust offset */ // TODO: make slider width dynamic, so that they can be easier to use when the view is wide enough offset += SLIDER_WIDTH; @@ -4105,7 +4301,28 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle /* need backdrop behind sliders... */ UI_block_emboss_set(block, UI_EMBOSS); - if (ale->id) { /* Slider using RNA Access -------------------- */ + if (ale->owner) { /* Slider using custom RNA Access ---------- */ + if (ale->type == ANIMTYPE_NLACURVE) { + NlaStrip *strip = (NlaStrip *)ale->owner; + FCurve *fcu = (FCurve *)ale->data; + PointerRNA ptr; + PropertyRNA *prop; + + /* create RNA pointers */ + RNA_pointer_create(ale->id, &RNA_NlaStrip, strip, &ptr); + prop = RNA_struct_find_property(&ptr, fcu->rna_path); + + /* create property slider */ + if (prop) { + uiBut *but; + + /* create the slider button, and assign relevant callback to ensure keyframes are inserted... */ + but = uiDefAutoButR(block, &ptr, prop, fcu->array_index, "", ICON_NONE, (int)v2d->cur.xmax - offset, ymid, SLIDER_WIDTH, (int)ymaxc - yminc); + UI_but_func_set(but, achannel_setting_slider_nla_curve_cb, ale->id, ale->data); + } + } + } + else if (ale->id) { /* Slider using RNA Access --------------- */ PointerRNA id_ptr, ptr; PropertyRNA *prop; char *rna_path = NULL; diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index d8ad6506186..f9e3c8a9fa6 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -34,6 +34,7 @@ #include "MEM_guardedalloc.h" +#include "BKE_depsgraph.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" @@ -101,6 +102,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat break; } case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: { FCurve *fcu = (FCurve *)ale->data; @@ -157,6 +159,7 @@ void ANIM_set_active_channel(bAnimContext *ac, void *data, eAnimCont_Types datat break; } case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: { FCurve *fcu = (FCurve *)channel_data; fcu->flag |= FCURVE_ACTIVE; @@ -255,6 +258,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d sel = ACHANNEL_SETFLAG_CLEAR; break; case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: if (ale->flag & FCURVE_SELECTED) sel = ACHANNEL_SETFLAG_CLEAR; break; @@ -339,6 +343,7 @@ void ANIM_deselect_anim_channels(bAnimContext *ac, void *data, eAnimCont_Types d break; } case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: { FCurve *fcu = (FCurve *)ale->data; @@ -444,7 +449,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn return; } else { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale_setting); if (acf == NULL) { printf("ERROR: no channel info for the changed channel\n"); @@ -473,7 +478,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn { /* go backwards in the list, until the highest-ranking element (by indention has been covered) */ for (ale = match->prev; ale; ale = ale->prev) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); int level; /* if no channel info was found, skip, since this type might not have any useful info */ @@ -517,7 +522,7 @@ void ANIM_flush_setting_anim_channels(bAnimContext *ac, ListBase *anim_data, bAn { /* go forwards in the list, until the lowest-ranking element (by indention has been covered) */ for (ale = match->next; ale; ale = ale->next) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); int level; /* if no channel info was found, skip, since this type might not have any useful info */ @@ -847,6 +852,7 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr break; } case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: { FCurve *fcu = (FCurve *)channel; @@ -868,7 +874,7 @@ static void rearrange_animchannel_add_to_islands(ListBase *islands, ListBase *sr break; } default: - printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %d\n", type); + printf("rearrange_animchannel_add_to_islands(): don't know how to handle channels of type %u\n", type); return; } @@ -1191,6 +1197,40 @@ static void rearrange_action_channels(bAnimContext *ac, bAction *act, eRearrange /* ------------------- */ +static void rearrange_nla_control_channels(bAnimContext *ac, AnimData *adt, eRearrangeAnimChan_Mode mode) +{ + ListBase anim_data_visible = {NULL, NULL}; + + NlaTrack *nlt; + NlaStrip *strip; + + /* get rearranging function */ + AnimChanRearrangeFp rearrange_func = rearrange_get_mode_func(mode); + + if (rearrange_func == NULL) + return; + + /* skip if these curves aren't being shown */ + if (adt->flag & ADT_NLA_SKEYS_COLLAPSED) + return; + + /* Filter visible data. */ + rearrange_animchannels_filter_visible(&anim_data_visible, ac, ANIMTYPE_NLACURVE); + + /* we cannot rearrange between strips, but within each strip, we can rearrange those curves */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + for (strip = nlt->strips.first; strip; strip = strip->next) { + rearrange_animchannel_islands(&strip->fcurves, rearrange_func, mode, ANIMTYPE_NLACURVE, + &anim_data_visible); + } + } + + /* free temp data */ + BLI_freelistN(&anim_data_visible); +} + +/* ------------------- */ + static void rearrange_gpencil_channels(bAnimContext *ac, eRearrangeAnimChan_Mode mode) { ListBase anim_data = {NULL, NULL}; @@ -1278,13 +1318,29 @@ static int animchannels_rearrange_exec(bContext *C, wmOperator *op) rearrange_driver_channels(&ac, adt, mode); break; + case ANIMCONT_ACTION: /* Single Action only... */ case ANIMCONT_SHAPEKEY: // DOUBLE CHECK ME... - default: /* some collection of actions */ + { if (adt->action) rearrange_action_channels(&ac, adt->action, mode); else if (G.debug & G_DEBUG) printf("Animdata has no action\n"); break; + } + + default: /* DopeSheet/Graph Editor - Some Actions + NLA Control Curves */ + { + /* NLA Control Curves */ + if (adt->nla_tracks.first) + rearrange_nla_control_channels(&ac, adt, mode); + + /* Action */ + if (adt->action) + rearrange_action_channels(&ac, adt->action, mode); + else if (G.debug & G_DEBUG) + printf("Animdata has no action\n"); + break; + } } } @@ -1602,6 +1658,27 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) ANIM_fcurve_delete_from_animdata(&ac, adt, fcu); break; } + case ANIMTYPE_NLACURVE: + { + /* NLA Control Curve - Deleting it should disable the corresponding setting... */ + NlaStrip *strip = (NlaStrip *)ale->owner; + FCurve *fcu = (FCurve *)ale->data; + + if (STREQ(fcu->rna_path, "strip_time")) { + strip->flag &= ~NLASTRIP_FLAG_USR_TIME; + } + else if (STREQ(fcu->rna_path, "influence")) { + strip->flag &= ~NLASTRIP_FLAG_USR_INFLUENCE; + } + else { + printf("ERROR: Trying to delete NLA Control Curve for unknown property '%s'\n", fcu->rna_path); + } + + /* unlink and free the F-Curve */ + BLI_remlink(&strip->fcurves, fcu); + free_fcurve(fcu); + break; + } case ANIMTYPE_GPLAYER: { /* Grease Pencil layer */ @@ -1631,7 +1708,8 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) /* send notifier that things have changed */ WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); - + DAG_relations_tag_update(CTX_data_main(C)); + return OPERATOR_FINISHED; } @@ -2041,7 +2119,7 @@ static int animchannels_clean_empty_exec(bContext *C, wmOperator *UNUSED(op)) /* remove AnimData? */ if (action_empty && nla_empty && drivers_empty) { - BKE_free_animdata(id); + BKE_animdata_free(id); } } @@ -2420,12 +2498,13 @@ static void ANIM_OT_channels_select_border(wmOperatorType *ot) /* ******************* Rename Operator ***************************** */ /* Allow renaming some channels by clicking on them */ -static void rename_anim_channels(bAnimContext *ac, int channel_index) +static bool rename_anim_channels(bAnimContext *ac, int channel_index) { ListBase anim_data = {NULL, NULL}; - bAnimChannelType *acf; + const bAnimChannelType *acf; bAnimListElem *ale; int filter; + bool success = false; /* get the channel that was clicked on */ /* filter channels */ @@ -2440,7 +2519,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index) printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index); ANIM_animdata_freelist(&anim_data); - return; + return false; } /* check that channel can be renamed */ @@ -2460,6 +2539,7 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index) */ if (ac->ads) { ac->ads->renameIndex = channel_index + 1; + success = true; } } } @@ -2467,22 +2547,18 @@ static void rename_anim_channels(bAnimContext *ac, int channel_index) /* free temp data and tag for refresh */ ANIM_animdata_freelist(&anim_data); ED_region_tag_redraw(ac->ar); + return success; } -static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +static int animchannels_channel_get(bAnimContext *ac, const int mval[2]) { - bAnimContext ac; ARegion *ar; View2D *v2d; int channel_index; float x, y; - /* get editor data */ - if (ANIM_animdata_get_context(C, &ac) == 0) - return OPERATOR_CANCELLED; - /* get useful pointers from animation context data */ - ar = ac.ar; + ar = ac->ar; v2d = &ar->v2d; /* figure out which channel user clicked in @@ -2490,20 +2566,36 @@ static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const * so that the tops of channels get caught ok. Since ACHANNEL_FIRST is really ACHANNEL_HEIGHT, we simply use * ACHANNEL_HEIGHT_HALF. */ - UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y); + UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); - if (ac.datatype == ANIMCONT_NLA) { - SpaceNla *snla = (SpaceNla *)ac.sl; + if (ac->datatype == ANIMCONT_NLA) { + SpaceNla *snla = (SpaceNla *)ac->sl; UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index); } else { UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index); } - + + return channel_index; +} + +static int animchannels_rename_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + bAnimContext ac; + int channel_index; + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + channel_index = animchannels_channel_get(&ac, event->mval); + /* handle click */ - rename_anim_channels(&ac, channel_index); - - return OPERATOR_FINISHED; + if (rename_anim_channels(&ac, channel_index)) + return OPERATOR_FINISHED; + else + /* allow event to be handled by selectall operator */ + return OPERATOR_PASS_THROUGH; } static void ANIM_OT_channels_rename(wmOperatorType *ot) @@ -2727,7 +2819,8 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; } - case ANIMTYPE_FCURVE: + case ANIMTYPE_FCURVE: + case ANIMTYPE_NLACURVE: { FCurve *fcu = (FCurve *)ale->data; @@ -2744,7 +2837,7 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, /* if F-Curve is selected now, make F-Curve the 'active' one in the visible list */ if (fcu->flag & FCURVE_SELECTED) - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; @@ -2767,6 +2860,19 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index, notifierFlags |= (ND_ANIMCHAN | NA_SELECTED); break; } + case ANIMTYPE_NLACONTROLS: + { + AnimData *adt = (AnimData *)ale->data; + + /* toggle expand + * - Although the triangle widget already allows this, since there's nothing else that can be done here now, + * let's just use it for easier expand/collapse for now + */ + adt->flag ^= ADT_NLA_SKEYS_COLLAPSED; + + notifierFlags |= (ND_ANIMCHAN | NA_EDITED); + break; + } case ANIMTYPE_GPDATABLOCK: { bGPdata *gpd = (bGPdata *)ale->data; @@ -2918,6 +3024,105 @@ static void ANIM_OT_channels_click(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +static bool select_anim_channel_keys(bAnimContext *ac, int channel_index, bool extend) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + int filter; + bool success = false; + FCurve *fcu; + int i; + + /* get the channel that was clicked on */ + /* filter channels */ + filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + + /* get channel from index */ + ale = BLI_findlink(&anim_data, channel_index); + if (ale == NULL) { + /* channel not found */ + if (G.debug & G_DEBUG) + printf("Error: animation channel (index = %d) not found in rename_anim_channels()\n", channel_index); + + ANIM_animdata_freelist(&anim_data); + return false; + } + + fcu = (FCurve *)ale->key_data; + success = (fcu != NULL); + + ANIM_animdata_freelist(&anim_data); + + /* F-Curve may not have any keyframes */ + if (fcu && fcu->bezt) { + BezTriple *bezt; + + if (!extend) { + filter = (ANIMFILTER_DATA_VISIBLE); + ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu_inner = (FCurve *)ale->key_data; + + if (fcu_inner) { + for (i = 0, bezt = fcu_inner->bezt; i < fcu_inner->totvert; i++, bezt++) { + bezt->f2 = bezt->f1 = bezt->f3 = 0; + } + } + } + + ANIM_animdata_freelist(&anim_data); + } + + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + bezt->f2 = bezt->f1 = bezt->f3 = SELECT; + } + } + + /* free temp data and tag for refresh */ + ED_region_tag_redraw(ac->ar); + return success; +} + +static int animchannels_channel_select_keys_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + bAnimContext ac; + int channel_index; + bool extend = RNA_boolean_get(op->ptr, "extend"); + + /* get editor data */ + if (ANIM_animdata_get_context(C, &ac) == 0) + return OPERATOR_CANCELLED; + + channel_index = animchannels_channel_get(&ac, event->mval); + + /* handle click */ + if (select_anim_channel_keys(&ac, channel_index, extend)) { + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_SELECTED, NULL); + return OPERATOR_FINISHED; + } + else + /* allow event to be handled by selectall operator */ + return OPERATOR_PASS_THROUGH; +} + +static void ANIM_OT_channel_select_keys(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Select Channel keyframes"; + ot->idname = "ANIM_OT_channel_select_keys"; + ot->description = "Select all keyframes of channel under mouse"; + + /* api callbacks */ + ot->invoke = animchannels_channel_select_keys_invoke; + ot->poll = animedit_poll_channels_active; + + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + /* ************************************************************************** */ /* Operator Registration */ @@ -2927,8 +3132,9 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channels_select_border); WM_operatortype_append(ANIM_OT_channels_click); + WM_operatortype_append(ANIM_OT_channel_select_keys); WM_operatortype_append(ANIM_OT_channels_rename); - + WM_operatortype_append(ANIM_OT_channels_find); WM_operatortype_append(ANIM_OT_channels_setting_enable); @@ -2968,7 +3174,9 @@ 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); - + WM_keymap_add_item(keymap, "ANIM_OT_channel_select_keys", LEFTMOUSE, KM_DBL_CLICK, 0, 0); + RNA_boolean_set(WM_keymap_add_item(keymap, "ANIM_OT_channel_select_keys", LEFTMOUSE, KM_DBL_CLICK, KM_SHIFT, 0)->ptr, "extend", true); + /* 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); diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index eb57907c9ec..a38f5dbc8ea 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -73,8 +73,10 @@ void ANIM_list_elem_update(Scene *scene, bAnimListElem *ale) /* tag AnimData for refresh so that other views will update in realtime with these changes */ adt = BKE_animdata_from_id(id); - if (adt) + if (adt) { adt->recalc |= ADT_RECALC_ANIM; + DAG_id_tag_update(id, OB_RECALC_TIME); + } /* update data */ fcu = (ale->datatype == ALE_FCURVE) ? ale->key_data : NULL; diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 0e052279796..d5945425576 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -34,16 +34,25 @@ #include "DNA_scene_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" +#include "DNA_screen_types.h" +#include "DNA_object_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_mask_types.h" #include "BLI_math.h" #include "BLI_timecode.h" +#include "BLI_utildefines.h" +#include "BLI_rect.h" +#include "BLI_dlrbTree.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_nla.h" +#include "BKE_mask.h" #include "ED_anim_api.h" #include "ED_keyframes_edit.h" +#include "ED_keyframes_draw.h" #include "RNA_access.h" @@ -173,10 +182,14 @@ AnimData *ANIM_nla_mapping_get(bAnimContext *ac, bAnimListElem *ale) if (G.is_rendering) return NULL; /* handling depends on the type of animation-context we've got */ - if (ale) - return ale->adt; - else - return NULL; + if (ale) { + /* NLA Control Curves occur on NLA strips, and shouldn't be subjected to this kind of mapping */ + if (ale->type != ANIMTYPE_NLACURVE) + return ale->adt; + } + + /* cannot handle... */ + return NULL; } /* ------------------- */ @@ -262,19 +275,26 @@ short ANIM_get_normalization_flags(bAnimContext *ac) return 0; } -static float normalzation_factor_get(FCurve *fcu, short flag) +static float normalization_factor_get(Scene *scene, FCurve *fcu, short flag, float *r_offset) { - float factor = 1.0f; + float factor = 1.0f, offset = 0.0f; if (flag & ANIM_UNITCONV_RESTORE) { + if (r_offset) + *r_offset = fcu->prev_offset; + return 1.0f / fcu->prev_norm_factor; } if (flag & ANIM_UNITCONV_NORMALIZE_FREEZE) { + if (r_offset) + *r_offset = fcu->prev_offset; return fcu->prev_norm_factor; } if (G.moving & G_TRANSFORM_FCURVES) { + if (r_offset) + *r_offset = fcu->prev_offset; return fcu->prev_norm_factor; } @@ -283,32 +303,65 @@ static float normalzation_factor_get(FCurve *fcu, short flag) BezTriple *bezt; int i; float max_coord = -FLT_MAX; + float min_coord = FLT_MAX; + float range; if (fcu->totvert < 1) { return 1.0f; } - for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { - max_coord = max_ff(max_coord, fabsf(bezt->vec[0][1])); - max_coord = max_ff(max_coord, fabsf(bezt->vec[1][1])); - max_coord = max_ff(max_coord, fabsf(bezt->vec[2][1])); + if (PRVRANGEON) { + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + if (IN_RANGE_INCL(bezt->vec[1][0], scene->r.psfra, scene->r.pefra)) { + max_coord = max_ff(max_coord, bezt->vec[0][1]); + max_coord = max_ff(max_coord, bezt->vec[1][1]); + max_coord = max_ff(max_coord, bezt->vec[2][1]); + + min_coord = min_ff(min_coord, bezt->vec[0][1]); + min_coord = min_ff(min_coord, bezt->vec[1][1]); + min_coord = min_ff(min_coord, bezt->vec[2][1]); + } + } + } + else { + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + max_coord = max_ff(max_coord, bezt->vec[0][1]); + max_coord = max_ff(max_coord, bezt->vec[1][1]); + max_coord = max_ff(max_coord, bezt->vec[2][1]); + + min_coord = min_ff(min_coord, bezt->vec[0][1]); + min_coord = min_ff(min_coord, bezt->vec[1][1]); + min_coord = min_ff(min_coord, bezt->vec[2][1]); + } } - if (max_coord > FLT_EPSILON) { - factor = 1.0f / max_coord; + range = max_coord - min_coord; + + if (range > FLT_EPSILON) { + factor = 2.0f / range; } + offset = -min_coord - range / 2.0f; } + + if (r_offset) { + *r_offset = offset; + } + fcu->prev_norm_factor = factor; + fcu->prev_offset = offset; return factor; } /* Get unit conversion factor for given ID + F-Curve */ -float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag) +float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag, float *r_offset) { if (flag & ANIM_UNITCONV_NORMALIZE) { - return normalzation_factor_get(fcu, flag); + return normalization_factor_get(scene, fcu, flag, r_offset); } + if (r_offset) + *r_offset = 0.0f; + /* sanity checks */ if (id && fcu && fcu->rna_path) { PointerRNA ptr, id_ptr; @@ -336,4 +389,137 @@ float ANIM_unit_mapping_get_factor(Scene *scene, ID *id, FCurve *fcu, short flag return 1.0f; } +static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prevfra) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Mask *mask = CTX_data_edit_mask(C); + bDopeSheet ads = {NULL}; + DLRBT_Tree keys; + ActKeyColumn *aknext, *akprev; + float cfranext, cfraprev; + bool donenext = false, doneprev = false; + int nextcount = 0, prevcount = 0; + + cfranext = cfraprev = (float)(CFRA); + + /* init binarytree-list for getting keyframes */ + BLI_dlrbTree_init(&keys); + + /* seed up dummy dopesheet context with flags to perform necessary filtering */ + if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { + /* only selected channels are included */ + ads.filterflag |= ADS_FILTER_ONLYSEL; + } + + /* populate tree with keyframe nodes */ + scene_to_keylist(&ads, scene, &keys, NULL); + + if (ob) + ob_to_keylist(&ads, ob, &keys, NULL); + + gpencil_to_keylist(&ads, gpd, &keys); + + if (mask) { + MaskLayer *masklay = BKE_mask_layer_active(mask); + mask_to_keylist(&ads, masklay, &keys); + } + + /* build linked-list for searching */ + BLI_dlrbTree_linkedlist_sync(&keys); + + /* find matching keyframe in the right direction */ + do { + aknext = (ActKeyColumn *)BLI_dlrbTree_search_next(&keys, compare_ak_cfraPtr, &cfranext); + + if (aknext) { + if (CFRA == (int)aknext->cfra) { + /* make this the new starting point for the search and ignore */ + cfranext = aknext->cfra; + } + else { + /* this changes the frame, so set the frame and we're done */ + if (++nextcount == U.view_frame_keyframes) + donenext = true; + } + cfranext = aknext->cfra; + } + } while ((aknext != NULL) && (donenext == false)); + + do { + akprev = (ActKeyColumn *)BLI_dlrbTree_search_prev(&keys, compare_ak_cfraPtr, &cfraprev); + + if (akprev) { + if (CFRA == (int)akprev->cfra) { + /* make this the new starting point for the search */ + } + else { + /* this changes the frame, so set the frame and we're done */ + if (++prevcount == U.view_frame_keyframes) + doneprev = true; + } + cfraprev = akprev->cfra; + } + } while ((akprev != NULL) && (doneprev == false)); + + /* free temp stuff */ + BLI_dlrbTree_free(&keys); + + /* any success? */ + if (doneprev || donenext) { + if (doneprev) + *prevfra = cfraprev; + else + *prevfra = CFRA - (cfranext - CFRA); + + if (donenext) + *nextfra = cfranext; + else + *nextfra = CFRA + (CFRA - cfraprev); + + return true; + } + + return false; +} + +void ANIM_center_frame(struct bContext *C, int smooth_viewtx) +{ + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + float w = BLI_rctf_size_x(&ar->v2d.cur); + rctf newrct; + int nextfra, prevfra; + + switch (U.view_frame_type) { + case ZOOM_FRAME_MODE_SECONDS: + newrct.xmax = scene->r.cfra + U.view_frame_seconds * FPS + 1; + newrct.xmin = scene->r.cfra - U.view_frame_seconds * FPS - 1; + newrct.ymax = ar->v2d.cur.ymax; + newrct.ymin = ar->v2d.cur.ymin; + break; + + /* hardest case of all, look for all keyframes around frame and display those */ + case ZOOM_FRAME_MODE_KEYFRAMES: + if (find_prev_next_keyframes(C, &nextfra, &prevfra)) { + newrct.xmax = nextfra; + newrct.xmin = prevfra; + newrct.ymax = ar->v2d.cur.ymax; + newrct.ymin = ar->v2d.cur.ymin; + break; + } + /* else drop through, keep range instead */ + + case ZOOM_FRAME_MODE_KEEP_RANGE: + default: + newrct.xmax = scene->r.cfra + (w / 2); + newrct.xmin = scene->r.cfra - (w / 2); + newrct.ymax = ar->v2d.cur.ymax; + newrct.ymin = ar->v2d.cur.ymin; + break; + } + + UI_view2d_smooth_view(C, ar, &newrct, smooth_viewtx); +} /* *************************************************** */ diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index d6daa64a9f2..75f5eb17b09 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -423,6 +423,7 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) * - adtOk: line or block of code to execute for AnimData-blocks case (usually ANIMDATA_ADD_ANIMDATA) * - nlaOk: line or block of code to execute for NLA tracks+strips case * - driversOk: line or block of code to execute for Drivers case + * - nlaKeysOk: line or block of code for NLA Strip Keyframes case * - keysOk: line or block of code for Keyframes case * * The checks for the various cases are as follows: @@ -433,9 +434,10 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) * converted to a new NLA strip, and the filtering options allow this * 2C) allow non-animated datablocks to be included so that datablocks can be added * 3) drivers: include drivers from animdata block (for Drivers mode in Graph Editor) - * 4) normal keyframes: only when there is an active action + * 4A) nla strip keyframes: these are the per-strip controls for time and influence + * 4B) normal keyframes: only when there is an active action */ -#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, keysOk) \ +#define ANIMDATA_FILTER_CASES(id, adtOk, nlaOk, driversOk, nlaKeysOk, keysOk) \ { \ if ((id)->adt) { \ if (!(filter_mode & ANIMFILTER_CURVE_VISIBLE) || !((id)->adt->flag & ADT_CURVES_NOT_VISIBLE)) { \ @@ -456,6 +458,9 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac) } \ } \ else { \ + if (ANIMDATA_HAS_NLA(id)) { \ + nlaKeysOk \ + } \ if (ANIMDATA_HAS_KEYS(id)) { \ keysOk \ } \ @@ -786,6 +791,16 @@ static bAnimListElem *make_new_animlistelem(void *data, short datatype, ID *owne ale->adt = BKE_animdata_from_id(data); break; } + case ANIMTYPE_NLACONTROLS: + { + AnimData *adt = (AnimData *)data; + + ale->flag = adt->flag; + + ale->key_data = NULL; + ale->datatype = ALE_NONE; + break; + } case ANIMTYPE_GROUP: { bActionGroup *agrp = (bActionGroup *)data; @@ -977,7 +992,7 @@ static bool skip_fcurve_selected_data(bDopeSheet *ads, FCurve *fcu, ID *owner_id static bool skip_fcurve_with_name(bDopeSheet *ads, FCurve *fcu, ID *owner_id) { bAnimListElem ale_dummy = {NULL}; - bAnimChannelType *acf; + const bAnimChannelType *acf; /* create a dummy wrapper for the F-Curve */ ale_dummy.type = ANIMTYPE_FCURVE; @@ -1286,7 +1301,7 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac), ListBase *anim_data, bDop * - active track should still get shown though (even though it has disabled flag set) */ // FIXME: the channels after should still get drawn, just 'differently', and after an active-action channel - if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED) && !(nlt->flag & NLATRACK_ACTIVE)) + if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED) && (adt->act_track != nlt)) continue; /* only work with this channel and its subchannels if it is editable */ @@ -1295,6 +1310,30 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac), ListBase *anim_data, bDop if (ANIMCHANNEL_SELOK(SEL_NLT(nlt))) { /* only include if this track is active */ if (!(filter_mode & ANIMFILTER_ACTIVE) || (nlt->flag & NLATRACK_ACTIVE)) { + /* name based filtering... */ + if (((ads) && (ads->filterflag & ADS_FILTER_BY_FCU_NAME)) && (owner_id)) { + bool track_ok = false, strip_ok = false; + + /* check if the name of the track, or the strips it has are ok... */ + track_ok = BLI_strcasestr(nlt->name, ads->searchstr); + + if (track_ok == false) { + NlaStrip *strip; + for (strip = nlt->strips.first; strip; strip = strip->next) { + if (BLI_strcasestr(strip->name, ads->searchstr)) { + strip_ok = true; + break; + } + } + } + + /* skip if both fail this test... */ + if (!track_ok && !strip_ok) { + continue; + } + } + + /* add the track now that it has passed all our tests */ ANIMCHANNEL_NEW_CHANNEL(nlt, ANIMTYPE_NLATRACK, owner_id); } } @@ -1305,6 +1344,80 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac), ListBase *anim_data, bDop return items; } +/* Include the control FCurves per NLA Strip in the channel list + * NOTE: This is includes the expander too... + */ +static size_t animfilter_nla_controls(ListBase *anim_data, bDopeSheet *ads, AnimData *adt, int filter_mode, ID *owner_id) +{ + ListBase tmp_data = {NULL, NULL}; + size_t tmp_items = 0; + size_t items = 0; + + /* add control curves from each NLA strip... */ + /* NOTE: ANIMTYPE_FCURVES are created here, to avoid duplicating the code needed */ + BEGIN_ANIMFILTER_SUBCHANNELS(((adt->flag & ADT_NLA_SKEYS_COLLAPSED) == 0)) + { + NlaTrack *nlt; + NlaStrip *strip; + + /* for now, we only go one level deep - so controls on grouped FCurves are not handled */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + for (strip = nlt->strips.first; strip; strip = strip->next) { + ListBase strip_curves = {NULL, NULL}; + size_t strip_items = 0; + + /* create the raw items */ + strip_items += animfilter_fcurves(&strip_curves, ads, strip->fcurves.first, NULL, filter_mode, owner_id); + + /* change their types and add extra data + * - There is no point making a separate copy of animfilter_fcurves for this now/yet, + * unless we later get per-element control curves for other stuff too + */ + if (strip_items) { + bAnimListElem *ale, *ale_n = NULL; + + for (ale = strip_curves.first; ale; ale = ale_n) { + ale_n = ale->next; + + /* change the type to being a FCurve for editing NLA strip controls */ + BLI_assert(ale->type == ANIMTYPE_FCURVE); + + ale->type = ANIMTYPE_NLACURVE; + ale->owner = strip; + + ale->adt = NULL; /* XXX: This way, there are no problems with time mapping errors */ + } + } + + /* add strip curves to the set of channels inside the group being collected */ + BLI_movelisttolist(&tmp_data, &strip_curves); + BLI_assert(BLI_listbase_is_empty(&strip_curves)); + tmp_items += strip_items; + } + } + } + END_ANIMFILTER_SUBCHANNELS; + + /* did we find anything? */ + if (tmp_items) { + /* add the expander as a channel first */ + if (filter_mode & ANIMFILTER_LIST_CHANNELS) { + /* currently these channels cannot be selected, so they should be skipped */ + if ((filter_mode & (ANIMFILTER_SEL | ANIMFILTER_UNSEL)) == 0) { + ANIMCHANNEL_NEW_CHANNEL(adt, ANIMTYPE_NLACONTROLS, owner_id); + } + } + + /* now add the list of collected channels */ + BLI_movelisttolist(anim_data, &tmp_data); + BLI_assert(BLI_listbase_is_empty(&tmp_data)); + items += tmp_items; + } + + /* return the numebr of items added to the list */ + return items; +} + /* determine what animation data from AnimData block should get displayed */ static size_t animfilter_block_data(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, ID *id, int filter_mode) { @@ -1332,6 +1445,9 @@ static size_t animfilter_block_data(bAnimContext *ac, ListBase *anim_data, bDope { /* Drivers */ items += animfilter_fcurves(anim_data, ads, adt->drivers.first, NULL, filter_mode, id); }, + { /* NLA Control Keyframes */ + items += animfilter_nla_controls(anim_data, ads, adt, filter_mode, id); + }, { /* Keyframes */ items += animfilter_action(ac, anim_data, ads, adt->action, filter_mode, id); } @@ -2214,6 +2330,7 @@ static size_t animdata_filter_ds_obanim(bAnimContext *ac, ListBase *anim_data, b cdata = adt; expanded = EXPANDED_DRVD(adt); }, + { /* NLA Strip Controls - no dedicated channel for now (XXX) */ }, { /* Keyframes */ type = ANIMTYPE_FILLACTD; cdata = adt->action; @@ -2385,6 +2502,7 @@ static size_t animdata_filter_ds_scene(bAnimContext *ac, ListBase *anim_data, bD cdata = adt; expanded = EXPANDED_DRVD(adt); }, + { /* NLA Strip Controls - no dedicated channel for now (XXX) */ }, { /* Keyframes */ type = ANIMTYPE_FILLACTD; cdata = adt->action; @@ -2804,7 +2922,7 @@ size_t ANIM_animdata_filter(bAnimContext *ac, ListBase *anim_data, eAnimFilter_F /* unhandled */ default: { - printf("ANIM_animdata_filter() - Invalid datatype argument %d\n", datatype); + printf("ANIM_animdata_filter() - Invalid datatype argument %u\n", datatype); break; } } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 140b7e0b117..40376c38c3b 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -266,8 +266,20 @@ void ED_markers_make_cfra_list(ListBase *markers, ListBase *lb, short only_sel) { TimeMarker *marker; - if (markers == NULL) + if (lb) { + /* Clear the list first, since callers have no way of knowing + * whether this terminated early otherwise. This may lead + * to crashes if the user didn't clear the memory first. + */ + lb->first = lb->last = NULL; + } + else { + return; + } + + if (markers == NULL) { return; + } for (marker = markers->first; marker; marker = marker->next) add_marker_to_cfra_elem(lb, marker, only_sel); @@ -487,11 +499,32 @@ static int ed_markers_poll_selected_markers(bContext *C) return ED_markers_get_first_selected(markers) != NULL; } +static int ed_markers_poll_selected_no_locked_markers(bContext *C) +{ + ListBase *markers = ED_context_get_markers(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + if (ts->lock_markers) + return 0; + + /* first things first: markers can only exist in timeline views */ + if (ED_operator_animview_active(C) == 0) + return 0; + + /* check if some marker is selected */ + return ED_markers_get_first_selected(markers) != NULL; +} + + /* special poll() which checks if there are any markers at all first */ static int ed_markers_poll_markers_exist(bContext *C) { ListBase *markers = ED_context_get_markers(C); + ToolSettings *ts = CTX_data_tool_settings(C); + if (ts->lock_markers) + return 0; + /* first things first: markers can only exist in timeline views */ if (ED_operator_animview_active(C) == 0) return 0; @@ -929,11 +962,11 @@ static void MARKER_OT_move(wmOperatorType *ot) ot->exec = ed_marker_move_exec; ot->invoke = ed_marker_move_invoke_wrapper; ot->modal = ed_marker_move_modal; - ot->poll = ed_markers_poll_selected_markers; + ot->poll = ed_markers_poll_selected_no_locked_markers; ot->cancel = ed_marker_move_cancel; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; /* rna storage */ RNA_def_int(ot->srna, "frames", 0, INT_MIN, INT_MAX, "Frames", "", INT_MIN, INT_MAX); @@ -1022,7 +1055,7 @@ static void MARKER_OT_duplicate(wmOperatorType *ot) ot->exec = ed_marker_duplicate_exec; ot->invoke = ed_marker_duplicate_invoke_wrapper; ot->modal = ed_marker_move_modal; - ot->poll = ed_markers_poll_selected_markers; + ot->poll = ed_markers_poll_selected_no_locked_markers; ot->cancel = ed_marker_move_cancel; /* flags */ @@ -1352,7 +1385,7 @@ static void MARKER_OT_delete(wmOperatorType *ot) /* api callbacks */ ot->invoke = ed_marker_delete_invoke_wrapper; ot->exec = ed_marker_delete_exec; - ot->poll = ed_markers_poll_selected_markers; + ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1400,7 +1433,7 @@ static void MARKER_OT_rename(wmOperatorType *ot) /* api callbacks */ ot->invoke = ed_marker_rename_invoke_wrapper; ot->exec = ed_marker_rename_exec; - ot->poll = ed_markers_poll_selected_markers; + ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1428,6 +1461,11 @@ static int ed_marker_make_links_scene_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (scene_to->toolsettings->lock_markers) { + BKE_report(op->reports, RPT_ERROR, "Target scene has locked markers"); + return OPERATOR_CANCELLED; + } + /* copy markers */ for (marker = markers->first; marker; marker = marker->next) { if (marker->flag & SELECT) { @@ -1503,13 +1541,13 @@ static void MARKER_OT_camera_bind(wmOperatorType *ot) { /* identifiers */ ot->name = "Bind Camera to Markers"; - ot->description = "Bind the active camera to selected markers(s)"; + ot->description = "Bind the active camera to selected marker(s)"; ot->idname = "MARKER_OT_camera_bind"; /* api callbacks */ ot->exec = ed_marker_camera_bind_exec; ot->invoke = ed_markers_opwrap_invoke; - ot->poll = ed_markers_poll_selected_markers; + ot->poll = ed_markers_poll_selected_no_locked_markers; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index cbcbc8743f1..2de42933dc0 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -107,7 +107,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -247,7 +247,7 @@ static void ANIM_OT_change_frame(wmOperatorType *ot) ot->poll = change_frame_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR; /* rna */ ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 67ba82f1858..b2a34d7c317 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -42,6 +42,7 @@ #include "DNA_texture_types.h" #include "BKE_animsys.h" +#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_context.h" #include "BKE_report.h" @@ -84,7 +85,7 @@ FCurve *verify_driver_fcurve(ID *id, const char rna_path[], const int array_inde /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) - adt = BKE_id_add_animdata(id); + adt = BKE_animdata_add_id(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ return NULL; @@ -450,7 +451,7 @@ static int add_driver_button_exec(bContext *C, wmOperator *op) if (success) { /* send updates */ UI_context_update_anim_flag(C); - + DAG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX } @@ -504,7 +505,7 @@ static int remove_driver_button_exec(bContext *C, wmOperator *op) if (success) { /* send updates */ UI_context_update_anim_flag(C); - + DAG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_ANIMATION | ND_FCURVES_ORDER, NULL); // XXX } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index bcdad1c93ad..2c0018b000d 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -78,7 +78,7 @@ static void validate_fmodifier_cb(bContext *UNUSED(C), void *fcm_v, void *UNUSED(arg)) { FModifier *fcm = (FModifier *)fcm_v; - FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); + const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); /* call the verify callback on the modifier if applicable */ if (fmi && fmi->verify_data) @@ -555,7 +555,7 @@ static void draw_modifier__stepped(uiLayout *layout, ID *id, FModifier *fcm, sho void ANIM_uiTemplate_fmodifier_draw(uiLayout *layout, ID *id, ListBase *modifiers, FModifier *fcm) { - FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); + const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); uiLayout *box, *row, *sub, *col; uiBlock *block; uiBut *but; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index d2dbe961b42..9e38dd5507d 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -528,7 +528,7 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel, } /* NOTE: we don't use the straight alpha from the theme, or else effects such as - * greying out protected/muted channels doesn't work correctly! + * graying out protected/muted channels doesn't work correctly! */ inner_col[3] *= alpha; glColor4fv(inner_col); diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index 2d869d272bd..b3dc0021f00 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -199,13 +199,17 @@ void clean_fcurve(FCurve *fcu, float thresh) /* now insert first keyframe, as it should be ok */ bezt = old_bezts; insert_vert_fcurve(fcu, bezt->vec[1][0], bezt->vec[1][1], 0); + if (!(bezt->f2 & SELECT)) { + lastb = fcu->bezt; + lastb->f1 = lastb->f2 = lastb->f3 = 0; + } /* Loop through BezTriples, comparing them. Skip any that do * not fit the criteria for "ok" points. */ for (i = 1; i < totCount; i++) { float prev[2], cur[2], next[2]; - + /* get BezTriples and their values */ if (i < (totCount - 1)) { beztn = (old_bezts + (i + 1)); @@ -217,10 +221,17 @@ void clean_fcurve(FCurve *fcu, float thresh) } lastb = (fcu->bezt + (fcu->totvert - 1)); bezt = (old_bezts + i); - + /* get references for quicker access */ prev[0] = lastb->vec[1][0]; prev[1] = lastb->vec[1][1]; cur[0] = bezt->vec[1][0]; cur[1] = bezt->vec[1][1]; + + if (!(bezt->f2 & SELECT)) { + insert_vert_fcurve(fcu, cur[0], cur[1], 0); + lastb = (fcu->bezt + (fcu->totvert - 1)); + lastb->f1 = lastb->f2 = lastb->f3 = 0; + continue; + } /* check if current bezt occurs at same time as last ok */ if (IS_EQT(cur[0], prev[0], thresh)) { @@ -228,7 +239,7 @@ void clean_fcurve(FCurve *fcu, float thresh) * if there is a considerable distance between the points, and also if the * current is further away than the next one is to the previous. */ - if (beztn && (IS_EQT(cur[0], next[0], thresh)) && + if (beztn && (IS_EQT(cur[0], next[0], thresh)) && (IS_EQT(next[1], prev[1], thresh) == 0)) { /* only add if current is further away from previous */ @@ -548,24 +559,15 @@ short copy_animedit_keys(bAnimContext *ac, ListBase *anim_data) * storing the relevant information here helps avoiding crashes if we undo-repaste */ if ((aci->id_type == ID_OB) && (((Object *)aci->id)->type == OB_ARMATURE) && aci->rna_path) { Object *ob = (Object *)aci->id; - char *str_start; - - if ((str_start = strstr(aci->rna_path, "pose.bones["))) { - bPoseChannel *pchan; - int length = 0; - char *str_end; - - str_start += 12; - str_end = strchr(str_start, '\"'); - length = str_end - str_start; - str_start[length] = 0; - pchan = BKE_pose_channel_find_name(ob->pose, str_start); - str_start[length] = '\"'; - - if (pchan) { - aci->is_bone = true; - } + bPoseChannel *pchan; + char *bone_name; + + bone_name = BLI_str_quoted_substrN(aci->rna_path, "pose.bones["); + pchan = BKE_pose_channel_find_name(ob->pose, bone_name); + if (pchan) { + aci->is_bone = true; } + if (bone_name) MEM_freeN(bone_name); } BLI_addtail(&animcopybuf, aci); @@ -624,22 +626,22 @@ static void flip_names(tAnimCopybufItem *aci, char **name) char bname_new[MAX_VGROUP_NAME]; char *str_iter, *str_end; int length, prefix_l, postfix_l; - + str_start += 12; prefix_l = str_start - aci->rna_path; - + str_end = strchr(str_start, '\"'); - + length = str_end - str_start; postfix_l = strlen(str_end); - + /* more ninja stuff, temporary substitute with NULL terminator */ str_start[length] = 0; BKE_deform_flip_side_name(bname_new, str_start, false); str_start[length] = '\"'; - + str_iter = *name = MEM_mallocN(sizeof(char) * (prefix_l + postfix_l + length + 1), "flipped_path"); - + BLI_strncpy(str_iter, aci->rna_path, prefix_l + 1); str_iter += prefix_l; BLI_strncpy(str_iter, bname_new, length + 1); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 7e2ce4cd3f1..85ea5526908 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -130,7 +130,7 @@ bAction *verify_adt_action(ID *id, short add) /* init animdata if none available yet */ adt = BKE_animdata_from_id(id); if ((adt == NULL) && (add)) - adt = BKE_id_add_animdata(id); + adt = BKE_animdata_add_id(id); if (adt == NULL) { /* if still none (as not allowed to add, or ID doesn't have animdata for some reason) */ printf("ERROR: Couldn't add AnimData (ID = %s)\n", (id) ? (id->name) : "<None>"); @@ -152,6 +152,10 @@ bAction *verify_adt_action(ID *id, short add) * to the wrong places */ adt->action->idroot = GS(id->name); + + /* tag depsgraph to be rebuilt to include time dependency */ + /* XXX: we probably should have bmain passed down, but that involves altering too many API's */ + DAG_relations_tag_update(G.main); } /* return the action */ @@ -1696,7 +1700,7 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) char *path; float cfra = (float)CFRA; short success = 0; - int a, index, length; + int index; const bool all = RNA_boolean_get(op->ptr, "all"); short flag = 0; @@ -1707,33 +1711,35 @@ static int insert_key_button_exec(bContext *C, wmOperator *op) UI_context_active_but_prop_get(C, &ptr, &prop, &index); if ((ptr.id.data && ptr.data && prop) && RNA_property_animateable(&ptr, prop)) { - path = RNA_path_from_ID_to_property(&ptr, prop); - - if (path) { - if (all) { - length = RNA_property_array_length(&ptr, prop); - - if (length) index = 0; - else length = 1; - } - else - length = 1; - - for (a = 0; a < length; a++) - success += insert_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index + a, cfra, flag); - - MEM_freeN(path); - } - else if (ptr.type == &RNA_NlaStrip) { - /* handle special vars for NLA-strips */ + if (ptr.type == &RNA_NlaStrip) { + /* Handle special properties for NLA Strips, whose F-Curves are stored on the + * strips themselves. These are stored separately or else the properties will + * not have any effect. + */ NlaStrip *strip = (NlaStrip *)ptr.data; FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), flag); - success += insert_keyframe_direct(op->reports, ptr, prop, fcu, cfra, 0); + success = insert_keyframe_direct(op->reports, ptr, prop, fcu, cfra, 0); } else { - BKE_report(op->reports, RPT_WARNING, - "Failed to resolve path to property, try manually specifying this using a Keying Set instead"); + /* standard properties */ + path = RNA_path_from_ID_to_property(&ptr, prop); + + if (path) { + if (all) { + /* -1 indicates operating on the entire array (or the property itself otherwise) */ + index = -1; + } + + success = insert_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index, cfra, flag); + + MEM_freeN(path); + } + else { + BKE_report(op->reports, RPT_WARNING, + "Failed to resolve path to property, " + "try manually specifying this using a Keying Set instead"); + } } } else { @@ -1788,32 +1794,62 @@ static int delete_key_button_exec(bContext *C, wmOperator *op) char *path; float cfra = (float)CFRA; // XXX for now, don't bother about all the yucky offset crap short success = 0; - int a, index, length; + int index; const bool all = RNA_boolean_get(op->ptr, "all"); /* try to insert keyframe using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.id.data && ptr.data && prop) { - path = RNA_path_from_ID_to_property(&ptr, prop); - - if (path) { - if (all) { - length = RNA_property_array_length(&ptr, prop); + if (ptr.type == &RNA_NlaStrip) { + /* Handle special properties for NLA Strips, whose F-Curves are stored on the + * strips themselves. These are stored separately or else the properties will + * not have any effect. + */ + ID *id = ptr.id.data; + NlaStrip *strip = (NlaStrip *)ptr.data; + FCurve *fcu = list_find_fcurve(&strip->fcurves, RNA_property_identifier(prop), 0); + + BLI_assert(fcu != NULL); /* NOTE: This should be true, or else we wouldn't be able to get here */ + + if (BKE_fcurve_is_protected(fcu)) { + BKE_reportf(op->reports, RPT_WARNING, + "Not deleting keyframe for locked F-Curve for NLA Strip influence on %s - %s '%s'", + strip->name, BKE_idcode_to_name(GS(id->name)), id->name + 2); + } + else { + /* remove the keyframe directly + * NOTE: cannot use delete_keyframe_fcurve(), as that will free the curve, + * and delete_keyframe() expects the FCurve to be part of an action + */ + bool found = false; + int i; - if (length) index = 0; - else length = 1; + /* try to find index of beztriple to get rid of */ + i = binarysearch_bezt_index(fcu->bezt, cfra, fcu->totvert, &found); + if (found) { + /* delete the key at the index (will sanity check + do recalc afterwards) */ + delete_fcurve_key(fcu, i, 1); + success = true; + } } - else - length = 1; - - for (a = 0; a < length; a++) - success += delete_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index + a, cfra, 0); + } + else { + /* standard properties */ + path = RNA_path_from_ID_to_property(&ptr, prop); - MEM_freeN(path); + if (path) { + if (all) { + /* -1 indicates operating on the entire array (or the property itself otherwise) */ + index = -1; + } + + success = delete_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index, cfra, 0); + MEM_freeN(path); + } + else if (G.debug & G_DEBUG) + printf("Button Delete-Key: no path to property\n"); } - else if (G.debug & G_DEBUG) - printf("Button Delete-Key: no path to property\n"); } else if (G.debug & G_DEBUG) { printf("ptr.data = %p, prop = %p\n", (void *)ptr.data, (void *)prop); @@ -1858,7 +1894,7 @@ static int clear_key_button_exec(bContext *C, wmOperator *op) PropertyRNA *prop = NULL; char *path; short success = 0; - int a, index, length; + int index; const bool all = RNA_boolean_get(op->ptr, "all"); /* try to insert keyframe using property retrieved from UI */ @@ -1869,17 +1905,11 @@ static int clear_key_button_exec(bContext *C, wmOperator *op) if (path) { if (all) { - length = RNA_property_array_length(&ptr, prop); - - if (length) index = 0; - else length = 1; + /* -1 indicates operating on the entire array (or the property itself otherwise) */ + index = -1; } - else - length = 1; - - for (a = 0; a < length; a++) - success += clear_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index + a, 0); + success += clear_keyframe(op->reports, ptr.id.data, NULL, NULL, path, index, 0); MEM_freeN(path); } else if (G.debug & G_DEBUG) diff --git a/source/blender/editors/armature/BIF_retarget.h b/source/blender/editors/armature/BIF_retarget.h index 21e85b6fe89..aa56f847f00 100644 --- a/source/blender/editors/armature/BIF_retarget.h +++ b/source/blender/editors/armature/BIF_retarget.h @@ -40,7 +40,6 @@ struct bContext; struct EditBone; -struct RigJoint; struct RigGraph; struct RigNode; struct RigArc; diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 22aaeccc4a8..59453f0ac71 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -43,6 +43,7 @@ #include "BKE_constraint.h" #include "BKE_context.h" #include "BKE_idprop.h" +#include "BKE_deform.h" #include "RNA_access.h" #include "RNA_define.h" @@ -89,7 +90,7 @@ EditBone *ED_armature_edit_bone_add_primitive(Object *obedit_arm, float length, bArmature *arm = obedit_arm->data; EditBone *bone; - ED_armature_deselect_all(obedit_arm, 0); + ED_armature_deselect_all(obedit_arm); /* Create a bone */ bone = ED_armature_edit_bone_add(arm, "Bone"); @@ -144,7 +145,7 @@ static int armature_click_extrude_exec(bContext *C, wmOperator *UNUSED(op)) to_root = 1; } - ED_armature_deselect_all(obedit, 0); + ED_armature_deselect_all(obedit); /* we re-use code for mirror editing... */ flipbone = NULL; @@ -282,12 +283,8 @@ static EditBone *get_named_editbone(ListBase *edbo, const char *name) * */ void preEditBoneDuplicate(ListBase *editbones) { - EditBone *eBone; - /* clear temp */ - for (eBone = editbones->first; eBone; eBone = eBone->next) { - eBone->temp = NULL; - } + ED_armature_ebone_listbase_temp_clear(editbones); } /* @@ -311,7 +308,7 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone, ListBase *editbones, Obj /* does this constraint have a subtarget in * this armature? */ - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -327,8 +324,8 @@ void updateDuplicateSubtargetObjects(EditBone *dupBone, ListBase *editbones, Obj * so, update the constraint to point at the * duplicate of the old subtarget. */ - if (oldtarget->temp) { - newtarget = (EditBone *) oldtarget->temp; + if (oldtarget->temp.ebone) { + newtarget = oldtarget->temp.ebone; BLI_strncpy(ct->subtarget, newtarget->name, sizeof(ct->subtarget)); } } @@ -357,8 +354,8 @@ EditBone *duplicateEditBoneObjects(EditBone *curBone, const char *name, ListBase /* Copy data from old bone to new bone */ memcpy(eBone, curBone, sizeof(EditBone)); - curBone->temp = eBone; - eBone->temp = curBone; + curBone->temp.ebone = eBone; + eBone->temp.ebone = curBone; if (name != NULL) { BLI_strncpy(eBone->name, name, sizeof(eBone->name)); @@ -398,13 +395,11 @@ EditBone *duplicateEditBone(EditBone *curBone, const char *name, ListBase *editb return duplicateEditBoneObjects(curBone, name, editbones, ob, ob); } -/* previously adduplicate_armature */ static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op)) { bArmature *arm; - EditBone *eBone = NULL; - EditBone *curBone; - EditBone *firstDup = NULL; /* The beginning of the duplicated bones in the edbo list */ + EditBone *ebone_iter; + EditBone *ebone_first_dupe = NULL; /* The beginning of the duplicated bones in the edbo list */ Object *obedit = CTX_data_edit_object(C); arm = obedit->data; @@ -419,77 +414,80 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *UNUSED(op)) /* Select mirrored bones */ if (arm->flag & ARM_MIRROR_EDIT) { - for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - eBone = ED_armature_bone_get_mirrored(arm->edbo, curBone); - if (eBone) - eBone->flag |= BONE_SELECTED; + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && + (ebone_iter->flag & BONE_SELECTED)) + { + EditBone *ebone; + + ebone = ED_armature_bone_get_mirrored(arm->edbo, ebone_iter); + if (ebone) { + ebone->flag |= BONE_SELECTED; } } } } - /* Find the selected bones and duplicate them as needed */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - - eBone = duplicateEditBone(curBone, curBone->name, arm->edbo, obedit); - - if (!firstDup) - firstDup = eBone; + /* Find the selected bones and duplicate them as needed */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && + (ebone_iter->flag & BONE_SELECTED)) + { + EditBone *ebone; + ebone = duplicateEditBone(ebone_iter, ebone_iter->name, arm->edbo, obedit); + + if (!ebone_first_dupe) { + ebone_first_dupe = ebone; } } } - /* Run though the list and fix the pointers */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) { - if (curBone->flag & BONE_SELECTED) { - eBone = (EditBone *) curBone->temp; - - if (!curBone->parent) { - /* If this bone has no parent, - * Set the duplicate->parent to NULL - */ - eBone->parent = NULL; - } - else if (curBone->parent->temp) { - /* If this bone has a parent that was duplicated, - * Set the duplicate->parent to the curBone->parent->temp - */ - eBone->parent = (EditBone *)curBone->parent->temp; - } - else { - /* If this bone has a parent that IS not selected, - * Set the duplicate->parent to the curBone->parent - */ - eBone->parent = (EditBone *) curBone->parent; - eBone->flag &= ~BONE_CONNECTED; - } - - /* Lets try to fix any constraint subtargets that might - * have been duplicated + /* Run though the list and fix the pointers */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && + (ebone_iter->flag & BONE_SELECTED)) + { + EditBone *ebone = ebone_iter->temp.ebone; + + if (!ebone_iter->parent) { + /* If this bone has no parent, + * Set the duplicate->parent to NULL + */ + ebone->parent = NULL; + } + else if (ebone_iter->parent->temp.ebone) { + /* If this bone has a parent that was duplicated, + * Set the duplicate->parent to the curBone->parent->temp + */ + ebone->parent = ebone_iter->parent->temp.ebone; + } + else { + /* If this bone has a parent that IS not selected, + * Set the duplicate->parent to the curBone->parent */ - updateDuplicateSubtarget(eBone, arm->edbo, obedit); + ebone->parent = (EditBone *) ebone_iter->parent; + ebone->flag &= ~BONE_CONNECTED; } + + /* Lets try to fix any constraint subtargets that might + * have been duplicated + */ + updateDuplicateSubtarget(ebone, arm->edbo, obedit); } } /* correct the active bone */ - if (arm->act_edbone) { - eBone = arm->act_edbone; - if (eBone->temp) - arm->act_edbone = eBone->temp; + if (arm->act_edbone && arm->act_edbone->temp.ebone) { + arm->act_edbone = arm->act_edbone->temp.ebone; } - /* Deselect the old bones and select the new ones */ - for (curBone = arm->edbo->first; curBone && curBone != firstDup; curBone = curBone->next) { - if (EBONE_VISIBLE(arm, curBone)) - curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + /* Deselect the old bones and select the new ones */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter)) { + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } } ED_armature_validate_active(arm); @@ -515,6 +513,214 @@ void ARMATURE_OT_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** + * near duplicate of #armature_duplicate_selected_exec, + * except for parenting part (keep in sync) + */ +static int armature_symmetrize_exec(bContext *C, wmOperator *op) +{ + bArmature *arm; + EditBone *ebone_iter; + EditBone *ebone_first_dupe = NULL; /* The beginning of the duplicated mirrored bones in the edbo list */ + + Object *obedit = CTX_data_edit_object(C); + const int direction = RNA_enum_get(op->ptr, "direction"); + const int axis = 0; + + arm = obedit->data; + + /* cancel if nothing selected */ + if (CTX_DATA_COUNT(C, selected_bones) == 0) + return OPERATOR_CANCELLED; + + ED_armature_sync_selection(arm->edbo); // XXX why is this needed? + + preEditBoneDuplicate(arm->edbo); + + /* Select mirrored bones */ + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && + (ebone_iter->flag & BONE_SELECTED)) + { + char name_flip[MAX_VGROUP_NAME]; + + BKE_deform_flip_side_name(name_flip, ebone_iter->name, false); + + if (STREQ(name_flip, ebone_iter->name)) { + /* if the name matches, we don't have the potential to be mirrored, just skip */ + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + EditBone *ebone = ED_armature_bone_find_name(arm->edbo, name_flip); + + if (ebone) { + if ((ebone->flag & BONE_SELECTED) == 0) { + /* simple case, we're selected, the other bone isn't! */ + ebone_iter->temp.ebone = ebone; + } + else { + /* complicated - choose which direction to copy */ + float axis_delta; + + axis_delta = ebone->head[axis] - ebone_iter->head[axis]; + if (axis_delta == 0.0f) { + axis_delta = ebone->tail[axis] - ebone_iter->tail[axis]; + } + + if (axis_delta == 0.0f) { + /* both mirrored bones exist and point to eachother and overlap exactly. + * + * in this case theres no well defined solution, so de-select both and skip. + */ + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + else { + EditBone *ebone_src, *ebone_dst; + if (((axis_delta < 0.0f) ? -1 : 1) == direction) { + ebone_src = ebone; + ebone_dst = ebone_iter; + } + else { + ebone_src = ebone_iter; + ebone_dst = ebone; + } + + ebone_src->temp.ebone = ebone_dst; + ebone_dst->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + } + } + } + } + + /* Find the selected bones and duplicate them as needed, with mirrored name */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter) && + (ebone_iter->flag & BONE_SELECTED) && + /* will be set if the mirror bone already exists (no need to make a new one) */ + (ebone_iter->temp.ebone == NULL)) + { + char name_flip[MAX_VGROUP_NAME]; + + BKE_deform_flip_side_name(name_flip, ebone_iter->name, false); + + /* bones must have a side-suffix */ + if (!STREQ(name_flip, ebone_iter->name)) { + EditBone *ebone; + + ebone = duplicateEditBone(ebone_iter, name_flip, arm->edbo, obedit); + + if (!ebone_first_dupe) { + ebone_first_dupe = ebone; + } + } + } + } + + /* Run though the list and fix the pointers */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (ebone_iter->temp.ebone) { + /* copy all flags except for ... */ + const int flag_copy = ((int)~0) & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + + EditBone *ebone = ebone_iter->temp.ebone; + + /* copy flags incase bone is pre-existing data */ + ebone->flag = (ebone->flag & ~flag_copy) | (ebone_iter->flag & flag_copy); + + if (ebone_iter->parent == NULL) { + /* If this bone has no parent, + * Set the duplicate->parent to NULL + */ + ebone->parent = NULL; + ebone->flag &= ~BONE_CONNECTED; + } + else { + /* the parent may have been duplicated, if not lookup the mirror parent */ + EditBone *ebone_parent = + (ebone_iter->parent->temp.ebone ? + ebone_iter->parent->temp.ebone : ED_armature_bone_get_mirrored(arm->edbo, ebone_iter->parent)); + + if (ebone_parent == NULL) { + /* If the mirror lookup failed, (but the current bone has a parent) + * then we can assume the parent has no L/R but is a center bone. + * So just use the same parent for both. + */ + ebone_parent = ebone_iter->parent; + ebone->flag &= ~BONE_CONNECTED; + } + + ebone->parent = ebone_parent; + } + + /* Lets try to fix any constraint subtargets that might + * have been duplicated + */ + updateDuplicateSubtarget(ebone, arm->edbo, obedit); + } + } + + transform_armature_mirror_update(obedit); + + /* Selected bones now have their 'temp' pointer set, + * so we don't need this anymore */ + + /* Deselect the old bones and select the new ones */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + if (EBONE_VISIBLE(arm, ebone_iter)) { + ebone_iter->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } + + /* New bones will be selected, but some of the bones may already exist */ + for (ebone_iter = arm->edbo->first; ebone_iter && ebone_iter != ebone_first_dupe; ebone_iter = ebone_iter->next) { + EditBone *ebone = ebone_iter->temp.ebone; + if (ebone && EBONE_SELECTABLE(arm, ebone)) { + ED_armature_ebone_select_set(ebone, true); + } + } + + /* correct the active bone */ + if (arm->act_edbone && arm->act_edbone->temp.ebone) { + arm->act_edbone = arm->act_edbone->temp.ebone; + } + + ED_armature_validate_active(arm); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +/* following conventions from #MESH_OT_symmetrize */ +void ARMATURE_OT_symmetrize(wmOperatorType *ot) +{ + /* subset of 'symmetrize_direction_items' */ + static EnumPropertyItem arm_symmetrize_direction_items[] = { + {-1, "NEGATIVE_X", 0, "-X to +X", ""}, + {+1, "POSITIVE_X", 0, "+X to -X", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Symmetrize"; + ot->idname = "ARMATURE_OT_symmetrize"; + ot->description = "Enforce symmetry, make copies of the selection or use existing"; + + /* api callbacks */ + ot->exec = armature_symmetrize_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum( + ot->srna, "direction", arm_symmetrize_direction_items, -1, + "Direction", "Which sides to copy from and to (when both are selected)"); +} + /* ------------------------------------------ */ /* previously extrude_armature */ @@ -704,7 +910,7 @@ static int armature_bone_primitive_add_exec(bContext *C, wmOperator *op) mul_m3_m3m3(totmat, obmat, viewmat); invert_m3_m3(imat, totmat); - ED_armature_deselect_all(obedit, 0); + ED_armature_deselect_all(obedit); /* Create a bone */ bone = ED_armature_edit_bone_add(obedit->data, name); @@ -830,7 +1036,7 @@ void ARMATURE_OT_subdivide(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10); /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 88c52989c07..b3297db6176 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -42,6 +42,7 @@ #include "BLI_blenlib.h" #include "BLI_math.h" +#include "BLI_ghash.h" #include "BKE_action.h" #include "BKE_armature.h" @@ -578,6 +579,7 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ListBase points = {NULL, NULL}; + EditBone *newbone = NULL; int count; /* sanity checks */ @@ -610,94 +612,97 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) float curs[3]; /* Get Points - selected joint */ - ebp = (EditBonePoint *)points.first; + ebp = points.first; /* Get points - cursor (tail) */ invert_m4_m4(obedit->imat, obedit->obmat); mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); /* Create a bone */ - /* newbone = */ add_points_bone(obedit, ebp->vec, curs); + newbone = add_points_bone(obedit, ebp->vec, curs); } else if (count == 2) { - EditBonePoint *ebp, *ebp2; + EditBonePoint *ebp_a, *ebp_b; float head[3], tail[3]; short headtail = 0; /* check that the points don't belong to the same bone */ - ebp = (EditBonePoint *)points.first; - ebp2 = ebp->next; + ebp_a = (EditBonePoint *)points.first; + ebp_b = ebp_a->next; - if ((ebp->head_owner == ebp2->tail_owner) && (ebp->head_owner != NULL)) { - BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); - BLI_freelistN(&points); - return OPERATOR_CANCELLED; - } - if ((ebp->tail_owner == ebp2->head_owner) && (ebp->tail_owner != NULL)) { + if (((ebp_a->head_owner == ebp_b->tail_owner) && (ebp_a->head_owner != NULL)) || + ((ebp_a->tail_owner == ebp_b->head_owner) && (ebp_a->tail_owner != NULL))) + { BKE_report(op->reports, RPT_ERROR, "Same bone selected..."); BLI_freelistN(&points); return OPERATOR_CANCELLED; } /* find which one should be the 'head' */ - if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) { - /* rule: whichever one is closer to 3d-cursor */ - float curs[3]; - float vecA[3], vecB[3]; - float distA, distB; - - /* get cursor location */ - invert_m4_m4(obedit->imat, obedit->obmat); - mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); - - /* get distances */ - sub_v3_v3v3(vecA, ebp->vec, curs); - sub_v3_v3v3(vecB, ebp2->vec, curs); - distA = len_v3(vecA); - distB = len_v3(vecB); - - /* compare distances - closer one therefore acts as direction for bone to go */ - headtail = (distA < distB) ? 2 : 1; + if ((ebp_a->head_owner && ebp_b->head_owner) || (ebp_a->tail_owner && ebp_b->tail_owner)) { + /* use active, nice predictable */ + if (arm->act_edbone && ELEM(arm->act_edbone, ebp_a->head_owner, ebp_a->tail_owner)) { + headtail = 1; + } + else if (arm->act_edbone && ELEM(arm->act_edbone, ebp_b->head_owner, ebp_b->tail_owner)) { + headtail = 2; + } + else { + /* rule: whichever one is closer to 3d-cursor */ + float curs[3]; + float dist_sq_a, dist_sq_b; + + /* get cursor location */ + invert_m4_m4(obedit->imat, obedit->obmat); + mul_v3_m4v3(curs, obedit->imat, ED_view3d_cursor3d_get(scene, v3d)); + + /* get distances */ + dist_sq_a = len_squared_v3v3(ebp_a->vec, curs); + dist_sq_b = len_squared_v3v3(ebp_b->vec, curs); + + /* compare distances - closer one therefore acts as direction for bone to go */ + headtail = (dist_sq_a < dist_sq_b) ? 2 : 1; + } } - else if (ebp->head_owner) { + else if (ebp_a->head_owner) { headtail = 1; } - else if (ebp2->head_owner) { + else if (ebp_b->head_owner) { headtail = 2; } /* assign head/tail combinations */ if (headtail == 2) { - copy_v3_v3(head, ebp->vec); - copy_v3_v3(tail, ebp2->vec); + copy_v3_v3(head, ebp_a->vec); + copy_v3_v3(tail, ebp_b->vec); } else if (headtail == 1) { - copy_v3_v3(head, ebp2->vec); - copy_v3_v3(tail, ebp->vec); + copy_v3_v3(head, ebp_b->vec); + copy_v3_v3(tail, ebp_a->vec); } /* add new bone and parent it to the appropriate end */ if (headtail) { - EditBone *newbone = add_points_bone(obedit, head, tail); + newbone = add_points_bone(obedit, head, tail); /* do parenting (will need to set connected flag too) */ if (headtail == 2) { /* ebp tail or head - tail gets priority */ - if (ebp->tail_owner) - newbone->parent = ebp->tail_owner; + if (ebp_a->tail_owner) + newbone->parent = ebp_a->tail_owner; else - newbone->parent = ebp->head_owner; + newbone->parent = ebp_a->head_owner; } else { - /* ebp2 tail or head - tail gets priority */ - if (ebp2->tail_owner) - newbone->parent = ebp2->tail_owner; + /* ebp_b tail or head - tail gets priority */ + if (ebp_b->tail_owner) + newbone->parent = ebp_b->tail_owner; else - newbone->parent = ebp2->head_owner; + newbone->parent = ebp_b->head_owner; } /* don't set for bone connecting two head points of bones */ - if (ebp->tail_owner || ebp2->tail_owner) { + if (ebp_a->tail_owner || ebp_b->tail_owner) { newbone->flag |= BONE_CONNECTED; } } @@ -708,6 +713,12 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) BLI_freelistN(&points); return OPERATOR_CANCELLED; } + + if (newbone) { + ED_armature_deselect_all(obedit); + arm->act_edbone = newbone; + newbone->flag |= BONE_TIPSEL; + } /* updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POSE, obedit); @@ -1223,13 +1234,21 @@ void ARMATURE_OT_split(wmOperatorType *ot) /* ********************************* Delete ******************************* */ +static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p) +{ + bArmature *arm = arm_p; + EditBone *ebone; + + ebone = ED_armature_bone_find_name(arm->edbo, bone_name); + return (ebone && (ebone->flag & BONE_SELECTED) && (arm->layer & ebone->layer)); +} + /* previously delete_armature */ /* only editmode! */ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) { bArmature *arm; EditBone *curBone, *ebone_next; - bConstraint *con; Object *obedit = CTX_data_edit_object(C); // XXX get from context bool changed = false; arm = obedit->data; @@ -1240,48 +1259,8 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) armature_select_mirrored(arm); - /* First erase any associated pose channel */ - if (obedit->pose) { - bPoseChannel *pchan, *pchan_next; - for (pchan = obedit->pose->chanbase.first; pchan; pchan = pchan_next) { - pchan_next = pchan->next; - curBone = ED_armature_bone_find_name(arm->edbo, pchan->name); - - if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { - BKE_pose_channel_free(pchan); - BKE_pose_channels_hash_free(obedit->pose); - BLI_freelinkN(&obedit->pose->chanbase, pchan); - } - else { - for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(con, &targets); - - for (ct = targets.first; ct; ct = ct->next) { - if (ct->tar == obedit) { - if (ct->subtarget[0]) { - curBone = ED_armature_bone_find_name(arm->edbo, ct->subtarget); - if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { - con->flag |= CONSTRAINT_DISABLE; - ct->subtarget[0] = 0; - } - } - } - } - - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(con, &targets, 0); - } - } - } - } - } - - + BKE_pose_channels_remove(obedit, armature_delete_ebone_cb, arm); + for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { ebone_next = curBone->next; if (arm->layer & curBone->layer) { @@ -1319,6 +1298,170 @@ void ARMATURE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static bool armature_dissolve_ebone_cb(const char *bone_name, void *arm_p) +{ + bArmature *arm = arm_p; + EditBone *ebone; + + ebone = ED_armature_bone_find_name(arm->edbo, bone_name); + return (ebone && (ebone->flag & BONE_DONE)); +} + +static int armature_dissolve_selected_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bArmature *arm; + EditBone *ebone, *ebone_next; + Object *obedit = CTX_data_edit_object(C); + bool changed = false; + + /* store for mirror */ + GHash *ebone_flag_orig = NULL; + int ebone_num = 0; + + arm = obedit->data; + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->temp.p = NULL; + ebone->flag &= ~BONE_DONE; + ebone_num++; + } + + if (arm->flag & ARM_MIRROR_EDIT) { + GHashIterator gh_iter; + + ebone_flag_orig = BLI_ghash_ptr_new_ex(__func__, ebone_num); + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + union { int flag; void *p; } val = {0}; + val.flag = ebone->flag; + BLI_ghash_insert(ebone_flag_orig, ebone, val.p); + } + + armature_select_mirrored_ex(arm, BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL); + + GHASH_ITER (gh_iter, ebone_flag_orig) { + union { int flag; void *p; } *val_p = (void *)BLI_ghashIterator_getValue_p(&gh_iter); + ebone = BLI_ghashIterator_getKey(&gh_iter); + val_p->flag = ebone->flag & ~val_p->flag; + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && ebone->flag & BONE_CONNECTED) { + if (ebone->parent->temp.ebone == ebone->parent) { + /* ignore */ + } + else if (ebone->parent->temp.ebone) { + /* set ignored */ + ebone->parent->temp.ebone = ebone->parent; + } + else { + /* set child */ + ebone->parent->temp.ebone = ebone; + } + } + } + + /* cleanup multiple used bones */ + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->temp.ebone == ebone) { + ebone->temp.ebone = NULL; + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + /* break connections for unseen bones */ + if (((arm->layer & ebone->layer) && + ((ED_armature_ebone_selectflag_get(ebone) & (BONE_TIPSEL | BONE_SELECTED)))) == 0) + { + ebone->temp.ebone = NULL; + } + + if (((arm->layer & ebone->layer) && + ((ED_armature_ebone_selectflag_get(ebone) & (BONE_ROOTSEL | BONE_SELECTED)))) == 0) + { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + ebone->parent->temp.ebone = NULL; + } + + } + } + + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + + if (ebone->parent && + (ebone->parent->temp.ebone == ebone)) + { + ebone->flag |= BONE_DONE; + } + } + + BKE_pose_channels_remove(obedit, armature_dissolve_ebone_cb, arm); + + for (ebone = arm->edbo->first; ebone; ebone = ebone_next) { + ebone_next = ebone->next; + + if (ebone->flag & BONE_DONE) { + copy_v3_v3(ebone->parent->tail, ebone->tail); + ebone->parent->rad_tail = ebone->rad_tail; + + ED_armature_edit_bone_remove(arm, ebone); + changed = true; + } + } + + if (changed) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && + ebone->parent->temp.ebone && + (ebone->flag & BONE_CONNECTED) == 0) + { + ebone->flag |= BONE_CONNECTED; + ebone->rad_head = ebone->parent->rad_head; + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + union { int flag; void *p; } *val_p = (void *)BLI_ghash_lookup_p(ebone_flag_orig, ebone); + if (val_p && val_p->flag) { + ebone->flag &= ~val_p->flag; + } + } + } + } + + if (arm->flag & ARM_MIRROR_EDIT) { + BLI_ghash_free(ebone_flag_orig, NULL, NULL); + } + + if (!changed) { + return OPERATOR_CANCELLED; + } + + ED_armature_sync_selection(arm->edbo); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void ARMATURE_OT_dissolve(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Dissolve Selected Bone(s)"; + ot->idname = "ARMATURE_OT_dissolve"; + ot->description = "Dissolve selected bones from the armature"; + + /* api callbacks */ + ot->exec = armature_dissolve_selected_exec; + ot->poll = ED_operator_editarmature; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + + /* ********************************* Show/Hide ******************************* */ static int armature_hide_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 2c64c9aa345..2968851f2eb 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -71,7 +71,9 @@ void ARMATURE_OT_select_similar(struct wmOperatorType *ot); void ARMATURE_OT_shortest_path_pick(struct wmOperatorType *ot); void ARMATURE_OT_delete(struct wmOperatorType *ot); +void ARMATURE_OT_dissolve(struct wmOperatorType *ot); void ARMATURE_OT_duplicate(struct wmOperatorType *ot); +void ARMATURE_OT_symmetrize(struct wmOperatorType *ot); void ARMATURE_OT_extrude(struct wmOperatorType *ot); void ARMATURE_OT_hide(struct wmOperatorType *ot); void ARMATURE_OT_reveal(struct wmOperatorType *ot); @@ -233,6 +235,7 @@ EditBone *add_points_bone(struct Object *obedit, float head[3], float tail[3]); void bone_free(struct bArmature *arm, struct EditBone *bone); void armature_tag_select_mirrored(struct bArmature *arm); +void armature_select_mirrored_ex(struct bArmature *arm, const int flag); void armature_select_mirrored(struct bArmature *arm); void armature_tag_unselect(struct bArmature *arm); diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index a8b5f888597..eedff896315 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -104,7 +104,7 @@ static void constraint_bone_name_fix(Object *ob, ListBase *conlist, const char * bConstraintTarget *ct; for (curcon = conlist->first; curcon; curcon = curcon->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); ListBase targets = {NULL, NULL}; /* constraint targets */ @@ -270,7 +270,7 @@ void ED_armature_bone_rename(bArmature *arm, const char *oldnamep, const char *n // XXX: the ID here is for armatures, but most bone drivers are actually on the object instead... { - BKE_all_animdata_fix_paths_rename(&arm->id, "pose.bones", oldname, newname); + BKE_animdata_fix_paths_rename_all(&arm->id, "pose.bones", oldname, newname); } /* correct view locking */ diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index c80953d6737..e7f036f6911 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -65,7 +65,9 @@ void ED_operatortypes_armature(void) WM_operatortype_append(ARMATURE_OT_shortest_path_pick); WM_operatortype_append(ARMATURE_OT_delete); + WM_operatortype_append(ARMATURE_OT_dissolve); WM_operatortype_append(ARMATURE_OT_duplicate); + WM_operatortype_append(ARMATURE_OT_symmetrize); WM_operatortype_append(ARMATURE_OT_extrude); WM_operatortype_append(ARMATURE_OT_hide); WM_operatortype_append(ARMATURE_OT_reveal); @@ -265,8 +267,9 @@ void ED_keymap_armature(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "ARMATURE_OT_shortest_path_pick", SELECTMOUSE, KM_PRESS, KM_CTRL, 0); - WM_keymap_add_item(keymap, "ARMATURE_OT_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ARMATURE_OT_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_armature_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_armature_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ARMATURE_OT_dissolve", XKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_move", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_extrude_forked", EKEY, KM_PRESS, KM_SHIFT, 0); @@ -275,7 +278,7 @@ void ED_keymap_armature(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "ARMATURE_OT_merge", MKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_split", YKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ARMATURE_OT_separate", PKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); + WM_keymap_add_item(keymap, "ARMATURE_OT_separate", PKEY, KM_PRESS, 0, 0); /* set flags */ WM_keymap_add_menu(keymap, "VIEW3D_MT_bone_options_toggle", WKEY, KM_PRESS, KM_SHIFT, 0); @@ -411,5 +414,6 @@ void ED_keymap_armature(wmKeyConfig *keyconf) /* menus */ WM_keymap_add_menu(keymap, "VIEW3D_MT_pose_specials", WKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "VIEW3D_MT_pose_propagate", PKEY, KM_PRESS, KM_ALT, 0); } diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 53989dd783c..8cda6f6db77 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -78,7 +78,7 @@ static void joined_armature_fix_links_constraints( bConstraint *con; for (con = lb->first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -380,7 +380,7 @@ int join_armature_exec(bContext *C, wmOperator *op) if (base->object->adt) { if (ob->adt == NULL) { /* no animdata, so just use a copy of the whole thing */ - ob->adt = BKE_copy_animdata(base->object->adt, false); + ob->adt = BKE_animdata_copy(base->object->adt, false); } else { /* merge in data - we'll fix the drivers manually */ @@ -391,7 +391,7 @@ int join_armature_exec(bContext *C, wmOperator *op) if (curarm->adt) { if (arm->adt == NULL) { /* no animdata, so just use a copy of the whole thing */ - arm->adt = BKE_copy_animdata(curarm->adt, false); + arm->adt = BKE_animdata_copy(curarm->adt, false); } else { /* merge in data - we'll fix the drivers manually */ @@ -435,7 +435,7 @@ static void separated_armature_fix_links(Object *origArm, Object *newArm) if (ob->type == OB_ARMATURE) { for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -473,7 +473,7 @@ static void separated_armature_fix_links(Object *origArm, Object *newArm) /* fix object-level constraints */ if (ob != origArm) { for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -548,7 +548,7 @@ static void separate_armature_bones(Object *ob, short sel) for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { if (ebo->parent == curbone) { ebo->parent = NULL; - ebo->temp = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */ + ebo->temp.p = NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */ ebo->flag &= ~BONE_CONNECTED; } } @@ -641,6 +641,9 @@ static int separate_armature_exec(bContext *C, wmOperator *op) ED_armature_to_edit(obedit->data); + /* parents tips remain selected when connected children are removed. */ + ED_armature_deselect_all(obedit); + BKE_report(op->reports, RPT_INFO, "Separated bones"); /* note, notifier might evolve */ @@ -660,6 +663,7 @@ void ARMATURE_OT_separate(wmOperatorType *ot) ot->description = "Isolate selected bones into a separate armature"; /* callbacks */ + ot->invoke = WM_operator_confirm; ot->exec = separate_armature_exec; ot->poll = ED_operator_editarmature; diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 581dd00e285..5aa2c628252 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -54,9 +54,9 @@ #include "armature_intern.h" -/* utility macros fro storing a temp int in the bone (selection flag) */ -#define EBONE_PREV_FLAG_GET(ebone) ((void)0, (GET_INT_FROM_POINTER((ebone)->temp))) -#define EBONE_PREV_FLAG_SET(ebone, val) ((ebone)->temp = SET_INT_IN_POINTER(val)) +/* utility macros for storing a temp int in the bone (selection flag) */ +#define EBONE_PREV_FLAG_GET(ebone) ((void)0, (ebone)->temp.i) +#define EBONE_PREV_FLAG_SET(ebone, val) ((ebone)->temp.i = val) /* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ @@ -388,61 +388,14 @@ static EditBone *get_nearest_editbonepoint(ViewContext *vc, const int mval[2], return NULL; } - - -/* toggle==0: deselect - * toggle==1: swap (based on test) - * toggle==2: swap (no test), CURRENTLY UNUSED - */ -void ED_armature_deselect_all(Object *obedit, int toggle) +void ED_armature_deselect_all(Object *obedit) { bArmature *arm = obedit->data; - EditBone *eBone; - int sel = 1; - - if (toggle == 1) { - /* Determine if there are any selected bones - * and therefore whether we are selecting or deselecting */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - // if (arm->layer & eBone->layer) { - if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)) { - sel = 0; - break; - } - // } - } - } - else { - sel = toggle; - } + EditBone *ebone; - /* Set the flags */ - for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - if (sel == 2) { - /* invert selection of bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } - } - else if (sel == 1) { - /* select bone */ - if (EBONE_VISIBLE(arm, eBone)) { - eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (eBone->parent) - eBone->parent->flag |= (BONE_TIPSEL); - } - } - else { - /* deselect bone */ - eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - if (arm->act_edbone == eBone) - arm->act_edbone = NULL; - } + for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - - ED_armature_sync_selection(arm->edbo); } void ED_armature_deselect_all_visible(Object *obedit) @@ -489,8 +442,9 @@ bool mouse_armature(bContext *C, const int mval[2], bool extend, bool deselect, nearBone = get_nearest_editbonepoint(&vc, mval, arm->edbo, 1, &selmask); if (nearBone) { - if (!extend && !deselect && !toggle) - ED_armature_deselect_all(obedit, 0); + if (!extend && !deselect && !toggle) { + ED_armature_deselect_all(obedit); + } /* by definition the non-root connected bones have no root point drawn, * so a root selection needs to be delivered to the parent tip */ @@ -731,7 +685,7 @@ static void armature_select_more_less(Object *ob, bool more) } } } - ebone->temp = NULL; + ebone->temp.p = NULL; } ED_armature_sync_selection(arm->edbo); @@ -962,75 +916,78 @@ void ARMATURE_OT_select_similar(wmOperatorType *ot) /* ********************* select hierarchy operator ************** */ -/* Get the first available child of an editbone */ -static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility) -{ - EditBone *curbone, *chbone = NULL; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - if (curbone->parent == pabone) { - if (use_visibility) { - if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A)) { - chbone = curbone; - } - } - else - chbone = curbone; - } - } - - return chbone; -} - static int armature_select_hierarchy_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); Object *ob; bArmature *arm; - EditBone *curbone, *pabone, *chbone; + EditBone *ebone_active; int direction = RNA_enum_get(op->ptr, "direction"); const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); + bool changed = false; ob = obedit; arm = (bArmature *)ob->data; - - for (curbone = arm->edbo->first; curbone; curbone = curbone->next) { - /* only work on bone if it is visible and its selection can change */ - if (EBONE_SELECTABLE(arm, curbone)) { - if (curbone == arm->act_edbone) { - if (direction == BONE_SELECT_PARENT) { - if (curbone->parent == NULL) continue; - else pabone = curbone->parent; - - if (EBONE_VISIBLE(arm, pabone)) { - pabone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = pabone; - if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL; - - if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - break; - } - + + ebone_active = arm->act_edbone; + if (ebone_active == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (ebone_active->parent) { + EditBone *ebone_parent; + + ebone_parent = ebone_active->parent; + + if (EBONE_SELECTABLE(arm, ebone_parent)) { + arm->act_edbone = ebone_parent; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); } - else { // BONE_SELECT_CHILD - chbone = editbone_get_child(arm, curbone, 1); - if (chbone == NULL) continue; - - if (EBONE_SELECTABLE(arm, chbone)) { - chbone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); - arm->act_edbone = chbone; - - if (!add_to_sel) { - curbone->flag &= ~(BONE_SELECTED | BONE_ROOTSEL); - if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL; + ED_armature_ebone_select_set(ebone_parent, true); + + changed = true; + } + } + + } + else { /* BONE_SELECT_CHILD */ + EditBone *ebone_iter, *ebone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (ebone_child == NULL); pass++) { + for (ebone_iter = arm->edbo->first; ebone_iter; ebone_iter = ebone_iter->next) { + /* possible we have multiple children, some invisible */ + if (EBONE_SELECTABLE(arm, ebone_iter)) { + if (ebone_iter->parent == ebone_active) { + if ((pass == 1) || (ebone_iter->flag & BONE_CONNECTED)) { + ebone_child = ebone_iter; + break; } - break; } } } } + + if (ebone_child) { + arm->act_edbone = ebone_child; + + if (!add_to_sel) { + ED_armature_ebone_select_set(ebone_active, false); + } + ED_armature_ebone_select_set(ebone_child, true); + + changed = true; + } } + if (changed == false) { + return OPERATOR_CANCELLED; + } + ED_armature_sync_selection(arm->edbo); WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index 97f69d86aee..09284860ec4 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -276,18 +276,19 @@ EditBone *ED_armature_bone_get_mirrored(const ListBase *edbo, EditBone *ebo) /* helper function for tools to work on mirrored parts. * it leaves mirrored bones selected then too, which is a good indication of what happened */ -void armature_select_mirrored(bArmature *arm) +void armature_select_mirrored_ex(bArmature *arm, const int flag) { + BLI_assert((flag & ~(BONE_SELECTED | BONE_ROOTSEL | BONE_TIPSEL)) == 0); /* Select mirrored bones */ if (arm->flag & ARM_MIRROR_EDIT) { EditBone *curBone, *ebone_mirr; for (curBone = arm->edbo->first; curBone; curBone = curBone->next) { if (arm->layer & curBone->layer) { - if (curBone->flag & BONE_SELECTED) { + if (curBone->flag & flag) { ebone_mirr = ED_armature_bone_get_mirrored(arm->edbo, curBone); if (ebone_mirr) - ebone_mirr->flag |= BONE_SELECTED; + ebone_mirr->flag |= (curBone->flag & flag); } } } @@ -295,6 +296,11 @@ void armature_select_mirrored(bArmature *arm) } +void armature_select_mirrored(bArmature *arm) +{ + armature_select_mirrored_ex(arm, BONE_SELECTED); +} + void armature_tag_select_mirrored(bArmature *arm) { EditBone *curBone; @@ -488,7 +494,7 @@ static void fix_bonelist_roll(ListBase *bonelist, ListBase *editbonelist) /* Find the associated editbone */ for (ebone = editbonelist->first; ebone; ebone = ebone->next) - if ((Bone *)ebone->temp == curBone) + if (ebone->temp.bone == curBone) break; if (ebone) { @@ -548,7 +554,7 @@ void ED_armature_from_edit(bArmature *arm) /* Copy the bones from the editData into the armature */ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { newBone = MEM_callocN(sizeof(Bone), "bone"); - eBone->temp = newBone; /* Associate the real Bones with the EditBones */ + eBone->temp.bone = newBone; /* Associate the real Bones with the EditBones */ BLI_strncpy(newBone->name, eBone->name, sizeof(newBone->name)); copy_v3_v3(newBone->arm_head, eBone->head); @@ -584,9 +590,9 @@ void ED_armature_from_edit(bArmature *arm) /* Fix parenting in a separate pass to ensure ebone->bone connections * are valid at this point */ for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { - newBone = (Bone *)eBone->temp; + newBone = eBone->temp.bone; if (eBone->parent) { - newBone->parent = (Bone *)eBone->parent->temp; + newBone->parent = eBone->parent->temp.bone; BLI_addtail(&newBone->parent->childbase, newBone); { @@ -695,24 +701,24 @@ static void ED_armature_ebone_listbase_copy(ListBase *lb_dst, ListBase *lb_src) if (ebone_dst->prop) { ebone_dst->prop = IDP_CopyProperty(ebone_dst->prop); } - ebone_src->temp = ebone_dst; + ebone_src->temp.ebone = ebone_dst; BLI_addtail(lb_dst, ebone_dst); } /* set pointers */ for (ebone_dst = lb_dst->first; ebone_dst; ebone_dst = ebone_dst->next) { if (ebone_dst->parent) { - ebone_dst->parent = ebone_dst->parent->temp; + ebone_dst->parent = ebone_dst->parent->temp.ebone; } } } -static void ED_armature_ebone_listbase_temp_clear(ListBase *lb) +void ED_armature_ebone_listbase_temp_clear(ListBase *lb) { EditBone *ebone; /* be sure they don't hang ever */ for (ebone = lb->first; ebone; ebone = ebone->next) { - ebone->temp = NULL; + ebone->temp.p = NULL; } } @@ -733,7 +739,7 @@ static void undoBones_to_editBones(void *uarmv, void *armv, void *UNUSED(data)) /* active bone */ if (uarm->act_edbone) { ebone = uarm->act_edbone; - arm->act_edbone = ebone->temp; + arm->act_edbone = ebone->temp.ebone; } else { arm->act_edbone = NULL; @@ -755,7 +761,7 @@ static void *editBones_to_undoBones(void *armv, void *UNUSED(obdata)) /* active bone */ if (arm->act_edbone) { ebone = arm->act_edbone; - uarm->act_edbone = ebone->temp; + uarm->act_edbone = ebone->temp.ebone; } ED_armature_ebone_listbase_temp_clear(&uarm->lb); diff --git a/source/blender/editors/armature/editarmature_retarget.c b/source/blender/editors/armature/editarmature_retarget.c index 5376fc8c79b..2235f9f92cc 100644 --- a/source/blender/editors/armature/editarmature_retarget.c +++ b/source/blender/editors/armature/editarmature_retarget.c @@ -707,7 +707,7 @@ static void RIG_reconnectControlBones(RigGraph *rg) /* DO SOME MAGIC HERE */ for (pchan = rg->ob->pose->chanbase.first; pchan; pchan = pchan->next) { for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -832,7 +832,7 @@ static void RIG_reconnectControlBones(RigGraph *rg) /* DO SOME MAGIC HERE */ for (pchan = rg->ob->pose->chanbase.first; pchan; pchan = pchan->next) { for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index b7d14e089cf..c630ea6478a 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -127,12 +127,12 @@ struct LaplacianSystem { static void laplacian_increase_edge_count(EdgeHash *edgehash, int v1, int v2) { - void **p = BLI_edgehash_lookup_p(edgehash, v1, v2); + void **p; - if (p) + if (BLI_edgehash_ensure_p(edgehash, v1, v2, &p)) *p = (void *)((intptr_t)*p + (intptr_t)1); else - BLI_edgehash_insert(edgehash, v1, v2, (void *)(intptr_t)1); + *p = (void *)((intptr_t)1); } static int laplacian_edge_count(EdgeHash *edgehash, int v1, int v2) @@ -1719,7 +1719,7 @@ static void meshdeform_matrix_solve(MeshDeformModifierData *mmd, MeshDeformBind /* sanity check */ for (b = 0; b < mdb->size3; b++) if (mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR) - if (fabs(mdb->totalphi[b] - 1.0f) > 1e-4) + if (fabsf(mdb->totalphi[b] - 1.0f) > 1e-4f) printf("totalphi deficiency [%s|%d] %d: %.10f\n", (mdb->tag[b] == MESHDEFORM_TAG_INTERIOR) ? "interior" : "boundary", mdb->semibound[b], mdb->varidx[b], mdb->totalphi[b]); #endif diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h index 820aedc5467..1412136c1a8 100644 --- a/source/blender/editors/armature/meshlaplacian.h +++ b/source/blender/editors/armature/meshlaplacian.h @@ -29,11 +29,9 @@ //#define RIGID_DEFORM -struct Scene; struct Object; struct Mesh; struct bDeformGroup; -struct MeshDeformModifierData; #ifdef RIGID_DEFORM struct EditMesh; diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 549a3854bac..e87c324d7ec 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -436,7 +436,7 @@ static void pose_copy_menu(Scene *scene) pchan->constflag |= pchanact->constflag; if (ob->pose) - ob->pose->flag |= POSE_RECALC; + BKE_pose_tag_recalc(bmain, ob->pose); } break; case 6: /* Transform Locks */ @@ -550,7 +550,7 @@ static void pose_copy_menu(Scene *scene) BKE_pose_update_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */ if (ob->pose) - ob->pose->flag |= POSE_RECALC; + BKE_pose_tag_recalc(bmain, ob->pose); } DAG_id_tag_update(&ob->id, OB_RECALC_DATA); // and all its relations @@ -862,7 +862,7 @@ static int pose_bone_layers_invoke(bContext *C, wmOperator *op, const wmEvent *e /* loop over the bits for this pchan's layers, adding layers where they're needed */ for (bit = 0; bit < 32; bit++) { - layers[bit] = (pchan->bone->layer & (1 << bit)) != 0; + layers[bit] = (pchan->bone->layer & (1u << bit)) != 0; } } CTX_DATA_END; @@ -936,8 +936,9 @@ static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEven /* loop over the bits for this pchan's layers, adding layers where they're needed */ for (bit = 0; bit < 32; bit++) { - if (ebone->layer & (1 << bit)) + if (ebone->layer & (1u << bit)) { layers[bit] = 1; + } } } CTX_DATA_END; diff --git a/source/blender/editors/armature/pose_group.c b/source/blender/editors/armature/pose_group.c index 413a74d24b5..4d9df06f33f 100644 --- a/source/blender/editors/armature/pose_group.c +++ b/source/blender/editors/armature/pose_group.c @@ -130,6 +130,7 @@ static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *U { Object *ob = ED_pose_object_from_context(C); bPose *pose; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type"); uiPopupMenu *pup; uiLayout *layout; @@ -140,6 +141,17 @@ static int pose_groups_menu_invoke(bContext *C, wmOperator *op, const wmEvent *U if (ELEM(NULL, ob, ob->pose)) return OPERATOR_CANCELLED; pose = ob->pose; + + /* If group index is set, try to use it! */ + if (RNA_property_is_set(op->ptr, prop)) { + const int num_groups = BLI_listbase_count(&pose->agroups); + const int group = RNA_property_int_get(op->ptr, prop); + + /* just use the active group index, and call the exec callback for the calling operator */ + if (group > 0 && group <= num_groups) { + return op->type->exec(C, op); + } + } /* if there's no active group (or active is invalid), create a new menu to find it */ if (pose->active_group <= 0) { diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 5d5a9bf363c..44470c1f827 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -62,7 +62,7 @@ #include "armature_intern.h" -/* utility macros fro storing a temp int in the bone (selection flag) */ +/* utility macros for storing a temp int in the bone (selection flag) */ #define PBONE_PREV_FLAG_GET(pchan) ((void)0, (GET_INT_FROM_POINTER((pchan)->temp))) #define PBONE_PREV_FLAG_SET(pchan, val) ((pchan)->temp = SET_INT_IN_POINTER(val)) @@ -458,7 +458,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op { if (pchan->bone->flag & BONE_SELECTED) { for (con = pchan->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; @@ -518,71 +518,67 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op) { Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); bArmature *arm = ob->data; - Bone *curbone, *pabone, *chbone; + bPoseChannel *pchan_act; int direction = RNA_enum_get(op->ptr, "direction"); const bool add_to_sel = RNA_boolean_get(op->ptr, "extend"); - bool found = false; + bool changed = false; - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - curbone = pchan->bone; - - if ((curbone->flag & BONE_UNSELECTABLE) == 0) { - if (curbone == arm->act_bone) { - if (direction == BONE_SELECT_PARENT) { - if (pchan->parent == NULL) continue; - else pabone = pchan->parent->bone; - - if (PBONE_SELECTABLE(arm, pabone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - pabone->flag |= BONE_SELECTED; - arm->act_bone = pabone; - - found = 1; - break; - } + pchan_act = BKE_pose_channel_active(ob); + if (pchan_act == NULL) { + return OPERATOR_CANCELLED; + } + + if (direction == BONE_SELECT_PARENT) { + if (pchan_act->parent) { + Bone *bone_parent; + bone_parent = pchan_act->parent->bone; + + if (PBONE_SELECTABLE(arm, bone_parent)) { + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; } - else { /* direction == BONE_SELECT_CHILD */ - /* the child member is only assigned to connected bones, see [#30340] */ -#if 0 - if (pchan->child == NULL) continue; - else chbone = pchan->child->bone; -#else - /* instead. find _any_ visible child bone, using the first one is a little arbitrary - campbell */ - chbone = pchan->child ? pchan->child->bone : NULL; - if (chbone == NULL) { - bPoseChannel *pchan_child; - - for (pchan_child = ob->pose->chanbase.first; pchan_child; pchan_child = pchan_child->next) { - /* possible we have multiple children, some invisible */ - if (PBONE_SELECTABLE(arm, pchan_child->bone)) { - if (pchan_child->parent == pchan) { - chbone = pchan_child->bone; - break; - } - } - } - } + bone_parent->flag |= BONE_SELECTED; + arm->act_bone = bone_parent; - if (chbone == NULL) continue; -#endif - - if (PBONE_SELECTABLE(arm, chbone)) { - if (!add_to_sel) curbone->flag &= ~BONE_SELECTED; - chbone->flag |= BONE_SELECTED; - arm->act_bone = chbone; - - found = 1; - break; + changed = true; + } + } + } + else { /* direction == BONE_SELECT_CHILD */ + bPoseChannel *pchan_iter; + Bone *bone_child = NULL; + int pass; + + /* first pass, only connected bones (the logical direct child) */ + for (pass = 0; pass < 2 && (bone_child == NULL); pass++) { + for (pchan_iter = ob->pose->chanbase.first; pchan_iter; pchan_iter = pchan_iter->next) { + /* possible we have multiple children, some invisible */ + if (PBONE_SELECTABLE(arm, pchan_iter->bone)) { + if (pchan_iter->parent == pchan_act) { + if ((pass == 1) || (pchan_iter->bone->flag & BONE_CONNECTED)) { + bone_child = pchan_iter->bone; + break; + } } } } } + + if (bone_child) { + arm->act_bone = bone_child; + + if (!add_to_sel) { + pchan_act->bone->flag &= ~BONE_SELECTED; + } + bone_child->flag |= BONE_SELECTED; + + changed = true; + } } - CTX_DATA_END; - if (found == 0) + if (changed == false) { return OPERATOR_CANCELLED; + } /* updates */ WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); @@ -825,7 +821,7 @@ static int pose_select_grouped_exec(bContext *C, wmOperator *op) break; default: - printf("pose_select_grouped() - Unknown selection type %d\n", type); + printf("pose_select_grouped() - Unknown selection type %u\n", type); break; } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 5f9f24d23a4..d583fd864b0 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -43,6 +43,7 @@ #include "BKE_context.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_unit.h" #include "RNA_access.h" #include "RNA_define.h" @@ -53,6 +54,7 @@ #include "ED_armature.h" #include "ED_keyframes_draw.h" #include "ED_markers.h" +#include "ED_numinput.h" #include "ED_screen.h" #include "armature_intern.h" @@ -98,6 +100,8 @@ typedef struct tPoseSlideOp { int flag; /* unused for now, but can later get used for storing runtime settings.... */ float percentage; /* 0-1 value for determining the influence of whatever is relevant */ + + NumInput num; /* numeric input */ } tPoseSlideOp; /* Pose Sliding Modes */ @@ -154,6 +158,12 @@ static int pose_slide_init(bContext *C, wmOperator *op, short mode) */ BLI_dlrbTree_init(&pso->keys); + /* initialise numeric input */ + initNumInput(&pso->num); + pso->num.idx_max = 0; /* one axis */ + pso->num.val_flag[0] |= NUM_NO_NEGATIVE; + pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ + /* return status is whether we've got all the data we were requested to get */ return 1; } @@ -201,6 +211,12 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val) /* next/end */ eVal = evaluate_fcurve(fcu, (float)pso->nextFrame); + /* if both values are equal, don't do anything */ + if (IS_EQF(sVal, eVal)) { + (*val) = sVal; + return; + } + /* calculate the relative weights of the endpoints */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* get weights from the percentage control */ @@ -234,7 +250,7 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val) * - perform this weighting a number of times given by the percentage... */ int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ - + while (iters-- > 0) { (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; } @@ -247,7 +263,7 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val) * - perform this weighting a number of times given by the percentage... */ int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ - + while (iters-- > 0) { (*val) = ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f; } @@ -320,6 +336,7 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) if (prop) { switch (RNA_property_type(prop)) { + /* continuous values that can be smoothly interpolated... */ case PROP_FLOAT: { float tval = RNA_property_float_get(&ptr, prop); @@ -327,8 +344,6 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) RNA_property_float_set(&ptr, prop, tval); break; } - case PROP_BOOLEAN: - case PROP_ENUM: case PROP_INT: { float tval = (float)RNA_property_int_get(&ptr, prop); @@ -336,6 +351,23 @@ static void pose_slide_apply_props(tPoseSlideOp *pso, tPChanFCurveLink *pfl) RNA_property_int_set(&ptr, prop, (int)tval); break; } + + /* values which can only take discrete values */ + case PROP_BOOLEAN: + { + float tval = (float)RNA_property_boolean_get(&ptr, prop); + pose_slide_apply_val(pso, fcu, &tval); + RNA_property_boolean_set(&ptr, prop, (int)tval); // XXX: do we need threshold clamping here? + break; + } + case PROP_ENUM: + { + /* don't handle this case - these don't usually represent interchangeable + * set of values which should be interpolated between + */ + break; + } + default: /* cannot handle */ //printf("Cannot Pose Slide non-numerical property\n"); @@ -404,11 +436,11 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) } else if (pso->mode == POSESLIDE_PUSH) { float quat_diff[4], quat_orig[4]; - + /* calculate the delta transform from the previous to the current */ /* TODO: investigate ways to favour one transform more? */ sub_qt_qtqt(quat_diff, pchan->quat, quat_prev); - + /* make a copy of the original rotation */ copy_qt_qt(quat_orig, pchan->quat); @@ -418,7 +450,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) else { float quat_interp[4], quat_orig[4]; int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ - + /* perform this blending several times until a satisfactory result is reached */ while (iters-- > 0) { /* calculate the interpolation between the endpoints */ @@ -512,7 +544,7 @@ static void pose_slide_reset(tPoseSlideOp *pso) /* draw percentage indicator in header */ static void pose_slide_draw_status(tPoseSlideOp *pso) { - char status_str[32]; + char status_str[256]; char mode_str[32]; switch (pso->mode) { @@ -532,7 +564,18 @@ static void pose_slide_draw_status(tPoseSlideOp *pso) break; } - BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(pso->percentage * 100.0f)); + if (hasNumInput(&pso->num)) { + Scene *scene = pso->scene; + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&pso->num, str_offs, &scene->unit); + + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", mode_str, str_offs); + } + else { + BLI_snprintf(status_str, sizeof(status_str), "%s: %d %%", mode_str, (int)(pso->percentage * 100.0f)); + } + ED_area_headerprint(pso->sa, status_str); } @@ -617,6 +660,7 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) { tPoseSlideOp *pso = op->customdata; wmWindow *win = CTX_wm_window(C); + const bool has_numinput = hasNumInput(&pso->num); switch (event->type) { case LEFTMOUSE: /* confirm */ @@ -656,25 +700,54 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: /* calculate new position */ { - /* calculate percentage based on position of mouse (we only use x-axis for now. - * since this is more convenient for users to do), and store new percentage value - */ - pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx); - RNA_float_set(op->ptr, "percentage", pso->percentage); - - /* update percentage indicator in header */ - pose_slide_draw_status(pso); - - /* reset transforms (to avoid accumulation errors) */ - pose_slide_reset(pso); - - /* apply... */ - pose_slide_apply(C, pso); + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* calculate percentage based on position of mouse (we only use x-axis for now. + * since this is more convenient for users to do), and store new percentage value + */ + pso->percentage = (event->x - pso->ar->winrct.xmin) / ((float)pso->ar->winx); + RNA_float_set(op->ptr, "percentage", pso->percentage); + + /* update percentage indicator in header */ + pose_slide_draw_status(pso); + + /* reset transforms (to avoid accumulation errors) */ + pose_slide_reset(pso); + + /* apply... */ + pose_slide_apply(C, pso); + } break; } - default: /* unhandled event (maybe it was some view manip? */ - /* allow to pass through */ - return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + default: + if ((event->val == KM_PRESS) && handleNumInput(C, &pso->num, event)) { + float value; + + /* Grab percentage from numeric input, and store this new value for redo + * NOTE: users see ints, while internally we use a 0-1 float + */ + value = pso->percentage * 100.0f; + applyNumInput(&pso->num, &value); + + pso->percentage = value / 100.0f; + CLAMP(pso->percentage, 0.0f, 1.0f); + RNA_float_set(op->ptr, "percentage", pso->percentage); + + /* update percentage indicator in header */ + pose_slide_draw_status(pso); + + /* reset transforms (to avoid accumulation errors) */ + pose_slide_reset(pso); + + /* apply... */ + pose_slide_apply(C, pso); + break; + } + else { + /* unhandled event - maybe it was some view manip? */ + /* allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } } /* still running... */ @@ -898,6 +971,8 @@ typedef enum ePosePropagate_Termination { /* stop when we run out of keyframes */ POSE_PROPAGATE_BEFORE_END, + /* only do on keyframes that are selected */ + POSE_PROPAGATE_SELECTED_KEYS, /* only do on the frames where markers are selected */ POSE_PROPAGATE_SELECTED_MARKERS } ePosePropagate_Termination; @@ -1098,13 +1173,20 @@ static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu, * since it may be as of yet unkeyed * - if starting before the starting frame, don't touch the key, as it may have had some valid * values + * - if only doing selected keyframes, start from the first one */ - match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); - - if (fcu->bezt[match].vec[1][0] < startFrame) - i = match + 1; - else - i = match; + if (mode != POSE_PROPAGATE_SELECTED_KEYS) { + match = binarysearch_bezt_index(fcu->bezt, startFrame, fcu->totvert, &keyExists); + + if (fcu->bezt[match].vec[1][0] < startFrame) + i = match + 1; + else + i = match; + } + else { + /* selected - start from first keyframe */ + i = 0; + } for (bezt = &fcu->bezt[i]; i < fcu->totvert; i++, bezt++) { /* additional termination conditions based on the operator 'mode' property go here... */ @@ -1137,6 +1219,11 @@ static void pose_propagate_fcurve(wmOperator *op, Object *ob, FCurve *fcu, if (ce == NULL) continue; } + else if (mode == POSE_PROPAGATE_SELECTED_KEYS) { + /* only allow if this keyframe is already selected - skip otherwise */ + if (BEZSELECTED(bezt) == 0) + continue; + } /* just flatten handles, since values will now be the same either side... */ /* TODO: perhaps a fade-out modulation of the value is required here (optional once again)? */ @@ -1219,12 +1306,20 @@ static int pose_propagate_exec(bContext *C, wmOperator *op) void POSE_OT_propagate(wmOperatorType *ot) { static EnumPropertyItem terminate_items[] = { - {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, - {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", "Propagate pose to first keyframe following the current frame only"}, - {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, - {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", "Propagate pose to all keyframes between current frame and 'Frame' property"}, - {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", "Propagate pose to all keyframes from current frame until no more are found"}, - {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"}, + {POSE_PROPAGATE_SMART_HOLDS, "WHILE_HELD", 0, "While Held", + "Propagate pose to all keyframes after current frame that don't change (Default behavior)"}, + {POSE_PROPAGATE_NEXT_KEY, "NEXT_KEY", 0, "To Next Keyframe", + "Propagate pose to first keyframe following the current frame only"}, + {POSE_PROPAGATE_LAST_KEY, "LAST_KEY", 0, "To Last Keyframe", + "Propagate pose to the last keyframe only (i.e. making action cyclic)"}, + {POSE_PROPAGATE_BEFORE_FRAME, "BEFORE_FRAME", 0, "Before Frame", + "Propagate pose to all keyframes between current frame and 'Frame' property"}, + {POSE_PROPAGATE_BEFORE_END, "BEFORE_END", 0, "Before Last Keyframe", + "Propagate pose to all keyframes from current frame until no more are found"}, + {POSE_PROPAGATE_SELECTED_KEYS, "SELECTED_KEYS", 0, "On Selected Keyframes", + "Propagate pose to all selected keyframes"}, + {POSE_PROPAGATE_SELECTED_MARKERS, "SELECTED_MARKERS", 0, "On Selected Markers", + "Propagate pose to all keyframes occurring on frames with Scene Markers after the current frame"}, {0, NULL, 0, NULL, NULL}}; /* identifiers */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index fbf6dccdb38..01e16df9f08 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -320,7 +320,7 @@ static bPoseChannel *pose_bone_do_paste(Object *ob, bPoseChannel *chan, const bo if (selOnly) paste_ok = ((pchan) && (pchan->bone->flag & BONE_SELECTED)); else - paste_ok = ((pchan != NULL)); + paste_ok = (pchan != NULL); /* continue? */ if (paste_ok) { diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index 0b449c4334d..1b877c0fba9 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -38,7 +38,6 @@ set(SRC editcurve.c editcurve_add.c editfont.c - lorem.c curve_intern.h ) diff --git a/source/blender/editors/curve/curve_intern.h b/source/blender/editors/curve/curve_intern.h index d54db77754f..579f149ea8b 100644 --- a/source/blender/editors/curve/curve_intern.h +++ b/source/blender/editors/curve/curve_intern.h @@ -38,9 +38,6 @@ struct EditNurb; struct Object; struct wmOperatorType; -/* lorem.c */ -extern const char ED_lorem[]; - /* editfont.c */ enum { DEL_ALL, DEL_NEXT_CHAR, DEL_PREV_CHAR, DEL_SELECTION, DEL_NEXT_SEL, DEL_PREV_SEL }; enum { CASE_LOWER, CASE_UPPER }; diff --git a/source/blender/editors/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c index 0f42dc923a4..4bcb16d31ef 100644 --- a/source/blender/editors/curve/curve_ops.c +++ b/source/blender/editors/curve/curve_ops.c @@ -54,7 +54,6 @@ void ED_operatortypes_curve(void) { WM_operatortype_append(FONT_OT_text_insert); WM_operatortype_append(FONT_OT_line_break); - WM_operatortype_append(FONT_OT_insert_lorem); WM_operatortype_append(FONT_OT_case_toggle); WM_operatortype_append(FONT_OT_case_set); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 936a40b83b6..1a10814bbf1 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -327,17 +327,17 @@ static void init_editNurb_keyIndex(EditNurb *editnurb, ListBase *origBase) editnurb->keyindex = gh; } -static CVKeyIndex *getCVKeyIndex(EditNurb *editnurb, void *cv) +static CVKeyIndex *getCVKeyIndex(EditNurb *editnurb, const void *cv) { return BLI_ghash_lookup(editnurb->keyindex, cv); } -static CVKeyIndex *popCVKeyIndex(EditNurb *editnurb, void *cv) +static CVKeyIndex *popCVKeyIndex(EditNurb *editnurb, const void *cv) { return BLI_ghash_popkey(editnurb->keyindex, cv, NULL); } -static BezTriple *getKeyIndexOrig_bezt(EditNurb *editnurb, BezTriple *bezt) +static BezTriple *getKeyIndexOrig_bezt(EditNurb *editnurb, const BezTriple *bezt) { CVKeyIndex *index = getCVKeyIndex(editnurb, bezt); @@ -370,7 +370,7 @@ static int getKeyIndexOrig_keyIndex(EditNurb *editnurb, void *cv) return index->key_index; } -static void keyIndex_delCV(EditNurb *editnurb, void *cv) +static void keyIndex_delCV(EditNurb *editnurb, const void *cv) { if (!editnurb->keyindex) { return; @@ -398,7 +398,7 @@ static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu) } if (nu->bezt) { - BezTriple *bezt = nu->bezt; + const BezTriple *bezt = nu->bezt; a = nu->pntsu; while (a--) { @@ -407,7 +407,7 @@ static void keyIndex_delNurb(EditNurb *editnurb, Nurb *nu) } } else { - BPoint *bp = nu->bp; + const BPoint *bp = nu->bp; a = nu->pntsu * nu->pntsv; while (a--) { @@ -1188,7 +1188,7 @@ static int *initialize_index_map(Object *obedit, int *r_old_totvert) while (a--) { keyIndex = getCVKeyIndex(editnurb, bezt); - if (keyIndex) { + if (keyIndex && keyIndex->vertex_index + 2 < old_totvert) { if (keyIndex->switched) { old_to_new_map[keyIndex->vertex_index] = vertex_index + 2; old_to_new_map[keyIndex->vertex_index + 1] = vertex_index + 1; @@ -1538,6 +1538,7 @@ void CURVE_OT_separate(wmOperatorType *ot) ot->description = "Separate selected points from connected unselected points into a new object"; /* api callbacks */ + ot->invoke = WM_operator_confirm; ot->exec = separate_exec; ot->poll = ED_operator_editsurfcurve; @@ -2526,7 +2527,7 @@ void CURVE_OT_radius_set(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, FLT_MAX, "Radius", "", 0.0001f, 10.0f); + RNA_def_float(ot->srna, "radius", 1.0f, 0.0f, OBJECT_ADD_SIZE_MAXF, "Radius", "", 0.0001f, 10.0f); } /********************* smooth operator ********************/ @@ -3746,7 +3747,7 @@ void CURVE_OT_subdivide(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of cuts", "", 1, 10); + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of cuts", "", 1, 10); /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -4881,8 +4882,9 @@ void CURVE_OT_spin(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float_vector_xyz(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX); - RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX, "Axis", "Axis in global view space", -1.0f, 1.0f); + RNA_def_float_vector_xyz(ot->srna, "center", 3, NULL, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, + "Center", "Center in global view space", -1000.0f, 1000.0f); + RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f); } /***************** extrude vertex operator **********************/ @@ -5351,7 +5353,8 @@ void CURVE_OT_vertex_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_float_vector_xyz(ot->srna, "location", 3, NULL, -FLT_MAX, FLT_MAX, "Location", "Location to add new vertex at", -1e4, 1e4); + RNA_def_float_vector_xyz(ot->srna, "location", 3, NULL, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, + "Location", "Location to add new vertex at", -1.0e4f, 1.0e4f); } /***************** extrude operator **********************/ @@ -6136,13 +6139,14 @@ void CURVE_OT_select_random(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly", 0.f, 100.0f); + RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, + "Percent", "Percentage of elements to select randomly", 0.0f, 100.0f); WM_operator_properties_select_action_simple(ot, SEL_SELECT); } /********************* every nth number of point *******************/ -static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth) +static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth, int skip, int offset) { int a, start; @@ -6151,7 +6155,8 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth) bezt = &nu->bezt[a - 1]; while (a--) { - if (abs(start - a) % nth) { + const int depth = abs(start - a); + if ((offset + depth) % (skip + nth) >= skip) { select_beztriple(bezt, DESELECT, SELECT, HIDDEN); } @@ -6159,10 +6164,10 @@ static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth) } } -static void select_nth_bp(Nurb *nu, BPoint *bp, int nth) +static void select_nth_bp(Nurb *nu, BPoint *bp, int nth, int skip, int offset) { int a, startrow, startpnt; - int dist, row, pnt; + int row, pnt; startrow = (bp - nu->bp) / nu->pntsu; startpnt = (bp - nu->bp) % nu->pntsu; @@ -6173,8 +6178,8 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, int nth) pnt = nu->pntsu - 1; while (a--) { - dist = abs(pnt - startpnt) + abs(row - startrow); - if (dist % nth) { + const int depth = abs(pnt - startpnt) + abs(row - startrow); + if ((offset + depth) % (skip + nth) >= skip) { select_bpoint(bp, DESELECT, SELECT, HIDDEN); } @@ -6188,7 +6193,7 @@ static void select_nth_bp(Nurb *nu, BPoint *bp, int nth) } } -bool ED_curve_select_nth(Curve *cu, int nth) +bool ED_curve_select_nth(Curve *cu, int nth, int skip, int offset) { Nurb *nu = NULL; void *vert = NULL; @@ -6197,10 +6202,10 @@ bool ED_curve_select_nth(Curve *cu, int nth) return false; if (nu->bezt) { - select_nth_bezt(nu, vert, nth); + select_nth_bezt(nu, vert, nth, skip, offset); } else { - select_nth_bp(nu, vert, nth); + select_nth_bp(nu, vert, nth, skip, offset); } return true; @@ -6209,9 +6214,14 @@ bool ED_curve_select_nth(Curve *cu, int nth) static int select_nth_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); - int nth = RNA_int_get(op->ptr, "nth"); + const int nth = RNA_int_get(op->ptr, "nth") - 1; + const int skip = RNA_int_get(op->ptr, "skip"); + int offset = RNA_int_get(op->ptr, "offset"); + + /* so input of offset zero ends up being (nth - 1) */ + offset = mod_i(offset, nth + skip); - if (!ED_curve_select_nth(obedit->data, nth)) { + if (!ED_curve_select_nth(obedit->data, nth, skip, offset)) { if (obedit->type == OB_SURF) { BKE_report(op->reports, RPT_ERROR, "Surface has not got active point"); } @@ -6242,6 +6252,8 @@ void CURVE_OT_select_nth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_int(ot->srna, "nth", 2, 2, INT_MAX, "Nth Selection", "", 2, 100); + RNA_def_int(ot->srna, "skip", 1, 1, INT_MAX, "Skip", "", 1, 100); + RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "", -100, 100); } /********************** add duplicate operator *********************/ diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 7c53896b969..555a53ae532 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -121,7 +121,7 @@ Nurb *add_nurbs_primitive(bContext *C, Object *obedit, float mat[4][4], int type 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 */ + const bool force_3d = (((Curve *)obedit->data)->flag & CU_3D) != 0; /* could be adding to an existing 3D curve */ unit_m4(umat); unit_m4(viewmat); @@ -488,9 +488,10 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) if (!isSurf) { /* adding curve */ if (obedit == NULL || obedit->type != OB_CURVE) { + const char *name = get_curve_defname(type); Curve *cu; - obedit = ED_object_add_type(C, OB_CURVE, loc, rot, true, layer); + obedit = ED_object_add_type(C, OB_CURVE, name, loc, rot, true, layer); newob = true; cu = (Curve *)obedit->data; @@ -505,7 +506,8 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } else { /* adding surface */ if (obedit == NULL || obedit->type != OB_SURF) { - obedit = ED_object_add_type(C, OB_SURF, loc, rot, true, layer); + const char *name = get_surf_defname(type); + obedit = ED_object_add_type(C, OB_SURF, name, loc, rot, true, layer); newob = true; } else { @@ -513,18 +515,6 @@ static int curvesurf_prim_add(bContext *C, wmOperator *op, int type, int isSurf) } } - /* rename here, the undo stack checks name for valid undo pushes */ - if (newob) { - if (obedit->type == OB_CURVE) { - rename_id((ID *)obedit, get_curve_defname(type)); - rename_id((ID *)obedit->data, get_curve_defname(type)); - } - else { - rename_id((ID *)obedit, get_surf_defname(type)); - rename_id((ID *)obedit->data, get_surf_defname(type)); - } - } - /* ED_object_add_type doesnt do an undo, is needed for redo operator on primitive */ if (newob && enter_editmode) ED_undo_push(C, "Enter Editmode"); diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index 8393acc6919..dd807828d9d 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -275,57 +275,6 @@ static void text_update_edited(bContext *C, Object *obedit, int mode) WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); } -/********************** insert lorem operator *********************/ - -static int insert_lorem_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *obedit = CTX_data_edit_object(C); - const char *p, *p2; - int i; - static const char *lastlorem = NULL; - - if (lastlorem) - p = lastlorem; - else - p = ED_lorem; - - i = rand() / (RAND_MAX / 6) + 4; - - for (p2 = p; *p2 && i; p2++) { - insert_into_textbuf(obedit, *p2); - - if (*p2 == '.') - i--; - } - - lastlorem = p2 + 1; - if (strlen(lastlorem) < 5) - lastlorem = ED_lorem; - - insert_into_textbuf(obedit, '\n'); - insert_into_textbuf(obedit, '\n'); - - DAG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); - - return OPERATOR_FINISHED; -} - -void FONT_OT_insert_lorem(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Insert Lorem"; - ot->description = "Insert placeholder text"; - ot->idname = "FONT_OT_insert_lorem"; - - /* api callbacks */ - ot->exec = insert_lorem_exec; - ot->poll = ED_operator_editfont; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* -------------------------------------------------------------------- */ /* Generic Paste Functions */ @@ -577,7 +526,7 @@ static void txt_add_object(bContext *C, TextLine *firstline, int totline, const int a; float rot[3] = {0.f, 0.f, 0.f}; - obedit = BKE_object_add(bmain, scene, OB_FONT); + obedit = BKE_object_add(bmain, scene, OB_FONT, NULL); base = scene->basact; /* seems to assume view align ? TODO - look into this, could be an operator option */ @@ -1417,7 +1366,7 @@ static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event) accentcode = 0; } else if (event->utf8_buf[0]) { - BLI_strncpy_wchar_from_utf8(inserted_text, event->utf8_buf, 2); + inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf); ascii = inserted_text[0]; insert_into_textbuf(obedit, ascii); accentcode = 0; diff --git a/source/blender/editors/curve/lorem.c b/source/blender/editors/curve/lorem.c deleted file mode 100644 index 59bf3f50dbc..00000000000 --- a/source/blender/editors/curve/lorem.c +++ /dev/null @@ -1,658 +0,0 @@ -/* - * ***** 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/curve/lorem.c - * \ingroup edcurve - */ - - -#include "BLI_sys_types.h" -#include "curve_intern.h" - -const char ED_lorem[] = { -76, 111, 114, 101, 109, 32, 105, 112, 115, 117, 109, 32, 100, 111, 108, 111, 114, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 99, -111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 32, 101, 108, 105, 116, 46, 32, 65, 108, 105, -113, 117, 97, 109, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 115, 101, 109, 46, 32, 78, 117, 108, -108, 97, 109, 32, 112, 114, 101, 116, 105, 117, 109, 44, 32, 116, 111, 114, 116, 111, 114, 32, 110, 111, 110, 32, 101, 117, 105, 115, 109, 111, 100, 32, -118, 97, 114, 105, 117, 115, 44, 32, 110, 117, 108, 108, 97, 32, 111, 100, 105, 111, 32, 115, 111, 100, 97, 108, 101, 115, 32, 110, 117, 108, 108, 97, -44, 32, 97, 116, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 108, 111, 114, 101, 109, 32, 109, 101, 116, 117, 115, 32, 115, 101, 100, 32, 110, 117, -108, 108, 97, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 105, 110, 32, 108, 101, 99, 116, 117, 115, 32, 97, 116, 32, 112, 101, 100, -101, 32, 98, 108, 97, 110, 100, 105, 116, 32, 118, 105, 118, 101, 114, 114, 97, 46, 32, 70, 117, 115, 99, 101, 32, 115, 99, 101, 108, 101, 114, 105, -115, 113, 117, 101, 32, 105, 112, 115, 117, 109, 32, 110, 101, 99, 32, 101, 110, 105, 109, 46, 32, 70, 117, 115, 99, 101, 32, 101, 117, 105, 115, 109, -111, 100, 32, 110, 117, 110, 99, 32, 105, 100, 32, 101, 110, 105, 109, 46, 32, 73, 110, 32, 118, 101, 110, 101, 110, 97, 116, 105, 115, 32, 99, 117, -114, 115, 117, 115, 32, 97, 114, 99, 117, 46, 32, 65, 101, 110, 101, 97, 110, 32, 113, 117, 105, 115, 32, 100, 117, 105, 46, 32, 77, 97, 101, 99, -101, 110, 97, 115, 32, 108, 97, 111, 114, 101, 101, 116, 46, 32, 78, 117, 108, 108, 97, 32, 116, 101, 109, 112, 111, 114, 44, 32, 97, 114, 99, 117, -32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 112, 114, 101, 116, 105, 117, 109, 32, 115, 117, 115, 99, 105, 112, 105, 116, 44, 32, 116, 111, 114, 116, -111, 114, 32, 119, 105, 115, 105, 32, 100, 97, 112, 105, 98, 117, 115, 32, 108, 105, 98, 101, 114, 111, 44, 32, 105, 100, 32, 111, 114, 110, 97, 114, -101, 32, 102, 101, 108, 105, 115, 32, 105, 112, 115, 117, 109, 32, 115, 117, 115, 99, 105, 112, 105, 116, 32, 112, 117, 114, 117, 115, 46, 32, 77, 97, -101, 99, 101, 110, 97, 115, 32, 105, 112, 115, 117, 109, 46, 32, 77, 111, 114, 98, 105, 32, 99, 117, 114, 115, 117, 115, 46, 32, 86, 101, 115, 116, -105, 98, 117, 108, 117, 109, 32, 100, 105, 97, 109, 32, 112, 117, 114, 117, 115, 44, 32, 99, 111, 109, 109, 111, 100, 111, 32, 101, 116, 44, 32, 99, -111, 110, 118, 97, 108, 108, 105, 115, 32, 101, 117, 44, 32, 112, 111, 115, 117, 101, 114, 101, 32, 97, 116, 44, 32, 108, 105, 103, 117, 108, 97, 46, -32, 78, 117, 108, 108, 97, 32, 97, 108, 105, 113, 117, 97, 109, 32, 97, 108, 105, 113, 117, 101, 116, 32, 108, 111, 114, 101, 109, 46, 32, 78, 117, -110, 99, 32, 101, 116, 32, 109, 97, 117, 114, 105, 115, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 101, 115, 116, 32, 98, 105, 98, 101, 110, -100, 117, 109, 32, 115, 117, 115, 99, 105, 112, 105, 116, 46, 32, 68, 111, 110, 101, 99, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, -32, 108, 105, 98, 101, 114, 111, 32, 101, 117, 32, 110, 105, 115, 108, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 101, 103, -101, 116, 32, 108, 105, 98, 101, 114, 111, 46, 32, 68, 111, 110, 101, 99, 32, 116, 101, 109, 112, 117, 115, 32, 105, 112, 115, 117, 109, 32, 115, 101, -100, 32, 113, 117, 97, 109, 46, 32, 83, 101, 100, 32, 98, 108, 97, 110, 100, 105, 116, 32, 110, 117, 110, 99, 32, 113, 117, 105, 115, 32, 101, 110, -105, 109, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 108, 101, 99, 116, 117, 115, 32, 100, 105, 97, 109, 44, 32, 97, 100, 105, 112, 105, 115, 99, -105, 110, 103, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 44, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 110, 111, 110, 44, 32, 112, 117, 108, -118, 105, 110, 97, 114, 32, 105, 100, 44, 32, 102, 101, 108, 105, 115, 46, 32, 73, 110, 32, 99, 111, 110, 103, 117, 101, 32, 109, 97, 103, 110, 97, -32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 117, 114, 110, 97, 46, 32, 78, 117, 110, 99, 32, 110, 111, 110, 32, 97, 117, 103, 117, 101, 32, 115, -101, 100, 32, 110, 105, 115, 108, 32, 100, 105, 99, 116, 117, 109, 32, 108, 97, 111, 114, 101, 101, 116, 46, 32, 67, 117, 109, 32, 115, 111, 99, 105, -105, 115, 32, 110, 97, 116, 111, 113, 117, 101, 32, 112, 101, 110, 97, 116, 105, 98, 117, 115, 32, 101, 116, 32, 109, 97, 103, 110, 105, 115, 32, 100, -105, 115, 32, 112, 97, 114, 116, 117, 114, 105, 101, 110, 116, 32, 109, 111, 110, 116, 101, 115, 44, 32, 110, 97, 115, 99, 101, 116, 117, 114, 32, 114, -105, 100, 105, 99, 117, 108, 117, 115, 32, 109, 117, 115, 46, 32, 73, 110, 32, 118, 101, 110, 101, 110, 97, 116, 105, 115, 32, 100, 97, 112, 105, 98, -117, 115, 32, 109, 97, 115, 115, 97, 46, 32, 78, 117, 108, 108, 97, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 115, 97, 112, 105, 101, 110, -32, 101, 116, 32, 113, 117, 97, 109, 46, 32, 78, 117, 110, 99, 32, 97, 99, 32, 109, 97, 103, 110, 97, 32, 108, 111, 98, 111, 114, 116, 105, 115, -32, 116, 101, 108, 108, 117, 115, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 112, 111, 115, 117, 101, 114, 101, 46, 32, 67, 114, 97, 115, 32, -97, 117, 103, 117, 101, 32, 109, 97, 117, 114, 105, 115, 44, 32, 109, 97, 116, 116, 105, 115, 32, 108, 111, 98, 111, 114, 116, 105, 115, 44, 32, 102, -101, 114, 109, 101, 110, 116, 117, 109, 32, 97, 116, 44, 32, 115, 101, 109, 112, 101, 114, 32, 97, 99, 44, 32, 116, 101, 108, 108, 117, 115, 46, 32, -67, 114, 97, 115, 32, 118, 105, 116, 97, 101, 32, 108, 105, 103, 117, 108, 97, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 115, 101, 109, 32, 112, -111, 115, 117, 101, 114, 101, 32, 105, 97, 99, 117, 108, 105, 115, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 99, 111, 110, 100, 105, 109, 101, 110, -116, 117, 109, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 102, 101, 108, 105, 115, 46, 32, 85, 116, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, -115, 97, 112, 105, 101, 110, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 112, 111, 116, 101, 110, 116, 105, 46, 32, 77, 97, 117, -114, 105, 115, 32, 117, 114, 110, 97, 46, 32, 85, 116, 32, 101, 117, 32, 101, 110, 105, 109, 32, 101, 117, 32, 97, 110, 116, 101, 32, 112, 111, 114, -116, 97, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 46, 32, 65, 101, 110, 101, 97, 110, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, -101, 32, 101, 115, 116, 32, 97, 99, 32, 102, 101, 108, 105, 115, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 97, 117, 99, 116, -111, 114, 46, 32, 78, 117, 110, 99, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 46, 32, 77, 111, 114, 98, 105, 32, 108, 97, 111, -114, 101, 101, 116, 32, 97, 110, 116, 101, 32, 101, 116, 32, 110, 105, 98, 104, 46, 32, 68, 111, 110, 101, 99, 32, 102, 101, 117, 103, 105, 97, 116, -32, 97, 114, 99, 117, 32, 101, 103, 101, 116, 32, 101, 110, 105, 109, 46, 32, 77, 111, 114, 98, 105, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, -116, 111, 114, 116, 111, 114, 32, 97, 99, 32, 105, 112, 115, 117, 109, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 108, 97, 99, 117, 115, 32, 97, -114, 99, 117, 44, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 97, 99, 44, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 118, 101, 108, 44, -32, 112, 111, 115, 117, 101, 114, 101, 32, 105, 100, 44, 32, 101, 115, 116, 46, 32, 80, 114, 111, 105, 110, 32, 99, 111, 109, 109, 111, 100, 111, 32, -103, 114, 97, 118, 105, 100, 97, 32, 115, 101, 109, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 118, -101, 104, 105, 99, 117, 108, 97, 32, 108, 105, 98, 101, 114, 111, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 119, 105, 115, 105, 46, 32, -77, 97, 101, 99, 101, 110, 97, 115, 32, 112, 114, 101, 116, 105, 117, 109, 32, 116, 101, 108, 108, 117, 115, 32, 101, 117, 32, 115, 97, 112, 105, 101, -110, 46, 32, 78, 117, 110, 99, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 110, 117, 110, 99, 46, 32, 73, 110, 32, 104, 97, 99, 32, 104, 97, -98, 105, 116, 97, 115, 115, 101, 32, 112, 108, 97, 116, 101, 97, 32, 100, 105, 99, 116, 117, 109, 115, 116, 46, 32, 65, 101, 110, 101, 97, 110, 32, -100, 105, 99, 116, 117, 109, 32, 110, 101, 113, 117, 101, 32, 115, 101, 100, 32, 116, 111, 114, 116, 111, 114, 46, 32, 68, 111, 110, 101, 99, 32, 101, -116, 32, 101, 114, 97, 116, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 97, 110, 116, 101, 32, 105, 112, 115, 117, 109, 32, 112, 114, -105, 109, 105, 115, 32, 105, 110, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 111, 114, 99, 105, 32, 108, 117, 99, 116, 117, 115, 32, 101, 116, 32, -117, 108, 116, 114, 105, 99, 101, 115, 32, 112, 111, 115, 117, 101, 114, 101, 32, 99, 117, 98, 105, 108, 105, 97, 32, 67, 117, 114, 97, 101, 59, 32, -83, 101, 100, 32, 106, 117, 115, 116, 111, 32, 116, 117, 114, 112, 105, 115, 44, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 32, 117, 116, -44, 32, 109, 97, 116, 116, 105, 115, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 111, 114, 110, 97, 114, 101, 32, 114, 117, 116, 114, 117, 109, -44, 32, 109, 97, 115, 115, 97, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 101, 110, 105, -109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 118, 101, 108, 105, 116, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 116, 101, 108, 108, 117, 115, -32, 105, 112, 115, 117, 109, 44, 32, 108, 117, 99, 116, 117, 115, 32, 117, 116, 44, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, -118, 105, 116, 97, 101, 44, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 110, 111, 110, 44, 32, 108, 105, 103, 117, 108, 97, 46, 32, 80, 104, -97, 115, 101, 108, 108, 117, 115, 32, 108, 97, 99, 105, 110, 105, 97, 32, 119, 105, 115, 105, 32, 97, 116, 32, 101, 115, 116, 46, 32, 68, 111, 110, -101, 99, 32, 101, 108, 105, 116, 32, 119, 105, 115, 105, 44, 32, 99, 111, 109, 109, 111, 100, 111, 32, 110, 111, 110, 44, 32, 112, 108, 97, 99, 101, -114, 97, 116, 32, 105, 110, 44, 32, 99, 111, 110, 118, 97, 108, 108, 105, 115, 32, 105, 100, 44, 32, 101, 108, 105, 116, 46, 32, 78, 117, 110, 99, -32, 100, 111, 108, 111, 114, 32, 100, 111, 108, 111, 114, 44, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 105, 100, 44, 32, 98, 105, 98, -101, 110, 100, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 108, 97, 99, 105, 110, 105, 97, 32, 105, 100, 44, 32, 101, 114, 97, 116, 46, 32, 67, -114, 97, 115, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 101, 114, 111, 115, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 115, -117, 115, 99, 105, 112, 105, 116, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 108, 101, 99, 116, 117, 115, 46, 32, 67, 108, 97, 115, 115, 32, 97, -112, 116, 101, 110, 116, 32, 116, 97, 99, 105, 116, 105, 32, 115, 111, 99, 105, 111, 115, 113, 117, 32, 97, 100, 32, 108, 105, 116, 111, 114, 97, 32, -116, 111, 114, 113, 117, 101, 110, 116, 32, 112, 101, 114, 32, 99, 111, 110, 117, 98, 105, 97, 32, 110, 111, 115, 116, 114, 97, 44, 32, 112, 101, 114, -32, 105, 110, 99, 101, 112, 116, 111, 115, 32, 104, 121, 109, 101, 110, 97, 101, 111, 115, 46, 32, 67, 114, 97, 115, 32, 111, 114, 99, 105, 46, 32, -80, 114, 97, 101, 115, 101, 110, 116, 32, 109, 97, 115, 115, 97, 32, 117, 114, 110, 97, 44, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 115, 101, -109, 112, 101, 114, 44, 32, 97, 117, 99, 116, 111, 114, 32, 97, 44, 32, 112, 114, 101, 116, 105, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 116, -101, 108, 108, 117, 115, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 97, 116, 32, 112, 117, 114, 117, 115, 46, 32, 77, 111, 114, 98, 105, -32, 116, 111, 114, 116, 111, 114, 32, 113, 117, 97, 109, 44, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 32, 118, 101, 110, 101, 110, 97, 116, 105, -115, 44, 32, 101, 103, 101, 115, 116, 97, 115, 32, 97, 44, 32, 99, 117, 114, 115, 117, 115, 32, 101, 117, 44, 32, 101, 115, 116, 46, 32, 78, 117, -110, 99, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 108, 101, 99, 116, 117, 115, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 108, 105, 98, 101, -114, 111, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 108, -105, 103, 117, 108, 97, 46, 32, 78, 117, 110, 99, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 112, 111, 115, 117, 101, 114, 101, 32, 97, 114, -99, 117, 46, 32, 77, 97, 117, 114, 105, 115, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 113, 117, 97, 109, 32, 97, 116, 32, 109, 97, 115, 115, -97, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 115, 111, 100, 97, 108, 101, 115, 32, 97, 108, 105, 113, 117, 101, 116, 32, 109, 97, 117, 114, 105, -115, 46, 32, 73, 110, 32, 105, 100, 32, 97, 110, 116, 101, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 118, 97, 114, 105, -117, 115, 32, 105, 112, 115, 117, 109, 32, 105, 110, 32, 97, 114, 99, 117, 46, 32, 70, 117, 115, 99, 101, 32, 109, 97, 117, 114, 105, 115, 32, 108, -97, 99, 117, 115, 44, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 97, 99, 44, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 113, 117, 105, -115, 44, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 108, 117, 99, 116, 117, 115, 44, 32, 112, 101, 100, 101, 46, 32, 83, 101, 100, 32, 119, 105, -115, 105, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 109, 97, 116, 116, 105, 115, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, -104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 115, 101, 109, 32, 110, 101, 99, 32, 112, 117, 114, 117, 115, 46, 32, 80, 114, 111, 105, 110, 32, 105, -100, 32, 113, 117, 97, 109, 46, 32, 67, 114, 97, 115, 32, 110, 101, 99, 32, 109, 97, 117, 114, 105, 115, 46, 32, 73, 110, 116, 101, 103, 101, 114, -32, 111, 114, 99, 105, 46, 32, 78, 117, 108, 108, 97, 109, 32, 100, 117, 105, 32, 115, 101, 109, 44, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, -115, 101, 100, 44, 32, 101, 103, 101, 115, 116, 97, 115, 32, 113, 117, 105, 115, 44, 32, 99, 117, 114, 115, 117, 115, 32, 105, 110, 44, 32, 109, 97, -103, 110, 97, 46, 32, 77, 97, 117, 114, 105, 115, 32, 110, 101, 113, 117, 101, 32, 108, 97, 99, 117, 115, 44, 32, 99, 111, 110, 115, 101, 99, 116, -101, 116, 117, 101, 114, 32, 110, 101, 99, 44, 32, 115, 97, 103, 105, 116, 116, 105, 115, 32, 101, 117, 44, 32, 112, 111, 114, 116, 116, 105, 116, 111, -114, 32, 101, 103, 101, 116, 44, 32, 100, 117, 105, 46, 32, 70, 117, 115, 99, 101, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 46, -32, 68, 111, 110, 101, 99, 32, 110, 101, 99, 32, 116, 101, 108, 108, 117, 115, 32, 113, 117, 105, 115, 32, 108, 101, 111, 32, 108, 111, 98, 111, 114, -116, 105, 115, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 46, 32, 69, 116, 105, 97, 109, 32, 109, 101, 116, 117, 115, 32, 117, 114, 110, -97, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, 112, 114, 101, 116, 105, 117, 109, 44, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 101, 117, 44, -32, 99, 117, 114, 115, 117, 115, 32, 117, 116, 44, 32, 116, 117, 114, 112, 105, 115, 46, 32, 77, 111, 114, 98, 105, 32, 98, 105, 98, 101, 110, 100, -117, 109, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, 108, 101, 99, 116, 117, 115, 46, 32, 83, 101, 100, 32, 110, 111, 110, 32, 97, 110, 116, 101, -32, 118, 105, 116, 97, 101, 32, 97, 114, 99, 117, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 116, 101, 109, 112, 111, 114, 46, -32, 70, 117, 115, 99, 101, 32, 115, 101, 100, 32, 108, 105, 103, 117, 108, 97, 32, 105, 110, 32, 115, 101, 109, 32, 116, 101, 109, 112, 111, 114, 32, -105, 109, 112, 101, 114, 100, 105, 101, 116, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 118, 101, 108, 32, 101, 115, 116, 46, 32, 80, 104, 97, 115, -101, 108, 108, 117, 115, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, -110, 105, 98, 104, 46, 32, 67, 108, 97, 115, 115, 32, 97, 112, 116, 101, 110, 116, 32, 116, 97, 99, 105, 116, 105, 32, 115, 111, 99, 105, 111, 115, -113, 117, 32, 97, 100, 32, 108, 105, 116, 111, 114, 97, 32, 116, 111, 114, 113, 117, 101, 110, 116, 32, 112, 101, 114, 32, 99, 111, 110, 117, 98, 105, -97, 32, 110, 111, 115, 116, 114, 97, 44, 32, 112, 101, 114, 32, 105, 110, 99, 101, 112, 116, 111, 115, 32, 104, 121, 109, 101, 110, 97, 101, 111, 115, -46, 32, 83, 101, 100, 32, 118, 101, 108, 32, 108, 101, 111, 32, 110, 101, 99, 32, 101, 114, 111, 115, 32, 98, 108, 97, 110, 100, 105, 116, 32, 105, -109, 112, 101, 114, 100, 105, 101, 116, 46, 32, 78, 117, 108, 108, 97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 83, 117, 115, 112, 101, 110, -100, 105, 115, 115, 101, 32, 108, 111, 98, 111, 114, 116, 105, 115, 44, 32, 100, 117, 105, 32, 117, 116, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, -32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 44, 32, 106, 117, 115, 116, 111, 32, 112, 117, 114, 117, 115, 32, 117, 108, 108, 97, 109, 99, 111, 114, -112, 101, 114, 32, 108, 105, 103, 117, 108, 97, 44, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 100, -111, 108, 111, 114, 32, 101, 110, 105, 109, 32, 105, 110, 32, 108, 105, 98, 101, 114, 111, 46, 32, 83, 101, 100, 32, 101, 108, 101, 109, 101, 110, 116, -117, 109, 44, 32, 112, 101, 100, 101, 32, 101, 103, 101, 116, 32, 112, 111, 114, 116, 97, 32, 99, 111, 110, 118, 97, 108, 108, 105, 115, 44, 32, 100, -117, 105, 32, 110, 117, 108, 108, 97, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 112, 101, 100, 101, 44, 32, 101, 103, 101, 116, 32, 118, 101, -104, 105, 99, 117, 108, 97, 32, 111, 100, 105, 111, 32, 97, 110, 116, 101, 32, 97, 116, 32, 115, 101, 109, 46, 32, 80, 101, 108, 108, 101, 110, 116, -101, 115, 113, 117, 101, 32, 104, 97, 98, 105, 116, 97, 110, 116, 32, 109, 111, 114, 98, 105, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 115, -101, 110, 101, 99, 116, 117, 115, 32, 101, 116, 32, 110, 101, 116, 117, 115, 32, 101, 116, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 102, 97, -109, 101, 115, 32, 97, 99, 32, 116, 117, 114, 112, 105, 115, 32, 101, 103, 101, 115, 116, 97, 115, 46, 32, 83, 101, 100, 32, 117, 108, 108, 97, 109, -99, 111, 114, 112, 101, 114, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 105, 112, 115, 117, 109, 46, 32, 70, 117, 115, 99, 101, 32, 114, 105, -115, 117, 115, 32, 110, 105, 98, 104, 44, 32, 97, 99, 99, 117, 109, 115, 97, 110, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 116, 101, 109, -112, 117, 115, 32, 101, 103, 101, 116, 44, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 97, 99, 44, 32, 110, 101, 113, 117, 101, 46, 32, 83, -101, 100, 32, 113, 117, 105, 115, 32, 108, 111, 114, 101, 109, 32, 117, 116, 32, 116, 111, 114, 116, 111, 114, 32, 102, 97, 99, 105, 108, 105, 115, 105, -115, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 46, 32, 70, 117, 115, 99, 101, 32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 113, 117, 97, 109, -32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 105, 112, 115, 117, 109, 46, 32, 77, 111, 114, 98, 105, 32, 97, 99, 32, 101, 108, 105, 116, 32, 113, -117, 105, 115, 32, 116, 101, 108, 108, 117, 115, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 98, 108, 97, 110, 100, 105, 116, 46, 32, 77, 97, -101, 99, 101, 110, 97, 115, 32, 115, 117, 115, 99, 105, 112, 105, 116, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 115, 101, 109, -46, 32, 78, 97, 109, 32, 115, 101, 100, 32, 101, 114, 111, 115, 32, 118, 101, 108, 32, 108, 97, 99, 117, 115, 32, 108, 111, 98, 111, 114, 116, 105, -115, 32, 99, 111, 110, 103, 117, 101, 46, 32, 80, 114, 111, 105, 110, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 110, 117, 110, 99, 32, 108, 111, -98, 111, 114, 116, 105, 115, 32, 111, 114, 99, 105, 46, 32, 68, 111, 110, 101, 99, 32, 101, 103, 101, 115, 116, 97, 115, 32, 101, 110, 105, 109, 32, -101, 117, 32, 111, 100, 105, 111, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 105, 100, 32, 109, 101, 116, 117, 115, 46, 32, 80, 101, -108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 97, 117, 99, 116, 111, 114, 44, 32, 115, 101, 109, 32, 118, 97, 114, 105, 117, 115, 32, 108, 117, -99, 116, 117, 115, 32, 116, 101, 109, 112, 117, 115, 44, 32, 108, 105, 98, 101, 114, 111, 32, 109, 97, 103, 110, 97, 32, 99, 117, 114, 115, 117, 115, -32, 110, 101, 113, 117, 101, 44, 32, 101, 116, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 100, 105, 97, 109, 32, 100, 105, 97, 109, 32, 113, -117, 105, 115, 32, 112, 117, 114, 117, 115, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 100, -111, 108, 111, 114, 46, 32, 78, 117, 108, 108, 97, 32, 105, 110, 32, 109, 97, 103, 110, 97, 46, 32, 67, 114, 97, 115, 32, 105, 100, 32, 100, 105, -97, 109, 32, 97, 116, 32, 108, 101, 99, 116, 117, 115, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 112, 108, 97, 99, 101, 114, 97, 116, 46, 32, -78, 117, 110, 99, 32, 112, 111, 114, 116, 97, 32, 112, 111, 115, 117, 101, 114, 101, 32, 115, 97, 112, 105, 101, 110, 46, 32, 69, 116, 105, 97, 109, -32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 46, 32, 67, 108, 97, 115, 115, 32, 97, 112, 116, 101, 110, 116, 32, 116, 97, 99, 105, 116, -105, 32, 115, 111, 99, 105, 111, 115, 113, 117, 32, 97, 100, 32, 108, 105, 116, 111, 114, 97, 32, 116, 111, 114, 113, 117, 101, 110, 116, 32, 112, 101, -114, 32, 99, 111, 110, 117, 98, 105, 97, 32, 110, 111, 115, 116, 114, 97, 44, 32, 112, 101, 114, 32, 105, 110, 99, 101, 112, 116, 111, 115, 32, 104, -121, 109, 101, 110, 97, 101, 111, 115, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 109, -105, 32, 115, 101, 100, 32, 116, 111, 114, 116, 111, 114, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, -100, 105, 97, 109, 32, 110, 111, 110, 32, 117, 114, 110, 97, 46, 32, 68, 111, 110, 101, 99, 32, 108, 117, 99, 116, 117, 115, 32, 112, 104, 97, 114, -101, 116, 114, 97, 32, 116, 101, 108, 108, 117, 115, 46, 32, 80, 114, 111, 105, 110, 32, 97, 99, 32, 105, 112, 115, 117, 109, 32, 101, 103, 101, 116, -32, 108, 105, 98, 101, 114, 111, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 118, 111, 108, 117, 116, 112, 97, 116, 46, 32, 67, 114, 97, 115, 32, -105, 100, 32, 116, 101, 108, 108, 117, 115, 46, 32, 78, 117, 108, 108, 97, 32, 110, 111, 110, 32, 114, 105, 115, 117, 115, 46, 32, 73, 110, 32, 100, -111, 108, 111, 114, 46, 32, 70, 117, 115, 99, 101, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 32, 113, 117, 97, 109, 32, 105, 110, 32, -109, 97, 115, 115, 97, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 112, 111, 114, 116, 97, 46, 32, 77, 111, 114, 98, 105, 32, 117, 116, 32, 100, -111, 108, 111, 114, 32, 101, 117, 32, 109, 97, 115, 115, 97, 32, 101, 103, 101, 115, 116, 97, 115, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, -109, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 46, 32, 73, 110, 32, 104, 97, 99, 32, 104, 97, 98, 105, -116, 97, 115, 115, 101, 32, 112, 108, 97, 116, 101, 97, 32, 100, 105, 99, 116, 117, 109, 115, 116, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, -115, 101, 32, 112, 111, 116, 101, 110, 116, 105, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, 108, 101, 111, 32, -105, 110, 32, 116, 111, 114, 116, 111, 114, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 101, 108, 32, 101, 114, 97, 116, 32, 97, 32, 117, 114, 110, -97, 32, 108, 97, 111, 114, 101, 101, 116, 32, 115, 101, 109, 112, 101, 114, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 117, -116, 32, 109, 101, 116, 117, 115, 32, 97, 99, 32, 116, 101, 108, 108, 117, 115, 32, 99, 111, 109, 109, 111, 100, 111, 32, 101, 108, 101, 105, 102, 101, -110, 100, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 113, 117, 105, 115, 32, 117, 114, 110, 97, 46, 32, 67, 117, 114, 97, 98, -105, 116, 117, 114, 32, 108, 97, 99, 105, 110, 105, 97, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 100, 117, 105, 46, 32, 78, 97, 109, 32, -110, 101, 99, 32, 97, 110, 116, 101, 46, 32, 73, 110, 32, 105, 100, 32, 101, 110, 105, 109, 46, 32, 65, 101, 110, 101, 97, 110, 32, 109, 97, 116, -116, 105, 115, 32, 101, 110, 105, 109, 46, 32, 73, 110, 32, 117, 116, 32, 110, 101, 113, 117, 101, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, -114, 105, 115, 117, 115, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 46, 32, 83, 117, 115, 112, 101, -110, 100, 105, 115, 115, 101, 32, 112, 111, 116, 101, 110, 116, 105, 46, 32, 85, 116, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 108, 101, -99, 116, 117, 115, 32, 118, 105, 116, 97, 101, 32, 116, 111, 114, 116, 111, 114, 46, 32, 68, 117, 105, 115, 32, 118, 101, 108, 105, 116, 46, 32, 78, -117, 108, 108, 97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, -117, 114, 110, 97, 46, 32, 67, 114, 97, 115, 32, 118, 97, 114, 105, 117, 115, 32, 116, 111, 114, 116, 111, 114, 32, 105, 110, 32, 112, 101, 100, 101, -46, 32, 83, 101, 100, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 108, 97, 99, 105, 110, 105, -97, 32, 108, 105, 98, 101, 114, 111, 32, 110, 101, 99, 32, 110, 105, 98, 104, 46, 32, 68, 111, 110, 101, 99, 32, 97, 108, 105, 113, 117, 97, 109, -32, 114, 105, 115, 117, 115, 32, 110, 111, 110, 32, 110, 105, 115, 108, 46, 32, 78, 97, 109, 32, 97, 32, 110, 117, 110, 99, 32, 101, 116, 32, 102, -101, 108, 105, 115, 32, 116, 101, 109, 112, 111, 114, 32, 102, 101, 117, 103, 105, 97, 116, 46, 32, 78, 117, 110, 99, 32, 109, 101, 116, 117, 115, 46, -32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 101, 117, 105, 115, 109, 111, 100, 44, 32, 109, 101, 116, 117, 115, 32, 105, 110, 32, 115, 101, -109, 112, 101, 114, 32, 108, 97, 111, 114, 101, 101, 116, 44, 32, 117, 114, 110, 97, 32, 105, 112, 115, 117, 109, 32, 112, 104, 97, 114, 101, 116, 114, -97, 32, 108, 111, 114, 101, 109, 44, 32, 115, 101, 100, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 109, 97, 103, 110, 97, 32, 108, 111, 114, -101, 109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 119, 105, 115, 105, 46, 32, 83, 101, 100, 32, 119, 105, 115, 105, 46, 32, 78, 117, 108, 108, -97, 109, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 101, 108, 105, 116, 32, 115, 101, 100, 32, 110, 105, 115, 108, 46, 32, 80, 104, 97, 115, -101, 108, 108, 117, 115, 32, 109, 97, 116, 116, 105, 115, 32, 108, 101, 111, 32, 110, 101, 99, 32, 109, 97, 115, 115, 97, 46, 32, 65, 101, 110, 101, -97, 110, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 46, 32, 67, 114, 97, 115, 32, 119, 105, 115, 105, 32, 101, 114, 97, 116, 44, 32, 108, 111, -98, 111, 114, 116, 105, 115, 32, 110, 101, 99, 44, 32, 99, 117, 114, 115, 117, 115, 32, 101, 103, 101, 116, 44, 32, 108, 111, 98, 111, 114, 116, 105, -115, 32, 97, 116, 44, 32, 108, 105, 98, 101, 114, 111, 46, 32, 73, 110, 32, 109, 97, 115, 115, 97, 32, 110, 105, 115, 108, 44, 32, 114, 117, 116, -114, 117, 109, 32, 110, 111, 110, 44, 32, 99, 117, 114, 115, 117, 115, 32, 110, 101, 99, 44, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 115, 101, -100, 44, 32, 108, 97, 99, 117, 115, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, -46, 32, 67, 114, 97, 115, 32, 101, 117, 105, 115, 109, 111, 100, 44, 32, 110, 101, 113, 117, 101, 32, 97, 99, 32, 115, 117, 115, 99, 105, 112, 105, -116, 32, 116, 101, 109, 112, 117, 115, 44, 32, 118, 101, 108, 105, 116, 32, 108, 111, 114, 101, 109, 32, 108, 117, 99, 116, 117, 115, 32, 101, 108, 105, -116, 44, 32, 100, 97, 112, 105, 98, 117, 115, 32, 114, 104, 111, 110, 99, 117, 115, 32, 119, 105, 115, 105, 32, 101, 115, 116, 32, 117, 116, 32, 115, -101, 109, 46, 32, 80, 114, 111, 105, 110, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 101, 110, 105, 109, 32, 105, 110, 32, 101, 114, 111, 115, -32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 97, 99, 99, 117, 109, 115, 97, 110, 46, 32, 85, 116, 32, 108, 111, 114, 101, 109, 32, 110, 105, -115, 108, 44, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 101, 116, 44, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 110, 101, 99, 44, 32, -108, 97, 99, 105, 110, 105, 97, 32, 105, 110, 44, 32, 100, 111, 108, 111, 114, 46, 32, 68, 117, 105, 115, 32, 110, 101, 99, 32, 113, 117, 97, 109, -46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 118, 101, 108, 105, 116, 32, 102, 101, 108, 105, 115, 44, 32, 112, 111, 115, 117, 101, 114, 101, 32, -105, 100, 44, 32, 108, 117, 99, 116, 117, 115, 32, 113, 117, 105, 115, 44, 32, 108, 97, 111, 114, 101, 101, 116, 32, 117, 116, 44, 32, 105, 112, 115, -117, 109, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 101, 103, 101, 116, 32, 97, 114, 99, 117, 46, 32, 77, 97, 117, 114, 105, 115, 32, 109, -97, 115, 115, 97, 32, 102, 101, 108, 105, 115, 44, 32, 111, 114, 110, 97, 114, 101, 32, 110, 111, 110, 44, 32, 117, 108, 116, 114, 105, 99, 101, 115, -32, 105, 100, 44, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 105, 110, 44, 32, 101, 108, 105, 116, 46, 32, 80, 101, 108, 108, 101, 110, 116, -101, 115, 113, 117, 101, 32, 104, 97, 98, 105, 116, 97, 110, 116, 32, 109, 111, 114, 98, 105, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 115, -101, 110, 101, 99, 116, 117, 115, 32, 101, 116, 32, 110, 101, 116, 117, 115, 32, 101, 116, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 102, 97, -109, 101, 115, 32, 97, 99, 32, 116, 117, 114, 112, 105, 115, 32, 101, 103, 101, 115, 116, 97, 115, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, -32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 44, 32, 109, 105, 32, 97, 99, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 102, 97, 99, 105, 108, -105, 115, 105, 115, 44, 32, 108, 105, 98, 101, 114, 111, 32, 101, 110, 105, 109, 32, 115, 117, 115, 99, 105, 112, 105, 116, 32, 108, 101, 111, 44, 32, -110, 111, 110, 32, 110, 111, 110, 117, 109, 109, 121, 32, 112, 101, 100, 101, 32, 100, 105, 97, 109, 32, 101, 103, 101, 116, 32, 110, 105, 98, 104, 46, -32, 83, 101, 100, 32, 116, 101, 109, 112, 117, 115, 46, 32, 65, 101, 110, 101, 97, 110, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 115, 117, 115, -99, 105, 112, 105, 116, 32, 100, 117, 105, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 101, 114, 97, 116, 32, 118, 111, 108, 117, 116, 112, 97, 116, -46, 32, 85, 116, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 46, 32, 78, 97, 109, 32, 99, 111, 109, 109, 111, 100, 111, 44, 32, 110, 117, 108, -108, 97, 32, 117, 116, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 114, 117, 116, 114, 117, 109, 44, 32, 111, 114, 99, 105, 32, 101, 108, 105, -116, 32, 118, 105, 118, 101, 114, 114, 97, 32, 100, 105, 97, 109, 44, 32, 118, 105, 116, 97, 101, 32, 109, 111, 108, 108, 105, 115, 32, 111, 100, 105, -111, 32, 111, 100, 105, 111, 32, 101, 116, 32, 112, 117, 114, 117, 115, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 101, 114, 97, 116, 32, 118, 111, -108, 117, 116, 112, 97, 116, 46, 32, 83, 101, 100, 32, 97, 108, 105, 113, 117, 101, 116, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 105, 112, 115, -117, 109, 46, 32, 73, 110, 32, 117, 116, 32, 101, 115, 116, 46, 32, 69, 116, 105, 97, 109, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, 109, -46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 116, 111, 114, 116, 111, 114, -32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 108, 97, 99, 117, 115, 46, 32, 65, 101, 110, 101, 97, 110, 32, 111, 114, 99, 105, 46, 32, 83, 117, -115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 108, 97, 99, 117, 115, 32, 110, 117, 108, 108, 97, 44, 32, 110, 111, 110, 117, 109, 109, 121, 32, 97, -44, 32, 100, 105, 99, 116, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 101, 103, 101, 115, 116, 97, 115, 32, 113, 117, 105, 115, 44, 32, 101, 114, -111, 115, 46, 32, 68, 111, 110, 101, 99, 32, 97, 117, 99, 116, 111, 114, 32, 103, 114, 97, 118, 105, 100, 97, 32, 110, 105, 115, 108, 46, 32, 67, -114, 97, 115, 32, 97, 99, 32, 101, 115, 116, 32, 114, 117, 116, 114, 117, 109, 32, 97, 117, 103, 117, 101, 32, 112, 117, 108, 118, 105, 110, 97, 114, -32, 111, 114, 110, 97, 114, 101, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 109, 97, 117, 114, 105, 115, 32, 110, 105, 98, 104, 44, 32, -118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 105, 110, 44, 32, 114, 104, 111, 110, 99, 117, 115, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 44, -32, 100, 97, 112, 105, 98, 117, 115, 32, 101, 103, 101, 116, 44, 32, 108, 97, 99, 117, 115, 46, 32, 78, 97, 109, 32, 110, 117, 110, 99, 32, 109, -97, 117, 114, 105, 115, 44, 32, 115, 117, 115, 99, 105, 112, 105, 116, 32, 97, 116, 44, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 97, 44, -32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 97, 116, 44, 32, 116, 101, 108, 108, 117, 115, 46, 32, 78, 97, 109, 32, 97, 99, 99, 117, 109, -115, 97, 110, 32, 109, 111, 108, 108, 105, 115, 32, 108, 105, 98, 101, 114, 111, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 99, 111, 110, 100, 105, -109, 101, 110, 116, 117, 109, 32, 109, 97, 116, 116, 105, 115, 32, 101, 115, 116, 46, 32, 68, 111, 110, 101, 99, 32, 108, 97, 99, 117, 115, 46, 32, -78, 117, 108, 108, 97, 109, 32, 97, 99, 32, 115, 97, 112, 105, 101, 110, 32, 105, 100, 32, 109, 97, 115, 115, 97, 32, 108, 111, 98, 111, 114, 116, -105, 115, 32, 109, 111, 108, 101, 115, 116, 105, 101, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 101, 108, 101, 109, 101, 110, -116, 117, 109, 46, 32, 80, 114, 111, 105, 110, 32, 117, 116, 32, 112, 117, 114, 117, 115, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 101, 116, 32, -115, 97, 112, 105, 101, 110, 32, 113, 117, 105, 115, 32, 116, 117, 114, 112, 105, 115, 32, 99, 111, 109, 109, 111, 100, 111, 32, 109, 111, 108, 108, 105, -115, 46, 32, 78, 117, 108, 108, 97, 32, 99, 111, 110, 115, 101, 113, 117, 97, 116, 46, 32, 80, 114, 111, 105, 110, 32, 97, 32, 119, 105, 115, 105, -32, 117, 116, 32, 116, 101, 108, 108, 117, 115, 32, 98, 108, 97, 110, 100, 105, 116, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 46, 32, 65, 108, -105, 113, 117, 97, 109, 32, 110, 117, 108, 108, 97, 32, 108, 111, 114, 101, 109, 44, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 97, 99, 44, 32, -109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 118, 101, 108, 44, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 101, 116, 44, 32, 109, 101, 116, -117, 115, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 101, 103, 101, 115, 116, 97, 115, 32, 110, 105, 98, 104, 32, 101, 116, 32, 108, 105, -103, 117, 108, 97, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 100, 105, 97, 109, 32, 111, 100, 105, 111, 44, 32, 108, 97, 99, 105, 110, 105, 97, -32, 113, 117, 105, 115, 44, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 113, 117, 105, 115, 44, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, -100, 105, 110, 32, 117, 116, 44, 32, 101, 114, 111, 115, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 97, 108, 105, 113, 117, 101, 116, 32, -108, 111, 114, 101, 109, 32, 97, 99, 32, 105, 112, 115, 117, 109, 46, 32, 83, 101, 100, 32, 99, 117, 114, 115, 117, 115, 32, 116, 101, 108, 108, 117, -115, 32, 97, 99, 32, 111, 114, 99, 105, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 97, 116, 32, 110, 117, 108, 108, 97, 46, 32, 68, -111, 110, 101, 99, 32, 112, 111, 114, 116, 97, 32, 115, 111, 100, 97, 108, 101, 115, 32, 97, 110, 116, 101, 46, 32, 80, 101, 108, 108, 101, 110, 116, -101, 115, 113, 117, 101, 32, 104, 97, 98, 105, 116, 97, 110, 116, 32, 109, 111, 114, 98, 105, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 115, -101, 110, 101, 99, 116, 117, 115, 32, 101, 116, 32, 110, 101, 116, 117, 115, 32, 101, 116, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 102, 97, -109, 101, 115, 32, 97, 99, 32, 116, 117, 114, 112, 105, 115, 32, 101, 103, 101, 115, 116, 97, 115, 46, 32, 83, 101, 100, 32, 105, 100, 32, 108, 101, -99, 116, 117, 115, 32, 97, 116, 32, 109, 97, 115, 115, 97, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 116, 114, 105, 115, 116, 105, -113, 117, 101, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 108, 97, 99, 117, 115, -46, 32, 73, 110, 32, 104, 97, 99, 32, 104, 97, 98, 105, 116, 97, 115, 115, 101, 32, 112, 108, 97, 116, 101, 97, 32, 100, 105, 99, 116, 117, 109, -115, 116, 46, 32, 78, 117, 110, 99, 32, 110, 111, 110, 32, 116, 117, 114, 112, 105, 115, 46, 32, 83, 101, 100, 32, 115, 97, 103, 105, 116, 116, 105, -115, 46, 32, 77, 111, 114, 98, 105, 32, 108, 97, 111, 114, 101, 101, 116, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 32, 100, 117, 105, -46, 32, 78, 97, 109, 32, 97, 114, 99, 117, 32, 116, 101, 108, 108, 117, 115, 44, 32, 116, 101, 109, 112, 111, 114, 32, 118, 105, 116, 97, 101, 44, -32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 101, 116, 44, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 32, 117, 116, 44, 32, 118, 101, -108, 105, 116, 46, 32, 73, 110, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 97, 117, 103, 117, 101, 32, 97, 32, 97, 114, 99, 117, 32, 118, 111, -108, 117, 116, 112, 97, 116, 32, 115, 117, 115, 99, 105, 112, 105, 116, 46, 32, 68, 111, 110, 101, 99, 32, 100, 105, 99, 116, 117, 109, 32, 117, 108, -116, 114, 105, 99, 101, 115, 32, 108, 101, 99, 116, 117, 115, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 97, 32, 112, 117, 114, 117, 115, -32, 101, 116, 32, 111, 114, 99, 105, 32, 100, 105, 99, 116, 117, 109, 32, 108, 97, 99, 105, 110, 105, 97, 46, 32, 85, 116, 32, 108, 101, 111, 46, -32, 67, 114, 97, 115, 32, 115, 101, 109, 112, 101, 114, 44, 32, 108, 111, 114, 101, 109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 116, 105, 110, -99, 105, 100, 117, 110, 116, 32, 99, 111, 110, 103, 117, 101, 44, 32, 106, 117, 115, 116, 111, 32, 101, 114, 111, 115, 32, 118, 97, 114, 105, 117, 115, -32, 112, 101, 100, 101, 44, 32, 105, 110, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 116, 117, 114, 112, 105, 115, 32, 108, 101, 99, 116, 117, 115, -32, 110, 111, 110, 32, 101, 114, 111, 115, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 113, 117, 97, 109, 46, 32, 83, 117, 115, 112, 101, -110, 100, 105, 115, 115, 101, 32, 109, 97, 116, 116, 105, 115, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 109, 97, 103, 110, 97, -46, 32, 65, 101, 110, 101, 97, 110, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 100, 105, 97, 109, 32, 118, 101, 108, 32, 110, 105, 115, 108, -32, 110, 111, 110, 117, 109, 109, 121, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, 109, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, -101, 32, 118, 101, 108, 32, 100, 111, 108, 111, 114, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 110, 111, 110, 117, 109, 109, 121, 46, -32, 77, 97, 117, 114, 105, 115, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 32, 115, 101, 109, 112, 101, 114, 32, 97, 110, 116, 101, 46, 32, 77, -97, 101, 99, 101, 110, 97, 115, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 101, 114, 111, 115, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, -117, 109, 32, 97, 99, 32, 100, 111, 108, 111, 114, 46, 32, 70, 117, 115, 99, 101, 32, 114, 105, 115, 117, 115, 32, 109, 101, 116, 117, 115, 44, 32, -97, 108, 105, 113, 117, 101, 116, 32, 101, 103, 101, 116, 44, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 101, 116, 44, 32, 102, 101, 117, 103, -105, 97, 116, 32, 118, 101, 108, 44, 32, 111, 114, 99, 105, 46, 32, 85, 116, 32, 97, 116, 32, 110, 117, 110, 99, 32, 105, 100, 32, 97, 110, 116, -101, 32, 115, 111, 100, 97, 108, 101, 115, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 46, 32, 68, 117, 105, 115, 32, 116, 114, 105, 115, 116, 105, -113, 117, 101, 32, 109, 97, 116, 116, 105, 115, 32, 97, 110, 116, 101, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 110, 101, 113, 117, -101, 32, 109, 97, 117, 114, 105, 115, 44, 32, 108, 97, 111, 114, 101, 101, 116, 32, 105, 100, 44, 32, 99, 111, 110, 103, 117, 101, 32, 105, 110, 116, -101, 114, 100, 117, 109, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, 117, 116, 44, 32, 100, 105, 97, 109, 46, 32, 78, 117, 108, 108, 97, 32, 118, -111, 108, 117, 116, 112, 97, 116, 32, 98, 108, 97, 110, 100, 105, 116, 32, 109, 97, 103, 110, 97, 46, 32, 68, 111, 110, 101, 99, 32, 97, 99, 99, -117, 109, 115, 97, 110, 32, 99, 111, 110, 103, 117, 101, 32, 100, 105, 97, 109, 46, 32, 69, 116, 105, 97, 109, 32, 118, 101, 108, 32, 100, 117, 105, -32, 101, 103, 101, 116, 32, 110, 105, 115, 108, 32, 116, 101, 109, 112, 111, 114, 32, 118, 97, 114, 105, 117, 115, 46, 32, 67, 114, 97, 115, 32, 100, -105, 99, 116, 117, 109, 32, 109, 97, 115, 115, 97, 32, 115, 101, 100, 32, 101, 110, 105, 109, 46, 32, 67, 114, 97, 115, 32, 117, 114, 110, 97, 32, -116, 111, 114, 116, 111, 114, 44, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 97, 99, 44, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, -114, 32, 105, 110, 44, 32, 101, 117, 105, 115, 109, 111, 100, 32, 118, 101, 108, 44, 32, 97, 114, 99, 117, 46, 32, 77, 111, 114, 98, 105, 32, 112, -111, 115, 117, 101, 114, 101, 32, 108, 117, 99, 116, 117, 115, 32, 97, 117, 103, 117, 101, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 100, 117, 105, -32, 100, 117, 105, 44, 32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 32, 105, 110, 44, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 101, 117, -44, 32, 108, 117, 99, 116, 117, 115, 32, 101, 117, 44, 32, 110, 117, 108, 108, 97, 46, 32, 67, 114, 97, 115, 32, 118, 101, 108, 105, 116, 32, 112, -101, 100, 101, 44, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 102, 101, 117, 103, 105, -97, 116, 32, 105, 110, 44, 32, 97, 117, 99, 116, 111, 114, 32, 118, 105, 116, 97, 101, 44, 32, 97, 110, 116, 101, 46, 32, 83, 117, 115, 112, 101, -110, 100, 105, 115, 115, 101, 32, 100, 105, 99, 116, 117, 109, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 109, 97, 117, 114, 105, 115, 46, 32, -73, 110, 32, 97, 32, 110, 105, 98, 104, 46, 32, 68, 111, 110, 101, 99, 32, 97, 99, 32, 108, 105, 103, 117, 108, 97, 46, 32, 73, 110, 32, 113, -117, 97, 109, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 118, 105, 116, 97, 101, 32, 117, 114, 110, 97, 32, 117, 108, 116, 114, 105, 99, 105, -101, 115, 32, 115, 101, 109, 32, 97, 108, 105, 113, 117, 97, 109, 32, 112, 108, 97, 99, 101, 114, 97, 116, 46, 32, 65, 108, 105, 113, 117, 97, 109, -32, 101, 114, 97, 116, 32, 118, 111, 108, 117, 116, 112, 97, 116, 46, 32, 78, 97, 109, 32, 101, 115, 116, 46, 32, 68, 111, 110, 101, 99, 32, 102, -97, 117, 99, 105, 98, 117, 115, 32, 115, 111, 100, 97, 108, 101, 115, 32, 109, 101, 116, 117, 115, 46, 32, 85, 116, 32, 99, 111, 110, 103, 117, 101, -46, 32, 68, 111, 110, 101, 99, 32, 97, 114, 99, 117, 32, 116, 101, 108, 108, 117, 115, 44, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, 97, 99, -44, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 97, 99, 44, 32, 109, 111, 108, 108, 105, 115, 32, 115, 101, 100, 44, 32, 108, 101, 99, 116, -117, 115, 46, 32, 78, 117, 108, 108, 97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 78, 117, 108, 108, 97, 109, 32, 118, 111, 108, 117, 116, -112, 97, 116, 32, 110, 117, 110, 99, 32, 101, 116, 32, 102, 101, 108, 105, 115, 46, 32, 83, 101, 100, 32, 112, 101, 100, 101, 32, 111, 100, 105, 111, -44, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 105, 110, 44, 32, 118, 111, 108, 117, 116, 112, 97, 116, 32, 109, 97, 108, 101, 115, 117, 97, -100, 97, 44, 32, 102, 101, 117, 103, 105, 97, 116, 32, 103, 114, 97, 118, 105, 100, 97, 44, 32, 110, 117, 108, 108, 97, 46, 32, 78, 117, 108, 108, -97, 32, 97, 108, 105, 113, 117, 97, 109, 32, 112, 101, 100, 101, 32, 118, 105, 116, 97, 101, 32, 97, 114, 99, 117, 46, 32, 80, 114, 111, 105, 110, -32, 118, 101, 108, 105, 116, 32, 101, 108, 105, 116, 44, 32, 110, 111, 110, 117, 109, 109, 121, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 101, -108, 101, 109, 101, 110, 116, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 118, 97, 114, 105, 117, 115, 32, 118, 105, 116, 97, 101, 44, 32, 100, 111, -108, 111, 114, 46, 32, 68, 111, 110, 101, 99, 32, 114, 117, 116, 114, 117, 109, 32, 105, 112, 115, 117, 109, 32, 101, 117, 32, 109, 105, 46, 32, 65, -108, 105, 113, 117, 97, 109, 32, 101, 116, 32, 115, 101, 109, 46, 32, 73, 110, 32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 32, 114, 104, 111, -110, 99, 117, 115, 32, 118, 101, 108, 105, 116, 46, 32, 78, 97, 109, 32, 118, 105, 118, 101, 114, 114, 97, 32, 115, 99, 101, 108, 101, 114, 105, 115, -113, 117, 101, 32, 97, 114, 99, 117, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 115, 101, 109, 46, 32, 83, 101, 100, 32, 116, 105, 110, 99, 105, -100, 117, 110, 116, 32, 110, 117, 108, 108, 97, 32, 113, 117, 105, 115, 32, 109, 97, 115, 115, 97, 46, 32, 77, 97, 117, 114, 105, 115, 32, 102, 97, -117, 99, 105, 98, 117, 115, 32, 116, 101, 109, 112, 117, 115, 32, 110, 117, 110, 99, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 99, 111, -110, 100, 105, 109, 101, 110, 116, 117, 109, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 97, 108, 105, 113, 117, 101, 116, 32, 105, 97, 99, -117, 108, 105, 115, 32, 115, 97, 112, 105, 101, 110, 46, 32, 78, 117, 110, 99, 32, 114, 104, 111, 110, 99, 117, 115, 44, 32, 111, 100, 105, 111, 32, -118, 105, 116, 97, 101, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 44, 32, 116, 101, 108, 108, 117, 115, -32, 108, 105, 98, 101, 114, 111, 32, 99, 111, 109, 109, 111, 100, 111, 32, 105, 112, 115, 117, 109, 44, 32, 117, 116, 32, 115, 111, 108, 108, 105, 99, -105, 116, 117, 100, 105, 110, 32, 110, 105, 115, 108, 32, 110, 105, 115, 108, 32, 118, 101, 108, 32, 106, 117, 115, 116, 111, 46, 32, 78, 117, 108, 108, -97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 98, 108, 97, 110, 100, 105, 116, 32, 101, 110, 105, -109, 32, 117, 116, 32, 106, 117, 115, 116, 111, 46, 32, 80, 114, 111, 105, 110, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 44, 32, 101, 108, 105, -116, 32, 101, 103, 101, 116, 32, 97, 99, 99, 117, 109, 115, 97, 110, 32, 112, 117, 108, 118, 105, 110, 97, 114, 44, 32, 111, 114, 99, 105, 32, 113, -117, 97, 109, 32, 97, 117, 99, 116, 111, 114, 32, 110, 101, 113, 117, 101, 44, 32, 115, 101, 100, 32, 99, 111, 110, 118, 97, 108, 108, 105, 115, 32, -100, 105, 97, 109, 32, 112, 117, 114, 117, 115, 32, 118, 101, 108, 32, 102, 101, 108, 105, 115, 46, 32, 83, 101, 100, 32, 111, 114, 99, 105, 32, 108, -101, 111, 44, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 118, 101, 108, 44, 32, 98, 108, 97, 110, 100, 105, 116, 32, 110, 111, 110, 44, 32, 115, -101, 109, 112, 101, 114, 32, 101, 117, 44, 32, 112, 117, 114, 117, 115, 46, 32, 80, 114, 111, 105, 110, 32, 98, 105, 98, 101, 110, 100, 117, 109, 44, -32, 108, 105, 98, 101, 114, 111, 32, 97, 99, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, 99, 111, 109, 109, 111, 100, 111, 44, -32, 101, 114, 111, 115, 32, 115, 97, 112, 105, 101, 110, 32, 98, 108, 97, 110, 100, 105, 116, 32, 110, 105, 115, 108, 44, 32, 101, 117, 32, 101, 108, -101, 105, 102, 101, 110, 100, 32, 110, 105, 98, 104, 32, 110, 105, 98, 104, 32, 118, 101, 108, 32, 108, 101, 99, 116, 117, 115, 46, 32, 86, 105, 118, -97, 109, 117, 115, 32, 112, 108, 97, 99, 101, 114, 97, 116, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 111, 100, 105, 111, 32, 100, 111, 108, 111, -114, 44, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, 110, 111, 110, 44, 32, 115, 111, 100, 97, 108, 101, 115, 32, 105, 100, 44, 32, 118, 105, 118, -101, 114, 114, 97, 32, 101, 103, 101, 116, 44, 32, 100, 105, 97, 109, 46, 32, 78, 117, 110, 99, 32, 109, 97, 117, 114, 105, 115, 32, 109, 97, 103, -110, 97, 44, 32, 101, 103, 101, 115, 116, 97, 115, 32, 113, 117, 105, 115, 44, 32, 102, 101, 117, 103, 105, 97, 116, 32, 105, 100, 44, 32, 102, 101, -114, 109, 101, 110, 116, 117, 109, 32, 118, 105, 118, 101, 114, 114, 97, 44, 32, 109, 105, 46, 32, 65, 101, 110, 101, 97, 110, 32, 115, 117, 115, 99, -105, 112, 105, 116, 32, 110, 105, 115, 108, 32, 110, 111, 110, 32, 110, 117, 110, 99, 46, 32, 80, 114, 111, 105, 110, 32, 113, 117, 105, 115, 32, 108, -101, 99, 116, 117, 115, 32, 97, 99, 32, 116, 101, 108, 108, 117, 115, 32, 110, 111, 110, 117, 109, 109, 121, 32, 99, 111, 109, 109, 111, 100, 111, 46, -32, 78, 117, 110, 99, 32, 101, 103, 101, 116, 32, 100, 105, 97, 109, 32, 97, 99, 32, 101, 108, 105, 116, 32, 118, 101, 115, 116, 105, 98, 117, 108, -117, 109, 32, 97, 117, 99, 116, 111, 114, 46, 32, 69, 116, 105, 97, 109, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 44, 32, 111, 100, 105, 111, -32, 115, 101, 100, 32, 108, 97, 99, 105, 110, 105, 97, 32, 99, 111, 110, 115, 101, 113, 117, 97, 116, 44, 32, 106, 117, 115, 116, 111, 32, 109, 105, -32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 112, 117, 114, 117, 115, 44, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 101, 117, 105, 115, 109, -111, 100, 32, 108, 105, 98, 101, 114, 111, 32, 109, 101, 116, 117, 115, 32, 115, 101, 100, 32, 116, 111, 114, 116, 111, 114, 46, 32, 77, 97, 101, 99, -101, 110, 97, 115, 32, 97, 99, 32, 101, 108, 105, 116, 32, 115, 101, 100, 32, 108, 111, 114, 101, 109, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, -32, 103, 114, 97, 118, 105, 100, 97, 46, 32, 80, 114, 111, 105, 110, 32, 108, 101, 99, 116, 117, 115, 32, 101, 114, 111, 115, 44, 32, 117, 108, 108, -97, 109, 99, 111, 114, 112, 101, 114, 32, 105, 100, 44, 32, 118, 111, 108, 117, 116, 112, 97, 116, 32, 113, 117, 105, 115, 44, 32, 99, 111, 110, 100, -105, 109, 101, 110, 116, 117, 109, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 44, 32, 115, 97, 112, 105, 101, 110, 46, 32, 83, 101, 100, 32, 101, -116, 32, 109, 97, 115, 115, 97, 32, 101, 103, 101, 116, 32, 108, 111, 114, 101, 109, 32, 97, 108, 105, 113, 117, 101, 116, 32, 116, 101, 109, 112, 117, -115, 46, 32, 68, 117, 105, 115, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 110, 105, 115, 108, 32, 110, 111, 110, 32, 114, 105, 115, 117, 115, -46, 32, 78, 97, 109, 32, 105, 100, 32, 113, 117, 97, 109, 46, 32, 78, 117, 108, 108, 97, 109, 32, 101, 115, 116, 46, 32, 80, 114, 111, 105, 110, -32, 111, 114, 99, 105, 32, 100, 105, 97, 109, 44, 32, 112, 111, 115, 117, 101, 114, 101, 32, 101, 116, 44, 32, 112, 104, 97, 114, 101, 116, 114, 97, -32, 99, 111, 109, 109, 111, 100, 111, 44, 32, 100, 105, 99, 116, 117, 109, 32, 118, 101, 108, 44, 32, 101, 110, 105, 109, 46, 32, 80, 114, 111, 105, -110, 32, 101, 103, 101, 116, 32, 101, 114, 97, 116, 46, 32, 68, 111, 110, 101, 99, 32, 110, 105, 115, 108, 46, 32, 77, 97, 101, 99, 101, 110, 97, -115, 32, 97, 117, 99, 116, 111, 114, 32, 118, 101, 108, 105, 116, 32, 117, 116, 32, 112, 101, 100, 101, 46, 32, 78, 117, 110, 99, 32, 118, 105, 116, -97, 101, 32, 108, 101, 99, 116, 117, 115, 32, 110, 101, 99, 32, 108, 105, 98, 101, 114, 111, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 104, -101, 110, 100, 114, 101, 114, 105, 116, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 118, 97, 114, 105, 117, 115, 44, 32, 101, 114, 97, 116, 32, 117, -108, 116, 114, 105, 99, 101, 115, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 101, 117, 105, 115, 109, 111, 100, 44, 32, 112, 117, 114, 117, 115, 32, -108, 97, 99, 117, 115, 32, 100, 105, 99, 116, 117, 109, 32, 101, 114, 111, 115, 44, 32, 97, 116, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, -109, 32, 101, 110, 105, 109, 32, 100, 117, 105, 32, 110, 101, 99, 32, 109, 97, 103, 110, 97, 46, 32, 77, 111, 114, 98, 105, 32, 100, 105, 97, 109, -46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 115, 101, 100, 32, 101, 115, 116, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 110, -101, 99, 32, 108, 105, 98, 101, 114, 111, 32, 105, 110, 32, 97, 114, 99, 117, 32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 115, 111, 108, 108, -105, 99, 105, 116, 117, 100, 105, 110, 46, 32, 73, 110, 32, 114, 117, 116, 114, 117, 109, 32, 110, 105, 115, 108, 32, 97, 116, 32, 97, 114, 99, 117, -46, 32, 78, 117, 108, 108, 97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 77, 97, 117, 114, 105, 115, 32, 100, 105, 103, 110, 105, 115, 115, -105, 109, 46, 32, 69, 116, 105, 97, 109, 32, 101, 115, 116, 32, 109, 97, 117, 114, 105, 115, 44, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, 115, -101, 100, 44, 32, 118, 105, 118, 101, 114, 114, 97, 32, 101, 116, 44, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 115, 101, 100, 44, 32, 110, -101, 113, 117, 101, 46, 32, 85, 116, 32, 97, 116, 32, 108, 101, 99, 116, 117, 115, 32, 105, 100, 32, 110, 105, 98, 104, 32, 108, 117, 99, 116, 117, -115, 32, 111, 114, 110, 97, 114, 101, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 97, 114, 105, 117, 115, 32, 112, 111, 114, 116, 116, 105, 116, 111, -114, 32, 114, 105, 115, 117, 115, 46, 32, 85, 116, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 97, 108, 105, 113, 117, 101, 116, 32, 114, 105, -115, 117, 115, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 108, 117, 99, 116, 117, 115, 32, 110, 101, 113, 117, 101, 32, 115, 105, 116, -32, 97, 109, 101, 116, 32, 110, 117, 110, 99, 46, 32, 68, 117, 105, 115, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 32, 110, 105, 98, 104, 46, -32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 100, 97, 112, 105, 98, 117, 115, 46, 32, 80, 114, 111, 105, 110, 32, 101, 114, 111, -115, 32, 108, 105, 98, 101, 114, 111, 44, 32, 97, 108, 105, 113, 117, 97, 109, 32, 110, 111, 110, 44, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, -117, 109, 32, 97, 44, 32, 115, 111, 100, 97, 108, 101, 115, 32, 117, 116, 44, 32, 116, 117, 114, 112, 105, 115, 46, 32, 73, 110, 116, 101, 103, 101, -114, 32, 97, 99, 99, 117, 109, 115, 97, 110, 32, 109, 105, 32, 115, 101, 100, 32, 108, 111, 114, 101, 109, 46, 32, 86, 101, 115, 116, 105, 98, 117, -108, 117, 109, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 115, 111, 100, 97, 108, 101, 115, 32, 110, 105, 115, 108, 46, 32, 78, -117, 108, 108, 97, 32, 101, 117, 32, 106, 117, 115, 116, 111, 32, 113, 117, 105, 115, 32, 100, 117, 105, 32, 112, 114, 101, 116, 105, 117, 109, 32, 114, -104, 111, 110, 99, 117, 115, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 118, 105, 118, 101, 114, 114, 97, 32, 99, 111, 109, 109, 111, 100, 111, -32, 109, 105, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, 100, 111, 108, 111, 114, 32, 108, 105, 98, 101, 114, 111, 44, 32, 118, 105, 118, 101, -114, 114, 97, 32, 97, 44, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, -118, 105, 116, 97, 101, 44, 32, 100, 117, 105, 46, 32, 77, 97, 117, 114, 105, 115, 32, 99, 111, 110, 118, 97, 108, 108, 105, 115, 32, 108, 101, 99, -116, 117, 115, 32, 101, 116, 32, 109, 105, 46, 32, 77, 97, 117, 114, 105, 115, 32, 115, 97, 103, 105, 116, 116, 105, 115, 46, 32, 83, 101, 100, 32, -97, 114, 99, 117, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 97, 117, 99, 116, 111, 114, 46, 32, 68, 111, 110, 101, 99, -32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 112, 117, 114, 117, 115, 32, 110, 111, 110, 32, 116, 101, 108, 108, 117, 115, 46, 32, -85, 116, 32, 108, 101, 111, 32, 119, 105, 115, 105, 44, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, -117, 108, 116, 114, 105, 99, 101, 115, 32, 101, 117, 44, 32, 103, 114, 97, 118, 105, 100, 97, 32, 97, 99, 44, 32, 108, 105, 98, 101, 114, 111, 46, -32, 77, 97, 117, 114, 105, 115, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 32, 100, 97, 112, 105, 98, 117, 115, 32, 100, 105, 97, 109, 46, 32, -73, 110, 116, 101, 103, 101, 114, 32, 113, 117, 105, 115, 32, 108, 97, 99, 117, 115, 32, 100, 97, 112, 105, 98, 117, 115, 32, 111, 100, 105, 111, 32, -112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 118, 97, 114, 105, 117, 115, 46, 32, 70, 117, 115, 99, 101, 32, 112, 101, 100, 101, 32, -113, 117, 97, 109, 44, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, 117, 116, 44, 32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 101, 116, 44, 32, -116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 115, 101, 100, 44, 32, 102, 101, 108, 105, 115, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, -101, 114, 111, 115, 32, 101, 110, 105, 109, 44, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 115, 101, 100, 44, 32, 97, 108, 105, 113, 117, 97, -109, 32, 97, 99, 44, 32, 101, 117, 105, 115, 109, 111, 100, 32, 97, 99, 44, 32, 101, 114, 97, 116, 46, 32, 85, 116, 32, 100, 105, 103, 110, 105, -115, 115, 105, 109, 44, 32, 108, 97, 99, 117, 115, 32, 97, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 105, 97, 99, 117, 108, 105, 115, 44, 32, -101, 110, 105, 109, 32, 111, 114, 99, 105, 32, 112, 111, 115, 117, 101, 114, 101, 32, 110, 117, 110, 99, 44, 32, 110, 101, 99, 32, 117, 108, 116, 114, -105, 99, 105, 101, 115, 32, 108, 101, 99, 116, 117, 115, 32, 114, 105, 115, 117, 115, 32, 105, 110, 32, 111, 100, 105, 111, 46, 32, 69, 116, 105, 97, -109, 32, 101, 116, 32, 109, 97, 115, 115, 97, 32, 105, 100, 32, 100, 117, 105, 32, 99, 111, 109, 109, 111, 100, 111, 32, 118, 101, 104, 105, 99, 117, -108, 97, 46, 32, 78, 117, 110, 99, 32, 98, 108, 97, 110, 100, 105, 116, 32, 116, 111, 114, 116, 111, 114, 32, 113, 117, 105, 115, 32, 100, 117, 105, -46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 110, 105, 115, 108, 46, 32, 83, 101, 100, 32, 118, 101, 110, 101, 110, 97, 116, 105, 115, 32, 98, 108, -97, 110, 100, 105, 116, 32, 108, 105, 103, 117, 108, 97, 46, 32, 70, 117, 115, 99, 101, 32, 118, 105, 118, 101, 114, 114, 97, 32, 105, 109, 112, 101, -114, 100, 105, 101, 116, 32, 109, 97, 103, 110, 97, 46, 32, 68, 111, 110, 101, 99, 32, 101, 103, 101, 116, 32, 110, 117, 110, 99, 32, 113, 117, 105, -115, 32, 101, 115, 116, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, 108, 111, 98, 111, 114, 116, 105, 115, 46, 32, 86, 101, 115, 116, 105, 98, 117, -108, 117, 109, 32, 113, 117, 105, 115, 32, 108, 101, 99, 116, 117, 115, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 101, 108, 32, 111, 114, 99, 105, -32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 110, 117, 110, 99, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 32, 98, 105, 98, 101, 110, 100, 117, -109, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 101, 103, 101, 116, 32, 108, 101, 111, 46, 32, 77, 111, 114, 98, 105, 32, -118, 101, 108, 32, 117, 114, 110, 97, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 101, 114, 97, 116, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, -32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 46, 32, 83, 101, 100, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 44, 32, 108, 105, 98, 101, 114, -111, 32, 101, 116, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 99, 111, 110, 103, 117, 101, 44, 32, 119, 105, 115, 105, 32, 108, -101, 99, 116, 117, 115, 32, 115, 111, 100, 97, 108, 101, 115, 32, 100, 111, 108, 111, 114, 44, 32, 101, 103, 101, 116, 32, 109, 111, 108, 101, 115, 116, -105, 101, 32, 109, 97, 103, 110, 97, 32, 111, 114, 99, 105, 32, 118, 101, 108, 32, 116, 101, 108, 108, 117, 115, 46, 32, 83, 101, 100, 32, 116, 101, -109, 112, 111, 114, 32, 97, 110, 116, 101, 32, 101, 116, 32, 101, 110, 105, 109, 46, 32, 77, 97, 117, 114, 105, 115, 32, 101, 108, 105, 116, 46, 32, -67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, 109, -97, 115, 115, 97, 46, 32, 83, 101, 100, 32, 118, 105, 118, 101, 114, 114, 97, 46, 32, 68, 117, 105, 115, 32, 110, 117, 108, 108, 97, 46, 32, 78, -97, 109, 32, 98, 105, 98, 101, 110, 100, 117, 109, 46, 32, 78, 97, 109, 32, 116, 111, 114, 116, 111, 114, 32, 108, 111, 114, 101, 109, 44, 32, 117, -108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 118, 105, 116, 97, 101, 44, 32, 100, 105, 99, 116, 117, 109, 32, 115, 101, 100, 44, 32, 112, 111, -115, 117, 101, 114, 101, 32, 101, 117, 44, 32, 106, 117, 115, 116, 111, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 97, 100, 105, 112, 105, 115, 99, -105, 110, 103, 32, 97, 114, 99, 117, 32, 118, 105, 116, 97, 101, 32, 116, 117, 114, 112, 105, 115, 46, 32, 68, 111, 110, 101, 99, 32, 109, 97, 108, -101, 115, 117, 97, 100, 97, 32, 112, 111, 115, 117, 101, 114, 101, 32, 108, 105, 98, 101, 114, 111, 46, 32, 85, 116, 32, 115, 101, 100, 32, 116, 101, -108, 108, 117, 115, 46, 32, 70, 117, 115, 99, 101, 32, 115, 101, 100, 32, 110, 117, 110, 99, 32, 101, 103, 101, 116, 32, 110, 105, 115, 108, 32, 100, -97, 112, 105, 98, 117, 115, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 112, 111, -116, 101, 110, 116, 105, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 108, 105, 98, 101, 114, 111, 32, -101, 116, 32, 109, 101, 116, 117, 115, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 112, 111, 115, 117, 101, 114, 101, 46, 32, 77, 97, 101, 99, 101, -110, 97, 115, 32, 110, 111, 110, 32, 115, 101, 109, 32, 110, 111, 110, 32, 113, 117, 97, 109, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 32, 98, -108, 97, 110, 100, 105, 116, 46, 32, 68, 117, 105, 115, 32, 114, 105, 115, 117, 115, 32, 116, 101, 108, 108, 117, 115, 44, 32, 114, 117, 116, 114, 117, -109, 32, 118, 105, 116, 97, 101, 44, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 32, 110, 101, 99, 44, 32, 109, 97, 108, 101, 115, 117, 97, 100, -97, 32, 110, 101, 99, 44, 32, 105, 112, 115, 117, 109, 46, 32, 78, 117, 110, 99, 32, 113, 117, 97, 109, 32, 100, 111, 108, 111, 114, 44, 32, 108, -117, 99, 116, 117, 115, 32, 101, 103, 101, 116, 44, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 110, 111, 110, 44, 32, 114, 104, 111, 110, 99, 117, -115, 32, 97, 116, 44, 32, 116, 101, 108, 108, 117, 115, 46, 32, 68, 117, 105, 115, 32, 112, 101, 100, 101, 32, 108, 101, 99, 116, 117, 115, 44, 32, -109, 97, 116, 116, 105, 115, 32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 44, 32, 116, 101, 109, 112, 111, 114, 32, 117, 116, 44, 32, 112, 111, -114, 116, 97, 32, 97, 116, 44, 32, 109, 105, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 114, 105, 115, 117, 115, 32, 110, -117, 108, 108, 97, 44, 32, 115, 111, 100, 97, 108, 101, 115, 32, 115, 101, 100, 44, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 105, 100, 44, 32, -110, 111, 110, 117, 109, 109, 121, 32, 118, 105, 116, 97, 101, 44, 32, 108, 105, 103, 117, 108, 97, 46, 32, 77, 111, 114, 98, 105, 32, 112, 117, 108, -118, 105, 110, 97, 114, 32, 112, 101, 100, 101, 32, 117, 116, 32, 109, 97, 115, 115, 97, 46, 32, 78, 117, 110, 99, 32, 114, 105, 115, 117, 115, 32, -109, 97, 117, 114, 105, 115, 44, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 101, 116, 44, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 101, -117, 44, 32, 115, 117, 115, 99, 105, 112, 105, 116, 32, 118, 101, 108, 44, 32, 111, 114, 99, 105, 46, 32, 73, 110, 32, 102, 97, 117, 99, 105, 98, -117, 115, 32, 102, 101, 108, 105, 115, 32, 105, 110, 32, 97, 114, 99, 117, 46, 32, 78, 117, 108, 108, 97, 32, 115, 105, 116, 32, 97, 109, 101, 116, -32, 101, 108, 105, 116, 46, 32, 78, 117, 108, 108, 97, 32, 101, 114, 97, 116, 32, 115, 97, 112, 105, 101, 110, 44, 32, 115, 97, 103, 105, 116, 116, -105, 115, 32, 101, 103, 101, 116, 44, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 101, 103, 101, 116, 44, 32, 118, 105, 118, 101, 114, 114, 97, -32, 101, 117, 44, 32, 102, 101, 108, 105, 115, 46, 32, 78, 97, 109, 32, 97, 99, 32, 105, 112, 115, 117, 109, 46, 32, 83, 117, 115, 112, 101, 110, -100, 105, 115, 115, 101, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 116, 117, 114, 112, 105, 115, 32, 118, 101, 108, 32, 115, 101, 109, 32, 108, -97, 99, 105, 110, 105, 97, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 46, 32, 77, 97, 117, 114, 105, 115, 32, 111, 114, 110, 97, 114, -101, 32, 105, 112, 115, 117, 109, 32, 115, 101, 100, 32, 108, 105, 103, 117, 108, 97, 46, 32, 68, 117, 105, 115, 32, 102, 97, 99, 105, 108, 105, 115, -105, 115, 32, 110, 101, 113, 117, 101, 32, 113, 117, 105, 115, 32, 111, 114, 99, 105, 46, 32, 78, 117, 108, 108, 97, 109, 32, 101, 116, 32, 101, 114, -97, 116, 32, 101, 116, 32, 111, 114, 99, 105, 32, 108, 97, 99, 105, 110, 105, 97, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 46, -32, 68, 111, 110, 101, 99, 32, 97, 99, 32, 105, 112, 115, 117, 109, 46, 32, 68, 117, 105, 115, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, 105, -112, 115, 117, 109, 32, 97, 99, 32, 97, 114, 99, 117, 46, 32, 65, 101, 110, 101, 97, 110, 32, 99, 111, 110, 103, 117, 101, 32, 97, 99, 99, 117, -109, 115, 97, 110, 32, 97, 110, 116, 101, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 98, 105, 98, 101, 110, 100, 117, 109, 44, 32, 108, 101, 111, -32, 117, 116, 32, 111, 114, 110, 97, 114, 101, 32, 97, 108, 105, 113, 117, 97, 109, 44, 32, 110, 117, 110, 99, 32, 101, 114, 97, 116, 32, 99, 111, -110, 100, 105, 109, 101, 110, 116, 117, 109, 32, 97, 114, 99, 117, 44, 32, 117, 116, 32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 109, 105, 32, 97, -117, 103, 117, 101, 32, 101, 116, 32, 110, 117, 108, 108, 97, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 108, 97, 99, 105, 110, 105, 97, 32, 97, -108, 105, 113, 117, 101, 116, 32, 119, 105, 115, 105, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 110, 101, 99, 32, 100, 117, 105, 46, 32, 69, 116, -105, 97, 109, 32, 119, 105, 115, 105, 32, 108, 101, 111, 44, 32, 101, 117, 105, 115, 109, 111, 100, 32, 118, 105, 116, 97, 101, 44, 32, 118, 117, 108, -112, 117, 116, 97, 116, 101, 32, 97, 44, 32, 100, 105, 99, 116, 117, 109, 32, 118, 105, 116, 97, 101, 44, 32, 113, 117, 97, 109, 46, 32, 81, 117, -105, 115, 113, 117, 101, 32, 113, 117, 105, 115, 32, 116, 111, 114, 116, 111, 114, 46, 32, 69, 116, 105, 97, 109, 32, 105, 110, 116, 101, 114, 100, 117, -109, 46, 32, 73, 110, 32, 109, 97, 115, 115, 97, 32, 101, 114, 97, 116, 44, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 115, 101, 100, 44, -32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, 118, 101, 108, 44, 32, 118, 101, 104, 105, 99, 117, 108, 97, 32, 102, 114, 105, 110, 103, 105, 108, -108, 97, 44, 32, 97, 117, 103, 117, 101, 46, 32, 78, 117, 108, 108, 97, 32, 118, 101, 108, 32, 117, 114, 110, 97, 46, 32, 73, 110, 32, 108, 105, -98, 101, 114, 111, 32, 109, 105, 44, 32, 112, 114, 101, 116, 105, 117, 109, 32, 115, 101, 100, 44, 32, 109, 97, 116, 116, 105, 115, 32, 116, 101, 109, -112, 117, 115, 44, 32, 115, 97, 103, 105, 116, 116, 105, 115, 32, 115, 101, 100, 44, 32, 109, 97, 115, 115, 97, 46, 32, 83, 117, 115, 112, 101, 110, -100, 105, 115, 115, 101, 32, 113, 117, 97, 109, 32, 119, 105, 115, 105, 44, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, 32, 113, 117, 105, 115, 44, -32, 115, 97, 103, 105, 116, 116, 105, 115, 32, 97, 116, 44, 32, 99, 111, 110, 115, 101, 113, 117, 97, 116, 32, 101, 103, 101, 116, 44, 32, 111, 100, -105, 111, 46, 32, 78, 117, 108, 108, 97, 109, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 44, 32, 112, 117, 114, 117, 115, 32, 113, 117, 105, 115, -32, 97, 108, 105, 113, 117, 97, 109, 32, 99, 117, 114, 115, 117, 115, 44, 32, 116, 117, 114, 112, 105, 115, 32, 111, 100, 105, 111, 32, 101, 103, 101, -115, 116, 97, 115, 32, 106, 117, 115, 116, 111, 44, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 103, 114, 97, 118, 105, 100, 97, 32, 116, 117, 114, -112, 105, 115, 32, 119, 105, 115, 105, 32, 118, 101, 108, 32, 116, 111, 114, 116, 111, 114, 46, 32, 78, 117, 110, 99, 32, 117, 108, 116, 114, 105, 99, -105, 101, 115, 32, 112, 111, 114, 116, 97, 32, 112, 117, 114, 117, 115, 46, 32, 80, 114, 111, 105, 110, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, -32, 101, 114, 97, 116, 32, 97, 99, 32, 111, 114, 99, 105, 46, 32, 85, 116, 32, 118, 101, 108, 32, 109, 97, 103, 110, 97, 32, 110, 101, 99, 32, -109, 105, 32, 102, 101, 117, 103, 105, 97, 116, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 46, 32, 85, 116, 32, 108, 105, 103, 117, 108, 97, 46, -32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 97, 110, 116, 101, 32, 105, 112, 115, 117, 109, 32, 112, 114, 105, 109, 105, 115, 32, 105, 110, -32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 111, 114, 99, 105, 32, 108, 117, 99, 116, 117, 115, 32, 101, 116, 32, 117, 108, 116, 114, 105, 99, 101, -115, 32, 112, 111, 115, 117, 101, 114, 101, 32, 99, 117, 98, 105, 108, 105, 97, 32, 67, 117, 114, 97, 101, 59, 32, 68, 111, 110, 101, 99, 32, 101, -116, 32, 109, 97, 103, 110, 97, 32, 105, 110, 32, 100, 105, 97, 109, 32, 112, 111, 114, 116, 97, 32, 110, 111, 110, 117, 109, 109, 121, 46, 32, 77, -97, 101, 99, 101, 110, 97, 115, 32, 117, 116, 32, 115, 101, 109, 32, 105, 110, 32, 116, 117, 114, 112, 105, 115, 32, 102, 101, 114, 109, 101, 110, 116, -117, 109, 32, 118, 105, 118, 101, 114, 114, 97, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 97, 116, 32, 111, 114, 99, 105, 46, -32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 104, 97, 98, 105, 116, 97, 110, 116, 32, 109, 111, 114, 98, 105, 32, 116, 114, 105, -115, 116, 105, 113, 117, 101, 32, 115, 101, 110, 101, 99, 116, 117, 115, 32, 101, 116, 32, 110, 101, 116, 117, 115, 32, 101, 116, 32, 109, 97, 108, 101, -115, 117, 97, 100, 97, 32, 102, 97, 109, 101, 115, 32, 97, 99, 32, 116, 117, 114, 112, 105, 115, 32, 101, 103, 101, 115, 116, 97, 115, 46, 32, 86, -101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 97, 110, 116, 101, 32, 105, 112, 115, 117, 109, 32, 112, 114, 105, 109, 105, 115, 32, 105, 110, 32, 102, -97, 117, 99, 105, 98, 117, 115, 32, 111, 114, 99, 105, 32, 108, 117, 99, 116, 117, 115, 32, 101, 116, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, -112, 111, 115, 117, 101, 114, 101, 32, 99, 117, 98, 105, 108, 105, 97, 32, 67, 117, 114, 97, 101, 59, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, -113, 117, 101, 32, 114, 117, 116, 114, 117, 109, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 106, 117, 115, 116, 111, 46, 32, 78, 117, 108, 108, 97, -109, 32, 118, 105, 116, 97, 101, 32, 112, 101, 100, 101, 46, 32, 68, 111, 110, 101, 99, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, 109, 32, -110, 105, 98, 104, 32, 101, 116, 32, 111, 100, 105, 111, 46, 32, 83, 101, 100, 32, 101, 116, 32, 109, 101, 116, 117, 115, 46, 32, 67, 108, 97, 115, -115, 32, 97, 112, 116, 101, 110, 116, 32, 116, 97, 99, 105, 116, 105, 32, 115, 111, 99, 105, 111, 115, 113, 117, 32, 97, 100, 32, 108, 105, 116, 111, -114, 97, 32, 116, 111, 114, 113, 117, 101, 110, 116, 32, 112, 101, 114, 32, 99, 111, 110, 117, 98, 105, 97, 32, 110, 111, 115, 116, 114, 97, 44, 32, -112, 101, 114, 32, 105, 110, 99, 101, 112, 116, 111, 115, 32, 104, 121, 109, 101, 110, 97, 101, 111, 115, 46, 32, 78, 97, 109, 32, 116, 101, 109, 112, -117, 115, 46, 32, 83, 101, 100, 32, 97, 99, 32, 119, 105, 115, 105, 46, 32, 73, 110, 32, 104, 97, 99, 32, 104, 97, 98, 105, 116, 97, 115, 115, -101, 32, 112, 108, 97, 116, 101, 97, 32, 100, 105, 99, 116, 117, 109, 115, 116, 46, 32, 83, 101, 100, 32, 115, 101, 100, 32, 119, 105, 115, 105, 46, -32, 85, 116, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 116, 101, 108, 108, 117, 115, 32, 110, 111, 110, 32, 108, 105, 103, 117, 108, 97, 46, -32, 73, 110, 116, 101, 103, 101, 114, 32, 109, 101, 116, 117, 115, 46, 32, 73, 110, 32, 108, 97, 99, 105, 110, 105, 97, 32, 100, 117, 105, 46, 32, -67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 111, 114, 110, 97, 114, 101, 46, 32, 77, 97, 117, 114, 105, 115, 32, 118, 101, 108, 32, 117, 114, 110, -97, 46, 32, 78, 97, 109, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 117, 114, -110, 97, 46, 32, 78, 117, 110, 99, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 101, 114, 97, -116, 46, 32, 83, 101, 100, 32, 98, 108, 97, 110, 100, 105, 116, 44, 32, 114, 105, 115, 117, 115, 32, 110, 111, 110, 32, 99, 111, 109, 109, 111, 100, -111, 32, 110, 111, 110, 117, 109, 109, 121, 44, 32, 108, 105, 103, 117, 108, 97, 32, 101, 114, 97, 116, 32, 102, 101, 114, 109, 101, 110, 116, 117, 109, -32, 110, 105, 98, 104, 44, 32, 101, 117, 32, 102, 97, 99, 105, 108, 105, 115, 105, 115, 32, 97, 110, 116, 101, 32, 110, 101, 113, 117, 101, 32, 115, -101, 100, 32, 115, 101, 109, 46, 32, 69, 116, 105, 97, 109, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 32, 106, 117, 115, 116, 111, 32, -101, 103, 101, 116, 32, 119, 105, 115, 105, 46, 32, 78, 117, 110, 99, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 46, 32, 80, 114, 111, 105, 110, -32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 113, 117, 97, 109, 32, 110, 111, 110, 32, 108, 101, 99, 116, 117, 115, 46, 32, 80, 114, 111, 105, 110, -32, 117, 116, 32, 116, 117, 114, 112, 105, 115, 32, 113, 117, 105, 115, 32, 97, 117, 103, 117, 101, 32, 112, 101, 108, 108, 101, 110, 116, 101, 115, 113, -117, 101, 32, 100, 105, 99, 116, 117, 109, 46, 32, 70, 117, 115, 99, 101, 32, 101, 116, 32, 108, 111, 114, 101, 109, 46, 32, 65, 108, 105, 113, 117, -97, 109, 32, 117, 114, 110, 97, 32, 108, 97, 99, 117, 115, 44, 32, 98, 108, 97, 110, 100, 105, 116, 32, 115, 101, 100, 44, 32, 118, 101, 115, 116, -105, 98, 117, 108, 117, 109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 101, 116, 44, 32, 100, 111, -108, 111, 114, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 97, 117, 99, 116, 111, 114, 32, 101, 114, 97, 116, 32, 110, 101, 99, 32, 108, -111, 114, 101, 109, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 117, 114, 110, 97, 32, 119, 105, 115, 105, 44, 32, 108, 97, 99, 105, 110, -105, 97, 32, 117, 116, 44, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 44, 32, 99, 111, 110, 100, 105, -109, 101, 110, 116, 117, 109, 32, 105, 100, 44, 32, 111, 100, 105, 111, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 99, 111, 110, 118, 97, -108, 108, 105, 115, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 106, 117, 115, 116, 111, 46, 32, 68, 111, 110, 101, 99, 32, 118, 101, -115, 116, 105, 98, 117, 108, 117, 109, 32, 101, 115, 116, 32, 97, 99, 32, 113, 117, 97, 109, 46, 32, 78, 117, 108, 108, 97, 109, 32, 118, 105, 116, -97, 101, 32, 101, 108, 105, 116, 32, 101, 117, 32, 109, 97, 115, 115, 97, 32, 118, 97, 114, 105, 117, 115, 32, 118, 117, 108, 112, 117, 116, 97, 116, -101, 46, 32, 78, 117, 108, 108, 97, 32, 102, 97, 99, 105, 108, 105, 115, 105, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 112, -111, 116, 101, 110, 116, 105, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 110, 111, 110, 32, 108, 105, 98, 101, 114, 111, 46, 32, 78, 117, 108, -108, 97, 109, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 109, 97, 115, 115, 97, 32, 105, 100, 32, 109, 97, 103, 110, 97, 32, 118, 105, 118, -101, 114, 114, 97, 32, 99, 111, 109, 109, 111, 100, 111, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 108, 105, 98, 101, 114, 111, 32, -116, 111, 114, 116, 111, 114, 44, 32, 108, 117, 99, 116, 117, 115, 32, 97, 99, 44, 32, 118, 105, 118, 101, 114, 114, 97, 32, 99, 111, 110, 103, 117, -101, 44, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 32, 118, 101, 108, 44, 32, 108, 105, 98, 101, 114, 111, 46, 32, 65, 101, 110, -101, 97, 110, 32, 97, 114, 99, 117, 32, 97, 117, 103, 117, 101, 44, 32, 108, 117, 99, 116, 117, 115, 32, 105, 100, 44, 32, 108, 97, 111, 114, 101, -101, 116, 32, 112, 117, 108, 118, 105, 110, 97, 114, 44, 32, 100, 105, 99, 116, 117, 109, 32, 115, 101, 100, 44, 32, 108, 101, 99, 116, 117, 115, 46, -32, 68, 111, 110, 101, 99, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 118, 111, 108, 117, 116, 112, 97, 116, 32, 100, 111, 108, 111, 114, -46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 104, 97, 98, 105, 116, 97, 110, 116, 32, 109, 111, 114, 98, 105, 32, 116, 114, -105, 115, 116, 105, 113, 117, 101, 32, 115, 101, 110, 101, 99, 116, 117, 115, 32, 101, 116, 32, 110, 101, 116, 117, 115, 32, 101, 116, 32, 109, 97, 108, -101, 115, 117, 97, 100, 97, 32, 102, 97, 109, 101, 115, 32, 97, 99, 32, 116, 117, 114, 112, 105, 115, 32, 101, 103, 101, 115, 116, 97, 115, 46, 32, -80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 97, 117, 103, 117, 101, 32, 116, 117, 114, 112, 105, 115, 44, 32, 108, 97, 111, 114, 101, -101, 116, 32, 110, 101, 99, 44, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 97, 116, 44, 32, 110, 111, 110, 117, 109, 109, 121, 32, 118, 105, -116, 97, 101, 44, 32, 110, 105, 98, 104, 46, 32, 69, 116, 105, 97, 109, 32, 111, 114, 99, 105, 32, 115, 97, 112, 105, 101, 110, 44, 32, 99, 111, -110, 103, 117, 101, 32, 105, 110, 44, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 114, 117, 116, -114, 117, 109, 32, 118, 101, 108, 44, 32, 110, 105, 98, 104, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 101, 117, 32, 108, 111, 114, 101, 109, 46, -32, 77, 97, 117, 114, 105, 115, 32, 112, 114, 101, 116, 105, 117, 109, 32, 108, 101, 111, 32, 101, 116, 32, 101, 108, 105, 116, 46, 32, 73, 110, 32, -110, 111, 110, 117, 109, 109, 121, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 115, 97, 112, 105, 101, 110, 46, 32, 77, 97, 117, 114, 105, 115, -32, 118, 97, 114, 105, 117, 115, 46, 32, 77, 97, 117, 114, 105, 115, 32, 115, 101, 100, 32, 108, 105, 98, 101, 114, 111, 46, 32, 67, 117, 114, 97, -98, 105, 116, 117, 114, 32, 117, 108, 108, 97, 109, 99, 111, 114, 112, 101, 114, 32, 101, 108, 105, 116, 32, 101, 117, 32, 112, 117, 114, 117, 115, 46, -32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 118, 101, 108, 105, 116, 32, 112, 101, 100, 101, 44, 32, 115, 101, 109, 112, 101, 114, 32, 115, -105, 116, 32, 97, 109, 101, 116, 44, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 118, 105, 116, 97, 101, 44, 32, 116, 105, 110, 99, 105, 100, 117, -110, 116, 32, 118, 101, 108, 44, 32, 100, 117, 105, 46, 32, 78, 117, 108, 108, 97, 32, 110, 101, 113, 117, 101, 32, 97, 110, 116, 101, 44, 32, 115, -97, 103, 105, 116, 116, 105, 115, 32, 101, 117, 44, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 101, 116, 44, 32, 108, 97, 99, 105, 110, -105, 97, 32, 97, 44, 32, 108, 105, 98, 101, 114, 111, 46, 32, 77, 111, 114, 98, 105, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 119, 105, 115, -105, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 110, 111, 110, 32, 102, 101, 108, 105, 115, 32, 113, 117, 105, 115, 32, 97, -114, 99, 117, 32, 98, 105, 98, 101, 110, 100, 117, 109, 32, 111, 114, 110, 97, 114, 101, 46, 32, 65, 101, 110, 101, 97, 110, 32, 101, 110, 105, 109, -32, 109, 101, 116, 117, 115, 44, 32, 99, 111, 109, 109, 111, 100, 111, 32, 101, 117, 44, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 110, 111, -110, 117, 109, 109, 121, 44, 32, 101, 117, 105, 115, 109, 111, 100, 32, 117, 116, 44, 32, 113, 117, 97, 109, 46, 32, 78, 117, 108, 108, 97, 32, 101, -108, 101, 105, 102, 101, 110, 100, 32, 110, 105, 115, 108, 32, 113, 117, 105, 115, 32, 100, 111, 108, 111, 114, 46, 32, 67, 108, 97, 115, 115, 32, 97, -112, 116, 101, 110, 116, 32, 116, 97, 99, 105, 116, 105, 32, 115, 111, 99, 105, 111, 115, 113, 117, 32, 97, 100, 32, 108, 105, 116, 111, 114, 97, 32, -116, 111, 114, 113, 117, 101, 110, 116, 32, 112, 101, 114, 32, 99, 111, 110, 117, 98, 105, 97, 32, 110, 111, 115, 116, 114, 97, 44, 32, 112, 101, 114, -32, 105, 110, 99, 101, 112, 116, 111, 115, 32, 104, 121, 109, 101, 110, 97, 101, 111, 115, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, 112, 101, -108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 109, 97, 115, 115, 97, 32, 105, 110, 32, 101, 114, 97, 116, 32, 109, 111, 108, 101, 115, 116, 105, -101, 32, 109, 111, 108, 101, 115, 116, 105, 101, 46, 32, 77, 97, 117, 114, 105, 115, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 100, 97, 112, -105, 98, 117, 115, 32, 108, 105, 98, 101, 114, 111, 46, 32, 83, 101, 100, 32, 115, 101, 100, 32, 114, 105, 115, 117, 115, 32, 105, 100, 32, 110, 101, -113, 117, 101, 32, 100, 105, 99, 116, 117, 109, 32, 111, 114, 110, 97, 114, 101, 46, 32, 83, 101, 100, 32, 101, 117, 32, 108, 105, 103, 117, 108, 97, -32, 97, 116, 32, 102, 101, 108, 105, 115, 32, 115, 111, 100, 97, 108, 101, 115, 32, 97, 99, 99, 117, 109, 115, 97, 110, 46, 32, 83, 101, 100, 32, -105, 110, 116, 101, 114, 100, 117, 109, 44, 32, 117, 114, 110, 97, 32, 110, 111, 110, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, 104, 101, 110, 100, -114, 101, 114, 105, 116, 44, 32, 113, 117, 97, 109, 32, 109, 105, 32, 111, 114, 110, 97, 114, 101, 32, 108, 105, 98, 101, 114, 111, 44, 32, 105, 100, -32, 102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 116, 111, 114, 116, 111, 114, 32, 111, 114, 99, 105, 32, 110, 111, 110, 32, 118, 101, 108, 105, 116, -46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 110, 101, 99, 32, 114, 105, 115, 117, 115, 46, 32, 68, 111, 110, 101, 99, 32, 97, 116, 32, 110, 117, -110, 99, 32, 118, 105, 116, 97, 101, 32, 116, 101, 108, 108, 117, 115, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, 118, 101, 115, 116, 105, 98, 117, -108, 117, 109, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, 113, 117, 101, 32, 118, 101, 108, 32, 106, 117, 115, 116, 111, 46, 32, 68, 117, 105, -115, 32, 108, 105, 103, 117, 108, 97, 32, 108, 105, 98, 101, 114, 111, 44, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 113, 117, 105, 115, 44, -32, 97, 100, 105, 112, 105, 115, 99, 105, 110, 103, 32, 98, 105, 98, 101, 110, 100, 117, 109, 44, 32, 102, 101, 117, 103, 105, 97, 116, 32, 118, 105, -116, 97, 101, 44, 32, 118, 101, 108, 105, 116, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 101, 116, 32, 97, 114, 99, 117, 46, 32, 70, 117, 115, -99, 101, 32, 101, 103, 101, 116, 32, 113, 117, 97, 109, 46, 32, 85, 116, 32, 97, 110, 116, 101, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, -115, 101, 32, 102, 101, 117, 103, 105, 97, 116, 32, 109, 101, 116, 117, 115, 32, 110, 111, 110, 32, 105, 112, 115, 117, 109, 46, 32, 78, 117, 108, 108, -97, 32, 116, 101, 109, 112, 117, 115, 32, 108, 101, 111, 32, 117, 116, 32, 109, 105, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 118, 105, -116, 97, 101, 32, 110, 105, 115, 108, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 46, 32, 69, 116, 105, -97, 109, 32, 97, 32, 111, 114, 99, 105, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 97, 110, 116, 101, 32, 105, 112, 115, 117, 109, -32, 112, 114, 105, 109, 105, 115, 32, 105, 110, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 111, 114, 99, 105, 32, 108, 117, 99, 116, 117, 115, 32, -101, 116, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 112, 111, 115, 117, 101, 114, 101, 32, 99, 117, 98, 105, 108, 105, 97, 32, 67, 117, 114, 97, -101, 59, 32, 86, 105, 118, 97, 109, 117, 115, 32, 117, 114, 110, 97, 32, 113, 117, 97, 109, 44, 32, 116, 105, 110, 99, 105, 100, 117, 110, 116, 32, -97, 116, 44, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 118, 101, 108, 44, 32, 102, 101, 117, 103, 105, 97, 116, 32, 101, 103, 101, 116, 44, 32, -110, 117, 108, 108, 97, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, 108, 97, 99, 117, 115, 32, 109, 97, 103, 110, 97, 44, 32, 110, 111, 110, -117, 109, 109, 121, 32, 101, 117, 44, 32, 105, 97, 99, 117, 108, 105, 115, 32, 115, 101, 100, 44, 32, 99, 111, 110, 115, 101, 99, 116, 101, 116, 117, -101, 114, 32, 113, 117, 105, 115, 44, 32, 101, 110, 105, 109, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 97, 32, 101, 114, 111, 115, 46, 32, -65, 108, 105, 113, 117, 97, 109, 32, 110, 111, 110, 117, 109, 109, 121, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 110, 101, 113, 117, 101, 46, -32, 78, 117, 108, 108, 97, 32, 101, 110, 105, 109, 46, 32, 80, 114, 97, 101, 115, 101, 110, 116, 32, 109, 111, 108, 101, 115, 116, 105, 101, 44, 32, -111, 114, 99, 105, 32, 113, 117, 105, 115, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 32, 118, 111, 108, 117, 116, 112, 97, 116, 44, 32, 108, 97, -99, 117, 115, 32, 109, 101, 116, 117, 115, 32, 108, 117, 99, 116, 117, 115, 32, 115, 97, 112, 105, 101, 110, 44, 32, 101, 116, 32, 102, 97, 99, 105, -108, 105, 115, 105, 115, 32, 101, 114, 111, 115, 32, 110, 101, 113, 117, 101, 32, 105, 100, 32, 115, 97, 112, 105, 101, 110, 46, 32, 78, 117, 110, 99, -32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, 109, 32, 100, 111, 108, 111, 114, 32, 118, 101, 108, 32, 111, 114, 99, 105, 46, 32, 73, 110, 116, -101, 103, 101, 114, 32, 119, 105, 115, 105, 32, 100, 105, 97, 109, 44, 32, 112, 111, 114, 116, 116, 105, 116, 111, 114, 32, 115, 105, 116, 32, 97, 109, -101, 116, 44, 32, 102, 101, 117, 103, 105, 97, 116, 32, 105, 110, 44, 32, 100, 97, 112, 105, 98, 117, 115, 32, 105, 110, 44, 32, 108, 101, 99, 116, -117, 115, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 101, 114, 97, 116, 32, 118, 111, 108, 117, 116, 112, 97, 116, 46, 32, 81, 117, 105, 115, 113, -117, 101, 32, 109, 111, 108, 108, 105, 115, 32, 116, 117, 114, 112, 105, 115, 32, 118, 105, 116, 97, 101, 32, 116, 111, 114, 116, 111, 114, 46, 32, 77, -97, 117, 114, 105, 115, 32, 116, 117, 114, 112, 105, 115, 32, 109, 105, 44, 32, 112, 114, 101, 116, 105, 117, 109, 32, 117, 116, 44, 32, 117, 108, 116, -114, 105, 99, 101, 115, 32, 115, 101, 100, 44, 32, 112, 111, 114, 116, 97, 32, 105, 110, 44, 32, 106, 117, 115, 116, 111, 46, 32, 83, 117, 115, 112, -101, 110, 100, 105, 115, 115, 101, 32, 112, 111, 115, 117, 101, 114, 101, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 117, 108, 116, 114, 105, 99, 105, -101, 115, 32, 108, 97, 99, 117, 115, 32, 118, 105, 116, 97, 101, 32, 101, 110, 105, 109, 46, 32, 68, 111, 110, 101, 99, 32, 108, 97, 99, 117, 115, -46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, 112, 111, 116, 101, 110, 116, 105, 46, 32, 68, 111, 110, 101, 99, 32, 109, 111, 108, -101, 115, 116, 105, 101, 44, 32, 109, 97, 103, 110, 97, 32, 115, 101, 100, 32, 101, 117, 105, 115, 109, 111, 100, 32, 100, 105, 99, 116, 117, 109, 44, -32, 109, 97, 103, 110, 97, 32, 109, 97, 103, 110, 97, 32, 105, 110, 116, 101, 114, 100, 117, 109, 32, 100, 105, 97, 109, 44, 32, 118, 105, 116, 97, -101, 32, 115, 97, 103, 105, 116, 116, 105, 115, 32, 108, 101, 111, 32, 108, 111, 114, 101, 109, 32, 97, 99, 32, 110, 101, 113, 117, 101, 46, 32, 67, -114, 97, 115, 32, 109, 101, 116, 117, 115, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 110, 117, 110, 99, 46, 32, 68, 117, 105, 115, 32, 99, 111, -110, 115, 101, 99, 116, 101, 116, 117, 101, 114, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 103, 114, 97, 118, 105, 100, 97, 32, 115, -111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 117, 114, 110, 97, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 118, 111, 108, 117, 116, 112, -97, 116, 44, 32, 109, 97, 115, 115, 97, 32, 113, 117, 105, 115, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 112, 117, 108, 118, 105, 110, 97, 114, -44, 32, 101, 114, 111, 115, 32, 112, 117, 114, 117, 115, 32, 100, 105, 103, 110, 105, 115, 115, 105, 109, 32, 110, 117, 110, 99, 44, 32, 101, 103, 101, -116, 32, 114, 104, 111, 110, 99, 117, 115, 32, 101, 110, 105, 109, 32, 108, 101, 99, 116, 117, 115, 32, 113, 117, 105, 115, 32, 116, 111, 114, 116, 111, -114, 46, 32, 73, 110, 116, 101, 103, 101, 114, 32, 108, 97, 99, 105, 110, 105, 97, 32, 113, 117, 97, 109, 32, 113, 117, 105, 115, 32, 101, 114, 97, -116, 32, 99, 111, 110, 118, 97, 108, 108, 105, 115, 32, 109, 97, 116, 116, 105, 115, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, 32, -105, 97, 99, 117, 108, 105, 115, 32, 112, 111, 115, 117, 101, 114, 101, 32, 118, 101, 108, 105, 116, 46, 32, 69, 116, 105, 97, 109, 32, 116, 101, 108, -108, 117, 115, 32, 101, 110, 105, 109, 44, 32, 97, 108, 105, 113, 117, 101, 116, 32, 110, 101, 99, 44, 32, 108, 97, 111, 114, 101, 101, 116, 32, 97, -44, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, 110, 111, 110, 44, 32, 118, 101, 108, 105, 116, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 108, -97, 99, 117, 115, 32, 118, 101, 108, 105, 116, 44, 32, 101, 108, 101, 105, 102, 101, 110, 100, 32, 105, 109, 112, 101, 114, 100, 105, 101, 116, 44, 32, -102, 114, 105, 110, 103, 105, 108, 108, 97, 32, 105, 100, 44, 32, 100, 97, 112, 105, 98, 117, 115, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, -101, 44, 32, 108, 101, 99, 116, 117, 115, 46, 32, 78, 117, 108, 108, 97, 32, 113, 117, 105, 115, 32, 108, 111, 114, 101, 109, 46, 32, 78, 117, 108, -108, 97, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 110, 101, 113, 117, 101, 32, 101, 116, 32, 100, 117, 105, 46, 32, 80, 104, 97, 115, 101, -108, 108, 117, 115, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 117, 108, 116, 114, 105, 99, 105, 101, 115, 32, 111, 100, 105, 111, 46, 32, 80, -104, 97, 115, 101, 108, 108, 117, 115, 32, 118, 105, 116, 97, 101, 32, 108, 105, 103, 117, 108, 97, 46, 32, 80, 101, 108, 108, 101, 110, 116, 101, 115, -113, 117, 101, 32, 102, 101, 117, 103, 105, 97, 116, 32, 97, 114, 99, 117, 32, 97, 116, 32, 101, 114, 97, 116, 46, 32, 86, 105, 118, 97, 109, 117, -115, 32, 117, 116, 32, 101, 114, 111, 115, 32, 117, 116, 32, 108, 111, 114, 101, 109, 32, 112, 117, 108, 118, 105, 110, 97, 114, 32, 105, 97, 99, 117, -108, 105, 115, 46, 32, 80, 114, 111, 105, 110, 32, 108, 111, 98, 111, 114, 116, 105, 115, 32, 105, 112, 115, 117, 109, 32, 105, 100, 32, 110, 117, 110, -99, 46, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 118, 101, 108, 32, 109, 97, 115, 115, 97, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, -115, 115, 101, 32, 110, 117, 108, 108, 97, 32, 105, 112, 115, 117, 109, 44, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 118, 101, 108, 44, 32, -112, 111, 115, 117, 101, 114, 101, 32, 101, 103, 101, 116, 44, 32, 109, 111, 108, 108, 105, 115, 32, 97, 116, 44, 32, 114, 105, 115, 117, 115, 46, 32, -86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 115, 101, 100, 32, 100, 105, 97, 109, 32, 105, 100, 32, 101, 115, 116, 32, 100, 97, 112, 105, 98, -117, 115, 32, 117, 108, 116, 114, 105, 99, 101, 115, 46, 32, 80, 114, 111, 105, 110, 32, 116, 101, 109, 112, 117, 115, 44, 32, 101, 114, 111, 115, 32, -97, 32, 115, 99, 101, 108, 101, 114, 105, 115, 113, 117, 101, 32, 118, 101, 115, 116, 105, 98, 117, 108, 117, 109, 44, 32, 105, 112, 115, 117, 109, 32, -97, 114, 99, 117, 32, 97, 108, 105, 113, 117, 97, 109, 32, 109, 105, 44, 32, 117, 116, 32, 102, 101, 117, 103, 105, 97, 116, 32, 108, 105, 98, 101, -114, 111, 32, 111, 100, 105, 111, 32, 105, 110, 32, 110, 105, 115, 108, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 101, 116, 32, 109, 97, 115, 115, -97, 32, 97, 32, 109, 97, 117, 114, 105, 115, 32, 108, 117, 99, 116, 117, 115, 32, 99, 111, 110, 103, 117, 101, 46, 32, 85, 116, 32, 105, 100, 32, -101, 114, 111, 115, 46, 32, 70, 117, 115, 99, 101, 32, 97, 110, 116, 101, 32, 101, 114, 111, 115, 44, 32, 112, 104, 97, 114, 101, 116, 114, 97, 32, -110, 111, 110, 44, 32, 109, 111, 108, 101, 115, 116, 105, 101, 32, 116, 114, 105, 115, 116, 105, 113, 117, 101, 44, 32, 98, 105, 98, 101, 110, 100, 117, -109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 44, 32, 119, 105, 115, 105, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 114, 117, 116, 114, -117, 109, 44, 32, 100, 111, 108, 111, 114, 32, 101, 116, 32, 115, 101, 109, 112, 101, 114, 32, 101, 108, 101, 109, 101, 110, 116, 117, 109, 44, 32, 101, -114, 111, 115, 32, 97, 110, 116, 101, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 109, 97, 115, 115, 97, 44, 32, 115, 101, 100, 32, 115, 111, -108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 108, 101, 99, 116, 117, 115, 32, 118, 101, 108, 105, 116, 32, 101, 116, 32, 109, 97, 115, 115, 97, -46, 32, 73, 110, 32, 97, 117, 99, 116, 111, 114, 46, 32, 65, 108, 105, 113, 117, 97, 109, 32, 101, 114, 97, 116, 32, 118, 111, 108, 117, 116, 112, -97, 116, 46, 32, 69, 116, 105, 97, 109, 32, 114, 105, 115, 117, 115, 32, 108, 101, 111, 44, 32, 118, 117, 108, 112, 117, 116, 97, 116, 101, 32, 115, -117, 115, 99, 105, 112, 105, 116, 44, 32, 115, 111, 108, 108, 105, 99, 105, 116, 117, 100, 105, 110, 32, 101, 116, 44, 32, 115, 111, 100, 97, 108, 101, -115, 32, 101, 103, 101, 116, 44, 32, 110, 105, 115, 108, 46, 32, 86, 101, 115, 116, 105, 98, 117, 108, 117, 109, 32, 97, 110, 116, 101, 32, 105, 112, -115, 117, 109, 32, 112, 114, 105, 109, 105, 115, 32, 105, 110, 32, 102, 97, 117, 99, 105, 98, 117, 115, 32, 111, 114, 99, 105, 32, 108, 117, 99, 116, -117, 115, 32, 101, 116, 32, 117, 108, 116, 114, 105, 99, 101, 115, 32, 112, 111, 115, 117, 101, 114, 101, 32, 99, 117, 98, 105, 108, 105, 97, 32, 67, -117, 114, 97, 101, 59, 32, 67, 117, 114, 97, 98, 105, 116, 117, 114, 32, 108, 111, 98, 111, 114, 116, 105, 115, 44, 32, 108, 105, 98, 101, 114, 111, -32, 97, 99, 32, 108, 97, 111, 114, 101, 101, 116, 32, 109, 111, 108, 108, 105, 115, 44, 32, 108, 105, 103, 117, 108, 97, 32, 108, 101, 111, 32, 112, -111, 114, 116, 97, 32, 119, 105, 115, 105, 44, 32, 117, 116, 32, 101, 117, 105, 115, 109, 111, 100, 32, 102, 101, 108, 105, 115, 32, 108, 105, 103, 117, -108, 97, 32, 105, 100, 32, 101, 108, 105, 116, 46, 32, 86, 105, 118, 97, 109, 117, 115, 32, 109, 97, 108, 101, 115, 117, 97, 100, 97, 32, 110, 117, -108, 108, 97, 32, 101, 117, 32, 101, 110, 105, 109, 46, 32, 68, 111, 110, 101, 99, 32, 97, 99, 99, 117, 109, 115, 97, 110, 32, 102, 97, 117, 99, -105, 98, 117, 115, 32, 111, 114, 99, 105, 46, 32, 78, 117, 108, 108, 97, 32, 108, 97, 99, 105, 110, 105, 97, 32, 97, 110, 116, 101, 46, 32, 80, -114, 97, 101, 115, 101, 110, 116, 32, 97, 116, 32, 110, 105, 98, 104, 46, 32, 77, 97, 117, 114, 105, 115, 32, 112, 111, 114, 116, 97, 32, 100, 105, -103, 110, 105, 115, 115, 105, 109, 32, 119, 105, 115, 105, 46, 32, 85, 116, 32, 108, 97, 99, 105, 110, 105, 97, 32, 116, 111, 114, 116, 111, 114, 32, -110, 101, 99, 32, 110, 117, 110, 99, 46, 32, 80, 104, 97, 115, 101, 108, 108, 117, 115, 32, 101, 116, 32, 97, 117, 103, 117, 101, 46, 32, 73, 110, -116, 101, 103, 101, 114, 32, 114, 104, 111, 110, 99, 117, 115, 44, 32, 108, 105, 98, 101, 114, 111, 32, 97, 32, 112, 101, 108, 108, 101, 110, 116, 101, -115, 113, 117, 101, 32, 114, 104, 111, 110, 99, 117, 115, 44, 32, 116, 111, 114, 116, 111, 114, 32, 115, 97, 112, 105, 101, 110, 32, 108, 111, 98, 111, -114, 116, 105, 115, 32, 112, 101, 100, 101, 44, 32, 101, 103, 101, 116, 32, 99, 111, 110, 100, 105, 109, 101, 110, 116, 117, 109, 32, 115, 97, 112, 105, -101, 110, 32, 114, 105, 115, 117, 115, 32, 118, 105, 116, 97, 101, 32, 101, 108, 105, 116, 46, 32, 83, 117, 115, 112, 101, 110, 100, 105, 115, 115, 101, -32, 115, 101, 100, 32, 116, 117, 114, 112, 105, 115, 32, 117, 116, 32, 100, 111, 108, 111, 114, 32, 112, 108, 97, 99, 101, 114, 97, 116, 32, 100, 105, -103, 110, 105, 115, 115, 105, 109, 46, 32, 81, 117, 105, 115, 113, 117, 101, 32, 113, 117, 105, 115, 32, 108, 101, 111, 46, 32, 67, 114, 97, 115, 32, -117, 108, 116, 114, 105, 99, 101, 115, 46, 32, 77, 97, 101, 99, 101, 110, 97, 115, 32, 104, 101, 110, 100, 114, 101, 114, 105, 116, 32, 97, 117, 99, -116, 111, 114, 32, 116, 111, 114, 116, 111, 114, 46, 32, 69, 116, 105, 97, 109, 32, 115, 105, 116, 32, 97, 109, 101, 116, 32, 97, 114, 99, 117, 46, -0}; diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 58192f59219..56b8d284386 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -39,6 +39,8 @@ set(INC_SYS set(SRC drawgpencil.c editaction_gpencil.c + gpencil_convert.c + gpencil_data.c gpencil_edit.c gpencil_ops.c gpencil_paint.c diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 55224b87c59..b28c948db9f 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -40,6 +40,9 @@ #include "BLI_math.h" #include "BLI_utildefines.h" +#include "BLF_api.h" +#include "BLF_translation.h" + #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -57,12 +60,12 @@ #include "BIF_glutil.h" #include "ED_gpencil.h" +#include "ED_screen.h" #include "ED_view3d.h" +#include "UI_interface_icons.h" #include "UI_resources.h" -#include "gpencil_intern.h" - /* ************************************************** */ /* GREASE PENCIL DRAWING */ @@ -118,7 +121,7 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn /* draw stroke curve */ if (G.debug & G_DEBUG) setlinestyle(2); - glLineWidth(oldpressure * thickness); + glLineWidth(max_ff(oldpressure * thickness, 1.0)); glBegin(GL_LINE_STRIP); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { @@ -127,7 +130,7 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn */ if (fabsf(pt->pressure - oldpressure) > 0.2f) { glEnd(); - glLineWidth(pt->pressure * thickness); + glLineWidth(max_ff(pt->pressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ @@ -412,7 +415,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness int i; /* draw stroke curve */ - glLineWidth(curpressure * thickness); + glLineWidth(max_ff(curpressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { /* if there was a significant pressure change, stop the curve, change the thickness of the stroke, @@ -422,7 +425,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { glEnd(); curpressure = pt->pressure; - glLineWidth(curpressure * thickness); + glLineWidth(max_ff(curpressure * thickness, 1.0f)); glBegin(GL_LINE_STRIP); /* need to roll-back one point to ensure that there are no gaps in the stroke */ @@ -440,6 +443,8 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness /* draw debug points of curve on top? */ /* XXX: for now, we represent "selected" strokes in the same way as debug, which isn't used anymore */ if (debug) { + glPointSize((float)(thickness + 2)); + glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) glVertex3fv(&pt->x); @@ -614,6 +619,8 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness bGPDspoint *pt; int i; + glPointSize((float)(thickness_s + 2)); + glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; @@ -754,7 +761,7 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, { bGPDstroke *gps; - const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); + const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0; int mask_orig = 0; /* set up depth masks... */ @@ -873,7 +880,7 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, /* draw onion-skinning for a layer */ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, short debug, short lthick) + int UNUSED(cfra), int dflag, bool debug, short lthick) { const float alpha = gpl->color[3]; float color[4]; @@ -1044,6 +1051,51 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in } } +/* draw a short status message in the top-right corner */ +static void gp_draw_status_text(bGPdata *gpd, ARegion *ar) +{ + rcti rect; + + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_RENDER_OGL) + return; + + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); + + /* for now, this should only be used to indicate when we are in stroke editmode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + const char *printable = IFACE_("GPencil Stroke Editing"); + float printable_size[2]; + int xco, yco; + + BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + + xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + yco = (rect.ymax - U.widget_unit); + + /* text label */ + UI_ThemeColor(TH_TEXT_HI); +#ifdef WITH_INTERNATIONAL + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#else + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#endif + + /* grease pencil icon... */ + // XXX: is this too intrusive? + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; + + UI_icon_draw(xco, yco, ICON_GREASEPENCIL); + + glDisable(GL_BLEND); + } +} + /* draw grease-pencil datablock */ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { @@ -1059,7 +1111,13 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, */ /* turn on alpha-blending */ - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + if (GLEW_VERSION_1_4) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + glEnable(GL_BLEND); /* draw! */ @@ -1195,6 +1253,11 @@ void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) /* draw it! */ if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (onlyv2d == false) { + gp_draw_status_text(gpd, ar); + } } /* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly @@ -1229,10 +1292,27 @@ void ED_gpencil_draw_view3d(Scene *scene, View3D *v3d, ARegion *ar, bool only3d) winy = ar->winy; } - /* draw it! */ - if (only3d) dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + /* draw it! */ gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (only3d == false) { + gp_draw_status_text(gpd, ar); + } } void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c new file mode 100644 index 00000000000..2c8f86423a5 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -0,0 +1,1484 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * Bastien Montagne + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operator for converting Grease Pencil data to geometry + */ + +/** \file blender/editors/gpencil/gpencil_convert.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_blenlib.h" +#include "BLI_rand.h" +#include "BLI_utildefines.h" + +#include "BLF_translation.h" + +#include "DNA_anim_types.h" +#include "DNA_curve_types.h" +#include "DNA_object_types.h" +#include "DNA_node_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_depsgraph.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_tracking.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_view3d.h" +#include "ED_clip.h" +#include "ED_keyframing.h" + +#include "gpencil_intern.h" + +/* ************************************************ */ +/* Grease Pencil to Data Operator */ + +/* defines for possible modes */ +enum { + GP_STROKECONVERT_PATH = 1, + GP_STROKECONVERT_CURVE, + GP_STROKECONVERT_POLY, +}; + +/* Defines for possible timing modes */ +enum { + GP_STROKECONVERT_TIMING_NONE = 1, + GP_STROKECONVERT_TIMING_LINEAR = 2, + GP_STROKECONVERT_TIMING_FULL = 3, + GP_STROKECONVERT_TIMING_CUSTOMGAP = 4, +}; + +/* RNA enum define */ +static EnumPropertyItem prop_gpencil_convertmodes[] = { + {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""}, + {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""}, + {GP_STROKECONVERT_POLY, "POLY", 0, "Polygon Curve", ""}, + {0, NULL, 0, NULL, NULL} +}; + +static EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = { + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem prop_gpencil_convert_timingmodes[] = { + {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, + {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, + {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original timing, gaps included"}, + {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps", + "Use the original timing, but with custom gap lengths (in frames)"}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), + bool *UNUSED(r_free)) +{ + if (RNA_boolean_get(ptr, "use_timing_data")) { + return prop_gpencil_convert_timingmodes; + } + return prop_gpencil_convert_timingmodes_restricted; +} + +/* --- */ + +/* convert the coordinates from the given stroke point into 3d-coordinates + * - assumes that the active space is the 3D-View + */ +static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect) +{ + Scene *scene = CTX_data_scene(C); + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + + if (gps->flag & GP_STROKE_3DSPACE) { + /* directly use 3d-coordinates */ + copy_v3_v3(p3d, &pt->x); + } + else { + const float *fp = ED_view3d_cursor3d_get(scene, v3d); + float mvalf[2]; + + /* get screen coordinate */ + if (gps->flag & GP_STROKE_2DSPACE) { + View2D *v2d = &ar->v2d; + UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]); + } + else { + if (subrect) { + mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; + mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; + } + else { + mvalf[0] = (float)pt->x / 100.0f * ar->winx; + mvalf[1] = (float)pt->y / 100.0f * ar->winy; + } + } + + ED_view3d_win_to_3d(ar, fp, mvalf, p3d); + } +} + +/* --- */ + +/* temp struct for gp_stroke_path_animation() */ +typedef struct tGpTimingData { + /* Data set from operator settings */ + int mode; + int frame_range; /* Number of frames evaluated for path animation */ + int start_frame, end_frame; + bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */ + float gap_duration, gap_randomness; /* To be used with CustomGap mode*/ + int seed; + + /* Data set from points, used to compute final timing FCurve */ + int num_points, cur_point; + + /* Distances */ + float *dists; + float tot_dist; + + /* Times */ + float *times; /* Note: Gap times will be negative! */ + float tot_time, gap_tot_time; + double inittime; + + /* Only used during creation of dists & times lists. */ + float offset_time; +} tGpTimingData; + +/* Init point buffers for timing data. + * Note this assumes we only grow those arrays! + */ +static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr) +{ + float *tmp; + + BLI_assert(nbr > gtd->num_points); + + /* distances */ + tmp = gtd->dists; + gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); + if (tmp) { + memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); + MEM_freeN(tmp); + } + + /* times */ + tmp = gtd->times; + gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); + if (tmp) { + memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); + MEM_freeN(tmp); + } + + gtd->num_points = nbr; +} + +/* add stroke point to timing buffers */ +static void gp_timing_data_add_point(tGpTimingData *gtd, const double stroke_inittime, const float time, + const float delta_dist) +{ + float delta_time = 0.0f; + const int cur_point = gtd->cur_point; + + if (!cur_point) { + /* Special case, first point, if time is not 0.0f we have to compensate! */ + gtd->offset_time = -time; + gtd->times[cur_point] = 0.0f; + } + else if (time < 0.0f) { + /* This is a gap, negative value! */ + gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); + delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1]; + + gtd->gap_tot_time += delta_time; + } + else { + gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); + delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]); + } + + gtd->tot_time += delta_time; + gtd->tot_dist += delta_dist; + gtd->dists[cur_point] = gtd->tot_dist; + + gtd->cur_point++; +} + +/* In frames! Binary search for FCurve keys have a threshold of 0.01, so we can't set + * arbitrarily close points - this is esp. important with NoGaps mode! + */ +#define MIN_TIME_DELTA 0.02f + +/* Loop over next points to find the end of the stroke, and compute */ +static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps, + int *nbr_done_gaps, const float tot_gaps_time, const float delta_time, + float *next_delta_time) +{ + int j; + + for (j = idx + 1; j < gtd->num_points; j++) { + if (gtd->times[j] < 0) { + gtd->times[j] = -gtd->times[j]; + if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + /* In this mode, gap time between this stroke and the next should be 0 currently... + * So we have to compute its final duration! + */ + if (gtd->gap_randomness > 0.0f) { + /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, + * and which sum to exactly tot_gaps_time... + */ + int rem_gaps = nbr_gaps - (*nbr_done_gaps); + if (rem_gaps < 2) { + /* Last gap, just give remaining time! */ + *next_delta_time = tot_gaps_time; + } + else { + float delta, min, max; + + /* This code ensures that if the first gaps have been shorter than average gap_duration, + * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa! + */ + delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); + + /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ + min = -gtd->gap_randomness - delta; + CLAMP(min, -gtd->gap_randomness, 0.0f); + + /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */ + max = gtd->gap_randomness - delta; + CLAMP(max, 0.0f, gtd->gap_randomness); + *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min; + } + } + else { + *next_delta_time += gtd->gap_duration; + } + } + (*nbr_done_gaps)++; + break; + } + } + + return j - 1; +} + +static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, int *nbr_gaps, float *tot_gaps_time) +{ + int i; + float delta_time = 0.0f; + + for (i = 0; i < gtd->num_points; i++) { + if (gtd->times[i] < 0 && i) { + (*nbr_gaps)++; + gtd->times[i] = -gtd->times[i] - delta_time; + delta_time += gtd->times[i] - gtd->times[i - 1]; + gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */ + } + else { + gtd->times[i] -= delta_time; + } + } + gtd->tot_time -= delta_time; + + *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; + gtd->tot_time += *tot_gaps_time; + if (G.debug & G_DEBUG) { + printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps); + } + if (gtd->gap_randomness > 0.0f) { + BLI_rng_srandom(rng, gtd->seed); + } +} + +static void gp_stroke_path_animation_add_keyframes(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, + Curve *cu, tGpTimingData *gtd, RNG *rng, const float time_range, + const int nbr_gaps, const float tot_gaps_time) +{ + /* Use actual recorded timing! */ + const float time_start = (float)gtd->start_frame; + + float last_valid_time = 0.0f; + int end_stroke_idx = -1, start_stroke_idx = 0; + float end_stroke_time = 0.0f; + + /* CustomGaps specific */ + float delta_time = 0.0f, next_delta_time = 0.0f; + int nbr_done_gaps = 0; + + int i; + float cfra; + + /* This is a bit tricky, as: + * - We can't add arbitrarily close points on FCurve (in time). + * - We *must* have all "caps" points of all strokes in FCurve, as much as possible! + */ + for (i = 0; i < gtd->num_points; i++) { + /* If new stroke... */ + if (i > end_stroke_idx) { + start_stroke_idx = i; + delta_time = next_delta_time; + /* find end of that new stroke */ + end_stroke_idx = gp_find_end_of_stroke_idx(gtd, rng, i, nbr_gaps, &nbr_done_gaps, + tot_gaps_time, delta_time, &next_delta_time); + /* This one should *never* be negative! */ + end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); + } + + /* Simple proportional stuff... */ + cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen; + cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range); + + /* And now, the checks about timing... */ + if (i == start_stroke_idx) { + /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and + * that the end point of the stroke is far enough! + * In case it is not, we keep the end point... + * Note that with CustomGaps mode, this is here we set the actual gap timing! + */ + if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) { + if ((cfra - last_valid_time) < MIN_TIME_DELTA) { + cfra = last_valid_time + MIN_TIME_DELTA; + } + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); + last_valid_time = cfra; + } + else if (G.debug & G_DEBUG) { + printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); + } + } + else if (i == end_stroke_idx) { + /* Always try to insert end point of a curve (should be safe enough, anyway...) */ + if ((cfra - last_valid_time) < MIN_TIME_DELTA) { + cfra = last_valid_time + MIN_TIME_DELTA; + } + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); + last_valid_time = cfra; + } + else { + /* Else ("middle" point), we only insert it if it's far enough from last keyframe, + * and also far enough from (not yet added!) end_stroke keyframe! + */ + if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) { + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); + last_valid_time = cfra; + } + else if (G.debug & G_DEBUG) { + printf("\t Skipping \"middle\" point %d, too close from last added point or end point %d\n", + i, end_stroke_idx); + } + } + } +} + +static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd) +{ + Scene *scene = CTX_data_scene(C); + bAction *act; + FCurve *fcu; + PointerRNA ptr; + PropertyRNA *prop = NULL; + int nbr_gaps = 0, i; + + if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) + return; + + /* gap_duration and gap_randomness are in frames, but we need seconds!!! */ + gtd->gap_duration = FRA2TIME(gtd->gap_duration); + gtd->gap_randomness = FRA2TIME(gtd->gap_randomness); + + /* Enable path! */ + cu->flag |= CU_PATH; + cu->pathlen = gtd->frame_range; + + /* Get RNA pointer to read/write path time values */ + RNA_id_pointer_create((ID *)cu, &ptr); + prop = RNA_struct_find_property(&ptr, "eval_time"); + + /* Ensure we have an F-Curve to add keyframes to */ + act = verify_adt_action((ID *)cu, true); + fcu = verify_fcurve(act, NULL, &ptr, "eval_time", 0, true); + + if (G.debug & G_DEBUG) { + printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); + for (i = 0; i < gtd->num_points; i++) { + printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); + } + } + + if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { + float cfra; + + /* Linear extrapolation! */ + fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; + + cu->ctime = 0.0f; + cfra = (float)gtd->start_frame; + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); + + cu->ctime = cu->pathlen; + if (gtd->realtime) { + cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ + } + else { + cfra = (float)gtd->end_frame; + } + insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); + } + else { + /* Use actual recorded timing! */ + RNG *rng = BLI_rng_new(0); + float time_range; + + /* CustomGaps specific */ + float tot_gaps_time = 0.0f; + + /* Pre-process gaps, in case we don't want to keep their original timing */ + if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); + } + + if (gtd->realtime) { + time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ + } + else { + time_range = (float)(gtd->end_frame - gtd->start_frame); + } + + if (G.debug & G_DEBUG) { + printf("GP Stroke Path Conversion: Starting keying!\n"); + } + + gp_stroke_path_animation_add_keyframes(reports, ptr, prop, fcu, cu, gtd, rng, time_range, + nbr_gaps, tot_gaps_time); + + BLI_rng_free(rng); + } + + /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ + calchandles_fcurve(fcu); + + if (G.debug & G_DEBUG) { + printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); + for (i = 0; i < gtd->num_points; i++) { + printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); + } + printf("\n\n"); + } + + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); + + /* send updates */ + DAG_id_tag_update(&cu->id, 0); +} + +#undef MIN_TIME_DELTA + +#define GAP_DFAC 0.01f +#define WIDTH_CORR_FAC 0.1f +#define BEZT_HANDLE_FAC 0.3f + +/* convert stroke to 3d path */ + +/* helper */ +static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const float p[3], const float prev_p[3], + const bool do_gtd, const double inittime, const float time, + const float width, const float rad_fac, float minmax_weights[2]) +{ + copy_v3_v3(bp->vec, p); + bp->vec[3] = 1.0f; + + /* set settings */ + bp->f1 = SELECT; + bp->radius = width * rad_fac; + bp->weight = width; + CLAMP(bp->weight, 0.0f, 1.0f); + if (bp->weight < minmax_weights[0]) { + minmax_weights[0] = bp->weight; + } + else if (bp->weight > minmax_weights[1]) { + minmax_weights[1] = bp->weight; + } + + /* Update timing data */ + if (do_gtd) { + gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); + } +} + +static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, + float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, + const bool add_end_point, tGpTimingData *gtd) +{ + bGPDspoint *pt; + Nurb *nu = (curnu) ? *curnu : NULL; + BPoint *bp, *prev_bp = NULL; + const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); + const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); + int i, old_nbp = 0; + + /* create new 'nurb' or extend current one within the curve */ + if (nu) { + old_nbp = nu->pntsu; + + /* If stitch, the first point of this stroke is already present in current nu. + * Else, we have to add two additional points to make the zero-radius link between strokes. + */ + BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points); + } + else { + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); + + nu->pntsu = gps->totpoints + add_start_end_points; + nu->pntsv = 1; + nu->orderu = 2; /* point-to-point! */ + nu->type = CU_NURBS; + nu->flagu = CU_NURB_ENDPOINT; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + nu->knotsu = NULL; + + nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints"); + + stitch = false; /* Security! */ + } + + if (do_gtd) { + gp_timing_data_set_nbr(gtd, nu->pntsu); + } + + /* If needed, make the link between both strokes with two zero-radius additional points */ + /* About "zero-radius" point interpolations: + * - If we have at least two points in current curve (most common case), we linearly extrapolate + * the last segment to get the first point (p1) position and timing. + * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point + * with the first point of the current stroke. + * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated + * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... + */ + if (curnu && !stitch && old_nbp) { + float p1[3], p2[3], p[3], next_p[3]; + float dt1 = 0.0f, dt2 = 0.0f; + + BLI_assert(gps->prev != NULL); + + prev_bp = NULL; + if ((old_nbp > 1) && (gps->prev->totpoints > 1)) { + /* Only use last curve segment if previous stroke was not a single-point one! */ + prev_bp = &nu->bp[old_nbp - 2]; + } + bp = &nu->bp[old_nbp - 1]; + + /* First point */ + gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + if (prev_bp) { + interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); + if (do_gtd) { + const int idx = gps->prev->totpoints - 1; + dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC); + if (do_gtd) { + dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); + } + } + bp++; + gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1, + 0.0f, rad_fac, minmax_weights); + + /* Second point */ + /* Note dt2 is always negative, which marks the gap. */ + if (gps->totpoints > 1) { + gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); + } + } + bp++; + gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); + + old_nbp += 2; + } + else if (add_start_point) { + float p[3], next_p[3]; + float dt = 0.0f; + + gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); + if (gps->totpoints > 1) { + gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); + interp_v3_v3v3(p, p, next_p, -GAP_DFAC); + if (do_gtd) { + dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + p[0] -= GAP_DFAC; /* Rather arbitrary... */ + dt = -GAP_DFAC; /* Rather arbitrary too! */ + } + bp = &nu->bp[old_nbp]; + /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value) + * would not work (it would be *before* gtd->inittime, which is not supported currently). + */ + gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); + + old_nbp++; + } + + if (old_nbp) { + prev_bp = &nu->bp[old_nbp - 1]; + } + + /* add points */ + for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp]; + i < gps->totpoints; + i++, pt++, bp++) + { + float p[3]; + float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + + /* get coordinates to add at */ + gp_strokepoint_convertcoords(C, gps, pt, p, subrect); + + gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, + width, rad_fac, minmax_weights); + + prev_bp = bp; + } + + if (add_end_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC); + if (do_gtd) { + const int idx = gps->totpoints - 1; + dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, prev_bp->vec); + p[0] += GAP_DFAC; /* Rather arbitrary... */ + dt = GAP_DFAC; /* Rather arbitrary too! */ + } + /* Note bp has already been incremented in main loop above, so it points to the right place. */ + gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); + } + + /* add nurb to curve */ + if (!curnu || !*curnu) { + BLI_addtail(&cu->nurb, nu); + } + if (curnu) { + *curnu = nu; + } + + BKE_nurb_knot_calc_u(nu); +} + +/* convert stroke to 3d bezier */ + +/* helper */ +static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt, + const float p[3], const float h1[3], const float h2[3], const float prev_p[3], + const bool do_gtd, const double inittime, const float time, + const float width, const float rad_fac, float minmax_weights[2]) +{ + copy_v3_v3(bezt->vec[0], h1); + copy_v3_v3(bezt->vec[1], p); + copy_v3_v3(bezt->vec[2], h2); + + /* set settings */ + bezt->h1 = bezt->h2 = HD_FREE; + bezt->f1 = bezt->f2 = bezt->f3 = SELECT; + bezt->radius = width * rad_fac; + bezt->weight = width; + CLAMP(bezt->weight, 0.0f, 1.0f); + if (bezt->weight < minmax_weights[0]) { + minmax_weights[0] = bezt->weight; + } + else if (bezt->weight > minmax_weights[1]) { + minmax_weights[1] = bezt->weight; + } + + /* Update timing data */ + if (do_gtd) { + gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); + } +} + +static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, + float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, + const bool add_end_point, tGpTimingData *gtd) +{ + bGPDspoint *pt; + Nurb *nu = (curnu) ? *curnu : NULL; + BezTriple *bezt, *prev_bezt = NULL; + int i, tot, old_nbezt = 0; + const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); + float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3]; + const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); + + /* create new 'nurb' or extend current one within the curve */ + if (nu) { + old_nbezt = nu->pntsu; + /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke, + * so no need to add it. + * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes. + */ + BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points); + } + else { + nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); + + nu->pntsu = gps->totpoints + add_start_end_points; + nu->resolu = 12; + nu->resolv = 12; + nu->type = CU_BEZIER; + nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts"); + + stitch = false; /* Security! */ + } + + if (do_gtd) { + gp_timing_data_set_nbr(gtd, nu->pntsu); + } + + tot = gps->totpoints; + + /* get initial coordinates */ + pt = gps->points; + if (tot) { + gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + if (tot > 1) { + gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + } + if (stitch && tot > 2) { + gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + } + } + + /* If needed, make the link between both strokes with two zero-radius additional points */ + if (curnu && old_nbezt) { + BLI_assert(gps->prev != NULL); + + /* Update last point's second handle */ + if (stitch) { + bezt = &nu->bezt[old_nbezt - 1]; + interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC); + copy_v3_v3(bezt->vec[2], h2); + pt++; + } + + /* Create "link points" */ + /* About "zero-radius" point interpolations: + * - If we have at least two points in current curve (most common case), we linearly extrapolate + * the last segment to get the first point (p1) position and timing. + * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point + * with the first point of the current stroke. + * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated + * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... + */ + else { + float p1[3], p2[3]; + float dt1 = 0.0f, dt2 = 0.0f; + + prev_bezt = NULL; + if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) { + /* Only use last curve segment if previous stroke was not a single-point one! */ + prev_bezt = &nu->bezt[old_nbezt - 2]; + } + bezt = &nu->bezt[old_nbezt - 1]; + + /* First point */ + if (prev_bezt) { + interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC); + if (do_gtd) { + const int idx = gps->prev->totpoints - 1; + dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC); + if (do_gtd) { + dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); + } + } + + /* Second point */ + /* Note dt2 is always negative, which marks the gap. */ + if (tot > 1) { + interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC); + if (do_gtd) { + dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); + } + } + + /* Second handle of last point of previous stroke. */ + interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC); + copy_v3_v3(bezt->vec[2], h2); + + /* First point */ + interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC); + bezt++; + gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1, + 0.0f, rad_fac, minmax_weights); + + /* Second point */ + interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC); + bezt++; + gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, + 0.0f, rad_fac, minmax_weights); + + old_nbezt += 2; + copy_v3_v3(p3d_prev, p2); + } + } + else if (add_start_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC); + if (do_gtd) { + dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, p3d_cur); + p[0] -= GAP_DFAC; /* Rather arbitrary... */ + dt = -GAP_DFAC; /* Rather arbitrary too! */ + } + interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC); + bezt = &nu->bezt[old_nbezt]; + gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, + 0.0f, rad_fac, minmax_weights); + + old_nbezt++; + copy_v3_v3(p3d_prev, p); + } + + if (old_nbezt) { + prev_bezt = &nu->bezt[old_nbezt - 1]; + } + + /* add points */ + for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { + float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; + + if (i || old_nbezt) { + interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); + } + else { + interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC); + } + + if (i < tot - 1) { + interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC); + } + else { + interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC); + } + + gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur, + do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); + + /* shift coord vects */ + copy_v3_v3(p3d_prev, p3d_cur); + copy_v3_v3(p3d_cur, p3d_next); + + if (i + 2 < tot) { + gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); + } + + prev_bezt = bezt; + } + + if (add_end_point) { + float p[3]; + float dt = 0.0f; + + if (gps->totpoints > 1) { + interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC); + if (do_gtd) { + const int idx = gps->totpoints - 1; + dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); + } + } + else { + copy_v3_v3(p, prev_bezt->vec[1]); + p[0] += GAP_DFAC; /* Rather arbitrary... */ + dt = GAP_DFAC; /* Rather arbitrary too! */ + } + + /* Second handle of last point of this stroke. */ + interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC); + copy_v3_v3(prev_bezt->vec[2], h2); + + /* The end point */ + interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC); + interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC); + /* Note bezt has already been incremented in main loop above, so it points to the right place. */ + gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt, + 0.0f, rad_fac, minmax_weights); + } + + /* must calculate handles or else we crash */ + BKE_nurb_handles_calc(nu); + + if (!curnu || !*curnu) { + BLI_addtail(&cu->nurb, nu); + } + if (curnu) { + *curnu = nu; + } +} + +#undef GAP_DFAC +#undef WIDTH_CORR_FAC +#undef BEZT_HANDLE_FAC + +static void gp_stroke_finalize_curve_endpoints(Curve *cu) +{ + /* start */ + Nurb *nu = cu->nurb.first; + int i = 0; + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + if (bezt) { + bezt[i].weight = bezt[i].radius = 0.0f; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + if (bp) { + bp[i].weight = bp[i].radius = 0.0f; + } + } + + /* end */ + nu = cu->nurb.last; + i = nu->pntsu - 1; + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + if (bezt) { + bezt[i].weight = bezt[i].radius = 0.0f; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + if (bp) { + bp[i].weight = bp[i].radius = 0.0f; + } + } +} + +static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2]) +{ + Nurb *nu; + const float delta = minmax_weights[0]; + float fac; + int i; + + /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */ + if (IS_EQF(delta, minmax_weights[1])) + fac = 1.0f; + else + fac = 1.0f / (minmax_weights[1] - delta); + + for (nu = cu->nurb.first; nu; nu = nu->next) { + if (nu->bezt) { + BezTriple *bezt = nu->bezt; + for (i = 0; i < nu->pntsu; i++, bezt++) { + bezt->weight = (bezt->weight - delta) * fac; + } + } + else if (nu->bp) { + BPoint *bp = nu->bp; + for (i = 0; i < nu->pntsu; i++, bp++) { + bp->weight = (bp->weight - delta) * fac; + } + } + } +} + +static int gp_camera_view_subrect(bContext *C, rctf *subrect) +{ + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + + if (v3d) { + RegionView3D *rv3d = ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + Scene *scene = CTX_data_scene(C); + ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, true); /* no shift */ + return 1; + } + } + + return 0; +} + +/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ +static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode, + 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; + Object *ob; + Curve *cu; + Nurb *nu = NULL; + Base *base_orig = BASACT, *base_new = NULL; + float minmax_weights[2] = {1.0f, 0.0f}; + + /* camera framing */ + rctf subrect, *subrect_ptr = NULL; + + /* error checking */ + if (ELEM(NULL, gpd, gpl, gpf)) + return; + + /* only convert if there are any strokes on this layer's frame to convert */ + if (BLI_listbase_is_empty(&gpf->strokes)) + return; + + /* initialize camera framing */ + if (gp_camera_view_subrect(C, &subrect)) { + subrect_ptr = &subrect; + } + + /* init the curve object (remove rotation and get curve data from it) + * - must clear transforms set on object, as those skew our results + */ + ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); + cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); + base_new = BKE_scene_base_add(scene, ob); + + cu->flag |= CU_3D; + + gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime; + + /* add points to curve */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + const bool add_start_point = (link_strokes && !(prev_gps)); + const bool add_end_point = (link_strokes && !(gps->next)); + + /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, and stitch them to previous one. */ + bool stitch = false; + if (prev_gps) { + bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1]; + bGPDspoint *pt2 = &gps->points[0]; + + if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) { + stitch = true; + } + } + + /* Decide whether we connect this stroke to previous one */ + if (!(stitch || link_strokes)) { + nu = NULL; + } + + switch (mode) { + case GP_STROKECONVERT_PATH: + gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + add_start_point, add_end_point, gtd); + break; + case GP_STROKECONVERT_CURVE: + case GP_STROKECONVERT_POLY: /* convert after */ + gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + add_start_point, add_end_point, gtd); + break; + default: + BLI_assert(!"invalid mode"); + break; + } + prev_gps = gps; + } + + /* If link_strokes, be sure first and last points have a zero weight/size! */ + if (link_strokes) { + gp_stroke_finalize_curve_endpoints(cu); + } + + /* Update curve's weights, if needed */ + if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) { + gp_stroke_norm_curve_weights(cu, minmax_weights); + } + + /* Create the path animation, if needed */ + gp_stroke_path_animation(C, reports, cu, gtd); + + if (mode == GP_STROKECONVERT_POLY) { + for (nu = cu->nurb.first; nu; nu = nu->next) { + BKE_nurb_type_convert(nu, CU_POLY, false); + } + } + + /* set the layer and select */ + 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; +} + +/* --- */ + +/* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator. + * op may be NULL. + */ +static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + bGPDframe *gpf = NULL; + bGPDstroke *gps = NULL; + bGPDspoint *pt; + double base_time, cur_time, prev_time = -1.0; + int i; + bool valid = true; + + if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) + return false; + + do { + base_time = cur_time = gps->inittime; + if (cur_time <= prev_time) { + valid = false; + break; + } + + prev_time = cur_time; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + cur_time = base_time + (double)pt->time; + /* First point of a stroke should have the same time as stroke's inittime, + * so it's the only case where equality is allowed! + */ + if ((i && cur_time <= prev_time) || (cur_time < prev_time)) { + valid = false; + break; + } + prev_time = cur_time; + } + + if (!valid) { + break; + } + } while ((gps = gps->next)); + + if (op) { + RNA_boolean_set(op->ptr, "use_timing_data", valid); + } + return valid; +} + +/* Check end_frame is always > start frame! */ +static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UNUSED(scene), struct PointerRNA *ptr) +{ + int start_frame = RNA_int_get(ptr, "start_frame"); + int end_frame = RNA_int_get(ptr, "end_frame"); + + if (end_frame <= start_frame) { + RNA_int_set(ptr, "end_frame", start_frame + 1); + } +} + +static int gp_convert_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = NULL; + bGPDframe *gpf = NULL; + ScrArea *sa = CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); + + /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), + * and if we are not in edit mode! + */ + return ((sa && sa->spacetype == SPACE_VIEW3D) && + (gpl = gpencil_layer_getactive(gpd)) && + (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) && + (gpf->strokes.first) && + (scene->obedit == NULL)); +} + +static int gp_convert_layer_exec(bContext *C, wmOperator *op) +{ + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); + 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"); + const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); + const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier"); + const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes"); + bool valid_timing; + tGpTimingData gtd; + + /* check if there's data to work with */ + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on"); + return OPERATOR_CANCELLED; + } + + if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) { + BKE_report(op->reports, RPT_WARNING, + "Current Grease Pencil strokes have no valid timing data, most timing options will be hidden!"); + } + valid_timing = RNA_property_boolean_get(op->ptr, prop); + + gtd.mode = RNA_enum_get(op->ptr, "timing_mode"); + /* Check for illegal timing mode! */ + if (!valid_timing && !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) { + gtd.mode = GP_STROKECONVERT_TIMING_LINEAR; + RNA_enum_set(op->ptr, "timing_mode", gtd.mode); + } + if (!link_strokes) { + gtd.mode = GP_STROKECONVERT_TIMING_NONE; + } + + /* grab all relevant settings */ + gtd.frame_range = RNA_int_get(op->ptr, "frame_range"); + gtd.start_frame = RNA_int_get(op->ptr, "start_frame"); + gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false; + gtd.end_frame = RNA_int_get(op->ptr, "end_frame"); + gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration"); + gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness"); + gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration); + gtd.seed = RNA_int_get(op->ptr, "seed"); + gtd.num_points = gtd.cur_point = 0; + gtd.dists = gtd.times = NULL; + gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f; + gtd.inittime = 0.0; + gtd.offset_time = 0.0f; + + /* perform conversion */ + gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); + + /* free temp memory */ + if (gtd.dists) { + MEM_freeN(gtd.dists); + gtd.dists = NULL; + } + if (gtd.times) { + MEM_freeN(gtd.times); + gtd.times = NULL; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + /* done */ + return OPERATOR_FINISHED; +} + +static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes"); + int timing_mode = RNA_enum_get(ptr, "timing_mode"); + bool realtime = RNA_boolean_get(ptr, "use_realtime"); + float gap_duration = RNA_float_get(ptr, "gap_duration"); + float gap_randomness = RNA_float_get(ptr, "gap_randomness"); + const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data"); + + /* Always show those props */ + if (STREQ(prop_id, "type") || + STREQ(prop_id, "use_normalize_weights") || + STREQ(prop_id, "radius_multiplier") || + STREQ(prop_id, "use_link_strokes")) + { + return true; + } + + /* Never show this prop */ + if (STREQ(prop_id, "use_timing_data")) + return false; + + if (link_strokes) { + /* Only show when link_stroke is true */ + if (STREQ(prop_id, "timing_mode")) + return true; + + if (timing_mode != GP_STROKECONVERT_TIMING_NONE) { + /* Only show when link_stroke is true and stroke timing is enabled */ + if (STREQ(prop_id, "frame_range") || + STREQ(prop_id, "start_frame")) + { + return true; + } + + /* Only show if we have valid timing data! */ + if (valid_timing && STREQ(prop_id, "use_realtime")) + return true; + + /* Only show if realtime or valid_timing is false! */ + if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame")) + return true; + + if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { + /* Only show for custom gaps! */ + if (STREQ(prop_id, "gap_duration")) + return true; + + /* Only show randomness for non-null custom gaps! */ + if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f)) + return true; + + /* Only show seed for randomize action! */ + if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) + return true; + } + } + } + + /* Else, hidden! */ + return false; +} + +static void gp_convert_ui(bContext *C, wmOperator *op) +{ + uiLayout *layout = op->layout; + wmWindowManager *wm = CTX_wm_manager(C); + PointerRNA ptr; + + RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); + + /* Main auto-draw call */ + uiDefAutoButsRNA(layout, &ptr, gp_convert_draw_check_prop, '\0'); +} + +void GPENCIL_OT_convert(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Convert Grease Pencil"; + ot->idname = "GPENCIL_OT_convert"; + ot->description = "Convert the active Grease Pencil layer to a new Curve Object"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_convert_layer_exec; + ot->poll = gp_convert_poll; + ot->ui = gp_convert_ui; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to"); + + RNA_def_boolean(ot->srna, "use_normalize_weights", true, "Normalize Weight", + "Normalize weight (set from stroke width)"); + RNA_def_float(ot->srna, "radius_multiplier", 1.0f, 0.0f, 1000.0f, "Radius Fac", + "Multiplier for the points' radii (set from stroke width)", 0.0f, 10.0f); + RNA_def_boolean(ot->srna, "use_link_strokes", true, "Link Strokes", + "Whether to link strokes with zero-radius sections of curves"); + + prop = RNA_def_enum(ot->srna, "timing_mode", prop_gpencil_convert_timingmodes, GP_STROKECONVERT_TIMING_FULL, + "Timing Mode", "How to use timing data stored in strokes"); + RNA_def_enum_funcs(prop, rna_GPConvert_mode_items); + + RNA_def_int(ot->srna, "frame_range", 100, 1, 10000, "Frame Range", + "The duration of evaluation of the path control curve", 1, 1000); + RNA_def_int(ot->srna, "start_frame", 1, 1, 100000, "Start Frame", + "The start frame of the path control curve", 1, 100000); + RNA_def_boolean(ot->srna, "use_realtime", false, "Realtime", + "Whether the path control curve reproduces the drawing in realtime, starting from Start Frame"); + prop = RNA_def_int(ot->srna, "end_frame", 250, 1, 100000, "End Frame", + "The end frame of the path control curve (if Realtime is not set)", 1, 100000); + RNA_def_property_update_runtime(prop, gp_convert_set_end_frame); + + RNA_def_float(ot->srna, "gap_duration", 0.0f, 0.0f, 10000.0f, "Gap Duration", + "Custom Gap mode: (Average) length of gaps, in frames " + "(Note: Realtime value, will be scaled if Realtime is not set)", 0.0f, 1000.0f); + RNA_def_float(ot->srna, "gap_randomness", 0.0f, 0.0f, 10000.0f, "Gap Randomness", + "Custom Gap mode: Number of frames that gap lengths can vary", 0.0f, 1000.0f); + RNA_def_int(ot->srna, "seed", 0, 0, 1000, "Random Seed", + "Custom Gap mode: Random generator seed", 0, 100); + + /* Note: Internal use, this one will always be hidden by UI code... */ + prop = RNA_def_boolean(ot->srna, "use_timing_data", false, "Has Valid Timing", + "Whether the converted Grease Pencil layer has valid timing data (internal use)"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c new file mode 100644 index 00000000000..fa76029bb2e --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -0,0 +1,447 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for dealing with GP datablocks and layers + */ + +/** \file blender/editors/gpencil/gpencil_data.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" + +#include "BLF_translation.h" + +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" +#include "DNA_gpencil_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_object.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_gpencil.h" + +#include "gpencil_intern.h" + + +/* ************************************************ */ +/* Datablock Operators */ + +/* ******************* Add New Data ************************ */ + +/* add new datablock - wrapper around API */ +static int gp_data_add_exec(bContext *C, wmOperator *op) +{ + 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"); + return OPERATOR_CANCELLED; + } + else { + /* decrement user count and add new datablock */ + bGPdata *gpd = (*gpd_ptr); + + id_us_min(&gpd->id); + *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_data_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Add New"; + ot->idname = "GPENCIL_OT_data_add"; + ot->description = "Add new Grease Pencil datablock"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_data_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Unlink Data ************************ */ + +/* poll callback for adding data/layers - special */ +static int gp_data_unlink_poll(bContext *C) +{ + 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); +} + + +/* unlink datablock - wrapper around API */ +static int gp_data_unlink_exec(bContext *C, wmOperator *op) +{ + 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"); + return OPERATOR_CANCELLED; + } + else { + /* just unlink datablock now, decreasing its user count */ + bGPdata *gpd = (*gpd_ptr); + + id_us_min(&gpd->id); + *gpd_ptr = NULL; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_data_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Grease Pencil Unlink"; + ot->idname = "GPENCIL_OT_data_unlink"; + ot->description = "Unlink active Grease Pencil datablock"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_data_unlink_exec; + ot->poll = gp_data_unlink_poll; +} + + +/* ************************************************ */ +/* Layer Operators */ + +/* ******************* Add New Layer ************************ */ + +/* add new layer - wrapper around API */ +static int gp_layer_add_exec(bContext *C, wmOperator *op) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + + /* if there's no existing Grease-Pencil data there, add some */ + if (gpd_ptr == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); + return OPERATOR_CANCELLED; + } + if (*gpd_ptr == NULL) + *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); + + /* add new layer now */ + gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), 1); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add New Layer"; + ot->idname = "GPENCIL_OT_layer_add"; + ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_add_exec; + ot->poll = gp_add_poll; +} + +/* ******************* Remove Active Layer ************************* */ + +static int gp_layer_remove_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; + + if (gpl->flag & GP_LAYER_LOCKED) { + BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers"); + return OPERATOR_CANCELLED; + } + + /* make the layer before this the new active layer + * - use the one after if this is the first + * - if this is the only layer, this naturally becomes NULL + */ + if (gpl->prev) + gpencil_layer_setactive(gpd, gpl->prev); + else + gpencil_layer_setactive(gpd, gpl->next); + + /* delete the layer now... */ + gpencil_layer_delete(gpd, gpl); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Layer"; + ot->idname = "GPENCIL_OT_layer_remove"; + ot->description = "Remove active Grease Pencil layer"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* callbacks */ + ot->exec = gp_layer_remove_exec; + ot->poll = gp_active_layer_poll; +} + +/* ******************* Move Layer Up/Down ************************** */ + +enum { + GP_LAYER_MOVE_UP = -1, + GP_LAYER_MOVE_DOWN = 1 +}; + +static int gp_layer_move_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + + int direction = RNA_enum_get(op->ptr, "type"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; + + /* up or down? */ + if (direction == GP_LAYER_MOVE_UP) { + /* up */ + BLI_remlink(&gpd->layers, gpl); + BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl); + } + else { + /* down */ + BLI_remlink(&gpd->layers, gpl); + BLI_insertlinkafter(&gpd->layers, gpl->next, gpl); + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_move(wmOperatorType *ot) +{ + static EnumPropertyItem slot_move[] = { + {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, + {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Grease Pencil Layer"; + ot->idname = "GPENCIL_OT_layer_move"; + ot->description = "Move the active Grease Pencil layer up/down in the list"; + + /* api callbacks */ + ot->exec = gp_layer_move_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); +} + +/* ********************* Duplicate Layer ************************** */ + +static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + bGPDlayer *new_layer; + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; + + /* make copy of layer, and add it immediately after the existing layer */ + new_layer = gpencil_layer_duplicate(gpl); + BLI_insertlinkafter(&gpd->layers, gpl, new_layer); + + /* ensure new layer has a unique name, and is now the active layer */ + BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); + gpencil_layer_setactive(gpd, new_layer); + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Duplicate Layer"; + ot->idname = "GPENCIL_OT_layer_duplicate"; + ot->description = "Make a copy of the active Grease Pencil layer"; + + /* callbacks */ + ot->exec = gp_layer_copy_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* *********************** Hide Layers ******************************** */ + +static int gp_hide_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *layer = gpencil_layer_getactive(gpd); + bool unselected = RNA_boolean_get(op->ptr, "unselected"); + + /* sanity checks */ + if (ELEM(NULL, gpd, layer)) + return OPERATOR_CANCELLED; + + if (unselected) { + bGPDlayer *gpl; + + /* hide unselected */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if (gpl != layer) { + gpl->flag |= GP_LAYER_HIDE; + } + } + } + else { + /* hide selected/active */ + layer->flag |= GP_LAYER_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Layer(s)"; + ot->idname = "GPENCIL_OT_hide"; + ot->description = "Hide selected/unselected Grease Pencil layers"; + + /* callbacks */ + ot->exec = gp_hide_exec; + ot->poll = gp_active_layer_poll; /* NOTE: we need an active layer to play with */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected layers"); +} + +/* ********************** Show All Layers ***************************** */ + +/* poll callback for showing layers */ +static int gp_reveal_poll(bContext *C) +{ + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gp_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl; + + /* sanity checks */ + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* make all layers visible */ + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + gpl->flag &= ~GP_LAYER_HIDE; + } + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Show All Layers"; + ot->idname = "GPENCIL_OT_reveal"; + ot->description = "Show all Grease Pencil layers"; + + /* callbacks */ + ot->exec = gp_reveal_exec; + ot->poll = gp_reveal_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 212d384e5ca..2c6e9d75337 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -21,6 +21,8 @@ * Contributor(s): Joshua Leung * * ***** END GPL LICENSE BLOCK ***** + * + * Operators for editing Grease Pencil strokes */ /** \file blender/editors/gpencil/gpencil_edit.c @@ -38,15 +40,10 @@ #include "BLI_math.h" #include "BLI_blenlib.h" -#include "BLI_rand.h" #include "BLI_utildefines.h" #include "BLF_translation.h" -#include "DNA_anim_types.h" -#include "DNA_curve_types.h" -#include "DNA_object_types.h" -#include "DNA_node_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -54,17 +51,11 @@ #include "DNA_gpencil_types.h" #include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_depsgraph.h" -#include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_library.h" -#include "BKE_object.h" #include "BKE_report.h" -#include "BKE_scene.h" #include "BKE_screen.h" -#include "BKE_tracking.h" #include "UI_interface.h" @@ -78,454 +69,9 @@ #include "ED_gpencil.h" #include "ED_view3d.h" -#include "ED_clip.h" -#include "ED_keyframing.h" #include "gpencil_intern.h" - -/* ************************************************ */ -/* Context Wrangling... */ - -/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, - * when context info is not available. - */ -bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) -{ - /* if there's an active area, check if the particular editor may - * have defined any special Grease Pencil context for editing... - */ - if (sa) { - SpaceLink *sl = sa->spacedata.first; - - switch (sa->spacetype) { - case SPACE_VIEW3D: /* 3D-View */ - case SPACE_TIME: /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */ - case SPACE_ACTION: /* DepeSheet - XXX: this is a hack to get the keyframe jump operator to take GP Keyframes into account */ - { - BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, - GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); - - if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { - /* legacy behaviour for usage with old addons requiring object-linked to objects */ - - /* just in case no active/selected object... */ - if (ob && (ob->flag & SELECT)) { - /* for now, as long as there's an object, default to using that in 3D-View */ - if (ptr) RNA_id_pointer_create(&ob->id, ptr); - return &ob->gpd; - } - /* else: defaults to scene... */ - } - else { - if (ptr) RNA_id_pointer_create(&scene->id, ptr); - return &scene->gpd; - } - break; - } - case SPACE_NODE: /* Nodes Editor */ - { - SpaceNode *snode = (SpaceNode *)sl; - - /* return the GP data for the active node block/node */ - if (snode && snode->nodetree) { - /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ - if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr); - return &snode->nodetree->gpd; - } - - /* even when there is no node-tree, don't allow this to flow to scene */ - return NULL; - } - case SPACE_SEQ: /* Sequencer */ - { - SpaceSeq *sseq = (SpaceSeq *)sl; - - /* for now, Grease Pencil data is associated with the space (actually preview region only) */ - /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr); - return &sseq->gpd; - } - case SPACE_IMAGE: /* Image/UV Editor */ - { - SpaceImage *sima = (SpaceImage *)sl; - - /* for now, Grease Pencil data is associated with the space... */ - /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr); - return &sima->gpd; - } - case SPACE_CLIP: /* Nodes Editor */ - { - SpaceClip *sc = (SpaceClip *)sl; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip) { - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - - if (!track) - return NULL; - - if (ptr) - RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr); - - return &track->gpd; - } - else { - if (ptr) - RNA_id_pointer_create(&clip->id, ptr); - - return &clip->gpd; - } - } - break; - } - default: /* unsupported space */ - return NULL; - } - } - - /* just fall back on the scene's GP data */ - if (ptr) RNA_id_pointer_create((ID *)scene, ptr); - return (scene) ? &scene->gpd : NULL; -} - -/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ -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); - ScrArea *sa = CTX_wm_area(C); - Object *ob = CTX_data_active_object(C); - - return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); -} - -/* -------------------------------------------------------- */ - -/* Get the active Grease Pencil datablock, when context is not available */ -bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) -{ - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL); - return (gpd_ptr) ? *(gpd_ptr) : NULL; -} - -/* Get the active Grease Pencil datablock */ -bGPdata *ED_gpencil_data_get_active(const bContext *C) -{ - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - return (gpd_ptr) ? *(gpd_ptr) : NULL; -} - -/* -------------------------------------------------------- */ - -// XXX: this should be removed... We really shouldn't duplicate logic like this! -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 ED_gpencil_data_get_active's behavior. - */ - - if (base && TESTBASE(v3d, base)) { - gpd = base->object->gpd; - } - return gpd ? gpd : scene->gpd; -} - -/* ************************************************ */ -/* Panel Operators */ - -/* poll callback for adding data/layers - special */ -static int gp_add_poll(bContext *C) -{ - /* the base line we have is that we have somewhere to add Grease Pencil data */ - return ED_gpencil_data_get_pointers(C, NULL) != NULL; -} - -/* poll callback for checking if there is an active layer */ -static int gp_active_layer_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - - return (gpl != NULL); -} - -/* ******************* Add New Data ************************ */ - -/* add new datablock - wrapper around API */ -static int gp_data_add_exec(bContext *C, wmOperator *op) -{ - 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"); - return OPERATOR_CANCELLED; - } - else { - /* decrement user count and add new datablock */ - bGPdata *gpd = (*gpd_ptr); - - id_us_min(&gpd->id); - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_data_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Grease Pencil Add New"; - ot->idname = "GPENCIL_OT_data_add"; - ot->description = "Add new Grease Pencil datablock"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_data_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Unlink Data ************************ */ - -/* poll callback for adding data/layers - special */ -static int gp_data_unlink_poll(bContext *C) -{ - 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); -} - - -/* unlink datablock - wrapper around API */ -static int gp_data_unlink_exec(bContext *C, wmOperator *op) -{ - 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"); - return OPERATOR_CANCELLED; - } - else { - /* just unlink datablock now, decreasing its user count */ - bGPdata *gpd = (*gpd_ptr); - - id_us_min(&gpd->id); - *gpd_ptr = NULL; - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_data_unlink(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Grease Pencil Unlink"; - ot->idname = "GPENCIL_OT_data_unlink"; - ot->description = "Unlink active Grease Pencil datablock"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_data_unlink_exec; - ot->poll = gp_data_unlink_poll; -} - -/* ******************* Add New Layer ************************ */ - -/* add new layer - wrapper around API */ -static int gp_layer_add_exec(bContext *C, wmOperator *op) -{ - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - - /* if there's no existing Grease-Pencil data there, add some */ - if (gpd_ptr == NULL) { - BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); - return OPERATOR_CANCELLED; - } - if (*gpd_ptr == NULL) - *gpd_ptr = gpencil_data_addnew(DATA_("GPencil")); - - /* add new layer now */ - gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), 1); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_layer_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add New Layer"; - ot->idname = "GPENCIL_OT_layer_add"; - ot->description = "Add new Grease Pencil layer for the active Grease Pencil datablock"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_layer_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Remove Active Layer ************************* */ - -static int gp_layer_remove_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; - - if (gpl->flag & GP_LAYER_LOCKED) { - BKE_report(op->reports, RPT_ERROR, "Cannot delete locked layers"); - return OPERATOR_CANCELLED; - } - - /* make the layer before this the new active layer - * - use the one after if this is the first - * - if this is the only layer, this naturally becomes NULL - */ - if (gpl->prev) - gpencil_layer_setactive(gpd, gpl->prev); - else - gpencil_layer_setactive(gpd, gpl->next); - - /* delete the layer now... */ - gpencil_layer_delete(gpd, gpl); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_layer_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Layer"; - ot->idname = "GPENCIL_OT_layer_remove"; - ot->description = "Remove active Grease Pencil layer"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_layer_remove_exec; - ot->poll = gp_active_layer_poll; -} - -/* ******************* Move Layer Up/Down ************************** */ - -enum { - GP_LAYER_MOVE_UP = -1, - GP_LAYER_MOVE_DOWN = 1 -}; - -static int gp_layer_move_exec(bContext *C, wmOperator *op) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - - int direction = RNA_enum_get(op->ptr, "type"); - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; - - /* up or down? */ - if (direction == GP_LAYER_MOVE_UP) { - /* up */ - BLI_remlink(&gpd->layers, gpl); - BLI_insertlinkbefore(&gpd->layers, gpl->prev, gpl); - } - else { - /* down */ - BLI_remlink(&gpd->layers, gpl); - BLI_insertlinkafter(&gpd->layers, gpl->next, gpl); - } - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_layer_move(wmOperatorType *ot) -{ - static EnumPropertyItem slot_move[] = { - {GP_LAYER_MOVE_UP, "UP", 0, "Up", ""}, - {GP_LAYER_MOVE_DOWN, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL} - }; - - /* identifiers */ - ot->name = "Move Grease Pencil Layer"; - ot->idname = "GPENCIL_OT_layer_move"; - ot->description = "Move the active Grease Pencil layer up/down in the list"; - - /* api callbacks */ - ot->exec = gp_layer_move_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - ot->prop = RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); -} - -/* ********************* Duplicate Layer ************************** */ - -static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = gpencil_layer_getactive(gpd); - bGPDlayer *new_layer; - - /* sanity checks */ - if (ELEM(NULL, gpd, gpl)) - return OPERATOR_CANCELLED; - - /* make copy of layer, and add it immediately after the existing layer */ - new_layer = gpencil_layer_duplicate(gpl); - BLI_insertlinkafter(&gpd->layers, gpl, new_layer); - - /* ensure new layer has a unique name, and is now the active layer */ - BLI_uniquename(&gpd->layers, new_layer, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(new_layer->info)); - gpencil_layer_setactive(gpd, new_layer); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); - - return OPERATOR_FINISHED; -} - -void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Duplicate Layer"; - ot->idname = "GPENCIL_OT_layer_duplicate"; - ot->description = "Make a copy of the active Grease Pencil layer"; - - /* callbacks */ - ot->exec = gp_layer_copy_exec; - ot->poll = gp_active_layer_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* ************************************************ */ /* Stroke Editing Operators */ @@ -701,7 +247,7 @@ void ED_gpencil_strokes_copybuf_free(void) BLI_freelinkN(&gp_strokes_copypastebuf, gps); } - gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL; + BLI_listbase_clear(&gp_strokes_copypastebuf); } /* --------------------- */ @@ -1256,1398 +802,3 @@ void GPENCIL_OT_delete(wmOperatorType *ot) } /* ************************************************ */ -/* Grease Pencil to Data Operator */ - -/* defines for possible modes */ -enum { - GP_STROKECONVERT_PATH = 1, - GP_STROKECONVERT_CURVE, - GP_STROKECONVERT_POLY, -}; - -/* Defines for possible timing modes */ -enum { - GP_STROKECONVERT_TIMING_NONE = 1, - GP_STROKECONVERT_TIMING_LINEAR = 2, - GP_STROKECONVERT_TIMING_FULL = 3, - GP_STROKECONVERT_TIMING_CUSTOMGAP = 4, -}; - -/* RNA enum define */ -static EnumPropertyItem prop_gpencil_convertmodes[] = { - {GP_STROKECONVERT_PATH, "PATH", 0, "Path", ""}, - {GP_STROKECONVERT_CURVE, "CURVE", 0, "Bezier Curve", ""}, - {GP_STROKECONVERT_POLY, "POLY", 0, "Polygon Curve", ""}, - {0, NULL, 0, NULL, NULL} -}; - -static EnumPropertyItem prop_gpencil_convert_timingmodes_restricted[] = { - {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, - {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, - {0, NULL, 0, NULL, NULL}, -}; - -static EnumPropertyItem prop_gpencil_convert_timingmodes[] = { - {GP_STROKECONVERT_TIMING_NONE, "NONE", 0, "No Timing", "Ignore timing"}, - {GP_STROKECONVERT_TIMING_LINEAR, "LINEAR", 0, "Linear", "Simple linear timing"}, - {GP_STROKECONVERT_TIMING_FULL, "FULL", 0, "Original", "Use the original timing, gaps included"}, - {GP_STROKECONVERT_TIMING_CUSTOMGAP, "CUSTOMGAP", 0, "Custom Gaps", - "Use the original timing, but with custom gap lengths (in frames)"}, - {0, NULL, 0, NULL, NULL}, -}; - -static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), - bool *UNUSED(r_free)) -{ - if (RNA_boolean_get(ptr, "use_timing_data")) { - return prop_gpencil_convert_timingmodes; - } - return prop_gpencil_convert_timingmodes_restricted; -} - -/* --- */ - -/* convert the coordinates from the given stroke point into 3d-coordinates - * - assumes that the active space is the 3D-View - */ -static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect) -{ - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - - if (gps->flag & GP_STROKE_3DSPACE) { - /* directly use 3d-coordinates */ - copy_v3_v3(p3d, &pt->x); - } - else { - const float *fp = ED_view3d_cursor3d_get(scene, v3d); - float mvalf[2]; - - /* get screen coordinate */ - if (gps->flag & GP_STROKE_2DSPACE) { - View2D *v2d = &ar->v2d; - UI_view2d_view_to_region_fl(v2d, pt->x, pt->y, &mvalf[0], &mvalf[1]); - } - else { - if (subrect) { - mvalf[0] = (((float)pt->x / 100.0f) * BLI_rctf_size_x(subrect)) + subrect->xmin; - mvalf[1] = (((float)pt->y / 100.0f) * BLI_rctf_size_y(subrect)) + subrect->ymin; - } - else { - mvalf[0] = (float)pt->x / 100.0f * ar->winx; - mvalf[1] = (float)pt->y / 100.0f * ar->winy; - } - } - - ED_view3d_win_to_3d(ar, fp, mvalf, p3d); - } -} - -/* --- */ - -/* temp struct for gp_stroke_path_animation() */ -typedef struct tGpTimingData { - /* Data set from operator settings */ - int mode; - int frame_range; /* Number of frames evaluated for path animation */ - int start_frame, end_frame; - bool realtime; /* Will overwrite end_frame in case of Original or CustomGap timing... */ - float gap_duration, gap_randomness; /* To be used with CustomGap mode*/ - int seed; - - /* Data set from points, used to compute final timing FCurve */ - int num_points, cur_point; - - /* Distances */ - float *dists; - float tot_dist; - - /* Times */ - float *times; /* Note: Gap times will be negative! */ - float tot_time, gap_tot_time; - double inittime; - - /* Only used during creation of dists & times lists. */ - float offset_time; -} tGpTimingData; - -/* Init point buffers for timing data. - * Note this assumes we only grow those arrays! - */ -static void gp_timing_data_set_nbr(tGpTimingData *gtd, const int nbr) -{ - float *tmp; - - BLI_assert(nbr > gtd->num_points); - - /* distances */ - tmp = gtd->dists; - gtd->dists = MEM_callocN(sizeof(float) * nbr, __func__); - if (tmp) { - memcpy(gtd->dists, tmp, sizeof(float) * gtd->num_points); - MEM_freeN(tmp); - } - - /* times */ - tmp = gtd->times; - gtd->times = MEM_callocN(sizeof(float) * nbr, __func__); - if (tmp) { - memcpy(gtd->times, tmp, sizeof(float) * gtd->num_points); - MEM_freeN(tmp); - } - - gtd->num_points = nbr; -} - -/* add stroke point to timing buffers */ -static void gp_timing_data_add_point(tGpTimingData *gtd, const double stroke_inittime, const float time, - const float delta_dist) -{ - float delta_time = 0.0f; - const int cur_point = gtd->cur_point; - - if (!cur_point) { - /* Special case, first point, if time is not 0.0f we have to compensate! */ - gtd->offset_time = -time; - gtd->times[cur_point] = 0.0f; - } - else if (time < 0.0f) { - /* This is a gap, negative value! */ - gtd->times[cur_point] = -(((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); - delta_time = -gtd->times[cur_point] - gtd->times[cur_point - 1]; - - gtd->gap_tot_time += delta_time; - } - else { - gtd->times[cur_point] = (((float)(stroke_inittime - gtd->inittime)) + time + gtd->offset_time); - delta_time = gtd->times[cur_point] - fabsf(gtd->times[cur_point - 1]); - } - - gtd->tot_time += delta_time; - gtd->tot_dist += delta_dist; - gtd->dists[cur_point] = gtd->tot_dist; - - gtd->cur_point++; -} - -/* In frames! Binary search for FCurve keys have a threshold of 0.01, so we can't set - * arbitrarily close points - this is esp. important with NoGaps mode! - */ -#define MIN_TIME_DELTA 0.02f - -/* Loop over next points to find the end of the stroke, and compute */ -static int gp_find_end_of_stroke_idx(tGpTimingData *gtd, RNG *rng, const int idx, const int nbr_gaps, - int *nbr_done_gaps, const float tot_gaps_time, const float delta_time, - float *next_delta_time) -{ - int j; - - for (j = idx + 1; j < gtd->num_points; j++) { - if (gtd->times[j] < 0) { - gtd->times[j] = -gtd->times[j]; - if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - /* In this mode, gap time between this stroke and the next should be 0 currently... - * So we have to compute its final duration! - */ - if (gtd->gap_randomness > 0.0f) { - /* We want gaps that are in gtd->gap_duration +/- gtd->gap_randomness range, - * and which sum to exactly tot_gaps_time... - */ - int rem_gaps = nbr_gaps - (*nbr_done_gaps); - if (rem_gaps < 2) { - /* Last gap, just give remaining time! */ - *next_delta_time = tot_gaps_time; - } - else { - float delta, min, max; - - /* This code ensures that if the first gaps have been shorter than average gap_duration, - * next gaps will tend to be longer (i.e. try to recover the lateness), and vice-versa! - */ - delta = delta_time - (gtd->gap_duration * (*nbr_done_gaps)); - - /* Clamp min between [-gap_randomness, 0.0], with lower delta giving higher min */ - min = -gtd->gap_randomness - delta; - CLAMP(min, -gtd->gap_randomness, 0.0f); - - /* Clamp max between [0.0, gap_randomness], with lower delta giving higher max */ - max = gtd->gap_randomness - delta; - CLAMP(max, 0.0f, gtd->gap_randomness); - *next_delta_time += gtd->gap_duration + (BLI_rng_get_float(rng) * (max - min)) + min; - } - } - else { - *next_delta_time += gtd->gap_duration; - } - } - (*nbr_done_gaps)++; - break; - } - } - - return j - 1; -} - -static void gp_stroke_path_animation_preprocess_gaps(tGpTimingData *gtd, RNG *rng, int *nbr_gaps, float *tot_gaps_time) -{ - int i; - float delta_time = 0.0f; - - for (i = 0; i < gtd->num_points; i++) { - if (gtd->times[i] < 0 && i) { - (*nbr_gaps)++; - gtd->times[i] = -gtd->times[i] - delta_time; - delta_time += gtd->times[i] - gtd->times[i - 1]; - gtd->times[i] = -gtd->times[i - 1]; /* Temp marker, values *have* to be different! */ - } - else { - gtd->times[i] -= delta_time; - } - } - gtd->tot_time -= delta_time; - - *tot_gaps_time = (float)(*nbr_gaps) * gtd->gap_duration; - gtd->tot_time += *tot_gaps_time; - if (G.debug & G_DEBUG) { - printf("%f, %f, %f, %d\n", gtd->tot_time, delta_time, *tot_gaps_time, *nbr_gaps); - } - if (gtd->gap_randomness > 0.0f) { - BLI_rng_srandom(rng, gtd->seed); - } -} - -static void gp_stroke_path_animation_add_keyframes(ReportList *reports, PointerRNA ptr, PropertyRNA *prop, FCurve *fcu, - Curve *cu, tGpTimingData *gtd, RNG *rng, const float time_range, - const int nbr_gaps, const float tot_gaps_time) -{ - /* Use actual recorded timing! */ - const float time_start = (float)gtd->start_frame; - - float last_valid_time = 0.0f; - int end_stroke_idx = -1, start_stroke_idx = 0; - float end_stroke_time = 0.0f; - - /* CustomGaps specific */ - float delta_time = 0.0f, next_delta_time = 0.0f; - int nbr_done_gaps = 0; - - int i; - float cfra; - - /* This is a bit tricky, as: - * - We can't add arbitrarily close points on FCurve (in time). - * - We *must* have all "caps" points of all strokes in FCurve, as much as possible! - */ - for (i = 0; i < gtd->num_points; i++) { - /* If new stroke... */ - if (i > end_stroke_idx) { - start_stroke_idx = i; - delta_time = next_delta_time; - /* find end of that new stroke */ - end_stroke_idx = gp_find_end_of_stroke_idx(gtd, rng, i, nbr_gaps, &nbr_done_gaps, - tot_gaps_time, delta_time, &next_delta_time); - /* This one should *never* be negative! */ - end_stroke_time = time_start + ((gtd->times[end_stroke_idx] + delta_time) / gtd->tot_time * time_range); - } - - /* Simple proportional stuff... */ - cu->ctime = gtd->dists[i] / gtd->tot_dist * cu->pathlen; - cfra = time_start + ((gtd->times[i] + delta_time) / gtd->tot_time * time_range); - - /* And now, the checks about timing... */ - if (i == start_stroke_idx) { - /* If first point of a stroke, be sure it's enough ahead of last valid keyframe, and - * that the end point of the stroke is far enough! - * In case it is not, we keep the end point... - * Note that with CustomGaps mode, this is here we set the actual gap timing! - */ - if ((end_stroke_time - last_valid_time) > MIN_TIME_DELTA * 2) { - if ((cfra - last_valid_time) < MIN_TIME_DELTA) { - cfra = last_valid_time + MIN_TIME_DELTA; - } - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); - last_valid_time = cfra; - } - else if (G.debug & G_DEBUG) { - printf("\t Skipping start point %d, too close from end point %d\n", i, end_stroke_idx); - } - } - else if (i == end_stroke_idx) { - /* Always try to insert end point of a curve (should be safe enough, anyway...) */ - if ((cfra - last_valid_time) < MIN_TIME_DELTA) { - cfra = last_valid_time + MIN_TIME_DELTA; - } - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); - last_valid_time = cfra; - } - else { - /* Else ("middle" point), we only insert it if it's far enough from last keyframe, - * and also far enough from (not yet added!) end_stroke keyframe! - */ - if ((cfra - last_valid_time) > MIN_TIME_DELTA && (end_stroke_time - cfra) > MIN_TIME_DELTA) { - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); - last_valid_time = cfra; - } - else if (G.debug & G_DEBUG) { - printf("\t Skipping \"middle\" point %d, too close from last added point or end point %d\n", - i, end_stroke_idx); - } - } - } -} - -static void gp_stroke_path_animation(bContext *C, ReportList *reports, Curve *cu, tGpTimingData *gtd) -{ - Scene *scene = CTX_data_scene(C); - bAction *act; - FCurve *fcu; - PointerRNA ptr; - PropertyRNA *prop = NULL; - int nbr_gaps = 0, i; - - if (gtd->mode == GP_STROKECONVERT_TIMING_NONE) - return; - - /* gap_duration and gap_randomness are in frames, but we need seconds!!! */ - gtd->gap_duration = FRA2TIME(gtd->gap_duration); - gtd->gap_randomness = FRA2TIME(gtd->gap_randomness); - - /* Enable path! */ - cu->flag |= CU_PATH; - cu->pathlen = gtd->frame_range; - - /* Get RNA pointer to read/write path time values */ - RNA_id_pointer_create((ID *)cu, &ptr); - prop = RNA_struct_find_property(&ptr, "eval_time"); - - /* Ensure we have an F-Curve to add keyframes to */ - act = verify_adt_action((ID *)cu, true); - fcu = verify_fcurve(act, NULL, &ptr, "eval_time", 0, true); - - if (G.debug & G_DEBUG) { - printf("%s: tot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - } - - if (gtd->mode == GP_STROKECONVERT_TIMING_LINEAR) { - float cfra; - - /* Linear extrapolation! */ - fcu->extend = FCURVE_EXTRAPOLATE_LINEAR; - - cu->ctime = 0.0f; - cfra = (float)gtd->start_frame; - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); - - cu->ctime = cu->pathlen; - if (gtd->realtime) { - cfra += (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ - } - else { - cfra = (float)gtd->end_frame; - } - insert_keyframe_direct(reports, ptr, prop, fcu, cfra, INSERTKEY_FAST); - } - else { - /* Use actual recorded timing! */ - RNG *rng = BLI_rng_new(0); - float time_range; - - /* CustomGaps specific */ - float tot_gaps_time = 0.0f; - - /* Pre-process gaps, in case we don't want to keep their original timing */ - if (gtd->mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - gp_stroke_path_animation_preprocess_gaps(gtd, rng, &nbr_gaps, &tot_gaps_time); - } - - if (gtd->realtime) { - time_range = (float)TIME2FRA(gtd->tot_time); /* Seconds to frames */ - } - else { - time_range = (float)(gtd->end_frame - gtd->start_frame); - } - - if (G.debug & G_DEBUG) { - printf("GP Stroke Path Conversion: Starting keying!\n"); - } - - gp_stroke_path_animation_add_keyframes(reports, ptr, prop, fcu, cu, gtd, rng, time_range, - nbr_gaps, tot_gaps_time); - - BLI_rng_free(rng); - } - - /* As we used INSERTKEY_FAST mode, we need to recompute all curve's handles now */ - calchandles_fcurve(fcu); - - if (G.debug & G_DEBUG) { - printf("%s: \ntot len: %f\t\ttot time: %f\n", __func__, gtd->tot_dist, gtd->tot_time); - for (i = 0; i < gtd->num_points; i++) { - printf("\tpoint %d:\t\tlen: %f\t\ttime: %f\n", i, gtd->dists[i], gtd->times[i]); - } - printf("\n\n"); - } - - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); - - /* send updates */ - DAG_id_tag_update(&cu->id, 0); -} - -#undef MIN_TIME_DELTA - -#define GAP_DFAC 0.01f -#define WIDTH_CORR_FAC 0.1f -#define BEZT_HANDLE_FAC 0.3f - -/* convert stroke to 3d path */ - -/* helper */ -static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const float p[3], const float prev_p[3], - const bool do_gtd, const double inittime, const float time, - const float width, const float rad_fac, float minmax_weights[2]) -{ - copy_v3_v3(bp->vec, p); - bp->vec[3] = 1.0f; - - /* set settings */ - bp->f1 = SELECT; - bp->radius = width * rad_fac; - bp->weight = width; - CLAMP(bp->weight, 0.0f, 1.0f); - if (bp->weight < minmax_weights[0]) { - minmax_weights[0] = bp->weight; - } - else if (bp->weight > minmax_weights[1]) { - minmax_weights[1] = bp->weight; - } - - /* Update timing data */ - if (do_gtd) { - gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); - } -} - -static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, - float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, - const bool add_end_point, tGpTimingData *gtd) -{ - bGPDspoint *pt; - Nurb *nu = (curnu) ? *curnu : NULL; - BPoint *bp, *prev_bp = NULL; - const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); - const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); - int i, old_nbp = 0; - - /* create new 'nurb' or extend current one within the curve */ - if (nu) { - old_nbp = nu->pntsu; - - /* If stitch, the first point of this stroke is already present in current nu. - * Else, we have to add two additional points to make the zero-radius link between strokes. - */ - BKE_nurb_points_add(nu, gps->totpoints + (stitch ? -1 : 2) + add_start_end_points); - } - else { - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_path(nurb)"); - - nu->pntsu = gps->totpoints + add_start_end_points; - nu->pntsv = 1; - nu->orderu = 2; /* point-to-point! */ - nu->type = CU_NURBS; - nu->flagu = CU_NURB_ENDPOINT; - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - nu->knotsu = NULL; - - nu->bp = (BPoint *)MEM_callocN(sizeof(BPoint) * nu->pntsu, "bpoints"); - - stitch = false; /* Security! */ - } - - if (do_gtd) { - gp_timing_data_set_nbr(gtd, nu->pntsu); - } - - /* If needed, make the link between both strokes with two zero-radius additional points */ - /* About "zero-radius" point interpolations: - * - If we have at least two points in current curve (most common case), we linearly extrapolate - * the last segment to get the first point (p1) position and timing. - * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point - * with the first point of the current stroke. - * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated - * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... - */ - if (curnu && !stitch && old_nbp) { - float p1[3], p2[3], p[3], next_p[3]; - float dt1 = 0.0f, dt2 = 0.0f; - - BLI_assert(gps->prev != NULL); - - prev_bp = NULL; - if ((old_nbp > 1) && (gps->prev->totpoints > 1)) { - /* Only use last curve segment if previous stroke was not a single-point one! */ - prev_bp = &nu->bp[old_nbp - 2]; - } - bp = &nu->bp[old_nbp - 1]; - - /* First point */ - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); - if (prev_bp) { - interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); - if (do_gtd) { - const int idx = gps->prev->totpoints - 1; - dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p1, bp->vec, p, GAP_DFAC); - if (do_gtd) { - dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); - } - } - bp++; - gp_stroke_to_path_add_point(gtd, bp, p1, (bp - 1)->vec, do_gtd, gps->prev->inittime, dt1, - 0.0f, rad_fac, minmax_weights); - - /* Second point */ - /* Note dt2 is always negative, which marks the gap. */ - if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); - interp_v3_v3v3(p2, p, next_p, -GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p2, p, bp->vec, GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); - } - } - bp++; - gp_stroke_to_path_add_point(gtd, bp, p2, p1, do_gtd, gps->inittime, dt2, 0.0f, rad_fac, minmax_weights); - - old_nbp += 2; - } - else if (add_start_point) { - float p[3], next_p[3]; - float dt = 0.0f; - - gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect); - if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect); - interp_v3_v3v3(p, p, next_p, -GAP_DFAC); - if (do_gtd) { - dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - p[0] -= GAP_DFAC; /* Rather arbitrary... */ - dt = -GAP_DFAC; /* Rather arbitrary too! */ - } - bp = &nu->bp[old_nbp]; - /* Note we can't give anything else than 0.0 as time here, since a negative one (which would be expected value) - * would not work (it would be *before* gtd->inittime, which is not supported currently). - */ - gp_stroke_to_path_add_point(gtd, bp, p, p, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); - - old_nbp++; - } - - if (old_nbp) { - prev_bp = &nu->bp[old_nbp - 1]; - } - - /* add points */ - for (i = (stitch) ? 1 : 0, pt = &gps->points[(stitch) ? 1 : 0], bp = &nu->bp[old_nbp]; - i < gps->totpoints; - i++, pt++, bp++) - { - float p[3]; - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; - - /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gps, pt, p, subrect); - - gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time, - width, rad_fac, minmax_weights); - - prev_bp = bp; - } - - if (add_end_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, prev_bp->vec, (prev_bp - 1)->vec, -GAP_DFAC); - if (do_gtd) { - const int idx = gps->totpoints - 1; - dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, prev_bp->vec); - p[0] += GAP_DFAC; /* Rather arbitrary... */ - dt = GAP_DFAC; /* Rather arbitrary too! */ - } - /* Note bp has already been incremented in main loop above, so it points to the right place. */ - gp_stroke_to_path_add_point(gtd, bp, p, prev_bp->vec, do_gtd, gps->inittime, dt, 0.0f, rad_fac, minmax_weights); - } - - /* add nurb to curve */ - if (!curnu || !*curnu) { - BLI_addtail(&cu->nurb, nu); - } - if (curnu) { - *curnu = nu; - } - - BKE_nurb_knot_calc_u(nu); -} - -/* convert stroke to 3d bezier */ - -/* helper */ -static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt, - const float p[3], const float h1[3], const float h2[3], const float prev_p[3], - const bool do_gtd, const double inittime, const float time, - const float width, const float rad_fac, float minmax_weights[2]) -{ - copy_v3_v3(bezt->vec[0], h1); - copy_v3_v3(bezt->vec[1], p); - copy_v3_v3(bezt->vec[2], h2); - - /* set settings */ - bezt->h1 = bezt->h2 = HD_FREE; - bezt->f1 = bezt->f2 = bezt->f3 = SELECT; - bezt->radius = width * rad_fac; - bezt->weight = width; - CLAMP(bezt->weight, 0.0f, 1.0f); - if (bezt->weight < minmax_weights[0]) { - minmax_weights[0] = bezt->weight; - } - else if (bezt->weight > minmax_weights[1]) { - minmax_weights[1] = bezt->weight; - } - - /* Update timing data */ - if (do_gtd) { - gp_timing_data_add_point(gtd, inittime, time, len_v3v3(prev_p, p)); - } -} - -static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, - float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, - const bool add_end_point, tGpTimingData *gtd) -{ - bGPDspoint *pt; - Nurb *nu = (curnu) ? *curnu : NULL; - BezTriple *bezt, *prev_bezt = NULL; - int i, tot, old_nbezt = 0; - const int add_start_end_points = (add_start_point ? 1 : 0) + (add_end_point ? 1 : 0); - float p3d_cur[3], p3d_prev[3], p3d_next[3], h1[3], h2[3]; - const bool do_gtd = (gtd->mode != GP_STROKECONVERT_TIMING_NONE); - - /* create new 'nurb' or extend current one within the curve */ - if (nu) { - old_nbezt = nu->pntsu; - /* If we do stitch, first point of current stroke is assumed the same as last point of previous stroke, - * so no need to add it. - * If no stitch, we want to add two additional points to make a "zero-radius" link between both strokes. - */ - BKE_nurb_bezierPoints_add(nu, gps->totpoints + ((stitch) ? -1 : 2) + add_start_end_points); - } - else { - nu = (Nurb *)MEM_callocN(sizeof(Nurb), "gpstroke_to_bezier(nurb)"); - - nu->pntsu = gps->totpoints + add_start_end_points; - nu->resolu = 12; - nu->resolv = 12; - nu->type = CU_BEZIER; - nu->bezt = (BezTriple *)MEM_callocN(sizeof(BezTriple) * nu->pntsu, "bezts"); - - stitch = false; /* Security! */ - } - - if (do_gtd) { - gp_timing_data_set_nbr(gtd, nu->pntsu); - } - - tot = gps->totpoints; - - /* get initial coordinates */ - pt = gps->points; - if (tot) { - gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); - if (tot > 1) { - gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); - } - if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); - } - } - - /* If needed, make the link between both strokes with two zero-radius additional points */ - if (curnu && old_nbezt) { - BLI_assert(gps->prev != NULL); - - /* Update last point's second handle */ - if (stitch) { - bezt = &nu->bezt[old_nbezt - 1]; - interp_v3_v3v3(h2, bezt->vec[1], p3d_cur, BEZT_HANDLE_FAC); - copy_v3_v3(bezt->vec[2], h2); - pt++; - } - - /* Create "link points" */ - /* About "zero-radius" point interpolations: - * - If we have at least two points in current curve (most common case), we linearly extrapolate - * the last segment to get the first point (p1) position and timing. - * - If we do not have those (quite odd, but may happen), we linearly interpolate the last point - * with the first point of the current stroke. - * The same goes for the second point, first segment of the current stroke is "negatively" extrapolated - * if it exists, else (if the stroke is a single point), linear interpolation with last curve point... - */ - else { - float p1[3], p2[3]; - float dt1 = 0.0f, dt2 = 0.0f; - - prev_bezt = NULL; - if ((old_nbezt > 1) && (gps->prev->totpoints > 1)) { - /* Only use last curve segment if previous stroke was not a single-point one! */ - prev_bezt = &nu->bezt[old_nbezt - 2]; - } - bezt = &nu->bezt[old_nbezt - 1]; - - /* First point */ - if (prev_bezt) { - interp_v3_v3v3(p1, prev_bezt->vec[1], bezt->vec[1], 1.0f + GAP_DFAC); - if (do_gtd) { - const int idx = gps->prev->totpoints - 1; - dt1 = interpf(gps->prev->points[idx - 1].time, gps->prev->points[idx].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p1, bezt->vec[1], p3d_cur, GAP_DFAC); - if (do_gtd) { - dt1 = interpf(gps->inittime - gps->prev->inittime, 0.0f, GAP_DFAC); - } - } - - /* Second point */ - /* Note dt2 is always negative, which marks the gap. */ - if (tot > 1) { - interp_v3_v3v3(p2, p3d_cur, p3d_next, -GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - interp_v3_v3v3(p2, p3d_cur, bezt->vec[1], GAP_DFAC); - if (do_gtd) { - dt2 = interpf(gps->prev->inittime - gps->inittime, 0.0f, GAP_DFAC); - } - } - - /* Second handle of last point of previous stroke. */ - interp_v3_v3v3(h2, bezt->vec[1], p1, BEZT_HANDLE_FAC); - copy_v3_v3(bezt->vec[2], h2); - - /* First point */ - interp_v3_v3v3(h1, p1, bezt->vec[1], BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p1, p2, BEZT_HANDLE_FAC); - bezt++; - gp_stroke_to_bezier_add_point(gtd, bezt, p1, h1, h2, (bezt - 1)->vec[1], do_gtd, gps->prev->inittime, dt1, - 0.0f, rad_fac, minmax_weights); - - /* Second point */ - interp_v3_v3v3(h1, p2, p1, BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p2, p3d_cur, BEZT_HANDLE_FAC); - bezt++; - gp_stroke_to_bezier_add_point(gtd, bezt, p2, h1, h2, p1, do_gtd, gps->inittime, dt2, - 0.0f, rad_fac, minmax_weights); - - old_nbezt += 2; - copy_v3_v3(p3d_prev, p2); - } - } - else if (add_start_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, p3d_cur, p3d_next, -GAP_DFAC); - if (do_gtd) { - dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, p3d_cur); - p[0] -= GAP_DFAC; /* Rather arbitrary... */ - dt = -GAP_DFAC; /* Rather arbitrary too! */ - } - interp_v3_v3v3(h1, p, p3d_cur, -BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p, p3d_cur, BEZT_HANDLE_FAC); - bezt = &nu->bezt[old_nbezt]; - gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, p, do_gtd, gps->inittime, dt, - 0.0f, rad_fac, minmax_weights); - - old_nbezt++; - copy_v3_v3(p3d_prev, p); - } - - if (old_nbezt) { - prev_bezt = &nu->bezt[old_nbezt - 1]; - } - - /* add points */ - for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) { - float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC; - - if (i || old_nbezt) { - interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); - } - else { - interp_v3_v3v3(h1, p3d_cur, p3d_next, -BEZT_HANDLE_FAC); - } - - if (i < tot - 1) { - interp_v3_v3v3(h2, p3d_cur, p3d_next, BEZT_HANDLE_FAC); - } - else { - interp_v3_v3v3(h2, p3d_cur, p3d_prev, -BEZT_HANDLE_FAC); - } - - gp_stroke_to_bezier_add_point(gtd, bezt, p3d_cur, h1, h2, prev_bezt ? prev_bezt->vec[1] : p3d_cur, - do_gtd, gps->inittime, pt->time, width, rad_fac, minmax_weights); - - /* shift coord vects */ - copy_v3_v3(p3d_prev, p3d_cur); - copy_v3_v3(p3d_cur, p3d_next); - - if (i + 2 < tot) { - gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect); - } - - prev_bezt = bezt; - } - - if (add_end_point) { - float p[3]; - float dt = 0.0f; - - if (gps->totpoints > 1) { - interp_v3_v3v3(p, prev_bezt->vec[1], (prev_bezt - 1)->vec[1], -GAP_DFAC); - if (do_gtd) { - const int idx = gps->totpoints - 1; - dt = interpf(gps->points[idx - 1].time, gps->points[idx].time, -GAP_DFAC); - } - } - else { - copy_v3_v3(p, prev_bezt->vec[1]); - p[0] += GAP_DFAC; /* Rather arbitrary... */ - dt = GAP_DFAC; /* Rather arbitrary too! */ - } - - /* Second handle of last point of this stroke. */ - interp_v3_v3v3(h2, prev_bezt->vec[1], p, BEZT_HANDLE_FAC); - copy_v3_v3(prev_bezt->vec[2], h2); - - /* The end point */ - interp_v3_v3v3(h1, p, prev_bezt->vec[1], BEZT_HANDLE_FAC); - interp_v3_v3v3(h2, p, prev_bezt->vec[1], -BEZT_HANDLE_FAC); - /* Note bezt has already been incremented in main loop above, so it points to the right place. */ - gp_stroke_to_bezier_add_point(gtd, bezt, p, h1, h2, prev_bezt->vec[1], do_gtd, gps->inittime, dt, - 0.0f, rad_fac, minmax_weights); - } - - /* must calculate handles or else we crash */ - BKE_nurb_handles_calc(nu); - - if (!curnu || !*curnu) { - BLI_addtail(&cu->nurb, nu); - } - if (curnu) { - *curnu = nu; - } -} - -#undef GAP_DFAC -#undef WIDTH_CORR_FAC -#undef BEZT_HANDLE_FAC - -static void gp_stroke_finalize_curve_endpoints(Curve *cu) -{ - /* start */ - Nurb *nu = cu->nurb.first; - int i = 0; - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - if (bezt) { - bezt[i].weight = bezt[i].radius = 0.0f; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - if (bp) { - bp[i].weight = bp[i].radius = 0.0f; - } - } - - /* end */ - nu = cu->nurb.last; - i = nu->pntsu - 1; - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - if (bezt) { - bezt[i].weight = bezt[i].radius = 0.0f; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - if (bp) { - bp[i].weight = bp[i].radius = 0.0f; - } - } -} - -static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2]) -{ - Nurb *nu; - const float delta = minmax_weights[0]; - float fac; - int i; - - /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */ - if (IS_EQF(delta, minmax_weights[1])) - fac = 1.0f; - else - fac = 1.0f / (minmax_weights[1] - delta); - - for (nu = cu->nurb.first; nu; nu = nu->next) { - if (nu->bezt) { - BezTriple *bezt = nu->bezt; - for (i = 0; i < nu->pntsu; i++, bezt++) { - bezt->weight = (bezt->weight - delta) * fac; - } - } - else if (nu->bp) { - BPoint *bp = nu->bp; - for (i = 0; i < nu->pntsu; i++, bp++) { - bp->weight = (bp->weight - delta) * fac; - } - } - } -} - -static int gp_camera_view_subrect(bContext *C, rctf *subrect) -{ - View3D *v3d = CTX_wm_view3d(C); - ARegion *ar = CTX_wm_region(C); - - if (v3d) { - RegionView3D *rv3d = ar->regiondata; - - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - Scene *scene = CTX_data_scene(C); - ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, subrect, true); /* no shift */ - return 1; - } - } - - return 0; -} - -/* convert a given grease-pencil layer to a 3d-curve representation (using current view if appropriate) */ -static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bGPDlayer *gpl, const int mode, - 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; - Object *ob; - Curve *cu; - Nurb *nu = NULL; - Base *base_orig = BASACT, *base_new = NULL; - float minmax_weights[2] = {1.0f, 0.0f}; - - /* camera framing */ - rctf subrect, *subrect_ptr = NULL; - - /* error checking */ - if (ELEM(NULL, gpd, gpl, gpf)) - return; - - /* only convert if there are any strokes on this layer's frame to convert */ - if (BLI_listbase_is_empty(&gpf->strokes)) - return; - - /* initialize camera framing */ - if (gp_camera_view_subrect(C, &subrect)) { - subrect_ptr = &subrect; - } - - /* init the curve object (remove rotation and get curve data from it) - * - must clear transforms set on object, as those skew our results - */ - ob = BKE_object_add_only_object(bmain, OB_CURVE, gpl->info); - cu = ob->data = BKE_curve_add(bmain, gpl->info, OB_CURVE); - base_new = BKE_scene_base_add(scene, ob); - - cu->flag |= CU_3D; - - gtd->inittime = ((bGPDstroke *)gpf->strokes.first)->inittime; - - /* add points to curve */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - const bool add_start_point = (link_strokes && !(prev_gps)); - const bool add_end_point = (link_strokes && !(gps->next)); - - /* Detect new strokes created because of GP_STROKE_BUFFER_MAX reached, and stitch them to previous one. */ - bool stitch = false; - if (prev_gps) { - bGPDspoint *pt1 = &prev_gps->points[prev_gps->totpoints - 1]; - bGPDspoint *pt2 = &gps->points[0]; - - if ((pt1->x == pt2->x) && (pt1->y == pt2->y)) { - stitch = true; - } - } - - /* Decide whether we connect this stroke to previous one */ - if (!(stitch || link_strokes)) { - nu = NULL; - } - - switch (mode) { - case GP_STROKECONVERT_PATH: - gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, - add_start_point, add_end_point, gtd); - break; - case GP_STROKECONVERT_CURVE: - case GP_STROKECONVERT_POLY: /* convert after */ - gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, - add_start_point, add_end_point, gtd); - break; - default: - BLI_assert(!"invalid mode"); - break; - } - prev_gps = gps; - } - - /* If link_strokes, be sure first and last points have a zero weight/size! */ - if (link_strokes) { - gp_stroke_finalize_curve_endpoints(cu); - } - - /* Update curve's weights, if needed */ - if (norm_weights && ((minmax_weights[0] > 0.0f) || (minmax_weights[1] < 1.0f))) { - gp_stroke_norm_curve_weights(cu, minmax_weights); - } - - /* Create the path animation, if needed */ - gp_stroke_path_animation(C, reports, cu, gtd); - - if (mode == GP_STROKECONVERT_POLY) { - for (nu = cu->nurb.first; nu; nu = nu->next) { - BKE_nurb_type_convert(nu, CU_POLY, false); - } - } - - /* set the layer and select */ - 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; -} - -/* --- */ - -/* Check a GP layer has valid timing data! Else, most timing options are hidden in the operator. - * op may be NULL. - */ -static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = NULL; - bGPDstroke *gps = NULL; - bGPDspoint *pt; - double base_time, cur_time, prev_time = -1.0; - int i; - bool valid = true; - - if (!gpl || !(gpf = gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) - return false; - - do { - base_time = cur_time = gps->inittime; - if (cur_time <= prev_time) { - valid = false; - break; - } - - prev_time = cur_time; - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - cur_time = base_time + (double)pt->time; - /* First point of a stroke should have the same time as stroke's inittime, - * so it's the only case where equality is allowed! - */ - if ((i && cur_time <= prev_time) || (cur_time < prev_time)) { - valid = false; - break; - } - prev_time = cur_time; - } - - if (!valid) { - break; - } - } while ((gps = gps->next)); - - if (op) { - RNA_boolean_set(op->ptr, "use_timing_data", valid); - } - return valid; -} - -/* Check end_frame is always > start frame! */ -static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UNUSED(scene), struct PointerRNA *ptr) -{ - int start_frame = RNA_int_get(ptr, "start_frame"); - int end_frame = RNA_int_get(ptr, "end_frame"); - - if (end_frame <= start_frame) { - RNA_int_set(ptr, "end_frame", start_frame + 1); - } -} - -static int gp_convert_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDlayer *gpl = NULL; - bGPDframe *gpf = NULL; - ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); - - /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), - * and if we are not in edit mode! - */ - return ((sa && sa->spacetype == SPACE_VIEW3D) && - (gpl = gpencil_layer_getactive(gpd)) && - (gpf = gpencil_layer_getframe(gpl, CFRA, 0)) && - (gpf->strokes.first) && - (scene->obedit == NULL)); -} - -static int gp_convert_layer_exec(bContext *C, wmOperator *op) -{ - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "use_timing_data"); - 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"); - const bool norm_weights = RNA_boolean_get(op->ptr, "use_normalize_weights"); - const float rad_fac = RNA_float_get(op->ptr, "radius_multiplier"); - const bool link_strokes = RNA_boolean_get(op->ptr, "use_link_strokes"); - bool valid_timing; - tGpTimingData gtd; - - /* check if there's data to work with */ - if (gpd == NULL) { - BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data to work on"); - return OPERATOR_CANCELLED; - } - - if (!RNA_property_is_set(op->ptr, prop) && !gp_convert_check_has_valid_timing(C, gpl, op)) { - BKE_report(op->reports, RPT_WARNING, - "Current Grease Pencil strokes have no valid timing data, most timing options will be hidden!"); - } - valid_timing = RNA_property_boolean_get(op->ptr, prop); - - gtd.mode = RNA_enum_get(op->ptr, "timing_mode"); - /* Check for illegal timing mode! */ - if (!valid_timing && !ELEM(gtd.mode, GP_STROKECONVERT_TIMING_NONE, GP_STROKECONVERT_TIMING_LINEAR)) { - gtd.mode = GP_STROKECONVERT_TIMING_LINEAR; - RNA_enum_set(op->ptr, "timing_mode", gtd.mode); - } - if (!link_strokes) { - gtd.mode = GP_STROKECONVERT_TIMING_NONE; - } - - /* grab all relevant settings */ - gtd.frame_range = RNA_int_get(op->ptr, "frame_range"); - gtd.start_frame = RNA_int_get(op->ptr, "start_frame"); - gtd.realtime = valid_timing ? RNA_boolean_get(op->ptr, "use_realtime") : false; - gtd.end_frame = RNA_int_get(op->ptr, "end_frame"); - gtd.gap_duration = RNA_float_get(op->ptr, "gap_duration"); - gtd.gap_randomness = RNA_float_get(op->ptr, "gap_randomness"); - gtd.gap_randomness = min_ff(gtd.gap_randomness, gtd.gap_duration); - gtd.seed = RNA_int_get(op->ptr, "seed"); - gtd.num_points = gtd.cur_point = 0; - gtd.dists = gtd.times = NULL; - gtd.tot_dist = gtd.tot_time = gtd.gap_tot_time = 0.0f; - gtd.inittime = 0.0; - gtd.offset_time = 0.0f; - - /* perform conversion */ - gp_layer_to_curve(C, op->reports, gpd, gpl, mode, norm_weights, rad_fac, link_strokes, >d); - - /* free temp memory */ - if (gtd.dists) { - MEM_freeN(gtd.dists); - gtd.dists = NULL; - } - if (gtd.times) { - MEM_freeN(gtd.times); - gtd.times = NULL; - } - - /* notifiers */ - WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); - - /* done */ - return OPERATOR_FINISHED; -} - -static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) -{ - const char *prop_id = RNA_property_identifier(prop); - const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes"); - int timing_mode = RNA_enum_get(ptr, "timing_mode"); - bool realtime = RNA_boolean_get(ptr, "use_realtime"); - float gap_duration = RNA_float_get(ptr, "gap_duration"); - float gap_randomness = RNA_float_get(ptr, "gap_randomness"); - const bool valid_timing = RNA_boolean_get(ptr, "use_timing_data"); - - /* Always show those props */ - if (STREQ(prop_id, "type") || - STREQ(prop_id, "use_normalize_weights") || - STREQ(prop_id, "radius_multiplier") || - STREQ(prop_id, "use_link_strokes")) - { - return true; - } - - /* Never show this prop */ - if (STREQ(prop_id, "use_timing_data")) - return false; - - if (link_strokes) { - /* Only show when link_stroke is true */ - if (STREQ(prop_id, "timing_mode")) - return true; - - if (timing_mode != GP_STROKECONVERT_TIMING_NONE) { - /* Only show when link_stroke is true and stroke timing is enabled */ - if (STREQ(prop_id, "frame_range") || - STREQ(prop_id, "start_frame")) - { - return true; - } - - /* Only show if we have valid timing data! */ - if (valid_timing && STREQ(prop_id, "use_realtime")) - return true; - - /* Only show if realtime or valid_timing is false! */ - if ((!realtime || !valid_timing) && STREQ(prop_id, "end_frame")) - return true; - - if (valid_timing && timing_mode == GP_STROKECONVERT_TIMING_CUSTOMGAP) { - /* Only show for custom gaps! */ - if (STREQ(prop_id, "gap_duration")) - return true; - - /* Only show randomness for non-null custom gaps! */ - if (STREQ(prop_id, "gap_randomness") && (gap_duration > 0.0f)) - return true; - - /* Only show seed for randomize action! */ - if (STREQ(prop_id, "seed") && (gap_duration > 0.0f) && (gap_randomness > 0.0f)) - return true; - } - } - } - - /* Else, hidden! */ - return false; -} - -static void gp_convert_ui(bContext *C, wmOperator *op) -{ - uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - /* Main auto-draw call */ - uiDefAutoButsRNA(layout, &ptr, gp_convert_draw_check_prop, '\0'); -} - -void GPENCIL_OT_convert(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Convert Grease Pencil"; - ot->idname = "GPENCIL_OT_convert"; - ot->description = "Convert the active Grease Pencil layer to a new Curve Object"; - - /* callbacks */ - ot->invoke = WM_menu_invoke; - ot->exec = gp_convert_layer_exec; - ot->poll = gp_convert_poll; - ot->ui = gp_convert_ui; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_convertmodes, 0, "Type", "Which type of curve to convert to"); - - RNA_def_boolean(ot->srna, "use_normalize_weights", true, "Normalize Weight", - "Normalize weight (set from stroke width)"); - RNA_def_float(ot->srna, "radius_multiplier", 1.0f, 0.0f, 1000.0f, "Radius Fac", - "Multiplier for the points' radii (set from stroke width)", 0.0f, 10.0f); - RNA_def_boolean(ot->srna, "use_link_strokes", true, "Link Strokes", - "Whether to link strokes with zero-radius sections of curves"); - - prop = RNA_def_enum(ot->srna, "timing_mode", prop_gpencil_convert_timingmodes, GP_STROKECONVERT_TIMING_FULL, - "Timing Mode", "How to use timing data stored in strokes"); - RNA_def_enum_funcs(prop, rna_GPConvert_mode_items); - - RNA_def_int(ot->srna, "frame_range", 100, 1, 10000, "Frame Range", - "The duration of evaluation of the path control curve", 1, 1000); - RNA_def_int(ot->srna, "start_frame", 1, 1, 100000, "Start Frame", - "The start frame of the path control curve", 1, 100000); - RNA_def_boolean(ot->srna, "use_realtime", false, "Realtime", - "Whether the path control curve reproduces the drawing in realtime, starting from Start Frame"); - prop = RNA_def_int(ot->srna, "end_frame", 250, 1, 100000, "End Frame", - "The end frame of the path control curve (if Realtime is not set)", 1, 100000); - RNA_def_property_update_runtime(prop, gp_convert_set_end_frame); - - RNA_def_float(ot->srna, "gap_duration", 0.0f, 0.0f, 10000.0f, "Gap Duration", - "Custom Gap mode: (Average) length of gaps, in frames " - "(Note: Realtime value, will be scaled if Realtime is not set)", 0.0f, 1000.0f); - RNA_def_float(ot->srna, "gap_randomness", 0.0f, 0.0f, 10000.0f, "Gap Randomness", - "Custom Gap mode: Number of frames that gap lengths can vary", 0.0f, 1000.0f); - RNA_def_int(ot->srna, "seed", 0, 0, 1000, "Random Seed", - "Custom Gap mode: Random generator seed", 0, 100); - - /* Note: Internal use, this one will always be hidden by UI code... */ - prop = RNA_def_boolean(ot->srna, "use_timing_data", false, "Has Valid Timing", - "Whether the converted Grease Pencil layer has valid timing data (internal use)"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); -} - -/* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 56420434494..72cd9022d69 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -96,6 +96,12 @@ void gp_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc); void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct bGPDspoint *pt, int *r_x, int *r_y); +/* Poll Callbacks ------------------------------------ */ +/* gpencil_utils.c */ + +int gp_add_poll(struct bContext *C); +int gp_active_layer_poll(struct bContext *C); + /* ***************************************************** */ /* Operator Defines */ @@ -138,6 +144,9 @@ void GPENCIL_OT_layer_remove(struct wmOperatorType *ot); void GPENCIL_OT_layer_move(struct wmOperatorType *ot); void GPENCIL_OT_layer_duplicate(struct wmOperatorType *ot); +void GPENCIL_OT_hide(struct wmOperatorType *ot); +void GPENCIL_OT_reveal(struct wmOperatorType *ot); + void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 3fae208a2e2..4752f3dbb36 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -34,8 +34,6 @@ #include "BLI_sys_types.h" -#include "BLI_blenlib.h" - #include "BKE_context.h" #include "DNA_gpencil_types.h" @@ -183,7 +181,18 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) #ifdef __APPLE__ WM_keymap_add_item(keymap, "GPENCIL_OT_copy", CKEY, KM_PRESS, KM_OSKEY, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_paste", VKEY, KM_PRESS, KM_OSKEY, 0); -#endif +#endif + + /* Show/Hide */ + /* NOTE: These are available only in EditMode now, since they clash with general-purpose hotkeys */ + WM_keymap_add_item(keymap, "GPENCIL_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "unselected", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "unselected", true); + /* Transform Tools */ kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0); @@ -262,6 +271,9 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_layer_move); WM_operatortype_append(GPENCIL_OT_layer_duplicate); + WM_operatortype_append(GPENCIL_OT_hide); + WM_operatortype_append(GPENCIL_OT_reveal); + WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_convert); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index c8774cb73a1..f3eb1612576 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -48,6 +48,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_report.h" +#include "BKE_screen.h" #include "BKE_tracking.h" #include "DNA_object_types.h" @@ -1030,6 +1031,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* set current area * - must verify that region data is 3D-view (and not something else) */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ p->sa = curarea; p->ar = ar; @@ -1252,6 +1254,7 @@ static void gp_paint_initstroke(tGPsdata *p, short paintmode) /* when drawing in the camera view, in 2D space, set the subrect */ + p->subrect = NULL; if (!(p->gpd->flag & GP_DATA_VIEWALIGN)) { if (p->sa->spacetype == SPACE_VIEW3D) { View3D *v3d = p->sa->spacedata.first; @@ -1437,12 +1440,14 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) if (p->paintmode == GP_PAINTMODE_ERASER) { /* turn off radial brush cursor */ gpencil_draw_toggle_eraser_cursor(C, p, false); - - /* if successful, store the new eraser size to be used again next time */ - if (p->status == GP_STATUS_DONE) - U.gp_eraser = p->radius; } + /* always store the new eraser size to be used again next time + * NOTE: Do this even when not in eraser mode, as eraser may + * have been toggled at some point. + */ + U.gp_eraser = p->radius; + /* cleanup */ gp_paint_cleanup(p); gp_session_cleanup(p); @@ -1493,6 +1498,15 @@ static int gpencil_draw_init(bContext *C, wmOperator *op) /* ------------------------------- */ +/* ensure that the correct cursor icon is set */ +static void gpencil_draw_cursor_set(tGPsdata *p) +{ + if (p->paintmode == GP_PAINTMODE_ERASER) + WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + else + WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); +} + /* update UI indicators of status, including cursor and header prints */ static void gpencil_draw_status_indicators(tGPsdata *p) { @@ -1608,7 +1622,7 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event) /* handle pressure sensitivity (which is supplied by tablets) */ if (event->tablet_data) { - wmTabletData *wmtab = event->tablet_data; + const wmTabletData *wmtab = event->tablet_data; tablet = (wmtab->Active != EVT_TABLET_NONE); p->pressure = wmtab->Pressure; @@ -1733,7 +1747,6 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p = NULL; - wmWindow *win = CTX_wm_window(C); if (G.debug & G_DEBUG) printf("GPencil - Starting Drawing\n"); @@ -1759,11 +1772,11 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event gpencil_draw_toggle_eraser_cursor(C, p, true); } - /* set cursor */ - if (p->paintmode == GP_PAINTMODE_ERASER) - WM_cursor_modal_set(win, BC_CROSSCURSOR); /* XXX need a better cursor */ - else - WM_cursor_modal_set(win, BC_PAINTBRUSHCURSOR); + /* set cursor + * NOTE: This may change later (i.e. intentionally via brush toggle, + * or unintentionally if the user scrolls outside the area)... + */ + gpencil_draw_cursor_set(p); /* only start drawing immediately if we're allowed to do so... */ if (RNA_boolean_get(op->ptr, "wait_for_input") == false) { @@ -1777,6 +1790,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event else { /* toolbar invoked - don't start drawing yet... */ /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; } WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); @@ -1814,8 +1828,10 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) if (gp_session_initdata(C, p)) gp_paint_initstroke(p, p->paintmode); - if (p->status != GP_STATUS_ERROR) + if (p->status != GP_STATUS_ERROR) { p->status = GP_STATUS_PAINTING; + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } return op->customdata; } @@ -1831,6 +1847,7 @@ static void gpencil_stroke_end(wmOperator *op) gp_session_cleanup(p); p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; p->gpd = NULL; p->gpl = NULL; @@ -1856,6 +1873,11 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * in 3D space. */ + if (p->status == GP_STATUS_IDLING) { + ARegion *ar = CTX_wm_region(C); + p->ar = ar; + } + /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ if (ISKEYBOARD(event->type)) { if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { @@ -1885,8 +1907,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) * - RIGHTMOUSE = polyline (hotkey) / eraser (all) * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) + * also making sure we have a valid event value, to not exit too early */ - if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE)) { + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (event->val != KM_NOTHING)) { /* if painting, end stroke */ if (p->status == GP_STATUS_PAINTING) { int sketch = 0; @@ -1937,29 +1960,88 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (event->val == KM_PRESS) { - /* Switch paintmode (temporarily if need be) based on which button was used - * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + bool in_bounds = false; + + /* Check if we're outside the bounds of the active region + * NOTE: An exception here is that if launched from the toolbar, + * whatever region we're now in should become the new region */ - if (event->type == LEFTMOUSE) { - /* restore drawmode to default */ - p->paintmode = RNA_enum_get(op->ptr, "mode"); + if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { + /* Change to whatever region is now under the mouse */ + ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); + + if (G.debug & G_DEBUG) { + printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", + current_region, p->ar, event->x, event->y, + p->sa->totrct.xmin, p->sa->totrct.ymin, p->sa->totrct.xmax, p->sa->totrct.ymax); + } + + if (current_region) { + /* Assume that since we found the cursor in here, it is in bounds + * and that this should be the region that we begin drawing in + */ + p->ar = current_region; + in_bounds = true; + } + else { + /* Out of bounds, or invalid in some other way */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); + } + } + else if (p->ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(p->ar, ®ion_rect); + in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); } - else if (event->type == RIGHTMOUSE) { - /* turn on eraser */ - p->paintmode = GP_PAINTMODE_ERASER; + else { + /* No region */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: No active region found in GP Paint session data\n", __func__); } - - gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); - - /* not painting, so start stroke (this should be mouse-button down) */ - p = gpencil_stroke_begin(C, op); - if (p->status == GP_STATUS_ERROR) { - estate = OPERATOR_CANCELLED; + if (in_bounds) { + /* Switch paintmode (temporarily if need be) based on which button was used + * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + */ + if (event->type == LEFTMOUSE) { + /* restore drawmode to default */ + p->paintmode = RNA_enum_get(op->ptr, "mode"); + } + else if (event->type == RIGHTMOUSE) { + /* turn on eraser */ + p->paintmode = GP_PAINTMODE_ERASER; + } + + gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); + + /* not painting, so start stroke (this should be mouse-button down) */ + p = gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { + estate = OPERATOR_CANCELLED; + } + } + else if (p->status != GP_STATUS_ERROR) { + /* User clicked outside bounds of window while idling, so exit paintmode + * NOTE: Don't eter this case if an error occurred while finding the + * region (as above) + */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; } } else { p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; } } @@ -2023,9 +2105,11 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ if (0 == gpencil_area_exists(C, p->sa)) estate = OPERATOR_CANCELLED; - else + else { /* update status indicators - cursor, header, etc. */ gpencil_draw_status_indicators(p); + gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ + } /* process last operations before exiting */ switch (estate) { diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index 9ba77a4244e..1b66f366b70 100644 --- a/source/blender/editors/gpencil/gpencil_select.c +++ b/source/blender/editors/gpencil/gpencil_select.c @@ -35,28 +35,16 @@ #include "MEM_guardedalloc.h" -#include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_lasso.h" #include "BLI_utildefines.h" #include "DNA_gpencil_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" #include "DNA_screen_types.h" -#include "DNA_space_types.h" -#include "DNA_view3d_types.h" #include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_depsgraph.h" -#include "BKE_global.h" #include "BKE_gpencil.h" -#include "BKE_library.h" -#include "BKE_object.h" #include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" #include "UI_interface.h" @@ -69,8 +57,6 @@ #include "UI_view2d.h" #include "ED_gpencil.h" -#include "ED_view3d.h" -#include "ED_keyframing.h" #include "gpencil_intern.h" @@ -789,6 +775,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) bGPDstroke *hit_stroke = NULL; bGPDspoint *hit_point = NULL; + int hit_distance = radius_squared; /* sanity checks */ if (sa == NULL) { @@ -811,7 +798,6 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) { bGPDspoint *pt; int i; - int hit_index = -1; /* firstly, check for hit-point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { @@ -821,18 +807,19 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* do boundbox check first */ if (!ELEM(V2D_IS_CLIPPED, x0, x0)) { - /* only check if point is inside */ - if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius_squared) { - hit_stroke = gps; - hit_point = pt; - break; + const int pt_distance = ((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)); + + /* check if point is inside */ + if (pt_distance <= radius_squared) { + /* only use this point if it is a better match than the current hit - T44685 */ + if (pt_distance < hit_distance) { + hit_stroke = gps; + hit_point = pt; + hit_distance = pt_distance; + } } } } - - /* skip to next stroke if nothing found */ - if (hit_index == -1) - continue; } CTX_DATA_END; diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 289cbc568d2..0d7aac7f48f 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -32,8 +32,6 @@ #include <stddef.h> #include <math.h> -#include "MEM_guardedalloc.h" - #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -46,18 +44,10 @@ #include "DNA_view3d_types.h" #include "BKE_context.h" -#include "BKE_global.h" #include "BKE_gpencil.h" -#include "BKE_library.h" -#include "BKE_object.h" -#include "BKE_report.h" -#include "BKE_scene.h" -#include "BKE_screen.h" - -#include "UI_interface.h" +#include "BKE_tracking.h" #include "WM_api.h" -#include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" @@ -65,11 +55,183 @@ #include "UI_view2d.h" #include "ED_gpencil.h" +#include "ED_clip.h" #include "ED_view3d.h" #include "gpencil_intern.h" /* ******************************************************** */ +/* Context Wrangling... */ + +/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, + * when context info is not available. + */ +bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) +{ + /* if there's an active area, check if the particular editor may + * have defined any special Grease Pencil context for editing... + */ + if (sa) { + SpaceLink *sl = sa->spacedata.first; + + switch (sa->spacetype) { + case SPACE_VIEW3D: /* 3D-View */ + case SPACE_TIME: /* Timeline - XXX: this is a hack to get it to show GP keyframes for 3D view */ + case SPACE_ACTION: /* DepeSheet - XXX: this is a hack to get the keyframe jump operator to take GP Keyframes into account */ + { + BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, + GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); + + if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { + /* legacy behaviour for usage with old addons requiring object-linked to objects */ + + /* just in case no active/selected object... */ + if (ob && (ob->flag & SELECT)) { + /* for now, as long as there's an object, default to using that in 3D-View */ + if (ptr) RNA_id_pointer_create(&ob->id, ptr); + return &ob->gpd; + } + /* else: defaults to scene... */ + } + else { + if (ptr) RNA_id_pointer_create(&scene->id, ptr); + return &scene->gpd; + } + break; + } + case SPACE_NODE: /* Nodes Editor */ + { + SpaceNode *snode = (SpaceNode *)sl; + + /* return the GP data for the active node block/node */ + if (snode && snode->nodetree) { + /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ + if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr); + return &snode->nodetree->gpd; + } + + /* even when there is no node-tree, don't allow this to flow to scene */ + return NULL; + } + case SPACE_SEQ: /* Sequencer */ + { + SpaceSeq *sseq = (SpaceSeq *)sl; + + /* for now, Grease Pencil data is associated with the space (actually preview region only) */ + /* XXX our convention for everything else is to link to data though... */ + if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr); + return &sseq->gpd; + } + case SPACE_IMAGE: /* Image/UV Editor */ + { + SpaceImage *sima = (SpaceImage *)sl; + + /* for now, Grease Pencil data is associated with the space... */ + /* XXX our convention for everything else is to link to data though... */ + if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr); + return &sima->gpd; + } + case SPACE_CLIP: /* Nodes Editor */ + { + SpaceClip *sc = (SpaceClip *)sl; + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (clip) { + if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); + + if (!track) + return NULL; + + if (ptr) + RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr); + + return &track->gpd; + } + else { + if (ptr) + RNA_id_pointer_create(&clip->id, ptr); + + return &clip->gpd; + } + } + break; + } + default: /* unsupported space */ + return NULL; + } + } + + /* just fall back on the scene's GP data */ + if (ptr) RNA_id_pointer_create((ID *)scene, ptr); + return (scene) ? &scene->gpd : NULL; +} + +/* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ +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); + ScrArea *sa = CTX_wm_area(C); + Object *ob = CTX_data_active_object(C); + + return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); +} + +/* -------------------------------------------------------- */ + +/* Get the active Grease Pencil datablock, when context is not available */ +bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL); + return (gpd_ptr) ? *(gpd_ptr) : NULL; +} + +/* Get the active Grease Pencil datablock */ +bGPdata *ED_gpencil_data_get_active(const bContext *C) +{ + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + return (gpd_ptr) ? *(gpd_ptr) : NULL; +} + +/* -------------------------------------------------------- */ + +// XXX: this should be removed... We really shouldn't duplicate logic like this! +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 ED_gpencil_data_get_active's behavior. + */ + + if (base && TESTBASE(v3d, base)) { + gpd = base->object->gpd; + } + return gpd ? gpd : scene->gpd; +} + +/* ******************************************************** */ +/* Poll Callbacks */ + +/* poll callback for adding data/layers - special */ +int gp_add_poll(bContext *C) +{ + /* the base line we have is that we have somewhere to add Grease Pencil data */ + return ED_gpencil_data_get_pointers(C, NULL) != NULL; +} + +/* poll callback for checking if there is an active layer */ +int gp_active_layer_poll(bContext *C) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = gpencil_layer_getactive(gpd); + + return (gpl != NULL); +} + +/* ******************************************************** */ +/* Brush Tool Core */ /* Check if part of stroke occurs within last segment drawn by eraser */ bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), @@ -89,6 +251,7 @@ bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), } /* ******************************************************** */ +/* Stroke Validity Testing */ /* Check whether given stroke can be edited given the supplied context */ // XXX: do we need additional flags for screenspace vs dataspace? @@ -125,6 +288,7 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) } /* ******************************************************** */ +/* Space Conversion */ /* Init handling for space-conversion function (from passed-in parameters) */ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc) diff --git a/source/blender/editors/include/BIF_glutil.h b/source/blender/editors/include/BIF_glutil.h index 4dc39681ed2..b904417cfcb 100644 --- a/source/blender/editors/include/BIF_glutil.h +++ b/source/blender/editors/include/BIF_glutil.h @@ -181,8 +181,8 @@ typedef struct gla2DDrawInfo gla2DDrawInfo; #if 0 gla2DDrawInfo *glaBegin2DDraw(struct rcti *screen_rect, struct rctf *world_rect); -void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *sc_x_r, int *sc_y_r); -void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int screen_r[2]); +void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *r_sc_x, int *r_sc_y); +void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int r_screen[2]); void glaEnd2DDraw(gla2DDrawInfo *di); diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 50581429700..0f70bf3c745 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -49,7 +49,6 @@ struct Object; struct bDopeSheet; struct bAction; -struct bActionGroup; struct FCurve; struct FModifier; @@ -115,13 +114,17 @@ typedef struct bAnimListElem { int flag; /* copy of elem's flags for quick access */ int index; /* for un-named data, the index of the data in its collection */ - short update; /* (eAnim_Update_Flags) tag the element for updating */ + char update; /* (eAnim_Update_Flags) tag the element for updating */ + char tag; /* tag the included data. Temporary always */ + short datatype; /* (eAnim_KeyType) type of motion data to expect */ void *key_data; /* motion data - mostly F-Curves, but can be other types too */ struct ID *id; /* ID block that channel is attached to */ struct AnimData *adt; /* source of the animation data attached to ID block (for convenience) */ + + void *owner; /* for per-element F-Curves (e.g. NLA Control Curves), the element that this represents (e.g. NlaStrip) */ } bAnimListElem; @@ -141,6 +144,9 @@ typedef enum eAnim_ChannelType { ANIMTYPE_GROUP, ANIMTYPE_FCURVE, + ANIMTYPE_NLACONTROLS, + ANIMTYPE_NLACURVE, + ANIMTYPE_FILLACTD, ANIMTYPE_FILLDRIVERS, @@ -450,13 +456,13 @@ typedef struct bAnimChannelType { /* ------------------------ Drawing API -------------------------- */ /* Get typeinfo for the given channel */ -bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale); +const bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale); /* Print debugging info about a given channel */ void ANIM_channel_debug_print_info(bAnimListElem *ale, short indent_level); /* Draw the given channel */ -void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc); +void ANIM_channel_draw(bAnimContext *ac, bAnimListElem *ale, float yminc, float ymaxc, size_t channel_index); /* Draw the widgets for the given channel */ void ANIM_channel_draw_widgets(const struct bContext *C, bAnimContext *ac, bAnimListElem *ale, struct uiBlock *block, float yminc, float ymaxc, size_t channel_index); @@ -611,7 +617,7 @@ typedef enum eAnimUnitConv_Flags { short ANIM_get_normalization_flags(bAnimContext *ac); /* Get unit conversion factor for given ID + F-Curve */ -float ANIM_unit_mapping_get_factor(struct Scene *scene, struct ID *id, struct FCurve *fcu, short flag); +float ANIM_unit_mapping_get_factor(struct Scene *scene, struct ID *id, struct FCurve *fcu, short flag, float *r_offset); /* ------------- Utility macros ----------------------- */ @@ -653,6 +659,7 @@ void ANIM_list_elem_update(struct Scene *scene, bAnimListElem *ale); /* data -> channels syncing */ void ANIM_sync_animchannels_to_data(const struct bContext *C); +void ANIM_center_frame(struct bContext *C, int smooth_viewtx); /* ************************************************* */ /* OPERATORS */ @@ -670,6 +677,14 @@ void ED_operatormacros_graph(void); void ED_operatormacros_action(void); /* ************************************************ */ +/* Animation Editor Exports */ +/* XXX: Should we be doing these here, or at all? */ + +/* Action Editor - Action Management */ +struct AnimData *ED_actedit_animdata_from_context(struct bContext *C); +void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, struct bAction *act, struct ReportList *reports); + +/* ************************************************ */ #endif /* __ED_ANIM_API_H__ */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index b08cc12dc3e..35bb12ddaad 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -39,16 +39,12 @@ struct Base; struct bContext; struct Bone; struct bPoseChannel; -struct DerivedMesh; struct IDProperty; struct ListBase; struct MeshDeformModifierData; struct Object; -struct RegionView3D; struct ReportList; struct Scene; -struct SK_Sketch; -struct View3D; struct ViewContext; struct wmKeyConfig; struct wmOperator; @@ -59,8 +55,6 @@ typedef struct EditBone { struct EditBone *parent; /* Editbones have a one-way link (i.e. children refer * to parents. This is converted to a two-way link for * normal bones when leaving editmode. */ - void *temp; /* Used to store temporary data */ - char name[64]; /* MAXBONENAME */ float roll; /* Roll along axis. We'll ultimately use the axis/angle method * for determining the transformation matrix of the bone. The axis @@ -83,6 +77,14 @@ typedef struct EditBone { float oldlength; /* for envelope scaling */ short segments; + + /* Used to store temporary data */ + union { + struct EditBone *ebone; + struct Bone *bone; + void *p; + int i; + } temp; } EditBone; #define BONESEL_ROOT (1 << 28) @@ -119,10 +121,12 @@ void ED_keymap_armature(struct wmKeyConfig *keyconf); void ED_armature_from_edit(struct bArmature *arm); void ED_armature_to_edit(struct bArmature *arm); void ED_armature_edit_free(struct bArmature *arm); -void ED_armature_deselect_all(struct Object *obedit, int toggle); +void ED_armature_ebone_listbase_temp_clear(struct ListBase *lb); + +void ED_armature_deselect_all(struct Object *obedit); void ED_armature_deselect_all_visible(struct Object *obedit); -int ED_do_pose_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, +int ED_do_pose_selectbuffer(struct Scene *scene, struct Base *base, unsigned int *buffer, 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); diff --git a/source/blender/editors/include/ED_buttons.h b/source/blender/editors/include/ED_buttons.h index 64c16605dec..9a987d7618c 100644 --- a/source/blender/editors/include/ED_buttons.h +++ b/source/blender/editors/include/ED_buttons.h @@ -37,4 +37,6 @@ bool ED_texture_context_check_particles(const struct bContext *C); bool ED_texture_context_check_linestyle(const struct bContext *C); bool ED_texture_context_check_others(const struct bContext *C); +void ED_buttons_id_unref(struct SpaceButs *sbuts, const struct ID *id); + #endif /* __ED_BUTTONS_H__ */ diff --git a/source/blender/editors/include/ED_curve.h b/source/blender/editors/include/ED_curve.h index 27e9cad4fd5..fee3d44b8eb 100644 --- a/source/blender/editors/include/ED_curve.h +++ b/source/blender/editors/include/ED_curve.h @@ -31,13 +31,10 @@ #ifndef __ED_CURVE_H__ #define __ED_CURVE_H__ -struct Base; struct bContext; struct Nurb; struct Object; -struct Scene; struct Text; -struct View3D; struct wmOperator; struct wmKeyConfig; struct Curve; @@ -79,7 +76,7 @@ void free_editText(struct Object *obedit); void ED_text_to_object(struct bContext *C, struct Text *text, const bool split_lines); -bool ED_curve_select_nth(struct Curve *cu, int nth); +bool ED_curve_select_nth(struct Curve *cu, int nth, int skip, int offset); void ED_curve_beztcpy(struct EditNurb *editnurb, struct BezTriple *dst, struct BezTriple *src, int count); void ED_curve_bpcpy(struct EditNurb *editnurb, struct BPoint *dst, struct BPoint *src, int count); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index c4f08ca4775..448f2c83aad 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -33,19 +33,15 @@ struct ID; struct ListBase; struct bContext; -struct bScreen; struct ScrArea; struct ARegion; struct View3D; -struct SpaceNode; -struct SpaceSeq; struct Object; struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; struct PointerRNA; -struct ImBuf; struct wmKeyConfig; diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 704876e1261..f49d4b508c6 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -31,13 +31,11 @@ #define __ED_IMAGE_H__ struct SpaceImage; -struct Main; struct bContext; struct Image; struct ImageUser; struct ImBuf; struct ToolSettings; -struct uiBlock; struct wmWindowManager; struct ARegion; struct Scene; @@ -49,7 +47,7 @@ struct Mask *ED_space_image_get_mask(struct SpaceImage *sima); void ED_space_image_set_mask(struct bContext *C, struct SpaceImage *sima, struct Mask *mask); bool ED_space_image_color_sample(struct Scene *scene, struct SpaceImage *sima, struct ARegion *ar, int mval[2], float r_col[3]); -struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **lock_r); +struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock); void ED_space_image_release_buffer(struct SpaceImage *sima, struct ImBuf *ibuf, void *lock); bool ED_space_image_has_buffer(struct SpaceImage *sima); diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 0359153317b..7d163da0db0 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -34,7 +34,6 @@ struct bAnimContext; struct AnimData; -struct BezTriple; struct FCurve; struct bDopeSheet; struct bAction; diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h index e5b5e79875d..5d76c9e0f6f 100644 --- a/source/blender/editors/include/ED_keyframing.h +++ b/source/blender/editors/include/ED_keyframing.h @@ -46,10 +46,8 @@ struct FCurve; struct BezTriple; struct bPoseChannel; -struct bConstraint; struct bContext; -struct wmOperatorType; struct ReportList; struct PointerRNA; diff --git a/source/blender/editors/include/ED_lattice.h b/source/blender/editors/include/ED_lattice.h index 6636319dc9b..6fe1524cb6d 100644 --- a/source/blender/editors/include/ED_lattice.h +++ b/source/blender/editors/include/ED_lattice.h @@ -32,7 +32,6 @@ #define __ED_LATTICE_H__ struct Object; -struct Lattice; void free_editLatt(struct Object *ob); void make_editLatt(struct Object *obedit); diff --git a/source/blender/editors/include/ED_mask.h b/source/blender/editors/include/ED_mask.h index 97fd553ea19..1f13b46ff2a 100644 --- a/source/blender/editors/include/ED_mask.h +++ b/source/blender/editors/include/ED_mask.h @@ -31,10 +31,10 @@ #ifndef __ED_MASK_H__ #define __ED_MASK_H__ +struct bContext; struct wmKeyConfig; struct MaskLayer; struct MaskLayerShape; -struct wmEvent; /* mask_edit.c */ void ED_mask_get_size(struct ScrArea *sa, int *width, int *height); @@ -49,6 +49,7 @@ void ED_mask_point_pos__reverse(struct ScrArea *sa, struct ARegion *ar, float x, float y, float *xr, float *yr); void ED_mask_cursor_location_get(struct ScrArea *sa, float cursor[2]); +bool ED_mask_selected_minmax(const struct bContext *C, float min[2], float max[2]); void ED_operatortypes_mask(void); void ED_keymap_mask(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/include/ED_mball.h b/source/blender/editors/include/ED_mball.h index 5e774c63841..05a4ccabd1b 100644 --- a/source/blender/editors/include/ED_mball.h +++ b/source/blender/editors/include/ED_mball.h @@ -32,7 +32,6 @@ #define __ED_MBALL_H__ struct bContext; -struct MetaBall; struct Object; struct wmKeyConfig; diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index ccf97221112..8e19ec839d8 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -40,39 +40,27 @@ struct View3D; struct ARegion; struct bContext; struct wmOperator; -struct wmWindowManager; struct wmKeyConfig; struct ReportList; -struct EditSelection; struct ViewContext; struct bDeformGroup; -struct MDeformWeight; struct MDeformVert; struct Scene; struct Mesh; -struct MFace; -struct MEdge; -struct MVert; -struct MCol; struct UvVertMap; struct UvMapVert; -struct CustomData; struct BMEditMesh; -struct BMEditSelection; struct BMesh; struct BMVert; struct BMLoop; struct BMBVHTree; -struct MLoopCol; struct BMEdge; struct BMFace; struct UvVertMap; struct UvMapVert; struct ToolSettings; -struct Material; struct Object; struct rcti; -struct MeshStatVis; /* editmesh_utils.c */ void EDBM_verts_mirror_cache_begin_ex(struct BMEditMesh *em, const int axis, @@ -119,7 +107,9 @@ 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 *bm, const bool selected, const bool do_islands); +struct UvElementMap *BM_uv_element_map_create( + struct BMesh *bm, + const bool selected, const bool use_winding, 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); @@ -128,7 +118,9 @@ struct MTexPoly *EDBM_mtexpoly_active_get(struct BMEditMesh *em, struct BMFace * void BM_uv_vert_map_free(struct UvVertMap *vmap); struct UvMapVert *BM_uv_vert_map_at_index(struct UvVertMap *vmap, unsigned int v); -struct UvVertMap *BM_uv_vert_map_create(struct BMesh *bm, bool use_select, const float limit[2]); +struct UvVertMap *BM_uv_vert_map_create( + struct BMesh *bm, + const float limit[2], const bool use_select, const bool use_winding); void EDBM_flag_enable_all(struct BMEditMesh *em, const char hflag); void EDBM_flag_disable_all(struct BMEditMesh *em, const char hflag); @@ -149,9 +141,27 @@ bool EDBM_backbuf_border_mask_init(struct ViewContext *vc, const int mcords[][2] short xmin, short ymin, short xmax, short ymax); bool EDBM_backbuf_circle_init(struct ViewContext *vc, short xs, short ys, short rads); -struct BMVert *EDBM_vert_find_nearest(struct ViewContext *vc, float *r_dist, const bool sel, const bool strict); -struct BMEdge *EDBM_edge_find_nearest(struct ViewContext *vc, float *r_dist); -struct BMFace *EDBM_face_find_nearest(struct ViewContext *vc, float *r_dist); +struct BMVert *EDBM_vert_find_nearest_ex( + struct ViewContext *vc, float *r_dist, + const bool use_select_bias, bool use_cycle); +struct BMVert *EDBM_vert_find_nearest( + struct ViewContext *vc, float *r_dist); + +struct BMEdge *EDBM_edge_find_nearest_ex( + struct ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + struct BMEdge **r_eed_zbuf); +struct BMEdge *EDBM_edge_find_nearest( + struct ViewContext *vc, float *r_dist); + +struct BMFace *EDBM_face_find_nearest_ex( + struct ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + struct BMFace **r_efa_zbuf); +struct BMFace *EDBM_face_find_nearest( + struct ViewContext *vc, float *r_dist); bool EDBM_select_pick(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); @@ -233,6 +243,11 @@ void ED_vgroup_parray_mirror_assign(struct Object *ob, void ED_vgroup_parray_remove_zero(struct MDeformVert **dvert_array, const int dvert_tot, const bool *vgroup_validmap, const int vgroup_tot, const float epsilon, const bool keep_single); +void ED_vgroup_parray_to_weight_array(const struct MDeformVert **dvert_array, const int dvert_tot, + float *dvert_weights, const int def_nr); +void ED_vgroup_parray_from_weight_array(struct MDeformVert **dvert_array, const int dvert_tot, + const float *dvert_weights, const int def_nr, + const bool remove_zero); void ED_vgroup_mirror(struct Object *ob, const bool mirror_weights, const bool flip_vgroups, const bool all_vgroups, const bool use_topology, diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 824c82a0069..1445308c485 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -33,7 +33,6 @@ struct ID; struct Main; -struct Material; struct Scene; struct Tex; struct bContext; @@ -104,6 +103,8 @@ void ED_node_set_active(struct Main *bmain, struct bNodeTree *ntree, struct bNod void ED_node_composite_job(const struct bContext *C, struct bNodeTree *nodetree, struct Scene *scene_owner); +void ED_node_id_unref(struct SpaceNode *snode, const ID *id); + /* node_ops.c */ void ED_operatormacros_node(void); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 295bb752a08..c62bdc1ba87 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -35,33 +35,17 @@ extern "C" { #endif -struct BMEdge; -struct BMFace; -struct BMVert; -struct BPoint; struct Base; -struct BezTriple; -struct Curve; -struct EditBone; struct EnumPropertyItem; struct ID; -struct KeyBlock; -struct Lattice; struct Main; -struct Mesh; -struct MetaElem; struct ModifierData; -struct HookModifierData; -struct Nurb; struct Object; struct ReportList; struct Scene; -struct View3D; -struct ViewContext; struct bConstraint; struct bContext; struct bPoseChannel; -struct wmEvent; struct wmKeyConfig; struct wmKeyMap; struct wmOperator; @@ -93,7 +77,7 @@ typedef enum eParentType { PAR_PATH_CONST, PAR_LATTICE, PAR_VERTEX, - PAR_VERTEX_TRI + PAR_VERTEX_TRI, } eParentType; #ifdef __RNA_TYPES_H__ @@ -147,6 +131,10 @@ float ED_object_new_primitive_matrix( struct bContext *C, struct Object *editob, const float loc[3], const float rot[3], float primmat[4][4]); + +/* Avoid allowing too much insane values even by typing (typos can hang/crash Blender otherwise). */ +#define OBJECT_ADD_SIZE_MAXF 1.0e12f + void ED_object_add_unit_props(struct wmOperatorType *ot); void ED_object_add_generic_props(struct wmOperatorType *ot, bool do_editmode); bool ED_object_add_generic_get_opts(struct bContext *C, struct wmOperator *op, const char view_align_axis, @@ -154,8 +142,10 @@ bool ED_object_add_generic_get_opts(struct bContext *C, struct wmOperator *op, c 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) ATTR_RETURNS_NONNULL; + struct bContext *C, + int type, const char *name, const float loc[3], const float rot[3], + bool enter_editmode, unsigned int layer) + ATTR_NONNULL(1) ATTR_RETURNS_NONNULL; 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); @@ -175,6 +165,9 @@ void ED_object_constraint_set_active(struct Object *ob, struct bConstraint *con) void ED_object_constraint_update(struct Object *ob); void ED_object_constraint_dependency_update(struct Main *bmain, struct Object *ob); +void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con); +void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Object *ob, struct bConstraint *con); + /* object_lattice.c */ bool mouse_lattice(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); void undo_push_lattice(struct bContext *C, const char *name); diff --git a/source/blender/editors/include/ED_outliner.h b/source/blender/editors/include/ED_outliner.h new file mode 100644 index 00000000000..af4af8e2f5d --- /dev/null +++ b/source/blender/editors/include/ED_outliner.h @@ -0,0 +1,36 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2015, Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file ED_outliner.h + * \ingroup editors + */ + +#ifndef __ED_OUTLINER_H__ +#define __ED_OUTLINER_H__ + +struct ID; +struct SpaceOops; + +/* Used to check whether a given texture context is valid in current context. */ +void ED_outliner_id_unref(struct SpaceOops *so, const struct ID *id); + +#endif /* __ED_OUTLINER_H__ */ diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index decd79fcc7b..e46f4b966c0 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -26,7 +26,6 @@ #define __ED_PAINT_H__ struct bContext; -struct RegionView3D; struct wmKeyConfig; struct wmOperator; @@ -47,9 +46,9 @@ 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); +const char *ED_undo_paint_get_name(struct bContext *C, int type, int nr, bool *r_active); void ED_undo_paint_free(void); -int ED_undo_paint_valid(int type, const char *name); +bool ED_undo_paint_is_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); @@ -59,7 +58,7 @@ void ED_undo_paint_push_end(int type); 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_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old); 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_particle.h b/source/blender/editors/include/ED_particle.h index f9516f255cf..6cb8c0cfb19 100644 --- a/source/blender/editors/include/ED_particle.h +++ b/source/blender/editors/include/ED_particle.h @@ -35,10 +35,7 @@ struct bContext; struct Object; struct ParticleEditSettings; -struct ParticleSystem; -struct RadialControl; struct rcti; -struct wmKeyConfig; struct PTCacheEdit; struct Scene; @@ -69,9 +66,9 @@ void PE_undo_push(struct Scene *scene, const char *str); void PE_undo_step(struct Scene *scene, int step); void PE_undo(struct Scene *scene); void PE_redo(struct Scene *scene); -int PE_undo_valid(struct Scene *scene); +bool PE_undo_is_valid(struct Scene *scene); void PE_undo_number(struct Scene *scene, int nr); -const char *PE_undo_get_name(struct Scene *scene, int nr, int *active); +const char *PE_undo_get_name(struct Scene *scene, int nr, bool *r_active); #endif /* __ED_PARTICLE_H__ */ diff --git a/source/blender/editors/include/ED_render.h b/source/blender/editors/include/ED_render.h index 293621c3d19..ba58ae60b15 100644 --- a/source/blender/editors/include/ED_render.h +++ b/source/blender/editors/include/ED_render.h @@ -37,9 +37,6 @@ struct MTex; struct Render; struct Scene; struct ScrArea; -struct RegionView3D; -struct RenderEngine; -struct View3D; struct wmWindowManager; /* render_ops.c */ @@ -50,7 +47,7 @@ void ED_operatortypes_render(void); void ED_render_id_flush_update(struct Main *bmain, struct ID *id); void ED_render_engine_changed(struct Main *bmain); -void ED_render_engine_area_exit(struct ScrArea *sa); +void ED_render_engine_area_exit(struct Main *bmain, struct ScrArea *sa); void ED_render_scene_update(struct Main *bmain, struct Scene *scene, int updated); void ED_viewport_render_kill_jobs(struct wmWindowManager *wm, struct Main *bmain, bool free_database); @@ -63,17 +60,21 @@ struct Scene *ED_render_job_get_current_scene(const struct bContext *C); * - PR_BUTS_RENDER: preview is rendered for buttons window * - PR_ICON_RENDER: preview is rendered for icons. hopefully fast enough for at least 32x32 * - PR_NODE_RENDER: preview is rendered for node editor + * - PR_ICON_DEFERRED: No render, we just ensure deferred icon data gets generated. */ -#define PR_BUTS_RENDER 0 -#define PR_ICON_RENDER 1 -#define PR_NODE_RENDER 2 +enum { + PR_BUTS_RENDER = 0, + PR_ICON_RENDER = 1, + PR_NODE_RENDER = 2, + PR_ICON_DEFERRED = 3, +}; void ED_preview_init_dbase(void); void ED_preview_free_dbase(void); void ED_preview_shader_job(const struct bContext *C, void *owner, struct ID *id, struct ID *parent, struct MTex *slot, int sizex, int sizey, int method); -void ED_preview_icon_render(struct Scene *scene, struct ID *id, unsigned int *rect, int sizex, int sizey); +void ED_preview_icon_render(struct Main *bmain, struct Scene *scene, struct ID *id, unsigned int *rect, int sizex, int sizey); void ED_preview_icon_job(const struct bContext *C, void *owner, struct ID *id, unsigned int *rect, int sizex, int sizey); void ED_preview_kill_jobs(struct wmWindowManager *wm, struct Main *bmain); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 8c33395cc49..441a9bdb1c1 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -42,7 +42,6 @@ struct wmNotifier; struct wmEvent; struct wmKeyConfig; struct bContext; -struct SpaceType; struct Scene; struct bScreen; struct ARegion; @@ -67,6 +66,7 @@ void ED_region_header_init(struct ARegion *ar); void ED_region_header(const struct bContext *C, struct ARegion *ar); void ED_region_toggle_hidden(struct bContext *C, struct ARegion *ar); void ED_region_info_draw(struct ARegion *ar, const char *text, int block, float fill_color[4]); +void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, rctf frame, float zoomx, float zoomy); void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy); float ED_region_blend_factor(struct ARegion *ar); void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); @@ -76,7 +76,6 @@ void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); void ED_spacetypes_keymap(struct wmKeyConfig *keyconf); int ED_area_header_switchbutton(const struct bContext *C, struct uiBlock *block, int yco); - /* areas */ void ED_area_initialize(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *sa); void ED_area_exit(struct bContext *C, struct ScrArea *sa); @@ -86,6 +85,7 @@ void ED_area_tag_redraw(ScrArea *sa); void ED_area_tag_redraw_regiontype(ScrArea *sa, int type); void ED_area_tag_refresh(ScrArea *sa); void ED_area_do_refresh(struct bContext *C, ScrArea *sa); +void ED_area_azones_update(ScrArea *sa, const int mouse_xy[]); void ED_area_headerprint(ScrArea *sa, const char *str); void ED_area_newspace(struct bContext *C, ScrArea *sa, int type); void ED_area_prevspace(struct bContext *C, ScrArea *sa); @@ -109,10 +109,11 @@ void ED_screen_animation_timer(struct bContext *C, int redraws, int refresh, void ED_screen_animation_timer_update(struct bScreen *screen, int redraws, int refresh); void ED_screen_restore_temp_type(struct bContext *C, ScrArea *sa); 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_prevspace(struct bContext *C, ScrArea *sa, const bool was_prev_temp); void ED_screen_full_restore(struct bContext *C, 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); +bool ED_screen_stereo3d_required(struct bScreen *screen); /* anim */ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute); diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index d3b1a824104..6daaac5bb42 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -34,7 +34,6 @@ struct ARegion; struct bContext; struct Object; struct RegionView3D; -struct Scene; struct ViewContext; struct rcti; diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index a3b4981dad8..732e67a341d 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -35,17 +35,14 @@ /* ******************* Registration Function ********************** */ struct ARegion; -struct EnumPropertyItem; struct ListBase; struct Object; struct View3D; struct bContext; -struct uiLayout; struct wmEvent; struct wmKeyConfig; struct wmKeyMap; struct wmOperatorType; -struct wmWindowManager; void transform_keymap_for_space(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, int spaceid); void transform_operatortypes(void); @@ -86,7 +83,8 @@ enum TfmMode { TFM_ALIGN, TFM_EDGE_SLIDE, TFM_VERT_SLIDE, - TFM_SEQ_SLIDE + TFM_SEQ_SLIDE, + TFM_BONE_ENVELOPE_DIST, }; /* TRANSFORM CONTEXTS */ @@ -109,7 +107,6 @@ enum TfmMode { bool calculateTransformCenter(struct bContext *C, int centerMode, float cent3d[3], float cent2d[2]); struct TransInfo; -struct ScrArea; struct Base; struct Scene; struct Object; diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index e26e03473e0..496ce7f2c60 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -31,15 +31,9 @@ #ifndef __ED_UTIL_H__ #define __ED_UTIL_H__ -struct Scene; -struct Object; struct bContext; -struct ARegion; -struct uiBlock; struct wmOperator; struct wmOperatorType; -struct BMEditMesh; -struct Mesh; /* ed_util.c */ @@ -48,6 +42,8 @@ void ED_editors_exit(struct bContext *C); bool ED_editors_flush_edits(const struct bContext *C, bool for_render); +void ED_spacedata_id_unref(struct SpaceLink *sl, const struct ID *id); + /* ************** Undo ************************ */ /* undo.c */ @@ -66,7 +62,7 @@ int ED_undo_operator_repeat(struct bContext *C, struct wmOperator *op); void ED_undo_operator_repeat_cb(struct bContext *C, void *arg_op, void *arg_unused); void ED_undo_operator_repeat_cb_evt(struct bContext *C, void *arg_op, int arg_unused); -int ED_undo_valid(const struct bContext *C, const char *undoname); +bool ED_undo_is_valid(const struct bContext *C, const char *undoname); /* undo_editmode.c */ void undo_editmode_push(struct bContext *C, const char *name, diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 3e8f234e979..535683823bf 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -37,13 +37,11 @@ struct BMFace; struct BMLoop; struct Image; struct ImageUser; -struct MTFace; struct MTexPoly; struct Main; struct Object; struct Scene; struct SpaceImage; -struct bContext; struct bNode; struct wmKeyConfig; diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 5fc5738c88f..be4204e7cb7 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -61,6 +61,7 @@ struct rcti; struct wmOperator; struct wmOperatorType; struct wmWindow; +struct wmWindowManager; struct GPUFX; struct GPUOffScreen; struct GPUFXSettings; @@ -103,7 +104,7 @@ void ED_view3d_lastview_store(struct RegionView3D *rv3d); /* Depth buffer */ void ED_view3d_depth_update(struct ARegion *ar); -float ED_view3d_depth_read_cached(struct ViewContext *vc, int x, int y); +float ED_view3d_depth_read_cached(const struct ViewContext *vc, int x, int y); void ED_view3d_depth_tag_update(struct RegionView3D *rv3d); /* Projection */ @@ -218,51 +219,62 @@ void ED_view3d_win_to_vector(const struct ARegion *ar, const float mval[2], floa bool ED_view3d_win_to_segment(const struct ARegion *ar, struct View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_end[3], const bool do_clip); void ED_view3d_ob_project_mat_get(const struct RegionView3D *v3d, struct Object *ob, float pmat[4][4]); +void ED_view3d_ob_project_mat_get_from_obmat(const struct RegionView3D *rv3d, float obmat[4][4], float pmat[4][4]); void ED_view3d_unproject(struct bglMats *mats, float out[3], const float x, const float y, const float z); /* 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, - struct rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize); +void ED_view3d_dist_range_get( + const struct View3D *v3d, + float r_dist_range[2]); +bool ED_view3d_clip_range_get( + const struct View3D *v3d, const struct RegionView3D *rv3d, + float *r_clipsta, float *r_clipend, const bool use_ortho_factor); +bool ED_view3d_viewplane_get( + const struct View3D *v3d, const struct RegionView3D *rv3d, int winxi, int winyi, + struct rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize); void ED_view3d_polygon_offset(const struct RegionView3D *rv3d, const float dist); -void ED_view3d_calc_camera_border(struct Scene *scene, struct ARegion *ar, - struct View3D *v3d, struct RegionView3D *rv3d, - struct rctf *r_viewborder, const bool no_shift); -void ED_view3d_calc_camera_border_size(struct Scene *scene, struct ARegion *ar, - struct View3D *v3d, struct RegionView3D *rv3d, - float r_size[2]); +void ED_view3d_calc_camera_border( + const struct Scene *scene, const struct ARegion *ar, + const struct View3D *v3d, const struct RegionView3D *rv3d, + struct rctf *r_viewborder, const bool no_shift); +void ED_view3d_calc_camera_border_size( + const struct Scene *scene, const struct ARegion *ar, + const struct View3D *v3d, const struct RegionView3D *rv3d, + float r_size[2]); bool ED_view3d_calc_render_border(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, struct rcti *rect); void ED_view3d_clipping_calc_from_boundbox(float clip[6][4], const struct BoundBox *clipbb, const bool is_flip); void ED_view3d_clipping_calc(struct BoundBox *bb, float planes[4][4], struct bglMats *mats, const struct rcti *rect); void ED_view3d_clipping_local(struct RegionView3D *rv3d, float mat[4][4]); -bool ED_view3d_clipping_test(struct RegionView3D *rv3d, const float co[3], const bool is_local); +bool ED_view3d_clipping_test(const struct RegionView3D *rv3d, const float co[3], const bool is_local); void ED_view3d_clipping_set(struct RegionView3D *rv3d); void ED_view3d_clipping_enable(void); void ED_view3d_clipping_disable(void); -float ED_view3d_pixel_size(struct RegionView3D *rv3d, const float co[3]); +float ED_view3d_pixel_size(const struct RegionView3D *rv3d, const float co[3]); -float ED_view3d_radius_to_persp_dist(const float angle, const float radius); -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius); +float ED_view3d_radius_to_dist_persp(const float angle, const float radius); +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius); +float ED_view3d_radius_to_dist( + const struct View3D *v3d, const struct ARegion *ar, + const char persp, const bool use_aspect, + const float radius); void drawcircball(int mode, const float cent[3], float rad, float tmat[4][4]); /* backbuffer select and draw support */ -void view3d_validate_backbuf(struct ViewContext *vc); -struct ImBuf *view3d_read_backbuf(struct ViewContext *vc, short xmin, short ymin, short xmax, short ymax); -unsigned int view3d_sample_backbuf_rect(struct ViewContext *vc, const int mval[2], int size, - unsigned int min, unsigned int max, float *dist, short strict, - void *handle, bool (*indextest)(void *handle, unsigned int index)); -unsigned int view3d_sample_backbuf(struct ViewContext *vc, int x, int y); +void ED_view3d_backbuf_validate(struct ViewContext *vc); +struct ImBuf *ED_view3d_backbuf_read(struct ViewContext *vc, short xmin, short ymin, short xmax, short ymax); +unsigned int ED_view3d_backbuf_sample_rect( + struct ViewContext *vc, const int mval[2], int size, + unsigned int min, unsigned int max, float *r_dist); +int ED_view3d_backbuf_sample_size_clamp(struct ARegion *ar, const float dist); +unsigned int ED_view3d_backbuf_sample(struct ViewContext *vc, int x, int y); /* draws and does a 4x4 sample */ bool ED_view3d_autodist(struct Scene *scene, struct ARegion *ar, struct View3D *v3d, @@ -313,12 +325,13 @@ void ED_view3d_draw_offscreen( struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, struct GPUOffScreen *ofs, - struct GPUFX *fx, struct GPUFXSettings *fx_settings); + struct GPUFX *fx, struct GPUFXSettings *fx_settings, + const char *viewname); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]); + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]); + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip); @@ -329,15 +342,13 @@ char ED_view3d_lock_view_from_index(int index); char ED_view3d_axis_view_opposite(char view); bool ED_view3d_lock(struct RegionView3D *rv3d); -uint64_t ED_view3d_datamask(struct Scene *scene, struct View3D *v3d); -uint64_t ED_view3d_screen_datamask(struct bScreen *screen); +uint64_t ED_view3d_datamask(const struct Scene *scene, const struct View3D *v3d); +uint64_t ED_view3d_screen_datamask(const struct bScreen *screen); -bool ED_view3d_view_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); - -bool ED_view3d_offset_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_offset_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); /* camera lock functions */ -bool ED_view3d_camera_lock_check(struct View3D *v3d, struct RegionView3D *rv3d); +bool ED_view3d_camera_lock_check(const struct View3D *v3d, const struct RegionView3D *rv3d); /* copy the camera to the view before starting a view transformation */ void ED_view3d_camera_lock_init_ex(struct View3D *v3d, struct RegionView3D *rv3d, const bool calc_dist); void ED_view3d_camera_lock_init(struct View3D *v3d, struct RegionView3D *rv3d); @@ -377,6 +388,10 @@ void ED_view3d_operator_properties_viewmat_get(struct wmOperator *op, int *winx, #endif /* render */ +void ED_view3d_stop_render_preview(struct wmWindowManager *wm, struct ARegion *ar); void ED_view3d_shade_update(struct Main *bmain, struct Scene *scene, struct View3D *v3d, struct ScrArea *sa); +#define V3D_IS_ZBUF(v3d) \ + (((v3d)->flag & V3D_ZBUF_SELECT) && ((v3d)->drawtype > OB_WIRE)) + #endif /* __ED_VIEW3D_H__ */ diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index b1bb48b58ed..b5e1b9f08b1 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -40,14 +40,11 @@ /* Struct Declarations */ struct ID; -struct Main; struct ListBase; struct ARegion; -struct ARegionType; struct ScrArea; struct wmEvent; struct wmWindow; -struct wmWindowManager; struct wmOperator; struct AutoComplete; struct bContext; @@ -58,18 +55,14 @@ struct PointerRNA; struct PropertyRNA; struct ReportList; struct rcti; -struct rctf; struct uiList; struct uiStyle; struct uiFontStyle; struct uiWidgetColors; -struct ColorBand; -struct CurveMapping; struct Image; struct ImageUser; struct wmOperatorType; struct uiWidgetColors; -struct Tex; struct MTex; struct ImBuf; struct bNodeTree; @@ -181,10 +174,11 @@ enum { UI_BUT_COLOR_CUBIC = (1 << 23), /* cubic saturation for the color wheel */ UI_BUT_LIST_ITEM = (1 << 24), /* This but is "inside" a list item (currently used to change theme colors). */ 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_SCA_LINK_GREY = (1 << 26), /* used to flag if sca links shoud be gray 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 */ UI_BUT_TEXTEDIT_UPDATE = (1 << 29), /* when widget is in textedit mode, update value on each char stroke */ + UI_BUT_SEARCH_UNLINK = (1 << 30), /* show unlink for search button */ }; #define UI_PANEL_WIDTH 340 @@ -279,7 +273,6 @@ typedef enum { UI_BTYPE_WAVEFORM = (49 << 9), UI_BTYPE_VECTORSCOPE = (50 << 9), UI_BTYPE_PROGRESS_BAR = (51 << 9), - UI_BTYPE_SEARCH_MENU_UNLINK = (52 << 9), UI_BTYPE_NODE_SOCKET = (53 << 9), UI_BTYPE_SEPR = (54 << 9), UI_BTYPE_SEPR_LINE = (55 << 9), @@ -326,6 +319,11 @@ void UI_draw_safe_areas( #define UI_SCROLL_NO_OUTLINE (1 << 2) void UI_draw_widget_scroll(struct uiWidgetColors *wcol, const struct rcti *rect, const struct rcti *slider, int state); +/* Shortening string helper. */ +float UI_text_clip_middle_ex( + struct uiFontStyle *fstyle, char *str, float okwidth, const float minwidth, + const size_t max_len, const char rpart_sep); + /* Callbacks * * UI_block_func_handle_set/ButmFunc are for handling events through a callback. @@ -403,7 +401,7 @@ void UI_popup_block_invoke_ex(struct bContext *C, uiBlockCreateFunc func, void * void UI_popup_block_ex(struct bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc popup_func, uiBlockCancelFunc cancel_func, void *arg); /* void uiPupBlockOperator(struct bContext *C, uiBlockCreateFunc func, struct wmOperator *op, int opcontext); */ /* UNUSED */ -void UI_popup_block_close(struct bContext *C, uiBlock *block); +void UI_popup_block_close(struct bContext *C, struct wmWindow *win, uiBlock *block); /* Blocks * @@ -490,6 +488,14 @@ bool UI_but_active_only(const struct bContext *C, struct ARegion *ar, uiBlock void UI_but_execute(const struct bContext *C, uiBut *but); +bool UI_but_online_manual_id( + const uiBut *but, + char *r_str, size_t maxlength) + ATTR_WARN_UNUSED_RESULT; +bool UI_but_online_manual_id_from_active( + const struct bContext *C, + char *r_str, size_t maxlength) + ATTR_WARN_UNUSED_RESULT; /* Buttons * @@ -682,6 +688,7 @@ void UI_but_func_drawextra_set( void *arg1, void *arg2); void UI_but_func_tooltip_set(uiBut *but, uiButToolTipFunc func, void *argN); +void UI_but_tooltip_timer_remove(struct bContext *C, uiBut *but); bool UI_textbutton_activate_rna(const struct bContext *C, struct ARegion *ar, const void *rna_poin_data, const char *rna_prop_id); @@ -739,7 +746,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_region_handlers_add(struct ListBase *handlers); -void UI_popup_handlers_add(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click); +void UI_popup_handlers_add(struct bContext *C, struct ListBase *handlers, uiPopupBlockHandle *popup, const char flag); void UI_popup_handlers_remove(struct ListBase *handlers, uiPopupBlockHandle *popup); void UI_popup_handlers_remove_all(struct bContext *C, struct ListBase *handlers); @@ -882,7 +889,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, struct PointerRNA *ptr); void uiTemplatePreview(uiLayout *layout, struct bContext *C, struct ID *id, int show_buttons, struct ID *parent, struct MTex *slot, const char *preview_id); void uiTemplateColorRamp(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int expand); -void uiTemplateIconView(uiLayout *layout, struct PointerRNA *ptr, const char *propname); +void uiTemplateIconView(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int show_labels); 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); @@ -894,8 +901,11 @@ void uiTemplateLayers(uiLayout *layout, struct PointerRNA *ptr, const char *prop PointerRNA *used_ptr, const char *used_propname, int active_layer); void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char *propname, PointerRNA *used_ptr, const char *used_propname, int active_state); -void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact); +void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact, int multiview); void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management); +void uiTemplateImageStereo3d(uiLayout *layout, struct PointerRNA *stereo3d_format_ptr); +void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imaptr); +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr); void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser); void uiTemplateImageInfo(uiLayout *layout, struct bContext *C, Image *ima, ImageUser *iuser); void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); @@ -971,13 +981,17 @@ void ED_button_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); +bool UI_context_copy_to_selected_list( + struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, + struct ListBase *r_lb, bool *r_use_path_from_id, char **r_path); + /* Helpers for Operators */ uiBut *UI_context_active_but_get(const struct bContext *C); void UI_context_active_but_prop_get(const struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop, int *index); void UI_context_active_but_prop_handle(struct bContext *C); struct wmOperator *UI_context_active_operator_get(const struct bContext *C); void UI_context_update_anim_flag(const struct bContext *C); -void UI_context_active_but_prop_get_filebrowser(const struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop); +void UI_context_active_but_prop_get_filebrowser(const struct bContext *C, struct PointerRNA *r_ptr, struct PropertyRNA **r_prop, bool *r_is_undo); void UI_context_active_but_prop_get_templateID(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA **prop); /* Styled text draw */ diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 74927428363..634dd3d5bbc 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -34,12 +34,6 @@ struct bContext; struct ID; -struct Image; -struct ImBuf; -struct World; -struct Tex; -struct Lamp; -struct Material; struct PreviewImage; struct PointerRNA; @@ -55,7 +49,7 @@ typedef struct IconFile { #define ICON_DEFAULT_HEIGHT_SCALE ((int)(UI_UNIT_Y * 0.8f)) #define ICON_DEFAULT_WIDTH_SCALE ((int)(UI_UNIT_X * 0.8f)) -#define PREVIEW_DEFAULT_HEIGHT 96 +#define PREVIEW_DEFAULT_HEIGHT 128 /* * Resizable Icons for Blender @@ -70,7 +64,7 @@ void UI_id_icon_render( void UI_icon_draw(float x, float y, int icon_id); void UI_icon_draw_preview(float x, float y, int icon_id); void UI_icon_draw_preview_aspect(float x, float y, int icon_id, float aspect); -void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, int size); +void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, float alpha, int size); void UI_icon_draw_aspect(float x, float y, int icon_id, float aspect, float alpha); void UI_icon_draw_aspect_color(float x, float y, int icon_id, float aspect, const float rgb[3]); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 4a484212a63..2b19b6180e5 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -294,14 +294,16 @@ enum { TH_INFO_DEBUG_TEXT, TH_VIEW_OVERLAY, - TH_V3D_CLIPPING_BORDER + TH_V3D_CLIPPING_BORDER, + + TH_METADATA_BG, + TH_METADATA_TEXT }; /* XXX WARNING: previous is saved in file, so do not change order! */ /* specific defines per space should have higher define values */ struct bTheme; -struct PointerRNA; struct bThemeState { struct bTheme *theme; diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index 4d7446a7a81..2c8f5f6590a 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -233,7 +233,6 @@ void ED_keymap_view2d(struct wmKeyConfig *keyconf); void UI_view2d_smooth_view(struct bContext *C, struct ARegion *ar, const struct rctf *cur, const int smooth_viewtx); - #define UI_MARKER_MARGIN_Y (42 * UI_DPI_FAC) #endif /* __UI_VIEW2D_H__ */ diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index bad09a7c441..e045db8fdd2 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -37,6 +37,7 @@ #include "MEM_guardedalloc.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_userdef_types.h" @@ -82,11 +83,11 @@ #define B_NOP -1 -/* - * a full doc with API notes can be found in bf-blender/trunk/blender/doc/guides/interface_API.txt +/** + * a full doc with API notes can be found in 'blender/doc/guides/interface_API.txt' * - * uiBlahBlah() external function - * ui_blah_blah() internal function + * `uiBlahBlah()` external function. + * `ui_blah_blah()` internal function. */ static void ui_but_free(const bContext *C, uiBut *but); @@ -513,7 +514,7 @@ static void ui_draw_links(uiBlock *block) uiBut *but; uiLinkLine *line; - /* Draw the grey out lines. Do this first so they appear at the + /* Draw the gray out lines. Do this first so they appear at the * bottom of inactive or active lines. * As we go, remember if we see any active or selected lines. */ bool found_selectline = false; @@ -548,7 +549,7 @@ static void ui_draw_links(uiBlock *block) } /* Draw any active lines (lines with either button being hovered over). - * Do this last so they appear on top of inactive and grey out lines. */ + * Do this last so they appear on top of inactive and gray out lines. */ if (found_activeline) { for (but = block->buttons.first; but; but = but->next) { if (but->type == UI_BTYPE_LINK && but->link) { @@ -1099,13 +1100,16 @@ static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but, return found; } -/* this goes in a seemingly weird pattern: +/** + * This goes in a seemingly weird pattern: * + * <pre> * 4 * 5 6 * 1 2 * 7 8 * 3 + * </pre> * * 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 @@ -1930,7 +1934,7 @@ void ui_but_value_set(uiBut *but, double value) int ui_but_string_get_max_length(uiBut *but) { - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) + if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) return but->hardmax; else return UI_MAX_DRAW_STR; @@ -1951,6 +1955,56 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but) return but_iter; } +/** \name Check to show extra icons + * + * Extra icons are shown on the right hand side of buttons. + * \{ */ + +static bool ui_but_icon_extra_is_visible_search_unlink(const uiBut *but) +{ + BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); + return ((but->editstr == NULL) && + (but->drawstr[0] != '\0') && + (but->flag & UI_BUT_SEARCH_UNLINK)); +} + +static bool ui_but_icon_extra_is_visible_eyedropper(uiBut *but) +{ + StructRNA *type; + short idcode; + + BLI_assert(but->type == UI_BTYPE_SEARCH_MENU && (but->flag & UI_BUT_SEARCH_UNLINK)); + + if (but->rnaprop == NULL) { + return false; + } + + type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop); + idcode = RNA_type_to_ID_code(type); + + + return ((but->editstr == NULL) && + (idcode == ID_OB || OB_DATA_SUPPORT_ID(idcode))); +} + +uiButExtraIconType ui_but_icon_extra_get(uiBut *but) +{ + if ((but->flag & UI_BUT_SEARCH_UNLINK) == 0) { + /* pass */ + } + else if (ui_but_icon_extra_is_visible_search_unlink(but)) { + return UI_BUT_ICONEXTRA_UNLINK; + } + else if (ui_but_icon_extra_is_visible_eyedropper(but)) { + return UI_BUT_ICONEXTRA_EYEDROPPER; + } + + return UI_BUT_ICONEXTRA_NONE; +} + +/** \} */ + + static double ui_get_but_scale_unit(uiBut *but, double value) { UnitSettings *unit = but->block->unit; @@ -2031,7 +2085,7 @@ static float ui_get_but_step_unit(uiBut *but, float step_default) */ void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int float_precision) { - if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { PropertyType type; const char *buf = NULL; int buf_len; @@ -2073,7 +2127,7 @@ void ui_but_string_get_ex(uiBut *but, char *str, const size_t maxlen, const int BLI_strncpy(str, but->poin, maxlen); return; } - else if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + else if (but->type == UI_BTYPE_SEARCH_MENU) { /* string */ BLI_strncpy(str, but->poin, maxlen); return; @@ -2194,7 +2248,7 @@ static void ui_but_string_free_internal(uiBut *but) bool ui_but_string_set(bContext *C, uiBut *but, const char *str) { - if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (but->rnaprop && ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { if (RNA_property_editable(&but->rnapoin, but->rnaprop)) { PropertyType type; @@ -2246,7 +2300,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) return true; } - else if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + else if (but->type == UI_BTYPE_SEARCH_MENU) { /* string */ BLI_strncpy(but->poin, str, but->hardmax); return true; @@ -2264,6 +2318,7 @@ bool ui_but_string_set(bContext *C, uiBut *but, const char *str) double value; if (ui_but_string_set_eval_num(C, but, str, &value) == false) { + WM_report_banner_show(C); return false; } @@ -2714,11 +2769,11 @@ void ui_but_update(uiBut *but) } else { const int prec = ui_but_calc_float_precision(but, value); - slen += BLI_snprintf(but->drawstr + slen, sizeof(but->drawstr) - slen, "%.*f", prec, value); + slen += BLI_snprintf_rlen(but->drawstr + slen, sizeof(but->drawstr) - slen, "%.*f", prec, value); } } else { - slen += BLI_snprintf(but->drawstr + slen, sizeof(but->drawstr) - slen, "%d", (int)value); + slen += BLI_snprintf_rlen(but->drawstr + slen, sizeof(but->drawstr) - slen, "%d", (int)value); } if (but->rnaprop) { @@ -2754,7 +2809,6 @@ void ui_but_update(uiBut *but) case UI_BTYPE_TEXT: case UI_BTYPE_SEARCH_MENU: - case UI_BTYPE_SEARCH_MENU_UNLINK: if (!but->editstr) { char str[UI_MAX_DRAW_STR]; @@ -3047,17 +3101,18 @@ void ui_block_cm_to_scene_linear_v3(uiBlock *block, float pixel[3]) /** * \brief ui_def_but is the function that draws many button types * - * \param x,y The lower left hand corner of the button (X axis) - * \param width,height The size of the button. + * \param x, y: The lower left hand corner of the button (X axis) + * \param width, height: The size of the button. * * for float buttons: - * - \a a1 Click Step (how much to change the value each click) - * - \a a2 Number of decimal point values to display. 0 defaults to 3 (0.000) - * 1,2,3, and a maximum of 4, all greater values will be clamped to 4. + * \param a1: Click Step (how much to change the value each click) + * \param a2: Number of decimal point values to display. 0 defaults to 3 (0.000) + * 1,2,3, and a maximum of 4, all greater values will be clamped to 4. */ -static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, - int x, int y, short width, short height, - void *poin, float min, float max, float a1, float a2, const char *tip) +static uiBut *ui_def_but( + uiBlock *block, int type, int retval, const char *str, + int x, int y, short width, short height, + void *poin, float min, float max, float a1, float a2, const char *tip) { uiBut *but; int slen; @@ -3139,7 +3194,7 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, ELEM(but->type, UI_BTYPE_MENU, UI_BTYPE_TEXT, UI_BTYPE_LABEL, UI_BTYPE_BLOCK, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, - UI_BTYPE_PROGRESS_BAR, UI_BTYPE_SEARCH_MENU_UNLINK)) + UI_BTYPE_PROGRESS_BAR)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -3186,6 +3241,19 @@ static uiBut *ui_def_but(uiBlock *block, int type, int retval, const char *str, return but; } +void ui_def_but_icon(uiBut *but, const int icon, const int flag) +{ + if (icon) { + ui_icon_ensure_deferred(but->block->evil_C, icon, (flag & UI_BUT_ICON_PREVIEW) != 0); + } + but->icon = (BIFIconID)icon; + but->flag |= flag; + + if (but->str && but->str[0]) { + but->drawflag |= UI_BUT_ICON_LEFT; + } +} + static void ui_def_but_rna__disable(uiBut *but) { but->flag |= UI_BUT_DISABLED; @@ -3325,10 +3393,11 @@ static void ui_def_but_rna__menu(bContext *UNUSED(C), uiLayout *layout, void *bu * When this kind of change won't disrupt branches, best look into making more * of our UI functions take prop rather then propname. */ -static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *str, - int x, int y, short width, short height, - PointerRNA *ptr, PropertyRNA *prop, int index, - float min, float max, float a1, float a2, const char *tip) +static uiBut *ui_def_but_rna( + uiBlock *block, int type, int retval, const char *str, + int x, int y, short width, short height, + PointerRNA *ptr, PropertyRNA *prop, int index, + float min, float max, float a1, float a2, const char *tip) { const PropertyType proptype = RNA_property_type(prop); uiBut *but; @@ -3448,11 +3517,7 @@ static uiBut *ui_def_but_rna(uiBlock *block, int type, int retval, const char *s but->rnaindex = 0; if (icon) { - but->icon = (BIFIconID)icon; - but->flag |= UI_HAS_ICON; - if (str[0]) { - but->drawflag |= UI_BUT_ICON_LEFT; - } + ui_def_but_icon(but, icon, UI_HAS_ICON); } if ((type == UI_BTYPE_MENU) && (but->dt == UI_EMBOSS_PULLDOWN)) { @@ -3639,8 +3704,7 @@ int UI_autocomplete_end(AutoComplete *autocpl, char *autoname) static void ui_but_update_and_icon_set(uiBut *but, int icon) { if (icon) { - but->icon = (BIFIconID) icon; - but->flag |= UI_HAS_ICON; + ui_def_but_icon(but, icon, UI_HAS_ICON); } ui_but_update(but); @@ -4014,7 +4078,7 @@ void UI_but_drag_set_value(uiBut *but) void UI_but_drag_set_image(uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale) { but->dragtype = WM_DRAG_PATH; - but->icon = icon; /* no flag UI_HAS_ICON, so icon doesnt draw in button */ + ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesnt draw in button */ but->dragpoin = (void *)path; but->imb = imb; but->imb_scale = scale; @@ -4168,8 +4232,7 @@ uiBut *uiDefIconTextMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, in { uiBut *but = ui_def_but(block, UI_BTYPE_PULLDOWN, 0, str, x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip); - but->icon = (BIFIconID) icon; - but->flag |= UI_HAS_ICON; + ui_def_but_icon(but, icon, UI_HAS_ICON); but->drawflag |= UI_BUT_ICON_LEFT; but->flag |= UI_BUT_ICON_SUBMENU; @@ -4184,8 +4247,7 @@ uiBut *uiDefIconMenuBut(uiBlock *block, uiMenuCreateFunc func, void *arg, int ic { uiBut *but = ui_def_but(block, UI_BTYPE_PULLDOWN, 0, "", x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip); - but->icon = (BIFIconID) icon; - but->flag |= UI_HAS_ICON; + ui_def_but_icon(but, icon, UI_HAS_ICON); but->drawflag &= ~UI_BUT_ICON_LEFT; but->menu_create_func = func; @@ -4201,7 +4263,7 @@ uiBut *uiDefIconTextBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, /* XXX temp, old menu calls pass on icon arrow, which is now UI_BUT_ICON_SUBMENU flag */ if (icon != ICON_RIGHTARROW_THIN) { - but->icon = (BIFIconID) icon; + ui_def_but_icon(but, icon, 0); but->drawflag |= UI_BUT_ICON_LEFT; } but->flag |= UI_HAS_ICON; @@ -4218,9 +4280,8 @@ uiBut *uiDefIconBlockBut(uiBlock *block, uiBlockCreateFunc func, void *arg, int { uiBut *but = ui_def_but(block, UI_BTYPE_BLOCK, retval, "", x, y, width, height, arg, 0.0, 0.0, 0.0, 0.0, tip); - but->icon = (BIFIconID) icon; - but->flag |= UI_HAS_ICON; - + ui_def_but_icon(but, icon, UI_HAS_ICON); + but->drawflag |= UI_BUT_ICON_LEFT; but->block_create_func = func; @@ -4253,9 +4314,8 @@ uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxle { uiBut *but = ui_def_but(block, UI_BTYPE_SEARCH_MENU, retval, "", x, y, width, height, arg, 0.0, maxlen, a1, a2, tip); - but->icon = (BIFIconID) icon; - but->flag |= UI_HAS_ICON; - + ui_def_but_icon(but, icon, UI_HAS_ICON); + but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT; ui_but_update(but); @@ -4264,8 +4324,11 @@ uiBut *uiDefSearchBut(uiBlock *block, void *arg, int retval, int icon, int maxle } -/* arg is user value, searchfunc and handlefunc both get it as arg */ -/* if active set, button opens with this item visible and selected */ +/** + * \param sfunc, bfunc: both get it as \a arg. + * \param arg: user value, + * \param active: when set, button opens with this item visible and selected. + */ void UI_but_func_search_set(uiBut *but, uiButSearchFunc sfunc, void *arg, uiButHandleFunc bfunc, void *active) { but->search_func = sfunc; @@ -4334,11 +4397,14 @@ static void operator_enum_call_cb(struct bContext *UNUSED(C), void *but, void *a } } -/* Same parameters as for uiDefSearchBut, with additional operator type and properties, used by callback - * to call again the right op with the right options (properties values). */ -uiBut *uiDefSearchButO_ptr(uiBlock *block, wmOperatorType *ot, IDProperty *properties, - void *arg, int retval, int icon, int maxlen, int x, int y, - short width, short height, float a1, float a2, const char *tip) +/** + * Same parameters as for uiDefSearchBut, with additional operator type and properties, used by callback + * to call again the right op with the right options (properties values). + */ +uiBut *uiDefSearchButO_ptr( + uiBlock *block, wmOperatorType *ot, IDProperty *properties, + void *arg, int retval, int icon, int maxlen, int x, int y, + short width, short height, float a1, float a2, const char *tip) { uiBut *but; @@ -4357,7 +4423,8 @@ uiBut *uiDefSearchButO_ptr(uiBlock *block, wmOperatorType *ot, IDProperty *prope return but; } -/* push a new event onto event queue to activate the given button +/** + * push a new event onto event queue to activate the given button * (usually a text-field) upon entering a popup */ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) @@ -4390,7 +4457,19 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) if (type == BUT_GET_LABEL) { if (but->str) { - tmp = BLI_strdup(but->str); + const char *str_sep; + size_t str_len; + + if ((but->flag & UI_BUT_HAS_SEP_CHAR) && + (str_sep = strrchr(but->str, UI_SEP_CHAR))) + { + str_len = (str_sep - but->str); + } + else { + str_len = strlen(but->str); + } + + tmp = BLI_strdupn(but->str, str_len); } else { type = BUT_GET_RNA_LABEL; /* Fail-safe solution... */ diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 24a30ebe3d8..f6757b35462 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -54,13 +54,13 @@ #include "interface_intern.h" -static FCurve *ui_but_get_fcurve(uiBut *but, AnimData **adt, bAction **action, bool *r_driven) +static FCurve *ui_but_get_fcurve(uiBut *but, AnimData **adt, bAction **action, bool *r_driven, bool *r_special) { /* for entire array buttons we check the first component, it's not perfect * but works well enough in typical cases */ int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; - return rna_get_fcurve_context_ui(but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven); + return rna_get_fcurve_context_ui(but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special); } void ui_but_anim_flag(uiBut *but, float cfra) @@ -69,11 +69,16 @@ void ui_but_anim_flag(uiBut *but, float cfra) bAction *act; FCurve *fcu; bool driven; - + bool special; + but->flag &= ~(UI_BUT_ANIMATED | UI_BUT_ANIMATED_KEY | UI_BUT_DRIVEN); - - fcu = ui_but_get_fcurve(but, &adt, &act, &driven); - + + /* NOTE: "special" is reserved for special F-Curves stored on the animation data + * itself (which are used to animate properties of the animation data). + * We count those as "animated" too for now + */ + fcu = ui_but_get_fcurve(but, &adt, &act, &driven, &special); + if (fcu) { if (!driven) { but->flag |= UI_BUT_ANIMATED; @@ -98,10 +103,10 @@ bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen) { FCurve *fcu; ChannelDriver *driver; - bool driven; - - fcu = ui_but_get_fcurve(but, NULL, NULL, &driven); - + bool driven, special; + + fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special); + if (fcu && driven) { driver = fcu->driver; @@ -118,9 +123,9 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str) { FCurve *fcu; ChannelDriver *driver; - bool driven; + bool driven, special; - fcu = ui_but_get_fcurve(but, NULL, NULL, &driven); + fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special); if (fcu && driven) { driver = fcu->driver; @@ -215,8 +220,9 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) bAction *action; FCurve *fcu; bool driven; + bool special; - fcu = ui_but_get_fcurve(but, NULL, &action, &driven); + fcu = ui_but_get_fcurve(but, NULL, &action, &driven, &special); if (fcu && !driven) { id = but->rnapoin.id.data; diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index 07d580b9138..2c13b199bba 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -508,8 +508,9 @@ static void draw_scope_end(const rctf *rect, GLint *scissor) UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rect->xmin - 1, rect->ymin, rect->xmax + 1, rect->ymax + 1, 3.0f); } -static void histogram_draw_one(float r, float g, float b, float alpha, - float x, float y, float w, float h, const float *data, int res, const bool is_line) +static void histogram_draw_one( + float r, float g, float b, float alpha, + float x, float y, float w, float h, const float *data, int res, const bool is_line) { int i; @@ -1260,8 +1261,8 @@ void ui_draw_but_UNITVEC(uiBut *but, uiWidgetColors *wcol, const rcti *rect) size = BLI_rcti_size_x(rect) / 200.f; else size = BLI_rcti_size_y(rect) / 200.f; - - glScalef(size, size, size); + + glScalef(size, size, MIN2(size, 1.0f)); if (displist == 0) { GLUquadricObj *qobj; @@ -1453,7 +1454,7 @@ void ui_draw_but_CURVE(ARegion *ar, uiBut *but, uiWidgetColors *wcol, const rcti glEnd(); } else if (cumap->cur == 3) { - float lum = rgb_to_bw(cumap->sample); + float lum = IMB_colormanagement_get_luminance(cumap->sample); glColor3ub(240, 240, 240); glBegin(GL_LINES); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index aabb8e29fa9..97aa865c287 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -36,6 +36,8 @@ #include "BLI_blenlib.h" #include "BLI_math_vector.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_report.h" @@ -176,8 +178,8 @@ static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int if (sa) { if (sa->spacetype == SPACE_IMAGE) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); + if (ar) { SpaceImage *sima = sa->spacedata.first; int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -188,8 +190,8 @@ static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int } } else if (sa->spacetype == SPACE_NODE) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); + if (ar) { SpaceNode *snode = sa->spacedata.first; int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -200,8 +202,8 @@ static void eyedropper_color_sample_fl(bContext *C, Eyedropper *UNUSED(eye), int } } else if (sa->spacetype == SPACE_CLIP) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); + if (ar) { SpaceClip *sc = sa->spacedata.first; int mval[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -379,12 +381,11 @@ void UI_OT_eyedropper_color(wmOperatorType *ot) /* -------------------------------------------------------------------- */ -/* Data Dropper - * - * note: datadropper is only internal name to avoid confusion in this file - */ +/* Data Dropper */ /** \name Eyedropper (ID data-blocks) + * + * \note: datadropper is only internal name to avoid confusion in this file. * \{ */ typedef struct DataDropper { @@ -436,7 +437,8 @@ static int datadropper_init(bContext *C, wmOperator *op) type = RNA_property_pointer_type(&ddr->ptr, ddr->prop); ddr->idcode = RNA_type_to_ID_code(type); BLI_assert(ddr->idcode != 0); - ddr->idcode_name = BKE_idcode_to_name(ddr->idcode); + /* Note we can translate here (instead of on draw time), because this struct has very short lifetime. */ + ddr->idcode_name = TIP_(BKE_idcode_to_name(ddr->idcode)); return true; } @@ -456,6 +458,8 @@ static void datadropper_exit(bContext *C, wmOperator *op) op->customdata = NULL; } + + WM_event_add_mousemove(C); } static void datadropper_cancel(bContext *C, wmOperator *op) @@ -482,8 +486,8 @@ static void datadropper_id_sample_pt(bContext *C, DataDropper *ddr, int mx, int if (sa) { if (sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); + if (ar) { const int mval[2] = { mx - ar->winrct.xmin, my - ar->winrct.ymin}; @@ -632,7 +636,7 @@ void UI_OT_eyedropper_id(wmOperatorType *ot) /* identifiers */ ot->name = "Eyedropper Datablock"; ot->idname = "UI_OT_eyedropper_id"; - ot->description = "Sample a color from the Blender Window to store in a property"; + ot->description = "Sample a datablock from the Blender Window to store in a property"; /* api callbacks */ ot->invoke = datadropper_invoke; @@ -651,12 +655,11 @@ void UI_OT_eyedropper_id(wmOperatorType *ot) /* -------------------------------------------------------------------- */ -/* Depth Dropper - * - * note: depthdropper is only internal name to avoid confusion in this file - */ +/* Depth Dropper */ /** \name Eyedropper (Depth) + * + * \note: depthdropper is only internal name to avoid confusion in this file. * \{ */ typedef struct DepthDropper { @@ -765,8 +768,8 @@ static void depthdropper_depth_sample_pt(bContext *C, DepthDropper *ddr, int mx, if (sa) { if (sa->spacetype == SPACE_VIEW3D) { - ARegion *ar = BKE_area_find_region_type(sa, RGN_TYPE_WINDOW); - if (ar && BLI_rcti_isect_pt(&ar->winrct, mx, my)) { + ARegion *ar = BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my); + if (ar) { View3D *v3d = sa->spacedata.first; RegionView3D *rv3d = ar->regiondata; /* weak, we could pass in some reference point */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a6ff5bb3ad3..58cf6b900b9 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -52,9 +52,9 @@ #include "BLI_linklist.h" #include "BLI_string.h" #include "BLI_string_utf8.h" +#include "BLI_string_cursor_utf8.h" #include "BLI_rect.h" #include "BLI_utildefines.h" -#include "BLI_string_cursor_utf8.h" #include "BLF_translation.h" @@ -99,6 +99,9 @@ /* support dragging multiple number buttons at once */ #define USE_DRAG_MULTINUM +/* allow dragging/editing all other selected items at once */ +#define USE_ALLSELECT + /* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */ #define USE_KEYNAV_LIMIT @@ -158,17 +161,63 @@ typedef enum uiHandleButtonState { } uiHandleButtonState; +#ifdef USE_ALLSELECT + +/* Unfortunately theres no good way handle more generally: + * (propagate single clicks on layer buttons to other objects) */ +#define USE_ALLSELECT_LAYER_HACK + +typedef struct uiSelectContextElem { + PointerRNA ptr; + union { + bool val_b; + int val_i; + float val_f; + }; +} uiSelectContextElem; + +typedef struct uiSelectContextStore { + uiSelectContextElem *elems; + int elems_len; + bool do_free; + bool is_enabled; + /* When set, simply copy values (don't apply difference). + * Rules are: + * - dragging numbers uses delta. + * - typing in values will assign to all. */ + bool is_copy; +} uiSelectContextStore; + +static bool ui_selectcontext_begin( + bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data); +static void ui_selectcontext_end( + uiBut *but, uiSelectContextStore *selctx_data); +static void ui_selectcontext_apply( + bContext *C, uiBut *but, struct uiSelectContextStore *selctx_data, + const double value, const double value_orig); + +#define IS_ALLSELECT_EVENT(event) ((event)->alt != 0) + +/** just show a tinted color so users know its activated */ +#define UI_BUT_IS_SELECT_CONTEXT UI_BUT_NODE_ACTIVE + +#endif /* USE_ALLSELECT */ + + #ifdef USE_DRAG_MULTINUM -/* how far to drag before we check for gesture direction (in pixels), +/** + * how far to drag before we check for gesture direction (in pixels), * 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 checking 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) -/* how strict to be when detecting a vertical gesture, [0.5 == sloppy], [0.9 == strict], (unsigned dot-product) +/** + * how strict to be when detecting a vertical gesture, [0.5 == sloppy], [0.9 == strict], (unsigned dot-product) * note: we should be quite strict here, since doing a vertical gesture by accident should be avoided, * however with some care a user should be able to do a vertical movement without *missing*. */ #define DRAG_MULTINUM_THRESHOLD_VERTICAL (0.75f) @@ -178,6 +227,10 @@ typedef enum uiHandleButtonState { typedef struct uiButMultiState { double origvalue; uiBut *but; + +#ifdef USE_ALLSELECT + uiSelectContextStore select_others; +#endif } uiButMultiState; typedef struct uiHandleButtonMulti { @@ -210,8 +263,6 @@ typedef struct uiHandleButtonMulti { #endif /* USE_DRAG_MULTINUM */ - - typedef struct uiHandleButtonData { wmWindowManager *wm; wmWindow *window; @@ -280,6 +331,10 @@ typedef struct uiHandleButtonData { uiHandleButtonMulti multi_data; #endif +#ifdef USE_ALLSELECT + uiSelectContextStore select_others; +#endif + /* post activate */ uiButtonActivateType posttype; uiBut *postbut; @@ -328,14 +383,14 @@ static uiBut *ui_but_find_mouse_over_ex(ARegion *ar, const int x, const int y, c static uiBut *ui_but_find_mouse_over(ARegion *ar, const wmEvent *event); static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type); static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state); -static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *data, - const bool mousemove, const bool onfree); +static void button_activate_exit( + bContext *C, uiBut *but, uiHandleButtonData *data, + const bool mousemove, const bool onfree); static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *userdata); static void ui_handle_button_activate(bContext *C, ARegion *ar, uiBut *but, uiButtonActivateType type); -static void button_timers_tooltip_remove(bContext *C, uiBut *but); #ifdef USE_DRAG_MULTINUM -static void ui_multibut_restore(uiHandleButtonData *data, uiBlock *block); +static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block); static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but); #endif @@ -366,7 +421,7 @@ static void ui_color_snap_hue(const enum eSnapType snap, float *r_hue) { const float snap_increment = (snap == SNAP_ON_SMALL) ? 24 : 12; BLI_assert(snap != SNAP_OFF); - *r_hue = floorf(0.5f + ((*r_hue) * snap_increment)) / snap_increment; + *r_hue = roundf((*r_hue) * snap_increment) / snap_increment; } /* assumes event type is MOUSEPAN */ @@ -413,7 +468,7 @@ bool ui_but_is_editable_as_text(const uiBut *but) { return ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER, - UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK); + UI_BTYPE_SEARCH_MENU); } @@ -478,10 +533,9 @@ static float ui_mouse_scale_warp_factor(const bool shift) return shift ? 0.05f : 1.0f; } -static void ui_mouse_scale_warp(uiHandleButtonData *data, - const float mx, const float my, - float *r_mx, float *r_my, - const bool shift) +static void ui_mouse_scale_warp( + uiHandleButtonData *data, const float mx, const float my, + float *r_mx, float *r_my, const bool shift) { const float fac = ui_mouse_scale_warp_factor(shift); @@ -894,7 +948,7 @@ static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut return NULL; } -static void ui_multibut_restore(uiHandleButtonData *data, uiBlock *block) +static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block) { uiBut *but; @@ -903,6 +957,16 @@ static void ui_multibut_restore(uiHandleButtonData *data, uiBlock *block) uiButMultiState *mbut_state = ui_multibut_lookup(data, but); if (mbut_state) { ui_but_value_set(but, mbut_state->origvalue); + +#ifdef USE_ALLSELECT + if (mbut_state->select_others.elems_len > 0) { + ui_selectcontext_apply( + C, but, &mbut_state->select_others, + mbut_state->origvalue, mbut_state->origvalue); + } +#else + UNUSED_VARS(C); +#endif } } } @@ -910,7 +974,26 @@ static void ui_multibut_restore(uiHandleButtonData *data, uiBlock *block) static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block) { +#ifdef USE_ALLSELECT + if (data->multi_data.mbuts) { + LinkNode *list = data->multi_data.mbuts; + while (list) { + LinkNode *next = list->next; + uiButMultiState *mbut_state = list->link; + + if (mbut_state->select_others.elems) { + MEM_freeN(mbut_state->select_others.elems); + } + + MEM_freeN(list->link); + MEM_freeN(list); + list = next; + } + } +#else BLI_linklist_freeN(data->multi_data.mbuts); +#endif + data->multi_data.mbuts = NULL; if (data->multi_data.bs_mbuts) { @@ -979,6 +1062,7 @@ static void ui_multibut_states_create(uiBut *but_active, uiHandleButtonData *dat uiBut *but; BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP); + BLI_assert(data->multi_data.has_mbuts); data->multi_data.bs_mbuts = UI_butstore_create(but_active->block); @@ -1016,6 +1100,24 @@ static void ui_multibut_states_apply(bContext *C, uiHandleButtonData *data, uiBl void *active_back; ui_but_execute_begin(C, ar, but, &active_back); + +#ifdef USE_ALLSELECT + if (data->select_others.is_enabled) { + /* init once! */ + if (mbut_state->select_others.elems_len == 0) { + ui_selectcontext_begin(C, but, &mbut_state->select_others); + } + if (mbut_state->select_others.elems_len == 0) { + mbut_state->select_others.elems_len = -1; + } + } + + /* needed so we apply the right deltas */ + but->active->origvalue = mbut_state->origvalue; + but->active->select_others = mbut_state->select_others; + but->active->select_others.do_free = false; +#endif + BLI_assert(active_back == NULL); /* no need to check 'data->state' here */ if (data->str) { @@ -1064,8 +1166,9 @@ typedef struct uiDragToggleHandle { int xy_last[2]; } uiDragToggleHandle; -static bool ui_drag_toggle_set_xy_xy(bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, - const int xy_src[2], const int xy_dst[2]) +static bool ui_drag_toggle_set_xy_xy( + bContext *C, ARegion *ar, const bool is_set, const eButType but_type_start, + const int xy_src[2], const int xy_dst[2]) { /* popups such as layers won't re-evaluate on redraw */ const bool do_check = (ar->regiontype == RGN_TYPE_TEMPORARY); @@ -1225,6 +1328,267 @@ static bool ui_but_is_drag_toggle(const uiBut *but) #endif /* USE_DRAG_TOGGLE */ +#ifdef USE_ALLSELECT + +static bool ui_selectcontext_begin( + bContext *C, uiBut *but, uiSelectContextStore *selctx_data) +{ + PointerRNA ptr, lptr, idptr; + PropertyRNA *prop, *lprop; + bool success = false; + int index; + + char *path = NULL; + ListBase lb = {NULL}; + + ptr = but->rnapoin; + prop = but->rnaprop; + index = but->rnaindex; + + /* for now don't support whole colors */ + if (index == -1) + return false; + + /* if there is a valid property that is editable... */ + if (ptr.data && prop) { + CollectionPointerLink *link; + bool use_path_from_id; + int i; + + /* some facts we want to know */ + const bool is_array = RNA_property_array_check(prop); + const int rna_type = RNA_property_type(prop); + + if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) { + goto finally; + } + + selctx_data->elems_len = BLI_listbase_count(&lb); + if (selctx_data->elems_len == 0) { + goto finally; + } + + selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__); + + for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) { + uiSelectContextElem *other = &selctx_data->elems[i]; + /* TODO,. de-duplicate copy_to_selected_button */ + 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; + } + + /* lptr might not be the same as link->ptr! */ + if ((lptr.data != ptr.data) && + (lprop == prop) && + RNA_property_editable(&lptr, lprop)) + { + other->ptr = lptr; + if (is_array) { + if (rna_type == PROP_FLOAT) { + other->val_f = RNA_property_float_get_index(&lptr, lprop, index); + } + else if (rna_type == PROP_INT) { + other->val_i = RNA_property_int_get_index(&lptr, lprop, index); + } + /* ignored for now */ +#if 0 + else if (rna_type == PROP_BOOLEAN) { + other->val_b = RNA_property_boolean_get_index(&lptr, lprop, index); + } +#endif + } + else { + if (rna_type == PROP_FLOAT) { + other->val_f = RNA_property_float_get(&lptr, lprop); + } + else if (rna_type == PROP_INT) { + other->val_i = RNA_property_int_get(&lptr, lprop); + } + /* ignored for now */ +#if 0 + else if (rna_type == PROP_BOOLEAN) { + other->val_b = RNA_property_boolean_get(&lptr, lprop); + } + else if (rna_type == PROP_ENUM) { + other->val_i = RNA_property_enum_get(&lptr, lprop); + } +#endif + } + + continue; + } + } + + selctx_data->elems_len -= 1; + i -= 1; + } + } + + success = (selctx_data->elems_len != 0); + +finally: + if (selctx_data->elems_len == 0) { + MEM_SAFE_FREE(selctx_data->elems); + } + + MEM_SAFE_FREE(path); + BLI_freelistN(&lb); + + /* caller can clear */ + selctx_data->do_free = true; + + if (success) { + but->flag |= UI_BUT_IS_SELECT_CONTEXT; + } + + return success; +} + +static void ui_selectcontext_end( + uiBut *but, uiSelectContextStore *selctx_data) +{ + if (selctx_data->do_free) { + if (selctx_data->elems) { + MEM_freeN(selctx_data->elems); + } + } + + but->flag &= ~UI_BUT_IS_SELECT_CONTEXT; +} + +static void ui_selectcontext_apply( + bContext *C, uiBut *but, uiSelectContextStore *selctx_data, + const double value, const double value_orig) +{ + if (selctx_data->elems) { + PropertyRNA *prop = but->rnaprop; + PropertyRNA *lprop = but->rnaprop; + int index = but->rnaindex; + int i; + const bool use_delta = (selctx_data->is_copy == false); + + union { + bool b; + int i; + float f; + } delta, min, max; + + const bool is_array = RNA_property_array_check(prop); + const int rna_type = RNA_property_type(prop); + + if (rna_type == PROP_FLOAT) { + delta.f = use_delta ? (value - value_orig) : value; + RNA_property_float_range(&but->rnapoin, prop, &min.f, &max.f); + } + else if (rna_type == PROP_INT) { + delta.i = use_delta ? ((int)value - (int)value_orig) : (int)value; + RNA_property_int_range(&but->rnapoin, prop, &min.i, &max.i); + } + else if (rna_type == PROP_ENUM) { + delta.i = RNA_property_enum_get(&but->rnapoin, prop); /* not a delta infact */ + } + else if (rna_type == PROP_BOOLEAN) { + if (is_array) { + delta.b = RNA_property_boolean_get_index(&but->rnapoin, prop, index); /* not a delta infact */ + } + else { + delta.b = RNA_property_boolean_get(&but->rnapoin, prop); /* not a delta infact */ + } + } + +#ifdef USE_ALLSELECT_LAYER_HACK + /* make up for not having 'handle_layer_buttons' */ + { + PropertySubType subtype = RNA_property_subtype(prop); + + if ((rna_type == PROP_BOOLEAN) && + ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && + is_array && + /* could check for 'handle_layer_buttons' */ + but->func) + { + wmWindow *win = CTX_wm_window(C); + if (!win->eventstate->shift) { + const int len = RNA_property_array_length(&but->rnapoin, prop); + int *tmparray = MEM_callocN(sizeof(int) * len, __func__); + + tmparray[index] = true; + + for (i = 0; i < selctx_data->elems_len; i++) { + uiSelectContextElem *other = &selctx_data->elems[i]; + PointerRNA lptr = other->ptr; + RNA_property_boolean_set_array(&lptr, lprop, tmparray); + RNA_property_update(C, &lptr, lprop); + } + + MEM_freeN(tmparray); + + return; + } + } + } +#endif + + for (i = 0; i < selctx_data->elems_len; i++) { + uiSelectContextElem *other = &selctx_data->elems[i]; + PointerRNA lptr = other->ptr; + + if (rna_type == PROP_FLOAT) { + float other_value = use_delta ? (other->val_f + delta.f) : delta.f; + CLAMP(other_value, min.f, max.f); + if (is_array) { + RNA_property_float_set_index(&lptr, lprop, index, other_value); + } + else { + RNA_property_float_set(&lptr, lprop, other_value); + } + } + else if (rna_type == PROP_INT) { + int other_value = use_delta ? (other->val_i + delta.i) : delta.i; + CLAMP(other_value, min.i, max.i); + if (is_array) { + RNA_property_int_set_index(&lptr, lprop, index, other_value); + } + else { + RNA_property_int_set(&lptr, lprop, other_value); + } + } + else if (rna_type == PROP_BOOLEAN) { + const bool other_value = delta.b; + if (is_array) { + RNA_property_boolean_set_index(&lptr, lprop, index, other_value); + } + else { + RNA_property_boolean_set(&lptr, lprop, delta.b); + } + } + else if (rna_type == PROP_ENUM) { + const int other_value = delta.i; + BLI_assert(!is_array); + RNA_property_enum_set(&lptr, lprop, other_value); + } + + RNA_property_update(C, &lptr, prop); + } + } +} + +#endif /* USE_ALLSELECT */ + + static bool ui_but_contains_point_px_icon(uiBut *but, ARegion *ar, const wmEvent *event) { rcti rect; @@ -1278,10 +1642,11 @@ static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data, ar_prev = CTX_wm_region(C); CTX_wm_region_set(C, data->region); - WM_event_add_ui_handler(C, &data->window->modalhandlers, - ui_handler_region_drag_toggle, - ui_handler_region_drag_toggle_remove, - drag_info, false); + WM_event_add_ui_handler( + C, &data->window->modalhandlers, + ui_handler_region_drag_toggle, + ui_handler_region_drag_toggle_remove, + drag_info, WM_HANDLER_BLOCKING); CTX_wm_region_set(C, ar_prev); } @@ -1596,9 +1961,8 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton data->str = data->origstr; data->origstr = NULL; data->value = data->origvalue; - data->origvalue = 0.0; copy_v3_v3(data->vec, data->origvec); - data->origvec[0] = data->origvec[1] = data->origvec[2] = 0.0f; + /* postpone clearing origdata */ } else { /* we avoid applying interactive edits a second time @@ -1609,6 +1973,27 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton else if (data->applied_interactive) { return; } + +#ifdef USE_ALLSELECT +# ifdef USE_DRAG_MULTINUM + if (but->flag & UI_BUT_DRAG_MULTI) { + /* pass */ + } + else +# endif + if (data->select_others.elems_len == 0) { + wmWindow *win = CTX_wm_window(C); + /* may have been enabled before activating */ + if (data->select_others.is_enabled || IS_ALLSELECT_EVENT(win->eventstate)) { + ui_selectcontext_begin(C, but, &data->select_others); + data->select_others.is_enabled = true; + } + } + if (data->select_others.elems_len == 0) { + /* dont check again */ + data->select_others.elems_len = -1; + } +#endif } /* ensures we are writing actual values */ @@ -1629,7 +2014,6 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton ui_apply_but_BUT(C, but, data); break; case UI_BTYPE_TEXT: - case UI_BTYPE_SEARCH_MENU_UNLINK: case UI_BTYPE_SEARCH_MENU: ui_apply_but_TEX(C, but, data); break; @@ -1705,7 +2089,7 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton if (data->multi_data.has_mbuts) { if (data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) { if (data->cancel) { - ui_multibut_restore(data, block); + ui_multibut_restore(C, data, block); } else { ui_multibut_states_apply(C, data, block); @@ -1714,6 +2098,15 @@ static void ui_apply_but(bContext *C, uiBlock *block, uiBut *but, uiHandleButton } #endif +#ifdef USE_ALLSELECT + ui_selectcontext_apply(C, but, &data->select_others, data->value, data->origvalue); +#endif + + if (data->cancel) { + data->origvalue = 0.0; + zero_v3(data->origvec); + } + but->editstr = editstr; but->editval = editval; but->editvec = editvec; @@ -1732,13 +2125,13 @@ 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 (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { ID *id = (ID *)wmd->poin; button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); BLI_strncpy(data->str, id->name + 2, data->maxlen); - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) { but->changed = true; ui_searchbox_update(C, data->searchbox, but, true); } @@ -1879,7 +2272,7 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, } /* text/string and ID data */ - else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + else if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { uiHandleButtonData *active_data = but->active; if (but->poin == NULL && but->rnapoin.data == NULL) { @@ -1899,7 +2292,7 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, else BLI_strncpy(active_data->str, buf_paste, active_data->maxlen); - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (but->type == UI_BTYPE_SEARCH_MENU) { /* else uiSearchboxData.active member is not updated [#26856] */ but->changed = true; ui_searchbox_update(C, data->searchbox, but, true); @@ -1974,15 +2367,17 @@ static void ui_but_copy_paste(bContext *C, uiBut *but, uiHandleButtonData *data, } } -/* ************************ password text ****************************** +/** + * Password Text + * ============= * * Functions to convert password strings that should not be displayed - * to asterisk representation (e.g. mysecretpasswd -> *************) + * to asterisk representation (e.g. 'mysecretpasswd' -> '*************') * * It converts every UTF-8 character to an asterisk, and also remaps * the cursor position and selection start/end. * - * Note: remaping is used, because password could contain UTF-8 characters. + * \note: remaping is used, because password could contain UTF-8 characters. * */ @@ -2092,7 +2487,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con BLI_strncpy(origstr, but->editstr, data->maxlen); - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { if (but->flag & UI_HAS_ICON) { startx += UI_DPI_ICON_SIZE / aspect; } @@ -2182,11 +2577,13 @@ static void ui_textedit_set_cursor_select(uiBut *but, uiHandleButtonData *data, ui_but_update(but); } -/* this is used for both utf8 and ascii, its meant to be used for single keys, +/** + * This is used for both utf8 and ascii, its meant to be used for single keys, * notice the buffer is either copied or not, so its not suitable for pasting in * - campbell */ -static bool ui_textedit_type_buf(uiBut *but, uiHandleButtonData *data, - const char *utf8_buf, int utf8_buf_len) +static bool ui_textedit_type_buf( + uiBut *but, uiHandleButtonData *data, + const char *utf8_buf, int utf8_buf_len) { char *str; int len; @@ -2230,8 +2627,9 @@ static bool ui_textedit_type_ascii(uiBut *but, uiHandleButtonData *data, char as return ui_textedit_type_buf(but, data, buf, 1); } -static void ui_textedit_move(uiBut *but, uiHandleButtonData *data, strCursorJumpDirection direction, - const bool select, strCursorJumpType jump) +static void ui_textedit_move( + uiBut *but, uiHandleButtonData *data, strCursorJumpDirection direction, + const bool select, strCursorJumpType jump) { const char *str = data->str; const int len = strlen(str); @@ -2447,6 +2845,18 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in } #ifdef WITH_INPUT_IME +/* test if the translation context allows IME input - used to + * avoid weird character drawing if IME inputs non-ascii chars */ +static bool ui_ime_is_lang_supported(void) +{ + const char *uilng = BLF_lang_get(); + const bool is_lang_supported = STREQ(uilng, "zh_CN") || + STREQ(uilng, "zh_TW") || + STREQ(uilng, "ja_JP"); + + return ((U.transopts & USER_DOTRANSLATE) && is_lang_supported); +} + /* enable ime, and set up uibut ime data */ static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but)) { @@ -2477,8 +2887,7 @@ void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete) wm_window_IME_begin(but->active->window, x, y - 4, 0, 0, complete); } -/* should be ui_but_ime_data_get */ -wmIMEData *ui_but_get_ime_data(uiBut *but) +wmIMEData *ui_but_ime_data_get(uiBut *but) { if (but->active && but->active->window) { return but->active->window->ime_data; @@ -2512,6 +2921,16 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) } #endif +#ifdef USE_ALLSELECT + if (is_num_but) { + if (IS_ALLSELECT_EVENT(win->eventstate)) { + data->select_others.is_enabled = true; + data->select_others.is_copy = true; + + } + } +#endif + /* retrieve string */ data->maxlen = ui_but_string_get_max_length(but); data->str = MEM_callocN(sizeof(char) * data->maxlen + 1, "textedit str"); @@ -2539,7 +2958,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) but->selend = len; /* optional searchbox */ - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (but->type == UI_BTYPE_SEARCH_MENU) { data->searchbox = ui_searchbox_create(C, data->region, but); ui_searchbox_update(C, data->searchbox, but, true); /* true = reset */ } @@ -2552,7 +2971,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) WM_cursor_modal_set(win, BC_TEXTEDITCURSOR); #ifdef WITH_INPUT_IME - if (is_num_but == false) { + if (is_num_but == false && ui_ime_is_lang_supported()) { ui_textedit_ime_begin(win, but); } #endif @@ -2789,6 +3208,9 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_searchbox_event(C, data->searchbox, but, event); break; } + if (event->type == WHEELDOWNMOUSE) { + break; + } /* fall-through */ case ENDKEY: ui_textedit_move(but, data, STRCUR_DIR_NEXT, @@ -2804,6 +3226,9 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_searchbox_event(C, data->searchbox, but, event); break; } + if (event->type == WHEELUPMOUSE) { + break; + } /* fall-through */ case HOMEKEY: ui_textedit_move(but, data, STRCUR_DIR_PREV, @@ -2854,8 +3279,6 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle if (autocomplete == AUTOCOMPLETE_FULL_MATCH) button_activate_state(C, but, BUTTON_STATE_EXIT); - - update = true; /* do live update for tab key */ } /* the hotkey here is not well defined, was G.qual so we check all */ else if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { @@ -2934,7 +3357,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle #endif if (changed) { - /* only update when typing for TAB key */ + /* only do live update when but flag request it (UI_BUT_TEXTEDIT_UPDATE). */ if (update && data->interactive) { ui_apply_but(C, block, but, data, true); } @@ -3092,6 +3515,15 @@ static void ui_block_open_begin(bContext *C, uiBut *but, uiHandleButtonData *dat data->menu->popup = but->block->handle->popup; } +#ifdef USE_ALLSELECT + { + wmWindow *win = CTX_wm_window(C); + if (IS_ALLSELECT_EVENT(win->eventstate)) { + data->select_others.is_enabled = true; + } + } +#endif + /* this makes adjacent blocks auto open from now on */ //if (but->block->auto_open == 0) but->block->auto_open = 1; } @@ -3121,9 +3553,13 @@ int ui_but_menu_direction(uiBut *but) return 0; } -/* Hack for uiList UI_BTYPE_LISTROW buttons to "give" events to overlaying UI_BTYPE_TEXT buttons (cltr-clic rename feature & co). */ -static uiBut *ui_but_list_row_text_activate(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event, - uiButtonActivateType activate_type) +/** + * Hack for #uiList #UI_BTYPE_LISTROW buttons to "give" events to overlaying #UI_BTYPE_TEXT buttons + * (Ctrl-Click rename feature & co). + */ +static uiBut *ui_but_list_row_text_activate( + bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event, + uiButtonActivateType activate_type) { ARegion *ar = CTX_wm_region(C); uiBut *labelbut = ui_but_find_mouse_over_ex(ar, event->x, event->y, true); @@ -3293,9 +3729,11 @@ 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) { + uiButExtraIconType extra_icon_type; + /* unlink icon is on right */ - if (ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS && - ui_but_is_search_unlink_visible(but)) + if ((ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY)) && + ((extra_icon_type = ui_but_icon_extra_get(but)) != UI_BUT_ICONEXTRA_NONE)) { ARegion *ar = data->region; rcti rect; @@ -3306,14 +3744,29 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa BLI_rcti_rctf_copy(&rect, &but->rect); rect.xmin = rect.xmax - (BLI_rcti_size_y(&rect)); + /* handle click on unlink/eyedropper icon */ if (BLI_rcti_isect_pt(&rect, x, y)) { - /* most likely NULL, but let's check, and give it temp zero string */ - if (data->str == NULL) - data->str = MEM_callocN(1, "temp str"); - data->str[0] = 0; + /* doing this on KM_PRESS calls eyedropper after clicking unlink icon */ + if (event->val == KM_RELEASE) { + /* unlink */ + if (extra_icon_type == UI_BUT_ICONEXTRA_UNLINK) { + /* most likely NULL, but let's check, and give it temp zero string */ + if (data->str == NULL) { + data->str = MEM_callocN(1, "temp str"); + } + data->str[0] = 0; - ui_apply_but_TEX(C, but, data); - button_activate_state(C, but, BUTTON_STATE_EXIT); + ui_apply_but_TEX(C, but, data); + button_activate_state(C, but, BUTTON_STATE_EXIT); + } + /* eyedropper */ + else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) { + WM_operator_name_call(C, "UI_OT_eyedropper_id", WM_OP_INVOKE_DEFAULT, NULL); + } + else { + BLI_assert(0); + } + } return WM_UI_HANDLER_BREAK; } @@ -3352,7 +3805,7 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } - else if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { + else if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { /* Support alt+wheel on expanded enum rows */ if (but->type == UI_BTYPE_ROW) { const int direction = (event->type == WHEELDOWNMOUSE) ? -1 : 1; @@ -3448,8 +3901,9 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con } /* var names match ui_numedit_but_NUM */ -static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, float softmax, float softrange, - const enum eSnapType snap) +static float ui_numedit_apply_snapf( + uiBut *but, float tempf, float softmin, float softmax, float softrange, + const enum eSnapType snap) { if (tempf == softmin || tempf == softmax || snap == SNAP_OFF) { /* pass */ @@ -3477,15 +3931,25 @@ static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, floa softrange /= fac; } + /* workaround, too high snapping values */ + /* snapping by 10's for float buttons is quite annoying (location, scale...), + * but allow for rotations */ + if ((softrange > 2100.0f)) { + int unit_type = UI_but_unit_type_get(but); + if (!ELEM(unit_type, PROP_UNIT_ROTATION)) { + softrange = 20.0f; + } + } + if (snap == SNAP_ON) { - if (softrange < 2.10f) tempf = 0.1f * floorf(10.0f * tempf); - else if (softrange < 21.0f) tempf = floorf(tempf); - else tempf = 10.0f * floorf(tempf / 10.0f); + if (softrange < 2.10f) tempf = roundf(tempf * 10.0f) * 0.1f; + else if (softrange < 21.0f) tempf = roundf(tempf); + else tempf = roundf(tempf * 0.1f) * 10.0f; } else if (snap == SNAP_ON_SMALL) { - if (softrange < 2.10f) tempf = 0.01f * floorf(100.0f * tempf); - else if (softrange < 21.0f) tempf = 0.1f * floorf(10.0f * tempf); - else tempf = floor(tempf); + if (softrange < 2.10f) tempf = roundf(tempf * 100.0f) * 0.01f; + else if (softrange < 21.0f) tempf = roundf(tempf * 10.0f) * 0.1f; + else tempf = roundf(tempf); } else { BLI_assert(0); @@ -3498,8 +3962,9 @@ static float ui_numedit_apply_snapf(uiBut *but, float tempf, float softmin, floa return tempf; } -static float ui_numedit_apply_snap(int temp, float softmin, float softmax, - const enum eSnapType snap) +static float ui_numedit_apply_snap( + int temp, float softmin, float softmax, + const enum eSnapType snap) { if (temp == softmin || temp == softmax) return temp; @@ -3518,9 +3983,10 @@ static float ui_numedit_apply_snap(int temp, float softmin, float softmax, return temp; } -static bool ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, - int mx, - const enum eSnapType snap, float fac) +static bool ui_numedit_but_NUM( + uiBut *but, uiHandleButtonData *data, + int mx, + const enum eSnapType snap, float fac) { float deler, tempf, softmin, softmax, softrange; int lvalue, temp; @@ -3636,7 +4102,7 @@ static bool ui_numedit_but_NUM(uiBut *but, uiHandleButtonData *data, if (!is_float) { - temp = floorf(tempf + 0.5f); + temp = iroundf(tempf); temp = ui_numedit_apply_snap(temp, softmin, softmax, snap); @@ -3689,11 +4155,11 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton /* XXX hardcoded keymap check.... */ if (type == MOUSEPAN && event->alt) retval = WM_UI_HANDLER_BREAK; /* allow accumulating values, otherwise scrolling gets preference */ - else if (type == WHEELDOWNMOUSE && event->alt) { + else if (type == WHEELDOWNMOUSE && event->ctrl) { mx = but->rect.xmin; click = 1; } - else if (type == WHEELUPMOUSE && event->alt) { + else if (type == WHEELUPMOUSE && event->ctrl) { mx = but->rect.xmax; click = 1; } @@ -3759,7 +4225,6 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton fac = 1.0f; if (event->shift) fac /= 10.0f; - if (event->alt) fac /= 20.0f; if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), snap, fac)) ui_numedit_apply(C, block, but, data); @@ -3854,9 +4319,10 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton return retval; } -static bool ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, - int mx, const bool is_horizontal, - const bool snap, const bool shift) +static bool ui_numedit_but_SLI( + uiBut *but, uiHandleButtonData *data, + int mx, const bool is_horizontal, + const bool snap, const bool shift) { float deler, f, tempf, softmin, softmax, softrange; int temp, lvalue; @@ -3909,7 +4375,7 @@ static bool ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, tempf = softmin + f * softrange; - temp = floorf(tempf + 0.5f); + temp = iroundf(tempf); if (snap) { if (tempf == softmin || tempf == softmax) { @@ -3919,14 +4385,14 @@ static bool ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, if (shift) { if (tempf == softmin || tempf == softmax) {} - else if (softmax - softmin < 2.10f) tempf = 0.01f * floorf(100.0f * tempf); - else if (softmax - softmin < 21.0f) tempf = 0.1f * floorf(10.0f * tempf); - else tempf = floorf(tempf); + else if (softrange < 2.10f) tempf = roundf(tempf * 100.0f) * 0.01f; + else if (softrange < 21.0f) tempf = roundf(tempf * 10.0f) * 0.1f; + else tempf = roundf(tempf); } else { - if (softmax - softmin < 2.10f) tempf = 0.1f * floorf(10.0f * tempf); - else if (softmax - softmin < 21.0f) tempf = floorf(tempf); - else tempf = 10.0f * floorf(tempf / 10.0f); + if (softrange < 2.10f) tempf = roundf(tempf * 10.0f) * 0.1f; + else if (softrange < 21.0f) tempf = roundf(tempf); + else tempf = roundf(tempf * 0.1f) * 10.0f; } } else { @@ -3936,7 +4402,7 @@ static bool ui_numedit_but_SLI(uiBut *but, uiHandleButtonData *data, } if (!ui_but_is_float(but)) { - lvalue = floor(data->value + 0.5); + lvalue = round(data->value); CLAMP(temp, softmin, softmax); @@ -3978,11 +4444,11 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton /* XXX hardcoded keymap check.... */ if (type == MOUSEPAN && event->alt) retval = WM_UI_HANDLER_BREAK; /* allow accumulating values, otherwise scrolling gets preference */ - else if (type == WHEELDOWNMOUSE && event->alt) { + else if (type == WHEELDOWNMOUSE && event->ctrl) { mx = but->rect.xmin; click = 2; } - else if (type == WHEELUPMOUSE && event->alt) { + else if (type == WHEELUPMOUSE && event->ctrl) { mx = but->rect.xmax; click = 2; } @@ -3991,6 +4457,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); retval = WM_UI_HANDLER_BREAK; } +#ifndef USE_ALLSELECT /* alt-click on sides to get "arrows" like in UI_BTYPE_NUM buttons, and match wheel usage above */ else if (event->type == LEFTMOUSE && event->alt) { int halfpos = BLI_rctf_cent_x(&but->rect); @@ -4000,6 +4467,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton else mx = but->rect.xmax; } +#endif else if (event->type == LEFTMOUSE) { data->dragstartx = mx; data->draglastx = mx; @@ -4286,7 +4754,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_BREAK; } else if (but->type == UI_BTYPE_MENU) { - if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { + if (ELEM(event->type, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { const int direction = (event->type == WHEELDOWNMOUSE) ? -1 : 1; data->value = ui_but_menu_step(but, direction); @@ -4342,9 +4810,10 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co return WM_UI_HANDLER_CONTINUE; } -static bool ui_numedit_but_UNITVEC(uiBut *but, uiHandleButtonData *data, - int mx, int my, - const enum eSnapType snap) +static bool ui_numedit_but_UNITVEC( + uiBut *but, uiHandleButtonData *data, + int mx, int my, + const enum eSnapType snap) { float dx, dy, rad, radsq, mrad, *fp; int mdx, mdy; @@ -4410,7 +4879,7 @@ static bool ui_numedit_but_UNITVEC(uiBut *but, uiHandleButtonData *data, * do this in "angle" space - this gives increments of same size */ for (i = 0; i < 3; i++) { angle = asinf(fp[i]); - angle_snap = floorf(0.5f + (angle / snap_steps_angle)) * snap_steps_angle; + angle_snap = roundf((angle / snap_steps_angle)) * snap_steps_angle; fp[i] = sinf(angle_snap); } normalize_v3(fp); @@ -4460,7 +4929,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); return WM_UI_HANDLER_BREAK; } - else if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->alt) { + else if (ELEM(event->type, MOUSEPAN, WHEELDOWNMOUSE, WHEELUPMOUSE) && event->ctrl) { ColorPicker *cpicker = but->custom_data; float hsv_static[3] = {0.0f}; float *hsv = cpicker ? cpicker->color_data : hsv_static; @@ -4494,6 +4963,12 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co BKE_palette_color_remove(palette, color); button_activate_state(C, but, BUTTON_STATE_EXIT); + + /* this is risky. it works OK for now, + * but if it gives trouble we should delay execution */ + but->rnapoin = PointerRNA_NULL; + but->rnaprop = NULL; + return WM_UI_HANDLER_BREAK; } } @@ -4638,9 +5113,10 @@ static void ui_color_picker_to_rgb_HSVCUBE_v(uiBut *but, const float hsv[3], flo hsv_to_rgb_v(hsv, rgb); } -static bool ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, - int mx, int my, - const enum eSnapType snap, const bool shift) +static bool ui_numedit_but_HSVCUBE( + uiBut *but, uiHandleButtonData *data, + int mx, int my, + const enum eSnapType snap, const bool shift) { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; @@ -4699,8 +5175,8 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, switch ((int)but->a1) { case UI_GRAD_SV: - hsv[2] = x; - hsv[1] = y; + hsv[1] = x; + hsv[2] = y; break; case UI_GRAD_HV: hsv[0] = x; @@ -4757,9 +5233,10 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, return changed; } -static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, - const wmNDOFMotionData *ndof, - const enum eSnapType snap, const bool shift) +static void ui_ndofedit_but_HSVCUBE( + uiBut *but, uiHandleButtonData *data, + const wmNDOFMotionData *ndof, + const enum eSnapType snap, const bool shift) { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; @@ -4777,8 +5254,8 @@ static void ui_ndofedit_but_HSVCUBE(uiBut *but, uiHandleButtonData *data, switch ((int)but->a1) { case UI_GRAD_SV: - hsv[2] += ndof->rvec[2] * sensitivity; - hsv[1] += ndof->rvec[0] * sensitivity; + hsv[1] += ndof->rvec[2] * sensitivity; + hsv[2] += ndof->rvec[0] * sensitivity; break; case UI_GRAD_HV: hsv[0] += ndof->rvec[2] * sensitivity; @@ -4922,9 +5399,10 @@ static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleBu return WM_UI_HANDLER_CONTINUE; } -static bool ui_numedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, - float mx, float my, - const enum eSnapType snap, const bool shift) +static bool ui_numedit_but_HSVCIRCLE( + uiBut *but, uiHandleButtonData *data, + float mx, float my, + const enum eSnapType snap, const bool shift) { rcti rect; bool changed = true; @@ -5019,9 +5497,10 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, return changed; } -static void ui_ndofedit_but_HSVCIRCLE(uiBut *but, uiHandleButtonData *data, - const wmNDOFMotionData *ndof, - const enum eSnapType snap, const bool shift) +static void ui_ndofedit_but_HSVCIRCLE( + uiBut *but, uiHandleButtonData *data, + const wmNDOFMotionData *ndof, + const enum eSnapType snap, const bool shift) { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; @@ -5280,7 +5759,8 @@ static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandle return WM_UI_HANDLER_CONTINUE; } -static bool ui_numedit_but_CURVE(uiBlock *block, uiBut *but, uiHandleButtonData *data, +static bool ui_numedit_but_CURVE( + uiBlock *block, uiBut *but, uiHandleButtonData *data, int evtx, int evty, bool snap, const bool shift) { @@ -5330,8 +5810,8 @@ static bool ui_numedit_but_CURVE(uiBlock *block, uiBut *but, uiHandleButtonData cmp[a].x += fx; cmp[a].y += fy; if (snap) { - cmp[a].x = 0.125f * floorf(0.5f + 8.0f * cmp[a].x); - cmp[a].y = 0.125f * floorf(0.5f + 8.0f * cmp[a].y); + cmp[a].x = 0.125f * roundf(8.0f * cmp[a].x); + cmp[a].y = 0.125f * roundf(8.0f * cmp[a].y); } if (cmp[a].x != origx || cmp[a].y != origy) moved_point = true; @@ -5714,9 +6194,10 @@ static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, con return WM_UI_HANDLER_CONTINUE; } -static bool ui_numedit_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonData *data, - int mx, int my, - const bool shift) +static bool ui_numedit_but_TRACKPREVIEW( + bContext *C, uiBut *but, uiHandleButtonData *data, + int mx, int my, + const bool shift) { MovieClipScopes *scopes = (MovieClipScopes *)but->poin; bool changed = true; @@ -5925,7 +6406,7 @@ static void menu_add_shortcut_cancel(struct bContext *C, void *arg1) static void popup_change_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); UI_popup_block_invoke(C, menu_change_shortcut, but); } @@ -5946,7 +6427,7 @@ static void remove_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) static void popup_add_shortcut_func(bContext *C, void *arg1, void *UNUSED(arg2)) { uiBut *but = (uiBut *)arg1; - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); UI_popup_block_ex(C, menu_add_shortcut, NULL, menu_add_shortcut_cancel, but); } @@ -5995,7 +6476,7 @@ static bool ui_but_menu(bContext *C, uiBut *but) return false; } - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); /* highly unlikely getting the label ever fails */ UI_but_string_info_get(C, but, &label, NULL); @@ -6064,7 +6545,7 @@ static bool ui_but_menu(bContext *C, uiBut *but) } } - if (but->flag & UI_BUT_ANIMATED) { + if ((but->flag & UI_BUT_ANIMATED) && (but->rnapoin.type != &RNA_NlaStrip)) { if (is_array_component) { uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Keyframes"), ICON_NONE, "ANIM_OT_keyframe_clear_button", "all", 1); @@ -6211,7 +6692,7 @@ static bool ui_but_menu(bContext *C, uiBut *but) } /* Show header tools for header buttons. */ - { + if (ui_block_is_menu(but->block) == false) { ARegion *ar = CTX_wm_region(C); if (ar && (ar->regiontype == RGN_TYPE_HEADER)) { uiItemMenuF(layout, IFACE_("Header"), ICON_NONE, ED_screens_header_tools_menu_create, NULL); @@ -6223,14 +6704,9 @@ static bool ui_but_menu(bContext *C, uiBut *but) char buf[512]; PointerRNA ptr_props; - if (but->rnapoin.data && but->rnaprop) { - BLI_snprintf(buf, sizeof(buf), "%s.%s", - RNA_struct_identifier(but->rnapoin.type), RNA_property_identifier(but->rnaprop)); - - WM_operator_properties_create(&ptr_props, "WM_OT_doc_view_manual"); - RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view_manual", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"), - ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); + if (UI_but_online_manual_id(but, buf, sizeof(buf))) { + uiItemO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"), + ICON_NONE, "WM_OT_doc_view_manual_ui_context"); WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); RNA_string_set(&ptr_props, "doc_id", buf); @@ -6246,30 +6722,6 @@ static bool ui_but_menu(bContext *C, uiBut *but) uiItemFullO(layout, "WM_OT_doc_edit", "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0); #endif } - else if (but->optype) { - WM_operator_py_idname(buf, but->optype->idname); - - - WM_operator_properties_create(&ptr_props, "WM_OT_doc_view_manual"); - RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view_manual", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Manual"), - ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); - - WM_operator_properties_create(&ptr_props, "WM_OT_doc_view"); - RNA_string_set(&ptr_props, "doc_id", buf); - uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"), - ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0); - - /* XXX inactive option, not for public! */ -#if 0 - WM_operator_properties_create(&ptr_props, "WM_OT_doc_edit"); - RNA_string_set(&ptr_props, "doc_id", buf); - RNA_string_set(&ptr_props, "doc_new", but->optype->description); - - uiItemFullO(layout, "WM_OT_doc_edit", CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Submit Description"), - ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0); -#endif - } } /* perhaps we should move this into (G.debug & G_DEBUG) - campbell */ @@ -6325,7 +6777,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * WM_operator_name_call(C, "UI_OT_eyedropper_color", WM_OP_INVOKE_DEFAULT, NULL); return WM_UI_HANDLER_BREAK; } - else if (but->type == UI_BTYPE_SEARCH_MENU_UNLINK) { + else if ((but->type == UI_BTYPE_SEARCH_MENU) && + (but->flag & UI_BUT_SEARCH_UNLINK)) + { if (but->rnaprop && RNA_property_type(but->rnaprop) == PROP_POINTER) { StructRNA *type = RNA_property_pointer_type(&but->rnapoin, but->rnaprop); const short idcode = RNA_type_to_ID_code(type); @@ -6482,11 +6936,16 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * break; case UI_BTYPE_TEXT: case UI_BTYPE_SEARCH_MENU: + if ((but->type == UI_BTYPE_SEARCH_MENU) && + (but->flag & UI_BUT_SEARCH_UNLINK)) + { + retval = ui_do_but_SEARCH_UNLINK(C, block, but, data, event); + if (retval & WM_UI_HANDLER_BREAK) { + break; + } + } retval = ui_do_but_TEX(C, block, but, data, event); break; - case UI_BTYPE_SEARCH_MENU_UNLINK: - retval = ui_do_but_SEARCH_UNLINK(C, block, but, data, event); - break; case UI_BTYPE_MENU: case UI_BTYPE_BLOCK: case UI_BTYPE_PULLDOWN: @@ -6586,8 +7045,13 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* just to be sure, check we're dragging more hoz then virt */ abs(event->prevx - event->x) > abs(event->prevy - event->y))) { - ui_multibut_states_create(but, data); - data->multi_data.init = BUTTON_MULTI_INIT_ENABLE; + if (data->multi_data.has_mbuts) { + ui_multibut_states_create(but, data); + data->multi_data.init = BUTTON_MULTI_INIT_ENABLE; + } + else { + data->multi_data.init = BUTTON_MULTI_INIT_DISABLE; + } } } @@ -6725,7 +7189,7 @@ bool UI_but_active_drop_name(bContext *C) uiBut *but = ui_but_find_active_in_region(ar); if (but) { - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) + if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) return 1; } @@ -6840,13 +7304,6 @@ static bool ui_but_is_interactive(const uiBut *but, const bool labeledit) return true; } -bool ui_but_is_search_unlink_visible(const uiBut *but) -{ - BLI_assert(but->type == UI_BTYPE_SEARCH_MENU_UNLINK); - return ((but->editstr == NULL) && - (but->drawstr[0] != '\0')); -} - /* x and y are only used in case event is NULL... */ static uiBut *ui_but_find_mouse_over_ex(ARegion *ar, const int x, const int y, const bool labeledit) { @@ -6934,7 +7391,8 @@ static bool button_modal_state(uiHandleButtonState state) BUTTON_STATE_MENU_OPEN); } -static void button_timers_tooltip_remove(bContext *C, uiBut *but) +/* removes tooltip timer from active but (meaning tooltip is disabled until it's reenabled again) */ +void UI_but_tooltip_timer_remove(bContext *C, uiBut *but) { uiHandleButtonData *data; @@ -7018,7 +7476,7 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s } else { but->flag |= UI_SELECT; - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); } /* text editing */ @@ -7080,7 +7538,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, false); + WM_event_add_ui_handler(C, &data->window->modalhandlers, ui_handler_region_menu, NULL, data, 0); } else { if (button_modal_state(data->state)) { @@ -7125,7 +7583,7 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA copy_v2_fl(data->ungrab_mval, FLT_MAX); #endif - if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, UI_BTYPE_CURVE, UI_BTYPE_SEARCH_MENU)) { /* XXX curve is temp */ } else { @@ -7185,8 +7643,9 @@ static void button_activate_init(bContext *C, ARegion *ar, uiBut *but, uiButtonA } } -static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *data, - const bool mousemove, const bool onfree) +static void button_activate_exit( + bContext *C, uiBut *but, uiHandleButtonData *data, + const bool mousemove, const bool onfree) { uiBlock *block = but->block; uiBut *bt; @@ -7258,6 +7717,10 @@ static void button_activate_exit(bContext *C, uiBut *but, uiHandleButtonData *da if (data->origstr) MEM_freeN(data->origstr); +#ifdef USE_ALLSELECT + ui_selectcontext_end(but, &data->select_others); +#endif + /* redraw (data is but->active!) */ ED_region_tag_redraw(data->region); @@ -7464,7 +7927,7 @@ void UI_context_update_anim_flag(const bContext *C) /************** handle activating a button *************/ -static uiBut *uit_but_find_open_event(ARegion *ar, const wmEvent *event) +static uiBut *ui_but_find_open_event(ARegion *ar, const wmEvent *event) { uiBlock *block; uiBut *but; @@ -7495,7 +7958,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *ar) } } else if (event->type == EVT_BUT_OPEN) { - but = uit_but_find_open_event(ar, event); + but = ui_but_find_open_event(ar, event); if (but) { button_activate_init(C, ar, but, BUTTON_ACTIVATE_OVER); ui_do_button(C, but->block, but, event); @@ -7599,7 +8062,8 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) 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)) && + /* always deactivate button for pie menus, else moving to blank space will leave activated */ + if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(block)) && !ui_but_contains_point_px(ar, but, event->x, event->y)) { exit = true; @@ -7647,7 +8111,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) case WHEELDOWNMOUSE: case MIDDLEMOUSE: case MOUSEPAN: - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); /* fall-through */ default: /* handle button type specific events */ @@ -7808,7 +8272,7 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar) if (val == KM_PRESS) { if (ELEM(type, UPARROWKEY, DOWNARROWKEY) || - ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt))) + ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->ctrl))) { const int value_orig = RNA_property_int_get(&but->rnapoin, but->rnaprop); int value, min, max, inc; @@ -7958,13 +8422,14 @@ static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, u /* ************************* menu handling *******************************/ -/* function used to prevent loosing the open menu when using nested pulldowns, +/** + * Function used to prevent loosing the open menu when using nested pulldowns, * when moving mouse towards the pulldown menu over other buttons that could * steal the highlight from the current button, only checks: * * - while mouse moves in triangular area defined old mouse position and - * left/right side of new menu - * - only for 1 second + * left/right side of new menu. + * - only for 1 second. */ static void ui_mouse_motion_towards_init_ex(uiPopupBlockHandle *menu, const int xy[2], const bool force) @@ -7993,8 +8458,9 @@ static void ui_mouse_motion_towards_reinit(uiPopupBlockHandle *menu, const int x ui_mouse_motion_towards_init_ex(menu, xy, true); } -static bool ui_mouse_motion_towards_check(uiBlock *block, uiPopupBlockHandle *menu, const int xy[2], - const bool use_wiggle_room) +static bool ui_mouse_motion_towards_check( + uiBlock *block, uiPopupBlockHandle *menu, const int xy[2], + const bool use_wiggle_room) { float p1[2], p2[2], p3[2], p4[2]; float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]}; @@ -8182,8 +8648,9 @@ static int ui_menu_scroll(ARegion *ar, uiBlock *block, int my, uiBut *to_bt) * * Without this keyboard navigation from menu's wont work. */ -static bool ui_menu_pass_event_to_parent_if_nonactive(uiPopupBlockHandle *menu, const uiBut *but, - const int level, const int retval) +static bool ui_menu_pass_event_to_parent_if_nonactive( + uiPopupBlockHandle *menu, const uiBut *but, + const int level, const int retval) { if ((level != 0) && (but == NULL)) { menu->menuretval = UI_RETURN_OUT | UI_RETURN_OUT_PARENT; @@ -8553,7 +9020,7 @@ static int ui_handle_menu_event( ui_handle_button_activate(C, ar, but, BUTTON_ACTIVATE); } else { - printf("%s: error, but->menu_key type: %d\n", __func__, but->type); + printf("%s: error, but->menu_key type: %u\n", __func__, but->type); } break; @@ -8622,7 +9089,7 @@ static int ui_handle_menu_event( { if (!but || !ui_but_contains_point_px(ar, but, event->x, event->y)) { if (but) { - button_timers_tooltip_remove(C, but); + UI_but_tooltip_timer_remove(C, but); } menu->is_grab = true; @@ -8701,6 +9168,9 @@ static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPo block = ar->uiblocks.first; but = ui_but_find_active_in_region(ar); + + BLI_assert(but); + data = but->active; submenu = data->menu; @@ -9100,7 +9570,7 @@ static int ui_handle_menus_recursive( /* now handle events for our own menu */ if (retval == WM_UI_HANDLER_CONTINUE || event->type == TIMER) { - const bool do_but_search = (but && ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)); + const bool do_but_search = (but && (but->type == UI_BTYPE_SEARCH_MENU)); if (submenu && submenu->menuretval) { const bool do_ret_out_parent = (submenu->menuretval & UI_RETURN_OUT_PARENT) != 0; retval = ui_handle_menu_return_submenu(C, event, menu); @@ -9384,12 +9854,12 @@ static void ui_popup_handler_remove(bContext *C, void *userdata) void UI_region_handlers_add(ListBase *handlers) { WM_event_remove_ui_handler(handlers, ui_region_handler, ui_region_handler_remove, NULL, false); - WM_event_add_ui_handler(NULL, handlers, ui_region_handler, ui_region_handler_remove, NULL, false); + WM_event_add_ui_handler(NULL, handlers, ui_region_handler, ui_region_handler_remove, NULL, 0); } -void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, const bool accept_dbl_click) +void UI_popup_handlers_add(bContext *C, ListBase *handlers, uiPopupBlockHandle *popup, const char flag) { - WM_event_add_ui_handler(C, handlers, ui_popup_handler, ui_popup_handler_remove, popup, accept_dbl_click); + WM_event_add_ui_handler(C, handlers, ui_popup_handler, ui_popup_handler_remove, popup, flag); } void UI_popup_handlers_remove(ListBase *handlers, uiPopupBlockHandle *popup) @@ -9402,8 +9872,9 @@ void UI_popup_handlers_remove_all(bContext *C, ListBase *handlers) WM_event_free_ui_handler_all(C, handlers, ui_popup_handler, ui_popup_handler_remove); } -bool UI_textbutton_activate_rna(const bContext *C, ARegion *ar, - const void *rna_poin_data, const char *rna_prop_id) +bool UI_textbutton_activate_rna( + const bContext *C, ARegion *ar, + const void *rna_poin_data, const char *rna_prop_id) { uiBlock *block; uiBut *but = NULL; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 679681cb372..db1eacf57dc 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -55,6 +55,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_thumbs.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -888,11 +889,9 @@ int UI_icon_get_height(int icon_id) void UI_icons_init(int first_dyn_id) { -#ifdef WITH_HEADLESS - (void)first_dyn_id; -#else - init_iconfile_list(&iconfilelist); BKE_icons_init(first_dyn_id); +#ifndef WITH_HEADLESS + init_iconfile_list(&iconfilelist); init_internal_icons(); init_brush_icons(); init_matcap_icons(); @@ -904,10 +903,13 @@ void UI_icons_init(int first_dyn_id) static int preview_render_size(enum eIconSizes size) { switch (size) { - case ICON_SIZE_ICON: return 32; - case ICON_SIZE_PREVIEW: return PREVIEW_DEFAULT_HEIGHT; + case ICON_SIZE_ICON: + return ICON_RENDER_DEFAULT_HEIGHT; + case ICON_SIZE_PREVIEW: + return PREVIEW_RENDER_DEFAULT_HEIGHT; + default: + return 0; } - return 0; } /* Create rect for the icon @@ -923,12 +925,49 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) else if (!prv_img->rect[size]) { prv_img->w[size] = render_size; prv_img->h[size] = render_size; - prv_img->changed[size] = 1; + prv_img->flag[size] |= PRV_CHANGED; prv_img->changed_timestamp[size] = 0; prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(unsigned int), "prv_rect"); } } +void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big) +{ + Icon *icon = BKE_icon_get(icon_id); + + if (icon) { + DrawInfo *di = (DrawInfo *)icon->drawinfo; + + if (!di) { + di = icon_create_drawinfo(); + + icon->drawinfo = di; + icon->drawinfo_free = UI_icons_free_drawinfo; + } + + if (di) { + if (di->type == ICON_TYPE_PREVIEW) { + PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj; + + if (prv) { + const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON; + + if (!prv->use_deferred || prv->rect[size] || (prv->flag[size] & PRV_USER_EDITED)) { + return; + } + + icon_create_rect(prv, size); + + /* Always using job (background) version. */ + ED_preview_icon_job(C, prv, NULL, prv->rect[size], prv->w[size], prv->h[size]); + + prv->flag[size] &= ~PRV_CHANGED; + } + } + } + } +} + /* only called when icon has changed */ /* only call with valid pointer from UI_icon_draw */ static void icon_set_image( @@ -940,6 +979,11 @@ static void icon_set_image( return; } + if (prv_img->flag[size] & PRV_USER_EDITED) { + /* user-edited preview, do not auto-update! */ + return; + } + icon_create_rect(prv_img, size); if (use_job) { @@ -951,7 +995,7 @@ static void icon_set_image( scene = CTX_data_scene(C); } /* Immediate version */ - ED_preview_icon_render(scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]); + ED_preview_icon_render(CTX_data_main(C), scene, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size]); } } @@ -961,22 +1005,32 @@ PreviewImage *UI_icon_to_preview(int icon_id) if (icon) { DrawInfo *di = (DrawInfo *)icon->drawinfo; - if (di && di->data.buffer.image) { - ImBuf *bbuf; - - bbuf = IMB_ibImageFromMemory(di->data.buffer.image->datatoc_rect, di->data.buffer.image->datatoc_size, IB_rect, NULL, "<matcap buffer>"); - if (bbuf) { - PreviewImage *prv = BKE_previewimg_create(); - - prv->rect[0] = bbuf->rect; + if (di) { + if (di->type == ICON_TYPE_PREVIEW) { + PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj; - prv->w[0] = bbuf->x; - prv->h[0] = bbuf->y; - - bbuf->rect = NULL; - IMB_freeImBuf(bbuf); - - return prv; + if (prv) { + return BKE_previewimg_copy(prv); + } + } + else if (di->data.buffer.image) { + ImBuf *bbuf; + + bbuf = IMB_ibImageFromMemory(di->data.buffer.image->datatoc_rect, di->data.buffer.image->datatoc_size, + IB_rect, NULL, __func__); + if (bbuf) { + PreviewImage *prv = BKE_previewimg_create(); + + prv->rect[0] = bbuf->rect; + + prv->w[0] = bbuf->x; + prv->h[0] = bbuf->y; + + bbuf->rect = NULL; + IMB_freeImBuf(bbuf); + + return prv; + } } } } @@ -987,6 +1041,10 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), unsigned int *rect, float alpha, const float rgb[3], const bool is_preview) { ImBuf *ima = NULL; + int draw_w = w; + int draw_h = h; + int draw_x = x; + int draw_y = y; /* sanity check */ if (w <= 0 || h <= 0 || w > 2000 || h > 2000) { @@ -1006,21 +1064,34 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), } /* rect contains image in 'rendersize', we only scale if needed */ - if (rw != w && rh != h) { + if (rw != w || rh != h) { + /* preserve aspect ratio and center */ + if (rw > rh) { + draw_w = w; + draw_h = (int)(((float)rh / (float)rw) * (float)w); + draw_y += (h - draw_h) / 2; + } + else if (rw < rh) { + draw_w = (int)(((float)rw / (float)rh) * (float)h); + draw_h = h; + draw_x += (w - draw_w) / 2; + } + /* if the image is squared, the draw_ initialization values are good */ + /* first allocate imbuf for scaling and copy preview into it */ ima = IMB_allocImBuf(rw, rh, 32, IB_rect); memcpy(ima->rect, rect, rw * rh * sizeof(unsigned int)); - IMB_scaleImBuf(ima, w, h); /* scale it */ + IMB_scaleImBuf(ima, draw_w, draw_h); /* scale it */ rect = ima->rect; } /* draw */ if (is_preview) { - glaDrawPixelsSafe(x, y, w, h, w, GL_RGBA, GL_UNSIGNED_BYTE, rect); + glaDrawPixelsSafe(draw_x, draw_y, draw_w, draw_h, draw_w, GL_RGBA, GL_UNSIGNED_BYTE, rect); } else { - glRasterPos2f(x, y); - glDrawPixels(w, h, GL_RGBA, GL_UNSIGNED_BYTE, rect); + glRasterPos2f(draw_x, draw_y); + glDrawPixels(draw_w, draw_h, GL_RGBA, GL_UNSIGNED_BYTE, rect); } if (ima) @@ -1037,8 +1108,9 @@ static void icon_draw_rect(float x, float y, int w, int h, float UNUSED(aspect), } } -static void icon_draw_texture(float x, float y, float w, float h, int ix, int iy, - int UNUSED(iw), int ih, float alpha, const float rgb[3]) +static void icon_draw_texture( + float x, float y, float w, float h, int ix, int iy, + int UNUSED(iw), int ih, float alpha, const float rgb[3]) { float x1, x2, y1, y2; @@ -1080,16 +1152,20 @@ static void icon_draw_texture(float x, float y, float w, float h, int ix, int iy static int get_draw_size(enum eIconSizes size) { switch (size) { - case ICON_SIZE_ICON: return ICON_DEFAULT_HEIGHT; - case ICON_SIZE_PREVIEW: return PREVIEW_DEFAULT_HEIGHT; + case ICON_SIZE_ICON: + return ICON_DEFAULT_HEIGHT; + case ICON_SIZE_PREVIEW: + return PREVIEW_DEFAULT_HEIGHT; + default: + return 0; } - return 0; } -static void icon_draw_size(float x, float y, int icon_id, float aspect, float alpha, const float rgb[3], - enum eIconSizes size, int draw_size, const bool UNUSED(nocreate), const bool is_preview) +static void icon_draw_size( + float x, float y, int icon_id, float aspect, float alpha, const float rgb[3], + enum eIconSizes size, int draw_size, const bool UNUSED(nocreate), const bool is_preview) { bTheme *btheme = UI_GetTheme(); Icon *icon = NULL; @@ -1145,15 +1221,21 @@ static void icon_draw_size(float x, float y, int icon_id, float aspect, float al glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } else if (di->type == ICON_TYPE_PREVIEW) { - PreviewImage *pi = BKE_previewimg_get((ID *)icon->obj); + PreviewImage *pi = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj; if (pi) { /* no create icon on this level in code */ if (!pi->rect[size]) return; /* something has gone wrong! */ /* preview images use premul alpha ... */ - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - icon_draw_rect(x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], 1.0f, NULL, is_preview); + if (GLEW_VERSION_1_4) { + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + else { + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + icon_draw_rect(x, y, w, h, aspect, pi->w[size], pi->h[size], pi->rect[size], alpha, rgb, is_preview); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } } @@ -1162,17 +1244,17 @@ static void icon_draw_size(float x, float y, int icon_id, float aspect, float al static void ui_id_preview_image_render_size( const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job) { - if ((pi->changed[size] || !pi->rect[size])) { /* changed only ever set by dynamic icons */ + if (((pi->flag[size] & PRV_CHANGED) || !pi->rect[size])) { /* changed only ever set by dynamic icons */ /* create the rect if necessary */ icon_set_image(C, scene, id, pi, size, use_job); - pi->changed[size] = 0; + pi->flag[size] &= ~PRV_CHANGED; } } void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job) { - PreviewImage *pi = BKE_previewimg_get(id); + PreviewImage *pi = BKE_previewimg_id_ensure(id); if (pi) { if (big) @@ -1184,7 +1266,7 @@ void UI_id_icon_render(const bContext *C, Scene *scene, ID *id, const bool big, static void ui_id_brush_render(const bContext *C, ID *id) { - PreviewImage *pi = BKE_previewimg_get(id); + PreviewImage *pi = BKE_previewimg_id_ensure(id); enum eIconSizes i; if (!pi) @@ -1193,9 +1275,9 @@ static void ui_id_brush_render(const bContext *C, ID *id) for (i = 0; i < NUM_ICON_SIZES; i++) { /* check if rect needs to be created; changed * only set by dynamic icons */ - if ((pi->changed[i] || !pi->rect[i])) { + if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) { icon_set_image(C, NULL, id, pi, i, true); - pi->changed[i] = 0; + pi->flag[i] &= ~PRV_CHANGED; } } } @@ -1206,7 +1288,7 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) Brush *br = (Brush *)id; if (br->flag & BRUSH_CUSTOM_ICON) { - BKE_icon_getid(id); + BKE_icon_id_ensure(id); ui_id_brush_render(C, id); } else { @@ -1268,7 +1350,7 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big) case ID_IM: /* fall through */ case ID_WO: /* fall through */ case ID_LA: /* fall through */ - iconid = BKE_icon_getid(id); + iconid = BKE_icon_id_ensure(id); /* checks if not exists, or changed */ UI_id_icon_render(C, NULL, id, big, true); break; @@ -1317,8 +1399,9 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big return rnaicon; } -static void icon_draw_at_size(float x, float y, int icon_id, float aspect, float alpha, - enum eIconSizes size, const bool nocreate) +static void icon_draw_at_size( + float x, float y, int icon_id, float aspect, float alpha, + enum eIconSizes size, const bool nocreate) { int draw_size = get_draw_size(size); icon_draw_size(x, y, icon_id, aspect, alpha, NULL, size, draw_size, nocreate, false); @@ -1356,8 +1439,8 @@ void UI_icon_draw_preview_aspect(float x, float y, int icon_id, float aspect) icon_draw_at_size(x, y, icon_id, aspect, 1.0f, ICON_SIZE_PREVIEW, 0); } -void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, int size) +void UI_icon_draw_preview_aspect_size(float x, float y, int icon_id, float aspect, float alpha, int size) { - icon_draw_size(x, y, icon_id, aspect, 1.0f, NULL, ICON_SIZE_PREVIEW, size, false, true); + icon_draw_size(x, y, icon_id, aspect, alpha, NULL, ICON_SIZE_PREVIEW, size, false, true); } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 03816a255ad..9461547a164 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -40,11 +40,9 @@ struct ARegion; struct bContext; -struct IDProperty; struct uiHandleButtonData; struct wmEvent; struct wmOperatorType; -struct wmWindow; struct wmTimer; struct uiStyle; struct uiWidgetColors; @@ -123,6 +121,14 @@ enum { /* warn: rest of uiBut->flag in UI_interface.h */ }; +/* some buttons display icons only under special conditions + * (e.g. 'x' icon in search menu) - used with ui_but_icon_extra_get */ +typedef enum uiButExtraIconType { + UI_BUT_ICONEXTRA_NONE = 1, + UI_BUT_ICONEXTRA_UNLINK, + UI_BUT_ICONEXTRA_EYEDROPPER, +} uiButExtraIconType; + /* but->pie_dir */ typedef enum RadialDirection { UI_RADIAL_NONE = -1, @@ -445,8 +451,9 @@ extern void ui_but_hsv_set(uiBut *but); extern void ui_but_v3_get(uiBut *but, float vec[3]); extern void ui_but_v3_set(uiBut *but, const float vec[3]); -extern void ui_hsvcircle_vals_from_pos(float *val_rad, float *val_dist, const rcti *rect, - const float mx, const float my); +extern void ui_hsvcircle_vals_from_pos( + float *val_rad, float *val_dist, const rcti *rect, + const float mx, const float my); extern void ui_hsvcircle_pos_from_vals(struct uiBut *but, const rcti *rect, float *hsv, float *xpos, float *ypos); extern void ui_hsvcube_pos_from_vals(struct uiBut *but, const rcti *rect, float *hsv, float *xp, float *yp); bool ui_but_is_colorpicker_display_space(struct uiBut *but); @@ -459,6 +466,9 @@ extern bool ui_but_string_set_eval_num(struct bContext *C, uiBut *but, const cha extern int ui_but_string_get_max_length(uiBut *but); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); +void ui_def_but_icon(uiBut *but, const int icon, const int flag); +extern uiButExtraIconType ui_but_icon_extra_get(uiBut *but); + extern void ui_but_default_set(struct bContext *C, const bool all, const bool use_afterfunc); extern void ui_but_update(uiBut *but); @@ -468,7 +478,6 @@ extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_compatible(const uiBut *but_a, const uiBut *but_b) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_rna_valid(uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_utf8(const uiBut *but) ATTR_WARN_UNUSED_RESULT; -extern bool ui_but_is_search_unlink_visible(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern int ui_but_is_pushed_ex(uiBut *but, double *value) ATTR_WARN_UNUSED_RESULT; extern int ui_but_is_pushed(uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -575,20 +584,22 @@ bool ui_searchbox_apply(uiBut *but, struct ARegion *ar); void ui_searchbox_free(struct bContext *C, struct ARegion *ar); void ui_but_search_refresh(uiBut *but); -uiBlock *ui_popup_block_refresh(struct bContext *C, uiPopupBlockHandle *handle, - ARegion *butregion, uiBut *but); +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, - void *arg); -uiPopupBlockHandle *ui_popup_menu_create(struct bContext *C, struct ARegion *butregion, uiBut *but, - uiMenuCreateFunc create_func, void *arg); +uiPopupBlockHandle *ui_popup_block_create( + struct bContext *C, struct ARegion *butregion, uiBut *but, + uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, + void *arg); +uiPopupBlockHandle *ui_popup_menu_create( + struct bContext *C, struct ARegion *butregion, uiBut *but, + uiMenuCreateFunc create_func, void *arg); void ui_popup_block_free(struct bContext *C, uiPopupBlockHandle *handle); int ui_but_menu_step(uiBut *but, int step); -struct AutoComplete; /* interface_panel.c */ extern int ui_handler_panel_region(struct bContext *C, const struct wmEvent *event, struct ARegion *ar); @@ -635,7 +646,7 @@ uiBut *ui_but_find_new(uiBlock *block_old, const uiBut *but_new); #ifdef WITH_INPUT_IME void ui_but_ime_reposition(uiBut *but, int x, int y, bool complete); -struct wmIMEData *ui_but_get_ime_data(uiBut *but); +struct wmIMEData *ui_but_ime_data_get(uiBut *but); #endif /* interface_widgets.c */ @@ -664,6 +675,7 @@ void ui_draw_preview_item(struct uiFontStyle *fstyle, rcti *rect, const char *na void uiStyleInit(void); /* interface_icons.c */ +void ui_icon_ensure_deferred(const struct bContext *C, const int icon_id, const bool big); int ui_id_icon_get(const struct bContext *C, struct ID *id, const bool big); /* resources.c */ diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 44c1ed474d5..a61b208278d 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -356,9 +356,10 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) } /* create buttons for an item with an RNA array */ -static void ui_item_array(uiLayout *layout, uiBlock *block, const char *name, int icon, - PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int UNUSED(h), - bool expand, bool slider, bool toggle, bool icon_only) +static void ui_item_array( + uiLayout *layout, uiBlock *block, const char *name, int icon, + PointerRNA *ptr, PropertyRNA *prop, int len, int x, int y, int w, int UNUSED(h), + bool expand, bool slider, bool toggle, bool icon_only) { uiStyle *style = layout->root->style; uiBut *but; @@ -545,8 +546,9 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2) RNA_property_enum_set(&but->rnapoin, but->rnaprop, current_value); } } -static void ui_item_enum_expand(uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, - const char *uiname, int h, bool icon_only) +static void ui_item_enum_expand( + uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, + const char *uiname, int h, bool icon_only) { /* XXX The way this function currently handles uiname parameter is insane and inconsistent with general UI API: * * uiname is the *enum property* label. @@ -681,14 +683,17 @@ static uiBut *ui_item_with_label(uiLayout *layout, uiBlock *block, const char *n return but; } -void UI_context_active_but_prop_get_filebrowser(const bContext *C, PointerRNA *ptr, PropertyRNA **prop) +void UI_context_active_but_prop_get_filebrowser( + const bContext *C, + PointerRNA *r_ptr, PropertyRNA **r_prop, bool *r_is_undo) { ARegion *ar = CTX_wm_region(C); uiBlock *block; uiBut *but, *prevbut; - memset(ptr, 0, sizeof(*ptr)); - *prop = NULL; + memset(r_ptr, 0, sizeof(*r_ptr)); + *r_prop = NULL; + *r_is_undo = false; if (!ar) return; @@ -700,8 +705,9 @@ void UI_context_active_but_prop_get_filebrowser(const bContext *C, PointerRNA *p /* find the button before the active one */ if ((but->flag & UI_BUT_LAST_ACTIVE) && prevbut && prevbut->rnapoin.data) { if (RNA_property_type(prevbut->rnaprop) == PROP_STRING) { - *ptr = prevbut->rnapoin; - *prop = prevbut->rnaprop; + *r_ptr = prevbut->rnapoin; + *r_prop = prevbut->rnaprop; + *r_is_undo = (prevbut->flag & UI_BUT_UNDO) != 0; return; } } @@ -877,8 +883,9 @@ void uiItemEnumO(uiLayout *layout, const char *opname, const char *name, int ico } -void uiItemsFullEnumO(uiLayout *layout, const char *opname, const char *propname, IDProperty *properties, - int context, int flag) +void uiItemsFullEnumO( + uiLayout *layout, const char *opname, const char *propname, IDProperty *properties, + int context, int flag) { wmOperatorType *ot = WM_operatortype_find(opname, 0); /* print error next */ @@ -1129,8 +1136,9 @@ void uiItemO(uiLayout *layout, const char *name, int icon, const char *opname) /* RNA property items */ -static void ui_item_rna_size(uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, - int index, bool icon_only, int *r_w, int *r_h) +static void ui_item_rna_size( + uiLayout *layout, const char *name, int icon, PointerRNA *ptr, PropertyRNA *prop, + int index, bool icon_only, int *r_w, int *r_h) { PropertyType type; PropertySubType subtype; @@ -1573,11 +1581,14 @@ void ui_but_add_search(uiBut *but, PointerRNA *ptr, PropertyRNA *prop, PointerRN /* turn button into search button */ if (searchprop) { - but->type = RNA_property_is_unlink(prop) ? UI_BTYPE_SEARCH_MENU_UNLINK : UI_BTYPE_SEARCH_MENU; + but->type = UI_BTYPE_SEARCH_MENU; but->hardmax = MAX2(but->hardmax, 256.0f); but->rnasearchpoin = *searchptr; but->rnasearchprop = searchprop; but->drawflag |= UI_BUT_ICON_LEFT | UI_BUT_TEXT_LEFT; + if (RNA_property_is_unlink(prop)) { + but->flag |= UI_BUT_SEARCH_UNLINK; + } if (RNA_property_type(prop) == PROP_ENUM) { /* XXX, this will have a menu string, @@ -1677,8 +1688,9 @@ static void ui_item_menutype_func(bContext *C, uiLayout *layout, void *arg_mt) layout->root->block->flag ^= UI_BLOCK_IS_FLIP; } -static uiBut *ui_item_menu(uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN, - const char *tip, bool force_menu) +static uiBut *ui_item_menu( + uiLayout *layout, const char *name, int icon, uiMenuCreateFunc func, void *arg, void *argN, + const char *tip, bool force_menu) { uiBlock *block = layout->root->block; uiBut *but; @@ -2139,7 +2151,11 @@ static void ui_litem_layout_column(uiLayout *litem) static RadialDirection ui_get_radialbut_vec(float vec[2], short itemnum) { RadialDirection dir; - BLI_assert(itemnum < 8); + + if (itemnum >= 8) { + itemnum %= 8; + printf("Warning: Pie menus with more than 8 items are currently unsupported\n"); + } dir = ui_radial_dir_order[itemnum]; ui_but_pie_dir(dir, vec); @@ -2717,7 +2733,8 @@ uiLayout *uiLayoutBox(uiLayout *layout) return (uiLayout *)ui_layout_box(layout, UI_BTYPE_ROUNDBOX); } -/* Check all buttons defined in this layout, and set any button flagged as UI_BUT_LIST_ITEM as active/selected. +/** + * Check all buttons defined in this layout, and set any button flagged as UI_BUT_LIST_ITEM as active/selected. * Needed to handle correctly text colors of active (selected) list item. */ void ui_layout_list_set_labels_active(uiLayout *layout) @@ -2733,8 +2750,9 @@ void ui_layout_list_set_labels_active(uiLayout *layout) } } -uiLayout *uiLayoutListBox(uiLayout *layout, uiList *ui_list, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *actptr, - PropertyRNA *actprop) +uiLayout *uiLayoutListBox( + uiLayout *layout, uiList *ui_list, PointerRNA *ptr, PropertyRNA *prop, PointerRNA *actptr, + PropertyRNA *actprop) { uiLayoutItemBx *box = ui_layout_box(layout, UI_BTYPE_LISTBOX); uiBut *but = box->roundbox; @@ -3333,9 +3351,10 @@ static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C), void *op_pt, #endif /* this function does not initialize the layout, functions can be called on the layout before and after */ -void uiLayoutOperatorButs(const bContext *C, uiLayout *layout, wmOperator *op, - bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), - const char label_align, const short flag) +void uiLayoutOperatorButs( + const bContext *C, uiLayout *layout, wmOperator *op, + bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), + const char label_align, const short flag) { if (!op->properties) { IDPropertyTemplate val = {0}; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 41d2a90e548..23b20591275 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -33,6 +33,7 @@ #include "DNA_screen_types.h" #include "DNA_text_types.h" /* for UI_OT_reports_to_text */ +#include "DNA_object_types.h" /* for OB_DATA_SUPPORT_ID */ #include "BLI_blenlib.h" #include "BLI_math_color.h" @@ -43,6 +44,7 @@ #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_global.h" +#include "BKE_node.h" #include "BKE_text.h" /* for UI_OT_reports_to_text */ #include "BKE_report.h" @@ -261,7 +263,7 @@ static void UI_OT_unset_property_button(wmOperatorType *ot) /* Copy To Selected Operator ------------------------ */ -static bool copy_to_selected_list( +bool UI_context_copy_to_selected_list( bContext *C, PointerRNA *ptr, PropertyRNA *prop, ListBase *r_lb, bool *r_use_path_from_id, char **r_path) { @@ -277,6 +279,49 @@ static bool copy_to_selected_list( else if (RNA_struct_is_a(ptr->type, &RNA_Sequence)) { *r_lb = CTX_data_collection_get(C, "selected_editable_sequences"); } + else if (RNA_struct_is_a(ptr->type, &RNA_Node) || + RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) + { + ListBase lb = {NULL, NULL}; + char *path = NULL; + bNode *node = NULL; + + /* Get the node we're editing */ + if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { + bNodeTree *ntree = ptr->id.data; + bNodeSocket *sock = ptr->data; + if (nodeFindNode(ntree, sock, &node, NULL)) { + if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) { + /* we're good! */ + } + else { + node = NULL; + } + } + } + else { + node = ptr->data; + } + + /* Now filter by type */ + if (node) { + CollectionPointerLink *link, *link_next; + lb = CTX_data_collection_get(C, "selected_nodes"); + + for (link = lb.first; link; link = link_next) { + bNode *node_data = link->ptr.data; + link_next = link->next; + + if (node_data->type != node->type) { + BLI_remlink(&lb, link); + MEM_freeN(link); + } + } + } + + *r_lb = lb; + *r_path = path; + } else if (ptr->id.data) { ID *id = ptr->id.data; @@ -285,6 +330,51 @@ static bool copy_to_selected_list( *r_use_path_from_id = true; *r_path = RNA_path_from_ID_to_property(ptr, prop); } + else if (OB_DATA_SUPPORT_ID(GS(id->name))) { + /* check we're using the active object */ + const short id_code = GS(id->name); + ListBase lb = CTX_data_collection_get(C, "selected_editable_objects"); + char *path = RNA_path_from_ID_to_property(ptr, prop); + + /* de-duplicate obdata */ + if (!BLI_listbase_is_empty(&lb)) { + CollectionPointerLink *link, *link_next; + + for (link = lb.first; link; link = link->next) { + Object *ob = link->ptr.id.data; + if (ob->data) { + ID *id_data = ob->data; + id_data->flag |= LIB_DOIT; + } + } + + for (link = lb.first; link; link = link_next) { + Object *ob = link->ptr.id.data; + ID *id_data = ob->data; + link_next = link->next; + + if ((id_data == NULL) || + (id_data->flag & LIB_DOIT) == 0 || + (id_data->lib) || + (GS(id_data->name) != id_code)) + { + BLI_remlink(&lb, link); + MEM_freeN(link); + } + else { + /* avoid prepending 'data' to the path */ + RNA_id_pointer_create(id_data, &link->ptr); + } + + if (id_data) { + id_data->flag &= ~LIB_DOIT; + } + } + } + + *r_lb = lb; + *r_path = path; + } 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... */ @@ -325,7 +415,7 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) CollectionPointerLink *link; ListBase lb; - if (!copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) + if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) return success; for (link = lb.first; link; link = link->next) { @@ -553,8 +643,9 @@ void UI_editsource_active_but_test(uiBut *but) BLI_ghash_insert(ui_editsource_info->hash, but, but_store); } -static int editsource_text_edit(bContext *C, wmOperator *op, - char filepath[FILE_MAX], int line) +static int editsource_text_edit( + bContext *C, wmOperator *op, + char filepath[FILE_MAX], int line) { struct Main *bmain = CTX_data_main(C); Text *text; @@ -613,6 +704,7 @@ static int editsource_exec(bContext *C, wmOperator *op) /* redraw and get active button python info */ ED_region_do_draw(C, ar); + ar->do_draw = false; for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; @@ -667,10 +759,12 @@ static void UI_OT_editsource(wmOperatorType *ot) } /* ------------------------------------------------------------------------- */ -/* EditTranslation utility funcs and operator, - * Note: this includes utility functions and button matching checks. - * this only works in conjunction with a py operator! */ +/** + * EditTranslation utility funcs and operator, + * \note: this includes utility functions and button matching checks. + * this only works in conjunction with a py operator! + */ static void edittranslation_find_po_file(const char *root, const char *uilng, char *path, const size_t maxlen) { char tstr[32]; /* Should be more than enough! */ diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index d165e2719c5..665266edb9f 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -82,6 +82,14 @@ /* only show pin header button for pinned panels */ #define USE_PIN_HIDDEN +/* the state of the mouse position relative to the panel */ +typedef enum uiPanelMouseState { + PANEL_MOUSE_OUTSIDE, /* mouse is not in the panel */ + PANEL_MOUSE_INSIDE_CONTENT, /* mouse is in the actual panel content */ + PANEL_MOUSE_INSIDE_HEADER, /* mouse is in the panel header */ + PANEL_MOUSE_INSIDE_SCALE, /* mouse is inside panel scale widget */ +} uiPanelMouseState; + typedef enum uiHandlePanelState { PANEL_STATE_DRAG, PANEL_STATE_DRAG_SCALE, @@ -202,8 +210,9 @@ static void ui_panel_copy_offset(Panel *pa, Panel *papar) } -/* XXX Disabled paneltab handling for now. Old 2.4x feature, *DO NOT* confuse it with new tool tabs in 2.70. ;) - * See also T41704. +/** + * 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 */ @@ -560,6 +569,8 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con rcti headrect; rctf itemrect; int ofsx; + const bool is_closed_x = (panel->flag & PNL_CLOSEDX) ? true : false; + const bool is_closed_y = (panel->flag & PNL_CLOSEDY) ? true : false; if (panel->paneltab) return; if (panel->type && (panel->type->flag & PNL_NO_HEADER)) return; @@ -572,7 +583,7 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con { float minx = rect->xmin; - float maxx = rect->xmax; + float maxx = is_closed_x ? (minx + PNL_HEADER / block->aspect) : rect->xmax; float y = headrect.ymax; glEnable(GL_BLEND); @@ -587,8 +598,11 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con } else if (!(panel->runtime_flag & PNL_FIRST)) { /* draw embossed separator */ - minx += 5.0f / block->aspect; - maxx -= 5.0f / block->aspect; + + if (is_closed_x == false) { + minx += 5.0f / block->aspect; + maxx -= 5.0f / block->aspect; + } glColor4f(0.0f, 0.0f, 0.0f, 0.5f); fdrawline(minx, y, maxx, y); @@ -615,7 +629,7 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con } /* horizontal title */ - if (!(panel->flag & PNL_CLOSEDX)) { + if (is_closed_x == false) { ui_draw_aligned_panel_header(style, block, &headrect, 'h'); /* itemrect smaller */ @@ -631,9 +645,10 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con /* if the panel is minimized vertically: * (------) */ - if (panel->flag & PNL_CLOSEDY) { + if (is_closed_y) { + /* skip */ } - else if (panel->flag & PNL_CLOSEDX) { + else if (is_closed_x) { /* draw vertical title */ ui_draw_aligned_panel_header(style, block, &headrect, 'v'); } @@ -680,9 +695,9 @@ void ui_draw_aligned_panel(uiStyle *style, uiBlock *block, const rcti *rect, con BLI_rctf_scale(&itemrect, 0.35f); - if (panel->flag & PNL_CLOSEDY) + if (is_closed_y) ui_draw_tria_rect(&itemrect, 'h'); - else if (panel->flag & PNL_CLOSEDX) + else if (is_closed_x) ui_draw_tria_rect(&itemrect, 'h'); else ui_draw_tria_rect(&itemrect, 'v'); @@ -729,11 +744,13 @@ typedef struct PanelSort { Panel *pa, *orig; } PanelSort; -/* note about sorting; +/** + * \note about sorting; * the sortorder has a lower value for new panels being added. * however, that only works to insert a single panel, when more new panels get * added the coordinates of existing panels and the previously stored to-be-inserted - * panels do not match for sorting */ + * panels do not match for sorting + */ static int find_leftmost_panel(const void *a1, const void *a2) { @@ -1128,6 +1145,157 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) /******************* region level panel interaction *****************/ +static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, const Panel *pa, const int mx, const int my) +{ + /* open panel */ + if (pa->flag & PNL_CLOSEDX) { + if ((block->rect.xmin <= mx) && (block->rect.xmin + PNL_HEADER >= mx)) { + return PANEL_MOUSE_INSIDE_HEADER; + } + } + /* outside left/right side */ + else if ((block->rect.xmin > mx) || (block->rect.xmax < mx)) { + /* pass */ + } + else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { + return PANEL_MOUSE_INSIDE_HEADER; + } + /* open panel */ + else if (!(pa->flag & PNL_CLOSEDY)) { + if (pa->control & UI_PNL_SCALE) { + if (block->rect.xmax - PNL_HEADER <= mx) { + if (block->rect.ymin + PNL_HEADER >= my) { + return PANEL_MOUSE_INSIDE_SCALE; + } + } + } + if ((block->rect.xmin <= mx) && (block->rect.xmax >= mx)) { + if ((block->rect.ymin <= my) && (block->rect.ymax + PNL_HEADER >= my)) { + return PANEL_MOUSE_INSIDE_CONTENT; + } + } + } + return PANEL_MOUSE_OUTSIDE; +} + +typedef struct uiPanelDragCollapseHandle { + bool was_first_open; + int xy_init[2]; +} uiPanelDragCollapseHandle; + +static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *userdata) +{ + uiPanelDragCollapseHandle *dragcol_data = userdata; + MEM_freeN(dragcol_data); +} + +static void ui_panel_drag_collapse(bContext *C, uiPanelDragCollapseHandle *dragcol_data, const int xy_dst[2]) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + uiBlock *block; + Panel *pa; + + for (block = ar->uiblocks.first; block; block = block->next) { + float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)}; + float xy_b_block[2] = {UNPACK2(xy_dst)}; + rctf rect = block->rect; + int oldflag; + const bool is_horizontal = (panel_aligned(sa, ar) == BUT_HORIZONTAL); + + if ((pa = block->panel) == 0 || (pa->type && (pa->type->flag & PNL_NO_HEADER))) { + continue; + } + oldflag = pa->flag; + + /* lock one axis */ + if (is_horizontal) { + xy_b_block[1] = dragcol_data->xy_init[1]; + } + else { + xy_b_block[0] = dragcol_data->xy_init[0]; + } + + /* use cursor coords in block space */ + ui_window_to_block_fl(ar, block, &xy_a_block[0], &xy_a_block[1]); + ui_window_to_block_fl(ar, block, &xy_b_block[0], &xy_b_block[1]); + + /* set up rect to match header size */ + rect.ymin = rect.ymax; + rect.ymax = rect.ymin + PNL_HEADER; + if (pa->flag & PNL_CLOSEDX) { + rect.xmax = rect.xmin + PNL_HEADER; + } + + /* touch all panels between last mouse coord and the current one */ + if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) { + /* force panel to close */ + if (dragcol_data->was_first_open == true) { + pa->flag |= (is_horizontal ? PNL_CLOSEDX : PNL_CLOSEDY); + } + /* force panel to open */ + else { + pa->flag &= ~PNL_CLOSED; + } + + /* if pa->flag has changed this means a panel was opened/closed here */ + if (pa->flag != oldflag) { + panel_activate_state(C, pa, PANEL_STATE_ANIMATION); + } + } + } +} + +/** + * Panel drag-collapse (modal handler) + * Clicking and dragging over panels toggles their collapse state based on the panel that was first + * dragged over. If it was open all affected panels incl the initial one are closed and vise versa. + */ +static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata) +{ + wmWindow *win = CTX_wm_window(C); + uiPanelDragCollapseHandle *dragcol_data = userdata; + short retval = WM_UI_HANDLER_CONTINUE; + + switch (event->type) { + case MOUSEMOVE: + ui_panel_drag_collapse(C, dragcol_data, &event->x); + + retval = WM_UI_HANDLER_BREAK; + break; + case LEFTMOUSE: + if (event->val == KM_RELEASE) { + /* done! */ + WM_event_remove_ui_handler( + &win->modalhandlers, + ui_panel_drag_collapse_handler, + ui_panel_drag_collapse_handler_remove, + dragcol_data, true); + ui_panel_drag_collapse_handler_remove(C, dragcol_data); + } + /* don't let any left-mouse event fall through! */ + retval = WM_UI_HANDLER_BREAK; + break; + } + + return retval; +} + +static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was_open) +{ + wmWindow *win = CTX_wm_window(C); + wmEvent *event = win->eventstate; + uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__); + + dragcol_data->was_first_open = was_open; + copy_v2_v2_int(dragcol_data->xy_init, &event->x); + + WM_event_add_ui_handler( + C, &win->modalhandlers, + ui_panel_drag_collapse_handler, + ui_panel_drag_collapse_handler_remove, + dragcol_data, 0); +} /* this function is supposed to call general window drawing too */ /* also it supposes a block has panel, and isn't a menu */ @@ -1187,23 +1355,39 @@ static void ui_handle_panel_header(const bContext *C, uiBlock *block, int mx, in ED_region_tag_redraw(ar); } else { /* collapse */ - if (ctrl) + if (ctrl) { panels_collapse_all(sa, ar, block->panel); + /* reset the view - we don't want to display a view without content */ + UI_view2d_offset(&ar->v2d, 0.0f, 1.0f); + } + if (block->panel->flag & PNL_CLOSED) { block->panel->flag &= ~PNL_CLOSED; /* snap back up so full panel aligns with screen edge */ if (block->panel->snap & PNL_SNAP_BOTTOM) block->panel->ofsy = 0; + + if (event == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, false); + } } else if (align == BUT_HORIZONTAL) { block->panel->flag |= PNL_CLOSEDX; + + if (event == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, true); + } } else { - /* snap down to bottom screen edge*/ + /* snap down to bottom screen edge */ block->panel->flag |= PNL_CLOSEDY; if (block->panel->snap & PNL_SNAP_BOTTOM) block->panel->ofsy = -block->panel->sizey; + + if (event == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, true); + } } for (pa = ar->panels.first; pa; pa = pa->next) { @@ -1332,10 +1516,11 @@ void UI_panel_category_clear_all(ARegion *ar) } /* based on UI_draw_roundbox_gl_mode, check on making a version which allows us to skip some sides */ -static void ui_panel_category_draw_tab(int mode, float minx, float miny, float maxx, float maxy, float rad, - int roundboxtype, - const bool use_highlight, const bool use_shadow, - const unsigned char highlight_fade[3]) +static void ui_panel_category_draw_tab( + int mode, float minx, float miny, float maxx, float maxy, float rad, + int roundboxtype, + const bool use_highlight, const bool use_shadow, + const unsigned char highlight_fade[3]) { float vec[4][2] = { {0.195, 0.02}, @@ -1718,8 +1903,8 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) } for (block = ar->uiblocks.last; block; block = block->prev) { - bool inside = false, inside_header = false, inside_scale = false; - + uiPanelMouseState mouse_state; + mx = event->x; my = event->y; ui_window_to_block(ar, block, &mx, &my); @@ -1731,32 +1916,11 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) continue; if (pa->type && pa->type->flag & PNL_NO_HEADER) /* XXX - accessed freed panels when scripts reload, need to fix. */ continue; - - /* clicked at panel header? */ - if (pa->flag & PNL_CLOSEDX) { - if (block->rect.xmin <= mx && block->rect.xmin + PNL_HEADER >= mx) - inside_header = true; - } - else if (block->rect.xmin > mx || block->rect.xmax < mx) { - /* outside left/right side */ - } - else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { - inside_header = true; - } - else if (!(pa->flag & PNL_CLOSEDY)) { - /* open panel */ - if (pa->control & UI_PNL_SCALE) { - if (block->rect.xmax - PNL_HEADER <= mx) - if (block->rect.ymin + PNL_HEADER >= my) - inside_scale = true; - } - if (block->rect.xmin <= mx && block->rect.xmax >= mx) - if (block->rect.ymin <= my && block->rect.ymax + PNL_HEADER >= my) - inside = true; - } - + + mouse_state = ui_panel_mouse_state_get(block, pa, mx, my); + /* XXX hardcoded key warning */ - if ((inside || inside_header) && event->val == KM_PRESS) { + if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER) && event->val == KM_PRESS) { if (event->type == AKEY && ((event->ctrl + event->oskey + event->shift + event->alt) == 0)) { if (pa->flag & PNL_CLOSEDY) { @@ -1775,13 +1939,13 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) if (ui_but_is_active(ar)) continue; - if (inside || inside_header) { + if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { if (event->val == KM_PRESS) { /* open close on header */ if (ELEM(event->type, RETKEY, PADENTER)) { - if (inside_header) { + if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { ui_handle_panel_header(C, block, mx, my, RETKEY, event->ctrl, event->shift); retval = WM_UI_HANDLER_BREAK; break; @@ -1791,12 +1955,12 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) /* all inside clicks should return in break - overlapping/float panels */ retval = WM_UI_HANDLER_BREAK; - if (inside_header) { - ui_handle_panel_header(C, block, mx, my, 0, event->ctrl, event->shift); + if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { + ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); retval = WM_UI_HANDLER_BREAK; break; } - else if (inside_scale && !(pa->flag & PNL_CLOSED)) { + else if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(pa->flag & PNL_CLOSED)) { panel_activate_state(C, pa, PANEL_STATE_DRAG_SCALE); retval = WM_UI_HANDLER_BREAK; break; @@ -1804,7 +1968,7 @@ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *ar) } else if (event->type == RIGHTMOUSE) { - if (inside_header) { + if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { ui_panel_menu(C, ar, block->panel); retval = WM_UI_HANDLER_BREAK; break; @@ -1943,7 +2107,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, false); + WM_event_add_ui_handler(C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, pa, 0); } 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 6e5f6af3c5f..6aabbbe76b6 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -70,8 +70,9 @@ #include "interface_intern.h" -#define MENU_TOP 8 +#define MENU_TOP (int)(8 * UI_DPI_FAC) #define MENU_PADDING (int)(0.2f * UI_UNIT_Y) +#define MENU_BORDER (int)(0.3f * U.widget_unit) static int rna_property_enum_step(const bContext *C, PointerRNA *ptr, PropertyRNA *prop, int direction) { @@ -179,9 +180,10 @@ typedef struct uiTooltipData { 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) +static void rgb_tint( + float col[3], + float h, float h_strength, + float v, float v_strength) { float col_hsv_from[3]; float col_hsv_to[3]; @@ -241,10 +243,10 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) /* tone_fg = rgb_to_grayscale(main_color); */ /* mix the colors */ - rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* light grey */ + rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* light gray */ 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(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* gray */ + rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* dark gray */ rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* red */ /* draw text */ @@ -261,7 +263,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) /* override text-style */ fstyle_header.shadow = 1; - fstyle_header.shadowcolor = rgb_to_luma(tip_colors[UI_TIP_LC_MAIN]); + fstyle_header.shadowcolor = rgb_to_grayscale(tip_colors[UI_TIP_LC_MAIN]); fstyle_header.shadx = fstyle_header.shady = 0; fstyle_header.shadowalpha = 1.0f; @@ -394,7 +396,7 @@ ARegion *ui_tooltip_create(bContext *C, ARegion *butregion, uiBut *but) data->totline++; } - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) { + if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { /* better not show the value of a password */ if ((but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) == 0) { /* full string */ @@ -815,7 +817,7 @@ static void ui_searchbox_select(bContext *C, ARegion *ar, uiBut *but, int step) } else { /* only let users step into an 'unset' state for unlink buttons */ - data->active = (but->type == UI_BTYPE_SEARCH_MENU_UNLINK) ? -1 : 0; + data->active = (but->flag & UI_BUT_SEARCH_UNLINK) ? -1 : 0; } } @@ -826,8 +828,8 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr { /* thumbnail preview */ if (data->preview) { - int butw = BLI_rcti_size_x(&data->bbox) / data->prv_cols; - int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_TOP) / data->prv_rows; + int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols; + int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows; int row, col; *r_rect = data->bbox; @@ -835,10 +837,10 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr col = itemnr % data->prv_cols; row = itemnr / data->prv_cols; - r_rect->xmin += col * butw; + r_rect->xmin += MENU_BORDER + (col * butw); r_rect->xmax = r_rect->xmin + butw; - r_rect->ymax = data->bbox.ymax - MENU_TOP - (row * buth); + r_rect->ymax -= MENU_BORDER + (row * buth); r_rect->ymin = r_rect->ymax - buth; } /* list view */ @@ -886,7 +888,7 @@ bool ui_searchbox_apply(uiBut *but, ARegion *ar) return true; } - else if (but->type == UI_BTYPE_SEARCH_MENU_UNLINK) { + else if (but->flag & UI_BUT_SEARCH_UNLINK) { /* It is valid for _UNLINK flavor to have no active element (it's a valid way to unlink). */ but->editstr[0] = '\0'; @@ -1044,14 +1046,8 @@ static void ui_searchbox_region_draw_cb(const bContext *UNUSED(C), ARegion *ar) ui_searchbox_butrect(&rect, data, a); /* widget itself */ - if (data->preview) { - ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], - (a == data->active) ? UI_ACTIVE : 0); - } - else { - ui_draw_menu_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], - (a == data->active) ? UI_ACTIVE : 0, data->use_sep); - } + ui_draw_preview_item(&data->fstyle, &rect, data->items.names[a], data->items.icons[a], + (a == data->active) ? UI_ACTIVE : 0); } /* indicate more */ @@ -1164,25 +1160,27 @@ ARegion *ui_searchbox_create(bContext *C, ARegion *butregion, uiBut *but) /* compute position */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { - const int margin = UI_POPUP_MARGIN; + const int margin_x = UI_POPUP_MARGIN; + const int margin_y = MENU_TOP; + const int search_but_h = BLI_rctf_size_y(&but->rect) + 10; /* this case is search menu inside other menu */ /* we copy region size */ ar->winrct = butregion->winrct; /* widget rect, in region coords */ - data->bbox.xmin = margin; - data->bbox.xmax = BLI_rcti_size_x(&ar->winrct) - margin; + data->bbox.xmin = margin_x; + data->bbox.xmax = BLI_rcti_size_x(&ar->winrct) - margin_x; /* 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; + data->bbox.ymin = margin_y; + data->bbox.ymax = BLI_rcti_size_y(&ar->winrct) - margin_y; /* check if button is lower half */ if (but->rect.ymax < BLI_rctf_cent_y(&but->block->rect)) { - data->bbox.ymin += BLI_rctf_size_y(&but->rect); + data->bbox.ymin += search_but_h; } else { - data->bbox.ymax -= BLI_rctf_size_y(&but->rect); + data->bbox.ymax -= search_but_h; } } else { @@ -1426,7 +1424,8 @@ static void ui_block_position(wmWindow *window, ARegion *butregion, uiBut *but, if (down || top) { if (dir1 == UI_DIR_UP && top == 0) dir1 = UI_DIR_DOWN; if (dir1 == UI_DIR_DOWN && down == 0) dir1 = UI_DIR_UP; - if (dir2 == UI_DIR_UP && top == 0) dir2 = UI_DIR_DOWN; + BLI_assert(dir2 != UI_DIR_UP); +// if (dir2 == UI_DIR_UP && top == 0) dir2 = UI_DIR_DOWN; if (dir2 == UI_DIR_DOWN && down == 0) dir2 = UI_DIR_UP; } @@ -1638,7 +1637,7 @@ static void ui_popup_block_remove(bContext *C, uiPopupBlockHandle *handle) } /** - * Called for creatign new popups and refreshing existing ones. + * Called for creating new popups and refreshing existing ones. */ uiBlock *ui_popup_block_refresh( bContext *C, uiPopupBlockHandle *handle, @@ -1684,13 +1683,8 @@ uiBlock *ui_popup_block_refresh( ar->regiondata = handle; /* set UI_BLOCK_NUMSELECT before UI_block_end() so we get alphanumeric keys assigned */ - if (but) { - if (but->type == UI_BTYPE_PULLDOWN) { - block->flag |= UI_BLOCK_NUMSELECT; - } - } - else { - block->flag |= UI_BLOCK_POPUP | UI_BLOCK_NUMSELECT; + if (but == NULL) { + block->flag |= UI_BLOCK_POPUP; } block->flag |= UI_BLOCK_LOOP; @@ -1808,9 +1802,10 @@ uiBlock *ui_popup_block_refresh( return block; } -uiPopupBlockHandle *ui_popup_block_create(bContext *C, ARegion *butregion, uiBut *but, - uiBlockCreateFunc create_func, uiBlockHandleCreateFunc handle_create_func, - void *arg) +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; @@ -2571,8 +2566,9 @@ static uiBlock *ui_block_func_POPUP(bContext *C, uiPopupBlockHandle *handle, voi return pup->block; } -uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut *but, - uiMenuCreateFunc menu_func, void *arg) +uiPopupBlockHandle *ui_popup_menu_create( + bContext *C, ARegion *butregion, uiBut *but, + uiMenuCreateFunc menu_func, void *arg) { wmWindow *window = CTX_wm_window(C); uiStyle *style = UI_style_get_dpi(); @@ -2619,7 +2615,7 @@ uiPopupBlockHandle *ui_popup_menu_create(bContext *C, ARegion *butregion, uiBut if (!but) { handle->popup = true; - UI_popup_handlers_add(C, &window->modalhandlers, handle, false); + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); WM_event_add_mousemove(C); } @@ -2681,7 +2677,7 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) menu = ui_popup_block_create(C, NULL, NULL, NULL, ui_block_func_POPUP, pup); menu->popup = true; - UI_popup_handlers_add(C, &window->modalhandlers, menu, false); + UI_popup_handlers_add(C, &window->modalhandlers, menu, 0); WM_event_add_mousemove(C); MEM_freeN(pup); @@ -2807,7 +2803,9 @@ void UI_pie_menu_end(bContext *C, uiPieMenu *pie) menu->popup = true; menu->towardstime = PIL_check_seconds_timer(); - UI_popup_handlers_add(C, &window->modalhandlers, menu, true); + UI_popup_handlers_add( + C, &window->modalhandlers, + menu, WM_HANDLER_ACCEPT_DBL_CLICK); WM_event_add_mousemove(C); MEM_freeN(pie); @@ -2850,8 +2848,9 @@ int UI_pie_menu_invoke(struct bContext *C, const char *idname, const wmEvent *ev return OPERATOR_INTERFACE; } -int UI_pie_menu_invoke_from_operator_enum(struct bContext *C, const char *title, const char *opname, - const char *propname, const wmEvent *event) +int UI_pie_menu_invoke_from_operator_enum( + struct bContext *C, const char *title, const char *opname, + const char *propname, const wmEvent *event) { uiPieMenu *pie; uiLayout *layout; @@ -2867,8 +2866,9 @@ int UI_pie_menu_invoke_from_operator_enum(struct bContext *C, const char *title, return OPERATOR_INTERFACE; } -int UI_pie_menu_invoke_from_rna_enum(struct bContext *C, const char *title, const char *path, - const wmEvent *event) +int UI_pie_menu_invoke_from_rna_enum( + struct bContext *C, const char *title, const char *path, + const wmEvent *event) { PointerRNA ctx_ptr; PointerRNA r_ptr; @@ -2997,7 +2997,7 @@ void UI_popup_block_invoke_ex(bContext *C, uiBlockCreateFunc func, void *arg, co handle->optype = (opname) ? WM_operatortype_find(opname, 0) : NULL; handle->opcontext = opcontext; - UI_popup_handlers_add(C, &window->modalhandlers, handle, false); + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); WM_event_add_mousemove(C); } @@ -3020,7 +3020,7 @@ void UI_popup_block_ex(bContext *C, uiBlockCreateFunc func, uiBlockHandleFunc po handle->cancel_func = cancel_func; // handle->opcontext = opcontext; - UI_popup_handlers_add(C, &window->modalhandlers, handle, false); + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); WM_event_add_mousemove(C); } @@ -3039,17 +3039,15 @@ void uiPupBlockOperator(bContext *C, uiBlockCreateFunc func, wmOperator *op, int handle->cancel_func = confirm_cancel_operator; handle->opcontext = opcontext; - UI_popup_handlers_add(C, &window->modalhandlers, handle); + UI_popup_handlers_add(C, &window->modalhandlers, handle, 0); WM_event_add_mousemove(C); } #endif -void UI_popup_block_close(bContext *C, uiBlock *block) +void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block) { + /* if loading new .blend while popup is open, window will be NULL */ if (block->handle) { - wmWindow *win = CTX_wm_window(C); - - /* if loading new .blend while popup is open, window will be NULL */ if (win) { UI_popup_handlers_remove(&win->modalhandlers, block->handle); ui_popup_block_free(C, block->handle); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 2f46c0906ae..10575fcbc54 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -64,7 +64,7 @@ /* style + theme + layout-engine = UI */ -/* +/** * This is a complete set of layout rules, the 'state' of the Layout * Engine. Multiple styles are possible, defined via C or Python. Styles * get a name, and will typically get activated per region type, like @@ -323,10 +323,14 @@ void UI_fontstyle_draw_simple_backdrop( /* XXX: read a style configure */ uiStyle *UI_style_get(void) { +#if 0 uiStyle *style = NULL; /* offset is two struct uiStyle pointers */ - /* style = BLI_findstring(&U.uistyles, "Unifont Style", sizeof(style) * 2) */; + style = BLI_findstring(&U.uistyles, "Unifont Style", sizeof(style) * 2); return (style != NULL) ? style : U.uistyles.first; +#else + return U.uistyles.first; +#endif } /* for drawing, scaled with DPI setting */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index b3c31a1a644..c3b58e2d1c1 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -187,10 +187,10 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) /* preview thumbnails */ if (template.prv_rows > 0 && template.prv_cols > 0) { int w = 4 * U.widget_unit * template.prv_cols; - int h = 4 * U.widget_unit * template.prv_rows + U.widget_unit; + int h = 5 * U.widget_unit * template.prv_rows; /* fake button, it holds space for search items */ - uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 15, w, h, NULL, 0, 0, 0, 0, NULL); + uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL); but = uiDefSearchBut(block, search, 0, ICON_VIEWZOOM, sizeof(search), 10, 0, w, UI_UNIT_Y, template.prv_rows, template.prv_cols, ""); @@ -273,8 +273,11 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) RNA_property_pointer_set(&template->ptr, template->prop, idptr); RNA_property_update(C, &template->ptr, template->prop); - if (id && CTX_wm_window(C)->eventstate->shift) /* useful hidden functionality, */ + if (id && CTX_wm_window(C)->eventstate->shift) { + /* only way to force-remove data (on save) */ + id->flag &= ~LIB_FAKEUSER; id->us = 0; + } break; case UI_ID_FAKE_USER: @@ -359,49 +362,23 @@ static const char *template_id_browse_tip(StructRNA *type) return N_("Browse ID data to be linked"); } -/* Return a type-based i18n context, needed e.g. by "New" button. +/** + * \return a type-based i18n context, needed e.g. by "New" button. * In most languages, this adjective takes different form based on gender of type name... */ #ifdef WITH_INTERNATIONAL static const char *template_id_context(StructRNA *type) { if (type) { - switch (RNA_type_to_ID_code(type)) { - case ID_SCE: return BLF_I18NCONTEXT_ID_SCENE; - case ID_OB: return BLF_I18NCONTEXT_ID_OBJECT; - case ID_ME: return BLF_I18NCONTEXT_ID_MESH; - case ID_CU: return BLF_I18NCONTEXT_ID_CURVE; - case ID_MB: return BLF_I18NCONTEXT_ID_METABALL; - case ID_MA: return BLF_I18NCONTEXT_ID_MATERIAL; - case ID_TE: return BLF_I18NCONTEXT_ID_TEXTURE; - case ID_IM: return BLF_I18NCONTEXT_ID_IMAGE; - case ID_LS: return BLF_I18NCONTEXT_ID_FREESTYLELINESTYLE; - case ID_LT: return BLF_I18NCONTEXT_ID_LATTICE; - case ID_LA: return BLF_I18NCONTEXT_ID_LAMP; - case ID_CA: return BLF_I18NCONTEXT_ID_CAMERA; - case ID_WO: return BLF_I18NCONTEXT_ID_WORLD; - case ID_SCR: return BLF_I18NCONTEXT_ID_SCREEN; - case ID_TXT: return BLF_I18NCONTEXT_ID_TEXT; - case ID_SPK: return BLF_I18NCONTEXT_ID_SPEAKER; - case ID_SO: return BLF_I18NCONTEXT_ID_SOUND; - case ID_AR: return BLF_I18NCONTEXT_ID_ARMATURE; - case ID_AC: return BLF_I18NCONTEXT_ID_ACTION; - case ID_NT: return BLF_I18NCONTEXT_ID_NODETREE; - case ID_BR: return BLF_I18NCONTEXT_ID_BRUSH; - case ID_PA: return BLF_I18NCONTEXT_ID_PARTICLESETTINGS; - case ID_GD: return BLF_I18NCONTEXT_ID_GPENCIL; - case ID_MC: return BLF_I18NCONTEXT_ID_MOVIECLIP; - case ID_MSK: return BLF_I18NCONTEXT_ID_MASK; - case ID_PAL: return BLF_I18NCONTEXT_ID_PALETTE; - case ID_PC: return BLF_I18NCONTEXT_ID_PAINTCURVE; - } + return BKE_idcode_to_translation_context(RNA_type_to_ID_code(type)); } return BLF_I18NCONTEXT_DEFAULT; } #endif -static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, short idcode, int flag, - const char *newop, const char *openop, const char *unlinkop) +static void template_ID( + bContext *C, uiLayout *layout, TemplateID *template, StructRNA *type, short idcode, int flag, + const char *newop, const char *openop, const char *unlinkop) { uiBut *but; uiBlock *block; @@ -426,8 +403,8 @@ 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))); - but->icon = id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type); - UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + ui_def_but_icon(but, id ? ui_id_icon_get(C, id, true) : RNA_struct_ui_icon(type), + UI_HAS_ICON | UI_BUT_ICON_PREVIEW); if ((idfrom && idfrom->lib) || !editable) UI_but_flag_enable(but, UI_BUT_DISABLED); @@ -437,10 +414,9 @@ 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))); - but->icon = RNA_struct_ui_icon(type); + ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); /* default dragging of icon for id browse buttons */ UI_but_drag_set_id(but, id); - UI_but_flag_enable(but, UI_HAS_ICON); UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT); if ((idfrom && idfrom->lib) || !editable) @@ -617,8 +593,9 @@ static void template_ID(bContext *C, uiLayout *layout, TemplateID *template, Str UI_block_align_end(block); } -static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop, int flag, int prv_rows, int prv_cols) +static void ui_template_id( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, + const char *openop, const char *unlinkop, int flag, int prv_rows, int prv_cols) { TemplateID *template; PropertyRNA *prop; @@ -658,21 +635,24 @@ static void ui_template_id(uiLayout *layout, bContext *C, PointerRNA *ptr, const MEM_freeN(template); } -void uiTemplateID(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop) +void uiTemplateID( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, + const char *openop, const char *unlinkop) { ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, 0, 0); } -void uiTemplateIDBrowse(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop) +void uiTemplateIDBrowse( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, + const char *openop, const char *unlinkop) { ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME, 0, 0); } -void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop, int rows, int cols) +void uiTemplateIDPreview( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, + const char *openop, const char *unlinkop, int rows, int cols) { ui_template_id(layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, rows, cols); @@ -680,13 +660,15 @@ void uiTemplateIDPreview(uiLayout *layout, bContext *C, PointerRNA *ptr, const c /************************ ID Chooser Template ***************************/ -/* This is for selecting the type of ID-block to use, and then from the relevant type choosing the block to use +/** + * This is for selecting the type of ID-block to use, and then from the relevant type choosing the block to use * * - propname: property identifier for property that ID-pointer gets stored to * - proptypename: property identifier for property used to determine the type of ID-pointer that can be used */ -void uiTemplateAnyID(uiLayout *layout, PointerRNA *ptr, const char *propname, const char *proptypename, - const char *text) +void uiTemplateAnyID( + uiLayout *layout, PointerRNA *ptr, const char *propname, const char *proptypename, + const char *text) { PropertyRNA *propID, *propType; uiLayout *split, *row, *sub; @@ -739,7 +721,8 @@ void uiTemplateAnyID(uiLayout *layout, PointerRNA *ptr, const char *propname, co /* ---------- */ -/* This is creating/editing RNA-Paths +/** + * This is creating/editing RNA-Paths * * - ptr: struct which holds the path property * - propname: property identifier for property that path gets stored to @@ -820,10 +803,11 @@ static int modifier_is_simulation(ModifierData *md) } } -static uiLayout *draw_modifier(uiLayout *layout, Scene *scene, Object *ob, - ModifierData *md, int index, int cageIndex, int lastCageIndex) +static uiLayout *draw_modifier( + uiLayout *layout, Scene *scene, Object *ob, + ModifierData *md, int index, int cageIndex, int lastCageIndex) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); PointerRNA ptr; uiBut *but; uiBlock *block; @@ -1065,7 +1049,8 @@ static void do_constraint_panels(bContext *C, void *ob_pt, int event) case B_CONSTRAINT_CHANGETARGET: { Main *bmain = CTX_data_main(C); - if (ob->pose) ob->pose->flag |= POSE_RECALC; /* checks & sorts pose channels */ + if (ob->pose) + BKE_pose_tag_recalc(bmain, ob->pose); /* checks & sorts pose channels */ DAG_relations_tag_update(bmain); break; } @@ -1095,7 +1080,7 @@ static void constraint_active_func(bContext *UNUSED(C), void *ob_v, void *con_v) static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) { bPoseChannel *pchan = BKE_pose_channel_active(ob); - bConstraintTypeInfo *cti; + const bConstraintTypeInfo *cti; uiBlock *block; uiLayout *result = NULL, *col, *box, *row; PointerRNA ptr; @@ -1283,8 +1268,9 @@ static void do_preview_buttons(bContext *C, void *arg, int event) } } -void uiTemplatePreview(uiLayout *layout, bContext *C, ID *id, int show_buttons, ID *parent, MTex *slot, - const char *preview_id) +void uiTemplatePreview( + uiLayout *layout, bContext *C, ID *id, int show_buttons, ID *parent, MTex *slot, + const char *preview_id) { uiLayout *row, *col; uiBlock *block; @@ -1492,8 +1478,9 @@ static void colorband_update_cb(bContext *UNUSED(C), void *bt_v, void *coba_v) bt->rnapoin.data = coba->data + coba->cur; } -static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand *coba, const rctf *butr, - RNAUpdateCb *cb, int expand) +static void colorband_buttons_layout( + uiLayout *layout, uiBlock *block, ColorBand *coba, const rctf *butr, + RNAUpdateCb *cb, int expand) { uiLayout *row, *split, *subsplit; uiBut *bt; @@ -1558,6 +1545,7 @@ static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand row = uiLayoutRow(split, false); uiItemR(row, &ptr, "position", 0, IFACE_("Pos"), ICON_NONE); bt = block->buttons.last; + bt->a1 = 1.0f; /* gives a bit more precision for modifying position */ UI_but_func_set(bt, colorband_update_cb, bt, coba); row = uiLayoutRow(layout, false); @@ -1575,6 +1563,7 @@ static void colorband_buttons_layout(uiLayout *layout, uiBlock *block, ColorBand row = uiLayoutRow(subsplit, false); uiItemR(row, &ptr, "position", UI_ITEM_R_SLIDER, IFACE_("Pos"), ICON_NONE); bt = block->buttons.last; + bt->a1 = 1.0f; /* gives a bit more precision for modifying position */ UI_but_func_set(bt, colorband_update_cb, bt, coba); row = uiLayoutRow(split, false); @@ -1622,44 +1611,57 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname /********************* Icon viewer Template ************************/ +typedef struct IconViewMenuArgs { + PointerRNA ptr; + PropertyRNA *prop; + int show_labels; +} IconViewMenuArgs; /* ID Search browse menu, open */ static uiBlock *ui_icon_view_menu_cb(bContext *C, ARegion *ar, void *arg_litem) { - static RNAUpdateCb cb; + static IconViewMenuArgs args; uiBlock *block; uiBut *but; - int icon; + int icon, value; EnumPropertyItem *item; int a; bool free; /* arg_litem is malloced, can be freed by parent button */ - cb = *((RNAUpdateCb *)arg_litem); - - /* unused */ - // icon = RNA_property_enum_get(&cb.ptr, cb.prop); - - block = UI_block_begin(C, ar, "_popup", UI_EMBOSS); + args = *((IconViewMenuArgs *) arg_litem); + + block = UI_block_begin(C, ar, "_popup", UI_EMBOSS_PULLDOWN); UI_block_flag_enable(block, UI_BLOCK_LOOP); - - - RNA_property_enum_items(C, &cb.ptr, cb.prop, &item, NULL, &free); - + + RNA_property_enum_items(C, &args.ptr, args.prop, &item, NULL, &free); + for (a = 0; item[a].identifier; a++) { int x, y; - - /* XXX hardcoded size to 5 x unit */ - x = (a % 8) * UI_UNIT_X * 5; - y = (a / 8) * UI_UNIT_X * 5; - + /* XXX hardcoded size to 5 units */ + const int w = UI_UNIT_X * 5; + const int h = args.show_labels ? 6 * UI_UNIT_Y : UI_UNIT_Y * 5; + + x = (a % 8) * w; + y = (a / 8) * h; + icon = item[a].icon; - but = uiDefIconButR_prop(block, UI_BTYPE_ROW, 0, icon, x, y, UI_UNIT_X * 5, UI_UNIT_Y * 5, &cb.ptr, cb.prop, -1, 0, icon, -1, -1, NULL); - UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + value = item[a].value; + if (args.show_labels) { + but = uiDefIconTextButR_prop( + block, UI_BTYPE_ROW, 0, icon, item[a].name, x, y, w, h, + &args.ptr, args.prop, -1, 0, value, -1, -1, NULL); + } + else { + but = uiDefIconButR_prop( + block, UI_BTYPE_ROW, 0, icon, x, y, w, h, + &args.ptr, args.prop, -1, 0, value, -1, -1, NULL); + } + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } UI_block_bounds_set_normal(block, 0.3f * U.widget_unit); - UI_block_direction_set(block, UI_DIR_UP); + UI_block_direction_set(block, UI_DIR_DOWN); if (free) { MEM_freeN(item); @@ -1668,40 +1670,39 @@ static uiBlock *ui_icon_view_menu_cb(bContext *C, ARegion *ar, void *arg_litem) return block; } -void uiTemplateIconView(uiLayout *layout, PointerRNA *ptr, const char *propname) +void uiTemplateIconView(uiLayout *layout, PointerRNA *ptr, const char *propname, int show_labels) { PropertyRNA *prop = RNA_struct_find_property(ptr, propname); - RNAUpdateCb *cb; + IconViewMenuArgs *cb_args; + EnumPropertyItem *items; uiBlock *block; uiBut *but; -// rctf rect; /* UNUSED */ - int icon; - - if (!prop || RNA_property_type(prop) != PROP_ENUM) + int value, icon = ICON_NONE, tot_items; + bool free_items; + + if (!prop || RNA_property_type(prop) != PROP_ENUM) { + RNA_warning("property of type Enum not found: %s.%s", RNA_struct_identifier(ptr->type), propname); return; - - icon = RNA_property_enum_get(ptr, prop); - - cb = MEM_callocN(sizeof(RNAUpdateCb), "RNAUpdateCb"); - cb->ptr = *ptr; - cb->prop = prop; - -// rect.xmin = 0; rect.xmax = 10.0f * UI_UNIT_X; -// rect.ymin = 0; rect.ymax = 10.0f * UI_UNIT_X; - + } + block = uiLayoutAbsoluteBlock(layout); - but = uiDefBlockButN(block, ui_icon_view_menu_cb, MEM_dupallocN(cb), "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, ""); + RNA_property_enum_items(block->evil_C, ptr, prop, &items, &tot_items, &free_items); + value = RNA_property_enum_get(ptr, prop); + RNA_enum_icon_from_value(items, value, &icon); - -// but = uiDefIconButR_prop(block, UI_BTYPE_ROW, 0, icon, 0, 0, BLI_rctf_size_x(&rect), BLI_rctf_size_y(&rect), ptr, prop, -1, 0, icon, -1, -1, NULL); - - but->icon = icon; - UI_but_flag_enable(but, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); - - UI_but_funcN_set(but, rna_update_cb, MEM_dupallocN(cb), NULL); - - MEM_freeN(cb); + cb_args = MEM_callocN(sizeof(IconViewMenuArgs), __func__); + cb_args->ptr = *ptr; + cb_args->prop = prop; + cb_args->show_labels = show_labels; + + but = uiDefBlockButN(block, ui_icon_view_menu_cb, cb_args, "", 0, 0, UI_UNIT_X * 6, UI_UNIT_Y * 6, ""); + + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + + if (free_items) { + MEM_freeN(items); + } } /********************* Histogram Template ************************/ @@ -2071,8 +2072,9 @@ 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, int neg_slope, RNAUpdateCb *cb) +static void curvemap_buttons_layout( + uiLayout *layout, PointerRNA *ptr, char labeltype, int levels, + int brush, int neg_slope, RNAUpdateCb *cb) { CurveMapping *cumap = ptr->data; CurveMap *cm = &cumap->cm[cumap->cur]; @@ -2233,8 +2235,9 @@ static void curvemap_buttons_layout(uiLayout *layout, PointerRNA *ptr, char labe UI_block_funcN_set(block, NULL, NULL, NULL); } -void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, const char *propname, int type, - int levels, int brush, int neg_slope) +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); @@ -2277,8 +2280,9 @@ void uiTemplateCurveMapping(uiLayout *layout, PointerRNA *ptr, const char *propn #define WHEEL_SIZE (5 * U.widget_unit) /* This template now follows User Preference for type - name is not correct anymore... */ -void uiTemplateColorPicker(uiLayout *layout, PointerRNA *ptr, const char *propname, int value_slider, - int lock, int lock_luminosity, int cubic) +void uiTemplateColorPicker( + uiLayout *layout, PointerRNA *ptr, const char *propname, int value_slider, + int lock, int lock_luminosity, int cubic) { PropertyRNA *prop = RNA_struct_find_property(ptr, propname); uiBlock *block = uiLayoutGetBlock(layout); @@ -2398,9 +2402,6 @@ void uiTemplatePalette(uiLayout *layout, PointerRNA *ptr, const char *propname, palette = cptr.data; - /* first delete any pending colors */ - BKE_palette_cleanup(palette); - color = palette->colors.first; col = uiLayoutColumn(layout, true); @@ -2452,12 +2453,13 @@ static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) /* see view3d_header.c */ } -/* TODO: - * - for now, grouping of layers is determined by dividing up the length of - * the array of layer bitflags */ - -void uiTemplateLayers(uiLayout *layout, PointerRNA *ptr, const char *propname, - PointerRNA *used_ptr, const char *used_propname, int active_layer) +/** + * \todo for now, grouping of layers is determined by dividing up the length of + * the array of layer bitflags + */ +void uiTemplateLayers( + uiLayout *layout, PointerRNA *ptr, const char *propname, + PointerRNA *used_ptr, const char *used_propname, int active_layer) { uiLayout *uRow, *uCol; PropertyRNA *prop, *used_prop = NULL; @@ -2523,8 +2525,9 @@ void uiTemplateLayers(uiLayout *layout, PointerRNA *ptr, const char *propname, } } -void uiTemplateGameStates(uiLayout *layout, PointerRNA *ptr, const char *propname, - PointerRNA *used_ptr, const char *used_propname, int active_state) +void uiTemplateGameStates( + uiLayout *layout, PointerRNA *ptr, const char *propname, + PointerRNA *used_ptr, const char *used_propname, int active_state) { uiLayout *uRow, *uCol; PropertyRNA *prop, *used_prop = NULL; @@ -2594,10 +2597,11 @@ void uiTemplateGameStates(uiLayout *layout, PointerRNA *ptr, const char *propnam /************************* List Template **************************/ -static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout, - struct PointerRNA *UNUSED(dataptr), struct PointerRNA *itemptr, int icon, - struct PointerRNA *UNUSED(active_dataptr), const char *UNUSED(active_propname), - int UNUSED(index), int UNUSED(flt_flag)) +static void uilist_draw_item_default( + struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout, + struct PointerRNA *UNUSED(dataptr), struct PointerRNA *itemptr, int icon, + struct PointerRNA *UNUSED(active_dataptr), const char *UNUSED(active_propname), + int UNUSED(index), int UNUSED(flt_flag)) { PropertyRNA *nameprop = RNA_struct_name_property(itemptr->type); @@ -2764,8 +2768,9 @@ typedef struct { int end_idx; /* Index of last item to display + 1. */ } uiListLayoutdata; -static void uilist_prepare(uiList *ui_list, int len, int activei, int rows, int maxrows, int columns, - uiListLayoutdata *layoutdata) +static void uilist_prepare( + uiList *ui_list, int len, int activei, int rows, int maxrows, int columns, + uiListLayoutdata *layoutdata) { uiListDyn *dyn_data = ui_list->dyn_data; int activei_row, max_scroll; @@ -2851,9 +2856,10 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha return BLI_sprintfN("%s - %s", tip, dyn_tooltip); } -void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, const char *list_id, - PointerRNA *dataptr, const char *propname, PointerRNA *active_dataptr, const char *active_propname, - const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns) +void uiTemplateList( + uiLayout *layout, bContext *C, const char *listtype_name, const char *list_id, + PointerRNA *dataptr, const char *propname, PointerRNA *active_dataptr, const char *active_propname, + const char *item_dyntip_propname, int rows, int maxrows, int layout_type, int columns) { uiListType *ui_list_type; uiList *ui_list = NULL; @@ -3199,7 +3205,7 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co /* add scrollbar */ if (len > layoutdata.visual_items) { - col = uiLayoutColumn(row, false); + /* col = */ uiLayoutColumn(row, false); uiDefButI(block, UI_BTYPE_SCROLL, 0, "", 0, 0, UI_UNIT_X * 0.75, UI_UNIT_Y * dyn_data->visual_height, &ui_list->list_scroll, 0, dyn_data->height - dyn_data->visual_height, dyn_data->visual_height, 0, ""); @@ -3277,17 +3283,18 @@ static void operator_search_cb(const bContext *C, void *UNUSED(arg), const char for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { wmOperatorType *ot = BLI_ghashIterator_getValue(&iter); + const char *ot_ui_name = CTX_IFACE_(ot->translation_context, ot->name); if ((ot->flag & OPTYPE_INTERNAL) && (G.debug & G_DEBUG_WM) == 0) continue; - if (BLI_strcasestr(ot->name, str)) { + if (BLI_strcasestr(ot_ui_name, str)) { if (WM_operator_poll((bContext *)C, ot)) { char name[256]; - int len = strlen(ot->name); + int len = strlen(ot_ui_name); /* display name for menu, can hold hotkey */ - BLI_strncpy(name, ot->name, sizeof(name)); + BLI_strncpy(name, ot_ui_name, sizeof(name)); /* check for hotkey */ if (len < sizeof(name) - 6) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 28bd637ae59..0823238fcb1 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -52,6 +52,9 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "WM_api.h" +#include "WM_types.h" + #include "interface_intern.h" @@ -154,9 +157,10 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind * \a check_prop callback filters functions to avoid drawing certain properties, * in cases where PROP_HIDDEN flag can't be used for a property. */ -int uiDefAutoButsRNA(uiLayout *layout, PointerRNA *ptr, - bool (*check_prop)(PointerRNA *, PropertyRNA *), - const char label_align) +int uiDefAutoButsRNA( + uiLayout *layout, PointerRNA *ptr, + bool (*check_prop)(PointerRNA *, PropertyRNA *), + const char label_align) { uiLayout *split, *col; int flag; @@ -309,6 +313,34 @@ int UI_calc_float_precision(int prec, double value) return prec; } +bool UI_but_online_manual_id(const uiBut *but, char *r_str, size_t maxlength) +{ + if (but->rnapoin.id.data && but->rnapoin.data && but->rnaprop) { + BLI_snprintf(r_str, maxlength, "%s.%s", RNA_struct_identifier(but->rnapoin.type), + RNA_property_identifier(but->rnaprop)); + return true; + } + else if (but->optype) { + WM_operator_py_idname(r_str, but->optype->idname); + return true; + } + + *r_str = '\0'; + return false; +} + +bool UI_but_online_manual_id_from_active(const struct bContext *C, char *r_str, size_t maxlength) +{ + uiBut *but = UI_context_active_but_get(C); + + if (but) { + return UI_but_online_manual_id(but, r_str, maxlength); + } + + *r_str = '\0'; + return false; +} + /* -------------------------------------------------------------------- */ /* Modal Button Store API */ diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 91186a14b49..253f4616843 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -27,7 +27,6 @@ * \ingroup edinterface */ - #include <limits.h> #include <stdlib.h> #include <string.h> @@ -56,7 +55,6 @@ #include "UI_interface.h" #include "UI_interface_icons.h" - #include "interface_intern.h" #ifdef WITH_INPUT_IME @@ -67,12 +65,12 @@ #define ICON_SIZE_FROM_BUTRECT(rect) (0.8f * BLI_rcti_size_y(rect)) /* ************** widget base functions ************** */ -/* +/** * - in: roundbox codes for corner types and radius - * - return: array of [size][2][x,y] points, the edges of the roundbox, + UV coords + * - return: array of `[size][2][x, y]` points, the edges of the roundbox, + UV coords * * - draw black box with alpha 0 on exact button boundbox - * - for ever AA step: + * - for every AA step: * - draw the inner part for a round filled box, with color blend codes or texture coords * - draw outline in outline color * - draw outer part, bottom half, extruded 1 pixel to bottom, for emboss shadow @@ -102,14 +100,14 @@ typedef struct uiWidgetBase { float inner_v[WIDGET_SIZE_MAX][2]; float inner_uv[WIDGET_SIZE_MAX][2]; - bool inner, outline, emboss, shadedir; + bool draw_inner, draw_outline, draw_emboss, draw_shadedir; uiWidgetTrias tria1; uiWidgetTrias tria2; } uiWidgetBase; -/* uiWidgetType: for time being only for visual appearance, +/** uiWidgetType: for time being only for visual appearance, * later, a handling callback can be added too */ typedef struct uiWidgetType { @@ -239,10 +237,10 @@ static void widget_init(uiWidgetBase *wtb) wtb->tria1.tot = 0; wtb->tria2.tot = 0; - wtb->inner = 1; - wtb->outline = 1; - wtb->emboss = 1; - wtb->shadedir = 1; + wtb->draw_inner = true; + wtb->draw_outline = true; + wtb->draw_emboss = true; + wtb->draw_shadedir = true; } /* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */ @@ -549,12 +547,12 @@ static void widget_menu_trias(uiWidgetTrias *tria, const rcti *rect) { float centx, centy, size; int a; - + /* center position and size */ centx = rect->xmax - 0.32f * BLI_rcti_size_y(rect); centy = rect->ymin + 0.50f * BLI_rcti_size_y(rect); - size = 0.4f * (float)BLI_rcti_size_y(rect); - + size = 0.4f * BLI_rcti_size_y(rect); + for (a = 0; a < 6; a++) { tria->vec[a][0] = size * menu_tria_vert[a][0] + centx; tria->vec[a][1] = size * menu_tria_vert[a][1] + centy; @@ -587,7 +585,6 @@ static void widget_check_trias(uiWidgetTrias *tria, const rcti *rect) /* prepares shade colors */ static void shadecolors4(char coltop[4], char coldown[4], const char *color, short shadetop, short shadedown) { - coltop[0] = CLAMPIS(color[0] + shadetop, 0, 255); coltop[1] = CLAMPIS(color[1] + shadetop, 0, 255); coltop[2] = CLAMPIS(color[2] + shadetop, 0, 255); @@ -604,10 +601,10 @@ static void round_box_shade_col4_r(unsigned char r_col[4], const char col1[4], c const int faci = FTOCHAR(fac); const int facm = 255 - faci; - r_col[0] = (faci * col1[0] + facm * col2[0]) >> 8; - r_col[1] = (faci * col1[1] + facm * col2[1]) >> 8; - r_col[2] = (faci * col1[2] + facm * col2[2]) >> 8; - r_col[3] = (faci * col1[3] + facm * col2[3]) >> 8; + r_col[0] = (faci * col1[0] + facm * col2[0]) / 256; + r_col[1] = (faci * col1[1] + facm * col2[1]) / 256; + r_col[2] = (faci * col1[2] + facm * col2[2]) / 256; + r_col[3] = (faci * col1[3] + facm * col2[3]) / 256; } static void widget_verts_to_triangle_strip(uiWidgetBase *wtb, const int totvert, float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]) @@ -650,7 +647,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) glEnable(GL_BLEND); /* backdrop non AA */ - if (wtb->inner) { + if (wtb->draw_inner) { if (wcol->shaded == 0) { if (wcol->alpha_check) { float inner_v_half[WIDGET_SIZE_MAX][2]; @@ -716,7 +713,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) glShadeModel(GL_SMOOTH); for (a = 0; a < wtb->totvert; a++, col_pt += 4) { - round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->shadedir]); + round_box_shade_col4_r(col_pt, col1, col2, wtb->inner_uv[a][wtb->draw_shadedir ? 1 : 0]); } glEnableClientState(GL_VERTEX_ARRAY); @@ -732,7 +729,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) } /* for each AA step */ - if (wtb->outline) { + if (wtb->draw_outline) { float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ float triangle_strip_emboss[WIDGET_SIZE_MAX * 2][2]; /* only for emboss */ @@ -743,7 +740,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); - if (wtb->emboss) { + if (wtb->draw_emboss) { widget_verts_to_triangle_strip_open(wtb, wtb->halfwayvert, triangle_strip_emboss); } @@ -761,7 +758,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) glDrawArrays(GL_TRIANGLE_STRIP, 0, wtb->totvert * 2 + 2); /* emboss bottom shadow */ - if (wtb->emboss) { + if (wtb->draw_emboss) { UI_GetThemeColor4ubv(TH_WIDGET_EMBOSS, emboss); if (emboss[3]) { @@ -783,6 +780,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) wcol->item[1], wcol->item[2], (unsigned char)((float)wcol->item[3] / WIDGET_AA_JITTER)}; + /* for each AA step */ for (j = 0; j < WIDGET_AA_JITTER; j++) { glTranslatef(jit[j][0], jit[j][1], 0.0f); @@ -801,7 +799,6 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) } glDisable(GL_BLEND); - } /* *********************** text/icon ************************************** */ @@ -810,7 +807,7 @@ static void widgetbase_draw(uiWidgetBase *wtb, uiWidgetColors *wcol) #define PREVIEW_PAD 4 -static void widget_draw_preview(BIFIconID icon, float UNUSED(alpha), const rcti *rect) +static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect) { int w, h, size; @@ -826,7 +823,7 @@ static void widget_draw_preview(BIFIconID icon, float UNUSED(alpha), const rcti int x = rect->xmin + w / 2 - size / 2; int y = rect->ymin + h / 2 - size / 2; - UI_icon_draw_preview_aspect_size(x, y, icon, 1.0f, size); + UI_icon_draw_preview_aspect_size(x, y, icon, 1.0f, alpha, size); } } @@ -838,8 +835,9 @@ static int ui_but_draw_menu_icon(const uiBut *but) /* icons have been standardized... and this call draws in untransformed coordinates */ -static void widget_draw_icon(const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, - const bool show_menu_icon) +static void widget_draw_icon( + const uiBut *but, BIFIconID icon, float alpha, const rcti *rect, + const bool show_menu_icon) { float xs = 0.0f, ys = 0.0f; float aspect, height; @@ -875,7 +873,7 @@ static void widget_draw_icon(const uiBut *but, BIFIconID icon, float alpha, cons if (but->drawflag & UI_BUT_ICON_LEFT) { if (but->block->flag & UI_BLOCK_LOOP) { - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU, UI_BTYPE_SEARCH_MENU_UNLINK)) + if (but->type == UI_BTYPE_SEARCH_MENU) xs = rect->xmin + 4.0f * ofs; else xs = rect->xmin + ofs; @@ -931,12 +929,14 @@ static void ui_text_clip_give_next_off(uiBut *but, const char *str) but->ofs += bytes; } -/* Helper. +/** + * 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, size_t *r_final_len) +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, size_t *r_final_len) { float tmp; int l_end; @@ -968,11 +968,16 @@ static void ui_text_clip_right_ex(uiFontStyle *fstyle, char *str, const size_t m * 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, float okwidth, const float minwidth, - const size_t max_len, const char *rpart_sep) +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; + /* Add some epsilon to OK width, avoids 'ellipsing' text that nearly fits! + * Better to have a small piece of the last char cut out, than two remaining chars replaced by an allipsis... */ + okwidth += 1.0f + UI_DPI_FAC; + BLI_assert(str[0]); /* need to set this first */ @@ -998,7 +1003,7 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, float okwidt size_t final_lpart_len; if (rpart_sep) { - rpart = strstr(str, rpart_sep); + rpart = strrchr(str, rpart_sep); if (rpart) { rpart_len = strlen(rpart); @@ -1069,7 +1074,7 @@ static float ui_text_clip_middle_ex(uiFontStyle *fstyle, char *str, float okwidt } /** - * Wrapper around ui_text_clip_middle_ex. + * Wrapper around UI_text_clip_middle_ex. */ static void ui_text_clip_middle(uiFontStyle *fstyle, uiBut *but, const rcti *rect) { @@ -1080,14 +1085,14 @@ 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, NULL); + but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, '\0'); } /** * 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) +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, UI_BTYPE_LABEL, UI_BTYPE_MENU) ? 0 : (int)(UI_TEXT_CLIP_MARGIN + 0.5f); @@ -1096,7 +1101,7 @@ static void ui_text_clip_middle_protect_right(uiFontStyle *fstyle, uiBut *but, c 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); + but->strwidth = UI_text_clip_middle_ex(fstyle, but->drawstr, okwidth, minwidth, max_len, rsep); } /** @@ -1217,7 +1222,6 @@ static void ui_text_clip_right_label(uiFontStyle *fstyle, uiBut *but, const rcti but->strwidth = BLF_width(fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs); if (but->strwidth < 10) break; } - } @@ -1320,7 +1324,7 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b #ifdef WITH_INPUT_IME /* FIXME, IME is modifying 'const char *drawstr! */ - ime_data = ui_but_get_ime_data(but); + ime_data = ui_but_ime_data_get(but); if (ime_data && ime_data->composite_len) { /* insert composite string into cursor pos */ @@ -1384,7 +1388,7 @@ static void widget_draw_text(uiFontStyle *fstyle, uiWidgetColors *wcol, uiBut *b t = 0; } - glColor3f(0.20, 0.6, 0.9); + glColor3f(0.2, 0.6, 0.9); tx = rect->xmin + t + 2; ty = rect->ymin + 2; @@ -1505,6 +1509,7 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB const bool show_menu_icon = ui_but_draw_menu_icon(but); float alpha = (float)wcol->text[3] / 255.0f; char password_str[UI_MAX_DRAW_STR]; + uiButExtraIconType extra_icon_type; ui_but_text_password_hide(password_str, but, false); @@ -1518,7 +1523,34 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB /* If there's an icon too (made with uiDefIconTextBut) then draw the icon * and offset the text label to accommodate it */ - if (but->flag & UI_HAS_ICON || show_menu_icon) { + /* Big previews with optional text label below */ + if (but->flag & UI_BUT_ICON_PREVIEW && ui_block_is_menu(but->block)) { + const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE; + int icon_size = BLI_rcti_size_y(rect); + int text_size = 0; + + /* This is a bit britle, but avoids adding an 'UI_BUT_HAS_LABEL' flag to but... */ + if (icon_size > BLI_rcti_size_x(rect)) { + /* button is not square, it has extra height for label */ + text_size = UI_UNIT_Y; + icon_size -= text_size; + } + + /* draw icon in rect above the space reserved for the label */ + rect->ymin += text_size; + glEnable(GL_BLEND); + widget_draw_preview(icon, alpha, rect); + glDisable(GL_BLEND); + + /* offset rect to draw label in */ + rect->ymin -= text_size; + rect->ymax -= icon_size; + + /* vertically centering text */ + rect->ymin += UI_UNIT_Y / 2; + } + /* Icons on the left with optional text label on the right */ + else if (but->flag & UI_HAS_ICON || show_menu_icon) { const BIFIconID icon = (but->flag & UI_HAS_ICON) ? but->icon + but->iconadd : ICON_NONE; const float icon_size = ICON_SIZE_FROM_BUTRECT(rect); @@ -1544,11 +1576,23 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB } /* unlink icon for this button type */ - if ((but->type == UI_BTYPE_SEARCH_MENU_UNLINK) && ui_but_is_search_unlink_visible(but)) { + if ((but->type == UI_BTYPE_SEARCH_MENU) && + ((extra_icon_type = ui_but_icon_extra_get(but)) != UI_BUT_ICONEXTRA_NONE)) + { rcti temp = *rect; temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f); - widget_draw_icon(but, ICON_X, alpha, &temp, false); + + if (extra_icon_type == UI_BUT_ICONEXTRA_UNLINK) { + widget_draw_icon(but, ICON_X, alpha, &temp, false); + } + else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) { + widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, false); + } + else { + BLI_assert(0); + } + rect->xmax -= ICON_SIZE_FROM_BUTRECT(rect); } @@ -1564,9 +1608,9 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB else if (ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER)) { ui_text_clip_right_label(fstyle, but, rect); } - else if ((but->block->flag & UI_BLOCK_LOOP) && (but->type == UI_BTYPE_BUT)) { + else if (but->flag & UI_BUT_HAS_SEP_CHAR) { /* Clip middle, but protect in all case right part containing the shortcut, if any. */ - ui_text_clip_middle_protect_right(fstyle, but, rect, "|"); + ui_text_clip_middle_protect_right(fstyle, but, rect, UI_SEP_CHAR); } else { ui_text_clip_middle(fstyle, but, rect); @@ -1583,17 +1627,6 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB /* *********************** widget types ************************************* */ - -/* uiWidgetStateColors - * char inner_anim[4]; - * char inner_anim_sel[4]; - * char inner_key[4]; - * char inner_key_sel[4]; - * char inner_driven[4]; - * char inner_driven_sel[4]; - * float blend; - */ - static struct uiWidgetStateColors wcol_state_colors = { {115, 190, 76, 255}, {90, 166, 51, 255}, @@ -1604,18 +1637,6 @@ static struct uiWidgetStateColors wcol_state_colors = { 0.5f, 0.0f }; -/* uiWidgetColors - * char outline[3]; - * char inner[4]; - * char inner_sel[4]; - * char item[3]; - * char text[3]; - * char text_sel[3]; - * - * short shaded; - * float shadetop, shadedown; - */ - static struct uiWidgetColors wcol_num = { {25, 25, 25, 255}, {180, 180, 180, 255}, @@ -1746,7 +1767,7 @@ static struct uiWidgetColors wcol_tooltip = { {45, 45, 45, 230}, {100, 100, 100, 255}, - {160, 160, 160, 255}, + {255, 255, 255, 255}, {255, 255, 255, 255}, 0, @@ -2181,7 +2202,7 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir widget_softshadow(rect, roundboxalign, 0.25f * U.widget_unit); round_box_edges(&wtb, roundboxalign, rect, 0.25f * U.widget_unit); - wtb.emboss = 0; + wtb.draw_emboss = false; widgetbase_draw(&wtb, wcol); glDisable(GL_BLEND); @@ -2190,7 +2211,6 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir static void ui_hsv_cursor(float x, float y) { - glPushMatrix(); glTranslatef(x, y, 0.0f); @@ -2205,7 +2225,6 @@ static void ui_hsv_cursor(float x, float y) glDisable(GL_LINE_SMOOTH); glPopMatrix(); - } void ui_hsvcircle_vals_from_pos(float *val_rad, float *val_dist, const rcti *rect, @@ -2280,12 +2299,12 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * * Useful for color correction tools where you're only interested in hue. */ if (but->flag & UI_BUT_COLOR_LOCK) { if (U.color_picker_type == USER_CP_CIRCLE_HSV) - hsv[2] = 1.f; + hsv[2] = 1.0f; else hsv[2] = 0.5f; } - ui_color_picker_to_rgb(0.f, 0.f, hsv[2], colcent, colcent + 1, colcent + 2); + ui_color_picker_to_rgb(0.0f, 0.0f, hsv[2], colcent, colcent + 1, colcent + 2); glShadeModel(GL_SMOOTH); @@ -2331,7 +2350,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, uiWidgetColors *wcol, const rcti * void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, const float alpha) { /* allows for 4 steps (red->yellow) */ - const float color_step = (1.0 / 48.0); + const float color_step = 1.0f / 48.0f; int a; float h = hsv[0], s = hsv[1], v = hsv[2]; float dx, dy, sx1, sx2, sy; @@ -2344,9 +2363,9 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons switch (type) { case UI_GRAD_SV: hsv_to_rgb(h, 0.0, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]); - hsv_to_rgb(h, 0.333, 0.0, &col1[1][0], &col1[1][1], &col1[1][2]); - hsv_to_rgb(h, 0.666, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]); - hsv_to_rgb(h, 1.0, 0.0, &col1[3][0], &col1[3][1], &col1[3][2]); + hsv_to_rgb(h, 0.0, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]); + hsv_to_rgb(h, 0.0, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]); + hsv_to_rgb(h, 0.0, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]); break; case UI_GRAD_HV: hsv_to_rgb(0.0, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]); @@ -2361,26 +2380,26 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons hsv_to_rgb(0.0, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]); break; case UI_GRAD_H: - hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]); + hsv_to_rgb(0.0, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]); copy_v3_v3(col1[1], col1[0]); copy_v3_v3(col1[2], col1[0]); copy_v3_v3(col1[3], col1[0]); break; case UI_GRAD_S: - hsv_to_rgb(1.0, 0.0, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]); + hsv_to_rgb(1.0, 0.0, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]); copy_v3_v3(col1[0], col1[1]); copy_v3_v3(col1[2], col1[1]); copy_v3_v3(col1[3], col1[1]); break; case UI_GRAD_V: - hsv_to_rgb(1.0, 1.0, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]); + hsv_to_rgb(1.0, 1.0, 0.0, &col1[2][0], &col1[2][1], &col1[2][2]); copy_v3_v3(col1[0], col1[2]); copy_v3_v3(col1[1], col1[2]); copy_v3_v3(col1[3], col1[2]); break; default: assert(!"invalid 'type' argument"); - hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]); + hsv_to_rgb(1.0, 1.0, 1.0, &col1[2][0], &col1[2][1], &col1[2][2]); copy_v3_v3(col1[0], col1[2]); copy_v3_v3(col1[1], col1[2]); copy_v3_v3(col1[3], col1[2]); @@ -2401,10 +2420,10 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons /* new color */ switch (type) { case UI_GRAD_SV: - hsv_to_rgb(h, 0.0, dx, &col1[0][0], &col1[0][1], &col1[0][2]); - hsv_to_rgb(h, 0.333, dx, &col1[1][0], &col1[1][1], &col1[1][2]); - hsv_to_rgb(h, 0.666, dx, &col1[2][0], &col1[2][1], &col1[2][2]); - hsv_to_rgb(h, 1.0, dx, &col1[3][0], &col1[3][1], &col1[3][2]); + hsv_to_rgb(h, dx, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]); + hsv_to_rgb(h, dx, 0.333, &col1[1][0], &col1[1][1], &col1[1][2]); + hsv_to_rgb(h, dx, 0.666, &col1[2][0], &col1[2][1], &col1[2][2]); + hsv_to_rgb(h, dx, 1.0, &col1[3][0], &col1[3][1], &col1[3][2]); break; case UI_GRAD_HV: hsv_to_rgb(dx_next, s, 0.0, &col1[0][0], &col1[0][1], &col1[0][2]); @@ -2419,23 +2438,21 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons hsv_to_rgb(dx_next, 1.0, v, &col1[3][0], &col1[3][1], &col1[3][2]); break; case UI_GRAD_H: - { /* annoying but without this the color shifts - could be solved some other way * - campbell */ - hsv_to_rgb(dx_next, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]); + hsv_to_rgb(dx_next, 1.0, 1.0, &col1[0][0], &col1[0][1], &col1[0][2]); copy_v3_v3(col1[1], col1[0]); copy_v3_v3(col1[2], col1[0]); copy_v3_v3(col1[3], col1[0]); break; - } case UI_GRAD_S: - hsv_to_rgb(h, dx, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]); + hsv_to_rgb(h, dx, 1.0, &col1[1][0], &col1[1][1], &col1[1][2]); copy_v3_v3(col1[0], col1[1]); copy_v3_v3(col1[2], col1[1]); copy_v3_v3(col1[3], col1[1]); break; case UI_GRAD_V: - hsv_to_rgb(h, 1.0, dx, &col1[2][0], &col1[2][1], &col1[2][2]); + hsv_to_rgb(h, 1.0, dx, &col1[2][0], &col1[2][1], &col1[2][2]); copy_v3_v3(col1[0], col1[2]); copy_v3_v3(col1[1], col1[2]); copy_v3_v3(col1[3], col1[2]); @@ -2464,9 +2481,8 @@ void ui_draw_gradient(const rcti *rect, const float hsv[3], const int type, cons } glEnd(); } - + glShadeModel(GL_FLAT); - } bool ui_but_is_colorpicker_display_space(uiBut *but) @@ -2487,7 +2503,7 @@ void ui_hsvcube_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float *x switch ((int)but->a1) { case UI_GRAD_SV: - x = hsv[2]; y = hsv[1]; break; + x = hsv[1]; y = hsv[2]; break; case UI_GRAD_HV: x = hsv[0]; y = hsv[2]; break; case UI_GRAD_HS: @@ -2509,11 +2525,10 @@ void ui_hsvcube_pos_from_vals(uiBut *but, const rcti *rect, float *hsv, float *x y = (hsv[2] - but->softmin) / (but->softmax - but->softmin); break; } - + /* cursor */ *xp = rect->xmin + x * BLI_rcti_size_x(rect); *yp = rect->ymin + y * BLI_rcti_size_y(rect); - } static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect) @@ -2594,9 +2609,8 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) x = rect->xmin + 0.5f * BLI_rcti_size_x(rect); y = rect->ymin + v * BLI_rcti_size_y(rect); CLAMP(y, rect->ymin + 3.0f, rect->ymax - 3.0f); - + ui_hsv_cursor(x, y); - } @@ -2681,7 +2695,8 @@ bool ui_link_bezier_points(const rcti *rect, float coord_array[][2], int resol) BKE_curve_forward_diff_bezier(vec[0][0], vec[1][0], vec[2][0], vec[3][0], &coord_array[0][0], resol, sizeof(float[2])); BKE_curve_forward_diff_bezier(vec[0][1], vec[1][1], vec[2][1], vec[3][1], &coord_array[0][1], resol, sizeof(float[2])); - return 1; + /* TODO: why return anything if always true? */ + return true; } #define LINK_RESOL 24 @@ -2690,9 +2705,10 @@ void ui_draw_link_bezier(const rcti *rect) float coord_array[LINK_RESOL + 1][2]; if (ui_link_bezier_points(rect, coord_array, LINK_RESOL)) { +#if 0 /* unused */ /* we can reuse the dist variable here to increment the GL curve eval amount*/ - // const float dist = 1.0f / (float)LINK_RESOL; // UNUSED - + const float dist = 1.0f / (float)LINK_RESOL; +#endif glEnable(GL_BLEND); glEnable(GL_LINE_SMOOTH); @@ -2703,7 +2719,6 @@ void ui_draw_link_bezier(const rcti *rect) glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); - } } @@ -2725,7 +2740,7 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s else rad = 0.5f * BLI_rcti_size_x(rect); - wtb.shadedir = (horizontal) ? 1 : 0; + wtb.draw_shadedir = (horizontal) ? true : false; /* draw back part, colors swapped and shading inverted */ if (horizontal) @@ -2754,11 +2769,11 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s } /* draw */ - wtb.emboss = 0; /* only emboss once */ + wtb.draw_emboss = false; /* only emboss once */ /* exception for progress bar */ if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.outline); + SWAP(bool, outline, wtb.draw_outline); } round_box_edges(&wtb, UI_CNR_ALL, slider, rad); @@ -2781,7 +2796,7 @@ void UI_draw_widget_scroll(uiWidgetColors *wcol, const rcti *rect, const rcti *s widgetbase_draw(&wtb, wcol); if (state & UI_SCROLL_NO_OUTLINE) { - SWAP(bool, outline, wtb.outline); + SWAP(bool, outline, wtb.draw_outline); } } } @@ -2908,7 +2923,7 @@ static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int s toffs = offs * 0.75f; round_box_edges(&wtb, roundboxalign, rect, offs); - wtb.outline = 0; + wtb.draw_outline = false; widgetbase_draw(&wtb, wcol); /* draw left/right parts only when not in text editing */ @@ -2933,7 +2948,7 @@ static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int s /* left part of slider, always rounded */ rect1.xmax = rect1.xmin + ceil(offs + U.pixelsize); round_box_edges(&wtb1, roundboxalign & ~(UI_CNR_TOP_RIGHT | UI_CNR_BOTTOM_RIGHT), &rect1, offs); - wtb1.outline = 0; + wtb1.draw_outline = false; widgetbase_draw(&wtb1, wcol); /* right part of slider, interpolate roundness */ @@ -2958,8 +2973,8 @@ static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int s } /* outline */ - wtb.outline = 1; - wtb.inner = 0; + wtb.draw_outline = true; + wtb.draw_inner = false; widgetbase_draw(&wtb, wcol); /* add space at either side of the button so text aligns with numbuttons (which have arrow icons) */ @@ -2967,7 +2982,6 @@ static void widget_numslider(uiBut *but, uiWidgetColors *wcol, rcti *rect, int s rect->xmax -= toffs; rect->xmin += toffs; } - } /* I think 3 is sufficient border to indicate keyed status */ @@ -3027,7 +3041,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat 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); + float bw = rgb_to_grayscale(col); bw += (bw < 0.5f) ? 0.5f : -0.5f; @@ -3052,7 +3066,7 @@ static void widget_icon_has_anim(uiBut *but, uiWidgetColors *wcol, rcti *rect, i float rad; widget_init(&wtb); - wtb.outline = 0; + wtb.draw_outline = false; /* rounded */ rad = 0.5f * BLI_rcti_size_y(rect); @@ -3082,7 +3096,6 @@ static void widget_textbut(uiWidgetColors *wcol, rcti *rect, int state, int roun round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); - } @@ -3168,7 +3181,7 @@ static void widget_menu_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta widget_init(&wtb); /* not rounded, no outline */ - wtb.outline = 0; + wtb.draw_outline = false; round_box_edges(&wtb, 0, rect, 0.0f); widgetbase_draw(&wtb, wcol); @@ -3182,7 +3195,7 @@ static void widget_menu_radial_itembut(uiBut *but, uiWidgetColors *wcol, rcti *r widget_init(&wtb); - wtb.emboss = 0; + wtb.draw_emboss = false; rad = 0.5f * BLI_rcti_size_y(rect); round_box_edges(&wtb, UI_CNR_ALL, rect, rad); @@ -3205,7 +3218,7 @@ static void widget_list_itembut(uiWidgetColors *wcol, rcti *rect, int UNUSED(sta widget_init(&wtb); /* rounded, but no outline */ - wtb.outline = 0; + wtb.draw_outline = false; rad = 0.2f * U.widget_unit; round_box_edges(&wtb, UI_CNR_ALL, rect, rad); @@ -3278,7 +3291,6 @@ static void widget_radiobut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); - } static void widget_box(uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) @@ -3319,7 +3331,6 @@ static void widget_but(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int round_box_edges(&wtb, roundboxalign, rect, rad); widgetbase_draw(&wtb, wcol); - } static void widget_roundbut(uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int roundboxalign) @@ -3360,10 +3371,9 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType * /* outline */ round_box_edges(&wtb, UI_CNR_ALL, rect, rad); - wtb.outline = 1; - wtb.inner = 0; + wtb.draw_outline = true; + wtb.draw_inner = false; widgetbase_draw(&wtb, &wt->wcol); - } static uiWidgetType *widget_type(uiWidgetTypeEnum type) @@ -3653,8 +3663,7 @@ void ui_draw_but(const bContext *C, ARegion *ar, uiStyle *style, uiBut *but, rct case UI_BTYPE_TEXT: wt = widget_type(UI_WTYPE_NAME); break; - - case UI_BTYPE_SEARCH_MENU_UNLINK: + case UI_BTYPE_SEARCH_MENU: wt = widget_type(UI_WTYPE_NAME); if (but->block->flag & UI_BLOCK_LOOP) @@ -3931,7 +3940,6 @@ static void draw_disk_shaded( glVertex2f(c * radius_ext, s * radius_ext); } glEnd(); - } void ui_draw_pie_center(uiBlock *block) @@ -4020,7 +4028,6 @@ void ui_draw_search_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) wt->draw(&wt->wcol, rect, block->flag, UI_CNR_ALL); else wt->draw(&wt->wcol, rect, 0, UI_CNR_ALL); - } @@ -4070,7 +4077,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, NULL); + UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0'); glColor4ubv((unsigned char *)wt->wcol.text); UI_fontstyle_draw(fstyle, rect, drawstr); @@ -4105,39 +4112,31 @@ void ui_draw_menu_item(uiFontStyle *fstyle, rcti *rect, const char *name, int ic void ui_draw_preview_item(uiFontStyle *fstyle, rcti *rect, const char *name, int iconid, int state) { - rcti trect = *rect, bg_rect; + rcti trect = *rect; + const float text_size = UI_UNIT_Y; float font_dims[2] = {0.0f, 0.0f}; uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); + /* drawing button background */ wt->state(wt, state); wt->draw(&wt->wcol, rect, 0, 0); + /* draw icon in rect above the space reserved for the label */ + rect->ymin += text_size; glEnable(GL_BLEND); widget_draw_preview(iconid, 1.0f, rect); + glDisable(GL_BLEND); BLF_width_and_height(fstyle->uifont_id, name, BLF_DRAW_STR_DUMMY_MAX, &font_dims[0], &font_dims[1]); /* text rect */ trect.xmin += 0; - trect.xmax = trect.xmin + font_dims[0] + 10; - trect.ymin += 10; + trect.xmax = trect.xmin + font_dims[0] + U.widget_unit / 2; + trect.ymin += U.widget_unit / 2; trect.ymax = trect.ymin + font_dims[1]; if (trect.xmax > rect->xmax - PREVIEW_PAD) trect.xmax = rect->xmax - PREVIEW_PAD; - bg_rect = trect; - bg_rect.xmin = rect->xmin + PREVIEW_PAD; - bg_rect.ymin = rect->ymin + PREVIEW_PAD; - bg_rect.xmax = rect->xmax - PREVIEW_PAD; - bg_rect.ymax += PREVIEW_PAD / 2; - - if (bg_rect.xmax > rect->xmax - PREVIEW_PAD) - bg_rect.xmax = rect->xmax - PREVIEW_PAD; - - glColor4ubv((unsigned char *)wt->wcol_theme->inner_sel); - glRecti(bg_rect.xmin, bg_rect.ymin, bg_rect.xmax, bg_rect.ymax); - glDisable(GL_BLEND); - { char drawstr[UI_MAX_DRAW_STR]; const float okwidth = (float)BLI_rcti_size_x(&trect); @@ -4145,7 +4144,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, NULL); + UI_text_clip_middle_ex(fstyle, drawstr, okwidth, minwidth, max_len, '\0'); glColor4ubv((unsigned char *)wt->wcol.text); UI_fontstyle_draw(fstyle, &trect, drawstr); diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index a9cc53852c0..68016ae3dd8 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -569,6 +569,13 @@ const unsigned char *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colo cp = ts->paint_curve_pivot; break; + case TH_METADATA_BG: + cp = ts->metadatabg; + break; + case TH_METADATA_TEXT: + cp = ts->metadatatext; + break; + case TH_UV_OTHERS: cp = ts->uv_others; break; @@ -817,8 +824,9 @@ static void ui_theme_space_init_handles_color(ThemeSpace *theme_space) rgba_char_args_set(theme_space->act_spline, 0xdb, 0x25, 0x12, 255); } -/* initialize default theme - * Note: when you add new colors, created & saved themes need initialized +/** + * initialize default theme + * \note: when you add new colors, created & saved themes need initialized * use function below, init_userdef_do_versions() */ void ui_theme_init_default(void) @@ -1098,7 +1106,7 @@ void ui_theme_init_default(void) rgba_char_args_set(btheme->text.syntaxd, 50, 0, 140, 255); /* Decorator/Preprocessor Dir. Blue-purple */ rgba_char_args_set(btheme->text.syntaxr, 140, 60, 0, 255); /* Reserved Orange*/ rgba_char_args_set(btheme->text.syntaxb, 128, 0, 80, 255); /* Builtin Red-purple */ - rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Grey (mix between fg/bg) */ + rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Gray (mix between fg/bg) */ /* space oops */ btheme->toops = btheme->tv3d; @@ -1504,8 +1512,9 @@ void UI_GetColorPtrShade3ubv(const unsigned char cp[3], unsigned char col[3], in } /* get a 3 byte color, blended and shaded between two other char color pointers */ -void UI_GetColorPtrBlendShade3ubv(const unsigned char cp1[3], const unsigned char cp2[3], unsigned char col[3], - float fac, int offset) +void UI_GetColorPtrBlendShade3ubv( + const unsigned char cp1[3], const unsigned char cp2[3], unsigned char col[3], + float fac, int offset) { int r, g, b; @@ -1603,8 +1612,8 @@ void init_userdef_do_versions(void) U.tw_size = 25; /* percentage of window size */ U.tw_handlesize = 16; /* percentage of widget radius */ } - if (U.pad_rot_angle == 0) - U.pad_rot_angle = 15; + if (U.pad_rot_angle == 0.0f) + U.pad_rot_angle = 15.0f; /* graph editor - unselected F-Curve visibility */ if (U.fcu_inactive_alpha == 0) { @@ -2351,7 +2360,7 @@ void init_userdef_do_versions(void) for (btheme = U.themes.first; btheme; btheme = btheme->next) { rgba_char_args_set(btheme->text.syntaxd, 50, 0, 140, 255); /* Decorator/Preprocessor Dir. Blue-purple */ rgba_char_args_set(btheme->text.syntaxr, 140, 60, 0, 255); /* Reserved Orange */ - rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Grey (mix between fg/bg) */ + rgba_char_args_set(btheme->text.syntaxs, 76, 76, 76, 255); /* Gray (mix between fg/bg) */ } } @@ -2602,7 +2611,22 @@ void init_userdef_do_versions(void) cp[3] = 255; } } - + + if (U.versionfile < 274 || (U.versionfile == 274 && U.subversionfile < 5)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + copy_v4_v4_char(btheme->tima.metadatatext, btheme->tima.text_hi); + copy_v4_v4_char(btheme->tseq.metadatatext, btheme->tseq.text_hi); + } + } + + if (U.versionfile < 275 || (U.versionfile == 275 && U.subversionfile < 1)) { + bTheme *btheme; + for (btheme = U.themes.first; btheme; btheme = btheme->next) { + copy_v4_v4_char(btheme->tclip.metadatatext, btheme->tseq.text_hi); + } + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index b4a120b2dea..4f4b5ab07ff 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -99,11 +99,12 @@ BLI_INLINE void clamp_rctf_to_rcti(rcti *dst, const rctf *src) /* XXX still unresolved: scrolls hide/unhide vs region mask handling */ /* XXX there's V2D_SCROLL_HORIZONTAL_HIDE and V2D_SCROLL_HORIZONTAL_FULLR ... */ -/* helper to allow scrollbars to dynamically hide - * - returns a copy of the scrollbar settings with the flags to display - * horizontal/vertical scrollbars removed - * - input scroll value is the v2d->scroll var - * - hide flags are set per region at drawtime +/** + * helper to allow scrollbars to dynamically hide + * - returns a copy of the scrollbar settings with the flags to display + * horizontal/vertical scrollbars removed + * - input scroll value is the v2d->scroll var + * - hide flags are set per region at drawtime */ static int view2d_scroll_mapped(int scroll) { @@ -197,11 +198,12 @@ static void view2d_masks(View2D *v2d, int check_scrollers) /* Refresh and Validation */ -/* Initialize all relevant View2D data (including view rects if first time) and/or refresh mask sizes after view resize - * - for some of these presets, it is expected that the region will have defined some - * additional settings necessary for the customization of the 2D viewport to its requirements - * - this function should only be called from region init() callbacks, where it is expected that - * this is called before UI_view2d_size_update(), as this one checks that the rects are properly initialized. +/** + * Initialize all relevant View2D data (including view rects if first time) and/or refresh mask sizes after view resize + * - for some of these presets, it is expected that the region will have defined some + * additional settings necessary for the customization of the 2D viewport to its requirements + * - this function should only be called from region init() callbacks, where it is expected that + * this is called before UI_view2d_size_update(), as this one checks that the rects are properly initialized. */ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) { @@ -320,7 +322,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) if (do_init) { float panelzoom = (style) ? style->panelzoom : 1.0f; - float scrolw = v2d->scroll & V2D_SCROLL_RIGHT ? V2D_SCROLL_WIDTH : 0.0f; + float scrolw = (v2d->scroll & V2D_SCROLL_RIGHT) ? V2D_SCROLL_WIDTH : 0.0f; v2d->tot.xmin = 0.0f; v2d->tot.xmax = winx - scrolw; @@ -361,8 +363,9 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) } -/* Ensure View2D rects remain in a viable configuration - * - cur is not allowed to be: larger than max, smaller than min, or outside of tot +/** + * Ensure View2D rects remain in a viable configuration + * 'cur' is not allowed to be: larger than max, smaller than min, or outside of 'tot' */ // XXX pre2.5 -> this used to be called test_view2d() static void ui_view2d_curRect_validate_resize(View2D *v2d, int resize, int mask_scrollers) @@ -842,7 +845,8 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) } -/* Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot) +/** + * Restore 'cur' rect to standard orientation (i.e. optimal maximum view of tot) * This does not take into account if zooming the view on an axis will improve the view (if allowed) */ void UI_view2d_curRect_reset(View2D *v2d) @@ -1098,8 +1102,10 @@ void UI_view2d_view_ortho(View2D *v2d) glLoadIdentity(); } -/* Set view matrices to only use one axis of 'cur' only - * - xaxis = if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x) +/** + * Set view matrices to only use one axis of 'cur' only + * + * \param xaxis: if non-zero, only use cur x-axis, otherwise use cur-yaxis (mostly this will be used for x) */ void UI_view2d_view_orthoSpecial(ARegion *ar, View2D *v2d, const bool xaxis) { @@ -1192,20 +1198,23 @@ static void step_to_grid(float *step, int *power, int unit) } } -/* Initialize settings necessary for drawing gridlines in a 2d-view - * - Currently, will return pointer to View2DGrid struct that needs to - * be freed with UI_view2d_grid_free() - * - Is used for scrollbar drawing too (for units drawing) - * - Units + clamping args will be checked, to make sure they are valid values that can be used - * so it is very possible that we won't return grid at all! - * - * - xunits,yunits = V2D_UNIT_* grid steps in seconds or frames - * - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals - * - winx = width of region we're drawing to, note: not used but keeping for completeness. - * - winy = height of region we're drawing into +/** + * Initialize settings necessary for drawing gridlines in a 2d-view + * + * - Currently, will return pointer to View2DGrid struct that needs to + * be freed with UI_view2d_grid_free() + * - Is used for scrollbar drawing too (for units drawing) + * - Units + clamping args will be checked, to make sure they are valid values that can be used + * so it is very possible that we won't return grid at all! + * + * - xunits,yunits = V2D_UNIT_* grid steps in seconds or frames + * - xclamp,yclamp = V2D_CLAMP_* only show whole-number intervals + * - winx = width of region we're drawing to, note: not used but keeping for completeness. + * - winy = height of region we're drawing into */ -View2DGrid *UI_view2d_grid_calc(Scene *scene, View2D *v2d, - short xunits, short xclamp, short yunits, short yclamp, int UNUSED(winx), int winy) +View2DGrid *UI_view2d_grid_calc( + Scene *scene, View2D *v2d, + short xunits, short xclamp, short yunits, short yclamp, int UNUSED(winx), int winy) { View2DGrid *grid; @@ -1483,9 +1492,11 @@ void UI_view2d_grid_free(View2DGrid *grid) /* *********************************************************************** */ /* Scrollers */ -/* View2DScrollers is typedef'd in UI_view2d.h - * WARNING: the start of this struct must not change, as view2d_ops.c uses this too. - * For now, we don't need to have a separate (internal) header for structs like this... +/** + * View2DScrollers is typedef'd in UI_view2d.h + * + * \warning The start of this struct must not change, as view2d_ops.c uses this too. + * For now, we don't need to have a separate (internal) header for structs like this... */ struct View2DScrollers { /* focus bubbles */ @@ -1502,8 +1513,9 @@ struct View2DScrollers { }; /* Calculate relevant scroller properties */ -View2DScrollers *UI_view2d_scrollers_calc(const bContext *C, View2D *v2d, - short xunits, short xclamp, short yunits, short yclamp) +View2DScrollers *UI_view2d_scrollers_calc( + const bContext *C, View2D *v2d, + short xunits, short xclamp, short yunits, short yclamp) { View2DScrollers *scrollers; rcti vert, hor; @@ -1885,18 +1897,22 @@ void UI_view2d_scrollers_free(View2DScrollers *scrollers) /* *********************************************************************** */ /* List View Utilities */ -/* Get the view-coordinates of the nominated cell - * - columnwidth, rowheight = size of each 'cell' - * - startx, starty = coordinates (in 'tot' rect space) that the list starts from - * This should be (0,0) for most views. However, for those where the starting row was offsetted - * (like for Animation Editor channel lists, to make the first entry more visible), these will be - * the min-coordinates of the first item. - * - column, row = the 2d-coordinates (in 2D-view / 'tot' rect space) the cell exists at - * - rect = coordinates of the cell (passed as single var instead of 4 separate, as it's more useful this way) +/** Get the view-coordinates of the nominated cell + * + * \param columnwidth, rowheight: size of each 'cell' + * \param startx, starty: coordinates (in 'tot' rect space) that the list starts from. + * This should be (0,0) for most views. However, for those where the starting row was offsetted + * (like for Animation Editor channel lists, to make the first entry more visible), these will be + * the min-coordinates of the first item. + * \param column, row: The 2d-coordinates + * (in 2D-view / 'tot' rect space) the cell exists at + * \param rect: coordinates of the cell + * (passed as single var instead of 4 separate, as it's more useful this way) */ -void UI_view2d_listview_cell_to_view(View2D *v2d, float columnwidth, float rowheight, - float startx, float starty, - int column, int row, rctf *rect) +void UI_view2d_listview_cell_to_view( + View2D *v2d, float columnwidth, float rowheight, + float startx, float starty, + int column, int row, rctf *rect) { /* sanity checks */ if (ELEM(NULL, v2d, rect)) { @@ -1930,17 +1946,20 @@ void UI_view2d_listview_cell_to_view(View2D *v2d, float columnwidth, float rowhe } } -/* Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in. - * - columnwidth, rowheight = size of each 'cell' - * - startx, starty = coordinates (in 'tot' rect space) that the list starts from - * This should be (0,0) for most views. However, for those where the starting row was offsetted - * (like for Animation Editor channel lists, to make the first entry more visible), these will be - * the min-coordinates of the first item. - * - viewx, viewy = 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for - * - column, row = the 'coordinates' of the relevant 'cell' +/** + * Get the 'cell' (row, column) that the given 2D-view coordinates (i.e. in 'tot' rect space) lie in. + * + * \param columnwidth, rowheight: size of each 'cell' + * \param startx, starty: coordinates (in 'tot' rect space) that the list starts from. + * This should be (0,0) for most views. However, for those where the starting row was offsetted + * (like for Animation Editor channel lists, to make the first entry more visible), these will be + * the min-coordinates of the first item. + * \param viewx, viewy: 2D-coordinates (in 2D-view / 'tot' rect space) to get the cell for + * \param column, row: the 'coordinates' of the relevant 'cell' */ -void UI_view2d_listview_view_to_cell(View2D *v2d, float columnwidth, float rowheight, float startx, float starty, - float viewx, float viewy, int *column, int *row) +void UI_view2d_listview_view_to_cell( + View2D *v2d, float columnwidth, float rowheight, float startx, float starty, + float viewx, float viewy, int *column, int *row) { /* adjust view coordinates to be all positive ints, corrected for the start offset */ const int x = (int)(floorf(fabsf(viewx) + 0.5f) - startx); @@ -1967,13 +1986,16 @@ void UI_view2d_listview_view_to_cell(View2D *v2d, float columnwidth, float rowhe *row = 0; } -/* Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect - * - columnwidth, rowheight = size of each 'cell' - * - startx, starty = coordinates that the list starts from, which should be (0,0) for most views - * - column/row_min/max = the starting and ending column/row indices +/** + * Get the 'extreme' (min/max) column and row indices which are visible within the 'cur' rect + * + * \param columnwidth, rowheight: Size of each 'cell' + * \param startx, starty: Coordinates that the list starts from, which should be (0,0) for most views + * \param column_min, column_max, row_min, row_max: The starting and ending column/row indices */ -void UI_view2d_listview_visible_cells(View2D *v2d, float columnwidth, float rowheight, float startx, float starty, - int *column_min, int *column_max, int *row_min, int *row_max) +void UI_view2d_listview_visible_cells( + View2D *v2d, float columnwidth, float rowheight, float startx, float starty, + int *column_min, int *column_max, int *row_min, int *row_max) { /* using 'cur' rect coordinates, call the cell-getting function to get the cells for this */ if (v2d) { @@ -1999,10 +2021,11 @@ float UI_view2d_region_to_view_y(struct View2D *v2d, float y) return (v2d->cur.ymin + (BLI_rctf_size_y(&v2d->cur) * (y - v2d->mask.ymin) / BLI_rcti_size_y(&v2d->mask))); } -/* Convert from screen/region space to 2d-View space - * - * - x,y = coordinates to convert - * - viewx,viewy = resultant coordinates +/** + * Convert from screen/region space to 2d-View space + * + * \param x, y: coordinates to convert + * \param r_view_x, r_view_y: resultant coordinates */ void UI_view2d_region_to_view(View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) { @@ -2030,11 +2053,12 @@ float UI_view2d_view_to_region_y(View2D *v2d, float y) return (v2d->mask.ymin + (((y - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur)) * BLI_rcti_size_y(&v2d->mask))); } -/* Convert from 2d-View space to screen/region space - * - Coordinates are clamped to lie within bounds of region +/** + * Convert from 2d-View space to screen/region space + * \note Coordinates are clamped to lie within bounds of region * - * - x,y = coordinates to convert - * - regionx,regiony = resultant coordinates + * \param x, y: Coordinates to convert. + * \param r_region_x, r_region_y: Resultant coordinates. */ bool UI_view2d_view_to_region_clip(View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) { @@ -2057,11 +2081,13 @@ bool UI_view2d_view_to_region_clip(View2D *v2d, float x, float y, int *r_region_ } } -/* Convert from 2d-view space to screen/region space - * - Coordinates are NOT clamped to lie within bounds of region +/** + * Convert from 2d-view space to screen/region space + * + * \note Coordinates are NOT clamped to lie within bounds of region. * - * - x,y = coordinates to convert - * - regionx,regiony = resultant coordinates + * \param x, y: Coordinates to convert. + * \param r_region_x, r_region_y: Resultant coordinates. */ void UI_view2d_view_to_region(View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) { @@ -2175,25 +2201,30 @@ View2D *UI_view2d_fromcontext_rwin(const bContext *C) } -/* Calculate the scale per-axis of the drawing-area - * - Is used to inverse correct drawing of icons, etc. that need to follow view - * but not be affected by scale +/** + * Calculate the scale per-axis of the drawing-area * - * - x,y = scale on each axis + * Is used to inverse correct drawing of icons, etc. that need to follow view + * but not be affected by scale + * + * \param x, y: scale on each axis */ void UI_view2d_scale_get(View2D *v2d, float *x, float *y) { if (x) *x = BLI_rcti_size_x(&v2d->mask) / BLI_rctf_size_x(&v2d->cur); if (y) *y = BLI_rcti_size_y(&v2d->mask) / BLI_rctf_size_y(&v2d->cur); } -/* Same as UI_view2d_scale_get() - 1.0f / x, y */ +/** + * Same as ``UI_view2d_scale_get() - 1.0f / x, y`` + */ void UI_view2d_scale_get_inverse(View2D *v2d, float *x, float *y) { if (x) *x = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); if (y) *y = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask); } -/* Simple functions for consistent center offset access. +/** + * Simple functions for consistent center offset access. * Used by node editor to shift view center for each individual node tree. */ void UI_view2d_center_get(struct View2D *v2d, float *x, float *y) @@ -2239,13 +2270,15 @@ void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac) UI_view2d_curRect_validate(v2d); } -/* Check if mouse is within scrollers - * - Returns appropriate code for match - * 'h' = in horizontal scroller - * 'v' = in vertical scroller - * 0 = not in scroller - * - * - x,y = mouse coordinates in screen (not region) space +/** + * Check if mouse is within scrollers + * + * \param x, y: Mouse coordinates in screen (not region) space. + * + * \return appropriate code for match. + * - 'h' = in horizontal scroller. + * - 'v' = in vertical scroller. + * - 0 = not in scroller. */ short UI_view2d_mouse_in_scrollers(const bContext *C, View2D *v2d, int x, int y) { @@ -2288,8 +2321,9 @@ typedef struct View2DString { static MemArena *g_v2d_strings_arena = NULL; static View2DString *g_v2d_strings = NULL; -void UI_view2d_text_cache_add(View2D *v2d, float x, float y, - const char *str, size_t str_len, const char col[4]) +void UI_view2d_text_cache_add( + View2D *v2d, float x, float y, + const char *str, size_t str_len, const char col[4]) { int mval[2]; @@ -2319,8 +2353,9 @@ void UI_view2d_text_cache_add(View2D *v2d, float x, float y, } /* no clip (yet) */ -void UI_view2d_text_cache_add_rectf(View2D *v2d, const rctf *rect_view, - const char *str, size_t str_len, const char col[4]) +void UI_view2d_text_cache_add_rectf( + View2D *v2d, const rctf *rect_view, + const char *str, size_t str_len, const char col[4]) { rcti rect; diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 88140d897ae..6fbe8509188 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -63,12 +63,13 @@ static int view2d_poll(bContext *C) /* ********************************************************* */ /* VIEW PANNING OPERATOR */ -/* This group of operators come in several forms: - * 1) Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by - * 2) Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount +/** + * This group of operators come in several forms: + * -# Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by + * -# Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount * - * In order to make sure this works, each operator must define the following RNA-Operator Props: - * deltax, deltay - define how much to move view by (relative to zoom-correction factor) + * In order to make sure this works, each operator must define the following RNA-Operator Props: + * - `deltax, deltay` - define how much to move view by (relative to zoom-correction factor) */ /* ------------------ Shared 'core' stuff ---------------------- */ @@ -328,7 +329,7 @@ static void VIEW2D_OT_pan(wmOperatorType *ot) ot->cancel = view_pan_cancel; /* operator is modal */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; /* rna - must keep these in sync with the other operators */ RNA_def_int(ot->srna, "deltax", 0, INT_MIN, INT_MAX, "Delta X", "", INT_MIN, INT_MAX); @@ -525,15 +526,18 @@ static void VIEW2D_OT_scroll_up(wmOperatorType *ot) /* ********************************************************* */ /* SINGLE-STEP VIEW ZOOMING OPERATOR */ -/* This group of operators come in several forms: - * 1) Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount - * 2) Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y) // XXX this could be implemented... - * 3) Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount +/** + * This group of operators come in several forms: + * -# Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount. + * -# Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y). + * XXX this could be implemented... + * -# Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount. * * In order to make sure this works, each operator must define the following RNA-Operator Props: - * zoomfacx, zoomfacy - These two zoom factors allow for non-uniform scaling. - * It is safe to scale by 0, as these factors are used to determine - * amount to enlarge 'cur' by + * + * - zoomfacx, zoomfacy - These two zoom factors allow for non-uniform scaling. + * It is safe to scale by 0, as these factors are used to determine. + * amount to enlarge 'cur' by. */ /* ------------------ 'Shared' stuff ------------------------ */ @@ -620,8 +624,9 @@ static int view_zoom_poll(bContext *C) } /* apply transform to view (i.e. adjust 'cur' rect) */ -static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool use_mousepos, - const float facx, const float facy) +static void view_zoomstep_apply_ex( + bContext *C, v2dViewZoomData *vzd, const bool use_mousepos, + const float facx, const float facy) { ARegion *ar = CTX_wm_region(C); View2D *v2d = &ar->v2d; @@ -661,7 +666,9 @@ static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool const float zoomx = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur); /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ - if (IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) { + if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || + IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) + { float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); float mval_faci = 1.0f - mval_fac; float ofs = (mval_fac * dx) - (mval_faci * dx); @@ -692,7 +699,9 @@ static void view_zoomstep_apply_ex(bContext *C, v2dViewZoomData *vzd, const bool const float zoomy = (float)(BLI_rcti_size_y(&v2d->mask) + 1) / BLI_rctf_size_y(&v2d->cur); /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ - if (IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) { + if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || + IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) + { float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); float mval_faci = 1.0f - mval_fac; float ofs = (mval_fac * dy) - (mval_faci * dy); @@ -867,10 +876,11 @@ static void VIEW2D_OT_zoom_out(wmOperatorType *ot) /* ********************************************************* */ /* DRAG-ZOOM OPERATOR */ -/* MMB Drag - allows non-uniform scaling by dragging mouse +/** + * MMB Drag - allows non-uniform scaling by dragging mouse * - * In order to make sure this works, each operator must define the following RNA-Operator Props: - * deltax, deltay - amounts to add to each side of the 'cur' rect + * In order to make sure this works, each operator must define the following RNA-Operator Props: + * - `deltax, deltay` - amounts to add to each side of the 'cur' rect */ /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -1175,7 +1185,7 @@ static void VIEW2D_OT_zoom(wmOperatorType *ot) ot->poll = view_zoom_poll; /* operator is repeatable */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; /* rna - must keep these in sync with the other operators */ prop = RNA_def_float(ot->srna, "deltax", 0, -FLT_MAX, FLT_MAX, "Delta X", "", -FLT_MAX, FLT_MAX); @@ -1187,10 +1197,12 @@ static void VIEW2D_OT_zoom(wmOperatorType *ot) /* ********************************************************* */ /* BORDER-ZOOM */ -/* The user defines a rect using standard borderselect tools, and we use this rect to +/** + * The user defines a rect using standard borderselect tools, and we use this rect to * define the new zoom-level of the view in the following ways: - * 1) LEFTMOUSE - zoom in to view - * 2) RIGHTMOUSE - zoom out of view + * + * -# LEFTMOUSE - zoom in to view + * -# RIGHTMOUSE - zoom out of view * * Currently, these key mappings are hardcoded, but it shouldn't be too important to * have custom keymappings for this... @@ -1402,8 +1414,9 @@ static float smooth_view_rect_to_fac(const rctf *rect_a, const rctf *rect_b) /* will start timer if appropriate */ /* the arguments are the desired situation */ -void UI_view2d_smooth_view(bContext *C, ARegion *ar, - const rctf *cur, const int smooth_viewtx) +void UI_view2d_smooth_view( + bContext *C, ARegion *ar, + const rctf *cur, const int smooth_viewtx) { wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); @@ -1528,13 +1541,14 @@ static void VIEW2D_OT_smoothview(wmOperatorType *ot) /* ********************************************************* */ /* SCROLLERS */ -/* Scrollers should behave in the following ways, when clicked on with LMB (and dragged): - * 1) 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, - * enlarge 'cur' rect on the relevant side - * 2) 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite) +/** + * Scrollers should behave in the following ways, when clicked on with LMB (and dragged): + * -# 'Handles' on end of 'bubble' - when the axis that the scroller represents is zoomable, + * enlarge 'cur' rect on the relevant side. + * -# 'Bubble'/'bar' - just drag, and bar should move with mouse (view pans opposite). * - * In order to make sure this works, each operator must define the following RNA-Operator Props: - * deltax, deltay - define how much to move view by (relative to zoom-correction factor) + * In order to make sure this works, each operator must define the following RNA-Operator Props: + * - `deltax, deltay` - define how much to move view by (relative to zoom-correction factor) */ /* customdata for scroller-invoke data */ @@ -1556,10 +1570,12 @@ typedef struct v2dScrollerMove { } v2dScrollerMove; -/* View2DScrollers is typedef'd in UI_view2d.h +/** + * #View2DScrollers is typedef'd in UI_view2d.h * This is a CUT DOWN VERSION of the 'real' version, which is defined in view2d.c, as we only need focus bubble info - * WARNING: the start of this struct must not change, so that it stays in sync with the 'real' version - * For now, we don't need to have a separate (internal) header for structs like this... + * + * \warning: The start of this struct must not change, so that it stays in sync with the 'real' version + * For now, we don't need to have a separate (internal) header for structs like this... */ struct View2DScrollers { /* focus bubbles */ @@ -1578,10 +1594,12 @@ enum { /* ------------------------ */ -/* check if mouse is within scroller handle - * - mouse = relevant mouse coordinate in region space - * - sc_min, sc_max = extents of scroller 'groove' (potential available space for scroller) - * - sh_min, sh_max = positions of scrollbar handles +/** + * Check if mouse is within scroller handle. + * + * \param mouse: relevant mouse coordinate in region space. + * \param sc_min, sc_max: extents of scroller 'groove' (potential available space for scroller). + * \param sh_min, sh_max: positions of scrollbar handles. */ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_min, int sh_max) { @@ -1785,7 +1803,10 @@ static void scroller_activate_apply(bContext *C, wmOperator *op) UI_view2d_sync(CTX_wm_screen(C), CTX_wm_area(C), v2d, V2D_LOCK_COPY); } -/* handle user input for scrollers - calculations of mouse-movement need to be done here, not in the apply callback! */ +/** + * Handle user input for scrollers - calculations of mouse-movement need to be done here, + * not in the apply callback! + */ static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *event) { v2dScrollerMove *vsm = op->customdata; diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index cb47adbe73e..2dae9561d4e 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -72,7 +72,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, MaskLayer *masklay, *point_masklay; MaskSpline *point_spline; MaskSplinePoint *point = NULL; - float dist = FLT_MAX, co[2]; + float dist_best_sq = FLT_MAX, co[2]; int width, height; float u; float scalex, scaley; @@ -123,7 +123,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } for (j = 0; j < tot_point - 1; j++) { - float cur_dist, a[2], b[2]; + float dist_sq, a[2], b[2]; a[0] = points[2 * j] * scalex; a[1] = points[2 * j + 1] * scaley; @@ -131,16 +131,16 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, b[0] = points[2 * j + 2] * scalex; b[1] = points[2 * j + 3] * scaley; - cur_dist = dist_to_line_segment_v2(co, a, b); + dist_sq = dist_squared_to_line_segment_v2(co, a, b); - if (cur_dist < dist) { + if (dist_sq < dist_best_sq) { if (tangent) sub_v2_v2v2(tangent, &diff_points[2 * j + 2], &diff_points[2 * j]); point_masklay = masklay; point_spline = spline; point = use_deform ? &spline->points[(cur_point - spline->points_deform)] : cur_point; - dist = cur_dist; + dist_best_sq = dist_sq; u = (float)j / tot_point; } } @@ -154,7 +154,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } } - if (point && dist < threshold) { + if (point && dist_best_sq < threshold) { if (masklay_r) *masklay_r = point_masklay; @@ -174,7 +174,7 @@ bool ED_mask_find_nearest_diff_point(const bContext *C, } if (score_r) { - *score_r = dist; + *score_r = dist_best_sq; } return true; diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index 7e767d8f6c8..2efa9e211c9 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -253,7 +253,7 @@ static void draw_spline_points(const bContext *C, MaskLayer *masklay, MaskSpline return; if (sc) - undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT) != 0; + undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT); /* TODO, add this to sequence editor */ handle_size = UI_GetThemeValuef(TH_HANDLE_VERTEX_SIZE) * U.pixelsize; @@ -422,7 +422,7 @@ static void mask_draw_curve_type(const bContext *C, MaskSpline *spline, float (* float (*points)[2] = orig_points; if (sc) { - int undistort = sc->clip && sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT; + const bool undistort = sc->clip && (sc->user.render_flag & MCLIP_PROXY_RENDER_UNDISTORT); if (undistort) { int i; diff --git a/source/blender/editors/mask/mask_edit.c b/source/blender/editors/mask/mask_edit.c index e2eb32e86d9..e1a58d529b6 100644 --- a/source/blender/editors/mask/mask_edit.c +++ b/source/blender/editors/mask/mask_edit.c @@ -35,6 +35,7 @@ #include "BKE_context.h" #include "BKE_mask.h" +#include "DNA_mask_types.h" #include "DNA_scene_types.h" #include "WM_api.h" @@ -397,6 +398,58 @@ void ED_mask_cursor_location_get(ScrArea *sa, float cursor[2]) } } +bool ED_mask_selected_minmax(const bContext *C, float min[2], float max[2]) +{ + Mask *mask = CTX_data_edit_mask(C); + MaskLayer *mask_layer; + bool ok = false; + INIT_MINMAX2(min, max); + for (mask_layer = mask->masklayers.first; + mask_layer != NULL; + mask_layer = mask_layer->next) + { + MaskSpline *spline; + if (mask_layer->restrictflag & (MASK_RESTRICT_VIEW | MASK_RESTRICT_SELECT)) { + continue; + } + for (spline = mask_layer->splines.first; + spline != NULL; + spline = spline->next) + { + MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); + int i; + for (i = 0; i < spline->tot_point; i++) { + MaskSplinePoint *point = &spline->points[i]; + MaskSplinePoint *deform_point = &points_array[i]; + BezTriple *bezt = &point->bezt; + float handle[2]; + if (!MASKPOINT_ISSEL_ANY(point)) { + continue; + } + if (bezt->f2 & SELECT) { + minmax_v2v2_v2(min, max, deform_point->bezt.vec[1]); + } + if (BKE_mask_point_handles_mode_get(point) == MASK_HANDLE_MODE_STICK) { + BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_STICK, handle); + minmax_v2v2_v2(min, max, handle); + } + else { + if ((bezt->f1 & SELECT) && (bezt->h1 != HD_VECT)) { + BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_LEFT, handle); + minmax_v2v2_v2(min, max, handle); + } + if ((bezt->f3 & SELECT) && (bezt->h2 != HD_VECT)) { + BKE_mask_point_handle(deform_point, MASK_WHICH_HANDLE_RIGHT, handle); + minmax_v2v2_v2(min, max, handle); + } + } + ok = true; + } + } + } + return ok; +} + /********************** registration *********************/ void ED_operatortypes_mask(void) diff --git a/source/blender/editors/mask/mask_intern.h b/source/blender/editors/mask/mask_intern.h index 5cdb224ce21..66a6c75272e 100644 --- a/source/blender/editors/mask/mask_intern.h +++ b/source/blender/editors/mask/mask_intern.h @@ -34,7 +34,6 @@ struct bContext; struct Mask; -struct wmEvent; struct wmOperatorType; /* internal exports only */ diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 113f0f71b53..35fc4483702 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -446,7 +446,7 @@ int do_paintface_box_select(ViewContext *vc, rcti *rect, bool select, bool exten } } - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); ibuf = IMB_allocImBuf(sx, sy, 32, IB_rect); rt = ibuf->rect; diff --git a/source/blender/editors/mesh/editmesh_add.c b/source/blender/editors/mesh/editmesh_add.c index 6ce5e8a304b..7af6c59adab 100644 --- a/source/blender/editors/mesh/editmesh_add.c +++ b/source/blender/editors/mesh/editmesh_add.c @@ -52,6 +52,10 @@ #include "mesh_intern.h" /* own include */ + +#define MESH_ADD_VERTS_MAXI 10000000 + + /* ********* add primitive operators ************* */ static Object *make_prim_init(bContext *C, const char *idname, @@ -62,10 +66,7 @@ static Object *make_prim_init(bContext *C, const char *idname, *was_editmode = false; if (obedit == NULL || obedit->type != OB_MESH) { - obedit = ED_object_add_type(C, OB_MESH, loc, rot, false, layer); - - rename_id((ID *)obedit, idname); - rename_id((ID *)obedit->data, idname); + obedit = ED_object_add_type(C, OB_MESH, idname, loc, rot, false, layer); /* create editmode */ ED_object_editmode_enter(C, EM_DO_UNDO | EM_IGNORE_LAYER); /* rare cases the active layer is messed up */ @@ -240,7 +241,7 @@ void MESH_OT_primitive_circle_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "vertices", 32, 3, INT_MAX, "Vertices", "", 3, 500); + RNA_def_int(ot->srna, "vertices", 32, 3, MESH_ADD_VERTS_MAXI, "Vertices", "", 3, 500); ED_object_add_unit_props(ot); RNA_def_enum(ot->srna, "fill_type", fill_type_items, 0, "Fill Type", ""); @@ -298,9 +299,9 @@ void MESH_OT_primitive_cylinder_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "vertices", 32, 3, INT_MAX, "Vertices", "", 3, 500); + RNA_def_int(ot->srna, "vertices", 32, 3, MESH_ADD_VERTS_MAXI, "Vertices", "", 3, 500); ED_object_add_unit_props(ot); - prop = RNA_def_float(ot->srna, "depth", 2.0f, 0.0, FLT_MAX, "Depth", "", 0.001, 100.00); + prop = RNA_def_float(ot->srna, "depth", 2.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Depth", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); RNA_def_enum(ot->srna, "end_fill_type", fill_type_items, 1, "Cap Fill Type", ""); @@ -355,12 +356,12 @@ void MESH_OT_primitive_cone_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "vertices", 32, 3, INT_MAX, "Vertices", "", 3, 500); - prop = RNA_def_float(ot->srna, "radius1", 1.0f, 0.0, FLT_MAX, "Radius 1", "", 0.001, 100.00); + RNA_def_int(ot->srna, "vertices", 32, 3, MESH_ADD_VERTS_MAXI, "Vertices", "", 3, 500); + prop = RNA_def_float(ot->srna, "radius1", 1.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Radius 1", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); - prop = RNA_def_float(ot->srna, "radius2", 0.0f, 0.0, FLT_MAX, "Radius 2", "", 0.001, 100.00); + prop = RNA_def_float(ot->srna, "radius2", 0.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Radius 2", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); - prop = RNA_def_float(ot->srna, "depth", 2.0f, 0.0, FLT_MAX, "Depth", "", 0.001, 100.00); + prop = RNA_def_float(ot->srna, "depth", 2.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Depth", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); RNA_def_enum(ot->srna, "end_fill_type", fill_type_items, 1, "Base Fill Type", ""); @@ -411,8 +412,10 @@ void MESH_OT_primitive_grid_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "x_subdivisions", 10, 2, INT_MAX, "X Subdivisions", "", 2, 1000); - RNA_def_int(ot->srna, "y_subdivisions", 10, 2, INT_MAX, "Y Subdivisions", "", 2, 1000); + /* Note that if you use MESH_ADD_VERTS_MAXI for both x and y at the same time you will still reach + * impossible values (10^12 vertices or so...). */ + RNA_def_int(ot->srna, "x_subdivisions", 10, 2, MESH_ADD_VERTS_MAXI, "X Subdivisions", "", 2, 1000); + RNA_def_int(ot->srna, "y_subdivisions", 10, 2, MESH_ADD_VERTS_MAXI, "Y Subdivisions", "", 2, 1000); ED_object_add_unit_props(ot); ED_object_add_generic_props(ot, true); @@ -513,9 +516,9 @@ void MESH_OT_primitive_uv_sphere_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "segments", 32, 3, INT_MAX, "Segments", "", 3, 500); - RNA_def_int(ot->srna, "ring_count", 16, 3, INT_MAX, "Rings", "", 3, 500); - prop = RNA_def_float(ot->srna, "size", 1.0f, 0.0, FLT_MAX, "Size", "", 0.001, 100.00); + RNA_def_int(ot->srna, "segments", 32, 3, MESH_ADD_VERTS_MAXI / 100, "Segments", "", 3, 500); + RNA_def_int(ot->srna, "ring_count", 16, 3, MESH_ADD_VERTS_MAXI / 100, "Rings", "", 3, 500); + prop = RNA_def_float(ot->srna, "size", 1.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Size", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); ED_object_add_generic_props(ot, true); @@ -566,8 +569,8 @@ void MESH_OT_primitive_ico_sphere_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "subdivisions", 2, 1, INT_MAX, "Subdivisions", "", 1, 8); - prop = RNA_def_float(ot->srna, "size", 1.0f, 0.0f, FLT_MAX, "Size", "", 0.001f, 100.00); + RNA_def_int(ot->srna, "subdivisions", 2, 1, 10, "Subdivisions", "", 1, 8); + prop = RNA_def_float(ot->srna, "size", 1.0f, 0.0f, OBJECT_ADD_SIZE_MAXF, "Size", "", 0.001f, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); ED_object_add_generic_props(ot, true); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 0e48cbcd589..cadfad34942 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -477,10 +477,10 @@ void MESH_OT_bevel(wmOperatorType *ot) ot->poll = ED_operator_editmesh; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; RNA_def_enum(ot->srna, "offset_type", offset_type_items, 0, "Amount Type", "What distance Amount measures"); - prop = 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, -1e6f, 1e6f, "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); diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index 6a54f1979cf..db4b0c16d9d 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -264,7 +264,7 @@ static int mesh_bisect_exec(bContext *C, wmOperator *op) invert_m4_m4(imat, obedit->obmat); mul_m4_v3(imat, plane_co); - mul_mat3_m4_v3(imat, plane_no); + mul_transposed_mat3_m4_v3(obedit->obmat, plane_no); EDBM_op_init(em, &bmop, op, "bisect_plane geom=%hvef plane_co=%v plane_no=%v dist=%f clear_inner=%b clear_outer=%b", @@ -335,11 +335,11 @@ void MESH_OT_bisect(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_float_vector(ot->srna, "plane_co", 3, NULL, -FLT_MAX, FLT_MAX, - "Plane Point", "A point on the plane", -FLT_MAX, FLT_MAX); + prop = RNA_def_float_vector(ot->srna, "plane_co", 3, NULL, -1e12f, 1e12f, + "Plane Point", "A point on the plane", -1e4f, 1e4f); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_float_vector(ot->srna, "plane_no", 3, NULL, -FLT_MAX, FLT_MAX, - "Plane Normal", "The direction the plane points", -FLT_MAX, FLT_MAX); + prop = RNA_def_float_vector(ot->srna, "plane_no", 3, NULL, -1.0f, 1.0f, + "Plane Normal", "The direction the plane points", -1.0f, 1.0f); RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_boolean(ot->srna, "use_fill", false, "Fill", "Fill in the cut"); diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 9b1b0b915c1..c4f2537eb59 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -326,8 +326,8 @@ void MESH_OT_extrude_repeat(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, FLT_MAX, "Offset", "", 0.0f, 100.0f); - RNA_def_int(ot->srna, "steps", 10, 0, INT_MAX, "Steps", "", 0, 180); + RNA_def_float(ot->srna, "offset", 2.0f, 0.0f, 1e12f, "Offset", "", 0.0f, 100.0f); + RNA_def_int(ot->srna, "steps", 10, 0, 1000000, "Steps", "", 0, 180); } /* generic extern called extruder */ @@ -510,7 +510,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w float min[3], max[3]; bool done = false; bool use_proj; - + em_setup_viewcontext(C, &vc); ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); @@ -574,8 +574,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w mul_mat3_m4_v3(vc.obedit->imat, nor); /* local space */ /* correct the normal to be aligned on the view plane */ - copy_v3_v3(view_vec, vc.rv3d->viewinv[2]); - mul_mat3_m4_v3(vc.obedit->imat, view_vec); + mul_v3_mat3_m4v3(view_vec, vc.obedit->imat, vc.rv3d->viewinv[2]); cross_v3_v3v3(cross, nor, view_vec); cross_v3_v3v3(nor, view_vec, cross); normalize_v3(nor); @@ -679,7 +678,7 @@ void MESH_OT_dupli_extrude_cursor(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "rotate_source", 1, "Rotate Source", "Rotate initial selection giving better shape"); + RNA_def_boolean(ot->srna, "rotate_source", true, "Rotate Source", "Rotate initial selection giving better shape"); } @@ -752,13 +751,15 @@ void MESH_OT_spin(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "steps", 9, 0, INT_MAX, "Steps", "Steps", 0, INT_MAX); + RNA_def_int(ot->srna, "steps", 9, 0, 1000000, "Steps", "Steps", 0, 1000); RNA_def_boolean(ot->srna, "dupli", 0, "Dupli", "Make Duplicates"); - prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -FLT_MAX, FLT_MAX, "Angle", "Angle", DEG2RADF(-360.0f), DEG2RADF(360.0f)); + prop = RNA_def_float(ot->srna, "angle", DEG2RADF(90.0f), -1e12f, 1e12f, "Angle", "Rotation for each step", + DEG2RADF(-360.0f), DEG2RADF(360.0f)); RNA_def_property_subtype(prop, PROP_ANGLE); - RNA_def_float_vector(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, "Center", "Center in global view space", -FLT_MAX, FLT_MAX); - RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX, "Axis", "Axis in global view space", -1.0f, 1.0f); + RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f, + "Center", "Center in global view space", -1e4f, 1e4f); + RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f); } @@ -870,11 +871,11 @@ void MESH_OT_screw(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "steps", 9, 1, INT_MAX, "Steps", "Steps", 3, 256); - RNA_def_int(ot->srna, "turns", 1, 1, INT_MAX, "Turns", "Turns", 1, 256); + RNA_def_int(ot->srna, "steps", 9, 1, 100000, "Steps", "Steps", 3, 256); + RNA_def_int(ot->srna, "turns", 1, 1, 100000, "Turns", "Turns", 1, 256); - RNA_def_float_vector(ot->srna, "center", 3, NULL, -FLT_MAX, FLT_MAX, - "Center", "Center in global view space", -FLT_MAX, FLT_MAX); - RNA_def_float_vector(ot->srna, "axis", 3, NULL, -FLT_MAX, FLT_MAX, + RNA_def_float_vector(ot->srna, "center", 3, NULL, -1e12f, 1e12f, + "Center", "Center in global view space", -1e4f, 1e4f); + RNA_def_float_vector(ot->srna, "axis", 3, NULL, -1.0f, 1.0f, "Axis", "Axis in global view space", -1.0f, 1.0f); } diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index f1c3ca30610..a5874b94edf 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -499,7 +499,7 @@ void MESH_OT_inset(wmOperatorType *ot) ot->poll = ED_operator_editmesh; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; /* properties */ RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries"); @@ -507,10 +507,10 @@ void MESH_OT_inset(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_relative_offset", false, "Offset Relative", "Scale the offset by surrounding geometry"); RNA_def_boolean(ot->srna, "use_edge_rail", false, "Edge Rail", "Inset the region along existing edges"); - prop = RNA_def_float(ot->srna, "thickness", 0.01f, 0.0f, FLT_MAX, "Thickness", "", 0.0f, 10.0f); + prop = RNA_def_float(ot->srna, "thickness", 0.01f, 0.0f, 1e12f, "Thickness", "", 0.0f, 10.0f); /* use 1 rather then 10 for max else dragging the button moves too far */ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4); - prop = RNA_def_float(ot->srna, "depth", 0.0f, -FLT_MAX, FLT_MAX, "Depth", "", -10.0f, 10.0f); + prop = RNA_def_float(ot->srna, "depth", 0.0f, -1e12f, 1e12f, "Depth", "", -10.0f, 10.0f); 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"); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index f3745ed72fb..8775160de76 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1319,18 +1319,63 @@ static BMElem *bm_elem_from_knife_edge(KnifeEdge *kfe) return ele_test; } +/* Do edges e1 and e2 go between exactly the same coordinates? */ +static bool coinciding_edges(BMEdge *e1, BMEdge *e2) +{ + const float *co11, *co12, *co21, *co22; + + co11 = e1->v1->co; + co12 = e1->v2->co; + co21 = e2->v1->co; + co22 = e2->v2->co; + if ((equals_v3v3(co11, co21) && equals_v3v3(co12, co22)) || + (equals_v3v3(co11, co22) && equals_v3v3(co12, co21))) + { + return true; + } + else { + return false; + } +} + +/* Callback used in point_is_visible to exclude hits on the faces that are the same + * as or contain the hitting element (which is in user_data). + * Also (see T44492) want to exclude hits on faces that butt up to the hitting element + * (e.g., when you double an edge by an edge split). + */ static bool bm_ray_cast_cb_elem_not_in_face_check(BMFace *f, void *user_data) { + bool ans; + BMEdge *e, *e2; + BMIter iter; + switch (((BMElem *)user_data)->head.htype) { case BM_FACE: - return (BMFace *)user_data != f; + ans = (BMFace *)user_data != f; + break; case BM_EDGE: - return !BM_edge_in_face((BMEdge *)user_data, f); + e = (BMEdge *)user_data; + ans = !BM_edge_in_face(e, f); + if (ans) { + /* Is it a boundary edge, coincident with a split edge? */ + if (BM_edge_is_boundary(e)) { + BM_ITER_ELEM(e2, &iter, f, BM_EDGES_OF_FACE) { + if (coinciding_edges(e, e2)) { + ans = false; + break; + } + } + } + } + break; case BM_VERT: - return !BM_vert_in_face((BMVert *)user_data, f); + ans = !BM_vert_in_face((BMVert *)user_data, f); + break; default: - return true; + ans = true; + break; } + return ans; } @@ -2783,7 +2828,7 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) for (lst = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); lst; lst = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) { - BLI_listbase_sort_r(lst, e->v1->co, sort_verts_by_dist_cb); + BLI_listbase_sort_r(lst, sort_verts_by_dist_cb, e->v1->co); for (ref = lst->first; ref; ref = ref->next) { kfv = ref->ref; @@ -2831,8 +2876,7 @@ static void knife_recalc_projmat(KnifeTool_OpData *kcd) ED_view3d_ob_project_mat_get(kcd->ar->regiondata, kcd->ob, kcd->projmat); 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); + mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob->imat, kcd->vc.rv3d->viewinv[2]); normalize_v3(kcd->proj_zaxis); kcd->is_ortho = ED_view3d_clip_range_get(kcd->vc.v3d, kcd->vc.rv3d, @@ -3006,6 +3050,8 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) knifetool_init(C, kcd, only_select, cut_through, true); + op->flag |= OP_IS_MODAL_CURSOR_REGION; + /* add a modal handler for this operator - handles loop selection */ WM_cursor_modal_set(CTX_wm_window(C), BC_KNIFECURSOR); WM_event_add_modal_handler(C, op); @@ -3098,6 +3144,9 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; } + em_setup_viewcontext(C, &kcd->vc); + kcd->ar = kcd->vc.ar; + view3d_operator_needs_opengl(C); ED_view3d_init_mats_rv3d(obedit, kcd->vc.rv3d); /* needed to initialize clipping */ @@ -3257,6 +3306,13 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } } + if (kcd->mode == MODE_DRAGGING) { + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } + else { + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + if (do_refresh) { /* we don't really need to update mval, * but this happens to be the best way to refresh at the moment */ @@ -3315,9 +3371,7 @@ static void edbm_mesh_knife_face_point(BMFace *f, float r_cent[3]) const float *p3 = loops[index[j][2]]->v->co; float area; - float cross[3]; - cross_v3_v3v3(cross, p2, p3); - area = fabsf(dot_v3v3(p1, cross)); + area = area_squared_tri_v3(p1, p2, p3); if (area > area_best) { j_best = j; area_best = area; diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 553c1faa36a..0d3cc07589b 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -166,7 +166,7 @@ void MESH_OT_knife_project(wmOperatorType *ot) /* callbacks */ ot->exec = knifeproject_exec; - ot->poll = ED_operator_editmesh_view3d; + ot->poll = ED_operator_editmesh_region_view3d; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 3d93210382c..74a40a23fe1 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -32,7 +32,7 @@ #include "MEM_guardedalloc.h" -#include "BLI_array.h" +#include "BLI_stack.h" #include "BLI_string.h" #include "BLI_math.h" @@ -224,7 +224,8 @@ static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, con BMEdge *eed, *eed_last; BMVert *v[2][2] = {{NULL}}, *v_last; float (*edges)[2][3] = NULL; - BLI_array_declare(edges); + BLI_Stack *edge_stack; + int i, tot = 0; BMW_init(&walker, bm, BMW_EDGERING, @@ -232,10 +233,27 @@ static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, con BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); + + edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__); + + eed_last = NULL; + for (eed = eed_last = BMW_begin(&walker, lcd->eed); eed; eed = BMW_step(&walker)) { + BLI_stack_push(edge_stack, &eed); + } + BMW_end(&walker); + + + eed_start = *(BMEdge **)BLI_stack_peek(edge_stack); + + edges = MEM_mallocN( + (sizeof(*edges) * (BLI_stack_count(edge_stack) + (eed_last != eed_start))) * previewlines, __func__); + v_last = NULL; eed_last = NULL; - for (eed = eed_start = BMW_begin(&walker, eed_start); eed; eed = BMW_step(&walker)) { + while (!BLI_stack_is_empty(edge_stack)) { + BLI_stack_pop(edge_stack, &eed); + if (eed_last) { if (v_last) { v[1][0] = v[0][0]; @@ -250,8 +268,6 @@ static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, con edgering_find_order(eed_last, eed, v_last, v); v_last = v[0][0]; - BLI_array_grow_items(edges, previewlines); - for (i = 1; i <= previewlines; i++) { const float fac = (i / ((float)previewlines + 1)); float v_cos[2][2][3]; @@ -279,8 +295,6 @@ static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, con edgering_find_order(eed_last, eed_start, v_last, v); - BLI_array_grow_items(edges, previewlines); - for (i = 1; i <= previewlines; i++) { const float fac = (i / ((float)previewlines + 1)); float v_cos[2][2][3]; @@ -297,7 +311,8 @@ static void edgering_preview_calc_edges(RingSelOpData *lcd, DerivedMesh *dm, con } } - BMW_end(&walker); + BLI_stack_free(edge_stack); + lcd->edges = edges; lcd->totedge = tot; } @@ -553,6 +568,7 @@ static int loopcut_init(bContext *C, wmOperator *op, const wmEvent *event) /* add a modal handler for this operator - handles loop selection */ if (is_interactive) { + op->flag |= OP_IS_MODAL_CURSOR_REGION; WM_event_add_modal_handler(C, op); } @@ -639,6 +655,9 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) bool show_cuts = false; const bool has_numinput = hasNumInput(&lcd->num); + em_setup_viewcontext(C, &lcd->vc); + lcd->ar = lcd->vc.ar; + view3d_operator_needs_opengl(C); /* using the keyboard to input the number of cuts */ @@ -825,21 +844,21 @@ void MESH_OT_loopcut(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; /* properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 100); + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000000, "Number of Cuts", "", 1, 100); /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_float(ot->srna, "smoothness", 0.0f, -FLT_MAX, FLT_MAX, + prop = RNA_def_float(ot->srna, "smoothness", 0.0f, -1e3f, 1e3f, "Smoothness", "Smoothness factor", -SUBD_SMOOTH_MAX, SUBD_SMOOTH_MAX); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_property(ot->srna, "falloff", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, proportional_falloff_curve_only_items); - RNA_def_property_enum_default(prop, PROP_ROOT); + RNA_def_property_enum_default(prop, PROP_INVSQUARE); RNA_def_property_ui_text(prop, "Falloff", "Falloff type the feather"); RNA_def_property_translation_context(prop, BLF_I18NCONTEXT_ID_CURVE); /* Abusing id_curve :/ */ - prop = RNA_def_int(ot->srna, "edge_index", -1, -1, INT_MAX, "Number of Cuts", "", 0, INT_MAX); + prop = RNA_def_int(ot->srna, "edge_index", -1, -1, INT_MAX, "Edge Index", "", 0, INT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); #ifdef USE_LOOPSLIDE_HACK diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 4eaac6cc1d3..72dfb89e5f3 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -94,7 +94,7 @@ static bool mouse_mesh_shortest_path_vert(ViewContext *vc) float dist = ED_view3d_select_dist_px(); const bool use_length = true; - v_dst = EDBM_vert_find_nearest(vc, &dist, false, false); + v_dst = EDBM_vert_find_nearest(vc, &dist); if (v_dst) { struct UserData user_data = {bm, vc->obedit->data, vc->scene}; LinkNode *path = NULL; diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index ead243d6de8..c7a7828f3b2 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -63,9 +63,10 @@ * point and would result in the same distance. */ #define INSET_DEFAULT 0.00001f -static float edbm_rip_edgedist_squared(ARegion *ar, float mat[4][4], - const float co1[3], const float co2[3], const float mvalf[2], - const float inset) +static float edbm_rip_edgedist_squared( + ARegion *ar, float mat[4][4], + const float co1[3], const float co2[3], const float mvalf[2], + const float inset) { float vec1[2], vec2[2], dist_sq; @@ -89,8 +90,9 @@ static float edbm_rip_edgedist_squared(ARegion *ar, float mat[4][4], } #if 0 -static float edbm_rip_linedist(ARegion *ar, float mat[4][4], - const float co1[3], const float co2[3], const float mvalf[2]) +static float edbm_rip_linedist( + ARegion *ar, float mat[4][4], + const float co1[3], const float co2[3], const float mvalf[2]) { float vec1[2], vec2[2]; @@ -114,9 +116,10 @@ static void edbm_calc_loop_co(BMLoop *l, float l_mid_co[3]) } -static float edbm_rip_edge_side_measure(BMEdge *e, BMLoop *e_l, - ARegion *ar, - float projectMat[4][4], const float fmval[2]) +static float edbm_rip_edge_side_measure( + BMEdge *e, BMLoop *e_l, + ARegion *ar, + float projectMat[4][4], const float fmval[2]) { float cent[3] = {0, 0, 0}, mid[3]; @@ -532,14 +535,14 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve BMesh *bm = em->bm; BMIter iter, liter; BMLoop *l; - BMEdge *e, *e2; + BMEdge *e_best; BMVert *v; const int totvert_orig = bm->totvert; int i; float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]}; float dist_sq = FLT_MAX; float d; - bool is_wire; + bool is_wire, is_manifold_region; BMEditSelection ese; int totboundary_edge = 0; @@ -559,65 +562,91 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } - /* this should be impossible, but sanity checks are a good thing */ - if (!v) + /* (v == NULL) should be impossible */ + if ((v == NULL) || (v->e == NULL)) { return OPERATOR_CANCELLED; + } is_wire = BM_vert_is_wire(v); + is_manifold_region = BM_vert_is_manifold_region(v); - e2 = NULL; + e_best = NULL; - if (v->e) { + { + BMEdge *e; /* find closest edge to mouse cursor */ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - const bool is_boundary = BM_edge_is_boundary(e); /* consider wire as boundary for this purpose, * otherwise we can't a face away from a wire edge */ - totboundary_edge += (is_boundary != 0 || BM_edge_is_wire(e)); + totboundary_edge += (BM_edge_is_boundary(e) || BM_edge_is_wire(e)); if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - if (is_boundary == false && BM_edge_is_manifold(e)) { + if ((is_manifold_region == false) || BM_edge_is_manifold(e)) { d = edbm_rip_edgedist_squared(ar, projectMat, e->v1->co, e->v2->co, fmval, INSET_DEFAULT); - if ((e2 == NULL) || (d < dist_sq)) { + if ((e_best == NULL) || (d < dist_sq)) { dist_sq = d; - e2 = e; + e_best = e; } } } } + } - /* if we are ripping a single vertex from 3 faces, - * then measure the distance to the face corner as well as the edge */ - if (BM_vert_face_count(v) == 3 && - BM_vert_edge_count(v) == 3) - { - BMEdge *e_all[3]; - BMLoop *l_all[3]; - int i1, i2; - - BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3); - BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3); - - /* not do a loop similar to the one above, but test against loops */ - for (i1 = 0; i1 < 3; i1++) { - /* consider wire as boundary for this purpose, - * otherwise we can't a face away from a wire edge */ - float l_mid_co[3]; - l = l_all[i1]; - edbm_calc_loop_co(l, l_mid_co); - d = edbm_rip_edgedist_squared(ar, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT); - if ((e2 == NULL) || (d < dist_sq)) { - dist_sq = d; - - /* find the edge that is not in this loop */ - e2 = NULL; - for (i2 = 0; i2 < 3; i2++) { - if (!BM_edge_in_loop(e_all[i2], l)) { - e2 = e_all[i2]; - break; - } + if (e_best && (is_manifold_region == false)) { + /* Try to split off a non-manifold fan (when we have multiple disconnected fans) */ + BMLoop *l_sep = e_best->l->v == v ? e_best->l : e_best->l->next; + BMVert *v_new; + + BLI_assert(l_sep->v == v); + v_new = bmesh_urmv_loop_region(bm, l_sep); + BLI_assert(BM_vert_find_first_loop(v)); + + BM_vert_select_set(bm, v, false); + BM_select_history_remove(bm, v); + + BM_vert_select_set(bm, v_new, true); + if (ese.ele) { + BM_select_history_store(bm, v_new); + } + + if (do_fill) { + BM_edge_create(bm, v, v_new, NULL, BM_CREATE_NOP); + } + + return OPERATOR_FINISHED; + } + + /* if we are ripping a single vertex from 3 faces, + * then measure the distance to the face corner as well as the edge */ + if (BM_vert_face_count_is_equal(v, 3) && + BM_vert_edge_count_is_equal(v, 3)) + { + BMEdge *e_all[3]; + BMLoop *l_all[3]; + int i1, i2; + + BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3); + BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3); + + /* not do a loop similar to the one above, but test against loops */ + for (i1 = 0; i1 < 3; i1++) { + /* consider wire as boundary for this purpose, + * otherwise we can't a face away from a wire edge */ + float l_mid_co[3]; + l = l_all[i1]; + edbm_calc_loop_co(l, l_mid_co); + d = edbm_rip_edgedist_squared(ar, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT); + if ((e_best == NULL) || (d < dist_sq)) { + dist_sq = d; + + /* find the edge that is not in this loop */ + e_best = NULL; + for (i2 = 0; i2 < 3; i2++) { + if (!BM_edge_in_loop(e_all[i2], l)) { + e_best = e_all[i2]; + break; } - BLI_assert(e2 != NULL); } + BLI_assert(e_best != NULL); } } } @@ -677,6 +706,7 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } else { + BMEdge *e; /* a wire vert, find the best edge */ BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) { if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { @@ -694,6 +724,15 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } + /* vout[0] == best + * vout[1] == glue + * vout[2+] == splice with glue (when vout_len > 2) + */ + if (vi_best != 0) { + SWAP(BMVert *, vout[0], vout[vi_best]); + vi_best = 0; + } + /* select the vert from the best region */ v = vout[vi_best]; BM_vert_select_set(bm, v, true); @@ -704,18 +743,15 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve /* splice all others back together */ if (vout_len > 2) { - - /* vout[0] == best - * vout[1] == glue - * vout[2+] == splice with glue - */ - if (vi_best != 0) { - SWAP(BMVert *, vout[0], vout[vi_best]); - vi_best = 0; + for (i = 2; i < vout_len; i++) { + BM_vert_splice(bm, vout[1], vout[i]); } + } - for (i = 2; i < vout_len; i++) { - BM_vert_splice(bm, vout[i], vout[1]); + if (do_fill) { + if (do_fill) { + /* match extrude vert-order */ + BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP); } } @@ -725,7 +761,7 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } - if (!e2) { + if (!e_best) { BKE_report(op->reports, RPT_ERROR, "Selected vertex has no edge/face pairs attached"); return OPERATOR_CANCELLED; } @@ -734,20 +770,51 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve /* unlike edge split, for single vertex split we only use the operator in one of the cases * but both allocate fill */ - /* rip two adjacent edges */ - if (BM_edge_is_boundary(e2) || BM_vert_face_count(v) == 2) { - /* Don't run the edge split operator in this case */ + { BMVert *v_rip; + BMLoop *larr[2]; + int larr_len = 0; - l = BM_edge_vert_share_loop(e2->l, v); + /* rip two adjacent edges */ + if (BM_edge_is_boundary(e_best) || BM_vert_face_count_is_equal(v, 2)) { + /* Don't run the edge split operator in this case */ - /* only tag for face-fill (we don't call the operator) */ - if (BM_edge_is_boundary(e2)) { - BM_elem_flag_enable(e2, BM_ELEM_TAG); + l = BM_edge_vert_share_loop(e_best->l, v); + larr[larr_len] = l; + larr_len++; + + /* only tag for face-fill (we don't call the operator) */ + if (BM_edge_is_boundary(e_best)) { + BM_elem_flag_enable(e_best, BM_ELEM_TAG); + } + else { + BM_elem_flag_enable(l->e, BM_ELEM_TAG); + BM_elem_flag_enable(l->prev->e, BM_ELEM_TAG); + } } else { - BM_elem_flag_enable(l->e, BM_ELEM_TAG); - BM_elem_flag_enable(l->prev->e, BM_ELEM_TAG); + if (BM_edge_is_manifold(e_best)) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e_best->l; + do { + larr[larr_len] = BM_edge_vert_share_loop(l_iter, v); + + if (do_fill) { + /* Only needed when filling... + * Also, we never want to tag best edge, that one won't change during split. See T44618. */ + if (larr[larr_len]->e == e_best) { + BM_elem_flag_enable(larr[larr_len]->prev->e, BM_ELEM_TAG); + } + else { + BM_elem_flag_enable(larr[larr_len]->e, BM_ELEM_TAG); + } + } + larr_len++; + } while ((l_iter = l_iter->radial_next) != l_first); + } + else { + /* looks like there are no split edges, we could just return/report-error? - Campbell */ + } } /* keep directly before edgesplit */ @@ -755,13 +822,12 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm); } -#if 0 - v_rip = BM_face_vert_separate(bm, l->f, v); -#else - v_rip = BM_face_loop_separate(bm, l); -#endif - - BLI_assert(v_rip); + if (larr_len) { + v_rip = BM_face_loop_separate_multi(bm, larr, larr_len); + } + else { + v_rip = NULL; + } if (v_rip) { BM_vert_select_set(bm, v_rip, true); @@ -771,27 +837,6 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve return OPERATOR_CANCELLED; } } - else { - if (BM_edge_is_manifold(e2)) { - l = e2->l; - e = BM_loop_other_edge_loop(l, v)->e; - BM_elem_flag_enable(e, BM_ELEM_TAG); - - l = e2->l->radial_next; - e = BM_loop_other_edge_loop(l, v)->e; - BM_elem_flag_enable(e, BM_ELEM_TAG); - } - else { - /* looks like there are no split edges, we could just return/report-error? - Campbell */ - } - - /* keep directly before edgesplit */ - if (do_fill) { - fill_uloop_pairs = edbm_tagged_loop_pairs_to_fill(bm); - } - - BM_mesh_edgesplit(em->bm, true, true, true); - } { /* --- select which vert --- */ @@ -854,7 +899,7 @@ static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, const wmEvent *eve BMesh *bm = em->bm; BMIter iter, eiter; BMLoop *l; - BMEdge *e, *e2; + BMEdge *e_best; BMVert *v; const int totedge_orig = bm->totedge; float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]}; @@ -868,11 +913,12 @@ static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, const wmEvent *eve /* expand edge selection */ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + BMEdge *e; bool all_manifold; int totedge_manifold; /* manifold, visible edges */ int i; - e2 = NULL; + e_best = NULL; i = 0; totedge_manifold = 0; all_manifold = true; @@ -884,7 +930,7 @@ static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, const wmEvent *eve /* important to check selection rather then tag here * else we get feedback loop */ if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - e2 = e; + e_best = e; i++; } totedge_manifold++; @@ -897,18 +943,18 @@ static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, const wmEvent *eve } /* single edge, extend */ - if (i == 1 && e2->l) { + if (i == 1 && e_best->l) { /* note: if the case of 3 edges has one change in loop stepping, * if this becomes more involved we may be better off splitting * the 3 edge case into its own else-if branch */ if ((totedge_manifold == 4 || totedge_manifold == 3) || (all_manifold == false)) { - BMLoop *l_a = e2->l; + BMLoop *l_a = e_best->l; BMLoop *l_b = l_a->radial_next; /* find the best face to follow, this way the edge won't point away from * the mouse when there are more than 4 (takes the shortest face fan around) */ - l = (edbm_rip_edge_side_measure(e2, l_a, ar, projectMat, fmval) < - edbm_rip_edge_side_measure(e2, l_b, ar, projectMat, fmval)) ? l_a : l_b; + l = (edbm_rip_edge_side_measure(e_best, l_a, ar, projectMat, fmval) < + edbm_rip_edge_side_measure(e_best, l_b, ar, projectMat, fmval)) ? l_a : l_b; l = BM_loop_other_edge_loop(l, v); /* important edge is manifold else we can be attempting to split off a fan that don't budge, @@ -926,7 +972,7 @@ static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, const wmEvent *eve } } else { - e = BM_vert_other_disk_edge(v, e2); + e = BM_vert_other_disk_edge(v, e_best); if (e) { BLI_assert(!BM_elem_flag_test(e, BM_ELEM_TAG)); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index d3073d519f6..27c43f9f1d3 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -36,6 +36,7 @@ #include "BLI_linklist.h" #include "BLI_linklist_stack.h" #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_rand.h" #include "BLI_array.h" @@ -52,6 +53,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "ED_mesh.h" #include "ED_screen.h" @@ -196,11 +198,11 @@ bool EDBM_backbuf_border_init(ViewContext *vc, short xmin, short ymin, short xma unsigned int *dr; int a; - if (vc->obedit == NULL || vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + if (vc->obedit == NULL || !V3D_IS_ZBUF(vc->v3d)) { return false; } - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (buf == NULL) return false; if (bm_vertoffs == 0) return false; @@ -271,11 +273,11 @@ bool EDBM_backbuf_border_mask_init(ViewContext *vc, const int mcords[][2], short return false; } } - else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + else if (!V3D_IS_ZBUF(vc->v3d)) { return false; } - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (buf == NULL) return false; if (bm_vertoffs == 0) return false; @@ -320,13 +322,13 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads) return false; } } - else if (vc->v3d->drawtype < OB_SOLID || (vc->v3d->flag & V3D_ZBUF_SELECT) == 0) { + else if (!V3D_IS_ZBUF(vc->v3d)) { return false; } xmin = xs - rads; xmax = xs + rads; ymin = ys - rads; ymax = ys + rads; - buf = view3d_read_backbuf(vc, xmin, ymin, xmax, ymax); + buf = ED_view3d_backbuf_read(vc, xmin, ymin, xmax, ymax); if (bm_vertoffs == 0) return false; if (buf == NULL) return false; @@ -350,194 +352,369 @@ bool EDBM_backbuf_circle_init(ViewContext *vc, short xs, short ys, short rads) } + +/* -------------------------------------------------------------------- */ + +/** \name Find Nearest Vert/Edge/Face + * + * \note Screen-space manhatten distances are used here, + * since its faster and good enough for the purpose of selection. + * + * \note \a dist_bias is used so we can bias against selected items. + * when choosing between elements of a single type, but return the real distance + * to avoid the bias interfering with distance comparisons when mixing types. + * \{ */ + +#define FIND_NEAR_SELECT_BIAS 5 +#define FIND_NEAR_CYCLE_THRESHOLD_MIN 3 + +struct NearestVertUserData_Hit { + float dist; + float dist_bias; + int index; + BMVert *vert; +}; + +struct NearestVertUserData { + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestVertUserData_Hit hit; + struct NearestVertUserData_Hit hit_cycle; +}; + static void findnearestvert__doClosest(void *userData, BMVert *eve, const float screen_co[2], int index) { - struct { float mval_fl[2], pass, select, strict; float dist, lastIndex, closestIndex; BMVert *closest; } *data = userData; + struct NearestVertUserData *data = userData; + float dist_test, dist_test_bias; - if (data->pass == 0) { - if (index <= data->lastIndex) - return; - } - else { - if (index > data->lastIndex) - return; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); + + if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + dist_test_bias += FIND_NEAR_SELECT_BIAS; } - if (data->dist > 3) { - float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); - if (BM_elem_flag_test(eve, BM_ELEM_SELECT) == data->select) { - if (data->strict == 1) { - return; - } - else { - dist_test += 5; - } - } + if (dist_test_bias < data->hit.dist_bias) { + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.vert = eve; + } - if (dist_test < data->dist) { - data->dist = dist_test; - data->closest = eve; - data->closestIndex = index; + if (data->use_cycle) { + if ((data->hit_cycle.vert == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.vert = eve; } } } - -static bool findnearestvert__backbufIndextest(void *handle, unsigned int index) -{ - BMEditMesh *em = (BMEditMesh *)handle; - BMVert *eve = BM_vert_at_index_find(em->bm, index - 1); - return !(eve && BM_elem_flag_test(eve, BM_ELEM_SELECT)); -} /** - * findnearestvert - * - * dist (in/out): minimal distance to the nearest and at the end, actual distance - * sel: selection bias - * if SELECT, selected vertice are given a 5 pixel bias to make them further than unselect verts - * if 0, unselected vertice are given the bias - * strict: if 1, the vertice corresponding to the sel parameter are ignored and not just biased + * Nearest vertex under the cursor. + * + * \param r_dist (in/out), minimal distance to the nearest and at the end, actual distance + * \param use_select_bias + * - When true, selected vertice are given a 5 pixel bias to make them further than unselect verts. + * - When false, unselected vertice are given the bias. + * \param use_cycle Cycle over elements within #FIND_NEAR_CYCLE_THRESHOLD_MIN in order of index. */ -BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist, const bool sel, const bool strict) +BMVert *EDBM_vert_find_nearest_ex( + ViewContext *vc, float *r_dist, + const bool use_select_bias, bool use_cycle) { - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { - float distance; + BMesh *bm = vc->em->bm; + + if (V3D_IS_ZBUF(vc->v3d)) { + const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist); + float dist_test; unsigned int index; BMVert *eve; - if (strict) { - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, - strict, vc->em, findnearestvert__backbufIndextest); - } - else { - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_wireoffs, 0xFFFFFF, &distance, - 0, NULL, NULL); - } - - eve = index ? BM_vert_at_index_find(vc->em->bm, index - 1) : NULL; + index = ED_view3d_backbuf_sample_rect( + vc, vc->mval, dist_px, bm_wireoffs, 0xFFFFFF, &dist_test); + eve = index ? BM_vert_at_index_find_or_table(bm, index - 1) : NULL; - if (eve && distance < *r_dist) { - *r_dist = distance; - return eve; - } - else { - return NULL; + if (eve) { + if (dist_test < *r_dist) { + *r_dist = dist_test; + return eve; + } } - + return NULL; } else { - struct { float mval_fl[2], pass, select, strict; float dist, lastIndex, closestIndex; BMVert *closest; } data; - static int lastSelectedIndex = 0; - static BMVert *lastSelected = NULL; - - if (lastSelected && BM_vert_at_index_find(vc->em->bm, lastSelectedIndex) != lastSelected) { - lastSelectedIndex = 0; - lastSelected = NULL; + struct NearestVertUserData data = {{0}}; + const struct NearestVertUserData_Hit *hit; + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT; + + static int prev_select_index = 0; + static const BMVert *prev_select_elem = NULL; + + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_vert_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; } - data.lastIndex = lastSelectedIndex; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.select = sel ? BM_ELEM_SELECT : 0; - data.dist = *r_dist; - data.strict = strict; - data.closest = NULL; - data.closestIndex = 0; - - data.pass = 0; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, clip_flag); - mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; - if (data.dist > 3) { - data.pass = 1; - mesh_foreachScreenVert(vc, findnearestvert__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); - } + prev_select_elem = hit->vert; + prev_select_index = hit->index; + + return hit->vert; + } +} + +BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *r_dist) +{ + return EDBM_vert_find_nearest_ex(vc, r_dist, false, false); +} + +/* find the distance to the edge we already have */ +struct NearestEdgeUserData_ZBuf { + float mval_fl[2]; + float dist; + const BMEdge *edge_test; +}; + +static void find_nearest_edge_center__doZBuf( + void *userData, BMEdge *eed, + const float screen_co_a[2], const float screen_co_b[2], + int UNUSED(index)) +{ + struct NearestEdgeUserData_ZBuf *data = userData; + + if (eed == data->edge_test) { + float dist_test; + float screen_co_mid[2]; - *r_dist = data.dist; - lastSelected = data.closest; - lastSelectedIndex = data.closestIndex; + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid); - return data.closest; + if (dist_test < data->dist) { + data->dist = dist_test; + } } } +struct NearestEdgeUserData_Hit { + float dist; + float dist_bias; + int index; + BMEdge *edge; + + /* edges only, un-biased manhatten distance to which ever edge we pick + * (not used for choosing) */ + float dist_center; +}; + +struct NearestEdgeUserData { + ViewContext vc; + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestEdgeUserData_Hit hit; + struct NearestEdgeUserData_Hit hit_cycle; +}; + /* note; uses v3d, so needs active 3d window */ -static void findnearestedge__doClosest(void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int UNUSED(index)) +static void find_nearest_edge__doClosest( + void *userData, BMEdge *eed, + const float screen_co_a[2], const float screen_co_b[2], + int index) { - struct { ViewContext vc; float mval_fl[2]; float dist; BMEdge *closest; } *data = userData; - int distance; + struct NearestEdgeUserData *data = userData; + float dist_test, dist_test_bias; - distance = dist_to_line_segment_v2(data->mval_fl, screen_co_a, screen_co_b); - - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - distance += 5; + float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b); + float screen_co[2]; + + if (fac <= 0.0f) { + fac = 0.0f; + copy_v2_v2(screen_co, screen_co_a); + } + else if (fac >= 1.0f) { + fac = 1.0f; + copy_v2_v2(screen_co, screen_co_b); + } + else { + interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac); } - if (distance < data->dist) { - if (data->vc.rv3d->rflag & RV3D_CLIPPING) { - float lambda = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b); - float vec[3]; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); - vec[0] = eed->v1->co[0] + lambda * (eed->v2->co[0] - eed->v1->co[0]); - vec[1] = eed->v1->co[1] + lambda * (eed->v2->co[1] - eed->v1->co[1]); - vec[2] = eed->v1->co[2] + lambda * (eed->v2->co[2] - eed->v1->co[2]); + if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) { + dist_test += FIND_NEAR_SELECT_BIAS; + } - if (ED_view3d_clipping_test(data->vc.rv3d, vec, true) == 0) { - data->dist = distance; - data->closest = eed; - } + if (data->vc.rv3d->rflag & RV3D_CLIPPING) { + float vec[3]; + + interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac); + if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) { + return; } - else { - data->dist = distance; - data->closest = eed; + } + + if (dist_test_bias < data->hit.dist_bias) { + float screen_co_mid[2]; + + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.edge = eed; + + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + data->hit.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid); + } + + if (data->use_cycle) { + if ((data->hit_cycle.edge == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + float screen_co_mid[2]; + + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.edge = eed; + + mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b); + data->hit_cycle.dist_center = len_manhattan_v2v2(data->mval_fl, screen_co_mid); } } } -BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *r_dist) + +BMEdge *EDBM_edge_find_nearest_ex( + ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + BMEdge **r_eed_zbuf) { + BMesh *bm = vc->em->bm; - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { - float distance; + if (V3D_IS_ZBUF(vc->v3d)) { + const int dist_px = ED_view3d_backbuf_sample_size_clamp(vc->ar, *r_dist); + float dist_test = 0.0f; unsigned int index; BMEdge *eed; - view3d_validate_backbuf(vc); - - index = view3d_sample_backbuf_rect(vc, vc->mval, 50, bm_solidoffs, bm_wireoffs, &distance, 0, NULL, NULL); - eed = index ? BM_edge_at_index_find(vc->em->bm, index - 1) : NULL; - - if (eed && distance < *r_dist) { - *r_dist = distance; - return eed; + ED_view3d_backbuf_validate(vc); + + index = ED_view3d_backbuf_sample_rect(vc, vc->mval, dist_px, bm_solidoffs, bm_wireoffs, &dist_test); + eed = index ? BM_edge_at_index_find_or_table(bm, index - 1) : NULL; + + if (r_eed_zbuf) { + *r_eed_zbuf = eed; } - else { - return NULL; + + /* exception for faces (verts don't need this) */ + if (r_dist_center && eed) { + struct NearestEdgeUserData_ZBuf data; + + data.mval_fl[0] = vc->mval[0]; + data.mval_fl[1] = vc->mval[1]; + data.dist = FLT_MAX; + data.edge_test = eed; + + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + + mesh_foreachScreenEdge(vc, find_nearest_edge_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + *r_dist_center = data.dist; + } + /* end exception */ + + if (eed) { + if (dist_test < *r_dist) { + *r_dist = dist_test; + return eed; + } } + return NULL; } else { - struct { ViewContext vc; float mval_fl[2]; float dist; BMEdge *closest; } data; + struct NearestEdgeUserData data = {{0}}; + const struct NearestEdgeUserData_Hit *hit; + /* interpolate along the edge before doing a clipping plane test */ + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB; + + static int prev_select_index = 0; + static const BMEdge *prev_select_elem = NULL; + + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_edge_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; + } data.vc = *vc; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.dist = *r_dist; - data.closest = NULL; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; + ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); + mesh_foreachScreenEdge(vc, find_nearest_edge__doClosest, &data, clip_flag); - mesh_foreachScreenEdge(vc, findnearestedge__doClosest, &data, V3D_PROJ_TEST_CLIP_WIN); + hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; + if (r_dist_center) { + *r_dist_center = hit->dist_center; + } - *r_dist = data.dist; - return data.closest; + prev_select_elem = hit->edge; + prev_select_index = hit->index; + + return hit->edge; } } -static void findnearestface__getDistance(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) +BMEdge *EDBM_edge_find_nearest( + ViewContext *vc, float *r_dist) +{ + return EDBM_edge_find_nearest_ex(vc, r_dist, false, false, false, NULL); +} + +/* find the distance to the face we already have */ +struct NearestFaceUserData_ZBuf { + float mval_fl[2]; + float dist; + const BMFace *face_test; +}; + +static void find_nearest_face_center__doZBuf(void *userData, BMFace *efa, const float screen_co[2], int UNUSED(index)) { - struct { float mval_fl[2]; float dist; BMFace *toFace; } *data = userData; + struct NearestFaceUserData_ZBuf *data = userData; - if (efa == data->toFace) { + if (efa == data->face_test) { const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); if (dist_test < data->dist) { @@ -545,96 +722,152 @@ static void findnearestface__getDistance(void *userData, BMFace *efa, const floa } } } + + +struct NearestFaceUserData_Hit { + float dist; + float dist_bias; + int index; + BMFace *face; +}; + +struct NearestFaceUserData { + float mval_fl[2]; + bool use_select_bias; + bool use_cycle; + int cycle_index_prev; + + struct NearestFaceUserData_Hit hit; + struct NearestFaceUserData_Hit hit_cycle; +}; + static void findnearestface__doClosest(void *userData, BMFace *efa, const float screen_co[2], int index) { - struct { float mval_fl[2], pass; float dist, lastIndex, closestIndex; BMFace *closest; } *data = userData; + struct NearestFaceUserData *data = userData; + float dist_test, dist_test_bias; - if (data->pass == 0) { - if (index <= data->lastIndex) - return; - } - else { - if (index > data->lastIndex) - return; + dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co); + + if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + dist_test_bias += FIND_NEAR_SELECT_BIAS; } - if (data->dist > 3) { - const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co); + if (dist_test_bias < data->hit.dist_bias) { + data->hit.dist_bias = dist_test_bias; + data->hit.dist = dist_test; + data->hit.index = index; + data->hit.face = efa; + } - if (dist_test < data->dist) { - data->dist = dist_test; - data->closest = efa; - data->closestIndex = index; + if (data->use_cycle) { + if ((data->hit_cycle.face == NULL) && + (index > data->cycle_index_prev) && + (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN)) + { + data->hit_cycle.dist_bias = dist_test_bias; + data->hit_cycle.dist = dist_test; + data->hit_cycle.index = index; + data->hit_cycle.face = efa; } } } -BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) + +BMFace *EDBM_face_find_nearest_ex( + ViewContext *vc, float *r_dist, + float *r_dist_center, + const bool use_select_bias, const bool use_cycle, + BMFace **r_efa_zbuf) { + BMesh *bm = vc->em->bm; - if (vc->v3d->drawtype > OB_WIRE && (vc->v3d->flag & V3D_ZBUF_SELECT)) { + if (V3D_IS_ZBUF(vc->v3d)) { + float dist_test = 0.0f; unsigned int index; BMFace *efa; - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); - index = view3d_sample_backbuf(vc, vc->mval[0], vc->mval[1]); - efa = index ? BM_face_at_index_find(vc->em->bm, index - 1) : NULL; + index = ED_view3d_backbuf_sample(vc, vc->mval[0], vc->mval[1]); + efa = index ? BM_face_at_index_find_or_table(bm, index - 1) : NULL; - if (efa) { - struct { float mval_fl[2]; float dist; BMFace *toFace; } data; + if (r_efa_zbuf) { + *r_efa_zbuf = efa; + } + + /* exception for faces (verts don't need this) */ + if (r_dist_center && efa) { + struct NearestFaceUserData_ZBuf data; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; data.dist = FLT_MAX; - data.toFace = efa; + data.face_test = efa; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__getDistance, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + mesh_foreachScreenFace(vc, find_nearest_face_center__doZBuf, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + + *r_dist_center = data.dist; + } + /* end exception */ - if ((vc->em->selectmode == SCE_SELECT_FACE) || (data.dist < *r_dist)) { /* only faces, no dist check */ - *r_dist = data.dist; + if (efa) { + if (dist_test < *r_dist) { + *r_dist = dist_test; return efa; } } - return NULL; } else { - struct { float mval_fl[2], pass; float dist, lastIndex, closestIndex; BMFace *closest; } data; - static int lastSelectedIndex = 0; - static BMFace *lastSelected = NULL; + struct NearestFaceUserData data = {{0}}; + const struct NearestFaceUserData_Hit *hit; + const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT; - if (lastSelected && BM_face_at_index_find(vc->em->bm, lastSelectedIndex) != lastSelected) { - lastSelectedIndex = 0; - lastSelected = NULL; + static int prev_select_index = 0; + static const BMFace *prev_select_elem = NULL; + + if ((use_cycle == false) || + (prev_select_elem && (prev_select_elem != BM_face_at_index_find_or_table(bm, prev_select_index)))) + { + prev_select_index = 0; + prev_select_elem = NULL; } - data.lastIndex = lastSelectedIndex; data.mval_fl[0] = vc->mval[0]; data.mval_fl[1] = vc->mval[1]; - data.dist = *r_dist; - data.closest = NULL; - data.closestIndex = 0; + data.use_select_bias = use_select_bias; + data.use_cycle = use_cycle; + data.hit.dist = data.hit_cycle.dist = \ + data.hit.dist_bias = data.hit_cycle.dist_bias = *r_dist; + data.cycle_index_prev = prev_select_index; - data.pass = 0; ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, clip_flag); - if (data.dist > 3.0f) { - data.pass = 1; - mesh_foreachScreenFace(vc, findnearestface__doClosest, &data, V3D_PROJ_TEST_CLIP_DEFAULT); + hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit; + *r_dist = hit->dist; + if (r_dist_center) { + *r_dist_center = hit->dist; } - *r_dist = data.dist; - lastSelected = data.closest; - lastSelectedIndex = data.closestIndex; + prev_select_elem = hit->face; + prev_select_index = hit->index; - return data.closest; + return hit->face; } } +BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) +{ + return EDBM_face_find_nearest_ex(vc, r_dist, NULL, false, false, NULL); +} + +#undef FIND_NEAR_SELECT_BIAS +#undef FIND_NEAR_CYCLE_THRESHOLD_MIN + + /* best distance based on screen coords. * use em->selectmode to define how to use * selected vertices and edges get disadvantage @@ -643,35 +876,78 @@ BMFace *EDBM_face_find_nearest(ViewContext *vc, float *r_dist) static int unified_findnearest(ViewContext *vc, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa) { BMEditMesh *em = vc->em; - float dist = ED_view3d_select_dist_px(); - - *r_eve = NULL; - *r_eed = NULL; - *r_efa = NULL; + static short mval_prev[2] = {-1, -1}; + /* only cycle while the mouse remains still */ + const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1])); + const float dist_init = ED_view3d_select_dist_px(); + /* since edges select lines, we give dots advantage of ~20 pix */ + const float dist_margin = (dist_init / 2); + float dist = dist_init; + BMFace *efa_zbuf = NULL; + BMEdge *eed_zbuf = NULL; + BMVert *eve = NULL; + BMEdge *eed = NULL; + BMFace *efa = NULL; + + /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */ - view3d_validate_backbuf(vc); - - if (em->selectmode & SCE_SELECT_VERTEX) - *r_eve = EDBM_vert_find_nearest(vc, &dist, BM_ELEM_SELECT, 0); - if (em->selectmode & SCE_SELECT_FACE) - *r_efa = EDBM_face_find_nearest(vc, &dist); + ED_view3d_backbuf_validate(vc); - dist -= 20; /* since edges select lines, we give dots advantage of 20 pix */ - if (em->selectmode & SCE_SELECT_EDGE) - *r_eed = EDBM_edge_find_nearest(vc, &dist); + if ((dist > 0.0f) && em->selectmode & SCE_SELECT_FACE) { + float dist_center = 0.0f; + float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ? &dist_center : NULL; + efa = EDBM_face_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf); + if (efa && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + } + + if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) { + float dist_center = 0.0f; + float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : NULL; + eed = EDBM_edge_find_nearest_ex(vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf); + if (eed && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + } + + if ((dist > 0.0f) && em->selectmode & SCE_SELECT_VERTEX) { + eve = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle); + } /* return only one of 3 pointers, for frontbuffer redraws */ - if (*r_eed) { - *r_efa = NULL; *r_eve = NULL; + if (eve) { + efa = NULL; eed = NULL; } - else if (*r_efa) { - *r_eve = NULL; + else if (eed) { + efa = NULL; } - - return (*r_eve || *r_eed || *r_efa); + + /* there may be a face under the cursor, who's center if too far away + * use this if all else fails, it makes sense to select this */ + if ((eve || eed || efa) == 0) { + if (eed_zbuf) { + eed = eed_zbuf; + } + else if (efa_zbuf) { + efa = efa_zbuf; + } + } + + mval_prev[0] = vc->mval[0]; + mval_prev[1] = vc->mval[1]; + + *r_eve = eve; + *r_eed = eed; + *r_efa = efa; + + return (eve || eed || efa); } +/** \} */ + + /* **************** SIMILAR "group" SELECTS. FACE, EDGE AND VERTEX ************** */ static EnumPropertyItem prop_similar_compare_types[] = { {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""}, @@ -923,7 +1199,7 @@ void MESH_OT_select_similar(wmOperatorType *ot) RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", ""); - RNA_def_float(ot->srna, "threshold", 0.0, 0.0, 1.0, "Threshold", "", 0.0, 1.0); + RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); } @@ -1179,7 +1455,7 @@ static int edbm_loop_multiselect_exec(bContext *C, wmOperator *op) else { for (edindex = 0; edindex < totedgesel; edindex += 1) { eed = edarray[edindex]; - walker_select(em, BMW_LOOP, eed, true); + walker_select(em, BMW_EDGELOOP, eed, true); } EDBM_selectmode_flush(em); } @@ -1237,12 +1513,12 @@ static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool { bool edge_boundary = false; - /* cycle between BMW_LOOP / BMW_EDGEBOUNDARY */ + /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY */ if (select_cycle && BM_edge_is_boundary(eed)) { int tot[2]; /* if the loops selected toggle the boundaries */ - walker_select_count(em, BMW_LOOP, eed, select, false, + walker_select_count(em, BMW_EDGELOOP, eed, select, false, &tot[0], &tot[1]); if (tot[select] == 0) { edge_boundary = true; @@ -1264,7 +1540,7 @@ static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool walker_select(em, BMW_EDGEBOUNDARY, eed, select); } else { - walker_select(em, BMW_LOOP, eed, select); + walker_select(em, BMW_EDGELOOP, eed, select); } } @@ -1286,7 +1562,7 @@ static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool de em = vc.em; /* no afterqueue (yet), so we check it now, otherwise the bm_xxxofs indices are bad */ - view3d_validate_backbuf(&vc); + ED_view3d_backbuf_validate(&vc); eed = EDBM_edge_find_nearest(&vc, &dist); if (eed == NULL) { @@ -2042,7 +2318,7 @@ bool EDBM_select_interior_faces(BMEditMesh *em) ok = true; BM_ITER_ELEM (eed, &eiter, efa, BM_EDGES_OF_FACE) { - if (BM_edge_face_count(eed) < 3) { + if (!BM_edge_face_count_is_over(eed, 2)) { ok = false; break; } @@ -2060,16 +2336,98 @@ bool EDBM_select_interior_faces(BMEditMesh *em) /************************ Select Linked Operator *************************/ -static void linked_limit_default(bContext *C, wmOperator *op) +struct DelimitData { + int cd_loop_type; + int cd_loop_offset; +}; + +static bool select_linked_delimit_test( + BMEdge *e, int delimit, + const struct DelimitData *delimit_data) { - if (!RNA_struct_property_is_set(op->ptr, "limit")) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->selectmode == SCE_SELECT_FACE) - RNA_boolean_set(op->ptr, "limit", true); - else - RNA_boolean_set(op->ptr, "limit", false); + BLI_assert(delimit); + + if (delimit & BMO_DELIM_SEAM) { + if (BM_elem_flag_test(e, BM_ELEM_SEAM)) { + return true; + } + } + + if (delimit & BMO_DELIM_SHARP) { + if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) { + return true; + } + } + + if (delimit & BMO_DELIM_NORMAL) { + if (!BM_edge_is_contiguous(e)) { + return true; + } } + + if (delimit & BMO_DELIM_MATERIAL) { + if (e->l && e->l->radial_next != e->l) { + const short mat_nr = e->l->f->mat_nr; + BMLoop *l_iter = e->l->radial_next; + do { + if (l_iter->f->mat_nr != mat_nr) { + return true; + } + } while ((l_iter = l_iter->radial_next) != e->l); + } + } + + if (delimit & BMO_DELIM_UV) { + if (BM_edge_is_contiguous_loop_cd(e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0) { + return true; + } + } + + return false; +} + +static void select_linked_delimit_begin(BMesh *bm, short selectmode, int delimit) +{ + struct DelimitData delimit_data = {0}; + + BMIter iter; + BMEdge *e; + + if (delimit & BMO_DELIM_UV) { + delimit_data.cd_loop_type = CD_MLOOPUV; + delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type); + if (delimit_data.cd_loop_offset == -1) { + delimit &= ~BMO_DELIM_UV; + } + } + + /* grr, shouldn't need to alloc BMO flags here */ + BM_mesh_elem_toolflags_ensure(bm); + if (selectmode == SCE_SELECT_FACE) { + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + const bool is_walk_ok = ( + (select_linked_delimit_test(e, delimit, &delimit_data) == false)); + + BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); + } + } + else { + /* don't delimit selected edges in vert/edge mode */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + const bool is_walk_ok = ( + BM_elem_flag_test(e, BM_ELEM_SELECT) || + (select_linked_delimit_test(e, delimit, &delimit_data) == false)); + + BMO_elem_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok); + } + } +} + +static void select_linked_delimit_end(BMEditMesh *em) +{ + BMesh *bm = em->bm; + + BM_mesh_elem_toolflags_clear(bm); } static int edbm_select_linked_exec(bContext *C, wmOperator *op) @@ -2078,72 +2436,139 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMIter iter; - BMEdge *e; BMWalker walker; - int limit; - - linked_limit_default(C, op); + const int delimit = RNA_enum_get(op->ptr, "delimit"); - limit = RNA_boolean_get(op->ptr, "limit"); + if (delimit) { + select_linked_delimit_begin(em->bm, em->selectmode, delimit); + } - if (em->selectmode == SCE_SELECT_FACE) { - BMFace *efa; + if (em->selectmode & SCE_SELECT_VERTEX) { + BMVert *v; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_elem_flag_set(efa, BM_ELEM_TAG, BM_elem_flag_test(efa, BM_ELEM_SELECT)); + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); } - if (limit) { - /* grr, shouldn't need to alloc BMO flags here */ - BM_mesh_elem_toolflags_ensure(bm); - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BMO_elem_flag_set(bm, e, BMO_ELE_TAG, !BM_elem_flag_test(e, BM_ELEM_SEAM)); + BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, v) { + if (ele_walk->head.htype == BM_LOOP) { + BMVert *v_step = ((BMLoop *)ele_walk)->v; + BM_vert_select_set(em->bm, v_step, true); + BM_elem_flag_disable(v_step, BM_ELEM_TAG); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(em->bm, e_step, true); + BM_elem_flag_disable(e_step->v1, BM_ELEM_TAG); + BM_elem_flag_disable(e_step->v2, BM_ELEM_TAG); + } + } + } + } + } + else { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_TAG)) { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, v) { + BM_edge_select_set(em->bm, e_walk, true); + BM_elem_flag_disable(e_walk, BM_ELEM_TAG); + } + } } } - BMW_init(&walker, bm, BMW_ISLAND, - BMW_MASK_NOP, limit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if (em->selectmode & SCE_SELECT_EDGE) { + BMEdge *e; + + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + } + + BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) { - BM_face_select_set(bm, efa, true); - BM_elem_flag_disable(efa, BM_ELEM_TAG); + if (delimit) { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, e) { + if (ele_walk->head.htype == BM_LOOP) { + BMLoop *l_step = (BMLoop *)ele_walk; + BM_edge_select_set(em->bm, l_step->e, true); + BM_edge_select_set(em->bm, l_step->prev->e, true); + BM_elem_flag_disable(l_step->e, BM_ELEM_TAG); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(em->bm, e_step, true); + BM_elem_flag_disable(e_step, BM_ELEM_TAG); + } + } } } } + else { + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, e) { + BM_edge_select_set(em->bm, e_walk, true); + BM_elem_flag_disable(e_walk, BM_ELEM_TAG); + } + } + } + } + BMW_end(&walker); - if (limit) { - BM_mesh_elem_toolflags_clear(bm); - } + EDBM_selectmode_flush(em); } else { - BMVert *v; + BMFace *f; - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT)); + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_set(f, BM_ELEM_TAG, BM_elem_flag_test(f, BM_ELEM_SELECT)); } - BMW_init(&walker, em->bm, BMW_VERT_SHELL, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, + BMW_init(&walker, bm, BMW_ISLAND, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, BMW_FLAG_TEST_HIDDEN, BMW_NIL_LAY); - BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_TAG)) { - for (e = BMW_begin(&walker, v); e; e = BMW_step(&walker)) { - BM_edge_select_set(em->bm, e, true); - BM_elem_flag_disable(e, BM_ELEM_TAG); + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG)) { + BMFace *f_walk; + BMW_ITER (f_walk, &walker, f) { + BM_face_select_set(bm, f_walk, true); + BM_elem_flag_disable(f_walk, BM_ELEM_TAG); } } } + BMW_end(&walker); + } - EDBM_selectmode_flush(em); + if (delimit) { + select_linked_delimit_end(em); } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); @@ -2165,99 +2590,209 @@ void MESH_OT_select_linked(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", ""); + RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit", + "Delimit selected region"); +} + +static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op); + +static void edbm_select_linked_pick_ex( + BMEditMesh *em, + BMVert *eve, BMEdge *eed, BMFace *efa, + bool sel, int delimit) +{ + BMesh *bm = em->bm; + BMWalker walker; + + if (delimit) { + select_linked_delimit_begin(bm, em->selectmode, delimit); + } + + /* Note: logic closely matches 'edbm_select_linked_exec', keep in sync */ + + if ((em->selectmode & SCE_SELECT_VERTEX) && eve) { + + BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, eve) { + if (ele_walk->head.htype == BM_LOOP) { + BMVert *v_step = ((BMLoop *)ele_walk)->v; + BM_vert_select_set(bm, v_step, sel); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(bm, e_step, sel); + } + } + } + else { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, eve) { + BM_edge_select_set(bm, e_walk, sel); + } + } + + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if ((em->selectmode & SCE_SELECT_EDGE) && eed) { + + BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + if (delimit) { + BMElem *ele_walk; + BMW_ITER (ele_walk, &walker, eed) { + if (ele_walk->head.htype == BM_LOOP) { + BMEdge *e_step = ((BMLoop *)ele_walk)->e; + BM_edge_select_set(bm, e_step, sel); + } + else { + BMEdge *e_step = (BMEdge *)ele_walk; + BLI_assert(ele_walk->head.htype == BM_EDGE); + BM_edge_select_set(bm, e_step, sel); + } + } + } + else { + BMEdge *e_walk; + BMW_ITER (e_walk, &walker, eed) { + BM_edge_select_set(bm, e_walk, sel); + } + } + + BMW_end(&walker); + + EDBM_selectmode_flush(em); + } + else if ((em->selectmode & SCE_SELECT_FACE) && efa) { + + BMW_init(&walker, bm, BMW_ISLAND, + BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, + BMW_FLAG_TEST_HIDDEN, + BMW_NIL_LAY); + + { + BMFace *f_walk; + BMW_ITER (f_walk, &walker, efa) { + BM_face_select_set(bm, f_walk, sel); + BM_elem_flag_disable(f_walk, BM_ELEM_TAG); + } + } + + BMW_end(&walker); + } + + if (delimit) { + select_linked_delimit_end(em); + } } static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { Object *obedit = CTX_data_edit_object(C); ViewContext vc; - BMesh *bm; - BMWalker walker; BMEditMesh *em; + BMesh *bm; BMVert *eve; - BMEdge *e, *eed; + BMEdge *eed; BMFace *efa; const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + const int delimit = RNA_enum_get(op->ptr, "delimit"); + int index; - int limit; - - linked_limit_default(C, op); - - limit = RNA_boolean_get(op->ptr, "limit"); + if (RNA_struct_property_is_set(op->ptr, "index")) { + return edbm_select_linked_pick_exec(C, op); + } /* unified_finednearest needs ogl */ view3d_operator_needs_opengl(C); - + /* setup view context for argument to callbacks */ em_setup_viewcontext(C, &vc); em = vc.em; + bm = em->bm; - if (em->bm->totedge == 0) + if (bm->totedge == 0) { return OPERATOR_CANCELLED; - - bm = em->bm; + } vc.mval[0] = event->mval[0]; vc.mval[1] = event->mval[1]; - + /* return warning! */ - if (unified_findnearest(&vc, &eve, &eed, &efa) == 0) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); - + return OPERATOR_CANCELLED; } - - if (em->selectmode == SCE_SELECT_FACE) { - BMIter iter; - - if (efa == NULL) - return OPERATOR_CANCELLED; - if (limit) { - /* grr, shouldn't need to alloc BMO flags here */ - BM_mesh_elem_toolflags_ensure(bm); - /* hflag no-seam --> bmo-tag */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - BMO_elem_flag_set(bm, e, BMO_ELE_TAG, !BM_elem_flag_test(e, BM_ELEM_SEAM)); - } - } + edbm_select_linked_pick_ex(em, eve, eed, efa, sel, delimit); - /* walk */ - BMW_init(&walker, bm, BMW_ISLAND, - BMW_MASK_NOP, limit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); - - for (efa = BMW_begin(&walker, efa); efa; efa = BMW_step(&walker)) { - BM_face_select_set(bm, efa, sel); - } - BMW_end(&walker); + /* to support redo */ + if ((em->selectmode & SCE_SELECT_VERTEX) && eve) { + BM_mesh_elem_index_ensure(bm, BM_VERT); + index = BM_elem_index_get(eve); + } + else if ((em->selectmode & SCE_SELECT_EDGE) && eed) { + BM_mesh_elem_index_ensure(bm, BM_EDGE); + index = BM_elem_index_get(eed) + bm->totvert; + } + else if ((em->selectmode & SCE_SELECT_FACE) && efa) { + BM_mesh_elem_index_ensure(bm, BM_FACE); + index = BM_elem_index_get(efa) + bm->totvert + bm->totedge; } else { - if (efa) { - eed = BM_FACE_FIRST_LOOP(efa)->e; - } - else if (!eed) { - if (!eve || !eve->e) - return OPERATOR_CANCELLED; + index = -1; + } - eed = eve->e; - } + RNA_int_set(op->ptr, "index", index); - BMW_init(&walker, bm, BMW_VERT_SHELL, - BMW_MASK_NOP, BMW_MASK_NOP, BMW_MASK_NOP, - BMW_FLAG_TEST_HIDDEN, - BMW_NIL_LAY); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); - for (e = BMW_begin(&walker, eed->v1); e; e = BMW_step(&walker)) { - BM_edge_select_set(bm, e, sel); - } - BMW_end(&walker); + return OPERATOR_FINISHED; +} - EDBM_selectmode_flush(em); + +static int edbm_select_linked_pick_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + int index; + BMVert *eve = NULL; + BMEdge *eed = NULL; + BMFace *efa = NULL; + const bool sel = !RNA_boolean_get(op->ptr, "deselect"); + const int delimit = RNA_enum_get(op->ptr, "delimit"); + + index = RNA_int_get(op->ptr, "index"); + if (index < 0 || index >= (bm->totvert + bm->totedge + bm->totface)) { + return OPERATOR_CANCELLED; + } + + if (index < bm->totvert) { + eve = BM_vert_at_index_find_or_table(bm, index); } + else if (index < (bm->totvert + bm->totedge)) { + index -= bm->totvert; + eed = BM_edge_at_index_find_or_table(bm, index); + } + else if (index < (bm->totvert + bm->totedge + bm->totface)) { + index -= (bm->totvert + bm->totedge); + efa = BM_face_at_index_find_or_table(bm, index); + } + + edbm_select_linked_pick_ex(em, eve, eed, efa, sel, delimit); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit); @@ -2266,6 +2801,8 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE void MESH_OT_select_linked_pick(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Select Linked"; ot->idname = "MESH_OT_select_linked_pick"; @@ -2273,13 +2810,19 @@ void MESH_OT_select_linked_pick(wmOperatorType *ot) /* api callbacks */ ot->invoke = edbm_select_linked_pick_invoke; + ot->exec = edbm_select_linked_pick_exec; ot->poll = ED_operator_editmesh; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", ""); - RNA_def_boolean(ot->srna, "limit", 0, "Limit by Seams", ""); + RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, BMO_DELIM_SEAM, "Delimit", + "Delimit selected region"); + + /* use for redo */ + prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -2552,7 +3095,7 @@ static bool bm_edge_is_select_isolated(BMEdge *e) /* 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". */ -static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h_act) +static void walker_deselect_nth(BMEditMesh *em, int nth, int skip, int offset, BMHeader *h_act) { BMElem *ele; BMesh *bm = em->bm; @@ -2618,7 +3161,8 @@ static void walker_deselect_nth(BMEditMesh *em, int nth, int offset, BMHeader *h for (ele = BMW_begin(&walker, h_act); ele != NULL; ele = BMW_step(&walker)) { if (!BM_elem_flag_test(ele, BM_ELEM_TAG)) { /* Deselect elements that aren't at "nth" depth from active */ - if ((offset + BMW_current_depth(&walker)) % nth) { + const int depth = BMW_current_depth(&walker) - 1; + if ((offset + depth) % (skip + nth) >= skip) { BM_elem_select_set(bm, ele, false); } BM_elem_flag_enable(ele, BM_ELEM_TAG); @@ -2685,7 +3229,7 @@ static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, } } -static bool edbm_deselect_nth(BMEditMesh *em, int nth, int offset) +static bool edbm_deselect_nth(BMEditMesh *em, int nth, int skip, int offset) { BMVert *v; BMEdge *e; @@ -2694,15 +3238,15 @@ static bool edbm_deselect_nth(BMEditMesh *em, int nth, int offset) deselect_nth_active(em, &v, &e, &f); if (v) { - walker_deselect_nth(em, nth, offset, &v->head); + walker_deselect_nth(em, nth, skip, offset, &v->head); return true; } else if (e) { - walker_deselect_nth(em, nth, offset, &e->head); + walker_deselect_nth(em, nth, skip, offset, &e->head); return true; } else if (f) { - walker_deselect_nth(em, nth, offset, &f->head); + walker_deselect_nth(em, nth, skip, offset, &f->head); return true; } @@ -2713,15 +3257,14 @@ static int edbm_select_nth_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int nth = RNA_int_get(op->ptr, "nth"); + const int nth = RNA_int_get(op->ptr, "nth") - 1; + const int skip = RNA_int_get(op->ptr, "skip"); int offset = RNA_int_get(op->ptr, "offset"); /* 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; + offset = mod_i(offset, nth + skip); - if (edbm_deselect_nth(em, nth, offset) == false) { + if (edbm_deselect_nth(em, nth, skip, offset) == false) { BKE_report(op->reports, RPT_ERROR, "Mesh has no active vert/edge/face"); return OPERATOR_CANCELLED; } @@ -2747,6 +3290,7 @@ void MESH_OT_select_nth(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; RNA_def_int(ot->srna, "nth", 2, 2, INT_MAX, "Nth Selection", "", 2, 100); + RNA_def_int(ot->srna, "skip", 1, 1, INT_MAX, "Skip", "", 1, 100); RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "", -100, 100); } @@ -2937,7 +3481,7 @@ static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op) 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))) + (use_multi_face && (BM_edge_face_count_is_over(e, 2)))) { /* check we never select perfect edge (in test above) */ BLI_assert(!(BM_edge_is_manifold(e) && BM_edge_is_contiguous(e))); @@ -3047,8 +3591,8 @@ void MESH_OT_select_random(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, - "Percent", "Percentage of elements to select randomly", 0.f, 100.0f); + RNA_def_float_percentage(ot->srna, "percent", 50.0f, 0.0f, 100.0f, + "Percent", "Percentage of elements to select randomly", 0.0f, 100.0f); WM_operator_properties_select_action_simple(ot, SEL_SELECT); } @@ -3136,7 +3680,7 @@ static int edbm_select_axis_exec(bContext *C, wmOperator *op) else { BMVert *v; BMIter iter; - const float limit = CTX_data_tool_settings(C)->doublimit; // XXX + const float limit = RNA_float_get(op->ptr, "threshold"); float value = v_act->co[axis]; if (mode == 0) @@ -3201,6 +3745,7 @@ void MESH_OT_select_axis(wmOperatorType *ot) /* properties */ RNA_def_enum(ot->srna, "mode", axis_mode_items, 0, "Axis Mode", "Axis side to use when selecting"); RNA_def_enum(ot->srna, "axis", axis_items_xyz, 0, "Axis", "Select the axis to compare each vertex on"); + RNA_def_float(ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001f, 10.0f); } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 93ebbbcf620..2b437c02aef 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -96,7 +96,7 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) } BM_mesh_esubdivide(em->bm, BM_ELEM_SELECT, - smooth, SUBD_FALLOFF_ROOT, false, + smooth, SUBD_FALLOFF_INVSQUARE, false, fractal, along_normal, cuts, SUBDIV_SELECT_ORIG, RNA_enum_get(op->ptr, "quadcorner"), @@ -134,19 +134,20 @@ void MESH_OT_subdivide(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10); + prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10); /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, FLT_MAX, "Smoothness", "Smoothness factor", 0.0f, 1.0f); + RNA_def_float(ot->srna, "smoothness", 0.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 1.0f); RNA_def_boolean(ot->srna, "quadtri", 0, "Quad/Tri Mode", "Tries to prevent ngons"); RNA_def_enum(ot->srna, "quadcorner", prop_mesh_cornervert_types, SUBD_CORNER_STRAIGHT_CUT, "Quad Corner Type", "How to subdivide quad corners (anything other than Straight Cut will prevent ngons)"); - RNA_def_float(ot->srna, "fractal", 0.0f, 0.0f, FLT_MAX, "Fractal", "Fractal randomness factor", 0.0f, 1000.0f); - RNA_def_float(ot->srna, "fractal_along_normal", 0.0f, 0.0f, 1.0f, "Along Normal", "Apply fractal displacement along normal only", 0.0f, 1.0f); - RNA_def_int(ot->srna, "seed", 0, 0, 10000, "Random Seed", "Seed for the random number generator", 0, 50); + RNA_def_float(ot->srna, "fractal", 0.0f, 0.0f, 1e6f, "Fractal", "Fractal randomness factor", 0.0f, 1000.0f); + RNA_def_float(ot->srna, "fractal_along_normal", 0.0f, 0.0f, 1.0f, + "Along Normal", "Apply fractal displacement along normal only", 0.0f, 1.0f); + RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Random Seed", "Seed for the random number generator", 0, 255); } /* -------------------------------------------------------------------- */ @@ -176,18 +177,18 @@ static void mesh_operator_edgering_props(wmOperatorType *ot, const int cuts_defa PropertyRNA *prop; - prop = RNA_def_int(ot->srna, "number_cuts", cuts_default, 0, INT_MAX, "Number of Cuts", "", 0, 64); + prop = RNA_def_int(ot->srna, "number_cuts", cuts_default, 0, 1000, "Number of Cuts", "", 0, 64); RNA_def_property_flag(prop, PROP_SKIP_SAVE); RNA_def_enum(ot->srna, "interpolation", prop_subd_edgering_types, SUBD_RING_INTERP_PATH, "Interpolation", "Interpolation method"); - RNA_def_float(ot->srna, "smoothness", 1.0f, 0.0f, FLT_MAX, + RNA_def_float(ot->srna, "smoothness", 1.0f, 0.0f, 1e3f, "Smoothness", "Smoothness factor", 0.0f, 2.0f); /* profile-shape */ - RNA_def_float(ot->srna, "profile_shape_factor", 0.0f, -FLT_MAX, FLT_MAX, - "Profile Factor", "", -2.0f, 2.0f); + RNA_def_float(ot->srna, "profile_shape_factor", 0.0f, -1e3f, 1e3f, + "Profile Factor", "How much intermediary new edges are shrunk/expanded", -2.0f, 2.0f); prop = RNA_def_property(ot->srna, "profile_shape", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, proportional_falloff_curve_only_items); @@ -289,7 +290,7 @@ void MESH_OT_unsubdivide(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_int(ot->srna, "iterations", 2, 1, INT_MAX, "Iterations", "Number of times to unsubdivide", 1, 100); + RNA_def_int(ot->srna, "iterations", 2, 1, 1000, "Iterations", "Number of times to unsubdivide", 1, 100); } void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) @@ -313,6 +314,16 @@ void EMBM_project_snap_verts(bContext *C, ARegion *ar, BMEditMesh *em) } } + +/* Note, these values must match delete_mesh() event values */ +enum { + MESH_DELETE_VERT = 0, + MESH_DELETE_EDGE = 1, + MESH_DELETE_FACE = 2, + MESH_DELETE_EDGE_FACE = 3, + MESH_DELETE_ONLY_FACE = 4, +}; + static void edbm_report_delete_info(ReportList *reports, BMesh *bm, const int totelem[3]) { BKE_reportf(reports, RPT_INFO, @@ -320,45 +331,38 @@ static void edbm_report_delete_info(ReportList *reports, BMesh *bm, const int to totelem[0] - bm->totvert, totelem[1] - bm->totedge, totelem[2] - bm->totface); } -/* Note, these values must match delete_mesh() event values */ -static EnumPropertyItem prop_mesh_delete_types[] = { - {0, "VERT", 0, "Vertices", ""}, - {1, "EDGE", 0, "Edges", ""}, - {2, "FACE", 0, "Faces", ""}, - {3, "EDGE_FACE", 0, "Only Edges & Faces", ""}, - {4, "ONLY_FACE", 0, "Only Faces", ""}, - {0, NULL, 0, NULL, NULL} -}; - static int edbm_delete_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); - if (type == 0) { - if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) /* Erase Vertices */ - return OPERATOR_CANCELLED; - } - else if (type == 1) { - if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) /* Erase Edges */ - return OPERATOR_CANCELLED; - } - else if (type == 2) { - if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) /* Erase Faces */ - return OPERATOR_CANCELLED; - } - else if (type == 3) { - if (!EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) /* Edges and Faces */ - return OPERATOR_CANCELLED; - } - else if (type == 4) { - //"Erase Only Faces"; - if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", - BM_ELEM_SELECT, DEL_ONLYFACES)) - { - return OPERATOR_CANCELLED; - } + switch (type) { + case MESH_DELETE_VERT: + if (!EDBM_op_callf(em, op, "delete geom=%hv context=%i", BM_ELEM_SELECT, DEL_VERTS)) /* Erase Vertices */ + return OPERATOR_CANCELLED; + break; + case MESH_DELETE_EDGE: + if (!EDBM_op_callf(em, op, "delete geom=%he context=%i", BM_ELEM_SELECT, DEL_EDGES)) /* Erase Edges */ + return OPERATOR_CANCELLED; + break; + case MESH_DELETE_FACE: + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_FACES)) /* Erase Faces */ + return OPERATOR_CANCELLED; + break; + case MESH_DELETE_EDGE_FACE: + /* Edges and Faces */ + if (!EDBM_op_callf(em, op, "delete geom=%hef context=%i", BM_ELEM_SELECT, DEL_EDGESFACES)) + return OPERATOR_CANCELLED; + break; + case MESH_DELETE_ONLY_FACE: + /* Only faces. */ + if (!EDBM_op_callf(em, op, "delete geom=%hf context=%i", BM_ELEM_SELECT, DEL_ONLYFACES)) + return OPERATOR_CANCELLED; + break; + default: + BLI_assert(0); + break; } EDBM_flag_disable_all(em, BM_ELEM_SELECT); @@ -370,6 +374,15 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) void MESH_OT_delete(wmOperatorType *ot) { + static EnumPropertyItem prop_mesh_delete_types[] = { + {MESH_DELETE_VERT, "VERT", 0, "Vertices", ""}, + {MESH_DELETE_EDGE, "EDGE", 0, "Edges", ""}, + {MESH_DELETE_FACE, "FACE", 0, "Faces", ""}, + {MESH_DELETE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""}, + {MESH_DELETE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Delete"; ot->description = "Delete selected vertices, edges or faces"; @@ -385,7 +398,8 @@ void MESH_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - ot->prop = RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, 0, "Type", "Method used for deleting mesh data"); + ot->prop = RNA_def_enum(ot->srna, "type", prop_mesh_delete_types, MESH_DELETE_VERT, + "Type", "Method used for deleting mesh data"); } @@ -492,7 +506,7 @@ static int edbm_collapse_edge_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (!EDBM_op_callf(em, op, "collapse edges=%he", BM_ELEM_SELECT)) + if (!EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, true)) return OPERATOR_CANCELLED; EDBM_update_generic(em, true, true); @@ -1109,28 +1123,111 @@ static bool bm_vert_connect_select_history(BMesh *bm) return false; } +/** + * Convert an edge selection to a temp vertex selection + * (which must be cleared after use as a path to connect). + */ +static bool bm_vert_connect_select_history_edge_to_vert_path(BMesh *bm, ListBase *r_selected) +{ + ListBase selected_orig = {NULL, NULL}; + BMEditSelection *ese; + int edges_len = 0; + bool side = false; + + /* first check all edges are OK */ + for (ese = bm->selected.first; ese; ese = ese->next) { + if (ese->htype == BM_EDGE) { + edges_len += 1; + } + else { + return false; + } + } + /* if this is a mixed selection, bail out! */ + if (bm->totedgesel != edges_len) { + return false; + } + + SWAP(ListBase, bm->selected, selected_orig); + + /* convert edge selection into 2 ordered loops (where the first edge ends up in the middle) */ + for (ese = selected_orig.first; ese; ese = ese->next) { + BMEdge *e_curr = (BMEdge *)ese->ele; + BMEdge *e_prev = ese->prev ? (BMEdge *)ese->prev->ele : NULL; + BMLoop *l_curr; + BMLoop *l_prev; + BMVert *v; + + if (e_prev) { + BMFace *f = BM_edge_pair_share_face_by_len(e_curr, e_prev, &l_curr, &l_prev, true); + if (f) { + if ((e_curr->v1 != l_curr->v) == (e_prev->v1 != l_prev->v)) { + side = !side; + } + } + else if (is_quad_flip_v3(e_curr->v1->co, e_curr->v2->co, e_prev->v2->co, e_prev->v1->co)) { + side = !side; + } + } + + v = (&e_curr->v1)[side]; + if (!bm->selected.last || (BMVert *)((BMEditSelection *)bm->selected.last)->ele != v) { + BM_select_history_store_notest(bm, v); + } + + v = (&e_curr->v1)[!side]; + if (!bm->selected.first || (BMVert *)((BMEditSelection *)bm->selected.first)->ele != v) { + BM_select_history_store_head_notest(bm, v); + } + + e_prev = e_curr; + } + + *r_selected = bm->selected; + bm->selected = selected_orig; + + return true; +} static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; bool is_pair = (em->bm->totvertsel == 2); + ListBase selected_orig = {NULL, NULL}; + int retval; /* when there is only 2 vertices, we can ignore selection order */ if (is_pair) { return edbm_vert_connect_exec(C, op); } - if (bm_vert_connect_select_history(em->bm)) { + if (bm->selected.first) { + BMEditSelection *ese = bm->selected.first; + if (ese->htype == BM_EDGE) { + if (bm_vert_connect_select_history_edge_to_vert_path(bm, &selected_orig)) { + SWAP(ListBase, bm->selected, selected_orig); + } + } + } + + if (bm_vert_connect_select_history(bm)) { EDBM_selectmode_flush(em); EDBM_update_generic(em, true, true); - - return OPERATOR_FINISHED; + retval = OPERATOR_FINISHED; } else { BKE_report(op->reports, RPT_ERROR, "Invalid selection order"); - return OPERATOR_CANCELLED; + retval = OPERATOR_CANCELLED; } + + if (!BLI_listbase_is_empty(&selected_orig)) { + BM_select_history_clear(bm); + bm->selected = selected_orig; + } + + return retval; } void MESH_OT_vert_connect_path(wmOperatorType *ot) @@ -1226,6 +1323,43 @@ void MESH_OT_vert_connect_nonplanar(wmOperatorType *ot) RNA_def_property_float_default(prop, DEG2RADF(5.0f)); } +static int edbm_face_make_planar_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int repeat = RNA_int_get(op->ptr, "repeat"); + const float fac = RNA_float_get(op->ptr, "factor"); + + if (!EDBM_op_callf( + em, op, "planar_faces faces=%hf iterations=%i factor=%f", + BM_ELEM_SELECT, repeat, fac)) + { + return OPERATOR_CANCELLED; + } + + EDBM_update_generic(em, true, true); + return OPERATOR_FINISHED; +} + +void MESH_OT_face_make_planar(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Make Planar Faces"; + ot->idname = "MESH_OT_face_make_planar"; + ot->description = "Flatten selected faces"; + + /* api callbacks */ + ot->exec = edbm_face_make_planar_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* props */ + RNA_def_float(ot->srna, "factor", 1.0f, -10.0f, 10.0f, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); +} + static int edbm_edge_split_exec(bContext *C, wmOperator *op) { @@ -1460,7 +1594,7 @@ void MESH_OT_hide(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); + RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected"); } static int edbm_reveal_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1522,7 +1656,7 @@ void MESH_OT_normals_make_consistent(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "inside", 0, "Inside", ""); + RNA_def_boolean(ot->srna, "inside", false, "Inside", ""); } @@ -1609,9 +1743,9 @@ void MESH_OT_vertices_smooth(wmOperatorType *ot) 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"); + RNA_def_boolean(ot->srna, "xaxis", true, "X-Axis", "Smooth along the X axis"); + RNA_def_boolean(ot->srna, "yaxis", true, "Y-Axis", "Smooth along the Y axis"); + RNA_def_boolean(ot->srna, "zaxis", true, "Z-Axis", "Smooth along the Z axis"); } static int edbm_do_smooth_laplacian_vertex_exec(bContext *C, wmOperator *op) @@ -1686,16 +1820,16 @@ void MESH_OT_vertices_smooth_laplacian(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int(ot->srna, "repeat", 1, 1, 200, + RNA_def_int(ot->srna, "repeat", 1, 1, 1000, "Number of iterations to smooth the mesh", "", 1, 200); - RNA_def_float(ot->srna, "lambda_factor", 0.00005f, 0.0000001f, 1000.0f, - "Lambda factor", "", 0.0000001f, 1000.0f); - RNA_def_float(ot->srna, "lambda_border", 0.00005f, 0.0000001f, 1000.0f, - "Lambda factor in border", "", 0.0000001f, 1000.0f); - RNA_def_boolean(ot->srna, "use_x", 1, "Smooth X Axis", "Smooth object along X axis"); - RNA_def_boolean(ot->srna, "use_y", 1, "Smooth Y Axis", "Smooth object along Y axis"); - RNA_def_boolean(ot->srna, "use_z", 1, "Smooth Z Axis", "Smooth object along Z axis"); - RNA_def_boolean(ot->srna, "preserve_volume", 1, "Preserve Volume", "Apply volume preservation after smooth"); + RNA_def_float(ot->srna, "lambda_factor", 5e-5f, 1e-7f, 1000.0f, + "Lambda factor", "", 1e-7f, 1000.0f); + RNA_def_float(ot->srna, "lambda_border", 5e-5f, 1e-7f, 1000.0f, + "Lambda factor in border", "", 1e-7f, 1000.0f); + RNA_def_boolean(ot->srna, "use_x", true, "Smooth X Axis", "Smooth object along X axis"); + RNA_def_boolean(ot->srna, "use_y", true, "Smooth Y Axis", "Smooth object along Y axis"); + RNA_def_boolean(ot->srna, "use_z", true, "Smooth Z Axis", "Smooth object along Z axis"); + RNA_def_boolean(ot->srna, "preserve_volume", true, "Preserve Volume", "Apply volume preservation after smooth"); } /********************** Smooth/Solid Operators *************************/ @@ -1940,6 +2074,14 @@ void MESH_OT_colors_reverse(wmOperatorType *ot) } +enum { + MESH_MERGE_LAST = 1, + MESH_MERGE_CENTER = 3, + MESH_MERGE_CURSOR = 4, + MESH_MERGE_COLLAPSE = 5, + MESH_MERGE_FIRST = 6, +}; + static bool merge_firstlast(BMEditMesh *em, const bool use_first, const bool use_uvmerge, wmOperator *wmop) { BMVert *mergevert; @@ -2036,22 +2178,20 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) bool ok = false; switch (type) { - case 3: + case MESH_MERGE_CENTER: ok = merge_target(em, scene, v3d, obedit, false, uvs, op); break; - case 4: + case MESH_MERGE_CURSOR: ok = merge_target(em, scene, v3d, obedit, true, uvs, op); break; - case 1: + case MESH_MERGE_LAST: ok = merge_firstlast(em, false, uvs, op); break; - case 6: + case MESH_MERGE_FIRST: ok = merge_firstlast(em, true, uvs, op); break; - case 5: - ok = true; - if (!EDBM_op_callf(em, op, "collapse edges=%he", BM_ELEM_SELECT)) - ok = false; + case MESH_MERGE_COLLAPSE: + ok = EDBM_op_callf(em, op, "collapse edges=%he uvs=%b", BM_ELEM_SELECT, uvs); break; default: BLI_assert(0); @@ -2068,11 +2208,11 @@ static int edbm_merge_exec(bContext *C, wmOperator *op) } static EnumPropertyItem merge_type_items[] = { - {6, "FIRST", 0, "At First", ""}, - {1, "LAST", 0, "At Last", ""}, - {3, "CENTER", 0, "At Center", ""}, - {4, "CURSOR", 0, "At Cursor", ""}, - {5, "COLLAPSE", 0, "Collapse", ""}, + {MESH_MERGE_FIRST, "FIRST", 0, "At First", ""}, + {MESH_MERGE_LAST, "LAST", 0, "At Last", ""}, + {MESH_MERGE_CENTER, "CENTER", 0, "At Center", ""}, + {MESH_MERGE_CURSOR, "CURSOR", 0, "At Cursor", ""}, + {MESH_MERGE_COLLAPSE, "COLLAPSE", 0, "Collapse", ""}, {0, NULL, 0, NULL, NULL} }; @@ -2094,20 +2234,20 @@ static EnumPropertyItem *merge_type_itemf(bContext *C, PointerRNA *UNUSED(ptr), ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT && ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT) { - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6); - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_FIRST); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_LAST); } else if (em->bm->selected.first && ((BMEditSelection *)em->bm->selected.first)->htype == BM_VERT) { - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 6); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_FIRST); } else if (em->bm->selected.last && ((BMEditSelection *)em->bm->selected.last)->htype == BM_VERT) { - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 1); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_LAST); } } - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 3); - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 4); - RNA_enum_items_add_value(&item, &totitem, merge_type_items, 5); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_CENTER); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_CURSOR); + RNA_enum_items_add_value(&item, &totitem, merge_type_items, MESH_MERGE_COLLAPSE); RNA_enum_item_end(&item, &totitem); *r_free = true; @@ -2134,9 +2274,9 @@ void MESH_OT_merge(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", merge_type_items, 3, "Type", "Merge method to use"); + ot->prop = RNA_def_enum(ot->srna, "type", merge_type_items, MESH_MERGE_CENTER, "Type", "Merge method to use"); RNA_def_enum_funcs(ot->prop, merge_type_itemf); - RNA_def_boolean(ot->srna, "uvs", 0, "UVs", "Move UVs according to merge"); + RNA_def_boolean(ot->srna, "uvs", false, "UVs", "Move UVs according to merge"); } @@ -2212,9 +2352,9 @@ void MESH_OT_remove_doubles(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Merge Distance", - "Minimum distance between elements to merge", 0.00001, 10.0); - RNA_def_boolean(ot->srna, "use_unselected", 0, "Unselected", "Merge selected to other unselected vertices"); + RNA_def_float(ot->srna, "threshold", 1e-4f, 1e-6f, 50.0f, "Merge Distance", + "Minimum distance between elements to merge", 1e-5f, 10.0f); + RNA_def_boolean(ot->srna, "use_unselected", false, "Unselected", "Merge selected to other unselected vertices"); } @@ -2408,9 +2548,9 @@ void MESH_OT_blend_from_shape(wmOperatorType *ot) /* properties */ prop = RNA_def_enum(ot->srna, "shape", DummyRNA_NULL_items, 0, "Shape", "Shape key to use for blending"); RNA_def_enum_funcs(prop, shape_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - RNA_def_float(ot->srna, "blend", 1.0f, -FLT_MAX, FLT_MAX, "Blend", "Blending factor", -2.0f, 2.0f); - RNA_def_boolean(ot->srna, "add", 1, "Add", "Add rather than blend between shapes"); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE | PROP_NEVER_UNLINK); + RNA_def_float(ot->srna, "blend", 1.0f, -1e3f, 1e3f, "Blend", "Blending factor", -2.0f, 2.0f); + RNA_def_boolean(ot->srna, "add", true, "Add", "Add rather than blend between shapes"); } static int edbm_solidify_exec(bContext *C, wmOperator *op) @@ -2463,8 +2603,8 @@ void MESH_OT_solidify(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_float(ot->srna, "thickness", 0.01f, -FLT_MAX, FLT_MAX, "thickness", "", -10.0f, 10.0f); - RNA_def_property_ui_range(prop, -10, 10, 0.1, 4); + prop = RNA_def_float(ot->srna, "thickness", 0.01f, -1e4f, 1e4f, "Thickness", "", -10.0f, 10.0f); + RNA_def_property_ui_range(prop, -10.0, 10.0, 0.1, 4); } /* ******************************************************************** */ @@ -2797,9 +2937,18 @@ void MESH_OT_knife_cut(wmOperatorType *ot) RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath); /* internal */ - RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX); + RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, BC_NUMCURSORS, "Cursor", "", 0, BC_NUMCURSORS); } + +/* *************** Operator: separate parts *************/ + +enum { + MESH_SEPARATE_SELECTED = 0, + MESH_SEPARATE_MATERIAL = 1, + MESH_SEPARATE_LOOSE = 2, +}; + static Base *mesh_separate_tagged(Main *bmain, Scene *scene, Base *base_old, BMesh *bm_old) { Base *base_new; @@ -3083,17 +3232,27 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) } /* editmode separate */ - if (type == 0) retval = mesh_separate_selected(bmain, scene, base, em->bm); - else if (type == 1) retval = mesh_separate_material(bmain, scene, base, em->bm); - else if (type == 2) retval = mesh_separate_loose(bmain, scene, base, em->bm); - else BLI_assert(0); + switch (type) { + case MESH_SEPARATE_SELECTED: + retval = mesh_separate_selected(bmain, scene, base, em->bm); + break; + case MESH_SEPARATE_MATERIAL: + retval = mesh_separate_material(bmain, scene, base, em->bm); + break; + case MESH_SEPARATE_LOOSE: + retval = mesh_separate_loose(bmain, scene, base, em->bm); + break; + default: + BLI_assert(0); + break; + } if (retval) { EDBM_update_generic(em, true, true); } } else { - if (type == 0) { + if (type == MESH_SEPARATE_SELECTED) { BKE_report(op->reports, RPT_ERROR, "Selection not supported in object mode"); return OPERATOR_CANCELLED; } @@ -3112,9 +3271,17 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) BM_mesh_bm_from_me(bm_old, me, false, false, 0); - if (type == 1) retval_iter = mesh_separate_material(bmain, scene, base_iter, bm_old); - else if (type == 2) retval_iter = mesh_separate_loose(bmain, scene, base_iter, bm_old); - else BLI_assert(0); + switch (type) { + case MESH_SEPARATE_MATERIAL: + retval_iter = mesh_separate_material(bmain, scene, base_iter, bm_old); + break; + case MESH_SEPARATE_LOOSE: + retval_iter = mesh_separate_loose(bmain, scene, base_iter, bm_old); + break; + default: + BLI_assert(0); + break; + } if (retval_iter) { BM_mesh_bm_to_me(bm_old, me, false); @@ -3143,17 +3310,15 @@ static int edbm_separate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } -/* *************** Operator: separate parts *************/ - -static EnumPropertyItem prop_separate_types[] = { - {0, "SELECTED", 0, "Selection", ""}, - {1, "MATERIAL", 0, "By Material", ""}, - {2, "LOOSE", 0, "By loose parts", ""}, - {0, NULL, 0, NULL, NULL} -}; - void MESH_OT_separate(wmOperatorType *ot) { + static EnumPropertyItem prop_separate_types[] = { + {MESH_SEPARATE_SELECTED, "SELECTED", 0, "Selection", ""}, + {MESH_SEPARATE_MATERIAL, "MATERIAL", 0, "By Material", ""}, + {MESH_SEPARATE_LOOSE, "LOOSE", 0, "By loose parts", ""}, + {0, NULL, 0, NULL, NULL} + }; + /* identifiers */ ot->name = "Separate"; ot->description = "Separate selected geometry into a new mesh"; @@ -3167,7 +3332,7 @@ void MESH_OT_separate(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO; - ot->prop = RNA_def_enum(ot->srna, "type", prop_separate_types, 0, "Type", ""); + ot->prop = RNA_def_enum(ot->srna, "type", prop_separate_types, MESH_SEPARATE_SELECTED, "Type", ""); } @@ -3460,11 +3625,11 @@ void MESH_OT_fill_grid(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_int(ot->srna, "span", 1, 1, INT_MAX, "Span", "Number of sides (zero disables)", 1, 100); + prop = RNA_def_int(ot->srna, "span", 1, 1, 1000, "Span", "Number of sides (zero disables)", 1, 100); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "Number of sides (zero disables)", -100, 100); + prop = RNA_def_int(ot->srna, "offset", 0, -1000, 1000, "Offset", "Number of sides (zero disables)", -100, 100); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - RNA_def_boolean(ot->srna, "use_interp_simple", 0, "Simple Blending", ""); + RNA_def_boolean(ot->srna, "use_interp_simple", false, "Simple Blending", ""); } static int edbm_fill_holes_exec(bContext *C, wmOperator *op) @@ -3502,7 +3667,8 @@ void MESH_OT_fill_holes(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int(ot->srna, "sides", 4, 0, INT_MAX, "Sides", "Number of sides in hole required to fill (zero fills all holes)", 0, 100); + RNA_def_int(ot->srna, "sides", 4, 0, 1000, + "Sides", "Number of sides in hole required to fill (zero fills all holes)", 0, 100); } static int edbm_beautify_fill_exec(bContext *C, wmOperator *op) @@ -3622,9 +3788,10 @@ void MESH_OT_poke(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Poke Offset", "Poke Offset", -1.0f, 1.0f); + RNA_def_float(ot->srna, "offset", 0.0f, -1e3f, 1e3f, "Poke Offset", "Poke Offset", -1.0f, 1.0f); RNA_def_boolean(ot->srna, "use_relative_offset", false, "Offset Relative", "Scale the offset by surrounding geometry"); - RNA_def_enum(ot->srna, "center_mode", poke_center_modes, BMOP_POKE_MEAN_WEIGHTED, "Poke Center", "Poke Face Center Calculation"); + RNA_def_enum(ot->srna, "center_mode", poke_center_modes, BMOP_POKE_MEAN_WEIGHTED, + "Poke Center", "Poke Face Center Calculation"); } /********************** Quad/Tri Operators *************************/ @@ -3678,19 +3845,45 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); - int dosharp, douvs, dovcols, domaterials; - const float limit = RNA_float_get(op->ptr, "limit"); + bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; + float angle_face_threshold, angle_shape_threshold; + PropertyRNA *prop; + + /* When joining exactly 2 faces, no limit. + * this is useful for one off joins while editing. */ + prop = RNA_struct_find_property(op->ptr, "face_threshold"); + if ((em->bm->totfacesel == 2) && + (RNA_property_is_set(op->ptr, prop) == false)) + { + angle_face_threshold = DEG2RADF(180.0f); + } + else { + angle_face_threshold = RNA_property_float_get(op->ptr, prop); + } - dosharp = RNA_boolean_get(op->ptr, "sharp"); - douvs = RNA_boolean_get(op->ptr, "uvs"); - dovcols = RNA_boolean_get(op->ptr, "vcols"); - domaterials = RNA_boolean_get(op->ptr, "materials"); + prop = RNA_struct_find_property(op->ptr, "shape_threshold"); + if ((em->bm->totfacesel == 2) && + (RNA_property_is_set(op->ptr, prop) == false)) + { + angle_shape_threshold = DEG2RADF(180.0f); + } + else { + angle_shape_threshold = RNA_property_float_get(op->ptr, prop); + } + + do_seam = RNA_boolean_get(op->ptr, "seam"); + do_sharp = RNA_boolean_get(op->ptr, "sharp"); + do_uvs = RNA_boolean_get(op->ptr, "uvs"); + do_vcols = RNA_boolean_get(op->ptr, "vcols"); + do_materials = RNA_boolean_get(op->ptr, "materials"); if (!EDBM_op_call_and_selectf( em, op, "faces.out", true, - "join_triangles faces=%hf limit=%f cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b", - BM_ELEM_SELECT, limit, dosharp, douvs, dovcols, domaterials)) + "join_triangles faces=%hf angle_face_threshold=%f angle_shape_threshold=%f " + "cmp_seam=%b cmp_sharp=%b cmp_uvs=%b cmp_vcols=%b cmp_materials=%b", + BM_ELEM_SELECT, angle_face_threshold, angle_shape_threshold, + do_seam, do_sharp, do_uvs, do_vcols, do_materials)) { return OPERATOR_CANCELLED; } @@ -3704,14 +3897,21 @@ static void join_triangle_props(wmOperatorType *ot) { PropertyRNA *prop; - prop = RNA_def_float_rotation(ot->srna, "limit", 0, NULL, 0.0f, DEG2RADF(180.0f), - "Max Angle", "Angle Limit", 0.0f, DEG2RADF(180.0f)); + prop = RNA_def_float_rotation( + ot->srna, "face_threshold", 0, NULL, 0.0f, DEG2RADF(180.0f), + "Max Face Angle", "Face angle limit", 0.0f, DEG2RADF(180.0f)); + RNA_def_property_float_default(prop, DEG2RADF(40.0f)); + + prop = RNA_def_float_rotation( + ot->srna, "shape_threshold", 0, NULL, 0.0f, DEG2RADF(180.0f), + "Max Shape Angle", "Shape angle limit", 0.0f, DEG2RADF(180.0f)); RNA_def_property_float_default(prop, DEG2RADF(40.0f)); - RNA_def_boolean(ot->srna, "uvs", 0, "Compare UVs", ""); - RNA_def_boolean(ot->srna, "vcols", 0, "Compare VCols", ""); - RNA_def_boolean(ot->srna, "sharp", 0, "Compare Sharp", ""); - RNA_def_boolean(ot->srna, "materials", 0, "Compare Materials", ""); + RNA_def_boolean(ot->srna, "uvs", false, "Compare UVs", ""); + RNA_def_boolean(ot->srna, "vcols", false, "Compare VCols", ""); + RNA_def_boolean(ot->srna, "seam", false, "Compare Seam", ""); + RNA_def_boolean(ot->srna, "sharp", false, "Compare Sharp", ""); + RNA_def_boolean(ot->srna, "materials", false, "Compare Materials", ""); } void MESH_OT_tris_convert_to_quads(wmOperatorType *ot) @@ -3747,12 +3947,12 @@ static void edbm_dissolve_prop__use_verts(wmOperatorType *ot, bool value, int fl } static void edbm_dissolve_prop__use_face_split(wmOperatorType *ot) { - RNA_def_boolean(ot->srna, "use_face_split", 0, "Face Split", + RNA_def_boolean(ot->srna, "use_face_split", false, "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", + RNA_def_boolean(ot->srna, "use_boundary_tear", false, "Tear Boundary", "Split off face corners instead of merging faces"); } @@ -3986,9 +4186,9 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot) prop = RNA_def_float_rotation(ot->srna, "angle_limit", 0, NULL, 0.0f, DEG2RADF(180.0f), "Max Angle", "Angle limit", 0.0f, DEG2RADF(180.0f)); RNA_def_property_float_default(prop, DEG2RADF(5.0f)); - RNA_def_boolean(ot->srna, "use_dissolve_boundaries", 0, "All Boundaries", + RNA_def_boolean(ot->srna, "use_dissolve_boundaries", false, "All Boundaries", "Dissolve all vertices inbetween face boundaries"); - RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, 0, "Delimit", + RNA_def_enum_flag(ot->srna, "delimit", mesh_delimit_mode_items, BMO_DELIM_NORMAL, "Delimit", "Delimit dissolve operation"); } @@ -4032,8 +4232,8 @@ void MESH_OT_dissolve_degenerate(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Merge Distance", - "Minimum distance between elements to merge", 0.00001, 10.0); + RNA_def_float(ot->srna, "threshold", 1e-4f, 1e-6f, 50.0f, "Merge Distance", + "Minimum distance between elements to merge", 1e-5f, 10.0f); } @@ -4071,6 +4271,10 @@ static int edbm_delete_edgeloop_exec(bContext *C, wmOperator *op) BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); + if (em->selectmode & SCE_SELECT_VERTEX) { + EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX); + } + EDBM_update_generic(em, true, true); return OPERATOR_FINISHED; @@ -4710,8 +4914,9 @@ void MESH_OT_sort_elements(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", "Type of re-ordering operation to apply"); - RNA_def_enum_flag(ot->srna, "elements", elem_items, 0, "Elements", + ot->prop = RNA_def_enum(ot->srna, "type", type_items, SRT_VIEW_ZAXIS, + "Type", "Type of re-ordering operation to apply"); + RNA_def_enum_flag(ot->srna, "elements", elem_items, BM_VERT, "Elements", "Which elements to affect (vertices, edges and/or faces)"); RNA_def_boolean(ot->srna, "reverse", false, "Reverse", "Reverse the sorting effect"); RNA_def_int(ot->srna, "seed", 0, 0, INT_MAX, "Seed", "Seed for random-based operations", 0, 255); @@ -4786,10 +4991,16 @@ void MESH_OT_noise(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_float(ot->srna, "factor", 0.1f, -FLT_MAX, FLT_MAX, "Factor", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "factor", 0.1f, -1e4f, 1e4f, "Factor", "", 0.0f, 1.0f); } +enum { + MESH_BRIDGELOOP_SINGLE = 0, + MESH_BRIDGELOOP_CLOSED = 1, + MESH_BRIDGELOOP_PAIRS = 2, +}; + static int edbm_bridge_tag_boundary_edges(BMesh *bm) { /* tags boundary edges from a face selection */ @@ -4838,8 +5049,8 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); - const bool use_pairs = (type == 2); - const bool use_cyclic = (type == 1); + const bool use_pairs = (type == MESH_BRIDGELOOP_PAIRS); + const bool use_cyclic = (type == MESH_BRIDGELOOP_CLOSED); const bool use_merge = RNA_boolean_get(op->ptr, "use_merge"); const float merge_factor = RNA_float_get(op->ptr, "merge_factor"); const int twist_offset = RNA_int_get(op->ptr, "twist_offset"); @@ -4937,9 +5148,9 @@ static int edbm_bridge_edge_loops_exec(bContext *C, wmOperator *op) void MESH_OT_bridge_edge_loops(wmOperatorType *ot) { static EnumPropertyItem type_items[] = { - {0, "SINGLE", 0, "Open Loop", ""}, - {1, "CLOSED", 0, "Closed Loop", ""}, - {2, "PAIRS", 0, "Loop Pairs", ""}, + {MESH_BRIDGELOOP_SINGLE, "SINGLE", 0, "Open Loop", ""}, + {MESH_BRIDGELOOP_CLOSED, "CLOSED", 0, "Closed Loop", ""}, + {MESH_BRIDGELOOP_PAIRS, "PAIRS", 0, "Loop Pairs", ""}, {0, NULL, 0, NULL, NULL} }; @@ -4955,7 +5166,7 @@ void MESH_OT_bridge_edge_loops(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, + ot->prop = RNA_def_enum(ot->srna, "type", type_items, MESH_BRIDGELOOP_SINGLE, "Connect Loops", "Method of bridging multiple loops"); RNA_def_boolean(ot->srna, "use_merge", false, "Merge", "Merge rather than creating faces"); @@ -5016,19 +5227,73 @@ void MESH_OT_wireframe(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries"); - RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness"); + RNA_def_boolean(ot->srna, "use_boundary", true, "Boundary", "Inset face boundaries"); + RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness"); RNA_def_boolean(ot->srna, "use_relative_offset", false, "Offset Relative", "Scale the offset by surrounding geometry"); - RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces"); - prop = RNA_def_float(ot->srna, "thickness", 0.01f, 0.0f, FLT_MAX, "Thickness", "", 0.0f, 10.0f); + RNA_def_boolean(ot->srna, "use_replace", true, "Replace", "Remove original faces"); + prop = RNA_def_float(ot->srna, "thickness", 0.01f, 0.0f, 1e4f, "Thickness", "", 0.0f, 10.0f); /* use 1 rather then 10 for max else dragging the button moves too far */ RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 4); - RNA_def_float(ot->srna, "offset", 0.01f, 0.0f, FLT_MAX, "Offset", "", 0.0f, 10.0f); - RNA_def_boolean(ot->srna, "use_crease", false, "Crease", "Crease hub edges for improved subsurf"); - prop = RNA_def_float(ot->srna, "crease_weight", 0.01f, 0.0f, FLT_MAX, "Crease weight", "", 0.0f, 1.0f); + RNA_def_float(ot->srna, "offset", 0.01f, 0.0f, 1e4f, "Offset", "", 0.0f, 10.0f); + RNA_def_boolean(ot->srna, "use_crease", false, "Crease", "Crease hub edges for improved subsurf"); + prop = RNA_def_float(ot->srna, "crease_weight", 0.01f, 0.0f, 1e3f, "Crease weight", "", 0.0f, 1.0f); RNA_def_property_ui_range(prop, 0.0, 1.0, 0.1, 2); } +static int edbm_offset_edgeloop_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMOperator bmop; + const bool use_cap_endpoint = RNA_boolean_get(op->ptr, "use_cap_endpoint"); + + EDBM_op_init( + em, &bmop, op, + "offset_edgeloops edges=%he use_cap_endpoint=%b", + BM_ELEM_SELECT, use_cap_endpoint); + + BMO_op_exec(em->bm, &bmop); + + BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); + + /* If in face-only select mode, switch to edge select mode so that + * an edge-only selection is not inconsistent state */ + if (em->selectmode == SCE_SELECT_FACE) { + em->selectmode = SCE_SELECT_EDGE; + EDBM_selectmode_set(em); + EDBM_selectmode_to_scene(C); + } + + BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true); + + if (!EDBM_op_finish(em, &bmop, op, true)) { + return OPERATOR_CANCELLED; + } + else { + EDBM_update_generic(em, true, true); + return OPERATOR_FINISHED; + } +} + +void MESH_OT_offset_edge_loops(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Offset Edge Loop"; + ot->idname = "MESH_OT_offset_edge_loops"; + ot->description = "Create offset edge loop from the current selection"; + + /* api callbacks */ + ot->exec = edbm_offset_edgeloop_exec; + ot->poll = ED_operator_editmesh; + + /* Keep internal, since this is only meant to be accessed via 'MESH_OT_offset_edge_loops_slide' */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_boolean(ot->srna, "use_cap_endpoint", false, "Cap Endpoint", "Extend loop around end-points"); +} + #ifdef WITH_BULLET static int edbm_convex_hull_exec(bContext *C, wmOperator *op) { @@ -5072,11 +5337,16 @@ static int edbm_convex_hull_exec(bContext *C, wmOperator *op) /* Merge adjacent triangles */ if (RNA_boolean_get(op->ptr, "join_triangles")) { - if (!EDBM_op_call_and_selectf(em, op, - "faces.out", true, - "join_triangles faces=%S limit=%f", - &bmop, "geom.out", - RNA_float_get(op->ptr, "limit"))) + float angle_face_threshold = RNA_float_get(op->ptr, "face_threshold"); + float angle_shape_threshold = RNA_float_get(op->ptr, "shape_threshold"); + + if (!EDBM_op_call_and_selectf( + em, op, + "faces.out", true, + "join_triangles faces=%S " + "angle_face_threshold=%f angle_shape_threshold=%f", + &bmop, "geom.out", + angle_face_threshold, angle_shape_threshold)) { EDBM_op_finish(em, &bmop, op, true); return OPERATOR_CANCELLED; @@ -5172,7 +5442,7 @@ void MESH_OT_symmetrize(struct wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "direction", symmetrize_direction_items, BMO_SYMMETRIZE_NEGATIVE_X, "Direction", "Which sides to copy from and to"); - RNA_def_float(ot->srna, "threshold", 0.0001, 0.0, 10.0, "Threshold", "", 0.00001, 0.1); + RNA_def_float(ot->srna, "threshold", 1e-4f, 0.0f, 10.0f, "Threshold", "", 1e-5f, 0.1f); } static int mesh_symmetry_snap_exec(bContext *C, wmOperator *op) @@ -5296,8 +5566,8 @@ void MESH_OT_symmetry_snap(struct wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "direction", symmetrize_direction_items, BMO_SYMMETRIZE_NEGATIVE_X, "Direction", "Which sides to copy from and to"); - RNA_def_float(ot->srna, "threshold", 0.05, 0.0, 10.0, "Threshold", "", 0.0001, 1.0); - RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0); + RNA_def_float(ot->srna, "threshold", 0.05f, 0.0f, 10.0f, "Threshold", "", 1e-4f, 1.0f); + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f); RNA_def_boolean(ot->srna, "use_center", true, "Center", "Snap mid verts to the axis center"); } @@ -5364,7 +5634,7 @@ void MESH_OT_mark_freestyle_edge(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_boolean(ot->srna, "clear", 0, "Clear", ""); + prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } @@ -5428,7 +5698,7 @@ void MESH_OT_mark_freestyle_face(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - prop = RNA_def_boolean(ot->srna, "clear", 0, "Clear", ""); + prop = RNA_def_boolean(ot->srna, "clear", false, "Clear", ""); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 86cd75eed7a..d521b2c01e5 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -37,6 +37,8 @@ #include "BLI_math.h" #include "BLI_alloca.h" +#include "BLI_buffer.h" +#include "BLI_kdtree.h" #include "BLI_listbase.h" #include "BKE_DerivedMesh.h" @@ -613,7 +615,9 @@ void undo_push_mesh(bContext *C, const char *name) /** * Return a new UVVertMap from the editmesh */ -UvVertMap *BM_uv_vert_map_create(BMesh *bm, bool use_select, const float limit[2]) +UvVertMap *BM_uv_vert_map_create( + BMesh *bm, + const float limit[2], const bool use_select, const bool use_winding) { BMVert *ev; BMFace *efa; @@ -625,11 +629,14 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm, bool use_select, const float limit[2 /* MTexPoly *tf; */ /* UNUSED */ MLoopUV *luv; unsigned int a; - int totverts, i, totuv; + int totverts, i, totuv, totfaces; const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + bool *winding = NULL; + BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE); + totfaces = bm->totface; totverts = bm->totvert; totuv = 0; @@ -650,35 +657,46 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm, bool use_select, const float limit[2 vmap->vert = (UvMapVert **)MEM_callocN(sizeof(*vmap->vert) * totverts, "UvMapVert_pt"); buf = vmap->buf = (UvMapVert *)MEM_callocN(sizeof(*vmap->buf) * totuv, "UvMapVert"); + if (use_winding) { + winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); + } if (!vmap->vert || !vmap->buf) { BKE_mesh_uv_vert_map_free(vmap); return NULL; } - a = 0; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, a) { if ((use_select == false) || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - i = 0; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + float (*tf_uv)[2]; + + if (use_winding) { + tf_uv = (float (*)[2])BLI_buffer_resize_data(&tf_uv_buf, vec2f, efa->len); + } + + BM_ITER_ELEM_INDEX(l, &liter, efa, BM_LOOPS_OF_FACE, i) { buf->tfindex = i; buf->f = a; buf->separate = 0; buf->next = vmap->vert[BM_elem_index_get(l->v)]; vmap->vert[BM_elem_index_get(l->v)] = buf; - buf++; - i++; + + if (use_winding) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(tf_uv[i], luv->uv); + } } - } - a++; + if (use_winding) { + winding[a] = cross_poly_v2((const float (*)[2])tf_uv, efa->len) > 0; + } + } } /* sort individual uvs for each vert */ - a = 0; - BM_ITER_MESH (ev, &iter, bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, a) { UvMapVert *newvlist = NULL, *vlist = vmap->vert[a]; UvMapVert *iterv, *v, *lastv, *next; float *uv, *uv2, uvdiff[2]; @@ -710,7 +728,9 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm, bool use_select, const float limit[2 sub_v2_v2v2(uvdiff, uv2, uv); - if (fabsf(uvdiff[0]) < limit[0] && fabsf(uvdiff[1]) < limit[1]) { + if (fabsf(uvdiff[0]) < limit[0] && fabsf(uvdiff[1]) < limit[1] && + (!use_winding || winding[iterv->f] == winding[v->f])) + { if (lastv) lastv->next = next; else vlist = next; iterv->next = newvlist; @@ -727,9 +747,14 @@ UvVertMap *BM_uv_vert_map_create(BMesh *bm, bool use_select, const float limit[2 } vmap->vert[a] = newvlist; - a++; } + if (use_winding) { + MEM_freeN(winding); + } + + BLI_buffer_free(&tf_uv_buf); + return vmap; } @@ -743,7 +768,9 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, unsigned int v) /* A specialized vert map used by stitch operator */ -UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const bool do_islands) +UvElementMap *BM_uv_element_map_create( + BMesh *bm, + const bool selected, const bool use_winding, const bool do_islands) { BMVert *ev; BMFace *efa; @@ -752,29 +779,20 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo /* vars from original func */ UvElementMap *element_map; UvElement *buf; - UvElement *islandbuf; - /* island number for faces */ - int *island_number; + bool *winding; + BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); MLoopUV *luv; - int totverts, i, totuv, j, nislands = 0, islandbufsize = 0; - - unsigned int *map; - BMFace **stack; - int stacksize = 0; + int totverts, totfaces, i, totuv, j; const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE); + totfaces = bm->totface; totverts = bm->totvert; totuv = 0; - island_number = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_number_face"); - if (!island_number) { - return NULL; - } - /* generate UvElement array */ BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { @@ -783,28 +801,31 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo } if (totuv == 0) { - MEM_freeN(island_number); return NULL; } + element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap"); - if (!element_map) { - MEM_freeN(island_number); - return NULL; - } element_map->totalUVs = totuv; element_map->vert = (UvElement **)MEM_callocN(sizeof(*element_map->vert) * totverts, "UvElementVerts"); buf = element_map->buf = (UvElement *)MEM_callocN(sizeof(*element_map->buf) * totuv, "UvElement"); - if (!element_map->vert || !element_map->buf) { - BM_uv_element_map_free(element_map); - MEM_freeN(island_number); - return NULL; + if (use_winding) { + winding = MEM_mallocN(sizeof(*winding) * totfaces, "winding"); } - j = 0; - BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { - island_number[j++] = INVALID_ISLAND; + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { + + if (use_winding) { + winding[j] = false; + } + if (!selected || BM_elem_flag_test(efa, BM_ELEM_SELECT)) { + float (*tf_uv)[2]; + + if (use_winding) { + tf_uv = (float (*)[2])BLI_buffer_resize_data(&tf_uv_buf, vec2f, efa->len); + } + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { buf->l = l; buf->separate = 0; @@ -814,14 +835,22 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo buf->next = element_map->vert[BM_elem_index_get(l->v)]; element_map->vert[BM_elem_index_get(l->v)] = buf; + if (use_winding) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + copy_v2_v2(tf_uv[i], luv->uv); + } + buf++; } + + if (use_winding) { + winding[j] = cross_poly_v2((const float (*)[2])tf_uv, efa->len) > 0; + } } } /* sort individual uvs for each vert */ - i = 0; - BM_ITER_MESH (ev, &iter, bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) { UvElement *newvlist = NULL, *vlist = element_map->vert[i]; UvElement *iterv, *v, *lastv, *next; float *uv, *uv2, uvdiff[2]; @@ -848,7 +877,9 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo sub_v2_v2v2(uvdiff, uv2, uv); - if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT) { + if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT && + (!use_winding || winding[BM_elem_index_get(iterv->l->f)] == winding[BM_elem_index_get(v->l->f)])) + { if (lastv) lastv->next = next; else vlist = next; iterv->next = newvlist; @@ -865,14 +896,28 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo } element_map->vert[i] = newvlist; - i++; + } + + if (use_winding) { + MEM_freeN(winding); } if (do_islands) { + unsigned int *map; + BMFace **stack; + int stacksize = 0; + UvElement *islandbuf; + /* island number for faces */ + int *island_number = NULL; + + int nislands = 0, islandbufsize = 0; + /* map holds the map from current vmap->buf to the new, sorted map */ map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); + island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); + copy_vn_i(island_number, totfaces, INVALID_ISLAND); /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. Now we should sort uv's in islands. */ for (i = 0; i < totuv; i++) { @@ -921,6 +966,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo } } + MEM_freeN(island_number); + /* remap */ for (i = 0; i < bm->totvert; i++) { /* important since we may do selection only. Some of these may be NULL */ @@ -929,14 +976,6 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo } element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands, "UvElementMap_island_indices"); - if (!element_map->islandIndices) { - MEM_freeN(islandbuf); - MEM_freeN(stack); - MEM_freeN(map); - BM_uv_element_map_free(element_map); - MEM_freeN(island_number); - } - j = 0; for (i = 0; i < totuv; i++) { UvElement *element = element_map->buf[i].next; @@ -958,7 +997,8 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, const bool selected, const boo MEM_freeN(stack); MEM_freeN(map); } - MEM_freeN(island_number); + + BLI_buffer_free(&tf_uv_buf); return element_map; } @@ -1036,26 +1076,19 @@ static BMVert *cache_mirr_intptr_as_bmvert(intptr_t *index_lookup, int index) } /** - * [note: I've decided to use ideasman's code for non-editmode stuff, but since - * it has a big "not for editmode!" disclaimer, I'm going to keep what I have here - * - joeedh] - * - * x-mirror editing api. usage: + * Mirror editing API, usage: * - * EDBM_verts_mirror_cache_begin(em); - * ... - * ... - * BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { - * mirrorv = EDBM_verts_mirror_get(em, v); - * } - * ... - * ... - * EDBM_verts_mirror_cache_end(em); + * \code{.c} + * EDBM_verts_mirror_cache_begin(em, ...); * - * \param use_self Allow a vertex to reference its self. - * \param use_select Only cache selected verts. + * BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + * v_mirror = EDBM_verts_mirror_get(em, v); + * e_mirror = EDBM_verts_mirror_get_edge(em, e); + * f_mirror = EDBM_verts_mirror_get_face(em, f); + * } * - * \note why do we only allow x axis mirror editing? + * EDBM_verts_mirror_cache_end(em); + * \endcode */ /* BM_SEARCH_MAXDIST is too big, copied from 2.6x MOC_THRESH, should become a @@ -1080,9 +1113,10 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool BMVert *v; int cd_vmirr_offset; int i; + const float maxdist_sq = SQUARE(maxdist); /* one or the other is used depending if topo is enabled */ - struct BMBVHTree *tree = NULL; + KDTree *tree = NULL; MirrTopoStore_t mesh_topo_store = {NULL, -1, -1, -1}; BM_mesh_elem_table_ensure(bm, BM_VERT); @@ -1107,7 +1141,11 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool ED_mesh_mirrtopo_init(me, -1, &mesh_topo_store, true); } else { - tree = BKE_bmbvh_new_from_editmesh(em, 0, NULL, false); + tree = BLI_kdtree_new(bm->totvert); + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + BLI_kdtree_insert(tree, i, v->co); + } + BLI_kdtree_balance(tree); } #define VERT_INTPTR(_v, _i) r_index ? &r_index[_i] : BM_ELEM_CD_GET_VOID_P(_v, cd_vmirr_offset); @@ -1127,10 +1165,19 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool v_mirr = cache_mirr_intptr_as_bmvert(mesh_topo_store.index_lookup, i); } else { + int i_mirr; float co[3]; copy_v3_v3(co, v->co); co[axis] *= -1.0f; - v_mirr = BKE_bmbvh_find_vert_closest(tree, co, maxdist); + + v_mirr = NULL; + i_mirr = BLI_kdtree_find_nearest(tree, co, NULL); + if (i_mirr != -1) { + BMVert *v_test = BM_vert_at_index(bm, i_mirr); + if (len_squared_v3v3(co, v_test->co) < maxdist_sq) { + v_mirr = v_test; + } + } } if (v_mirr && (use_self || (v_mirr != v))) { @@ -1152,7 +1199,7 @@ void EDBM_verts_mirror_cache_begin_ex(BMEditMesh *em, const int axis, const bool ED_mesh_mirrtopo_free(&mesh_topo_store); } else { - BKE_bmbvh_free(tree); + BLI_kdtree_free(tree); } } diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index 86991d7dfeb..a2054a5f43c 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -429,7 +429,7 @@ int ED_mesh_color_add(Mesh *me, const char *name, const bool active_set) /* copy data from active vertex color layer */ if (layernum) { const int layernum_dst = CustomData_get_active_layer(&em->bm->ldata, CD_MLOOPCOL); - BM_data_layer_copy(em->bm, &em->bm->ldata, CD_MLOOPCOL, layernum, layernum_dst); + BM_data_layer_copy(em->bm, &em->bm->ldata, CD_MLOOPCOL, layernum_dst, layernum); } if (active_set || layernum == 0) { CustomData_set_layer_active(&em->bm->ldata, CD_MLOOPCOL, layernum); @@ -748,7 +748,7 @@ static int mesh_customdata_clear_exec__internal(bContext *C, } /* Clear Mask */ -static int mesh_customdata_clear_mask_poll(bContext *C) +static int mesh_customdata_mask_clear_poll(bContext *C) { Object *ob = ED_object_context(C); if (ob && ob->type == OB_MESH) { @@ -772,7 +772,7 @@ static int mesh_customdata_clear_mask_poll(bContext *C) } return false; } -static int mesh_customdata_clear_mask_exec(bContext *C, wmOperator *UNUSED(op)) +static int mesh_customdata_mask_clear_exec(bContext *C, wmOperator *UNUSED(op)) { int ret_a = mesh_customdata_clear_exec__internal(C, BM_VERT, CD_PAINT_MASK); int ret_b = mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_GRID_PAINT_MASK); @@ -787,24 +787,27 @@ static int mesh_customdata_clear_mask_exec(bContext *C, wmOperator *UNUSED(op)) } } -void MESH_OT_customdata_clear_mask(wmOperatorType *ot) +void MESH_OT_customdata_mask_clear(wmOperatorType *ot) { /* identifiers */ ot->name = "Clear Sculpt-Mask Data"; - ot->idname = "MESH_OT_customdata_clear_mask"; + ot->idname = "MESH_OT_customdata_mask_clear"; ot->description = "Clear vertex sculpt masking data from the mesh"; /* api callbacks */ - ot->exec = mesh_customdata_clear_mask_exec; - ot->poll = mesh_customdata_clear_mask_poll; + ot->exec = mesh_customdata_mask_clear_exec; + ot->poll = mesh_customdata_mask_clear_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* Clear Skin */ -static int mesh_customdata_clear_skin_poll(bContext *C) +/** + * Clear Skin + * \return -1 invalid state, 0 no skin, 1 has skin. + */ +static int mesh_customdata_skin_state(bContext *C) { Object *ob = ED_object_context(C); @@ -812,28 +815,65 @@ static int mesh_customdata_clear_skin_poll(bContext *C) Mesh *me = ob->data; if (me->id.lib == NULL) { CustomData *data = GET_CD_DATA(me, vdata); - if (CustomData_has_layer(data, CD_MVERT_SKIN)) { - return true; - } + return CustomData_has_layer(data, CD_MVERT_SKIN); } } - return false; + return -1; } -static int mesh_customdata_clear_skin_exec(bContext *C, wmOperator *UNUSED(op)) + +static int mesh_customdata_skin_add_poll(bContext *C) +{ + return (mesh_customdata_skin_state(C) == 0); +} + +static int mesh_customdata_skin_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = ED_object_context(C); + Mesh *me = ob->data; + + BKE_mesh_ensure_skin_customdata(me); + + DAG_id_tag_update(&me->id, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); + + return OPERATOR_FINISHED; +} + +void MESH_OT_customdata_skin_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Skin Data"; + ot->idname = "MESH_OT_customdata_skin_add"; + ot->description = "Add a vertex skin layer"; + + /* api callbacks */ + ot->exec = mesh_customdata_skin_add_exec; + ot->poll = mesh_customdata_skin_add_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int mesh_customdata_skin_clear_poll(bContext *C) +{ + return (mesh_customdata_skin_state(C) == 1); +} + +static int mesh_customdata_skin_clear_exec(bContext *C, wmOperator *UNUSED(op)) { return mesh_customdata_clear_exec__internal(C, BM_VERT, CD_MVERT_SKIN); } -void MESH_OT_customdata_clear_skin(wmOperatorType *ot) +void MESH_OT_customdata_skin_clear(wmOperatorType *ot) { /* identifiers */ ot->name = "Clear Skin Data"; - ot->idname = "MESH_OT_customdata_clear_skin"; + ot->idname = "MESH_OT_customdata_skin_clear"; ot->description = "Clear vertex skin layer"; /* api callbacks */ - ot->exec = mesh_customdata_clear_skin_exec; - ot->poll = mesh_customdata_clear_skin_poll; + ot->exec = mesh_customdata_skin_clear_exec; + ot->poll = mesh_customdata_skin_clear_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 8611872a1a0..c56465f570a 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -34,14 +34,9 @@ #ifndef __MESH_INTERN_H__ #define __MESH_INTERN_H__ -struct BMEdge; struct BMEditMesh; -struct BMFace; -struct BMHeader; struct BMOperator; -struct BMesh; struct EnumPropertyItem; -struct ViewContext; struct bContext; struct wmKeyConfig; struct wmKeyMap; @@ -178,8 +173,10 @@ void MESH_OT_vert_connect(struct wmOperatorType *ot); void MESH_OT_vert_connect_path(struct wmOperatorType *ot); void MESH_OT_vert_connect_concave(struct wmOperatorType *ot); void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot); +void MESH_OT_face_make_planar(struct wmOperatorType *ot); void MESH_OT_edge_split(struct wmOperatorType *ot); void MESH_OT_bridge_edge_loops(struct wmOperatorType *ot); +void MESH_OT_offset_edge_loops(struct wmOperatorType *ot); void MESH_OT_wireframe(struct wmOperatorType *ot); void MESH_OT_convex_hull(struct wmOperatorType *ot); void MESH_OT_symmetrize(struct wmOperatorType *ot); @@ -237,8 +234,9 @@ void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); void MESH_OT_vertex_color_add(struct wmOperatorType *ot); void MESH_OT_vertex_color_remove(struct wmOperatorType *ot); /* no create_mask yet */ -void MESH_OT_customdata_clear_mask(struct wmOperatorType *ot); -void MESH_OT_customdata_clear_skin(struct wmOperatorType *ot); +void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot); +void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); +void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_add(struct wmOperatorType *ot); void MESH_OT_customdata_custom_splitnormals_clear(struct wmOperatorType *ot); void MESH_OT_drop_named_image(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_navmesh.c b/source/blender/editors/mesh/mesh_navmesh.c index 440ab14dacd..1c36826b882 100644 --- a/source/blender/editors/mesh/mesh_navmesh.c +++ b/source/blender/editors/mesh/mesh_navmesh.c @@ -84,7 +84,7 @@ static void createVertsTrisData(bContext *C, LinkNode *obs, for (oblink = obs; oblink; oblink = oblink->next) { ob = (Object *) oblink->link; dm = mesh_create_derived_no_virtual(scene, ob, NULL, CD_MASK_MESH); - BLI_linklist_append(&dms, (void *)dm); + BLI_linklist_prepend(&dms, dm); nverts += dm->getNumVerts(dm); nfaces = dm->getNumTessFaces(dm); @@ -325,7 +325,7 @@ static Object *createRepresentation(bContext *C, struct recast_polyMesh *pmesh, if (createob) { /* create new object */ - obedit = ED_object_add_type(C, OB_MESH, co, rot, false, lay); + obedit = ED_object_add_type(C, OB_MESH, "Navmesh", co, rot, false, lay); } else { obedit = base->object; @@ -429,7 +429,6 @@ static Object *createRepresentation(bContext *C, struct recast_polyMesh *pmesh, obedit->gameflag &= ~OB_COLLISION; obedit->gameflag |= OB_NAVMESH; obedit->body_type = OB_BODY_TYPE_NAVMESH; - rename_id((ID *)obedit, "Navmesh"); } BKE_mesh_ensure_navmesh(obedit->data); @@ -452,7 +451,7 @@ static int navmesh_create_exec(bContext *C, wmOperator *op) } } else { - BLI_linklist_append(&obs, (void *)base->object); + BLI_linklist_prepend(&obs, base->object); } } } diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 2855af063c0..9718e63f012 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -151,8 +151,9 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_uv_texture_remove); WM_operatortype_append(MESH_OT_vertex_color_add); WM_operatortype_append(MESH_OT_vertex_color_remove); - WM_operatortype_append(MESH_OT_customdata_clear_mask); - WM_operatortype_append(MESH_OT_customdata_clear_skin); + WM_operatortype_append(MESH_OT_customdata_mask_clear); + WM_operatortype_append(MESH_OT_customdata_skin_add); + WM_operatortype_append(MESH_OT_customdata_skin_clear); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_add); WM_operatortype_append(MESH_OT_customdata_custom_splitnormals_clear); WM_operatortype_append(MESH_OT_drop_named_image); @@ -166,6 +167,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_vert_connect_path); WM_operatortype_append(MESH_OT_vert_connect_concave); WM_operatortype_append(MESH_OT_vert_connect_nonplanar); + WM_operatortype_append(MESH_OT_face_make_planar); WM_operatortype_append(MESH_OT_knife_tool); WM_operatortype_append(MESH_OT_knife_project); @@ -175,6 +177,7 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_bridge_edge_loops); WM_operatortype_append(MESH_OT_inset); + WM_operatortype_append(MESH_OT_offset_edge_loops); WM_operatortype_append(MESH_OT_intersect); WM_operatortype_append(MESH_OT_face_split_by_edges); WM_operatortype_append(MESH_OT_poke); @@ -223,6 +226,13 @@ void ED_operatormacros_mesh(void) otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide"); RNA_boolean_set(otmacro->ptr, "release_confirm", false); + ot = WM_operatortype_append_macro("MESH_OT_offset_edge_loops_slide", "Offset Edge Slide", "Offset edge loop slide", + OPTYPE_UNDO | OPTYPE_REGISTER); + WM_operatortype_macro_define(ot, "MESH_OT_offset_edge_loops"); + otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_edge_slide"); + RNA_boolean_set(otmacro->ptr, "release_confirm", false); + RNA_boolean_set(otmacro->ptr, "single_side", true); + ot = WM_operatortype_append_macro("MESH_OT_duplicate_move", "Add Duplicate", "Duplicate mesh and move", OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "MESH_OT_duplicate"); @@ -300,7 +310,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Mesh", 0, 0); keymap->poll = ED_operator_editmesh; - WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MESH_OT_loopcut_slide", RKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "MESH_OT_offset_edge_loops_slide", RKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); WM_keymap_add_item(keymap, "MESH_OT_inset", IKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "MESH_OT_poke", PKEY, KM_PRESS, KM_ALT, 0); kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index f85bb23ffc1..2491685161c 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -1025,7 +1025,7 @@ int *mesh_get_x_mirror_faces(Object *ob, BMEditMesh *em) ED_mesh_mirror_spatial_table(ob, em, NULL, 'e'); - fhash = BLI_ghash_new_ex(mirror_facehash, mirror_facecmp, "mirror_facehash gh", me->totface, false); + fhash = BLI_ghash_new_ex(mirror_facehash, mirror_facecmp, "mirror_facehash gh", me->totface); for (a = 0, mf = mface; a < me->totface; a++, mf++) BLI_ghash_insert(fhash, mf, mf); @@ -1082,11 +1082,11 @@ bool ED_mesh_pick_face(bContext *C, Object *ob, const int mval[2], unsigned int * on an edge in the backbuf, we can still select a face */ float dummy_dist; - *index = view3d_sample_backbuf_rect(&vc, mval, size, 1, me->totpoly + 1, &dummy_dist, 0, NULL, NULL); + *index = ED_view3d_backbuf_sample_rect(&vc, mval, size, 1, me->totpoly + 1, &dummy_dist); } else { /* sample only on the exact position */ - *index = view3d_sample_backbuf(&vc, mval[0], mval[1]); + *index = ED_view3d_backbuf_sample(&vc, mval[0], mval[1]); } if ((*index) == 0 || (*index) > (unsigned int)me->totpoly) @@ -1248,11 +1248,11 @@ bool ED_mesh_pick_vert(bContext *C, Object *ob, const int mval[2], unsigned int * on an face in the backbuf, we can still select a vert */ float dummy_dist; - *index = view3d_sample_backbuf_rect(&vc, mval, size, 1, me->totvert + 1, &dummy_dist, 0, NULL, NULL); + *index = ED_view3d_backbuf_sample_rect(&vc, mval, size, 1, me->totvert + 1, &dummy_dist); } else { /* sample only on the exact position */ - *index = view3d_sample_backbuf(&vc, mval[0], mval[1]); + *index = ED_view3d_backbuf_sample(&vc, mval[0], mval[1]); } if ((*index) == 0 || (*index) > (unsigned int)me->totvert) diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 24cac5b9b70..42eda235276 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -404,7 +404,8 @@ void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, "Percent", "Percentage of elements to select randomly", 0.f, 100.0f); + RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f, + "Percent", "Percentage of elements to select randomly", 0.0f, 100.0f); WM_operator_properties_select_action_simple(ot, SEL_SELECT); } @@ -532,7 +533,7 @@ void MBALL_OT_hide_metaelems(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); + RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected"); } /***************************** Unhide operator *****************************/ diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 99b351561c7..075e382f628 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -57,6 +57,7 @@ #include "BLF_translation.h" +#include "BKE_action.h" #include "BKE_anim.h" #include "BKE_animsys.h" #include "BKE_armature.h" @@ -262,7 +263,7 @@ void ED_object_add_unit_props(wmOperatorType *ot) { PropertyRNA *prop; - prop = RNA_def_float(ot->srna, "radius", 1.0f, 0.0, FLT_MAX, "Radius", "", 0.001, 100.00); + prop = RNA_def_float(ot->srna, "radius", 1.0f, 0.0, OBJECT_ADD_SIZE_MAXF, "Radius", "", 0.001, 100.00); RNA_def_property_subtype(prop, PROP_DISTANCE); } @@ -280,11 +281,12 @@ void ED_object_add_generic_props(wmOperatorType *ot, bool do_editmode) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } - prop = RNA_def_float_vector_xyz(ot->srna, "location", 3, NULL, -FLT_MAX, FLT_MAX, "Location", - "Location for the newly added object", -FLT_MAX, FLT_MAX); + prop = RNA_def_float_vector_xyz(ot->srna, "location", 3, NULL, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, + "Location", "Location for the newly added object", -1000.0f, 1000.0f); RNA_def_property_flag(prop, PROP_SKIP_SAVE); - prop = RNA_def_float_rotation(ot->srna, "rotation", 3, NULL, -FLT_MAX, FLT_MAX, "Rotation", - "Rotation for the newly added object", (float)-M_PI * 2.0f, (float)M_PI * 2.0f); + prop = RNA_def_float_rotation(ot->srna, "rotation", 3, NULL, -OBJECT_ADD_SIZE_MAXF, OBJECT_ADD_SIZE_MAXF, + "Rotation", "Rotation for the newly added object", + DEG2RADF(-360.0f), DEG2RADF(360.0f)); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean_layer_member(ot->srna, "layers", 20, NULL, "Layer", ""); @@ -394,8 +396,11 @@ bool ED_object_add_generic_get_opts(bContext *C, wmOperator *op, const char view /* For object add primitive operators. * Do not call undo push in this function (users of this function have to). */ -Object *ED_object_add_type(bContext *C, int type, const float loc[3], const float rot[3], - bool enter_editmode, unsigned int layer) +Object *ED_object_add_type( + bContext *C, + int type, const char *name, + const float loc[3], const float rot[3], + bool enter_editmode, unsigned int layer) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -406,7 +411,7 @@ Object *ED_object_add_type(bContext *C, int type, const float loc[3], const floa ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO); /* freedata, and undo */ /* deselects all, sets scene->basact */ - ob = BKE_object_add(bmain, scene, type); + ob = BKE_object_add(bmain, scene, type, name); BASACT->lay = ob->lay = layer; /* editor level activate, notifiers */ ED_base_object_activate(C, BASACT); @@ -447,7 +452,7 @@ static int object_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; radius = RNA_float_get(op->ptr, "radius"); - ob = 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"), NULL, loc, rot, enter_editmode, layer); if (ob->type == OB_LATTICE) { /* lattice is a special case! @@ -504,9 +509,9 @@ static int effector_add_exec(bContext *C, wmOperator *op) if (type == PFIELD_GUIDE) { Curve *cu; - ob = ED_object_add_type(C, OB_CURVE, loc, rot, false, layer); + const char *name = CTX_DATA_(BLF_I18NCONTEXT_ID_OBJECT, "CurveGuide"); + ob = ED_object_add_type(C, OB_CURVE, name, loc, rot, false, layer); - 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); @@ -516,9 +521,9 @@ static int effector_add_exec(bContext *C, wmOperator *op) ED_object_editmode_exit(C, EM_FREEDATA); } else { - ob = ED_object_add_type(C, OB_EMPTY, loc, rot, false, layer); + const char *name = CTX_DATA_(BLF_I18NCONTEXT_ID_OBJECT, "Field"); + ob = ED_object_add_type(C, OB_EMPTY, name, loc, rot, false, layer); 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; } @@ -569,7 +574,7 @@ static int object_camera_add_exec(bContext *C, wmOperator *op) if (!ED_object_add_generic_get_opts(C, op, 'Z', loc, rot, &enter_editmode, &layer, NULL)) return OPERATOR_CANCELLED; - ob = ED_object_add_type(C, OB_CAMERA, loc, rot, false, layer); + ob = ED_object_add_type(C, OB_CAMERA, NULL, loc, rot, false, layer); if (v3d) { if (v3d->camera == NULL) @@ -626,7 +631,7 @@ static int object_metaball_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; if (obedit == NULL || obedit->type != OB_MBALL) { - obedit = ED_object_add_type(C, OB_MBALL, loc, rot, true, layer); + obedit = ED_object_add_type(C, OB_MBALL, NULL, loc, rot, true, layer); newob = true; } else { @@ -685,7 +690,7 @@ static int object_add_text_exec(bContext *C, wmOperator *op) if (obedit && obedit->type == OB_FONT) return OPERATOR_CANCELLED; - obedit = ED_object_add_type(C, OB_FONT, loc, rot, enter_editmode, layer); + obedit = ED_object_add_type(C, OB_FONT, NULL, 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); @@ -729,7 +734,7 @@ static int object_armature_add_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; if ((obedit == NULL) || (obedit->type != OB_ARMATURE)) { - obedit = ED_object_add_type(C, OB_ARMATURE, loc, rot, true, layer); + obedit = ED_object_add_type(C, OB_ARMATURE, NULL, loc, rot, true, layer); ED_object_editmode_enter(C, 0); newob = true; } @@ -786,7 +791,7 @@ static int object_empty_add_exec(bContext *C, wmOperator *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); + ob = ED_object_add_type(C, OB_EMPTY, NULL, loc, rot, false, layer); BKE_object_empty_draw_type_set(ob, type); BKE_object_obdata_size_init(ob, RNA_float_get(op->ptr, "radius")); @@ -846,7 +851,7 @@ static int empty_drop_named_image_invoke(bContext *C, wmOperator *op, const wmEv if (!ED_object_add_generic_get_opts(C, op, 'Z', NULL, rot, NULL, &layer, NULL)) return OPERATOR_CANCELLED; - ob = ED_object_add_type(C, OB_EMPTY, NULL, rot, false, layer); + ob = ED_object_add_type(C, OB_EMPTY, NULL, NULL, rot, false, layer); /* add under the mouse */ ED_object_location_from_view(C, ob->loc); @@ -916,13 +921,11 @@ static int object_lamp_add_exec(bContext *C, wmOperator *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); + ob = ED_object_add_type(C, OB_LAMP, get_lamp_defname(type), loc, rot, false, layer); 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)); if (BKE_scene_use_new_shading_nodes(scene)) { ED_node_shader_default(C, &la->id); @@ -988,8 +991,7 @@ static int group_instance_add_exec(bContext *C, wmOperator *op) if (group) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Object *ob = ED_object_add_type(C, OB_EMPTY, loc, rot, false, layer); - rename_id(&ob->id, group->id.name + 2); + Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, loc, rot, false, layer); ob->dup_group = group; ob->transflag |= OB_DUPLIGROUP; id_lib_extern(&group->id); @@ -1044,14 +1046,14 @@ static int object_speaker_add_exec(bContext *C, wmOperator *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_SPEAKER, loc, rot, false, layer); + ob = ED_object_add_type(C, OB_SPEAKER, NULL, loc, rot, false, layer); /* to make it easier to start using this immediately in NLA, a default sound clip is created * ready to be moved around to retime the sound and/or make new sound clips */ { /* create new data for NLA hierarchy */ - AnimData *adt = BKE_id_add_animdata(&ob->id); + AnimData *adt = BKE_animdata_add_id(&ob->id); NlaTrack *nlt = add_nlatrack(adt, NULL); NlaStrip *strip = add_nla_soundstrip(scene, ob->data); strip->start = CFRA; @@ -1358,7 +1360,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, basen->object = ob; /* make sure apply works */ - BKE_free_animdata(&ob->id); + BKE_animdata_free(&ob->id); ob->adt = NULL; /* Proxies are not to be copied. */ @@ -1976,7 +1978,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base /* duplicates using userflags */ if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&obn->id); + BKE_animdata_copy_id_action(&obn->id); } if (dupflag & USER_DUP_MAT) { @@ -1989,7 +1991,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base id->us--; if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&obn->mat[a]->id); + BKE_animdata_copy_id_action(&obn->mat[a]->id); } } } @@ -2004,7 +2006,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base psys->part = BKE_particlesettings_copy(psys->part); if (dupflag & USER_DUP_ACT) { - BKE_copy_animdata_id_action(&psys->part->id); + BKE_animdata_copy_id_action(&psys->part->id); } id->us--; @@ -2082,7 +2084,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base case OB_ARMATURE: DAG_id_tag_update(&obn->id, OB_RECALC_DATA); if (obn->pose) - obn->pose->flag |= POSE_RECALC; + BKE_pose_tag_recalc(bmain, obn->pose); if (dupflag & USER_DUP_ARM) { ID_NEW_US2(obn->data) else { @@ -2132,9 +2134,9 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base if (dupflag & USER_DUP_ACT) { bActuator *act; - BKE_copy_animdata_id_action((ID *)obn->data); + BKE_animdata_copy_id_action((ID *)obn->data); if (key) { - BKE_copy_animdata_id_action((ID *)key); + BKE_animdata_copy_id_action((ID *)key); } /* Update the duplicated action in the action actuators */ diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 97cb45daae3..b382fbafcfd 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -574,6 +574,7 @@ static int bake( float *result = NULL; BakePixel *pixel_array_low = NULL; + BakePixel *pixel_array_high = NULL; const bool is_save_internal = (save_mode == R_BAKE_SAVE_INTERNAL); const bool is_noncolor = is_noncolor_pass(pass_type); @@ -682,10 +683,13 @@ static int bake( } pixel_array_low = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels low poly"); + pixel_array_high = MEM_mallocN(sizeof(BakePixel) * num_pixels, "bake pixels high poly"); result = MEM_callocN(sizeof(float) * depth * num_pixels, "bake return pixels"); /* get the mesh as it arrives in the renderer */ - me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_low = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_low); + BKE_mesh_tessface_ensure(me_low); /* populate the pixel array with the face data */ if ((is_selected_to_active && (ob_cage == NULL) && is_cage) == false) @@ -700,7 +704,9 @@ static int bake( /* prepare cage mesh */ if (ob_cage) { - me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 1, 0); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_cage, 1, 2, 0, 0); + BKE_mesh_split_faces(me_cage); + BKE_mesh_tessface_ensure(me_cage); if (me_low->totface != me_cage->totface) { BKE_report(reports, RPT_ERROR, "Invalid cage object, the cage mesh must have the same number " @@ -732,7 +738,9 @@ static int bake( ob_low->modifiers = modifiers_tmp; /* get the cage mesh as it arrives in the renderer */ - me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_cage = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_cage); + BKE_mesh_tessface_ensure(me_cage); RE_bake_pixels_populate(me_cage, pixel_array_low, num_pixels, &bake_images, uv_layer); } @@ -749,8 +757,6 @@ static int bake( /* initialize highpoly_data */ highpoly[i].ob = ob_iter; highpoly[i].restrict_flag = ob_iter->restrictflag; - 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 */ highpoly[i].tri_mod = ED_object_modifier_add( @@ -760,18 +766,16 @@ static int bake( tmd->quad_method = MOD_TRIANGULATE_QUAD_FIXED; tmd->ngon_method = MOD_TRIANGULATE_NGON_EARCLIP; - highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 1, 0); + highpoly[i].me = BKE_mesh_new_from_object(bmain, scene, highpoly[i].ob, 1, 2, 0, 0); highpoly[i].ob->restrictflag &= ~OB_RESTRICT_RENDER; + BKE_mesh_split_faces(highpoly[i].me); + BKE_mesh_tessface_ensure(highpoly[i].me); /* lowpoly to highpoly transformation matrix */ 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_mat3_m4(highpoly[i].rotmat); + highpoly[i].is_flip_object = is_negative_m4(highpoly[i].ob->obmat); i++; } @@ -782,7 +786,7 @@ static int bake( /* populate the pixel arrays with the corresponding face data for each high poly object */ if (!RE_bake_pixels_populate_from_objects( - me_low, pixel_array_low, highpoly, tot_highpoly, num_pixels, ob_cage != NULL, + me_low, pixel_array_low, pixel_array_high, 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"); @@ -791,8 +795,8 @@ static int bake( /* the baking itself */ for (i = 0; i < tot_highpoly; i++) { - ok = RE_bake_engine(re, highpoly[i].ob, highpoly[i].pixel_array, num_pixels, - depth, pass_type, result); + ok = RE_bake_engine(re, highpoly[i].ob, i, pixel_array_high, + 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; @@ -818,7 +822,7 @@ cage_cleanup: ob_low->restrictflag &= ~OB_RESTRICT_RENDER; if (RE_bake_has_engine(re)) { - ok = RE_bake_engine(re, ob_low, pixel_array_low, num_pixels, depth, pass_type, result); + ok = RE_bake_engine(re, ob_low, 0, pixel_array_low, num_pixels, depth, pass_type, result); } else { BKE_report(reports, RPT_ERROR, "Current render engine does not support baking"); @@ -867,7 +871,9 @@ cage_cleanup: md->mode &= ~eModifierMode_Render; } - me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 1, 0); + me_nores = BKE_mesh_new_from_object(bmain, scene, ob_low, 1, 2, 0, 0); + BKE_mesh_split_faces(me_nores); + BKE_mesh_tessface_ensure(me_nores); 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, ob_low->obmat); @@ -919,7 +925,7 @@ cage_cleanup: BakeData *bake = &scene->r.bake; char name[FILE_MAX]; - BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false); + BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false, NULL); if (is_automatic_name) { BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); @@ -980,9 +986,6 @@ cleanup: for (i = 0; i < tot_highpoly; i++) { highpoly[i].ob->restrictflag = highpoly[i].restrict_flag; - if (highpoly[i].pixel_array) - MEM_freeN(highpoly[i].pixel_array); - if (highpoly[i].tri_mod) ED_object_modifier_remove(reports, bmain, highpoly[i].ob, highpoly[i].tri_mod); @@ -1000,6 +1003,9 @@ cleanup: if (pixel_array_low) MEM_freeN(pixel_array_low); + if (pixel_array_high) + MEM_freeN(pixel_array_high); + if (bake_images.data) MEM_freeN(bake_images.data); diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index 231d23b481d..e14674ef517 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -226,7 +226,7 @@ static void update_pyconstraint_cb(void *arg1, void *arg2) /* helper function for add_constriant - sets the last target for the active constraint */ static void set_constraint_nth_target(bConstraint *con, Object *target, const char subtarget[], int index) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); ListBase targets = {NULL, NULL}; bConstraintTarget *ct; int num_targets, i; @@ -260,17 +260,221 @@ static void set_constraint_nth_target(bConstraint *con, Object *target, const ch /* ------------- Constraint Sanity Testing ------------------- */ -/* checks validity of object pointers, and NULLs, - * if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag. - */ -static void test_constraints(Object *owner, bPoseChannel *pchan) +static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con, int type) +{ + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + bool check_targets = true; + + /* clear disabled-flag first */ + con->flag &= ~CONSTRAINT_DISABLE; + + if (con->type == CONSTRAINT_TYPE_KINEMATIC) { + bKinematicConstraint *data = con->data; + + /* bad: we need a separate set of checks here as poletarget is + * optional... otherwise poletarget must exist too or else + * the constraint is deemed invalid + */ + /* default IK check ... */ + if (BKE_object_exists_check(data->tar) == 0) { + data->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->tar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + + if (data->poletar) { + if (BKE_object_exists_check(data->poletar) == 0) { + data->poletar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->poletar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + } + /* ... can be overwritten here */ + BIK_test_constraint(owner, con); + /* targets have already been checked for this */ + check_targets = false; + } + else if (con->type == CONSTRAINT_TYPE_PIVOT) { + bPivotConstraint *data = con->data; + + /* target doesn't have to exist, but if it is non-null, it must exist! */ + if (data->tar && BKE_object_exists_check(data->tar) == 0) { + data->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->tar == owner) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { + con->flag |= CONSTRAINT_DISABLE; + } + } + + /* targets have already been checked for this */ + check_targets = false; + } + else if (con->type == CONSTRAINT_TYPE_ACTION) { + bActionConstraint *data = con->data; + + /* validate action */ + if (data->act == NULL) { + /* must have action */ + con->flag |= CONSTRAINT_DISABLE; + } + else if (data->act->idroot != ID_OB) { + /* only object-rooted actions can be used */ + data->act = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + } + else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) { + bFollowPathConstraint *data = con->data; + + /* don't allow track/up axes to be the same */ + if (data->upflag == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + if (data->upflag + 3 == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_TRACKTO) { + bTrackToConstraint *data = con->data; + + /* don't allow track/up axes to be the same */ + if (data->reserved2 == data->reserved1) + con->flag |= CONSTRAINT_DISABLE; + if (data->reserved2 + 3 == data->reserved1) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_LOCKTRACK) { + bLockTrackConstraint *data = con->data; + + if (data->lockflag == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + if (data->lockflag + 3 == data->trackflag) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_SPLINEIK) { + bSplineIKConstraint *data = con->data; + + /* if the number of points does not match the amount required by the chain length, + * free the points array and request a rebind... + */ + if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { + /* free the points array */ + if (data->points) { + MEM_freeN(data->points); + data->points = NULL; + } + + /* clear the bound flag, forcing a rebind next time this is evaluated */ + data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; + } + } + else if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) { + bFollowTrackConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) { + if (data->clip != NULL && data->track[0]) { + MovieTracking *tracking = &data->clip->tracking; + MovieTrackingObject *tracking_object; + + if (data->object[0]) + tracking_object = BKE_tracking_object_get_named(tracking, data->object); + else + tracking_object = BKE_tracking_object_get_camera(tracking); + + if (!tracking_object) { + con->flag |= CONSTRAINT_DISABLE; + } + else { + if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track)) + con->flag |= CONSTRAINT_DISABLE; + } + } + else { + con->flag |= CONSTRAINT_DISABLE; + } + } + } + else if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) { + bCameraSolverConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) + con->flag |= CONSTRAINT_DISABLE; + } + else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = con->data; + + if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) + con->flag |= CONSTRAINT_DISABLE; + } + + /* Check targets for constraints */ + if (check_targets && cti && cti->get_constraint_targets) { + cti->get_constraint_targets(con, &targets); + + /* disable and clear constraints targets that are incorrect */ + for (ct = targets.first; ct; ct = ct->next) { + /* general validity checks (for those constraints that need this) */ + if (BKE_object_exists_check(ct->tar) == 0) { + /* object doesn't exist, but constraint requires target */ + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else if (ct->tar == owner) { + if (type == CONSTRAINT_OBTYPE_BONE) { + if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) { + /* bone must exist in armature... */ + /* TODO: clear subtarget? */ + con->flag |= CONSTRAINT_DISABLE; + } + else if (STREQ(pchan->name, ct->subtarget)) { + /* cannot target self */ + ct->subtarget[0] = '\0'; + con->flag |= CONSTRAINT_DISABLE; + } + } + else { + /* cannot use self as target */ + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + } + + /* target checks for specific constraints */ + if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) { + if (ct->tar) { + if (ct->tar->type != OB_CURVE) { + ct->tar = NULL; + con->flag |= CONSTRAINT_DISABLE; + } + else { + Curve *cu = ct->tar->data; + + /* auto-set 'Path' setting on curve so this works */ + cu->flag |= CU_PATH; + } + } + } + } + + /* free any temporary targets */ + if (cti->flush_constraint_targets) + cti->flush_constraint_targets(con, &targets, 0); + } +} + +static int constraint_type_get(Object *owner, bPoseChannel *pchan) { - bConstraint *curcon; - ListBase *conlist = NULL; int type; - - if (owner == NULL) return; - /* Check parents */ if (pchan) { switch (owner->type) { @@ -284,7 +488,22 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) } else type = CONSTRAINT_OBTYPE_OBJECT; + return type; +} + +/* checks validity of object pointers, and NULLs, + * if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag. + */ +static void test_constraints(Object *owner, bPoseChannel *pchan) +{ + bConstraint *curcon; + ListBase *conlist = NULL; + int type; + if (owner == NULL) return; + + type = constraint_type_get(owner, pchan); + /* Get the constraint list for this object */ switch (type) { case CONSTRAINT_OBTYPE_OBJECT: @@ -298,213 +517,7 @@ static void test_constraints(Object *owner, bPoseChannel *pchan) /* Check all constraints - is constraint valid? */ if (conlist) { for (curcon = conlist->first; curcon; curcon = curcon->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); - ListBase targets = {NULL, NULL}; - bConstraintTarget *ct; - - /* clear disabled-flag first */ - curcon->flag &= ~CONSTRAINT_DISABLE; - - if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) { - bKinematicConstraint *data = curcon->data; - - /* bad: we need a separate set of checks here as poletarget is - * optional... otherwise poletarget must exist too or else - * the constraint is deemed invalid - */ - /* default IK check ... */ - if (BKE_object_exists_check(data->tar) == 0) { - data->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->tar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - if (data->poletar) { - if (BKE_object_exists_check(data->poletar) == 0) { - data->poletar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->poletar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - } - /* ... can be overwritten here */ - BIK_test_constraint(owner, curcon); - /* targets have already been checked for this */ - continue; - } - else if (curcon->type == CONSTRAINT_TYPE_PIVOT) { - bPivotConstraint *data = curcon->data; - - /* target doesn't have to exist, but if it is non-null, it must exist! */ - if (data->tar && BKE_object_exists_check(data->tar) == 0) { - data->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->tar == owner) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - /* targets have already been checked for this */ - continue; - } - else if (curcon->type == CONSTRAINT_TYPE_ACTION) { - bActionConstraint *data = curcon->data; - - /* validate action */ - if (data->act == NULL) { - /* must have action */ - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (data->act->idroot != ID_OB) { - /* only object-rooted actions can be used */ - data->act = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else if (curcon->type == CONSTRAINT_TYPE_FOLLOWPATH) { - bFollowPathConstraint *data = curcon->data; - - /* don't allow track/up axes to be the same */ - if (data->upflag == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->upflag + 3 == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_TRACKTO) { - bTrackToConstraint *data = curcon->data; - - /* don't allow track/up axes to be the same */ - if (data->reserved2 == data->reserved1) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->reserved2 + 3 == data->reserved1) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_LOCKTRACK) { - bLockTrackConstraint *data = curcon->data; - - if (data->lockflag == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - if (data->lockflag + 3 == data->trackflag) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) { - bSplineIKConstraint *data = curcon->data; - - /* if the number of points does not match the amount required by the chain length, - * free the points array and request a rebind... - */ - if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) { - /* free the points array */ - if (data->points) { - MEM_freeN(data->points); - data->points = NULL; - } - - /* clear the bound flag, forcing a rebind next time this is evaluated */ - data->flag &= ~CONSTRAINT_SPLINEIK_BOUND; - } - } - else if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) { - bFollowTrackConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) { - if (data->clip != NULL && data->track[0]) { - MovieTracking *tracking = &data->clip->tracking; - MovieTrackingObject *tracking_object; - - if (data->object[0]) - tracking_object = BKE_tracking_object_get_named(tracking, data->object); - else - tracking_object = BKE_tracking_object_get_camera(tracking); - - if (!tracking_object) { - curcon->flag |= CONSTRAINT_DISABLE; - } - else { - if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track)) - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else { - curcon->flag |= CONSTRAINT_DISABLE; - } - } - } - else if (curcon->type == CONSTRAINT_TYPE_CAMERASOLVER) { - bCameraSolverConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) { - bObjectSolverConstraint *data = curcon->data; - - if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL)) - curcon->flag |= CONSTRAINT_DISABLE; - } - - /* Check targets for constraints */ - if (cti && cti->get_constraint_targets) { - cti->get_constraint_targets(curcon, &targets); - - /* disable and clear constraints targets that are incorrect */ - for (ct = targets.first; ct; ct = ct->next) { - /* general validity checks (for those constraints that need this) */ - if (BKE_object_exists_check(ct->tar) == 0) { - /* object doesn't exist, but constraint requires target */ - ct->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (ct->tar == owner) { - if (type == CONSTRAINT_OBTYPE_BONE) { - if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) { - /* bone must exist in armature... */ - /* TODO: clear subtarget? */ - curcon->flag |= CONSTRAINT_DISABLE; - } - else if (STREQ(pchan->name, ct->subtarget)) { - /* cannot target self */ - ct->subtarget[0] = '\0'; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - else { - /* cannot use self as target */ - ct->tar = NULL; - curcon->flag |= CONSTRAINT_DISABLE; - } - } - - /* target checks for specific constraints */ - 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; - curcon->flag |= CONSTRAINT_DISABLE; - } - else { - Curve *cu = ct->tar->data; - - /* auto-set 'Path' setting on curve so this works */ - cu->flag |= CU_PATH; - } - } - } - } - - /* free any temporary targets */ - if (cti->flush_constraint_targets) - cti->flush_constraint_targets(curcon, &targets, 0); - } + test_constraint(owner, pchan, curcon, type); } } } @@ -524,6 +537,26 @@ void object_test_constraints(Object *owner) } } +static void object_test_constraint(Object *owner, bConstraint *con) +{ + if (owner->type == OB_ARMATURE && owner->pose) { + if (BLI_findindex(&owner->constraints, con) != -1) { + test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT); + } + else { + bPoseChannel *pchan; + for (pchan = owner->pose->chanbase.first; pchan; pchan = pchan->next) { + if (BLI_findindex(&pchan->constraints, con) != -1) { + test_constraint(owner, pchan, con, CONSTRAINT_OBTYPE_BONE); + break; + } + } + } + } + else { + test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT); + } +} /************************ generic functions for operators using constraint names and data context *********************/ @@ -1161,20 +1194,49 @@ void ED_object_constraint_update(Object *ob) DAG_id_tag_update(&ob->id, OB_RECALC_OB); } +static void object_pose_tag_update(Main *bmain, Object *ob) +{ + BKE_pose_tag_recalc(bmain, ob->pose); /* Checks & sort pose channels. */ + if (ob->proxy && ob->adt) { + /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded + * after calling `BKE_pose_rebuild()`, which causes T43872. + * Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones + * or not... + * XXX Temp hack until new depsgraph hopefully solves this. */ + ob->adt->recalc |= ADT_RECALC_ANIM; + } +} + void ED_object_constraint_dependency_update(Main *bmain, Object *ob) { ED_object_constraint_update(ob); if (ob->pose) { - ob->pose->flag |= POSE_RECALC; /* Checks & sort pose channels. */ - if (ob->proxy && ob->adt) { - /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded - * after calling `BKE_pose_rebuild()`, which causes T43872. - * Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones - * or not... - * XXX Temp hack until new depsgraph hopefully solves this. */ - ob->adt->recalc |= ADT_RECALC_ANIM; - } + object_pose_tag_update(bmain, ob); + } + DAG_relations_tag_update(bmain); +} + +void ED_object_constraint_tag_update(Object *ob, bConstraint *con) +{ + if (ob->pose) { + BKE_pose_tag_update_constraint_flags(ob->pose); + } + + object_test_constraint(ob, con); + + if (ob->type == OB_ARMATURE) + DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB); + else + DAG_id_tag_update(&ob->id, OB_RECALC_OB); +} + +void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstraint *con) +{ + ED_object_constraint_tag_update(ob, con); + + if (ob->pose) { + object_pose_tag_update(bmain, ob); } DAG_relations_tag_update(bmain); } @@ -1198,7 +1260,10 @@ static int constraint_delete_exec(bContext *C, wmOperator *UNUSED(op)) /* there's no active constraint now, so make sure this is the case */ BKE_constraints_active_set(&ob->constraints, NULL); ED_object_constraint_update(ob); /* needed to set the flags on posebones correctly */ - + + /* relatiols */ + DAG_relations_tag_update(CTX_data_main(C)); + /* notifiers */ WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, ob); @@ -1422,8 +1487,8 @@ static int pose_constraint_copy_exec(bContext *C, wmOperator *op) BKE_constraints_copy(&chan->constraints, &pchan->constraints, true); /* update flags (need to add here, not just copy) */ chan->constflag |= pchan->constflag; - - ob->pose->flag |= POSE_RECALC; + + BKE_pose_tag_recalc(bmain, ob->pose); DAG_id_tag_update((ID *)ob, OB_RECALC_DATA); } } @@ -1600,7 +1665,7 @@ static short get_new_constraint_target(bContext *C, int con_type, Object **tar_o Object *obt; /* add new target object */ - obt = BKE_object_add(bmain, scene, OB_EMPTY); + obt = BKE_object_add(bmain, scene, OB_EMPTY, NULL); /* set layers OK */ newbase = BASACT; @@ -1734,7 +1799,7 @@ static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase DAG_relations_tag_update(bmain); if ((ob->type == OB_ARMATURE) && (pchan)) { - ob->pose->flag |= POSE_RECALC; /* sort pose channels */ + BKE_pose_tag_recalc(bmain, ob->pose); /* sort pose channels */ if (BKE_constraints_proxylocked_owner(ob, pchan) && ob->adt) { /* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded * after calling `BKE_pose_rebuild()`, which causes T43872. diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index b39e8470a95..d6a1694b4b0 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -29,8 +29,6 @@ * \ingroup edobj */ -#include "MEM_guardedalloc.h" - #include "DNA_mesh_types.h" #include "DNA_modifier_types.h" #include "DNA_object_types.h" @@ -59,7 +57,6 @@ #include "ED_object.h" #include "UI_interface.h" -#include "UI_resources.h" #include "object_intern.h" @@ -325,7 +322,7 @@ static bool data_transfer_exec_is_object_valid( static int data_transfer_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *ob_src = CTX_data_active_object(C); + Object *ob_src = ED_object_active_context(C); ListBase ctx_objects; CollectionPointerLink *ctx_ob_dst; @@ -419,7 +416,7 @@ static int data_transfer_exec(bContext *C, wmOperator *op) /* Note this context poll is only really partial, it cannot check for all possible invalid cases. */ static int data_transfer_poll(bContext *C) { - Object *ob = ED_object_context(C); + Object *ob = ED_object_active_context(C); ID *data = (ob) ? ob->data : NULL; return (ob && ob->type == OB_MESH && data); } @@ -534,7 +531,7 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot) /* Mapping options and filtering. */ RNA_def_boolean(ot->srna, "use_object_transform", true, "Object Transform", - "Evaluate source and destination meshes in their respective object spaces"); + "Evaluate source and destination meshes in global space"); RNA_def_boolean(ot->srna, "use_max_distance", false, "Only Neighbor Geometry", "Source elements must be closer than given distance from destination one"); prop = RNA_def_float(ot->srna, "max_distance", 1.0f, 0.0f, FLT_MAX, "Max Distance", diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index f7d51eb403f..3499a3cc364 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -65,6 +65,7 @@ #include "BKE_curve.h" #include "BKE_effect.h" #include "BKE_depsgraph.h" +#include "BKE_global.h" #include "BKE_image.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -318,7 +319,7 @@ void OBJECT_OT_hide_render_set(wmOperatorType *ot) * Load EditMode data back into the object, * optionally freeing the editmode data. */ -static bool ED_object_editmode_load_ex(Object *obedit, const bool freedata) +static bool ED_object_editmode_load_ex(Main *bmain, Object *obedit, const bool freedata) { if (obedit == NULL) { return false; @@ -348,6 +349,11 @@ static bool ED_object_editmode_load_ex(Object *obedit, const bool freedata) ED_armature_from_edit(obedit->data); if (freedata) ED_armature_edit_free(obedit->data); + /* TODO(sergey): Pose channels might have been changed, so need + * to inform dependency graph about this. But is it really the + * best place to do this? + */ + DAG_relations_tag_update(bmain); } else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { load_editNurb(obedit); @@ -376,7 +382,8 @@ static bool ED_object_editmode_load_ex(Object *obedit, const bool freedata) bool ED_object_editmode_load(Object *obedit) { - return ED_object_editmode_load_ex(obedit, false); + /* TODO(sergey): use proper main here? */ + return ED_object_editmode_load_ex(G.main, obedit, false); } void ED_object_editmode_exit(bContext *C, int flag) @@ -389,7 +396,7 @@ void ED_object_editmode_exit(bContext *C, int flag) if (flag & EM_WAITCURSOR) waitcursor(1); - if (ED_object_editmode_load_ex(obedit, freedata) == false) { + if (ED_object_editmode_load_ex(CTX_data_main(C), obedit, freedata) == false) { /* in rare cases (background mode) its possible active object * is flagged for editmode, without 'obedit' being set [#35489] */ if (UNLIKELY(scene->basact && (scene->basact->object->mode & OB_MODE_EDIT))) { @@ -898,6 +905,8 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event) base->object->rdamping = ob->rdamping; base->object->min_vel = ob->min_vel; base->object->max_vel = ob->max_vel; + base->object->min_angvel = ob->min_angvel; + base->object->max_angvel = ob->max_angvel; if (ob->gameflag & OB_BOUNDS) { base->object->collision_boundtype = ob->collision_boundtype; } @@ -1769,6 +1778,77 @@ void OBJECT_OT_game_property_remove(wmOperatorType *ot) RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Property index to remove ", 0, INT_MAX); } +#define GAME_PROPERTY_MOVE_UP 1 +#define GAME_PROPERTY_MOVE_DOWN -1 + +static int game_property_move(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bProperty *prop; + bProperty *otherprop = NULL; + const int index = RNA_int_get(op->ptr, "index"); + const int dir = RNA_enum_get(op->ptr, "direction"); + + if (ob == NULL) + return OPERATOR_CANCELLED; + + prop = BLI_findlink(&ob->prop, index); + /* invalid index */ + if (prop == NULL) + return OPERATOR_CANCELLED; + + if (dir == GAME_PROPERTY_MOVE_UP) { + otherprop = prop->prev; + } + else if (dir == GAME_PROPERTY_MOVE_DOWN) { + otherprop = prop->next; + } + else { + BLI_assert(0); + } + + if (prop && otherprop) { + BLI_listbase_swaplinks(&ob->prop, prop, otherprop); + + WM_event_add_notifier(C, NC_LOGIC, NULL); + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } +} + +void OBJECT_OT_game_property_move(wmOperatorType *ot) +{ + static EnumPropertyItem direction_property_move[] = { + {GAME_PROPERTY_MOVE_UP, "UP", 0, "Up", ""}, + {GAME_PROPERTY_MOVE_DOWN, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Move Game Property"; + ot->description = "Move game property"; + ot->idname = "OBJECT_OT_game_property_move"; + + /* api callbacks */ + ot->exec = game_property_move; + ot->poll = ED_operator_object_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Property index to move", 0, INT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN); + RNA_def_enum(ot->srna, "direction", direction_property_move, 0, "Direction", + "Direction for moving the property"); +} + +#undef GAME_PROPERTY_MOVE_UP +#undef GAME_PROPERTY_MOVE_DOWN + #define COPY_PROPERTIES_REPLACE 1 #define COPY_PROPERTIES_MERGE 2 #define COPY_PROPERTIES_COPY 3 @@ -1966,6 +2046,8 @@ static int game_physics_copy_exec(bContext *C, wmOperator *UNUSED(op)) ob_iter->rdamping = ob->rdamping; ob_iter->min_vel = ob->min_vel; ob_iter->max_vel = ob->max_vel; + ob_iter->min_angvel = ob->min_angvel; + ob_iter->max_angvel = ob->max_angvel; ob_iter->obstacleRad = ob->obstacleRad; ob_iter->mass = ob->mass; copy_v3_v3(ob_iter->anisotropicFriction, ob->anisotropicFriction); diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c index 1d764ced524..8797b75a801 100644 --- a/source/blender/editors/object/object_hook.c +++ b/source/blender/editors/object/object_hook.c @@ -448,11 +448,12 @@ static Object *add_hook_object_new(Main *bmain, Scene *scene, Object *obedit) Base *base, *basedit; Object *ob; - ob = BKE_object_add(bmain, scene, OB_EMPTY); + ob = BKE_object_add(bmain, scene, OB_EMPTY, NULL); basedit = BKE_scene_base_find(scene, obedit); - base = BKE_scene_base_find(scene, ob); + base = scene->basact; base->lay = ob->lay = obedit->lay; + BLI_assert(scene->basact->object == ob); /* icky, BKE_object_add sets new base as active. * so set it back to the original edit object */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 64fddf1b6a1..6344e04ef1b 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -32,17 +32,12 @@ #define __OBJECT_INTERN_H__ struct wmOperatorType; -struct KeyBlock; -struct Lattice; -struct Curve; struct Object; -struct Mesh; struct bContext; struct StructRNA; struct wmOperator; struct ModifierData; -struct HookModifierData; /* add hook menu */ enum eObject_Hook_Add_Mode { @@ -99,6 +94,7 @@ void OBJECT_OT_game_property_new(struct wmOperatorType *ot); void OBJECT_OT_game_property_remove(struct wmOperatorType *ot); void OBJECT_OT_game_property_copy(struct wmOperatorType *ot); void OBJECT_OT_game_property_clear(struct wmOperatorType *ot); +void OBJECT_OT_game_property_move(struct wmOperatorType *ot); void OBJECT_OT_logic_bricks_copy(struct wmOperatorType *ot); void OBJECT_OT_game_physics_copy(struct wmOperatorType *ot); @@ -179,6 +175,7 @@ void OBJECT_OT_multires_higher_levels_delete(struct wmOperatorType *ot); void OBJECT_OT_multires_base_apply(struct wmOperatorType *ot); void OBJECT_OT_multires_external_save(struct wmOperatorType *ot); void OBJECT_OT_multires_external_pack(struct wmOperatorType *ot); +void OBJECT_OT_correctivesmooth_bind(struct wmOperatorType *ot); void OBJECT_OT_meshdeform_bind(struct wmOperatorType *ot); void OBJECT_OT_explode_refresh(struct wmOperatorType *ot); void OBJECT_OT_ocean_bake(struct wmOperatorType *ot); @@ -233,7 +230,7 @@ void OBJECT_OT_vertex_group_levels(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_lock(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_fix(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_invert(struct wmOperatorType *ot); -void OBJECT_OT_vertex_group_blend(struct wmOperatorType *ot); +void OBJECT_OT_vertex_group_smooth(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_clean(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_quantize(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_limit_total(struct wmOperatorType *ot); @@ -248,7 +245,7 @@ void OBJECT_OT_vertex_weight_normalize_active_vertex(struct wmOperatorType *ot); void OBJECT_OT_vertex_weight_copy(struct wmOperatorType *ot); /* object_warp.c */ -void OBJECT_OT_vertex_warp(struct wmOperatorType *ot); +void TRANSFORM_OT_vertex_warp(struct wmOperatorType *ot); /* object_shapekey.c */ void OBJECT_OT_shape_key_add(struct wmOperatorType *ot); @@ -274,7 +271,7 @@ void OBJECT_OT_lod_add(struct wmOperatorType *ot); void OBJECT_OT_lod_remove(struct wmOperatorType *ot); /* object_random.c */ -void OBJECT_OT_vertex_random(struct wmOperatorType *ot); +void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot); /* object_transfer_data.c */ void OBJECT_OT_data_transfer(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_lattice.c b/source/blender/editors/object/object_lattice.c index b577dab8a77..76d9facf701 100644 --- a/source/blender/editors/object/object_lattice.c +++ b/source/blender/editors/object/object_lattice.c @@ -713,7 +713,7 @@ static int lattice_flip_exec(bContext *C, wmOperator *op) break; default: - printf("lattice_flip(): Unknown flipping axis (%d)\n", axis); + printf("lattice_flip(): Unknown flipping axis (%u)\n", axis); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 710cf2dbdd2..ce9693793a4 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -96,7 +96,7 @@ static void modifier_skin_customdata_delete(struct Object *ob); ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *scene, Object *ob, const char *name, int type) { ModifierData *md = NULL, *new_md = NULL; - ModifierTypeInfo *mti = modifierType_getInfo(type); + const ModifierTypeInfo *mti = modifierType_getInfo(type); /* only geometry objects should be able to get modifiers [#25291] */ if (!ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { @@ -152,10 +152,10 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc ob->pd = object_add_collision_fields(0); ob->pd->deflect = 1; - DAG_relations_tag_update(bmain); } - else if (type == eModifierType_Surface) - DAG_relations_tag_update(bmain); + else if (type == eModifierType_Surface) { + /* pass */ + } else if (type == eModifierType_Multires) { /* set totlvl from existing MDISPS layer if object already had it */ multiresModifier_set_levels_from_disps((MultiresModifierData *)new_md, ob); @@ -172,6 +172,7 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc } DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DAG_relations_tag_update(bmain); return new_md; } @@ -247,7 +248,7 @@ static bool object_has_modifier_cb(Object *ob, void *data) bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) { ModifierData *md; - int totlevel = *((int *)totlevel_v); + int totlevel = *((char *)totlevel_v); for (md = ob->modifiers.first; md; md = md->next) { if (md->type == eModifierType_Multires) { @@ -319,6 +320,8 @@ static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md, ob->mode &= ~OB_MODE_PARTICLE_EDIT; } + DAG_relations_tag_update(bmain); + BLI_remlink(&ob->modifiers, md); modifier_free(md); @@ -368,10 +371,10 @@ void ED_object_modifier_clear(Main *bmain, Object *ob) int ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *md) { if (md->prev) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); if (mti->type != eModifierTypeType_OnlyDeform) { - ModifierTypeInfo *nmti = modifierType_getInfo(md->prev->type); + const ModifierTypeInfo *nmti = modifierType_getInfo(md->prev->type); if (nmti->flags & eModifierTypeFlag_RequiresOriginalData) { BKE_report(reports, RPT_WARNING, "Cannot move above a modifier requiring original data"); @@ -389,10 +392,10 @@ int ED_object_modifier_move_up(ReportList *reports, Object *ob, ModifierData *md int ED_object_modifier_move_down(ReportList *reports, Object *ob, ModifierData *md) { if (md->next) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); if (mti->flags & eModifierTypeFlag_RequiresOriginalData) { - ModifierTypeInfo *nmti = modifierType_getInfo(md->next->type); + const ModifierTypeInfo *nmti = modifierType_getInfo(md->next->type); if (nmti->type != eModifierTypeType_OnlyDeform) { BKE_report(reports, RPT_WARNING, "Cannot move beyond a non-deforming modifier"); @@ -459,7 +462,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * if (totvert == 0) return 0; /* add new mesh */ - obn = BKE_object_add(bmain, scene, OB_MESH); + obn = BKE_object_add(bmain, scene, OB_MESH, NULL); me = obn->data; me->totvert = totvert; @@ -518,7 +521,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * static int modifier_apply_shape(ReportList *reports, Scene *scene, Object *ob, ModifierData *md) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); md->scene = scene; @@ -578,7 +581,7 @@ static int modifier_apply_shape(ReportList *reports, Scene *scene, Object *ob, M static int modifier_apply_obdata(ReportList *reports, Scene *scene, Object *ob, ModifierData *md) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); md->scene = scene; @@ -742,7 +745,7 @@ static EnumPropertyItem *modifier_add_itemf(bContext *C, PointerRNA *UNUSED(ptr) { Object *ob = ED_object_active_context(C); EnumPropertyItem *item = NULL, *md_item, *group_item = NULL; - ModifierTypeInfo *mti; + const ModifierTypeInfo *mti; int totitem = 0, a; if (!ob) @@ -1704,7 +1707,7 @@ static Object *modifier_skin_armature_create(Main *bmain, Scene *scene, Object * NULL, me->totvert); - arm_ob = BKE_object_add(bmain, scene, OB_ARMATURE); + arm_ob = BKE_object_add(bmain, scene, OB_ARMATURE, NULL); BKE_object_transform_copy(arm_ob, skin_ob); arm = arm_ob->data; arm->layer = 1; @@ -1815,6 +1818,73 @@ void OBJECT_OT_skin_armature_create(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; edit_modifier_properties(ot); } +/************************ delta mush bind operator *********************/ + +static int correctivesmooth_poll(bContext *C) +{ + return edit_modifier_poll_generic(C, &RNA_CorrectiveSmoothModifier, 0); +} + +static int correctivesmooth_bind_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_active_context(C); + CorrectiveSmoothModifierData *csmd = (CorrectiveSmoothModifierData *)edit_modifier_property_get(op, ob, eModifierType_CorrectiveSmooth); + bool is_bind; + + if (!csmd) { + return OPERATOR_CANCELLED; + } + + if (!modifier_isEnabled(scene, &csmd->modifier, eModifierMode_Realtime)) { + BKE_report(op->reports, RPT_ERROR, "Modifier is disabled"); + return OPERATOR_CANCELLED; + } + + is_bind = (csmd->bind_coords != NULL); + + MEM_SAFE_FREE(csmd->bind_coords); + MEM_SAFE_FREE(csmd->delta_cache); + + if (is_bind) { + /* toggle off */ + csmd->bind_coords_num = 0; + } + else { + /* signal to modifier to recalculate */ + csmd->bind_coords_num = (unsigned int)-1; + } + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int correctivesmooth_bind_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_modifier_invoke_properties(C, op)) + return correctivesmooth_bind_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Corrective Smooth Bind"; + ot->description = "Bind base pose in Corrective Smooth modifier"; + ot->idname = "OBJECT_OT_correctivesmooth_bind"; + + /* api callbacks */ + ot->poll = correctivesmooth_poll; + ot->invoke = correctivesmooth_bind_invoke; + ot->exec = correctivesmooth_bind_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_modifier_properties(ot); +} /************************ mdef bind operator *********************/ @@ -1978,7 +2048,7 @@ static void init_ocean_modifier_bake(struct Ocean *oc, struct OceanModifierData do_normals = (omd->flag & MOD_OCEAN_GENERATE_NORMALS); do_jacobian = (omd->flag & MOD_OCEAN_GENERATE_FOAM); - BKE_init_ocean(oc, omd->resolution * omd->resolution, omd->resolution * omd->resolution, omd->spatial_size, omd->spatial_size, + BKE_ocean_init(oc, omd->resolution * omd->resolution, omd->resolution * omd->resolution, omd->spatial_size, omd->spatial_size, omd->wind_velocity, omd->smallest_wave, 1.0, omd->wave_direction, omd->damp, omd->wave_alignment, omd->depth, omd->time, do_heightfield, do_chop, do_normals, do_jacobian, @@ -2036,7 +2106,7 @@ static void oceanbake_startjob(void *customdata, short *stop, short *do_update, G.is_break = false; /* XXX shared with render - replace with job 'stop' switch */ - BKE_bake_ocean(oj->ocean, oj->och, oceanbake_update, (void *)oj); + BKE_ocean_bake(oj->ocean, oj->och, oceanbake_update, (void *)oj); *do_update = true; *stop = 0; @@ -2047,7 +2117,7 @@ static void oceanbake_endjob(void *customdata) OceanBakeJob *oj = customdata; if (oj->ocean) { - BKE_free_ocean(oj->ocean); + BKE_ocean_free(oj->ocean); oj->ocean = NULL; } @@ -2078,7 +2148,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - och = BKE_init_ocean_cache(omd->cachepath, modifier_path_relbase(ob), + och = BKE_ocean_init_cache(omd->cachepath, modifier_path_relbase(ob), omd->bakestart, omd->bakeend, omd->wave_scale, omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); @@ -2112,11 +2182,11 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) } /* make a copy of ocean to use for baking - threadsafety */ - ocean = BKE_add_ocean(); + ocean = BKE_ocean_add(); init_ocean_modifier_bake(ocean, omd); #if 0 - BKE_bake_ocean(ocean, och); + BKE_ocean_bake(ocean, och); omd->oceancache = och; omd->cached = true; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 15eb9090ce5..22cac8638d9 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -144,6 +144,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_skin_radii_equalize); WM_operatortype_append(OBJECT_OT_skin_armature_create); + WM_operatortype_append(OBJECT_OT_correctivesmooth_bind); WM_operatortype_append(OBJECT_OT_meshdeform_bind); WM_operatortype_append(OBJECT_OT_explode_refresh); WM_operatortype_append(OBJECT_OT_ocean_bake); @@ -185,7 +186,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_group_fix); WM_operatortype_append(OBJECT_OT_vertex_group_invert); WM_operatortype_append(OBJECT_OT_vertex_group_levels); - WM_operatortype_append(OBJECT_OT_vertex_group_blend); + WM_operatortype_append(OBJECT_OT_vertex_group_smooth); WM_operatortype_append(OBJECT_OT_vertex_group_clean); WM_operatortype_append(OBJECT_OT_vertex_group_quantize); WM_operatortype_append(OBJECT_OT_vertex_group_limit_total); @@ -199,12 +200,13 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_vertex_weight_normalize_active_vertex); WM_operatortype_append(OBJECT_OT_vertex_weight_copy); - WM_operatortype_append(OBJECT_OT_vertex_warp); + WM_operatortype_append(TRANSFORM_OT_vertex_warp); WM_operatortype_append(OBJECT_OT_game_property_new); WM_operatortype_append(OBJECT_OT_game_property_remove); WM_operatortype_append(OBJECT_OT_game_property_copy); WM_operatortype_append(OBJECT_OT_game_property_clear); + WM_operatortype_append(OBJECT_OT_game_property_move); WM_operatortype_append(OBJECT_OT_logic_bricks_copy); WM_operatortype_append(OBJECT_OT_game_physics_copy); @@ -247,7 +249,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_lod_add); WM_operatortype_append(OBJECT_OT_lod_remove); - WM_operatortype_append(OBJECT_OT_vertex_random); + WM_operatortype_append(TRANSFORM_OT_vertex_random); WM_operatortype_append(OBJECT_OT_data_transfer); WM_operatortype_append(OBJECT_OT_datalayout_transfer); diff --git a/source/blender/editors/object/object_random.c b/source/blender/editors/object/object_random.c index 41b26b98047..a293f7a950e 100644 --- a/source/blender/editors/object/object_random.c +++ b/source/blender/editors/object/object_random.c @@ -124,12 +124,12 @@ static int object_rand_verts_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void OBJECT_OT_vertex_random(struct wmOperatorType *ot) +void TRANSFORM_OT_vertex_random(struct wmOperatorType *ot) { /* identifiers */ ot->name = "Randomize"; ot->description = "Randomize vertices"; - ot->idname = "OBJECT_OT_vertex_random"; + ot->idname = "TRANSFORM_OT_vertex_random"; /* api callbacks */ ot->exec = object_rand_verts_exec; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index bfa501c3732..c2d2bffbbe5 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -342,12 +342,10 @@ static int make_proxy_exec(bContext *C, wmOperator *op) 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); + /* Add new object for the proxy */ + newob = BKE_object_add(bmain, scene, OB_EMPTY, name); /* set layers OK */ newbase = BASACT; /* BKE_object_add sets active... */ @@ -573,6 +571,9 @@ void ED_object_parent(Object *ob, Object *par, const int type, const char *subst return; } + /* Other partypes are deprecated, do not use here! */ + BLI_assert(ELEM(type & PARTYPE, PAROBJECT, PARSKEL, PARVERT1, PARVERT3, PARBONE)); + /* this could use some more checks */ ob->parent = par; @@ -1574,13 +1575,13 @@ static int make_links_data_exec(bContext *C, wmOperator *op) DAG_id_tag_update(&ob_dst->id, 0); break; case MAKE_LINKS_ANIMDATA: - BKE_copy_animdata_id((ID *)ob_dst, (ID *)ob_src, false); + BKE_animdata_copy_id((ID *)ob_dst, (ID *)ob_src, false); if (ob_dst->data && ob_src->data) { if (obdata_id->lib) { is_lib = true; break; } - BKE_copy_animdata_id((ID *)ob_dst->data, (ID *)ob_src->data, false); + BKE_animdata_copy_id((ID *)ob_dst->data, (ID *)ob_src->data, false); } DAG_id_tag_update(&ob_dst->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); break; @@ -1871,7 +1872,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) case OB_MESH: ob->data = me = BKE_mesh_copy(ob->data); if (me->key) - BKE_copy_animdata_id_action((ID *)me->key); + BKE_animdata_copy_id_action((ID *)me->key); break; case OB_MBALL: ob->data = BKE_mball_copy(ob->data); @@ -1883,12 +1884,12 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) ID_NEW(cu->bevobj); ID_NEW(cu->taperobj); if (cu->key) - BKE_copy_animdata_id_action((ID *)cu->key); + BKE_animdata_copy_id_action((ID *)cu->key); break; case OB_LATTICE: ob->data = lat = BKE_lattice_copy(ob->data); if (lat->key) - BKE_copy_animdata_id_action((ID *)lat->key); + BKE_animdata_copy_id_action((ID *)lat->key); break; case OB_ARMATURE: DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -1909,7 +1910,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag) * AnimData structure, which is not what we want. * (sergey) */ - BKE_copy_animdata_id_action((ID *)ob->data); + BKE_animdata_copy_id_action((ID *)ob->data); id->us--; id->newid = ob->data; @@ -1933,7 +1934,7 @@ static void single_object_action_users(Scene *scene, const int flag) ob = base->object; if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - BKE_copy_animdata_id_action(&ob->id); + BKE_animdata_copy_id_action(&ob->id); } } } @@ -1956,7 +1957,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture if (ma->id.us > 1) { man = BKE_material_copy(ma); - BKE_copy_animdata_id_action(&man->id); + BKE_animdata_copy_id_action(&man->id); man->id.us = 0; assign_material(ob, man, a, BKE_MAT_ASSIGN_USERPREF); @@ -1967,7 +1968,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture if (tex->id.us > 1) { tex->id.us--; tex = BKE_texture_copy(tex); - BKE_copy_animdata_id_action(&tex->id); + BKE_animdata_copy_id_action(&tex->id); man->mtex[b]->tex = tex; } } @@ -1994,7 +1995,7 @@ static void do_single_tex_user(Tex **from) } else if (tex->id.us > 1) { texn = BKE_texture_copy(tex); - BKE_copy_animdata_id_action(&texn->id); + BKE_animdata_copy_id_action(&texn->id); tex->id.newid = (ID *)texn; tex->id.us--; *from = texn; diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index fb9687da6df..ed71af71ac9 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -48,7 +48,6 @@ #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" -#include "DNA_scene_types.h" #include "DNA_object_types.h" #include "BKE_context.h" @@ -78,7 +77,7 @@ static void ED_object_shape_key_add(bContext *C, Object *ob, const bool from_mix) { KeyBlock *kb; - if ((kb = BKE_object_insert_shape_key(ob, NULL, from_mix))) { + if ((kb = BKE_object_shapekey_insert(ob, NULL, from_mix))) { Key *key = BKE_key_from_object(ob); /* for absolute shape keys, new keys may not be added last */ ob->shapenr = BLI_findindex(&key->block, kb) + 1; @@ -89,89 +88,21 @@ static void ED_object_shape_key_add(bContext *C, Object *ob, const bool from_mix /*********************** remove shape key ***********************/ -static bool ED_object_shape_key_remove_all(Main *bmain, Object *ob) +static bool object_shapekey_remove(Main *bmain, Object *ob) { - Key *key; + KeyBlock *kb; + Key *key = BKE_key_from_object(ob); - key = BKE_key_from_object(ob); - if (key == NULL) + if (key == NULL) { return false; - - switch (GS(key->from->name)) { - case ID_ME: ((Mesh *)key->from)->key = NULL; break; - case ID_CU: ((Curve *)key->from)->key = NULL; break; - case ID_LT: ((Lattice *)key->from)->key = NULL; break; } - BKE_libblock_free_us(bmain, key); - - return true; -} - -static bool ED_object_shape_key_remove(Main *bmain, Object *ob) -{ - KeyBlock *kb, *rkb; - Key *key; - - key = BKE_key_from_object(ob); - if (key == NULL) - return false; - kb = BLI_findlink(&key->block, ob->shapenr - 1); - if (kb) { - for (rkb = key->block.first; rkb; rkb = rkb->next) { - if (rkb->relative == ob->shapenr - 1) { - /* remap to the 'Basis' */ - rkb->relative = 0; - } - else if (rkb->relative >= ob->shapenr) { - /* Fix positional shift of the keys when kb is deleted from the list */ - rkb->relative -= 1; - } - } - - BLI_remlink(&key->block, kb); - key->totkey--; - if (key->refkey == kb) { - key->refkey = key->block.first; - - if (key->refkey) { - /* apply new basis key on original data */ - switch (ob->type) { - case OB_MESH: - BKE_keyblock_convert_to_mesh(key->refkey, ob->data); - break; - case OB_CURVE: - case OB_SURF: - BKE_keyblock_convert_to_curve(key->refkey, ob->data, BKE_curve_nurbs_get(ob->data)); - break; - case OB_LATTICE: - BKE_keyblock_convert_to_lattice(key->refkey, ob->data); - break; - } - } - } - - if (kb->data) MEM_freeN(kb->data); - MEM_freeN(kb); - - if (ob->shapenr > 1) { - ob->shapenr--; - } + return BKE_object_shapekey_remove(bmain, ob, kb); } - - if (key->totkey == 0) { - switch (GS(key->from->name)) { - case ID_ME: ((Mesh *)key->from)->key = NULL; break; - case ID_CU: ((Curve *)key->from)->key = NULL; break; - case ID_LT: ((Lattice *)key->from)->key = NULL; break; - } - BKE_libblock_free_us(bmain, key); - } - - return true; + return false; } static bool object_shape_key_mirror(bContext *C, Object *ob, @@ -332,6 +263,9 @@ static int shape_key_add_exec(bContext *C, wmOperator *op) ED_object_shape_key_add(C, ob, from_mix); + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DAG_relations_tag_update(CTX_data_main(C)); + return OPERATOR_FINISHED; } @@ -360,14 +294,15 @@ static int shape_key_remove_exec(bContext *C, wmOperator *op) bool changed = false; if (RNA_boolean_get(op->ptr, "all")) { - changed = ED_object_shape_key_remove_all(bmain, ob); + changed = BKE_object_shapekey_free(bmain, ob); } else { - changed = ED_object_shape_key_remove(bmain, ob); + changed = object_shapekey_remove(bmain, ob); } if (changed) { DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DAG_relations_tag_update(CTX_data_main(C)); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index 92fe5ded4dd..340b662c0ef 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -939,8 +939,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) /* convert the offset to parent space */ BKE_object_to_mat4(ob, obmat); - copy_v3_v3(centn, cent); - mul_mat3_m4_v3(obmat, centn); /* omit translation part */ + mul_v3_mat3_m4v3(centn, obmat, cent); /* omit translation part */ add_v3_v3(ob->loc, centn); @@ -970,8 +969,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) ob_other->flag |= OB_DONE; 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); /* omit translation part */ + mul_v3_mat3_m4v3(centn, ob_other->obmat, cent); /* 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 f885cbbb24f..f2b2923ee0d 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -50,6 +50,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_linklist_stack.h" +#include "BLI_stackdefines.h" #include "BKE_context.h" @@ -427,6 +428,48 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) return true; } +void ED_vgroup_parray_to_weight_array( + const MDeformVert **dvert_array, const int dvert_tot, + float *dvert_weights, const int def_nr) +{ + int i; + + for (i = 0; i < dvert_tot; i++) { + const MDeformVert *dv = dvert_array[i]; + dvert_weights[i] = dv ? defvert_find_weight(dv, def_nr) : 0.0f; + } +} + +void ED_vgroup_parray_from_weight_array( + MDeformVert **dvert_array, const int dvert_tot, + const float *dvert_weights, const int def_nr, const bool remove_zero) +{ + int i; + + for (i = 0; i < dvert_tot; i++) { + MDeformVert *dv = dvert_array[i]; + if (dv) { + if (dvert_weights[i] > 0.0f) { + MDeformWeight *dw = defvert_verify_index(dv, def_nr); + BLI_assert(IN_RANGE_INCL(dvert_weights[i], 0.0f, 1.0f)); + dw->weight = dvert_weights[i]; + } + else { + MDeformWeight *dw = defvert_find_index(dv, def_nr); + if (dw) { + if (remove_zero) { + defvert_remove_group(dv, dw); + } + else { + dw->weight = 0.0f; + } + } + } + } + } +} + + /* TODO, cache flip data to speedup calls within a loop. */ static void mesh_defvert_mirror_update_internal(Object *ob, MDeformVert *dvert_dst, MDeformVert *dvert_src, @@ -1177,7 +1220,7 @@ static void getVerticalAndHorizontalChange(const float norm[3], float d, const f dists[index] = dot_v3v3(norm, end) + d; /* vertical change */ changes[index][0] = dists[index] - distToStart; - //printf("vc %f %f\n", distance(end, projB, 3)-distance(start, projA, 3), changes[index][0]); + //printf("vc %f %f\n", distance(end, projB, 3) - distance(start, projA, 3), changes[index][0]); /* horizontal change */ changes[index][1] = len_v3v3(projA, projB); } @@ -1631,9 +1674,17 @@ static void vgroup_invert_subset(Object *ob, } } -static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const int vgroup_tot, - const int subset_count, - const float fac) +enum { + WEIGHT_SMOOTH_ALL = -1, + WEIGHT_SMOOTH_DESELECT = false, + WEIGHT_SMOOTH_SELECT = true, +}; + +static void vgroup_smooth_subset( + Object *ob, const bool *vgroup_validmap, const int vgroup_tot, + const int subset_count, + const float fac, const int repeat, + const float fac_expand, const int source) { const float ifac = 1.0f - fac; MDeformVert **dvert_array = NULL; @@ -1642,6 +1693,10 @@ static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const i float *vgroup_subset_weights = BLI_array_alloca(vgroup_subset_weights, subset_count); const bool use_mirror = (ob->type == OB_MESH) ? (((Mesh *)ob->data)->editflag & ME_EDIT_MIRROR_X) != 0 : false; + const int expand_sign = signum_i(fac_expand); + const float expand = fabsf(fac_expand); + const float iexpand = 1.0f - expand; + BMEditMesh *em = BKE_editmesh_from_object(ob); BMesh *bm = em ? em->bm : NULL; Mesh *me = em ? NULL : ob->data; @@ -1649,7 +1704,15 @@ static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const i MeshElemMap *emap; int *emap_mem; - BLI_SMALLSTACK_DECLARE(dv_stack, MDeformVert *); + float *weight_accum_prev; + float *weight_accum_curr; + + unsigned int subset_index; + + /* vertex indices that will be smoothed, (only to avoid iterating over verts that do nothing) */ + unsigned int *verts_used; + STACK_DECLARE(verts_used); + BKE_object_defgroup_subset_to_index_array(vgroup_validmap, vgroup_tot, vgroup_subset_map); ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); @@ -1657,87 +1720,159 @@ static void vgroup_blend_subset(Object *ob, const bool *vgroup_validmap, const i if (bm) { BM_mesh_elem_table_ensure(bm, BM_VERT); + BM_mesh_elem_index_ensure(bm, BM_VERT); emap = NULL; emap_mem = NULL; } else { - BKE_mesh_vert_edge_map_create(&emap, &emap_mem, - me->medge, me->totvert, me->totedge); + BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge); } + weight_accum_prev = MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__); + weight_accum_curr = MEM_mallocN(sizeof(*weight_accum_curr) * dvert_tot, __func__); + + verts_used = MEM_mallocN(sizeof(*verts_used) * dvert_tot, __func__); + STACK_INIT(verts_used, dvert_tot); - for (i = 0; i < dvert_tot; i++) { - MDeformVert *dv; - int dv_stack_tot = 0; - int j; - /* in case its not selected */ - if (bm) { + /* initialize used verts */ + if (bm) { + for (i = 0; i < dvert_tot; i++) { BMVert *v = BM_vert_at_index(bm, i); if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { BMIter eiter; BMEdge *e; BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { BMVert *v_other = BM_edge_other_vert(e, v); - const int i_other = BM_elem_index_get(v_other); - - if (BM_elem_flag_test(v_other, BM_ELEM_SELECT) == 0) { - dv = dvert_array[i_other]; - BLI_SMALLSTACK_PUSH(dv_stack, dv); - dv_stack_tot++; + if ((source == WEIGHT_SMOOTH_ALL) || + (source == (BM_elem_flag_test(v_other, BM_ELEM_SELECT) != 0))) + { + STACK_PUSH(verts_used, i); + break; } } } } - else { + } + else { + for (i = 0; i < dvert_tot; i++) { MVert *v = &me->mvert[i]; if (v->flag & SELECT) { + int j; for (j = 0; j < emap[i].count; j++) { MEdge *e = &me->medge[emap[i].indices[j]]; const int i_other = (e->v1 == i ? e->v2 : e->v1); MVert *v_other = &me->mvert[i_other]; - - if ((v_other->flag & SELECT) == 0) { - dv = dvert_array[i_other]; - BLI_SMALLSTACK_PUSH(dv_stack, dv); - dv_stack_tot++; + if ((source == WEIGHT_SMOOTH_ALL) || + (source == ((v_other->flag & SELECT) != 0))) + { + STACK_PUSH(verts_used, i); + break; } } } } + } - if (dv_stack_tot) { - const float dv_mul = 1.0f / (float)dv_stack_tot; - - /* vgroup_subset_weights is zero'd at this point */ - while ((dv = BLI_SMALLSTACK_POP(dv_stack))) { - for (j = 0; j < subset_count; j++) { - vgroup_subset_weights[j] += dv_mul * defvert_find_weight(dv, vgroup_subset_map[j]); - } - } - - dv = dvert_array[i]; - for (j = 0; j < subset_count; j++) { - MDeformWeight *dw; - if (vgroup_subset_weights[j] > 0.0f) { - dw = defvert_verify_index(dv, vgroup_subset_map[j]); + for (subset_index = 0; subset_index < subset_count; subset_index++) { + const int def_nr = vgroup_subset_map[subset_index]; + int iter; + + ED_vgroup_parray_to_weight_array((const MDeformVert **)dvert_array, dvert_tot, weight_accum_prev, def_nr); + memcpy(weight_accum_curr, weight_accum_prev, sizeof(*weight_accum_curr) * dvert_tot); + + for (iter = 0; iter < repeat; iter++) { + unsigned *vi_step, *vi_end = verts_used + STACK_SIZE(verts_used); + + /* avoid looping over all verts */ + // for (i = 0; i < dvert_tot; i++) + for (vi_step = verts_used; vi_step != vi_end; vi_step++) { + const unsigned int i = *vi_step; + float weight_tot = 0.0f; + float weight = 0.0f; + +#define WEIGHT_ACCUMULATE \ + { \ + float weight_other = weight_accum_prev[i_other]; \ + float tot_factor = 1.0f; \ + if (expand_sign == 1) { /* expand */ \ + if (weight_other < weight_accum_prev[i]) { \ + weight_other = (weight_accum_prev[i_other] * iexpand) + (weight_other * expand); \ + tot_factor = iexpand; \ + } \ + } \ + else if (expand_sign == -1) { /* contract */ \ + if (weight_other > weight_accum_prev[i]) { \ + weight_other = (weight_accum_prev[i_other] * iexpand) + (weight_other * expand); \ + tot_factor = iexpand; \ + } \ + } \ + weight += tot_factor * weight_other; \ + weight_tot += tot_factor; \ + } ((void)0) + + + if (bm) { + BMVert *v = BM_vert_at_index(bm, i); + BMIter eiter; + BMEdge *e; + + /* checked already */ + BLI_assert(BM_elem_flag_test(v, BM_ELEM_SELECT)); + + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + BMVert *v_other = BM_edge_other_vert(e, v); + if ((source == WEIGHT_SMOOTH_ALL) || + (source == (BM_elem_flag_test(v_other, BM_ELEM_SELECT) != 0))) + { + const int i_other = BM_elem_index_get(v_other); + + WEIGHT_ACCUMULATE; + } + } } else { - dw = defvert_find_index(dv, vgroup_subset_map[j]); - } + int j; - if (dw) { - dw->weight = (fac * vgroup_subset_weights[j]) + (ifac * dw->weight); - CLAMP(dw->weight, 0.0f, 1.0f); + /* checked already */ + BLI_assert(me->mvert[i].flag & SELECT); + + for (j = 0; j < emap[i].count; j++) { + MEdge *e = &me->medge[emap[i].indices[j]]; + const int i_other = (e->v1 == i ? e->v2 : e->v1); + MVert *v_other = &me->mvert[i_other]; + + if ((source == WEIGHT_SMOOTH_ALL) || + (source == ((v_other->flag & SELECT) != 0))) + { + WEIGHT_ACCUMULATE; + } + } } - /* zero for next iteration */ - vgroup_subset_weights[j] = 0.0f; +#undef WEIGHT_ACCUMULATE + + if (weight_tot != 0.0f) { + weight /= weight_tot; + weight = (weight_accum_prev[i] * ifac) + (weight * fac); + + /* should be within range, just clamp because of float precision */ + CLAMP(weight, 0.0f, 1.0f); + weight_accum_curr[i] = weight; + } } + + SWAP(float *, weight_accum_curr, weight_accum_prev); } + + ED_vgroup_parray_from_weight_array(dvert_array, dvert_tot, weight_accum_prev, def_nr, true); } + MEM_freeN(weight_accum_curr); + MEM_freeN(weight_accum_prev); + MEM_freeN(verts_used); + if (bm) { /* pass */ } @@ -2062,7 +2197,7 @@ void ED_vgroup_mirror(Object *ob, /* object mode / weight paint */ MVert *mv, *mv_mirr; int vidx, vidx_mirr; - const int use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; if (me->dvert == NULL) { goto cleanup; @@ -2315,16 +2450,43 @@ static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext *C) } /* editmode _or_ weight paint vertex sel */ -static int vertex_group_vert_select_poll(bContext *C) +static int vertex_group_vert_select_poll_ex(bContext *C, const short ob_type_flag) { Object *ob = ED_object_context(C); ID *data = (ob) ? ob->data : NULL; if (!(ob && !ob->id.lib && data && !data->lib)) - return 0; + return false; - return (BKE_object_is_in_editmode_vgroup(ob) || - BKE_object_is_in_wpaint_select_vert(ob)); + if (ob_type_flag && (((1 << ob->type) & ob_type_flag)) == 0) { + return false; + } + + if (BKE_object_is_in_editmode_vgroup(ob)) { + return true; + } + else if (ob->mode & OB_MODE_WEIGHT_PAINT) { + if (BKE_object_is_in_wpaint_select_vert(ob)) { + return true; + } + else { + CTX_wm_operator_poll_msg_set(C, "Vertex select needs to be enabled in weight paint mode"); + return false; + } + } + else { + return false; + } +} + +static int vertex_group_vert_select_poll(bContext *C) +{ + return vertex_group_vert_select_poll_ex(C, 0); +} + +static int vertex_group_mesh_vert_select_poll(bContext *C) +{ + return vertex_group_vert_select_poll_ex(C, (1 << OB_MESH)); } /* editmode _or_ weight paint vertex sel and active group unlocked */ @@ -2851,17 +3013,19 @@ void OBJECT_OT_vertex_group_invert(wmOperatorType *ot) "Remove verts from groups that have zero weight after inverting"); } - -static int vertex_group_blend_exec(bContext *C, wmOperator *op) +static int vertex_group_smooth_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); - float fac = RNA_float_get(op->ptr, "factor"); + const float fac = RNA_float_get(op->ptr, "factor"); + const int repeat = RNA_int_get(op->ptr, "repeat"); eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const int source = RNA_enum_get(op->ptr, "source"); + const float fac_expand = RNA_float_get(op->ptr, "expand"); int subset_count, vgroup_tot; const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type(ob, subset_type, &vgroup_tot, &subset_count); - vgroup_blend_subset(ob, vgroup_validmap, vgroup_tot, subset_count, fac); + vgroup_smooth_subset(ob, vgroup_validmap, vgroup_tot, subset_count, fac, repeat, fac_expand, source); MEM_freeN((void *)vgroup_validmap); DAG_id_tag_update(&ob->id, OB_RECALC_DATA); @@ -2871,60 +3035,34 @@ static int vertex_group_blend_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -/* check we have a vertex selection, either in weight paint or editmode */ -static int vertex_group_blend_poll(bContext *C) +void OBJECT_OT_vertex_group_smooth(wmOperatorType *ot) { - Object *ob = ED_object_context(C); - ID *data = (ob) ? ob->data : NULL; - - if (!(ob && !ob->id.lib && data && !data->lib)) - return false; - - if (ob->type != OB_MESH) { - return false; - } - - if (BKE_object_is_in_editmode_vgroup(ob)) { - return true; - } - else if (ob->mode & OB_MODE_WEIGHT_PAINT) { - if (ME_EDIT_PAINT_SEL_MODE(((Mesh *)data)) == SCE_SELECT_VERTEX) { - return true; - } - else { - CTX_wm_operator_poll_msg_set(C, "Vertex select needs to be enabled in weight paint mode"); - return false; - } - - } - else { - return false; - } -} - -void OBJECT_OT_vertex_group_blend(wmOperatorType *ot) -{ - PropertyRNA *prop; + static EnumPropertyItem smooth_source_item[] = { + {WEIGHT_SMOOTH_ALL, "ALL", 0, "All", ""}, + {WEIGHT_SMOOTH_SELECT, "SELECT", 0, "Only Selected", ""}, + {WEIGHT_SMOOTH_DESELECT, "DESELECT", 0, "Only Deselected", ""}, + {0, NULL, 0, NULL, NULL} + }; /* identifiers */ - ot->name = "Blend Vertex Group"; - ot->idname = "OBJECT_OT_vertex_group_blend"; - ot->description = "Blend selected vertex weights with unselected for the active group"; + ot->name = "Smooth Vertex Weights"; + ot->idname = "OBJECT_OT_vertex_group_smooth"; + ot->description = "Smooth weights for selected vertices"; /* api callbacks */ - ot->poll = vertex_group_blend_poll; - ot->exec = vertex_group_blend_exec; + ot->poll = vertex_group_mesh_vert_select_poll; + ot->exec = vertex_group_smooth_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; vgroup_operator_subset_select_props(ot, true); - prop = RNA_def_property(ot->srna, "factor", PROP_FLOAT, PROP_FACTOR); - RNA_def_property_ui_text(prop, "Factor", ""); - RNA_def_property_range(prop, 0.0f, 1.0f); - RNA_def_property_float_default(prop, 1.0f); -} + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); + RNA_def_float(ot->srna, "expand", 0.0f, -1.0f, 1.0, "Expand/Contract", "Expand/contract weights", -1.0f, 1.0f); + RNA_def_enum(ot->srna, "source", smooth_source_item, -1, "Source", "Vertices to mix with"); +} static int vertex_group_clean_exec(bContext *C, wmOperator *op) { diff --git a/source/blender/editors/object/object_warp.c b/source/blender/editors/object/object_warp.c index 7413bb07c4c..9f4da87903d 100644 --- a/source/blender/editors/object/object_warp.c +++ b/source/blender/editors/object/object_warp.c @@ -275,14 +275,14 @@ static int object_warp_verts_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void OBJECT_OT_vertex_warp(struct wmOperatorType *ot) +void TRANSFORM_OT_vertex_warp(struct wmOperatorType *ot) { PropertyRNA *prop; /* identifiers */ ot->name = "Warp"; ot->description = "Warp vertices around the cursor"; - ot->idname = "OBJECT_OT_vertex_warp"; + ot->idname = "TRANSFORM_OT_vertex_warp"; /* api callbacks */ ot->exec = object_warp_verts_exec; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 6c90e319bc6..f25679986a5 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -400,12 +400,12 @@ static void PE_set_view3d_data(bContext *C, PEData *data) /* note, the object argument means the modelview matrix does not account for the objects matrix, use viewmat rather than (obmat * viewmat) */ view3d_get_transformation(data->vc.ar, data->vc.rv3d, NULL, &data->mats); - if ((data->vc.v3d->drawtype>OB_WIRE) && (data->vc.v3d->flag & V3D_ZBUF_SELECT)) { + if (V3D_IS_ZBUF(data->vc.v3d)) { if (data->vc.v3d->flag & V3D_INVALID_BACKBUF) { /* needed or else the draw matrix can be incorrect */ view3d_operator_needs_opengl(C); - view3d_validate_backbuf(&data->vc); + ED_view3d_backbuf_validate(&data->vc); /* we may need to force an update here by setting the rv3d as dirty * for now it seems ok, but take care!: * rv3d->depths->dirty = 1; */ @@ -414,18 +414,18 @@ static void PE_set_view3d_data(bContext *C, PEData *data) } } -static void PE_create_shape_tree(PEData *data, Object *shapeob) +static bool PE_create_shape_tree(PEData *data, Object *shapeob) { DerivedMesh *dm = shapeob->derivedFinal; memset(&data->shape_bvh, 0, sizeof(data->shape_bvh)); if (!dm) { - return; + return false; } DM_ensure_tessface(dm); - bvhtree_from_mesh_faces(&data->shape_bvh, dm, 0.0f, 4, 8); + return bvhtree_from_mesh_faces(&data->shape_bvh, dm, 0.0f, 4, 8); } static void PE_free_shape_tree(PEData *data) @@ -443,8 +443,8 @@ static bool key_test_depth(PEData *data, const float co[3], const int screen_co[ float depth; /* nothing to do */ - if ((v3d->drawtype<=OB_WIRE) || (v3d->flag & V3D_ZBUF_SELECT)==0) - return 1; + if (!V3D_IS_ZBUF(v3d)) + return true; /* used to calculate here but all callers have the screen_co already, so pass as arg */ #if 0 @@ -4059,11 +4059,12 @@ void PARTICLE_OT_brush_edit(wmOperatorType *ot) static int shape_cut_poll(bContext *C) { if (PE_hair_poll(C)) { - Scene *scene= CTX_data_scene(C); - ParticleEditSettings *pset= PE_settings(scene); + Scene *scene = CTX_data_scene(C); + ParticleEditSettings *pset = PE_settings(scene); - if (pset->shape_object) + if (pset->shape_object && (pset->shape_object->type == OB_MESH)) { return true; + } } return false; @@ -4179,7 +4180,10 @@ static int shape_cut_exec(bContext *C, wmOperator *UNUSED(op)) int removed; PE_set_data(C, &data); - PE_create_shape_tree(&data, shapeob); + if (!PE_create_shape_tree(&data, shapeob)) { + /* shapeob may not have faces... */ + return OPERATOR_CANCELLED; + } if (selected) foreach_selected_point(&data, shape_cut); @@ -4445,7 +4449,7 @@ void PE_undo_step(Scene *scene, int step) DAG_id_tag_update(&OBACT->id, OB_RECALC_DATA); } -int PE_undo_valid(Scene *scene) +bool PE_undo_is_valid(Scene *scene) { PTCacheEdit *edit= PE_get_current(scene, OBACT); @@ -4496,18 +4500,19 @@ void PE_undo_number(Scene *scene, int nr) /* get name of undo item, return null if no item with this index */ /* if active pointer, set it to 1 if true */ -const char *PE_undo_get_name(Scene *scene, int nr, int *active) +const char *PE_undo_get_name(Scene *scene, int nr, bool *r_active) { PTCacheEdit *edit= PE_get_current(scene, OBACT); PTCacheUndo *undo; - if (active) *active= 0; + if (r_active) *r_active = false; if (edit) { undo= BLI_findlink(&edit->undo, nr); if (undo) { - if (active && undo==edit->curundo) - *active= 1; + if (r_active && (undo == edit->curundo)) { + *r_active = true; + } return undo->name; } } diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index 9b4f128ef86..c8b944f7d2a 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -348,6 +348,7 @@ static int rigidbody_objects_shape_change_exec(bContext *C, wmOperator *op) if (changed) { /* send updates */ WM_event_add_notifier(C, NC_OBJECT | ND_POINTCACHE, NULL); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); /* done */ return OPERATOR_FINISHED; diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index f9377d576bf..54429f9f066 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -32,9 +32,9 @@ #ifndef __RENDER_INTERN_H__ #define __RENDER_INTERN_H__ +struct bContext; +struct RenderEngine; struct wmOperatorType; -struct RenderResult; -struct Scene; struct ScrArea; /* render_shading.c */ @@ -44,6 +44,7 @@ void OBJECT_OT_material_slot_assign(struct wmOperatorType *ot); void OBJECT_OT_material_slot_select(struct wmOperatorType *ot); void OBJECT_OT_material_slot_deselect(struct wmOperatorType *ot); void OBJECT_OT_material_slot_copy(struct wmOperatorType *ot); +void OBJECT_OT_material_slot_move(struct wmOperatorType *ot); void MATERIAL_OT_new(struct wmOperatorType *ot); void TEXTURE_OT_new(struct wmOperatorType *ot); @@ -55,6 +56,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot); void SCENE_OT_render_layer_add(struct wmOperatorType *ot); void SCENE_OT_render_layer_remove(struct wmOperatorType *ot); +void SCENE_OT_render_view_add(struct wmOperatorType *ot); +void SCENE_OT_render_view_remove(struct wmOperatorType *ot); + #ifdef WITH_FREESTYLE void SCENE_OT_freestyle_module_add(struct wmOperatorType *ot); void SCENE_OT_freestyle_module_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 1c9f32697d4..2ba1e615a9e 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -48,6 +48,7 @@ #include "DNA_userdef_types.h" #include "BKE_blender.h" +#include "BKE_camera.h" #include "BKE_context.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" @@ -116,7 +117,7 @@ typedef struct RenderJob { } RenderJob; /* called inside thread! */ -static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect) +static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect, const char *viewname) { Scene *scene = rj->scene; const float *rectf = NULL; @@ -187,12 +188,16 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu * - sergey - */ /* TODO(sergey): Need to check has_combined here? */ - if (iuser->pass == 0) { + if (iuser->passtype == SCE_PASS_COMBINED) { + RenderView *rv; + size_t view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); + rv = RE_RenderViewGetById(rr, view_id); + /* find current float rect for display, first case is after composite... still weak */ - if (rr->rectf) - rectf = rr->rectf; + if (rv->rectf) + rectf = rv->rectf; else { - if (rr->rect32) { + if (rv->rect32) { /* special case, currently only happens with sequencer rendering, * which updates the whole frame, so we can only mark display buffer * as invalid here (sergey) @@ -201,8 +206,8 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu return; } else { - if (rr->renlay == NULL || rr->renlay->rectf == NULL) return; - rectf = rr->renlay->rectf; + if (rr->renlay == NULL) return; + rectf = RE_RenderLayerGetPass(rr->renlay, SCE_PASS_COMBINED, viewname); } } if (rectf == NULL) return; @@ -518,7 +523,6 @@ static void render_image_update_pass_and_layer(RenderJob *rj, RenderResult *rr, } } - iuser->pass = sima->iuser.pass; iuser->layer = sima->iuser.layer; RE_ReleaseResult(rj->re); @@ -531,6 +535,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec Image *ima = rj->image; ImBuf *ibuf; void *lock; + const char *viewname = RE_GetActiveRenderView(rj->re); /* only update if we are displaying the slot being rendered */ if (ima->render_slot != ima->last_render_slot) { @@ -563,7 +568,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec ibuf->channels == 1 || U.image_draw_method != IMAGE_DRAW_METHOD_GLSL) { - image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect); + image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect, viewname); } /* make jobs timer to send notifier */ @@ -891,6 +896,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even rj->write_still = is_write_still && !is_animation; rj->iuser.scene = scene; rj->iuser.ok = 1; + rj->iuser.passtype = SCE_PASS_COMBINED; rj->reports = op->reports; rj->orig_layer = 0; rj->last_layer = 0; @@ -1227,10 +1233,10 @@ static void render_view3d_startjob(void *customdata, short *stop, short *do_upda if ((update_flag & (PR_UPDATE_RENDERSIZE | PR_UPDATE_DATABASE)) || rstats->convertdone == 0) { RenderData rdata; - /* no osa, blur, seq, layers, etc for preview render */ + /* no osa, blur, seq, layers, savebuffer 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_DOSEQ | R_DOCOMP | R_FREE_IMAGE | R_EXR_TILE_FILE | R_FULL_SAMPLE); rdata.scemode |= R_VIEWPORT_PREVIEW; /* we do use layers, but only active */ @@ -1487,7 +1493,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) if (re == NULL) return; } - RE_AcquireResultImage(re, &rres); + /* Viewport render preview doesn't support multiview, view hardcoded to 0 */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { RegionView3D *rv3d = CTX_wm_region_view3d(C); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 402e72db217..5c69748e423 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_jitter.h" +#include "BLI_threads.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -103,11 +104,17 @@ typedef struct OGLRender { bMovieHandle *mh; int cfrao, nfra; + size_t totvideos; + + /* quick lookup */ + int view_id; + /* wm vars for timer and progress cursor */ wmWindowManager *wm; wmWindow *win; wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/ + void **movie_ctx_arr; } OGLRender; /* added because v3d is not always valid */ @@ -121,25 +128,144 @@ static unsigned int screen_opengl_layers(OGLRender *oglrender) } } -static void screen_opengl_render_apply(OGLRender *oglrender) +static bool screen_opengl_is_multiview(OGLRender *oglrender) +{ + View3D *v3d = oglrender->v3d; + RegionView3D *rv3d = oglrender->rv3d; + RenderData *rd = &oglrender->scene->r; + + if ((rd == NULL) || ((!oglrender->is_sequencer) && ((rv3d == NULL) || (v3d == NULL)))) + return false; + + return (rd->scemode & R_MULTIVIEW) && ((oglrender->is_sequencer) || (rv3d->persp == RV3D_CAMOB && v3d->camera)); +} + +static void screen_opengl_views_setup(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + SceneRenderView *srv; + bool is_multiview; + Object *camera; + View3D *v3d = oglrender->v3d; + + RenderData *rd = &oglrender->scene->r; + + rr = RE_AcquireResultWrite(oglrender->re); + + is_multiview = screen_opengl_is_multiview(oglrender); + + if (!is_multiview) { + /* we only have one view when multiview is off */ + rv = rr->views.first; + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_addtail(&rr->views, rv); + } + + while (rv->next) { + RenderView *rv_del = rv->next; + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + else { + if (!oglrender->is_sequencer) + RE_SetOverrideCamera(oglrender->re, V3D_CAMERA_SCENE(oglrender->scene, v3d)); + + /* remove all the views that are not needed */ + rv = rr->views.last; + while (rv) { + srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name)); + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (rv->rectf == NULL) + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + rv = rv->prev; + } + else { + RenderView *rv_del = rv; + rv = rv_del->prev; + + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + + /* create all the views that are needed */ + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + + rv = BLI_findstring(&rr->views, srv->name, offsetof(SceneRenderView, name)); + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + BLI_addtail(&rr->views, rv); + } + } + } + + for (rv = rr->views.first; rv; rv = rv->next) { + if (rv->rectf == NULL) { + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + } + } + + BLI_lock_thread(LOCK_DRAW_IMAGE); + if (is_multiview && BKE_scene_multiview_is_stereo3d(rd)) { + oglrender->ima->flag |= IMA_IS_STEREO; + } + else { + oglrender->ima->flag &= ~IMA_IS_STEREO; + oglrender->iuser.flag &= ~IMA_SHOW_STEREO; + } + BLI_unlock_thread(LOCK_DRAW_IMAGE); + + /* will only work for non multiview correctly */ + if (v3d) { + camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, "new opengl render view"); + BKE_render_result_stamp_info(oglrender->scene, camera, rr); + } + else { + BKE_render_result_stamp_info(oglrender->scene, oglrender->scene->camera, rr); + } + + RE_ReleaseResult(oglrender->re); +} + +static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr) { Scene *scene = oglrender->scene; ARegion *ar = oglrender->ar; View3D *v3d = oglrender->v3d; RegionView3D *rv3d = oglrender->rv3d; - RenderResult *rr; Object *camera = NULL; ImBuf *ibuf; - void *lock; float winmat[4][4]; + float *rectf = RE_RenderViewGetById(rr, oglrender->view_id)->rectf; int sizex = oglrender->sizex; int sizey = oglrender->sizey; const short view_context = (v3d != NULL); bool draw_bgpic = true; bool draw_sky = (scene->r.alphamode == R_ADDSKY); unsigned char *rect = NULL; - - rr = RE_AcquireResultRead(oglrender->re); + const char *viewname = RE_GetActiveRenderView(oglrender->re); if (oglrender->is_sequencer) { SeqRenderData context; @@ -152,6 +278,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) oglrender->sizex, oglrender->sizey, 100.0f, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); ibuf = BKE_sequencer_give_ibuf(&context, CFRA, chanshown); if (ibuf) { @@ -175,7 +302,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf); } - memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); + memcpy(rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); IMB_freeImBuf(linear_ibuf); } @@ -199,10 +326,12 @@ static void screen_opengl_render_apply(OGLRender *oglrender) gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect"); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, gp_rect); + BLI_assert(rectf != NULL); + 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); + blend_color_mix_float(&rectf[i], &rectf[i], col_src); } GPU_offscreen_unbind(oglrender->ofs, true); @@ -220,10 +349,17 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* render 3d view */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { - /*int is_ortho = scene->r.mode & R_ORTHO;*/ - camera = v3d->camera; +#if 0 + const bool is_ortho = (scene->r.mode & R_ORTHO) != 0; +#endif + camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat); - is_persp = true; + if (camera->type == OB_CAMERA) { + Camera *cam = camera->data; + is_persp = cam->type == CAM_PERSP; + } + else + is_persp = true; BKE_camera_to_gpu_dof(camera, &fx_settings); } else { @@ -243,7 +379,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); } else { @@ -259,7 +395,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -275,7 +411,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -295,7 +431,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) char err_out[256] = "unknown"; ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey, IB_rect, OB_SOLID, false, true, true, - (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, err_out); + (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, viewname, err_out); camera = scene->camera; if (ibuf_view) { @@ -320,7 +456,8 @@ static void screen_opengl_render_apply(OGLRender *oglrender) if (rect) { int profile_to; - + float *rectf = RE_RenderViewGetById(rr, oglrender->view_id)->rectf; + if (BKE_scene_check_color_management_enabled(scene)) profile_to = IB_PROFILE_LINEAR_RGB; else @@ -328,47 +465,68 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* sequencer has got trickier conversion happened above * also assume opengl's space matches byte buffer color space */ - IMB_buffer_float_from_byte(rr->rectf, rect, + IMB_buffer_float_from_byte(rectf, rect, profile_to, IB_PROFILE_SRGB, true, oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex); + + /* rr->rectf is now filled with image data */ + + if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) + BKE_image_stamp_buf(scene, camera, rect, rectf, rr->rectx, rr->recty, 4); + + MEM_freeN(rect); } +} - /* rr->rectf is now filled with image data */ +static void screen_opengl_render_write(OGLRender *oglrender) +{ + Scene *scene = oglrender->scene; + RenderResult *rr; + bool ok; + char name[FILE_MAX]; + + rr = RE_AcquireResultRead(oglrender->re); + + BKE_image_path_from_imformat( + name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); + + /* write images as individual images or stereo */ + BKE_render_result_stamp_info(scene, scene->camera, rr); + ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, false, name); + + RE_ReleaseResultImage(oglrender->re); + + if (ok) printf("OpenGL Render written to '%s'\n", name); + else printf("OpenGL Render failed to write '%s'\n", name); +} + +static void screen_opengl_render_apply(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + int view_id; + ImBuf *ibuf; + void *lock; - if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { - BKE_image_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4); + rr = RE_AcquireResultRead(oglrender->re); + for (rv = rr->views.first, view_id = 0; rv; rv = rv->next, view_id++) { + RE_SetActiveRenderView(oglrender->re, rv->name); + oglrender->view_id = view_id; + screen_opengl_render_doit(oglrender, rr); } RE_ReleaseResult(oglrender->re); - /* update byte from float buffer */ ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); - if (ibuf) { ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - - /* write file for animation */ - if (oglrender->write_still) { - char name[FILE_MAX]; - int ok; - - if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) { - IMB_color_to_bw(ibuf); - } - - BKE_image_path_from_imformat( - name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); - ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, true); /* no need to stamp here */ - if (ok) printf("OpenGL Render written to '%s'\n", name); - else printf("OpenGL Render failed to write '%s'\n", name); - } } - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); - if (rect) - MEM_freeN(rect); + if (oglrender->write_still) { + screen_opengl_render_write(oglrender); + } } static bool screen_opengl_render_init(bContext *C, wmOperator *op) @@ -380,7 +538,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ScrArea *prevsa = CTX_wm_area(C); ARegion *prevar = CTX_wm_region(C); - RenderResult *rr; GPUOffScreen *ofs; OGLRender *oglrender; int sizex, sizey; @@ -453,7 +610,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->sseq = CTX_wm_space_seq(C); } - oglrender->prevsa = prevsa; oglrender->prevar = prevar; @@ -487,15 +643,17 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* create render result */ RE_InitState(oglrender->re, NULL, &scene->r, NULL, sizex, sizey, NULL); - rr = RE_AcquireResultWrite(oglrender->re); - if (rr->rectf == NULL) - rr->rectf = MEM_callocN(sizeof(float) * 4 * sizex * sizey, "screen_opengl_render_init rect"); - RE_ReleaseResult(oglrender->re); + /* create render views */ + screen_opengl_views_setup(oglrender); /* wm vars */ oglrender->wm = wm; oglrender->win = win; + oglrender->totvideos = 0; + oglrender->mh = NULL; + oglrender->movie_ctx_arr = NULL; + return true; } @@ -503,10 +661,19 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) { Main *bmain = CTX_data_main(C); Scene *scene = oglrender->scene; + size_t i; if (oglrender->mh) { - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - oglrender->mh->end_movie(); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + for (i = 0; i < oglrender->totvideos; i++) { + oglrender->mh->end_movie(oglrender->movie_ctx_arr[i]); + oglrender->mh->context_free(oglrender->movie_ctx_arr[i]); + } + } + + if (oglrender->movie_ctx_arr) { + MEM_freeN(oglrender->movie_ctx_arr); + } } if (oglrender->timer) { /* exec will not have a timer */ @@ -547,13 +714,27 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) oglrender = op->customdata; scene = oglrender->scene; + oglrender->totvideos = BKE_scene_multiview_num_videos_get(&scene->r); oglrender->reports = op->reports; - oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports)) { - screen_opengl_render_end(C, oglrender); - return 0; + size_t i, width, height; + + BKE_scene_multiview_videos_dimensions_get(&scene->r, oglrender->sizex, oglrender->sizey, &width, &height); + oglrender->movie_ctx_arr = MEM_mallocN(sizeof(void *) * oglrender->totvideos, "Movies"); + oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + + for (i = 0; i < oglrender->totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + + oglrender->movie_ctx_arr[i] = oglrender->mh->context_create(); + if (!oglrender->mh->start_movie(oglrender->movie_ctx_arr[i], scene, &scene->r, oglrender->sizex, + oglrender->sizey, oglrender->reports, PRVRANGEON != 0, suffix)) + { + screen_opengl_render_end(C, oglrender); + return 0; + } } } @@ -563,18 +744,17 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) return 1; } + static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); OGLRender *oglrender = op->customdata; Scene *scene = oglrender->scene; - ImBuf *ibuf, *ibuf_save = NULL; - void *lock; char name[FILE_MAX]; bool ok = false; const bool view_context = (oglrender->v3d != NULL); - Object *camera = NULL; bool is_movie; + RenderResult *rr; /* go to next frame */ if (CFRA < oglrender->nfra) @@ -594,7 +774,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (!is_movie) { BKE_image_path_from_imformat( name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) { BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name); @@ -614,89 +794,41 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (BKE_scene_camera_switch_update(scene)) { oglrender->v3d->camera = scene->camera; } - - camera = oglrender->v3d->camera; } } else { BKE_scene_camera_switch_update(scene); - - camera = scene->camera; } /* render into offscreen buffer */ screen_opengl_render_apply(oglrender); /* save to disk */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); - - if (ibuf) { - bool needs_free = false; - - ibuf_save = ibuf; - - if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) { - ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - - needs_free = true; - } - - /* color -> grayscale */ - /* editing directly would alter the render view */ - if (scene->r.im_format.planes == R_IMF_PLANES_BW) { - ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save); - IMB_color_to_bw(ibuf_bw); - - if (needs_free) - IMB_freeImBuf(ibuf_save); - - ibuf_save = ibuf_bw; - } - else { - /* this is lightweight & doesnt re-alloc the buffers, only do this - * to save the correct bit depth since the image is always RGBA */ - ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0); - - ibuf_cpy->rect = ibuf_save->rect; - ibuf_cpy->rect_float = ibuf_save->rect_float; - ibuf_cpy->zbuf_float = ibuf_save->zbuf_float; - - if (needs_free) { - ibuf_cpy->mall = ibuf_save->mall; - ibuf_save->mall = 0; - IMB_freeImBuf(ibuf_save); - } + rr = RE_AcquireResultRead(oglrender->re); - ibuf_save = ibuf_cpy; + if (is_movie) { + ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh, oglrender->sizex, + oglrender->sizey, oglrender->movie_ctx_arr, oglrender->totvideos, PRVRANGEON != 0); + if (ok) { + printf("Append frame %d", scene->r.cfra); + BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); } - - if (is_movie) { - ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect, - oglrender->sizex, oglrender->sizey, oglrender->reports); - if (ok) { - printf("Append frame %d", scene->r.cfra); - BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); - } + } + else { + BKE_render_result_stamp_info(scene, scene->camera, rr); + ok = RE_WriteRenderViewsImage(op->reports, rr, scene, true, name); + if (ok) { + printf("Saved: %s", name); + BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); } else { - ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format); - - if (ok == 0) { - printf("Write error: cannot save %s\n", name); - BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); - } - else { - printf("Saved: %s", name); - BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); - } + printf("Write error: cannot save %s\n", name); + BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); } - - if (needs_free) - IMB_freeImBuf(ibuf_save); } - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); + RE_ReleaseResult(oglrender->re); + /* movie stats prints have no line break */ printf("\n"); diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c index 0d334082a2b..f98083f7e74 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -47,6 +47,7 @@ void ED_operatortypes_render(void) WM_operatortype_append(OBJECT_OT_material_slot_select); WM_operatortype_append(OBJECT_OT_material_slot_deselect); WM_operatortype_append(OBJECT_OT_material_slot_copy); + WM_operatortype_append(OBJECT_OT_material_slot_move); WM_operatortype_append(MATERIAL_OT_new); WM_operatortype_append(TEXTURE_OT_new); @@ -58,6 +59,9 @@ void ED_operatortypes_render(void) WM_operatortype_append(SCENE_OT_render_layer_add); WM_operatortype_append(SCENE_OT_render_layer_remove); + WM_operatortype_append(SCENE_OT_render_view_add); + WM_operatortype_append(SCENE_OT_render_view_remove); + #ifdef WITH_FREESTYLE WM_operatortype_append(SCENE_OT_freestyle_module_add); WM_operatortype_append(SCENE_OT_freestyle_module_remove); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 99edaff759e..9ed0cec5545 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -79,7 +79,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" - +#include "IMB_thumbs.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -128,7 +128,7 @@ ImBuf *get_brush_icon(Brush *brush) } if (brush->icon_imbuf) - BKE_icon_changed(BKE_icon_getid(&brush->id)); + BKE_icon_changed(BKE_icon_id_ensure(&brush->id)); } } } @@ -161,6 +161,7 @@ typedef struct ShaderPreview { unsigned int *pr_rect; int pr_method; + Main *bmain; Main *pr_main; } ShaderPreview; @@ -171,6 +172,7 @@ typedef struct IconPreviewSize { } IconPreviewSize; typedef struct IconPreview { + Main *bmain; Scene *scene; void *owner; ID *id; @@ -261,12 +263,14 @@ static Scene *preview_get_scene(Main *pr_main) /* call this with a pointer to initialize preview scene */ /* call this with NULL to restore assigned ID pointers in preview scene */ -static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPreview *sp) +static Scene *preview_prepare_scene(Main *bmain, Scene *scene, ID *id, int id_type, ShaderPreview *sp) { Scene *sce; Base *base; Main *pr_main = sp->pr_main; - + + memcpy(pr_main->name, bmain->name, sizeof(pr_main->name)); + sce = preview_get_scene(pr_main); if (sce) { @@ -423,7 +427,7 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre Tex *tex = NULL, *origtex = (Tex *)id; if (origtex) { - tex = localize_texture(origtex); + tex = BKE_texture_localize(origtex); sp->texcopy = tex; BLI_addtail(&pr_main->tex, tex); } @@ -525,6 +529,7 @@ static Scene *preview_prepare_scene(Scene *scene, ID *id, int id_type, ShaderPre static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, rcti *newrect) { Render *re; + RenderView *rv; RenderResult rres; char name[32]; int offx = 0; @@ -548,9 +553,16 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, /* test if something rendered ok */ re = RE_GetRender(name); - RE_AcquireResultImage(re, &rres); - if (rres.rectf) { + if (re == NULL) + return false; + + RE_AcquireResultImageViews(re, &rres); + + /* material preview only needs monoscopy (view 0) */ + rv = RE_RenderViewGetById(&rres, 0); + + if (rv->rectf) { if (ABS(rres.rectx - newx) < 2 && ABS(rres.recty - newy) < 2) { @@ -561,9 +573,11 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, unsigned char *rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect"); float fx = rect->xmin + offx; float fy = rect->ymin; + + /* material preview only needs monoscopy (view 0) */ if (re) - RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte); - + RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte, 0); + glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte); MEM_freeN(rect_byte); @@ -573,7 +587,7 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, } } - RE_ReleaseResultImage(re); + RE_ReleaseResultImageViews(re, &rres); return ok; } @@ -702,7 +716,7 @@ static void shader_preview_render(ShaderPreview *sp, ID *id, int split, int firs } /* get the stuff from the builtin preview dbase */ - sce = preview_prepare_scene(sp->scene, id, idtype, sp); + sce = preview_prepare_scene(sp->bmain, sp->scene, id, idtype, sp); if (sce == NULL) return; if (!split || first) sprintf(name, "Preview %p", sp->owner); @@ -757,7 +771,7 @@ static void shader_preview_render(ShaderPreview *sp, ID *id, int split, int firs } /* unassign the pointers, reset vars */ - preview_prepare_scene(sp->scene, NULL, GS(id->name), sp); + preview_prepare_scene(sp->bmain, sp->scene, NULL, GS(id->name), sp); /* XXX bad exception, end-exec is not being called in render, because it uses local main */ // if (idtype == ID_TE) { @@ -925,65 +939,87 @@ static void set_alpha(char *cp, int sizex, int sizey, char alpha) static void icon_preview_startjob(void *customdata, short *stop, short *do_update) { ShaderPreview *sp = customdata; - ID *id = sp->id; - short idtype = GS(id->name); - - if (idtype == ID_IM) { - Image *ima = (Image *)id; - ImBuf *ibuf = NULL; - ImageUser iuser = {NULL}; - /* ima->ok is zero when Image cannot load */ - if (ima == NULL || ima->ok == 0) - return; + if (sp->pr_method == PR_ICON_DEFERRED) { + PreviewImage *prv = sp->owner; + ImBuf *thumb; + char *deferred_data = PRV_DEFERRED_DATA(prv); + int source = deferred_data[0]; + char *path = &deferred_data[1]; - /* setup dummy image user */ - iuser.ok = iuser.framenr = 1; - iuser.scene = sp->scene; - - /* elubie: this needs to be changed: here image is always loaded if not - * already there. Very expensive for large images. Need to find a way to - * only get existing ibuf */ - ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); - if (ibuf == NULL || ibuf->rect == NULL) { - BKE_image_release_ibuf(ima, ibuf, NULL); - return; - } - - icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); +// printf("generating deferred %d×%d preview for %s\n", sp->sizex, sp->sizey, path); - *do_update = true; + thumb = IMB_thumb_manage(path, THB_LARGE, source); - BKE_image_release_ibuf(ima, ibuf, NULL); + if (thumb) { + /* PreviewImage assumes premultiplied alhpa... */ + IMB_premultiply_alpha(thumb); + + icon_copy_rect(thumb, sp->sizex, sp->sizey, sp->pr_rect); + IMB_freeImBuf(thumb); + } } - else if (idtype == ID_BR) { - Brush *br = (Brush *)id; + else { + ID *id = sp->id; + short idtype = GS(id->name); + + if (idtype == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf = NULL; + ImageUser iuser = {NULL}; + + /* ima->ok is zero when Image cannot load */ + if (ima == NULL || ima->ok == 0) + return; + + /* setup dummy image user */ + iuser.ok = iuser.framenr = 1; + iuser.scene = sp->scene; + + /* elubie: this needs to be changed: here image is always loaded if not + * already there. Very expensive for large images. Need to find a way to + * only get existing ibuf */ + ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (ibuf == NULL || ibuf->rect == NULL) { + BKE_image_release_ibuf(ima, ibuf, NULL); + return; + } - br->icon_imbuf = get_brush_icon(br); + icon_copy_rect(ibuf, sp->sizex, sp->sizey, sp->pr_rect); - memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(unsigned int)); + *do_update = true; - if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) - return; + BKE_image_release_ibuf(ima, ibuf, NULL); + } + else if (idtype == ID_BR) { + Brush *br = (Brush *)id; - icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + br->icon_imbuf = get_brush_icon(br); - *do_update = true; - } - else { - /* re-use shader job */ - shader_preview_startjob(customdata, stop, do_update); + memset(sp->pr_rect, 0x88, sp->sizex * sp->sizey * sizeof(unsigned int)); + + if (!(br->icon_imbuf) || !(br->icon_imbuf->rect)) + return; - /* world is rendered with alpha=0, so it wasn't displayed - * this could be render option for sky to, for later */ - if (idtype == ID_WO) { - set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); + icon_copy_rect(br->icon_imbuf, sp->sizex, sp->sizey, sp->pr_rect); + + *do_update = true; } - else if (idtype == ID_MA) { - Material *ma = (Material *)id; + else { + /* re-use shader job */ + shader_preview_startjob(customdata, stop, do_update); - if (ma->material_type == MA_TYPE_HALO) + /* world is rendered with alpha=0, so it wasn't displayed + * this could be render option for sky to, for later */ + if (idtype == ID_WO) { set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); + } + else if (idtype == ID_MA) { + Material *ma = (Material *)id; + + if (ma->material_type == MA_TYPE_HALO) + set_alpha((char *)sp->pr_rect, sp->sizex, sp->sizey, 255); + } } } } @@ -995,7 +1031,7 @@ static void common_preview_startjob(void *customdata, short *stop, short *do_upd { ShaderPreview *sp = customdata; - if (sp->pr_method == PR_ICON_RENDER) + if (ELEM(sp->pr_method, PR_ICON_RENDER, PR_ICON_DEFERRED)) icon_preview_startjob(customdata, stop, do_update); else shader_preview_startjob(customdata, stop, do_update); @@ -1031,29 +1067,35 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short const bool use_new_shading = BKE_scene_use_new_shading_nodes(ip->scene); while (cur_size) { + PreviewImage *prv = ip->owner; ShaderPreview *sp = MEM_callocN(sizeof(ShaderPreview), "Icon ShaderPreview"); + const bool is_render = !prv->use_deferred; /* construct shader preview from image size and previewcustomdata */ sp->scene = ip->scene; sp->owner = ip->owner; sp->sizex = cur_size->sizex; sp->sizey = cur_size->sizey; - sp->pr_method = PR_ICON_RENDER; + sp->pr_method = is_render ? PR_ICON_RENDER : PR_ICON_DEFERRED; sp->pr_rect = cur_size->rect; sp->id = ip->id; - - if (use_new_shading) { - /* texture icon rendering is hardcoded to use BI, - * so don't even think of using cycle's bmain for - * texture icons - */ - if (GS(ip->id->name) != ID_TE) - sp->pr_main = G_pr_main_cycles; - else + sp->bmain = ip->bmain; + + if (is_render) { + BLI_assert(ip->id); + if (use_new_shading) { + /* texture icon rendering is hardcoded to use BI, + * so don't even think of using cycle's bmain for + * texture icons + */ + if (GS(ip->id->name) != ID_TE) + sp->pr_main = G_pr_main_cycles; + else + sp->pr_main = G_pr_main; + } + else { sp->pr_main = G_pr_main; - } - else { - sp->pr_main = G_pr_main; + } } common_preview_startjob(sp, stop, do_update, progress); @@ -1098,12 +1140,13 @@ static void icon_preview_free(void *customdata) MEM_freeN(ip); } -void ED_preview_icon_render(Scene *scene, ID *id, unsigned int *rect, int sizex, int sizey) +void ED_preview_icon_render(Main *bmain, Scene *scene, ID *id, unsigned int *rect, int sizex, int sizey) { IconPreview ip = {NULL}; short stop = false, update = false; float progress = 0.0f; + ip.bmain = bmain; ip.scene = scene; ip.owner = id; ip.id = id; @@ -1134,8 +1177,9 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r BLI_movelisttolist(&ip->sizes, &old_ip->sizes); /* customdata for preview thread */ + ip->bmain = CTX_data_main(C); ip->scene = CTX_data_scene(C); - ip->owner = id; + ip->owner = owner; ip->id = id; icon_preview_add_size(ip, rect, sizex, sizey); @@ -1175,6 +1219,7 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M sp->id = id; sp->parent = parent; sp->slot = slot; + sp->bmain = CTX_data_main(C); /* hardcoded preview .blend for cycles/internal, this should be solved * once with custom preview .blend path for external engines */ diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index ff90f48d705..3b8b874a462 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -41,8 +41,9 @@ #include "DNA_space_types.h" #include "DNA_world_types.h" -#include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math_vector.h" #include "BLF_translation.h" @@ -202,7 +203,7 @@ static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) if (nurbs) { for (nu = nurbs->first; nu; nu = nu->next) if (isNurbsel(nu)) - nu->mat_nr = nu->charidx = ob->actcol - 1; + nu->mat_nr = ob->actcol - 1; } } else if (ob->type == OB_FONT) { @@ -381,6 +382,74 @@ void OBJECT_OT_material_slot_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +static int material_slot_move_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_context(C); + + unsigned int *slot_remap; + int index_pair[2]; + + int dir = RNA_enum_get(op->ptr, "direction"); + + if (!ob || ob->totcol < 2) { + return OPERATOR_CANCELLED; + } + + /* up */ + if (dir == 1 && ob->actcol > 1) { + index_pair[0] = ob->actcol - 2; + index_pair[1] = ob->actcol - 1; + ob->actcol--; + } + /* down */ + else if (dir == -1 && ob->actcol < ob->totcol) { + index_pair[0] = ob->actcol - 1; + index_pair[1] = ob->actcol - 0; + ob->actcol++; + } + else { + return OPERATOR_CANCELLED; + } + + slot_remap = MEM_mallocN(sizeof(unsigned int) * ob->totcol, __func__); + + range_vn_u(slot_remap, ob->totcol, 0); + + slot_remap[index_pair[0]] = index_pair[1]; + slot_remap[index_pair[1]] = index_pair[0]; + + BKE_material_remap_object(ob, slot_remap); + + MEM_freeN(slot_remap); + + DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW | ND_DATA, ob); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_material_slot_move(wmOperatorType *ot) +{ + static EnumPropertyItem material_slot_move[] = { + {1, "UP", 0, "Up", ""}, + {-1, "DOWN", 0, "Down", ""}, + {0, NULL, 0, NULL, NULL} + }; + + /* identifiers */ + ot->name = "Move Material"; + ot->idname = "OBJECT_OT_material_slot_move"; + ot->description = "Move the active material up/down in the list"; + + /* api callbacks */ + ot->exec = material_slot_move_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum(ot->srna, "direction", material_slot_move, 0, "Direction", "Direction to move, UP or DOWN"); +} + /********************** new material operator *********************/ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) @@ -450,7 +519,7 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) tex = BKE_texture_copy(tex); } else { - tex = add_texture(bmain, DATA_("Texture")); + tex = BKE_texture_add(bmain, DATA_("Texture")); } /* hook into UI */ @@ -605,6 +674,70 @@ void SCENE_OT_render_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/********************** render view operators *********************/ + +static int render_view_remove_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + /* don't allow user to remove "left" and "right" views */ + return scene->r.actview > 1; +} + +static int render_view_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + BKE_scene_add_render_view(scene, NULL); + scene->r.actview = BLI_listbase_count(&scene->r.views) - 1; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Render View"; + ot->idname = "SCENE_OT_render_view_add"; + ot->description = "Add a render view"; + + /* api callbacks */ + ot->exec = render_view_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + SceneRenderView *rv = BLI_findlink(&scene->r.views, scene->r.actview); + + if (!BKE_scene_remove_render_view(scene, rv)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Render View"; + ot->idname = "SCENE_OT_render_view_remove"; + ot->description = "Remove the selected render view"; + + /* api callbacks */ + ot->exec = render_view_remove_exec; + ot->poll = render_view_remove_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + #ifdef WITH_FREESTYLE static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) @@ -731,10 +864,11 @@ void SCENE_OT_freestyle_module_move(wmOperatorType *ot) static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op)) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SceneRenderLayer *srl = BLI_findlink(&scene->r.layers, scene->r.actlay); - BKE_freestyle_lineset_add(&srl->freestyleConfig, NULL); + BKE_freestyle_lineset_add(bmain, &srl->freestyleConfig, NULL); DAG_id_tag_update(&scene->id, 0); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); @@ -893,6 +1027,7 @@ void SCENE_OT_freestyle_lineset_move(wmOperatorType *ot) static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op) { + Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); SceneRenderLayer *srl = BLI_findlink(&scene->r.layers, scene->r.actlay); FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(&srl->freestyleConfig); @@ -903,10 +1038,10 @@ static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op) } if (lineset->linestyle) { lineset->linestyle->id.us--; - lineset->linestyle = BKE_linestyle_copy(lineset->linestyle); + lineset->linestyle = BKE_linestyle_copy(bmain, lineset->linestyle); } else { - lineset->linestyle = BKE_linestyle_new("LineStyle", NULL); + lineset->linestyle = BKE_linestyle_new(bmain, "LineStyle"); } DAG_id_tag_update(&lineset->linestyle->id, 0); WM_event_add_notifier(C, NC_LINESTYLE, lineset->linestyle); @@ -1490,7 +1625,7 @@ static int envmap_clear_exec(bContext *C, wmOperator *UNUSED(op)) { Tex *tex = CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data; - BKE_free_envmapdata(tex->env); + BKE_texture_envmap_free_data(tex->env); WM_event_add_notifier(C, NC_TEXTURE | NA_EDITED, tex); @@ -1533,7 +1668,7 @@ static int envmap_clear_all_exec(bContext *C, wmOperator *UNUSED(op)) for (tex = bmain->tex.first; tex; tex = tex->id.next) if (tex->env) - BKE_free_envmapdata(tex->env); + BKE_texture_envmap_free_data(tex->env); WM_event_add_notifier(C, NC_TEXTURE | NA_EDITED, tex); diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index be42e2ed518..dedcbb144aa 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -61,6 +61,7 @@ #include "ED_node.h" #include "ED_render.h" +#include "ED_view3d.h" #include "render_intern.h" // own include @@ -141,26 +142,19 @@ void ED_render_scene_update(Main *bmain, Scene *scene, int updated) recursive_check = false; } -void ED_render_engine_area_exit(ScrArea *sa) +void ED_render_engine_area_exit(Main *bmain, ScrArea *sa) { /* clear all render engines in this area */ ARegion *ar; + wmWindowManager *wm = bmain->wm.first; if (sa->spacetype != SPACE_VIEW3D) return; for (ar = sa->regionbase.first; ar; ar = ar->next) { - RegionView3D *rv3d; - if (ar->regiontype != RGN_TYPE_WINDOW || !(ar->regiondata)) continue; - - rv3d = ar->regiondata; - - if (rv3d->render_engine) { - RE_engine_free(rv3d->render_engine); - rv3d->render_engine = NULL; - } + ED_view3d_stop_render_preview(wm, ar); } } @@ -173,7 +167,7 @@ void ED_render_engine_changed(Main *bmain) for (sc = bmain->screen.first; sc; sc = sc->id.next) for (sa = sc->areabase.first; sa; sa = sa->next) - ED_render_engine_area_exit(sa); + ED_render_engine_area_exit(bmain, sa); RE_FreePersistentData(); @@ -276,7 +270,7 @@ static void material_changed(Main *bmain, Material *ma) int texture_draw = false; /* icons */ - BKE_icon_changed(BKE_icon_getid(&ma->id)); + BKE_icon_changed(BKE_icon_id_ensure(&ma->id)); /* glsl */ if (ma->gpumaterial.first) @@ -291,7 +285,7 @@ static void material_changed(Main *bmain, Material *ma) continue; } - BKE_icon_changed(BKE_icon_getid(&parent->id)); + BKE_icon_changed(BKE_icon_id_ensure(&parent->id)); if (parent->gpumaterial.first) GPU_material_free(&parent->gpumaterial); @@ -306,7 +300,7 @@ static void material_changed(Main *bmain, Material *ma) } /* find textured objects */ - if (texture_draw && !(U.gameflags & USER_DISABLE_VBO)) { + if (texture_draw) { for (ob = bmain->object.first; ob; ob = ob->id.next) { DerivedMesh *dm = ob->derivedFinal; Material ***material = give_matarar(ob); @@ -331,7 +325,7 @@ static void lamp_changed(Main *bmain, Lamp *la) Material *ma; /* icons */ - BKE_icon_changed(BKE_icon_getid(&la->id)); + BKE_icon_changed(BKE_icon_id_ensure(&la->id)); /* glsl */ for (ob = bmain->object.first; ob; ob = ob->id.next) @@ -367,7 +361,7 @@ static void texture_changed(Main *bmain, Tex *tex) bool texture_draw = false; /* icons */ - BKE_icon_changed(BKE_icon_getid(&tex->id)); + BKE_icon_changed(BKE_icon_id_ensure(&tex->id)); /* paint overlays */ for (scene = bmain->scene.first; scene; scene = scene->id.next) @@ -378,7 +372,7 @@ static void texture_changed(Main *bmain, Tex *tex) if (!material_uses_texture(ma, tex)) continue; - BKE_icon_changed(BKE_icon_getid(&ma->id)); + BKE_icon_changed(BKE_icon_id_ensure(&ma->id)); if (ma->gpumaterial.first) GPU_material_free(&ma->gpumaterial); @@ -409,7 +403,7 @@ static void texture_changed(Main *bmain, Tex *tex) continue; } - BKE_icon_changed(BKE_icon_getid(&wo->id)); + BKE_icon_changed(BKE_icon_id_ensure(&wo->id)); if (wo->gpumaterial.first) GPU_material_free(&wo->gpumaterial); @@ -429,7 +423,7 @@ static void texture_changed(Main *bmain, Tex *tex) } /* find textured objects */ - if (texture_draw && !(U.gameflags & USER_DISABLE_VBO)) { + if (texture_draw) { for (ob = bmain->object.first; ob; ob = ob->id.next) { DerivedMesh *dm = ob->derivedFinal; Material ***material = give_matarar(ob); @@ -457,7 +451,7 @@ static void world_changed(Main *bmain, World *wo) Material *ma; /* icons */ - BKE_icon_changed(BKE_icon_getid(&wo->id)); + BKE_icon_changed(BKE_icon_id_ensure(&wo->id)); /* glsl */ for (ma = bmain->mat.first; ma; ma = ma->id.next) @@ -476,7 +470,7 @@ static void image_changed(Main *bmain, Image *ima) Tex *tex; /* icons */ - BKE_icon_changed(BKE_icon_getid(&ima->id)); + BKE_icon_changed(BKE_icon_id_ensure(&ima->id)); /* textures */ for (tex = bmain->tex.first; tex; tex = tex->id.next) diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index fe357a7a0e2..f6690296890 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -63,7 +63,7 @@ static ScrArea *biggest_non_image_area(bContext *C) for (sa = sc->areabase.first; sa; sa = sa->next) { if (sa->winx > 30 && sa->winy > 30) { size = sa->winx * sa->winy; - if (sa->spacetype == SPACE_BUTS) { + if (!sa->full && sa->spacetype == SPACE_BUTS) { if (foundwin == 0 && size > bwmaxsize) { bwmaxsize = size; big = sa; @@ -197,7 +197,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my) /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ if (sa->full) { - sa->flag |= AREA_FLAG_STACKED_FULLSCREEN; + sa->flag |= (AREA_FLAG_STACKED_FULLSCREEN | AREA_FLAG_TEMP_TYPE); } } else { @@ -245,6 +245,11 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) ScrArea *sa = CTX_wm_area(C); SpaceImage *sima = sa->spacedata.first; + /* ensure image editor fullscreen and area fullscreen states are in sync */ + if ((sima->flag & SI_FULLWINDOW) && !sa->full) { + sima->flag &= ~SI_FULLWINDOW; + } + /* test if we have a temp screen in front */ if (win->screen->temp) { wm_window_lower(win); @@ -256,10 +261,11 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) if (sima->flag & SI_FULLWINDOW) { sima->flag &= ~SI_FULLWINDOW; - ED_screen_full_prevspace(C, sa); + ED_screen_full_prevspace(C, sa, false); } - else + else { ED_area_prevspace(C, sa); + } return OPERATOR_FINISHED; } @@ -320,7 +326,7 @@ static int render_view_show_invoke(bContext *C, wmOperator *UNUSED(op), const wm if (sima->flag & SI_FULLWINDOW) { sima->flag &= ~SI_FULLWINDOW; - ED_screen_full_prevspace(C, sa); + ED_screen_full_prevspace(C, sa, false); } else if (sima->next) { /* workaround for case of double prevspace, render window diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index c423d81bfe2..7fe6518134e 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -61,6 +61,9 @@ #include "BIF_glutil.h" #include "BLF_api.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" @@ -149,6 +152,34 @@ void ED_area_do_refresh(bContext *C, ScrArea *sa) } /** + * Action zones are only updated if the mouse is inside of them, but in some cases (currently only fullscreen icon) + * it might be needed to update their properties and redraw if the mouse isn't inside. + */ +void ED_area_azones_update(ScrArea *sa, const int mouse_xy[2]) +{ + AZone *az; + bool changed = false; + + for (az = sa->actionzones.first; az; az = az->next) { + if (az->type == AZONE_FULLSCREEN) { + /* only if mouse is not hovering the azone */ + if (BLI_rcti_isect_pt_v(&az->rect, mouse_xy) == false) { + az->alpha = 0.0f; + changed = true; + + /* can break since currently only this is handled here */ + break; + } + } + } + + if (changed) { + sa->flag &= ~AREA_FLAG_ACTIONZONES_UPDATE; + ED_area_tag_redraw(sa); + } +} + +/** * \brief Corner widget use for quitting fullscreen. */ static void area_draw_azone_fullscreen(short x1, short y1, short x2, short y2, float alpha) @@ -366,6 +397,11 @@ static void region_draw_azone_tria(AZone *az) glDisable(GL_BLEND); } +static void area_azone_tag_update(ScrArea *sa) +{ + sa->flag |= AREA_FLAG_ACTIONZONES_UPDATE; +} + static void region_draw_azones(ScrArea *sa, ARegion *ar) { AZone *az; @@ -406,6 +442,10 @@ 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); + + if (az->alpha != 0.0f) { + area_azone_tag_update(sa); + } } } } @@ -493,7 +533,6 @@ void ED_region_do_draw(bContext *C, ARegion *ar) glDisable(GL_BLEND); #endif - ar->do_draw = 0; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); UI_blocklist_free_inactive(C, &ar->uiblocks); @@ -773,7 +812,7 @@ static void region_azone_tab_plus(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2.5f * AZONEPAD_TAB_PLUSW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - 1.5f * AZONEPAD_TAB_PLUSW; @@ -818,7 +857,7 @@ static void region_azone_tab(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TABW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - AZONEPAD_TABW; @@ -863,7 +902,7 @@ static void region_azone_tria(ScrArea *sa, AZone *az, ARegion *ar) switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: - if (ar->winrct.ymax == sa->totrct.ymin) add = 1; else add = 0; + add = (ar->winrct.ymax == sa->totrct.ymin) ? 1 : 0; az->x1 = ar->winrct.xmax - 2 * AZONEPAD_TRIAW; az->y1 = ar->winrct.ymax - add; az->x2 = ar->winrct.xmax - AZONEPAD_TRIAW; @@ -1224,16 +1263,22 @@ static void region_rect_recursive(wmWindow *win, ScrArea *sa, ARegion *ar, rcti if (ar->flag & (RGN_FLAG_HIDDEN | RGN_FLAG_TOO_SMALL)) { ar->winrct = *remainder; - if (alignment == RGN_ALIGN_TOP) - ar->winrct.ymin = ar->winrct.ymax; - else if (alignment == RGN_ALIGN_BOTTOM) - ar->winrct.ymax = ar->winrct.ymin; - else if (alignment == RGN_ALIGN_RIGHT) - ar->winrct.xmin = ar->winrct.xmax; - else if (alignment == RGN_ALIGN_LEFT) - ar->winrct.xmax = ar->winrct.xmin; - else /* prevent winrct to be valid */ - ar->winrct.xmax = ar->winrct.xmin; + switch (alignment) { + case RGN_ALIGN_TOP: + ar->winrct.ymin = ar->winrct.ymax; + break; + case RGN_ALIGN_BOTTOM: + ar->winrct.ymax = ar->winrct.ymin; + break; + case RGN_ALIGN_RIGHT: + ar->winrct.xmin = ar->winrct.xmax; + break; + case RGN_ALIGN_LEFT: + default: + /* prevent winrct to be valid */ + ar->winrct.xmax = ar->winrct.xmin; + break; + } } /* restore prev-split exception */ @@ -2028,6 +2073,196 @@ void ED_region_info_draw(ARegion *ar, const char *text, int block, float fill_co glScissor(scissor[0], scissor[1], scissor[2], scissor[3]); } +#define MAX_METADATA_STR 1024 + +static const char *meta_data_list[] = +{ + "File", + "Strip", + "Note", + "Date", + "RenderTime", + "Marker", + "Time", + "Frame", + "Camera", + "Scene" +}; + +BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset) +{ + return (IMB_metadata_get_field(ibuf, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && r_str[0]); +} + +static void metadata_draw_imbuf(ImBuf *ibuf, rctf rect, int fontid, const bool is_top) +{ + char temp_str[MAX_METADATA_STR]; + int line_width; + int ofs_y = 0; + short i; + int len; + const float height = BLF_height_max(fontid); + const float vertical_offset = height + (0.1f * U.widget_unit); + + if (is_top) { + for (i = 0; i < 4; i++) { + /* first line */ + if (i == 0) { + bool do_newline = false; + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]); + if (metadata_is_valid(ibuf, temp_str, 0, len)) { + BLF_position(fontid, rect.xmin + (0.2f * U.widget_unit), + rect.ymax - vertical_offset, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + do_newline = true; + } + + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]); + if (metadata_is_valid(ibuf, temp_str, 1, len)) { + line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + BLF_position(fontid, rect.xmax - line_width - (0.2f * U.widget_unit), + rect.ymax - vertical_offset, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + do_newline = true; + } + + if (do_newline) + ofs_y += vertical_offset; + } + else if (i == 1) { + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); + if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { + BLF_position(fontid, rect.xmin + (0.2f * U.widget_unit), + rect.ymax - vertical_offset - ofs_y, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + ofs_y += vertical_offset; + } + } + else { + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); + if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { + line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + BLF_position(fontid, rect.xmax - line_width - (0.2f * U.widget_unit), + rect.ymax - vertical_offset - ofs_y, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + ofs_y += vertical_offset; + } + } + } + } + else { + int ofs_x = 0; + for (i = 5; i < 10; i++) { + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]); + if (metadata_is_valid(ibuf, temp_str, i, len)) { + BLF_position(fontid, rect.xmin + (0.2f * U.widget_unit) + ofs_x, + rect.ymin + (0.3f * U.widget_unit), 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + + ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X; + } + } + } +} + +static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top) +{ + char str[MAX_METADATA_STR] = ""; + short i, count = 0; + const float height = BLF_height_max(fontid) + 0.1f * U.widget_unit; + + if (is_top) { + if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) { + count++; + } + for (i = 2; i < 5; i++) { + if (metadata_is_valid(ibuf, str, i, 0)) { + count++; + } + } + } + else { + for (i = 5; i < 10; i++) { + if (metadata_is_valid(ibuf, str, i, 0)) { + count = 1; + } + } + } + + if (count) { + return (height * count + (0.1f * U.widget_unit)); + } + + return 0; +} + +#undef MAX_METADATA_STR + +void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, rctf frame, float zoomx, float zoomy) +{ + float box_y; + rctf rect; + uiStyle *style = UI_style_get_dpi(); + + if (!ibuf->metadata) + return; + + /* find window pixel coordinates of origin */ + glPushMatrix(); + + /* offset and zoom using ogl */ + glTranslatef(x, y, 0.0f); + glScalef(zoomx, zoomy, 1.0f); + + BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f, U.dpi); + + /* *** upper box*** */ + + /* get needed box height */ + box_y = metadata_box_height_get(ibuf, blf_mono_font, true); + + if (box_y) { + UI_ThemeColor(TH_METADATA_BG); + + /* set up rect */ + BLI_rctf_init(&rect, frame.xmin, frame.xmax, frame.ymax, frame.ymax + box_y); + /* draw top box */ + glRectf(rect.xmin, rect.ymin, rect.xmax, rect.ymax); + + BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + BLF_enable(blf_mono_font, BLF_CLIPPING); + + UI_ThemeColor(TH_METADATA_TEXT); + metadata_draw_imbuf(ibuf, rect, blf_mono_font, true); + + BLF_disable(blf_mono_font, BLF_CLIPPING); + } + + + /* *** lower box*** */ + + box_y = metadata_box_height_get(ibuf, blf_mono_font, false); + + if (box_y) { + UI_ThemeColor(TH_METADATA_BG); + + /* set up box rect */ + BLI_rctf_init(&rect, frame.xmin, frame.xmax, frame.ymin - box_y, frame.ymin); + /* draw top box */ + glRectf(rect.xmin, rect.ymin, rect.xmax, rect.ymax); + + BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + BLF_enable(blf_mono_font, BLF_CLIPPING); + + UI_ThemeColor(TH_METADATA_TEXT); + metadata_draw_imbuf(ibuf, rect, blf_mono_font, false); + + BLF_disable(blf_mono_font, BLF_CLIPPING); + } + + glPopMatrix(); +} + void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) { float gridsize, gridstep = 1.0f / 32.0f; diff --git a/source/blender/editors/screen/glutil.c b/source/blender/editors/screen/glutil.c index 6001534e88d..fd65d81baad 100644 --- a/source/blender/editors/screen/glutil.c +++ b/source/blender/editors/screen/glutil.c @@ -579,25 +579,25 @@ void glaDrawPixelsTexScaled(float x, float y, int img_w, int img_h, int format, continue; if (type == GL_FLOAT) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[subpart_y * offset_y * img_w * components + subpart_x * offset_x * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]); /* add an extra border of pixels so linear looks ok at edges of full image. */ if (subpart_w < tex_w) - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_FLOAT, &f_rect[subpart_y * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_FLOAT, &f_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); if (subpart_h < tex_h) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_FLOAT, &f_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]); if (subpart_w < tex_w && subpart_h < tex_h) - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_FLOAT, &f_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_FLOAT, &f_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); } else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[subpart_y * offset_y * img_w * components + subpart_x * offset_x * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, subpart_w, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + subpart_x * offset_x * components]); if (subpart_w < tex_w) - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[subpart_y * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, 0, 1, subpart_h, format, GL_UNSIGNED_BYTE, &uc_rect[((size_t)subpart_y) * offset_y * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); if (subpart_h < tex_h) - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, subpart_h, subpart_w, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + subpart_x * offset_x * components]); if (subpart_w < tex_w && subpart_h < tex_h) - glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(subpart_y * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); + glTexSubImage2D(GL_TEXTURE_2D, 0, subpart_w, subpart_h, 1, 1, format, GL_UNSIGNED_BYTE, &uc_rect[(((size_t)subpart_y) * offset_y + subpart_h - 1) * img_w * components + (subpart_x * offset_x + subpart_w - 1) * components]); } glEnable(GL_TEXTURE_2D); @@ -841,7 +841,7 @@ void gla2DDrawTranslatePt(gla2DDrawInfo *di, float wo_x, float wo_y, int *r_sc_x /** * Translate the \a world point from world coordinates into screen space. */ -void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int screen_r[2]) +void gla2DDrawTranslatePtv(gla2DDrawInfo *di, float world[2], int r_screen[2]) { screen_r[0] = (world[0] - di->world_rect.xmin) * di->wo_to_sc[0]; screen_r[1] = (world[1] - di->world_rect.ymin) * di->wo_to_sc[1]; @@ -991,7 +991,7 @@ void bgl_get_mats(bglMats *mats) /** * \note \a viewdist is only for ortho at the moment. */ -void bglPolygonOffset(float viewdist, float dist) +void bglPolygonOffset(float viewdist, float dist) { static float winmat[16], offset = 0.0; @@ -1007,8 +1007,25 @@ void bglPolygonOffset(float viewdist, float dist) /* dist is from camera to center point */ - if (winmat[15] > 0.5f) offs = 0.00001f * dist * viewdist; // ortho tweaking - else offs = 0.0005f * dist; // should be clipping value or so... + if (winmat[15] > 0.5f) { +#if 1 + offs = 0.00001f * dist * viewdist; // ortho tweaking +#else + static float depth_fac = 0.0f; + if (depth_fac == 0.0f) { + int depthbits; + glGetIntegerv(GL_DEPTH_BITS, &depthbits); + depth_fac = 1.0f / (float)((1 << depthbits) - 1); + } + offs = (-1.0 / winmat[10]) * dist * depth_fac; + + UNUSED_VARS(viewdist); +#endif + } + else { + /* should be clipping value or so... */ + offs = 0.0005f * dist; + } winmat[14] -= offs; offset += offs; diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 3431ce9f50a..87c0ce398e5 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -47,6 +47,7 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_gpencil.h" +#include "BKE_screen.h" #include "BKE_sequencer.h" #include "RNA_access.h" @@ -59,6 +60,18 @@ #include "screen_intern.h" +static unsigned int context_layers(bScreen *sc, Scene *scene, ScrArea *sa_ctx) +{ + /* needed for 'USE_ALLSELECT' define, otherwise we end up editing off-screen layers. */ + if (sc && sa_ctx && (sa_ctx->spacetype == SPACE_BUTS)) { + const unsigned int lay = BKE_screen_view3d_layer_all(sc); + if (lay) { + return lay; + } + } + return scene->lay; +} + const char *screen_context_dir[] = { "scene", "visible_objects", "visible_bases", "selectable_objects", "selectable_bases", "selected_objects", "selected_bases", @@ -81,7 +94,6 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult ScrArea *sa = CTX_wm_area(C); Scene *scene = sc->scene; Base *base; - unsigned int lay = scene->lay; #if 0 /* Using the context breaks adding objects in the UI. Need to find out why - campbell */ Object *obact = CTX_data_active_object(C); @@ -102,10 +114,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult return 1; } else if (CTX_data_equals(member, "visible_objects") || CTX_data_equals(member, "visible_bases")) { + const unsigned int lay = context_layers(sc, scene, sa); int visible_objects = CTX_data_equals(member, "visible_objects"); for (base = scene->base.first; base; base = base->next) { - if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & scene->lay)) { + if (((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) && (base->lay & lay)) { if (visible_objects) CTX_data_id_list_add(result, &base->object->id); else @@ -116,6 +129,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult return 1; } else if (CTX_data_equals(member, "selectable_objects") || CTX_data_equals(member, "selectable_bases")) { + const unsigned int lay = context_layers(sc, scene, sa); int selectable_objects = CTX_data_equals(member, "selectable_objects"); for (base = scene->base.first; base; base = base->next) { @@ -132,10 +146,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult return 1; } else if (CTX_data_equals(member, "selected_objects") || CTX_data_equals(member, "selected_bases")) { + const unsigned int lay = context_layers(sc, scene, sa); int selected_objects = CTX_data_equals(member, "selected_objects"); for (base = scene->base.first; base; base = base->next) { - if ((base->flag & SELECT) && (base->lay & scene->lay)) { + if ((base->flag & SELECT) && (base->lay & lay)) { if (selected_objects) CTX_data_id_list_add(result, &base->object->id); else @@ -146,10 +161,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult return 1; } else if (CTX_data_equals(member, "selected_editable_objects") || CTX_data_equals(member, "selected_editable_bases")) { + const unsigned int lay = context_layers(sc, scene, sa); int selected_editable_objects = CTX_data_equals(member, "selected_editable_objects"); for (base = scene->base.first; base; base = base->next) { - if ((base->flag & SELECT) && (base->lay & scene->lay)) { + if ((base->flag & SELECT) && (base->lay & lay)) { if ((base->object->restrictflag & OB_RESTRICT_VIEW) == 0) { if (0 == BKE_object_is_libdata(base->object)) { if (selected_editable_objects) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index aadfa9e6608..0c40c833c0d 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -59,6 +59,7 @@ #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_clip.h" +#include "ED_node.h" #include "ED_render.h" #include "UI_interface.h" @@ -1747,7 +1748,9 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) newsa = sa; } } - + + BLI_assert(newsa); + if (sa && (sa->spacetype != type)) { newsa->flag |= AREA_FLAG_TEMP_TYPE; } @@ -1760,11 +1763,18 @@ ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *sa, int type) return newsa; } -void ED_screen_full_prevspace(bContext *C, ScrArea *sa) +/** + * \a was_prev_temp for the case previous space was a temporary fullscreen as well + */ +void ED_screen_full_prevspace(bContext *C, ScrArea *sa, const bool was_prev_temp) { if (sa->flag & AREA_FLAG_STACKED_FULLSCREEN) { /* stacked fullscreen -> only go back to previous screen and don't toggle out of fullscreen */ ED_area_prevspace(C, sa); + /* only clear if previous space wasn't a temp fullscreen as well */ + if (!was_prev_temp) { + sa->flag &= ~AREA_FLAG_TEMP_TYPE; + } } else { ED_screen_restore_temp_type(C, sa); @@ -1799,13 +1809,12 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) if (sl->next) { if (sa->flag & AREA_FLAG_TEMP_TYPE) { - ED_screen_full_prevspace(C, sa); + ED_screen_full_prevspace(C, sa, false); } else { ED_screen_state_toggle(C, win, sa, state); } - - sa->flag &= ~AREA_FLAG_TEMP_TYPE; + /* warning: 'sa' may be freed */ } /* otherwise just tile the area again */ else { @@ -1813,7 +1822,11 @@ void ED_screen_full_restore(bContext *C, ScrArea *sa) } } -/* this function toggles: if area is maximized/full then the parent will be restored */ +/** + * this function toggles: if area is maximized/full then the parent will be restored + * + * \warning \a sa may be freed. + */ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const short state) { bScreen *sc, *oldscreen; @@ -2118,4 +2131,85 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) } +/* + * return true if any active area requires to see in 3D + */ +bool ED_screen_stereo3d_required(bScreen *screen) +{ + ScrArea *sa; + Scene *sce = screen->scene; + const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0; + + for (sa = screen->areabase.first; sa; sa = sa->next) { + switch (sa->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d; + + if (!is_multiview) + continue; + + v3d = sa->spacedata.first; + if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) { + ARegion *ar; + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiondata && ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = ar->regiondata; + if (rv3d->persp == RV3D_CAMOB) { + return true; + } + } + } + } + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima; + + /* images should always show in stereo, even if + * the file doesn't have views enabled */ + sima = sa->spacedata.first; + if (sima->image && (sima->image->flag & IMA_IS_STEREO) && + (sima->iuser.flag & IMA_SHOW_STEREO)) + { + return true; + } + break; + } + case SPACE_NODE: + { + SpaceNode *snode; + + if (!is_multiview) + continue; + + snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + return true; + } + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq; + + if (!is_multiview) + continue; + sseq = sa->spacedata.first; + if (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)) { + return true; + } + + if (sseq->draw_flag & SEQ_DRAW_BACKDROP) { + return true; + } + + break; + } + } + } + + return false; +} diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 79036d3356f..ccb6d5a6dca 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -32,8 +32,6 @@ #define __SCREEN_INTERN_H__ /* internal exports only */ -struct wmWindow; -struct Scene; #define AZONESPOT (0.6f * U.widget_unit) #define AZONEFADEIN (5.0f * U.widget_unit) /* when azone is totally visible */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 48d39020236..e5c3cb4e5e2 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -81,6 +81,7 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "UI_view2d.h" #include "screen_intern.h" /* own module include */ @@ -717,7 +718,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) else event.type = EVT_ACTIONZONE_REGION; - event.val = 0; + event.val = KM_NOTHING; event.customdata = op->customdata; event.customdatafree = true; op->customdata = NULL; @@ -990,6 +991,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize; newwin = WM_window_open(C, &rect); + *newwin->stereo3d_format = *win->stereo3d_format; /* allocs new screen and adds to newly created window, using window size */ newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2); @@ -1928,6 +1930,12 @@ static void region_scale_validate_size(RegionMoveData *rmd) static void region_scale_toggle_hidden(bContext *C, RegionMoveData *rmd) { + /* hidden areas may have bad 'View2D.cur' value, + * correct before displaying. see T45156 */ + if (rmd->ar->flag & RGN_FLAG_HIDDEN) { + UI_view2d_curRect_validate(&rmd->ar->v2d); + } + region_toggle_hidden(C, rmd->ar, 0); region_scale_validate_size(rmd); } @@ -2099,7 +2107,7 @@ static int frame_offset_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, false); - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -2151,7 +2159,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, true); - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -2257,7 +2265,7 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) else { areas_do_frame_follow(C, true); - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -2319,7 +2327,7 @@ static int marker_jump_exec(bContext *C, wmOperator *op) areas_do_frame_follow(C, true); - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); @@ -3413,10 +3421,17 @@ static int match_region_with_redraws(int spacetype, int regiontype, int redraws) return 0; } +//#define PROFILE_AUDIO_SYNCH + static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); +#ifdef PROFILE_AUDIO_SYNCH + static int old_frame = 0; + int newfra_int; +#endif + if (screen->animtimer && screen->animtimer == event->customdata) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); @@ -3435,14 +3450,27 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv if ((scene->audio.flag & AUDIO_SYNC) && (sad->flag & ANIMPLAY_FLAG_REVERSE) == false && - finite(time = sound_sync_scene(scene))) + finite(time = BKE_sound_sync_scene(scene))) { double newfra = (double)time * FPS; + /* give some space here to avoid jumps */ if (newfra + 0.5 > scene->r.cfra && newfra - 0.5 < scene->r.cfra) scene->r.cfra++; else scene->r.cfra = newfra + 0.5; + +#ifdef PROFILE_AUDIO_SYNCH + newfra_int = scene->r.cfra; + if (newfra_int < old_frame) { + printf("back jump detected, frame %d!\n", newfra_int); + } + else if (newfra_int > old_frame + 1) { + printf("forward jump detected, frame %d!\n", newfra_int); + } + fflush(stdout); + old_frame = newfra_int; +#endif } else { if (sync) { @@ -3509,8 +3537,12 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv sad->flag |= ANIMPLAY_FLAG_JUMPED; } - if (sad->flag & ANIMPLAY_FLAG_JUMPED) - sound_seek_scene(bmain, scene); + if (sad->flag & ANIMPLAY_FLAG_JUMPED) { + BKE_sound_seek_scene(bmain, scene); +#ifdef PROFILE_AUDIO_SYNCH + old_frame = CFRA; +#endif + } /* since we follow drawflags, we can't send notifier but tag regions ourselves */ ED_update_for_newframe(bmain, scene, 1); @@ -3610,7 +3642,7 @@ int ED_screen_animation_play(bContext *C, int sync, int mode) if (ED_screen_animation_playing(CTX_wm_manager(C))) { /* stop playback now */ ED_screen_animation_timer(C, 0, 0, 0, 0); - sound_stop_scene(scene); + BKE_sound_stop_scene(scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -3618,7 +3650,7 @@ int ED_screen_animation_play(bContext *C, int sync, int mode) int refresh = SPACE_TIME; /* these settings are currently only available from a menu in the TimeLine */ if (mode == 1) /* XXX only play audio forwards!? */ - sound_play_scene(scene); + BKE_sound_play_scene(scene); ED_screen_animation_timer(C, screen->redraws_flag, refresh, sync, mode); @@ -3775,7 +3807,7 @@ static int fullscreen_back_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - ED_screen_full_prevspace(C, sa); + ED_screen_full_prevspace(C, sa, false); return OPERATOR_FINISHED; } @@ -4227,6 +4259,8 @@ void ED_keymap_screen(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "SCREEN_OT_area_dupli", EVT_ACTIONZONE_AREA, 0, KM_SHIFT, 0); WM_keymap_verify_item(keymap, "SCREEN_OT_area_swap", EVT_ACTIONZONE_AREA, 0, KM_CTRL, 0); WM_keymap_verify_item(keymap, "SCREEN_OT_region_scale", EVT_ACTIONZONE_REGION, 0, 0, 0); + 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); /* area move after action zones */ WM_keymap_verify_item(keymap, "SCREEN_OT_area_move", LEFTMOUSE, KM_PRESS, 0, 0); @@ -4254,8 +4288,6 @@ void ED_keymap_screen(wmKeyConfig *keyconf) 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); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 9c05f1d4780..3f66d84c185 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -303,6 +303,9 @@ typedef struct ScreenshotJob { const short *stop; const short *do_update; ReportList reports; + + bMovieHandle *movie_handle; + void *movie_ctx; } ScreenshotJob; @@ -312,7 +315,13 @@ static void screenshot_freejob(void *sjv) if (sj->dumprect) MEM_freeN(sj->dumprect); - + + if (sj->movie_handle) { + bMovieHandle *mh = sj->movie_handle; + mh->end_movie(sj->movie_ctx); + mh->context_free(sj->movie_ctx); + } + MEM_freeN(sj); } @@ -337,20 +346,22 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float { ScreenshotJob *sj = sjv; RenderData rd = sj->scene->r; - bMovieHandle *mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); - + bMovieHandle *mh = NULL; + /* we need this as local variables for renderdata */ rd.frs_sec = U.scrcastfps; rd.frs_sec_base = 1.0f; if (BKE_imtype_is_movie(rd.im_format.imtype)) { - if (!mh->start_movie(sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports)) { + mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); + sj->movie_ctx = mh->context_create(); + sj->movie_handle = mh; + + if (!mh->start_movie(sj->movie_ctx, sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false, "")) { printf("screencast job stopped\n"); return; } } - else - mh = NULL; sj->stop = stop; sj->do_update = do_update; @@ -362,8 +373,8 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float if (sj->dumprect) { if (mh) { - if (mh->append_movie(&rd, rd.sfra, rd.cfra, (int *)sj->dumprect, - sj->dumpsx, sj->dumpsy, &sj->reports)) + if (mh->append_movie(sj->movie_ctx, &rd, rd.sfra, rd.cfra, (int *)sj->dumprect, + sj->dumpsx, sj->dumpsy, "", &sj->reports)) { BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra); printf("Appended frame %d\n", rd.cfra); @@ -379,7 +390,7 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float BKE_image_path_from_imformat( name, rd.pic, sj->bmain->name, rd.cfra, - &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); + &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true, NULL); ibuf->rect = sj->dumprect; ok = BKE_imbuf_write(ibuf, name, &rd.im_format); @@ -410,8 +421,11 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float PIL_sleep_ms(U.scrcastwait); } - if (mh) - mh->end_movie(); + if (mh) { + mh->end_movie(sj->movie_ctx); + mh->context_free(sj->movie_ctx); + sj->movie_handle = NULL; + } BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped"); } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 886e4e5ef8c..e4cad389004 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -426,7 +426,7 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) len = sqrtf(x * x + y * y); if (len <= 1) { - float avg = BKE_brush_curve_strength(br, len, 1.0f); /* Falloff curve */ + float avg = BKE_brush_curve_strength_clamped(br, len, 1.0f); /* Falloff curve */ buffer[index] = 255 - (GLubyte)(255 * avg); @@ -955,21 +955,32 @@ static void paint_cursor_on_hit(UnifiedPaintSettings *ups, Brush *brush, ViewCon } } +static bool ommit_cursor_drawing(Paint *paint, PaintMode mode, Brush *brush) +{ + if (paint->flags & PAINT_SHOW_BRUSH) { + if (ELEM(mode, PAINT_TEXTURE_2D, PAINT_TEXTURE_PROJECTIVE) && brush->imagepaint_tool == PAINT_TOOL_FILL) { + return true; + } + return false; + } + return true; +} + static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) { Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); ViewContext vc; - PaintMode mode; float final_radius; float translation[2]; float outline_alpha, *outline_col; float zoomx, zoomy; - + /* check that brush drawing is enabled */ - if (!(paint->flags & PAINT_SHOW_BRUSH)) + if (ommit_cursor_drawing(paint, mode, brush)) return; /* can't use stroke vc here because this will be called during @@ -978,7 +989,6 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) get_imapaint_zoom(C, &zoomx, &zoomy); zoomx = max_ff(zoomx, zoomy); - mode = BKE_paintmode_get_active_from_context(C); /* skip everything and draw brush here */ if (brush->flag & BRUSH_CURVE) { @@ -1018,8 +1028,8 @@ 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 ((!(ups->draw_inverted) ^ - !(brush->flag & BRUSH_DIR_IN)) && + if (((ups->draw_inverted == 0) ^ + ((brush->flag & BRUSH_DIR_IN) == 0)) && ELEM(brush->sculpt_tool, SCULPT_TOOL_DRAW, SCULPT_TOOL_INFLATE, SCULPT_TOOL_CLAY, SCULPT_TOOL_PINCH, SCULPT_TOOL_CREASE)) diff --git a/source/blender/editors/sculpt_paint/paint_curve.c b/source/blender/editors/sculpt_paint/paint_curve.c index 439c2a639bd..8c754d7adf3 100644 --- a/source/blender/editors/sculpt_paint/paint_curve.c +++ b/source/blender/editors/sculpt_paint/paint_curve.c @@ -222,6 +222,28 @@ static int paintcurve_point_co_index(char sel) return i; } +static char paintcurve_point_side_index(const BezTriple *bezt, const bool is_first, const char fallback) +{ + /* when matching, guess based on endpoint side */ + if (BEZSELECTED(bezt)) { + if ((bezt->f1 & SELECT) == (bezt->f3 & SELECT)) { + return is_first ? SEL_F1 : SEL_F3; + } + else if (bezt->f1 & SELECT) { + return SEL_F1; + } + else if (bezt->f3 & SELECT) { + return SEL_F3; + } + else { + return fallback; + } + } + else { + return 0; + } +} + /******************* Operators *********************************/ static int paintcurve_new_exec(bContext *C, wmOperator *UNUSED(op)) @@ -295,10 +317,17 @@ static void paintcurve_point_add(bContext *C, wmOperator *op, const int loc[2]) 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; + BKE_paint_curve_clamp_endpoint_add_index(pc, add_index); + + if (pc->add_index != 0) { + pcp[add_index].bez.f3 = SELECT; + pcp[add_index].bez.h2 = HD_ALIGN; + } + else { + pcp[add_index].bez.f1 = SELECT; + pcp[add_index].bez.h1 = HD_ALIGN; + } WM_paint_cursor_tag_redraw(window, ar); } @@ -384,7 +413,7 @@ static int paintcurve_delete_point_exec(bContext *C, wmOperator *op) points_new[j] = pc->points[i]; if ((i + 1) == pc->add_index) { - pc->add_index = j + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, j); } j++; } @@ -469,7 +498,7 @@ static bool paintcurve_point_select(bContext *C, wmOperator *op, const int loc[2 pcp = paintcurve_point_get_closest(pc, loc_fl, false, PAINT_CURVE_SELECT_THRESHOLD, &selflag); if (pcp) { - pc->add_index = (pcp - pc->points) + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); if (selflag == SEL_F2) { if (extend) @@ -599,9 +628,8 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e 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) { + if ((select = paintcurve_point_side_index(&pc->points[i].bez, i == 0, SEL_F3))) { pcp = &pc->points[i]; - select = SEL_F3; break; } } @@ -631,7 +659,7 @@ static int paintcurve_slide_invoke(bContext *C, wmOperator *op, const wmEvent *e /* only select the active point */ PAINT_CURVE_POINT_SELECT(pcp, psd->select); - pc->add_index = (pcp - pc->points) + 1; + BKE_paint_curve_clamp_endpoint_add_index(pc, pcp - pc->points); WM_event_add_modal_handler(C, op); WM_paint_cursor_tag_redraw(window, ar); diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index f1c91d0fcb5..52a60347f9f 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -150,7 +150,7 @@ static void partialvis_update_grids(Object *ob, /* get PBVH data */ BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, - &grids, NULL); + &grids); grid_hidden = BKE_pbvh_grid_hidden(pbvh); BKE_pbvh_get_grid_key(pbvh, &key); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5cfbd164153..346be5b336e 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -215,7 +215,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, unsigned short **mask, bool **valid, bool proj) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj, bool find_prev) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -226,7 +226,7 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, /* check if tile is already pushed */ /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ - if (!proj) { + if (find_prev) { data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); if (data) return data; @@ -445,7 +445,7 @@ void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty = (y >> IMAPAINT_TILE_BITS); } -void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h) +void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h, bool find_old) { ImBuf *tmpibuf = NULL; int tilex, tiley, tilew, tileh, tx, ty; @@ -474,7 +474,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, NULL, NULL, false); + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false, find_old); ibuf->userflags |= IB_BITMAPDIRTY; @@ -499,7 +499,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int w = imapaintpartial.x2 - imapaintpartial.x1; int h = imapaintpartial.y2 - imapaintpartial.y1; /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); + GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); } } @@ -1393,7 +1393,7 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) if (ma && ma->texpaintslot) ima = ma->texpaintslot[ma->paint_active_slot].ima; } - else if (imapaint->mode == IMAGEPAINT_MODE_MATERIAL) { + else if (imapaint->mode == IMAGEPAINT_MODE_IMAGE) { ima = imapaint->canvas; } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index eb96d1d20d5..483bf0f3190 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -359,7 +359,7 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int d float xy[2] = {x + xoff, y + yoff}; float len = len_v2(xy); - *m = (unsigned short)(65535.0f * BKE_brush_curve_strength(brush, len, radius)); + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); } } @@ -889,7 +889,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, /* 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); + outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); if (fabsf(outrgb[0]) > threshold) { float mask = BKE_brush_alpha_get(s->scene, s->brush); float alpha = rgba[3]; @@ -996,8 +996,8 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) { - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); + ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); } static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) @@ -1049,7 +1049,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign for (a = 0; a < tot; a++) { ED_imapaint_dirty_region(s->image, s->canvas, region[a].destx, region[a].desty, - region[a].width, region[a].height); + region[a].width, region[a].height, true); if (s->do_masking) { /* masking, find original pixels tiles from undo buffer to composite over */ @@ -1109,7 +1109,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) if (ima == NULL) { return 0; } - else if (ima->packedfile && ima->rr) { + else if (BKE_image_has_packedfile(ima) && ima->rr) { s->warnpackedfile = ima->id.name + 2; return 0; } @@ -1306,12 +1306,12 @@ 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; + size_t coordinate; if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) return; - coordinate = y_px * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; if (!BLI_BITMAP_TEST(touched, coordinate)) { float color_f[4]; @@ -1329,12 +1329,12 @@ 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; + size_t coordinate; if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) return; - coordinate = y_px * ibuf->x + x_px; + coordinate = ((size_t)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)) { @@ -1386,21 +1386,21 @@ void paint_2d_bucket_fill( 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); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); 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); + blend_color_mix_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)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); + blend_color_mix_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b); } } } @@ -1410,7 +1410,7 @@ void paint_2d_bucket_fill( * value is within the brush fill threshold from the fill color */ BLI_Stack *stack; BLI_bitmap *touched; - int coordinate; + size_t coordinate; int width = ibuf->x; float image_init[2]; int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; @@ -1428,12 +1428,12 @@ void paint_2d_bucket_fill( } /* change image invalidation method later */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - stack = BLI_stack_new(sizeof(int), __func__); - touched = BLI_BITMAP_NEW(ibuf->x * ibuf->y, "bucket_fill_bitmap"); + stack = BLI_stack_new(sizeof(size_t), __func__); + touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); - coordinate = (y_px * ibuf->x + x_px); + coordinate = (((size_t)y_px) * ibuf->x + x_px); if (do_float) { copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); @@ -1566,7 +1566,7 @@ void paint_2d_gradient_fill( 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); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); if (do_float) { for (x_px = 0; x_px < ibuf->x; x_px++) { @@ -1590,8 +1590,8 @@ void paint_2d_gradient_fill( /* 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), + IMB_blend_color_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), color_f, br->blend); } } @@ -1619,8 +1619,8 @@ void paint_2d_gradient_fill( 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), + IMB_blend_color_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b, br->blend); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 098f0d04d78..1549c31baf6 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -43,6 +43,7 @@ #include "BLI_blenlib.h" #include "BLI_linklist.h" #include "BLI_math.h" +#include "BLI_math_bits.h" #include "BLI_math_color_blend.h" #include "BLI_memarena.h" #include "BLI_threads.h" @@ -134,6 +135,8 @@ BLI_INLINE unsigned char f_to_char(const float val) //#define PROJ_DEBUG_PRINT_CLIP 1 #define PROJ_DEBUG_WINCLIP 1 + +#ifndef PROJ_DEBUG_NOSEAMBLEED /* projectFaceSeamFlags options */ //#define PROJ_FACE_IGNORE (1<<0) /* When the face is hidden, backfacing or occluded */ //#define PROJ_FACE_INIT (1<<1) /* When we have initialized the faces data */ @@ -151,6 +154,13 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_FACE_WINDING_INIT 1 #define PROJ_FACE_WINDING_CW 2 +/* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams + * as this number approaches 1.0f the likelihood increases of float precision errors where + * it is occluded by an adjacent face */ +#define PROJ_FACE_SCALE_SEAM 0.99f +#endif /* PROJ_DEBUG_NOSEAMBLEED */ + + #define PROJ_SRC_VIEW 1 #define PROJ_SRC_IMAGE_CAM 2 #define PROJ_SRC_IMAGE_VIEW 3 @@ -159,12 +169,6 @@ BLI_INLINE unsigned char f_to_char(const float val) #define PROJ_VIEW_DATA_ID "view_data" #define PROJ_VIEW_DATA_SIZE (4 * 4 + 4 * 4 + 3) /* viewmat + winmat + clipsta + clipend + is_ortho */ - -/* a slightly scaled down face is used to get fake 3D location for edge pixels in the seams - * as this number approaches 1.0f the likelihood increases of float precision errors where - * it is occluded by an adjacent face */ -#define PROJ_FACE_SCALE_SEAM 0.99f - #define PROJ_BUCKET_NULL 0 #define PROJ_BUCKET_INIT (1 << 0) // #define PROJ_BUCKET_CLONE_INIT (1<<1) @@ -190,9 +194,32 @@ typedef struct ProjPaintImage { 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; + bool touch; } ProjPaintImage; +/** + * Handle for stroke (operator customdata) + */ +typedef struct ProjStrokeHandle { + /* Support for painting from multiple views at once, + * currently used to impliment summetry painting, + * we can assume at least the first is set while painting. */ + struct ProjPaintState *ps_views[8]; + int ps_views_tot; + int symmetry_flags; + + int orig_brush_size; + + bool need_redraw; + + /* trick to bypass regular paint and allow clone picking */ + bool is_clone_cursor_pick; + + /* In ProjPaintState, only here for convenience */ + Scene *scene; + Brush *brush; +} ProjStrokeHandle; + /* Main projection painting struct passed to all projection painting functions */ typedef struct ProjPaintState { View3D *v3d; @@ -208,24 +235,14 @@ typedef struct ProjPaintState { Brush *brush; short tool, blend, mode; - int orig_brush_size; + float brush_size; Object *ob; + /* for symmetry, we need to store modified object matrix */ + float obmat[4][4]; + float obmat_imat[4][4]; /* end similarities with ImagePaintState */ - DerivedMesh *dm; - int dm_totface; - int dm_totedge; - int dm_totvert; - int dm_release; - - MVert *dm_mvert; - MEdge *dm_medge; - MFace *dm_mface; - 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; @@ -236,24 +253,16 @@ typedef struct ProjPaintState { LinkNode **bucketRect; /* screen sized 2D array, each pixel has a linked list of ProjPixel's */ LinkNode **bucketFaces; /* bucketRect aligned array linkList of faces overlapping each bucket */ 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 + char *vertFlags; /* store options per vert, now only store if the vert is pointing away from the view */ int buckets_x; /* The size of the bucket grid, the grid span's screenMin/screenMax so you can paint outsize the screen or with 2 brushes at once */ int buckets_y; - ProjPaintImage *projImages; - int pixel_sizeof; /* result of project_paint_pixel_sizeof(), constant per stroke */ int image_tot; /* size of projectImages array */ float (*screenCoords)[4]; /* verts projected into floating point screen space */ - float *cavities; /* cavity amount for vertices */ float screenMin[2]; /* 2D bounds for mesh verts on the screen's plane (screenspace) */ float screenMax[2]; float screen_width; /* Calculated from screenMin & screenMax */ @@ -272,12 +281,15 @@ typedef struct ProjPaintState { bool do_mask_normal; /* mask out pixels based on their normals */ bool do_mask_cavity; /* mask out pixels based on cavity */ bool do_new_shading_nodes; /* cache BKE_scene_use_new_shading_nodes value */ - float normal_angle; /* what angle to mask at*/ + float normal_angle; /* what angle to mask at */ + float normal_angle__cos; /* cos(normal_angle), faster to compare */ float normal_angle_inner; + float normal_angle_inner__cos; float normal_angle_range; /* difference between normal_angle and normal_angle_inner, for easy access */ bool do_face_sel; /* quick access to (me->editflag & ME_EDIT_PAINT_FACE_SEL) */ bool is_ortho; + bool is_flip_object; /* the object is negative scaled */ 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 */ @@ -302,13 +314,51 @@ typedef struct ProjPaintState { int bucketMax[2]; int context_bucket_x, context_bucket_y; /* must lock threads while accessing these */ - /* redraw */ - bool need_redraw; - struct CurveMapping *cavity_curve; BlurKernel *blurkernel; + + + /* -------------------------------------------------------------------- */ + /* Vars shared between multiple views (keep last) */ + /** + * This data is owned by ``ProjStrokeHandle.ps_views[0]``, + * all other views re-use the data. + */ + +#define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \ + MEMCPY_STRUCT_OFS(ps_dst, ps_src, is_shared_user) + +#define PROJ_PAINT_STATE_SHARED_CLEAR(ps) \ + MEMSET_STRUCT_OFS(ps, 0, is_shared_user) + + bool is_shared_user; + + ProjPaintImage *projImages; + float *cavities; /* cavity amount for vertices */ + +#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 + SpinLock *tile_lock; + + DerivedMesh *dm; + int dm_totface; + int dm_totedge; + int dm_totvert; + int dm_release; + + MVert *dm_mvert; + MEdge *dm_medge; + MFace *dm_mface; + MTFace *dm_mtface_stencil; + + MTFace **dm_mtface; + MTFace **dm_mtface_clone; /* other UV map, use for cloning between layers */ } ProjPaintState; typedef union pixelPointer { @@ -326,25 +376,27 @@ typedef union pixelStore { typedef struct ProjPixel { float projCoSS[2]; /* the floating point screen projection of this pixel */ float worldCoSS[3]; + + short x_px, y_px; + + unsigned short image_index; /* if anyone wants to paint onto more than 65535 images they can bite me */ + unsigned char bb_cell_index; + + /* for various reasons we may want to mask out painting onto this pixel */ + unsigned short mask; + /* Only used when the airbrush is disabled. * 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; - /* 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; PixelPointer origColor; PixelStore newColor; PixelPointer pixel; - - short image_index; /* if anyone wants to paint onto more than 32768 images they can bite me */ - unsigned char bb_cell_index; } ProjPixel; typedef struct ProjPixelClone { @@ -474,7 +526,9 @@ static float VecZDepthPersp( /* Return the top-most face index that the screen space coord 'pt' touches (or -1) */ -static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], float w[3], int *side) +static int project_paint_PickFace( + const ProjPaintState *ps, const float pt[2], + float w[3], int *r_side) { LinkNode *node; float w_tmp[3]; @@ -531,7 +585,7 @@ static int project_paint_PickFace(const ProjPaintState *ps, const float pt[2], f } } - *side = best_side; + *r_side = best_side; return best_face_index; /* will be -1 or a valid face */ } @@ -718,7 +772,7 @@ static bool project_bucket_point_occluded( int face_index; int isect_ret; float w[3]; /* not needed when clipping */ - const short do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; + const bool do_clip = ps->rv3d ? (ps->rv3d->rflag & RV3D_CLIPPING) != 0 : 0; /* we could return 0 for 1 face buckets, as long as this function assumes * that the point its testing is only every originated from an existing face */ @@ -776,11 +830,11 @@ static int line_isect_y(const float p1[2], const float p2[2], const float y_leve } if (p1[1] > y_level && p2[1] < y_level) { - *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; /*(p1[1]-p2[1]);*/ + *x_isect = (p2[0] * (p1[1] - y_level) + p1[0] * (y_level - p2[1])) / y_diff; /* (p1[1] - p2[1]); */ return ISECT_TRUE; } else if (p1[1] < y_level && p2[1] > y_level) { - *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; /*(p2[1]-p1[1]);*/ + *x_isect = (p2[0] * (y_level - p1[1]) + p1[0] * (p2[1] - y_level)) / y_diff; /* (p2[1] - p1[1]); */ return ISECT_TRUE; } else { @@ -809,11 +863,11 @@ static int line_isect_x(const float p1[2], const float p2[2], const float x_leve } if (p1[0] > x_level && p2[0] < x_level) { - *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; /*(p1[0]-p2[0]);*/ + *y_isect = (p2[1] * (p1[0] - x_level) + p1[1] * (x_level - p2[0])) / x_diff; /* (p1[0] - p2[0]); */ return ISECT_TRUE; } else if (p1[0] < x_level && p2[0] > x_level) { - *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; /*(p2[0]-p1[0]);*/ + *y_isect = (p2[1] * (x_level - p1[0]) + p1[1] * (p2[0] - x_level)) / x_diff; /* (p2[0] - p1[0]); */ return ISECT_TRUE; } else { @@ -1228,22 +1282,22 @@ static void screen_px_from_persp( static void project_face_pixel( const MTFace *tf_other, ImBuf *ibuf_other, const float w[3], - int side, unsigned char rgba_ub[4], float rgba_f[4]) + bool side, unsigned char rgba_ub[4], float rgba_f[4]) { const float *uvCo1, *uvCo2, *uvCo3; float uv_other[2], x, y; - uvCo1 = (float *)tf_other->uv[0]; + uvCo1 = tf_other->uv[0]; if (side == 1) { - uvCo2 = (float *)tf_other->uv[2]; - uvCo3 = (float *)tf_other->uv[3]; + uvCo2 = tf_other->uv[2]; + uvCo3 = tf_other->uv[3]; } else { - uvCo2 = (float *)tf_other->uv[1]; - uvCo3 = (float *)tf_other->uv[2]; + uvCo2 = tf_other->uv[1]; + uvCo3 = tf_other->uv[2]; } - interp_v2_v2v2v2(uv_other, uvCo1, uvCo2, uvCo3, (float *)w); + interp_v2_v2v2v2(uv_other, uvCo1, uvCo2, uvCo3, w); /* use */ uvco_to_wrapped_pxco(uv_other, ibuf_other->x, ibuf_other->y, &x, &y); @@ -1262,7 +1316,7 @@ static void project_face_pixel( static float project_paint_uvpixel_mask( const ProjPaintState *ps, const int face_index, - const int side, + const bool side, const float w[3]) { float mask; @@ -1327,7 +1381,7 @@ static float project_paint_uvpixel_mask( /* calculate mask */ if (ps->do_mask_normal) { MFace *mf = &ps->dm_mface[face_index]; - float no[3], angle; + float no[3], angle_cos; if (mf->flag & ME_SMOOTH) { const short *no1, *no2, *no3; no1 = ps->dm_mvert[mf->v1].no; @@ -1366,9 +1420,13 @@ static float project_paint_uvpixel_mask( #endif } + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } + /* now we can use the normal as a mask */ if (ps->is_ortho) { - angle = angle_normalized_v3v3(ps->viewDir, no); + angle_cos = dot_v3v3(ps->viewDir, no); } else { /* Annoying but for the perspective view we need to get the pixels location in 3D space :/ */ @@ -1389,15 +1447,18 @@ static float project_paint_uvpixel_mask( viewDirPersp[1] = (ps->viewPos[1] - (w[0] * co1[1] + w[1] * co2[1] + w[2] * co3[1])); viewDirPersp[2] = (ps->viewPos[2] - (w[0] * co1[2] + w[1] * co2[2] + w[2] * co3[2])); normalize_v3(viewDirPersp); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } - angle = angle_normalized_v3v3(viewDirPersp, no); + angle_cos = dot_v3v3(viewDirPersp, no); } - if (angle >= ps->normal_angle) { + if (angle_cos <= ps->normal_angle__cos) { return 0.0f; /* outsize the normal limit*/ } - else if (angle > ps->normal_angle_inner) { - mask *= (ps->normal_angle - angle) / ps->normal_angle_range; + else if (angle_cos < ps->normal_angle_inner__cos) { + mask *= (ps->normal_angle - acosf(angle_cos)) / ps->normal_angle_range; } /* otherwise no mask normal is needed, were within the limit */ } @@ -1440,10 +1501,10 @@ static int project_paint_undo_subtiles(const TileInfo *tinf, int tx, int ty) 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); + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, &pjIma->maskRect[tile_index], &pjIma->valid[tile_index], true, false); } else { - undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true); + undorect = image_undo_push_tile(pjIma->ima, pjIma->ibuf, tinf->tmpibuf, tx, ty, NULL, &pjIma->valid[tile_index], true, false); } pjIma->ibuf->userflags |= IB_BITMAPDIRTY; @@ -1470,7 +1531,7 @@ static ProjPixel *project_paint_uvpixel_init( const int face_index, const float pixelScreenCo[4], const float world_spaceCo[3], - const int side, + const bool side, const float w[3]) { ProjPixel *projPixel; @@ -1591,7 +1652,7 @@ static ProjPixel *project_paint_uvpixel_init( } else { float co[2]; - sub_v2_v2v2(co, projPixel->projCoSS, (float *)ps->cloneOffset); + sub_v2_v2v2(co, projPixel->projCoSS, ps->cloneOffset); /* no need to initialize the bucket, we're only checking buckets faces and for this * the faces are already initialized in project_paint_delayed_face_init(...) */ @@ -1619,13 +1680,14 @@ static ProjPixel *project_paint_uvpixel_init( } static bool line_clip_rect2f( + const rctf *cliprect, const rctf *rect, const float l1[2], const float l2[2], float l1_clip[2], float l2_clip[2]) { /* first account for horizontal, then vertical lines */ /* horiz */ - if (fabsf(l1[1] - l2[1]) < PROJ_GEOM_TOLERANCE) { + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { /* is the line out of range on its Y axis? */ if (l1[1] < rect->ymin || l1[1] > rect->ymax) { return 0; @@ -1636,7 +1698,7 @@ static bool line_clip_rect2f( } - if (fabsf(l1[0] - l2[0]) < PROJ_GEOM_TOLERANCE) { /* this is a single point (or close to)*/ + if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { /* this is a single point (or close to)*/ if (BLI_rctf_isect_pt_v(rect, l1)) { copy_v2_v2(l1_clip, l1); copy_v2_v2(l2_clip, l2); @@ -1653,7 +1715,7 @@ static bool line_clip_rect2f( CLAMP(l2_clip[0], rect->xmin, rect->xmax); return 1; } - else if (fabsf(l1[0] - l2[0]) < PROJ_GEOM_TOLERANCE) { + else if (fabsf(l1[0] - l2[0]) < PROJ_PIXEL_TOLERANCE) { /* is the line out of range on its X axis? */ if (l1[0] < rect->xmin || l1[0] > rect->xmax) { return 0; @@ -1664,7 +1726,7 @@ static bool line_clip_rect2f( return 0; } - if (fabsf(l1[1] - l2[1]) < PROJ_GEOM_TOLERANCE) { /* this is a single point (or close to)*/ + if (fabsf(l1[1] - l2[1]) < PROJ_PIXEL_TOLERANCE) { /* this is a single point (or close to)*/ if (BLI_rctf_isect_pt_v(rect, l1)) { copy_v2_v2(l1_clip, l1); copy_v2_v2(l2_clip, l2); @@ -1703,7 +1765,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; /* top/bottom */ - if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) { + if (line_isect_y(l1, l2, rect->ymin, &isect) && (isect >= cliprect->xmin) && (isect <= cliprect->xmax)) { if (l1[1] < l2[1]) { /* line 1 is outside */ l1_clip[0] = isect; l1_clip[1] = rect->ymin; @@ -1718,7 +1780,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; - if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= rect->xmin) && (isect <= rect->xmax)) { + if (line_isect_y(l1, l2, rect->ymax, &isect) && (isect >= cliprect->xmin) && (isect <= cliprect->xmax)) { if (l1[1] > l2[1]) { /* line 1 is outside */ l1_clip[0] = isect; l1_clip[1] = rect->ymax; @@ -1734,7 +1796,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; /* left/right */ - if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) { + if (line_isect_x(l1, l2, rect->xmin, &isect) && (isect >= cliprect->ymin) && (isect <= cliprect->ymax)) { if (l1[0] < l2[0]) { /* line 1 is outside */ l1_clip[0] = rect->xmin; l1_clip[1] = isect; @@ -1749,7 +1811,7 @@ static bool line_clip_rect2f( if (ok1 && ok2) return 1; - if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= rect->ymin) && (isect <= rect->ymax)) { + if (line_isect_x(l1, l2, rect->xmax, &isect) && (isect >= cliprect->ymin) && (isect <= cliprect->ymax)) { if (l1[0] > l2[0]) { /* line 1 is outside */ l1_clip[0] = rect->xmax; l1_clip[1] = isect; @@ -1964,7 +2026,7 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa v1[0] = p1[0] - p2[0]; v1[1] = p1[1] - p2[1]; v2[0] = p3[0] - p2[0]; v2[1] = p3[1] - p2[1]; - return -atan2(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); + return -atan2f(v1[0] * v2[1] - v1[1] * v2[0], v1[0] * v2[0] + v1[1] * v2[1]); } #endif @@ -1976,7 +2038,10 @@ static float angle_2d_clockwise(const float p1[2], const float p2[2], const floa #define ISECT_ALL4 ((1 << 4) - 1) /* limit must be a fraction over 1.0f */ -static bool IsectPT2Df_limit(float pt[2], float v1[2], float v2[2], float v3[2], float limit) +static bool IsectPT2Df_limit( + const float pt[2], + const float v1[2], const float v2[2], const float v3[2], + const float limit) { return ((area_tri_v2(pt, v1, v2) + area_tri_v2(pt, v2, v3) + @@ -2043,9 +2108,10 @@ static bool line_rect_clip( static void project_bucket_clip_face( - const bool is_ortho, + const bool is_ortho, const bool is_flip_object, + const rctf *cliprect, const rctf *bucket_bounds, - float *v1coSS, float *v2coSS, float *v3coSS, + const float *v1coSS, const float *v2coSS, const float *v3coSS, const float *uv1co, const float *uv2co, const float *uv3co, float bucket_bounds_uv[8][2], int *tot, bool cull) @@ -2071,7 +2137,8 @@ static void project_bucket_clip_face( inside_bucket_flag |= BLI_rctf_isect_pt_v(bucket_bounds, v3coSS) << 2; if (inside_bucket_flag == ISECT_ALL3) { - flip = ((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != + /* is_flip_object is used here because we use the face winding */ + flip = (((line_point_side_v2(v1coSS, v2coSS, v3coSS) > 0.0f) != is_flip_object) != (line_point_side_v2(uv1co, uv2co, uv3co) > 0.0f)); /* all screenspace points are inside the bucket bounding box, @@ -2193,7 +2260,7 @@ static void project_bucket_clip_face( float cent[2] = {0.0f, 0.0f}; /*float up[2] = {0.0f, 1.0f};*/ int i; - short doubles; + bool doubles; (*tot) = 0; @@ -2207,21 +2274,21 @@ static void project_bucket_clip_face( if (inside_bucket_flag & ISECT_3) { copy_v2_v2(isectVCosSS[*tot], v3coSS); (*tot)++; } if ((inside_bucket_flag & (ISECT_1 | ISECT_2)) != (ISECT_1 | ISECT_2)) { - if (line_clip_rect2f(bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v1coSS, v2coSS, v1_clipSS, v2_clipSS)) { if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } } } if ((inside_bucket_flag & (ISECT_2 | ISECT_3)) != (ISECT_2 | ISECT_3)) { - if (line_clip_rect2f(bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v2coSS, v3coSS, v1_clipSS, v2_clipSS)) { if ((inside_bucket_flag & ISECT_2) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } } } if ((inside_bucket_flag & (ISECT_3 | ISECT_1)) != (ISECT_3 | ISECT_1)) { - if (line_clip_rect2f(bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { + if (line_clip_rect2f(cliprect, bucket_bounds, v3coSS, v1coSS, v1_clipSS, v2_clipSS)) { if ((inside_bucket_flag & ISECT_3) == 0) { copy_v2_v2(isectVCosSS[*tot], v1_clipSS); (*tot)++; } if ((inside_bucket_flag & ISECT_1) == 0) { copy_v2_v2(isectVCosSS[*tot], v2_clipSS); (*tot)++; } } @@ -2404,7 +2471,7 @@ static bool IsectPoly2Df(const float pt[2], float uv[][2], const int tot) static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot) { int i; - int side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); + bool side = (line_point_side_v2(uv[tot - 1], uv[0], pt) > 0.0f); for (i = 1; i < tot; i++) { if ((line_point_side_v2(uv[i - 1], uv[i], pt) > 0.0f) != side) @@ -2423,7 +2490,8 @@ static bool IsectPoly2Df_twoside(const float pt[2], float uv[][2], const int tot 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, - const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, const short clamp_u, const short clamp_v) + const rctf *clip_rect, const rctf *bucket_bounds, ImBuf *ibuf, ImBuf **tmpibuf, + const bool clamp_u, const bool clamp_v) { /* Projection vars, to get the 3D locations into screen space */ MemArena *arena = ps->arena_mt[thread_index]; @@ -2448,8 +2516,8 @@ static void project_paint_face_init( float mask; float uv[2]; /* Image floating point UV - same as x, y but from 0.0-1.0 */ - int side; - float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */ + bool side; + const float *v1coSS, *v2coSS, *v3coSS; /* vert co screen-space, these will be assigned to mf->v1,2,3 or mf->v1,3,4 */ float *vCo[4]; /* vertex screenspace coords */ @@ -2468,11 +2536,10 @@ static void project_paint_face_init( int has_x_isect = 0, has_isect = 0; /* for early loop exit */ - int i1, i2, i3; - float uv_clip[8][2]; int uv_clip_tot; const bool is_ortho = ps->is_ortho; + const bool is_flip_object = ps->is_flip_object; const bool do_backfacecull = ps->do_backfacecull; const bool do_clip = ps->rv3d ? ps->rv3d->rflag & RV3D_CLIPPING : 0; @@ -2517,6 +2584,8 @@ static void project_paint_face_init( } do { + int i1, i2, i3; + if (side == 1) { i1 = 0; i2 = 2; i3 = 3; } @@ -2534,11 +2603,12 @@ static void project_paint_face_init( /* This funtion gives is a concave polyline in UV space from the clipped quad and tri*/ project_bucket_clip_face( - is_ortho, bucket_bounds, + is_ortho, is_flip_object, + clip_rect, bucket_bounds, v1coSS, v2coSS, v3coSS, uv1co, uv2co, uv3co, uv_clip, &uv_clip_tot, - ps->do_backfacecull || ps->do_occlude); + do_backfacecull || ps->do_occlude); /* sometimes this happens, better just allow for 8 intersectiosn even though there should be max 6 */ #if 0 @@ -2674,7 +2744,7 @@ static void project_paint_face_init( int fidx1, fidx2; /* face edge pairs - loop throuh these ((0,1), (1,2), (2,3), (3,0)) or ((0,1), (1,2), (2,0)) for a tri */ float seam_subsection[4][2]; - float fac1, fac2, ftot; + float fac1, fac2; if (outset_uv[0][0] == FLT_MAX) /* first time initialize */ uv_image_outset(tf_uv_pxoffset, outset_uv, ps->seam_bleed_px, ibuf->x, ibuf->y, mf->v4 != 0, (ps->faceWindingFlags[face_index] & PROJ_FACE_WINDING_CW) == 0); @@ -2706,19 +2776,16 @@ static void project_paint_face_init( else fidx2 = (fidx1 == 2) ? 0 : fidx1 + 1; /* next fidx in the face (0,1,2) -> (1,2,0) */ if ((face_seam_flag & (1 << fidx1)) && /* 1<<fidx1 -> PROJ_FACE_SEAM# */ - line_clip_rect2f(bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) + line_clip_rect2f(clip_rect, bucket_bounds, vCoSS[fidx1], vCoSS[fidx2], bucket_clip_edges[0], bucket_clip_edges[1])) { - - ftot = len_v2v2(vCoSS[fidx1], vCoSS[fidx2]); /* screenspace edge length */ - - if (ftot > 0.0f) { /* avoid div by zero */ + if (len_squared_v2v2(vCoSS[fidx1], vCoSS[fidx2]) > FLT_EPSILON) { /* avoid div by zero */ if (mf->v4) { - if (fidx1 == 2 || fidx2 == 2) side = 1; + if (fidx1 == 3 || fidx2 == 3) side = 1; else side = 0; } - fac1 = len_v2v2(vCoSS[fidx1], bucket_clip_edges[0]) / ftot; - fac2 = len_v2v2(vCoSS[fidx1], bucket_clip_edges[1]) / ftot; + fac1 = line_point_factor_v2(bucket_clip_edges[0], vCoSS[fidx1], vCoSS[fidx2]); + fac2 = line_point_factor_v2(bucket_clip_edges[1], vCoSS[fidx1], vCoSS[fidx2]); interp_v2_v2v2(seam_subsection[0], tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], fac1); interp_v2_v2v2(seam_subsection[1], tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], fac2); @@ -2760,11 +2827,8 @@ static void project_paint_face_init( /* Since this is a seam we need to work out where on the line this pixel is */ //fac = line_point_factor_v2(uv, uv_seam_quad[0], uv_seam_quad[1]); - - fac = line_point_factor_v2(uv, seam_subsection[0], seam_subsection[1]); - if (fac < 0.0f) { copy_v3_v3(pixelScreenCo, edge_verts_inset_clip[0]); } - else if (fac > 1.0f) { copy_v3_v3(pixelScreenCo, edge_verts_inset_clip[1]); } - else { interp_v3_v3v3(pixelScreenCo, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); } + fac = resolve_quad_u_v2(uv, UNPACK4(seam_subsection)); + interp_v3_v3v3(pixelScreenCo, edge_verts_inset_clip[0], edge_verts_inset_clip[1], fac); if (!is_ortho) { pixelScreenCo[3] = 1.0f; @@ -2779,24 +2843,20 @@ static void project_paint_face_init( { /* Only bother calculating the weights if we intersect */ if (ps->do_mask_normal || ps->dm_mtface_clone) { -#if 1 + const float uv_fac = fac1 + (fac * (fac2 - fac1)); +#if 0 /* get the UV on the line since we want to copy the pixels from there for bleeding */ float uv_close[2]; - float uv_fac = closest_to_line_v2(uv_close, uv, tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2]); - if (uv_fac < 0.0f) copy_v2_v2(uv_close, tf_uv_pxoffset[fidx1]); - else if (uv_fac > 1.0f) copy_v2_v2(uv_close, tf_uv_pxoffset[fidx2]); - + interp_v2_v2v2(uv_close, tf_uv_pxoffset[fidx1], tf_uv_pxoffset[fidx2], uv_fac); if (side) { barycentric_weights_v2(tf_uv_pxoffset[0], tf_uv_pxoffset[2], tf_uv_pxoffset[3], uv_close, w); } else { barycentric_weights_v2(tf_uv_pxoffset[0], tf_uv_pxoffset[1], tf_uv_pxoffset[2], uv_close, w); } -#else /* this is buggy with quads, don't use for now */ +#else /* Cheat, we know where we are along the edge so work out the weights from that */ - uv_fac = fac1 + (uv_fac * (fac2 - fac1)); - w[0] = w[1] = w[2] = 0.0; if (side) { w[fidx1 ? fidx1 - 1 : 0] = 1.0f - uv_fac; @@ -2811,8 +2871,8 @@ static void project_paint_face_init( /* a pity we need to get the worldspace pixel location here */ if (do_clip || do_3d_mapping) { - if (side) interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v3].co, ps->dm_mvert[mf->v4].co, w); - else interp_v3_v3v3v3(wco, ps->dm_mvert[mf->v1].co, ps->dm_mvert[mf->v2].co, ps->dm_mvert[mf->v3].co, w); + if (side) interp_v3_v3v3v3(wco, vCo[0], vCo[2], vCo[3], w); + else interp_v3_v3v3v3(wco, vCo[0], vCo[1], vCo[2], w); if (do_clip && ED_view3d_clipping_test(ps->rv3d, wco, true)) { continue; /* Watch out that no code below this needs to run */ @@ -2851,6 +2911,8 @@ static void project_paint_face_init( } } } +#else + UNUSED_VARS(vCo, threaded); #endif // PROJ_DEBUG_NOSEAMBLEED } @@ -2887,7 +2949,7 @@ static void project_bucket_bounds(const ProjPaintState *ps, const int bucket_x, /* Fill this bucket with pixels from the faces that intersect it. * * have bucket_bounds as an argument so we don't need to give bucket_x/y the rect function needs */ -static void project_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const rctf *bucket_bounds) +static void project_bucket_init(const ProjPaintState *ps, const int thread_index, const int bucket_index, const rctf *clip_rect, const rctf *bucket_bounds) { LinkNode *node; int face_index, image_index = 0; @@ -2902,7 +2964,10 @@ 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, &tmpibuf, 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, + clip_rect, bucket_bounds, ibuf, &tmpibuf, + (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0); } } else { @@ -2926,7 +2991,10 @@ 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, &tmpibuf, ima->tpageflag & IMA_CLAMP_U, ima->tpageflag & IMA_CLAMP_V); + project_paint_face_init( + ps, thread_index, bucket_index, face_index, image_index, + clip_rect, bucket_bounds, ibuf, &tmpibuf, + (ima->tpageflag & IMA_CLAMP_U) != 0, (ima->tpageflag & IMA_CLAMP_V) != 0); } } @@ -3078,7 +3146,8 @@ static void proj_paint_state_non_cddm_init(ProjPaintState *ps) } } -static void proj_paint_state_viewport_init(ProjPaintState *ps) +static void proj_paint_state_viewport_init( + ProjPaintState *ps, const char symmetry_flag) { float mat[3][3]; float viewmat[4][4]; @@ -3088,7 +3157,19 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) ps->viewDir[1] = 0.0f; ps->viewDir[2] = 1.0f; - invert_m4_m4(ps->ob->imat, ps->ob->obmat); + copy_m4_m4(ps->obmat, ps->ob->obmat); + + if (symmetry_flag) { + int i; + for (i = 0; i < 3; i++) { + if ((symmetry_flag >> i) & 1) { + negate_v3(ps->obmat[i]); + ps->is_flip_object = !ps->is_flip_object; + } + } + } + + invert_m4_m4(ps->obmat_imat, ps->obmat); if (ELEM(ps->source, PROJ_SRC_VIEW, PROJ_SRC_VIEW_FILL)) { /* normal drawing */ @@ -3098,7 +3179,7 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) copy_m4_m4(viewmat, ps->rv3d->viewmat); copy_m4_m4(viewinv, ps->rv3d->viewinv); - ED_view3d_ob_project_mat_get(ps->rv3d, ps->ob, ps->projectMat); + ED_view3d_ob_project_mat_get_from_obmat(ps->rv3d, ps->obmat, ps->projectMat); ps->is_ortho = ED_view3d_clip_range_get(ps->v3d, ps->rv3d, &ps->clipsta, &ps->clipend, true); } @@ -3151,24 +3232,27 @@ static void proj_paint_state_viewport_init(ProjPaintState *ps) } /* same as #ED_view3d_ob_project_mat_get */ - mul_m4_m4m4(vmat, viewmat, ps->ob->obmat); + mul_m4_m4m4(vmat, viewmat, ps->obmat); mul_m4_m4m4(ps->projectMat, winmat, vmat); } /* viewDir - object relative */ - invert_m4_m4(ps->ob->imat, ps->ob->obmat); copy_m3_m4(mat, viewinv); mul_m3_v3(mat, ps->viewDir); - copy_m3_m4(mat, ps->ob->imat); + copy_m3_m4(mat, ps->obmat_imat); mul_m3_v3(mat, ps->viewDir); normalize_v3(ps->viewDir); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(ps->viewDir); + } + /* viewPos - object relative */ copy_v3_v3(ps->viewPos, viewinv[3]); - copy_m3_m4(mat, ps->ob->imat); + copy_m3_m4(mat, ps->obmat_imat); mul_m3_v3(mat, ps->viewPos); - add_v3_v3(ps->viewPos, ps->ob->imat[3]); + add_v3_v3(ps->viewPos, ps->obmat_imat[3]); } static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int diameter) @@ -3234,6 +3318,8 @@ static void proj_paint_state_screen_coords_init(ProjPaintState *ps, const int di CLAMP(ps->screenMin[1], (float)(-diameter), (float)(ps->winy + diameter)); CLAMP(ps->screenMax[1], (float)(-diameter), (float)(ps->winy + diameter)); +#else + UNUSED_VARS(diameter); #endif } else if (ps->source != PROJ_SRC_VIEW_FILL) { /* re-projection, use bounds */ @@ -3313,12 +3399,14 @@ static void proj_paint_state_thread_init(ProjPaintState *ps, const bool reset_th 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); - } + if (ps->is_shared_user == false) { + 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(); + 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"); @@ -3337,16 +3425,22 @@ static void proj_paint_state_vert_flags_init(ProjPaintState *ps) for (a = 0, mv = ps->dm_mvert; a < ps->dm_totvert; a++, mv++) { normal_short_to_float_v3(no, mv->no); + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(no); + } if (ps->is_ortho) { - if (angle_normalized_v3v3(ps->viewDir, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */ + if (dot_v3v3(ps->viewDir, no) <= ps->normal_angle__cos) { /* 1 vert of this face is towards us */ ps->vertFlags[a] |= PROJ_VERT_CULL; } } else { sub_v3_v3v3(viewDirPersp, ps->viewPos, mv->co); normalize_v3(viewDirPersp); - if (angle_normalized_v3v3(viewDirPersp, no) >= ps->normal_angle) { /* 1 vert of this face is towards us */ + if (UNLIKELY(ps->is_flip_object)) { + negate_v3(viewDirPersp); + } + if (dot_v3v3(viewDirPersp, no) <= ps->normal_angle__cos) { /* 1 vert of this face is towards us */ ps->vertFlags[a] |= PROJ_VERT_CULL; } } @@ -3628,7 +3722,7 @@ static bool project_paint_backface_cull( } } else { - if (line_point_side_v2(coSS->v1, coSS->v2, coSS->v3) < 0.0f) { + if ((line_point_side_v2(coSS->v1, coSS->v2, coSS->v3) < 0.0f) != ps->is_flip_object) { return true; } @@ -3670,10 +3764,11 @@ static void project_paint_prepare_all_faces( ProjPaintState *ps, MemArena *arena, const ProjPaintFaceLookup *face_lookup, ProjPaintLayerClone *layer_clone, - MTFace *tf_base) + MTFace *tf_base, + const bool is_multi_view) { /* Image Vars - keep track of images we have used */ - LinkNode *image_LinkList = NULL; + LinkNodePair image_LinkList = {NULL, NULL}; Image *tpage_last = NULL, *tpage; TexPaintSlot *slot_last = NULL; @@ -3729,25 +3824,26 @@ static void project_paint_prepare_all_faces( ProjPaintFaceCoSS coSS; proj_paint_face_coSS_init(ps, mf, &coSS); - if (project_paint_flt_max_cull(ps, &coSS)) { - continue; - } + if (is_multi_view == false) { + if (project_paint_flt_max_cull(ps, &coSS)) { + continue; + } #ifdef PROJ_DEBUG_WINCLIP - if (project_paint_winclip(ps, mf, &coSS)) { - continue; - } + if (project_paint_winclip(ps, mf, &coSS)) { + continue; + } #endif //PROJ_DEBUG_WINCLIP - - if (project_paint_backface_cull(ps, mf, &coSS)) { - continue; + if (project_paint_backface_cull(ps, mf, &coSS)) { + continue; + } } if (tpage_last != tpage) { - image_index = BLI_linklist_index(image_LinkList, tpage); + image_index = BLI_linklist_index(image_LinkList.list, tpage); if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { /* MemArena dosnt have an append func */ BLI_linklist_append(&image_LinkList, tpage); @@ -3767,18 +3863,22 @@ static void project_paint_prepare_all_faces( } /* build an array of images we use*/ - project_paint_build_proj_ima(ps, arena, image_LinkList); + if (ps->is_shared_user == false) { + project_paint_build_proj_ima(ps, arena, image_LinkList.list); + } /* we have built the array, discard the linked list */ - BLI_linklist_free(image_LinkList, NULL); + BLI_linklist_free(image_LinkList.list, NULL); } /* run once per stroke before projection painting */ -static void project_paint_begin(ProjPaintState *ps) +static void project_paint_begin( + ProjPaintState *ps, + const bool is_multi_view, const char symmetry_flag) { ProjPaintLayerClone layer_clone; ProjPaintFaceLookup face_lookup; - MTFace *tf_base; + MTFace *tf_base = NULL; MemArena *arena; /* at the moment this is just ps->arena_mt[0], but use this to show were not multithreading */ @@ -3792,10 +3892,13 @@ static void project_paint_begin(ProjPaintState *ps) ED_view3d_clipping_local(ps->rv3d, ps->ob->obmat); /* faster clipping lookups */ ps->do_face_sel = ((((Mesh *)ps->ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) != 0); + ps->is_flip_object = (ps->ob->transflag & OB_NEG_SCALE) != 0; /* paint onto the derived mesh */ - if (!proj_paint_state_dm_init(ps)) { - return; + if (ps->is_shared_user == false) { + if (!proj_paint_state_dm_init(ps)) { + return; + } } proj_paint_face_lookup_init(ps, &face_lookup); @@ -3817,11 +3920,13 @@ static void project_paint_begin(ProjPaintState *ps) } /* when using subsurf or multires, mface arrays are thrown away, we need to keep a copy */ - proj_paint_state_non_cddm_init(ps); + if (ps->is_shared_user == false) { + proj_paint_state_non_cddm_init(ps); - proj_paint_state_cavity_init(ps); + proj_paint_state_cavity_init(ps); + } - proj_paint_state_viewport_init(ps); + proj_paint_state_viewport_init(ps, symmetry_flag); /* calculate vert screen coords * run this early so we can calculate the x/y resolution of our bucket rect */ @@ -3850,7 +3955,9 @@ static void project_paint_begin(ProjPaintState *ps) ps->bucketFlags = MEM_callocN(sizeof(char) * ps->buckets_x * ps->buckets_y, "paint-bucketFaces"); #ifndef PROJ_DEBUG_NOSEAMBLEED - proj_paint_state_seam_bleed_init(ps); + if (ps->is_shared_user == false) { + proj_paint_state_seam_bleed_init(ps); + } #endif proj_paint_state_thread_init(ps, reset_threads); @@ -3858,7 +3965,7 @@ static void project_paint_begin(ProjPaintState *ps) proj_paint_state_vert_flags_init(ps); - project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base); + project_paint_prepare_all_faces(ps, arena, &face_lookup, &layer_clone, tf_base, is_multi_view); } static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) @@ -3867,7 +3974,7 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) if (ps->tool == PAINT_TOOL_CLONE) { float projCo[4]; copy_v3_v3(projCo, ED_view3d_cursor3d_get(ps->scene, ps->v3d)); - mul_m4_v3(ps->ob->imat, projCo); + mul_m4_v3(ps->obmat_imat, projCo); projCo[3] = 1.0f; mul_m4_v4(ps->projectMat, projCo); @@ -3879,14 +3986,16 @@ static void paint_proj_begin_clone(ProjPaintState *ps, const float mouse[2]) static void project_paint_end(ProjPaintState *ps) { int a; - ProjPaintImage *projIma; 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); + if (ps->is_shared_user == false) { + ProjPaintImage *projIma; + 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); @@ -3895,68 +4004,81 @@ 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(); + + if (ps->is_shared_user == false) { + + /* must be set for non-shared */ + BLI_assert(ps->dm_mtface || ps->is_shared_user); + if (ps->dm_mtface) + 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); - } + 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->do_mask_cavity) { + MEM_freeN(ps->cavities); + } + + /* copy for subsurf/multires, so throw away */ + if (ps->dm->type != DM_TYPE_CDDM) { + if (ps->dm_mvert) MEM_freeN(ps->dm_mvert); + if (ps->dm_mface) MEM_freeN(ps->dm_mface); + /* looks like these don't need copying */ +#if 0 + if (ps->dm_mtface) MEM_freeN(ps->dm_mtface); + if (ps->dm_mtface_clone) MEM_freeN(ps->dm_mtface_clone); + if (ps->dm_mtface_stencil) MEM_freeN(ps->dm_mtface_stencil); #endif + } + + if (ps->dm_release) + ps->dm->release(ps->dm); + } if (ps->blurkernel) { paint_delete_blur_kernel(ps->blurkernel); MEM_freeN(ps->blurkernel); } - if (ps->do_mask_cavity) { - MEM_freeN(ps->cavities); - } - if (ps->vertFlags) MEM_freeN(ps->vertFlags); for (a = 0; a < ps->thread_tot; a++) { BLI_memarena_free(ps->arena_mt[a]); } +} - /* copy for subsurf/multires, so throw away */ - if (ps->dm->type != DM_TYPE_CDDM) { - if (ps->dm_mvert) MEM_freeN(ps->dm_mvert); - if (ps->dm_mface) MEM_freeN(ps->dm_mface); - /* looks like these don't need copying */ -#if 0 - if (ps->dm_mtface) MEM_freeN(ps->dm_mtface); - if (ps->dm_mtface_clone) MEM_freeN(ps->dm_mtface_clone); - if (ps->dm_mtface_stencil) MEM_freeN(ps->dm_mtface_stencil); -#endif - } +/* 1 = an undo, -1 is a redo. */ +static void partial_redraw_single_init(ImagePaintPartialRedraw *pr) +{ + pr->x1 = 10000000; + pr->y1 = 10000000; - if (ps->dm_release) - ps->dm->release(ps->dm); + pr->x2 = -1; + pr->y2 = -1; + + pr->enabled = 1; } -/* 1 = an undo, -1 is a redo. */ static void partial_redraw_array_init(ImagePaintPartialRedraw *pr) { int tot = PROJ_BOUNDBOX_SQUARED; while (tot--) { - pr->x1 = 10000000; - pr->y1 = 10000000; - - pr->x2 = -1; - pr->y2 = -1; - - pr->enabled = 1; - + partial_redraw_single_init(pr); pr++; } } @@ -4000,6 +4122,8 @@ static bool project_image_refresh_tagged(ProjPaintState *ps) imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); redraw = 1; } + + partial_redraw_single_init(pr); } projIma->touch = 0; /* clear for reuse */ @@ -4207,7 +4331,7 @@ static void do_projectpaint_soften_f(ProjPaintState *ps, ProjPixel *projPixel, f /* 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); + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { float alpha = projPixel->pixel.f_pt[3]; projPixel->pixel.f_pt[3] = rgba[3] = mask; @@ -4268,7 +4392,7 @@ static void do_projectpaint_soften(ProjPaintState *ps, ProjPixel *projPixel, flo 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); + rgba[0] = rgba[1] = rgba[2] = IMB_colormanagement_get_luminance(rgba); if (fabsf(rgba[0]) > ps->brush->sharp_threshold) { float alpha = rgba_pixel[3]; rgba[3] = rgba_pixel[3] = mask; @@ -4414,7 +4538,8 @@ static void *do_projectpaint_thread(void *ph_v) 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; + const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? + 0 : (brush->flag & BRUSH_LOCK_ALPHA) != 0; LinkNode *smearPixels = NULL; LinkNode *smearPixels_f = NULL; @@ -4440,8 +4565,13 @@ static void *do_projectpaint_thread(void *ph_v) /* Check this bucket and its faces are initialized */ if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) { + rctf clip_rect = bucket_bounds; + clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; + clip_rect.xmax += PROJ_PIXEL_TOLERANCE; + clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; + clip_rect.ymax += PROJ_PIXEL_TOLERANCE; /* No pixels initialized */ - project_bucket_init(ps, thread_index, bucket_index, &bucket_bounds); + project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); } if (ps->source != PROJ_SRC_VIEW) { @@ -4531,8 +4661,17 @@ static void *do_projectpaint_thread(void *ph_v) } 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]; + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } } last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; @@ -4583,7 +4722,7 @@ static void *do_projectpaint_thread(void *ph_v) if (dist_sq <= brush_radius_sq) { dist = sqrtf(dist_sq); - falloff = BKE_brush_curve_strength(ps->brush, dist, brush_radius); + falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); if (falloff > 0.0f) { float texrgb[3]; @@ -4697,11 +4836,20 @@ static void *do_projectpaint_thread(void *ph_v) else do_projectpaint_draw(ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); break; } - } - 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]; + if (lock_alpha) { + if (is_floatbuf) { + /* slightly more involved case since floats are in premultiplied space we need + * to make sure alpha is consistent, see T44627 */ + float rgb_straight[4]; + premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); + rgb_straight[3] = projPixel->origColor.f_pt[3]; + straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); + } + else { + projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; + } + } } /* done painting */ @@ -4818,7 +4966,8 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po /* calculate pivot for rotation around seletion if needed */ if (U.uiflag & USER_ORBIT_SELECTION) { float w[3]; - int side, index; + int index; + int side; index = project_paint_PickFace(ps, pos, w, &side); @@ -4837,7 +4986,7 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } ups->average_stroke_counter++; - mul_m4_v3(ps->ob->obmat, world); + mul_m4_v3(ps->obmat, world); add_v3_v3(ups->average_stroke_accum, world); ups->last_stroke_valid = true; } @@ -4847,33 +4996,21 @@ static bool project_paint_op(void *state, const float lastpos[2], const float po } -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) +static void paint_proj_stroke_ps( + const bContext *UNUSED(C), void *ps_handle_p, const float prev_pos[2], const float pos[2], + const bool eraser, float pressure, float distance, float size, + /* extra view */ + ProjPaintState *ps + ) { - ProjPaintState *ps = pps; + ProjStrokeHandle *ps_handle = ps_handle_p; 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) { - View3D *v3d = ps->v3d; - float *cursor = ED_view3d_cursor3d_get(scene, v3d); - int mval_i[2] = {(int)pos[0], (int)pos[1]}; - - view3d_operator_needs_opengl(C); - - if (!ED_view3d_autodist(scene, ps->ar, v3d, mval_i, cursor, false, NULL)) - return; - - ED_region_tag_redraw(ps->ar); - - return; - } /* handle gradient and inverted stroke color here */ if (ps->tool == PAINT_TOOL_DRAW) { @@ -4894,14 +5031,42 @@ void paint_proj_stroke(const bContext *C, void *pps, const float prev_pos[2], co } } - /* continue adding to existing partial redraw rects until redraw */ - if (!ps->need_redraw) { - for (a = 0; a < ps->image_tot; a++) - partial_redraw_array_init(ps->projImages[a].partRedrawRect); + if (project_paint_op(ps, prev_pos, pos)) { + ps_handle->need_redraw = true; + project_image_refresh_tagged(ps); } +} + + +void paint_proj_stroke( + const bContext *C, void *ps_handle_p, const float prev_pos[2], const float pos[2], + const bool eraser, float pressure, float distance, float size) +{ + int i; + ProjStrokeHandle *ps_handle = ps_handle_p; + + /* clone gets special treatment here to avoid going through image initialization */ + if (ps_handle->is_clone_cursor_pick) { + Scene *scene = ps_handle->scene; + View3D *v3d = CTX_wm_view3d(C); + ARegion *ar = CTX_wm_region(C); + float *cursor = ED_view3d_cursor3d_get(scene, v3d); + int mval_i[2] = {(int)pos[0], (int)pos[1]}; - if (project_paint_op(ps, prev_pos, pos)) - ps->need_redraw = true; + view3d_operator_needs_opengl(C); + + if (!ED_view3d_autodist(scene, ar, v3d, mval_i, cursor, false, NULL)) + return; + + ED_region_tag_redraw(ar); + + return; + } + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + paint_proj_stroke_ps(C, ps_handle_p, prev_pos, pos, eraser, pressure, distance, size, ps); + } } @@ -4920,7 +5085,7 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int 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) ? + 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); @@ -5001,6 +5166,9 @@ static void project_state_init(bContext *C, Object *ob, ProjPaintState *ps, int if (ps->normal_angle_range <= 0.0f) ps->do_mask_normal = false; /* no need to do blending */ + ps->normal_angle__cos = cosf(ps->normal_angle); + ps->normal_angle_inner__cos = cosf(ps->normal_angle_inner); + ps->dither = settings->imapaint.dither; return; @@ -5008,51 +5176,116 @@ 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"); + ProjStrokeHandle *ps_handle; + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + int i; + bool is_multi_view; + char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; - project_state_init(C, ob, ps, mode); + ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); + ps_handle->scene = scene; + ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); - if (ps->tool == PAINT_TOOL_CLONE && mode == BRUSH_STROKE_INVERT) { + /* bypass regular stroke logic */ + if ((ps_handle->brush->imagepaint_tool == PAINT_TOOL_CLONE) && + (mode == BRUSH_STROKE_INVERT)) + { view3d_operator_needs_opengl(C); - return ps; + ps_handle->is_clone_cursor_pick = true; + return ps_handle; } - paint_brush_init_tex(ps->brush); + ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); - ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; + ps_handle->symmetry_flags = settings->imapaint.paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + ps_handle->ps_views_tot = 1 + (pow_i(2, count_bits_i(ps_handle->symmetry_flags)) - 1); + is_multi_view = (ps_handle->ps_views_tot != 1); - if (ps->ob == NULL || !(ps->ob->lay & ps->v3d->lay)) { - MEM_freeN(ps); - return NULL; + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = MEM_callocN(sizeof(ProjPaintState), "ProjectionPaintState"); + ps_handle->ps_views[i] = ps; + } + + if (ps_handle->symmetry_flags) { + int index = 0; + + int x = 0; + do { + int y = 0; + do { + int z = 0; + do { + symmetry_flag_views[index++] = ( + (x ? PAINT_SYMM_X : 0) | + (y ? PAINT_SYMM_Y : 0) | + (z ? PAINT_SYMM_Z : 0)); + BLI_assert(index <= ps_handle->ps_views_tot); + } while ((z++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Z)); + } while ((y++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_Y)); + } while ((x++ == 0) && (ps_handle->symmetry_flags & PAINT_SYMM_X)); + BLI_assert(index == ps_handle->ps_views_tot); } - ps->orig_brush_size = BKE_brush_size_get(ps->scene, ps->brush); + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + project_state_init(C, ob, ps, mode); + + if (ps->ob == NULL || !(ps->ob->lay & ps->v3d->lay)) { + ps_handle->ps_views_tot = i + 1; + goto fail; + } + } /* Don't allow brush size below 2 */ - if (BKE_brush_size_get(ps->scene, ps->brush) < 2) - BKE_brush_size_set(ps->scene, ps->brush, 2 * U.pixelsize); + if (BKE_brush_size_get(scene, ps_handle->brush) < 2) + BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); /* allocate and initialize spatial data structures */ - project_paint_begin(ps); - if (ps->dm == NULL) { - MEM_freeN(ps); - return NULL; + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + + ps->source = (ps->tool == PAINT_TOOL_FILL) ? PROJ_SRC_VIEW_FILL : PROJ_SRC_VIEW; + project_image_refresh_tagged(ps); + + /* re-use! */ + if (i != 0) { + ps->is_shared_user = true; + PROJ_PAINT_STATE_SHARED_MEMCPY(ps, ps_handle->ps_views[0]); + } + + project_paint_begin(ps, is_multi_view, symmetry_flag_views[i]); + + paint_proj_begin_clone(ps, mouse); + + if (ps->dm == NULL) { + goto fail; + return NULL; + } } - paint_proj_begin_clone(ps, mouse); + paint_brush_init_tex(ps_handle->brush); + + return ps_handle; + - return ps; +fail: + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps = ps_handle->ps_views[i]; + MEM_freeN(ps); + } + MEM_freeN(ps_handle); + return NULL; } -void paint_proj_redraw(const bContext *C, void *pps, bool final) +void paint_proj_redraw(const bContext *C, void *ps_handle_p, bool final) { - ProjPaintState *ps = pps; - - if (ps->need_redraw) { - project_image_refresh_tagged(ps); + ProjStrokeHandle *ps_handle = ps_handle_p; - ps->need_redraw = false; + if (ps_handle->need_redraw) { + ps_handle->need_redraw = false; } else if (!final) { return; @@ -5067,19 +5300,34 @@ void paint_proj_redraw(const bContext *C, void *pps, bool final) } } -void paint_proj_stroke_done(void *pps) +void paint_proj_stroke_done(void *ps_handle_p) { - ProjPaintState *ps = pps; - if (ps->tool == PAINT_TOOL_CLONE && ps->mode == BRUSH_STROKE_INVERT) { - MEM_freeN(ps); + ProjStrokeHandle *ps_handle = ps_handle_p; + Scene *scene = ps_handle->scene; + int i; + + if (ps_handle->is_clone_cursor_pick) { + MEM_freeN(ps_handle); return; } - BKE_brush_size_set(ps->scene, ps->brush, ps->orig_brush_size); - paint_brush_exit_tex(ps->brush); + for (i = 1; i < ps_handle->ps_views_tot; i++) { + PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); + } + + BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); + + paint_brush_exit_tex(ps_handle->brush); + + for (i = 0; i < ps_handle->ps_views_tot; i++) { + ProjPaintState *ps; + ps = ps_handle->ps_views[i]; + project_paint_end(ps); + MEM_freeN(ps); + + } - project_paint_end(ps); - MEM_freeN(ps); + MEM_freeN(ps_handle); } /* use project paint to re-apply an image */ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) @@ -5159,7 +5407,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) ED_image_undo_restore, ED_image_undo_free, NULL); /* allocate and initialize spatial data structures */ - project_paint_begin(&ps); + project_paint_begin(&ps, false, 0); if (ps.dm == NULL) { BKE_brush_size_set(scene, ps.brush, orig_brush_size); @@ -5233,7 +5481,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) if (w > maxsize) w = maxsize; if (h > maxsize) h = maxsize; - ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, NULL, err_out); if (!ibuf) { /* Mostly happens when OpenGL offscreen buffer was failed to create, */ /* but could be other reasons. Should be handled in the future. nazgul */ @@ -5241,7 +5489,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - image = BKE_image_add_from_imbuf(ibuf); + image = BKE_image_add_from_imbuf(ibuf, "image_view"); /* Drop reference to ibuf so that the image owns it */ IMB_freeImBuf(ibuf); @@ -5271,8 +5519,6 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) array[2] = is_ortho ? 1.0f : 0.0f; IDP_AddToGroup(idgroup, view_data); - - rename_id(&image->id, "image_view"); } return OPERATOR_FINISHED; @@ -5444,7 +5690,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) RNA_string_get(op->ptr, "name", imagename); } ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color); + gen_type, color, false); return ima; } @@ -5487,7 +5733,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) ntreeUpdateTree(CTX_data_main(C), ntree); } else { - MTex *mtex = add_mtex_id(&ma->id, -1); + MTex *mtex = BKE_texture_mtex_add_id(&ma->id, -1); /* successful creation of mtex layer, now create set */ if (mtex) { @@ -5506,7 +5752,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op) } } - mtex->tex = add_texture(bmain, DATA_(layer_type_items[type_id].name)); + mtex->tex = BKE_texture_add(bmain, DATA_(layer_type_items[type_id].name)); mtex->mapto = type; if (mtex->tex) { diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 9e558092f73..fd7e053fea3 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -34,14 +34,11 @@ struct ARegion; struct bContext; -struct bglMats; struct Brush; struct ImagePool; struct ColorSpace; struct ColorManagedDisplay; struct ListBase; -struct Material; -struct Mesh; struct MTex; struct Object; struct PaintStroke; @@ -55,7 +52,6 @@ struct ViewContext; struct wmEvent; struct wmOperator; struct wmOperatorType; -struct ImagePaintState; struct wmWindowManager; struct DMCoNo; enum PaintMode; @@ -149,7 +145,7 @@ typedef struct ImagePaintPartialRedraw { 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, 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_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **, bool **valid, bool proj, bool find_prev); void image_undo_remove_masks(void); void image_undo_init_locks(void); void image_undo_end_locks(void); @@ -164,7 +160,7 @@ 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], 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_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(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); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 25f22996050..2254bc991b6 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -290,7 +290,7 @@ typedef struct LassoMaskData { static bool is_effected_lasso(LassoMaskData *data, float co[3]) { float scr_co_f[2]; - short scr_co_s[2]; + int scr_co_s[2]; float co_final[3]; flip_v3_v3(co_final, co, data->symmpass); @@ -301,8 +301,13 @@ static bool is_effected_lasso(LassoMaskData *data, float co[3]) scr_co_s[1] = scr_co_f[1]; /* clip against screen, because lasso is limited to screen only */ - if (scr_co_s[0] < data->rect.xmin || scr_co_s[1] < data->rect.ymin || scr_co_s[0] >= data->rect.xmax || scr_co_s[1] >= data->rect.ymax) + if ((scr_co_s[0] < data->rect.xmin) || + (scr_co_s[1] < data->rect.ymin) || + (scr_co_s[0] >= data->rect.xmax) || + (scr_co_s[1] >= data->rect.ymax)) + { return false; + } scr_co_s[0] -= data->rect.xmin; scr_co_s[1] -= data->rect.ymin; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 121b0b83a4b..eebd49895ef 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -196,7 +196,10 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op)) Brush *brush = paint->brush; PaintMode mode = BKE_paintmode_get_active_from_context(C); Palette *palette = paint->palette; - PaletteColor *color = BKE_palette_color_add(palette); + PaletteColor *color; + + color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; if (ELEM(mode, PAINT_TEXTURE_PROJECTIVE, PAINT_TEXTURE_2D, PAINT_VERTEX)) { copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush)); @@ -231,7 +234,9 @@ static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) Palette *palette = paint->palette; PaletteColor *color = BLI_findlink(&palette->colors, palette->active_color); - BKE_palette_color_remove(palette, color); + if (color) { + BKE_palette_color_remove(palette, color); + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index bfd429d0924..ece6ea7a940 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -68,7 +68,7 @@ #include <float.h> #include <math.h> -// #define DEBUG_TIME +#define DEBUG_TIME #ifdef DEBUG_TIME # include "PIL_time_utildefines.h" @@ -347,7 +347,8 @@ static bool paint_brush_update(bContext *C, if (!stroke->brush_init) { copy_v2_v2(ups->last_rake, mouse_init); } - else { + /* curve strokes do their own rake calculation */ + else if (!(brush->flag & BRUSH_CURVE)) { paint_calculate_rake_rotation(ups, brush, mouse_init); } } @@ -556,7 +557,7 @@ static float paint_stroke_integrate_overlap(Brush *br, float factor) g = 1.0f / m; max = 0; for (i = 0; i < m; i++) { - float overlap = paint_stroke_overlapped_curve(br, i * g, spacing); + float overlap = fabs(paint_stroke_overlapped_curve(br, i * g, spacing)); if (overlap > max) max = overlap; @@ -671,9 +672,8 @@ PaintStroke *paint_stroke_new(bContext *C, get_imapaint_zoom(C, &zoomx, &zoomy); stroke->zoom_2d = max_ff(zoomx, zoomy); - if (stroke->stroke_mode == BRUSH_STROKE_INVERT) - { - if (br->flag & (BRUSH_CURVE | BRUSH_LINE)) { + if (stroke->stroke_mode == BRUSH_STROKE_INVERT) { + if (br->flag & (BRUSH_CURVE)) { RNA_enum_set(op->ptr, "mode", BRUSH_STROKE_NORMAL); } } @@ -955,6 +955,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str Brush *br = stroke->brush; if (br->flag & BRUSH_CURVE) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; 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; @@ -975,18 +976,39 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str for (i = 0; i < pc->tot_points - 1; i++, pcp++) { int j; float data[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; + float tangents[(PAINT_CURVE_NUM_SEGMENTS + 1) * 2]; PaintCurvePoint *pcp_next = pcp + 1; + bool do_rake = false; - for (j = 0; j < 2; j++) + 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])); + } + if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || + (br->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) + { + do_rake = true; + for (j = 0; j < 2; j++) { + BKE_curve_forward_diff_tangent_bezier( + pcp->bez.vec[1][j], + pcp->bez.vec[2][j], + pcp_next->bez.vec[0][j], + pcp_next->bez.vec[1][j], + tangents + j, PAINT_CURVE_NUM_SEGMENTS, sizeof(float[2])); + } + } for (j = 0; j < PAINT_CURVE_NUM_SEGMENTS; j++) { + if (do_rake) { + float rotation = atan2f(tangents[2 * j], tangents[2 * j + 1]); + paint_update_brush_rake_rotation(ups, br, rotation); + } + if (!stroke->stroke_started) { stroke->last_pressure = 1.0; copy_v2_v2(stroke->last_mouse_position, data + 2 * j); @@ -1015,14 +1037,14 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str return false; } -static void paint_stroke_line_constrain (PaintStroke *stroke, float mouse[2]) +static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2]) { if (stroke->constrain_line) { float line[2]; float angle, len, res; sub_v2_v2v2(line, mouse, stroke->last_mouse_position); - angle = atan2(line[1], line[0]); + angle = atan2f(line[1], line[0]); len = len_v2(line); /* divide angle by PI/4 */ @@ -1129,7 +1151,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_FINISHED; } else if (br->flag & BRUSH_LINE) { - if (event->ctrl) + if (event->alt) stroke->constrain_line = true; else stroke->constrain_line = false; @@ -1137,8 +1159,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) copy_v2_fl2(mouse, event->mval[0], event->mval[1]); paint_stroke_line_constrain(stroke, mouse); - if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) - { + if (stroke->stroke_started && (first_modal || (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)))) { if ((br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) || (br->mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) { copy_v2_v2(stroke->ups->last_rake, stroke->last_mouse_position); } diff --git a/source/blender/editors/sculpt_paint/paint_undo.c b/source/blender/editors/sculpt_paint/paint_undo.c index 0293a0bfc00..42f0aaab173 100644 --- a/source/blender/editors/sculpt_paint/paint_undo.c +++ b/source/blender/editors/sculpt_paint/paint_undo.c @@ -331,32 +331,33 @@ void ED_undo_paint_step_num(bContext *C, int type, int step) undo_step_num(C, &MeshUndoStack, step); } -static char *undo_stack_get_name(UndoStack *stack, int nr, int *active) +static char *undo_stack_get_name(UndoStack *stack, int nr, bool *r_active) { UndoElem *uel; - if (active) *active = 0; + if (r_active) *r_active = false; uel = BLI_findlink(&stack->elems, nr); if (uel) { - if (active && uel == stack->current) - *active = 1; + if (r_active && (uel == stack->current)) { + *r_active = true; + } return uel->name; } return NULL; } -const char *ED_undo_paint_get_name(bContext *C, int type, int nr, int *active) +const char *ED_undo_paint_get_name(bContext *C, int type, int nr, bool *r_active) { if (type == UNDO_PAINT_IMAGE) { undo_stack_cleanup(&ImageUndoStack, C); - return undo_stack_get_name(&ImageUndoStack, nr, active); + return undo_stack_get_name(&ImageUndoStack, nr, r_active); } else if (type == UNDO_PAINT_MESH) { undo_stack_cleanup(&MeshUndoStack, C); - return undo_stack_get_name(&MeshUndoStack, nr, active); + return undo_stack_get_name(&MeshUndoStack, nr, r_active); } return NULL; } @@ -379,7 +380,7 @@ bool ED_undo_paint_empty(int type) return false; } -int ED_undo_paint_valid(int type, const char *name) +bool ED_undo_paint_is_valid(int type, const char *name) { UndoStack *stack; diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c index c0ed5005397..4e550543479 100644 --- a/source/blender/editors/sculpt_paint/paint_utils.c +++ b/source/blender/editors/sculpt_paint/paint_utils.c @@ -379,7 +379,7 @@ static int imapaint_pick_face(ViewContext *vc, const int mval[2], unsigned int * return 0; /* sample only on the exact position */ - *r_index = view3d_sample_backbuf(vc, mval[0], mval[1]); + *r_index = ED_view3d_backbuf_sample(vc, mval[0], mval[1]); if ((*r_index) == 0 || (*r_index) > (unsigned int)totpoly) { return 0; @@ -395,7 +395,7 @@ static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index) { Image *ima; MPoly *mp = me->mpoly + face_index; - Material *ma = give_current_material(ob, mp->mat_nr + 1);; + Material *ma = give_current_material(ob, mp->mat_nr + 1); ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; return ima; @@ -439,6 +439,7 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr } color = BKE_palette_color_add(palette); + palette->active_color = BLI_listbase_count(&palette->colors) - 1; } @@ -486,12 +487,15 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr 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; + u = u * ibuf->x; + v = v * ibuf->y; if (ibuf->rect_float) { float rgba_f[4]; - bilinear_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + if (U.gameflags & USER_DISABLE_MIPMAP) + nearest_interpolation_color_wrap(ibuf, NULL, rgba_f, u, v); + else + 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); @@ -503,7 +507,10 @@ void paint_sample_color(bContext *C, ARegion *ar, int x, int y, bool texpaint_pr } else { unsigned char rgba[4]; - bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + if (U.gameflags & USER_DISABLE_MIPMAP) + nearest_interpolation_color_wrap(ibuf, rgba, NULL, u, v); + else + bilinear_interpolation_color_wrap(ibuf, rgba, NULL, u, v); if (use_palette) { rgb_uchar_to_float(color->rgb, rgba); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index e87dd6c2810..5af327e7b49 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -37,6 +37,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_colormanagement.h" #include "DNA_armature_types.h" #include "DNA_mesh_types.h" @@ -208,7 +209,7 @@ static void do_shared_vertex_tesscol(Mesh *me, bool *mfacetag) { /* if no mcol: do not do */ /* if tface: only the involved faces, otherwise all */ - const int use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL); + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; MFace *mface; int a; short *scolmain, *scol; @@ -279,7 +280,7 @@ static void do_shared_vertex_tesscol(Mesh *me, bool *mfacetag) static void do_shared_vertexcol(Mesh *me, bool *mlooptag, bool *mfacetag, const bool do_tessface) { - const int use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL); + const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; MPoly *mp; int (*scol)[4]; int i, j; @@ -768,7 +769,7 @@ BLI_INLINE unsigned int mcol_lighten(unsigned int col1, unsigned int col2, int f /* See if are lighter, if so mix, else don't do anything. * if the paint col is darker then the original, then ignore */ - if (rgb_to_grayscale_byte(cp1) > rgb_to_grayscale_byte(cp2)) { + if (IMB_colormanagement_get_luminance_byte(cp1) > IMB_colormanagement_get_luminance_byte(cp2)) { return col1; } @@ -801,7 +802,7 @@ BLI_INLINE unsigned int mcol_darken(unsigned int col1, unsigned int col2, int fa /* See if were darker, if so mix, else don't do anything. * if the paint col is brighter then the original, then ignore */ - if (rgb_to_grayscale_byte(cp1) < rgb_to_grayscale_byte(cp2)) { + if (IMB_colormanagement_get_luminance_byte(cp1) < IMB_colormanagement_get_luminance_byte(cp2)) { return col1; } @@ -877,7 +878,7 @@ static int sample_backbuf_area(ViewContext *vc, int *indexar, int totface, int x * brushes with size > 64, why is this here? */ /*if (size > 64.0) size = 64.0;*/ - ibuf = view3d_read_backbuf(vc, x - size, y - size, x + size, y + size); + ibuf = ED_view3d_backbuf_read(vc, x - size, y - size, x + size, y + size); if (ibuf) { unsigned int *rt = ibuf->rect; @@ -935,7 +936,7 @@ static float calc_vp_strength_col_dl(VPaint *vp, ViewContext *vc, const float co else { factor = 1.0f; } - return factor * BKE_brush_curve_strength(brush, dist, brush_size_pressure); + return factor * BKE_brush_curve_strength_clamped(brush, dist, brush_size_pressure); } } if (rgba) @@ -1090,7 +1091,7 @@ static int weight_sample_invoke(bContext *C, wmOperator *op, const wmEvent *even me = BKE_mesh_from_object(vc.obact); if (me && me->dvert && vc.v3d && vc.rv3d) { - const int use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int v_idx_best = -1; unsigned int index; @@ -1176,7 +1177,7 @@ static EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, PointerRNA if (me && me->dvert && vc.v3d && vc.rv3d && vc.obact->defbase.first) { const int defbase_tot = BLI_listbase_count(&vc.obact->defbase); - const int use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; int *groups = MEM_callocN(defbase_tot * sizeof(int), "groups"); bool found = false; unsigned int index; @@ -3216,7 +3217,7 @@ static void gradientVert_update(DMGradient_userData *grad_data, int index) /* no need to clamp 'alpha' yet */ /* adjust weight */ - alpha = BKE_brush_curve_strength(grad_data->brush, alpha, 1.0f); + alpha = BKE_brush_curve_strength_clamped(grad_data->brush, alpha, 1.0f); if (alpha != 0.0f) { MDeformVert *dv = &me->dvert[index]; diff --git a/source/blender/editors/sculpt_paint/paint_vertex_proj.c b/source/blender/editors/sculpt_paint/paint_vertex_proj.c index ae729248f7e..c939eb6df35 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_proj.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_proj.c @@ -186,7 +186,7 @@ static void vpaint_proj_dm_map_cosnos_update(struct VertProjHandle *vp_handle, /* highly unlikely this will become unavailable once painting starts (perhaps with animated modifiers) */ if (LIKELY(dm->foreachMappedVert)) { - fill_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); + copy_vn_fl(vp_handle->dists_sq, me->totvert, FLT_MAX); dm->foreachMappedVert(dm, vpaint_proj_dm_map_cosnos_update__map_cb, &vp_update, DM_FOREACH_USE_NORMAL); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ea046b7d4d5..ede90b6e1f1 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -116,6 +116,13 @@ static int system_physical_thread_count(void) } #endif /* __APPLE__ */ +/** \name Tool Capabilities + * + * Avoid duplicate checks, internal logic only, + * share logic with #rna_def_sculpt_capabilities where possible. + * + * \{ */ + /* Check if there are any active modifiers in stack (used for flushing updates at enter/exit sculpt mode) */ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) { @@ -133,6 +140,43 @@ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) return 0; } +static bool sculpt_tool_needs_original(const char sculpt_tool) +{ + return ELEM(sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_LAYER); +} + +static bool sculpt_tool_is_proxy_used(const char sculpt_tool) +{ + return ELEM(sculpt_tool, + SCULPT_TOOL_SMOOTH, + SCULPT_TOOL_LAYER); +} + +/** + * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action + */ +static int sculpt_brush_needs_normal(const Brush *brush) +{ + return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && + (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)); +} + +/** \} */ typedef enum StrokeFlags { @@ -182,8 +226,6 @@ typedef struct StrokeCache { ViewContext *vc; Brush *brush; - float (*face_norms)[3]; /* Copy of the mesh faces' normals */ - float special_rotation; float grab_delta[3], grab_delta_symmetry[3]; float old_grab_location[3], orig_grab_location[3]; @@ -285,30 +327,70 @@ static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) { if (orig_data->unode->type == SCULPT_UNDO_COORDS) { - if (orig_data->coords) { - orig_data->co = orig_data->coords[iter->i]; + if (orig_data->bm_log) { + BM_log_original_vert_data( + orig_data->bm_log, iter->bm_vert, + &orig_data->co, &orig_data->no); } else { - orig_data->co = BM_log_original_vert_co(orig_data->bm_log, iter->bm_vert); - } - - if (orig_data->normals) { + orig_data->co = orig_data->coords[iter->i]; orig_data->no = orig_data->normals[iter->i]; } - else { - orig_data->no = BM_log_original_vert_no(orig_data->bm_log, iter->bm_vert); - } } else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->vmasks) { - orig_data->mask = orig_data->vmasks[iter->i]; + if (orig_data->bm_log) { + orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); } else { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + orig_data->mask = orig_data->vmasks[iter->i]; } } } +/** \name SculptProjectVector + * + * Fast-path for #project_plane_v3_v3v3 + * + * \{ */ + +typedef struct SculptProjectVector { + float plane[3]; + float len_sq; + float len_sq_inv_neg; + bool is_valid; + +} SculptProjectVector; + +/** + * \param plane Direction, can be any length. + */ +static void sculpt_project_v3_cache_init( + SculptProjectVector *spvc, const float plane[3]) +{ + copy_v3_v3(spvc->plane, plane); + spvc->len_sq = len_squared_v3(spvc->plane); + spvc->is_valid = (spvc->len_sq > FLT_EPSILON); + spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; +} + +/** + * Calculate the projection. + */ +static void sculpt_project_v3( + const SculptProjectVector *spvc, const float vec[3], + float r_vec[3]) +{ +#if 0 + project_plane_v3_v3v3(r_vec, vec, spvc->plane); +#else + /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ + madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); +#endif +} + +/** \} */ + + /**********************************************************************/ /* Returns true if the stroke will use dynamic topology, false @@ -317,8 +399,8 @@ static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, * Factors: some brushes like grab cannot do dynamic topology. * Others, like smooth, are better without. Same goes for alt- * key smoothing. */ -static int sculpt_stroke_dynamic_topology(const SculptSession *ss, - const Brush *brush) +static bool sculpt_stroke_is_dynamic_topology( + const SculptSession *ss, const Brush *brush) { return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && @@ -328,20 +410,8 @@ static int sculpt_stroke_dynamic_topology(const SculptSession *ss, * dynamic-topology */ !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - - (!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))); + + SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /*** paint mesh ***/ @@ -349,17 +419,11 @@ static int sculpt_stroke_dynamic_topology(const SculptSession *ss, static void paint_mesh_restore_co(Sculpt *sd, Object *ob) { SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; const Brush *brush = BKE_paint_brush(&sd->paint); - int i; PBVHNode **nodes; int n, totnode; -#ifndef _OPENMP - (void)sd; /* quied unused warning */ -#endif - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new @@ -404,12 +468,6 @@ static void paint_mesh_restore_co(Sculpt *sd, Object *ob) } } - if (ss->face_normals) { - for (i = 0; i < ss->totpoly; i++) { - copy_v3_v3(ss->face_normals[i], cache->face_norms[i]); - } - } - if (nodes) MEM_freeN(nodes); } @@ -509,7 +567,7 @@ static void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) } } -BLI_INLINE bool sculpt_brush_test_clipping(SculptBrushTest *test, const float co[3]) +BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) { RegionView3D *rv3d = test->clip_rv3d; return (rv3d && (ED_view3d_clipping_test(rv3d, co, true))); @@ -547,7 +605,7 @@ static bool sculpt_brush_test_sq(SculptBrushTest *test, const float co[3]) } } -static bool sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) +static bool sculpt_brush_test_fast(const SculptBrushTest *test, const float co[3]) { if (sculpt_brush_test_clipping(test, co)) { return 0; @@ -555,7 +613,7 @@ static bool sculpt_brush_test_fast(SculptBrushTest *test, float co[3]) return len_squared_v3v3(co, test->location) <= test->radius_squared; } -static bool sculpt_brush_test_cube(SculptBrushTest *test, float co[3], float local[4][4]) +static bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]) { float side = M_SQRT1_2; float local_co[3]; @@ -608,7 +666,7 @@ static float frontface(Brush *br, const float sculpt_normal[3], #if 0 -static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], float an[3]) +static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], const float area_no[3]) { if (sculpt_brush_test_fast(test, co)) { float t1[3], t2[3], t3[3], dist; @@ -616,7 +674,7 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca sub_v3_v3v3(t1, location, co); sub_v3_v3v3(t2, x2, location); - cross_v3_v3v3(t3, an, t1); + cross_v3_v3v3(t3, area_no, t1); dist = len_v3(t3) / len_v3(t2); @@ -702,18 +760,447 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) } } +/** \name Calculate Normal and Center + * + * Calculate geometry surrounding the brush center. + * (optionally using original coordinates). + * + * Functions are: + * - #calc_area_center + * - #calc_area_normal + * - #calc_area_normal_and_center + * + * \note These are all _very_ similar, when changing one, check others. + * \{ */ + +static void calc_area_center( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_co[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#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; + SculptUndoNode *unode; + float private_co[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + cross_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for flatten center */ + add_v3_v3(area_co[0], private_co[0]); + add_v3_v3(area_co[1], private_co[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_co); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_co[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } +} + + +static void calc_area_normal( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_no[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#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; + SculptUndoNode *unode; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for area normal */ + add_v3_v3(area_no[0], private_no[0]); + add_v3_v3(area_no[1], private_no[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_no); n++) { + if (normalize_v3_v3(r_area_no, area_no[n]) != 0.0f) { + break; + } + } +} + +/* this calculates flatten center and area normal together, + * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ +static void calc_area_normal_and_center( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3], float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_co[2][3] = {{0.0f}}; + float area_no[2][3] = {{0.0f}}; + + int count[2] = {0}; + +#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; + SculptUndoNode *unode; + float private_co[2][3] = {{0.0f}}; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original; + + unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); + sculpt_brush_test_init(ss, &test); + + use_original = (ss->cache->original && (unode->co || unode->bm_entry)); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && has_bm_orco) { + float (*orco_coords)[3]; + int (*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data( + nodes[n], + &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_fast(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_fast(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + add_v3_v3(private_co[flip_index], co); + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + +#pragma omp critical + { + /* for flatten center */ + add_v3_v3(area_co[0], private_co[0]); + add_v3_v3(area_co[1], private_co[1]); + + /* for area normal */ + add_v3_v3(area_no[0], private_no[0]); + add_v3_v3(area_no[1], private_no[1]); + + /* weights */ + count[0] += private_count[0]; + count[1] += private_count[1]; + } + } + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_co); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_co[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_no); n++) { + if (normalize_v3_v3(r_area_no, area_no[n]) != 0.0f) { + break; + } + } +} + +/** \} */ + + /* 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, UnifiedPaintSettings *ups) +static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const float feather, const UnifiedPaintSettings *ups) { const Scene *scene = cache->vc->scene; - Brush *brush = BKE_paint_brush(&sd->paint); + const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); /* Primary strength input; square it to make lower values more sensitive */ const float root_alpha = BKE_brush_alpha_get(scene, brush); float alpha = root_alpha * root_alpha; - float dir = brush->flag & BRUSH_DIR_IN ? -1 : 1; + float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; 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; @@ -927,132 +1414,34 @@ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float } } -static void add_norm_if(float view_vec[3], float out[3], float out_flip[3], float fno[3]) -{ - if ((dot_v3v3(view_vec, fno)) > 0) { - add_v3_v3(out, fno); - } - else { - add_v3_v3(out_flip, fno); /* out_flip is used when out is {0,0,0} */ - } -} - -static void calc_area_normal(Sculpt *sd, Object *ob, float an[3], PBVHNode **nodes, int totnode) -{ - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - - SculptSession *ss = ob->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); - int n; - bool original; - - /* Grab brush requires to test on original data (see r33888 and - * bug #25371) */ - original = (BKE_paint_brush(&sd->paint)->sculpt_tool == SCULPT_TOOL_GRAB ? - true : ss->cache->original); - - /* In general the original coords are not available with dynamic - * topology - * - * Mask tool could not use undo nodes to get coordinates from - * since the coordinates are not stored in those odes. - * And mask tool is not gonna to modify vertex coordinates, - * so we don't actually need to use modified coords. - */ - if (ss->bm || brush->sculpt_tool == SCULPT_TOOL_MASK) - original = false; - - (void)sd; /* unused w/o openmp */ - - zero_v3(an); - -#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; - SculptUndoNode *unode; - float private_an[3] = {0.0f, 0.0f, 0.0f}; - float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (original) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, fno); - } - else { - add_norm_if(ss->cache->view_normal, private_an, private_out_flip, vd.fno); - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - add_v3_v3(an, private_an); - add_v3_v3(out_flip, private_out_flip); - } - } - - if (is_zero_v3(an)) - copy_v3_v3(an, out_flip); - - normalize_v3(an); -} - /* Calculate primary direction of movement for many brushes */ -static void calc_sculpt_normal(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float an[3]) +static void calc_sculpt_normal( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3]) { const Brush *brush = BKE_paint_brush(&sd->paint); const SculptSession *ss = ob->sculpt; switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(an, ss->cache->true_view_normal); + copy_v3_v3(r_area_no, ss->cache->true_view_normal); break; case SCULPT_DISP_DIR_X: - an[1] = 0.0; - an[2] = 0.0; - an[0] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); break; case SCULPT_DISP_DIR_Y: - an[0] = 0.0; - an[2] = 0.0; - an[1] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); break; case SCULPT_DISP_DIR_Z: - an[0] = 0.0; - an[1] = 0.0; - an[2] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); break; case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, nodes, totnode, r_area_no); break; default: @@ -1153,27 +1542,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) } } -/* Test whether the StrokeCache.sculpt_normal needs update in - * do_brush_action() */ -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)) || - - 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)); -} - /* For the smooth brush, uses the neighboring vertices around vert to calculate * a smoothed location for vert. Skips corner vertices (used by only one * polygon.) */ @@ -1183,20 +1551,19 @@ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) const MVert *mvert = ss->mvert; float (*deform_co)[3] = ss->deform_cos; - zero_v3(avg); - /* Don't modify corner vertices */ if (vert_map->count > 1) { int i, total = 0; + zero_v3(avg); + for (i = 0; i < vert_map->count; i++) { const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - unsigned f_adj_v[3]; + unsigned f_adj_v[2]; if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) { int j; - - for (j = 0; j < 3; j++) { + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co); @@ -1227,12 +1594,11 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert) for (i = 0; i < ss->pmap[vert].count; i++) { const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; - unsigned f_adj_v[3]; + unsigned f_adj_v[2]; if (poly_get_adj_loops_from_vert(f_adj_v, p, ss->mloop, vert) != -1) { int j; - - for (j = 0; j < 3; j++) { + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { avg += vmask[f_adj_v[j]]; total++; } @@ -1248,22 +1614,24 @@ static float neighbor_average_mask(SculptSession *ss, unsigned vert) /* Same logic as neighbor_average(), but for bmesh rather than mesh */ static void bmesh_neighbor_average(float avg[3], BMVert *v) { - const int vfcount = BM_vert_face_count(v); + /* logic for 3 or more is identical */ + const int vfcount = BM_vert_face_count_ex(v, 3); - zero_v3(avg); - /* Don't modify corner vertices */ if (vfcount > 1) { BMIter liter; BMLoop *l; int i, total = 0; + zero_v3(avg); + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[3] = {l->prev->v, v, l->next->v}; + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < 3; i++) { - if (vfcount != 2 || BM_vert_face_count(adj_v[i]) <= 2) { - add_v3_v3(avg, adj_v[i]->co); + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + if (vfcount != 2 || BM_vert_face_count_ex(v_other, 2) <= 2) { + add_v3_v3(avg, v_other->co); total++; } } @@ -1279,7 +1647,7 @@ static void bmesh_neighbor_average(float avg[3], BMVert *v) } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ -static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) +static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offset) { BMIter liter; BMLoop *l; @@ -1287,13 +1655,12 @@ static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) int i, total = 0; BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[3] = {l->prev->v, v, l->next->v}; + /* skip this vertex */ + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < 3; i++) { - BMVert *v2 = adj_v[i]; - float *vmask = CustomData_bmesh_get(&bm->vdata, - v2->head.data, - CD_PAINT_MASK); + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); avg += (*vmask); total++; } @@ -1303,9 +1670,7 @@ static float bmesh_neighbor_average_mask(BMesh *bm, BMVert *v) return avg / (float)total; } else { - float *vmask = CustomData_bmesh_get(&bm->vdata, - v->head.data, - CD_PAINT_MASK); + const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); return (*vmask); } } @@ -1368,7 +1733,7 @@ static void do_bmesh_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *node, 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; + float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; val *= fade * bstrength; *vd.mask += val; CLAMP(*vd.mask, 0, 1); @@ -1399,12 +1764,11 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no SculptBrushTest test; CCGElem **griddata, *data; CCGKey key; - DMGridAdjacency *gridadj, *adj; float (*tmpgrid_co)[3], (*tmprow_co)[3]; float *tmpgrid_mask, *tmprow_mask; int v1, v2, v3, v4; int thread_num; - BLI_bitmap **grid_hidden; + BLI_bitmap * const *grid_hidden; int *grid_indices, totgrid, gridsize, i, x, y; sculpt_brush_test_init(ss, &test); @@ -1412,7 +1776,7 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no CLAMP(bstrength, 0.0f, 1.0f); BKE_pbvh_node_get_grids(ss->pbvh, node, &grid_indices, &totgrid, - NULL, &gridsize, &griddata, &gridadj); + NULL, &gridsize, &griddata); BKE_pbvh_get_grid_key(ss->pbvh, &key); grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); @@ -1429,9 +1793,8 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no for (i = 0; i < totgrid; ++i) { int gi = grid_indices[i]; - BLI_bitmap *gh = grid_hidden[gi]; + const BLI_bitmap *gh = grid_hidden[gi]; data = griddata[gi]; - adj = &gridadj[gi]; if (smooth_mask) memset(tmpgrid_mask, 0, sizeof(float) * gridsize * gridsize); @@ -1497,18 +1860,6 @@ static void do_multires_smooth_brush(Sculpt *sd, SculptSession *ss, PBVHNode *no continue; } - if (x == 0 && adj->index[0] == -1) - continue; - - if (x == gridsize - 1 && adj->index[2] == -1) - continue; - - if (y == 0 && adj->index[3] == -1) - continue; - - if (y == gridsize - 1 && adj->index[1] == -1) - continue; - index = x + y * gridsize; co = CCG_elem_offset_co(&key, data, index); fno = CCG_elem_offset_no(&key, data, index); @@ -1699,6 +2050,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float brush_alpha; int n; + SculptProjectVector spvc; + /* offset with as much as possible factored in already */ mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); mul_v3_v3(offset, ss->cache->scale); @@ -1715,6 +2068,10 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. + * Without this we get a 'flat' surface surrounding the pinch */ + sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); + /* threaded loop over nodes */ #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -1739,6 +2096,8 @@ static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod sub_v3_v3v3(val1, test.location, vd.co); mul_v3_fl(val1, fade * flippedbstrength); + sculpt_project_v3(&spvc, val1, val1); + /* then we draw */ mul_v3_v3fl(val2, offset, fade); @@ -2132,233 +2491,10 @@ static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno } } -static void calc_flatten_center(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float fc[3]) -{ - SculptSession *ss = ob->sculpt; - int n; - - int count = 0; - int count_flip = 0; - - float fc_flip[3] = {0.0, 0.0, 0.0}; - - (void)sd; /* unused w/o openmp */ - - zero_v3(fc); - -#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; - SculptUndoNode *unode; - float private_fc[3] = {0.0f, 0.0f, 0.0f}; - float private_fc_flip[3] = {0.0f, 0.0f, 0.0f}; - int private_count = 0; - int private_count_flip = 0; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (ss->cache->original && unode->co) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_fc, unode->co[vd.i]); - private_count++; - } - else { - add_v3_v3(private_fc_flip, unode->co[vd.i]); - private_count_flip++; - } - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - /* for area normal */ - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - else { - if (dot_v3v3(ss->cache->view_normal, vd.fno) > 0) { - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - add_v3_v3(fc, private_fc); - add_v3_v3(fc_flip, private_fc_flip); - count += private_count; - count_flip += private_count_flip; - } - } - if (count != 0) - mul_v3_fl(fc, 1.0f / count); - else if (count_flip != 0) - mul_v3_v3fl(fc, fc_flip, 1.0f / count_flip); - else - zero_v3(fc); -} - -/* this calculates flatten center and area normal together, - * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ -static void calc_area_normal_and_flatten_center(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float an[3], float fc[3]) -{ - SculptSession *ss = ob->sculpt; - int n; - - /* for area normal */ - float out_flip[3] = {0.0f, 0.0f, 0.0f}; - float fc_flip[3] = {0.0f, 0.0f, 0.0f}; - - /* for flatten center */ - int count = 0; - int count_flipped = 0; - - (void)sd; /* unused w/o openmp */ - - /* for area normal */ - zero_v3(an); - - /* for flatten center */ - zero_v3(fc); - -#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; - SculptUndoNode *unode; - float private_an[3] = {0.0f, 0.0f, 0.0f}; - float private_out_flip[3] = {0.0f, 0.0f, 0.0f}; - float private_fc[3] = {0.0f, 0.0f, 0.0f}; - float private_fc_flip[3] = {0.0f, 0.0f, 0.0f}; - int private_count = 0; - int private_count_flip = 0; - - unode = sculpt_undo_push_node(ob, nodes[n], SCULPT_UNDO_COORDS); - sculpt_brush_test_init(ss, &test); - - if (ss->cache->original && unode->co) { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, unode->co[vd.i])) { - /* for area normal */ - float fno[3]; - - normal_short_to_float_v3(fno, unode->no[vd.i]); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_an, fno); - add_v3_v3(private_fc, unode->co[vd.i]); - private_count++; - } - else { - add_v3_v3(private_out_flip, fno); - add_v3_v3(private_fc_flip, unode->co[vd.i]); - private_count_flip++; - } - } - } - BKE_pbvh_vertex_iter_end; - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_fast(&test, vd.co)) { - /* for area normal */ - if (vd.no) { - float fno[3]; - - normal_short_to_float_v3(fno, vd.no); - - if (dot_v3v3(ss->cache->view_normal, fno) > 0) { - add_v3_v3(private_an, fno); - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_out_flip, fno); - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - else { - if (dot_v3v3(ss->cache->view_normal, vd.fno) > 0) { - add_v3_v3(private_an, vd.fno); - add_v3_v3(private_fc, vd.co); - private_count++; - } - else { - add_v3_v3(private_out_flip, vd.fno); - add_v3_v3(private_fc_flip, vd.co); - private_count_flip++; - } - } - } - } - BKE_pbvh_vertex_iter_end; - } - -#pragma omp critical - { - /* for area normal */ - add_v3_v3(an, private_an); - add_v3_v3(out_flip, private_out_flip); - - /* for flatten center */ - add_v3_v3(fc, private_fc); - add_v3_v3(fc_flip, private_fc_flip); - count += private_count; - count_flipped += private_count_flip; - } - } - - /* for area normal */ - if (is_zero_v3(an)) - copy_v3_v3(an, out_flip); - - normalize_v3(an); - - /* for flatten center */ - if (count != 0) - mul_v3_fl(fc, 1.0f / count); - else if (count_flipped != 0) - mul_v3_v3fl(fc, fc_flip, 1.0f / count_flipped); - else - zero_v3(fc); -} - -static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float an[3], float fc[3]) +static void calc_sculpt_plane( + Sculpt *sd, Object *ob, + PBVHNode **nodes, int totnode, + float r_area_no[3], float r_area_co[3]) { SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); @@ -2369,29 +2505,23 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn { switch (brush->sculpt_plane) { case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(an, ss->cache->true_view_normal); + copy_v3_v3(r_area_no, ss->cache->true_view_normal); break; case SCULPT_DISP_DIR_X: - an[1] = 0.0; - an[2] = 0.0; - an[0] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); break; case SCULPT_DISP_DIR_Y: - an[0] = 0.0; - an[2] = 0.0; - an[1] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); break; case SCULPT_DISP_DIR_Z: - an[0] = 0.0; - an[1] = 0.0; - an[2] = 1.0; + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); break; case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_flatten_center(sd, ob, nodes, totnode, an, fc); + calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); break; default: @@ -2401,50 +2531,54 @@ static void calc_sculpt_plane(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn /* for flatten center */ /* flatten center has not been calculated yet if we are not using the area normal */ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) - calc_flatten_center(sd, ob, nodes, totnode, fc); + calc_area_center(sd, ob, nodes, totnode, r_area_co); /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, an); + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); /* for flatten center */ - copy_v3_v3(ss->cache->last_center, fc); + copy_v3_v3(ss->cache->last_center, r_area_co); } else { /* for area normal */ - copy_v3_v3(an, ss->cache->sculpt_normal); + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); /* for flatten center */ - copy_v3_v3(fc, ss->cache->last_center); + copy_v3_v3(r_area_co, ss->cache->last_center); /* for area normal */ - flip_v3(an, ss->cache->mirror_symmetry_pass); + flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); /* for flatten center */ - flip_v3(fc, ss->cache->mirror_symmetry_pass); + flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); /* for area normal */ - mul_m4_v3(ss->cache->symm_rot_mat, an); + mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); /* for flatten center */ - mul_m4_v3(ss->cache->symm_rot_mat, fc); + mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); } } /* Projects a point onto a plane along the plane's normal */ -static void point_plane_project(float intr[3], float co[3], float plane_normal[3], float plane_center[3]) +static void point_plane_project( + float intr[3], + const float co[3], const float plane_normal[3], const float plane_center[3]) { sub_v3_v3v3(intr, co, plane_center); mul_v3_v3fl(intr, plane_normal, dot_v3v3(plane_normal, intr)); sub_v3_v3v3(intr, co, intr); } -static int plane_trim(StrokeCache *cache, Brush *brush, float val[3]) +static int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { return (!(brush->flag & BRUSH_PLANE_TRIM) || ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); } -static int plane_point_side_flip(float co[3], float plane_normal[3], float plane_center[3], int flip) +static bool plane_point_side_flip( + const float co[3], const float plane_normal[3], const float plane_center[3], + const bool flip) { float delta[3]; float d; @@ -2457,7 +2591,7 @@ static int plane_point_side_flip(float co[3], float plane_normal[3], float plane return d <= 0.0f; } -static int plane_point_side(float co[3], float plane_normal[3], float plane_center[3]) +static int plane_point_side(const float co[3], const float plane_normal[3], const float plane_center[3]) { float delta[3]; @@ -2486,8 +2620,8 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); @@ -2497,13 +2631,13 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2521,7 +2655,7 @@ static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totno float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2551,16 +2685,16 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float displace; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; int n; float temp[3]; - int flip; + bool flip; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); flip = bstrength < 0; @@ -2571,11 +2705,11 @@ static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); - /* add_v3_v3v3(p, ss->cache->location, an); */ + /* add_v3_v3v3(p, ss->cache->location, area_no); */ #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2590,15 +2724,17 @@ static void do_clay_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_sq(&test, vd.co)) { - if (plane_point_side_flip(vd.co, an, fc, flip)) { + if (plane_point_side_flip(vd.co, area_no, area_co, flip)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ const float fade = bstrength * tex_strength(ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); @@ -2625,9 +2761,9 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t float displace; - float sn[3]; - float an[3]; - float fc[3]; + float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ + float area_no[3]; /* geometry normal */ + float area_co[3]; int n; @@ -2636,14 +2772,14 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t float scale[4][4]; float tmat[4][4]; - int flip; + bool flip; - calc_sculpt_plane(sd, ob, nodes, totnode, sn, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ob, an, nodes, totnode); + calc_area_normal(sd, ob, nodes, totnode, area_no); else - copy_v3_v3(an, sn); + copy_v3_v3(area_no, area_no_sp); /* delay the first daub because grab delta is not setup */ if (ss->cache->first_time) @@ -2658,16 +2794,16 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, sn, ss->cache->scale); + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); /* init mat */ - cross_v3_v3v3(mat[0], an, ss->cache->grab_delta_symmetry); + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); mat[0][3] = 0; - cross_v3_v3v3(mat[1], an, mat[0]); + cross_v3_v3v3(mat[1], area_no, mat[0]); mat[1][3] = 0; - copy_v3_v3(mat[2], an); + copy_v3_v3(mat[2], area_no); mat[2][3] = 0; copy_v3_v3(mat[3], ss->cache->location); mat[3][3] = 1; @@ -2691,15 +2827,17 @@ static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int t BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_cube(&test, vd.co, mat)) { - if (plane_point_side_flip(vd.co, sn, fc, flip)) { + if (plane_point_side_flip(vd.co, area_no_sp, area_co, flip)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, sn, fc); + point_plane_project(intr, vd.co, area_no_sp, area_co); sub_v3_v3v3(val, intr, vd.co); if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ const float fade = bstrength * tex_strength(ss, brush, vd.co, ss->cache->radius * test.dist, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f); @@ -2724,8 +2862,8 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); float displace; @@ -2734,13 +2872,13 @@ static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2755,11 +2893,11 @@ static void do_fill_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_sq(&test, vd.co)) { - if (plane_point_side(vd.co, an, fc)) { + if (plane_point_side(vd.co, area_no, area_co)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2788,8 +2926,8 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float bstrength = ss->cache->bstrength; const float radius = ss->cache->radius; - float an[3]; - float fc[3]; + float area_no[3]; + float area_co[3]; float offset = get_offset(sd, ss); float displace; @@ -2798,13 +2936,13 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, an, fc); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); displace = -radius * offset; - mul_v3_v3v3(temp, an, ss->cache->scale); + mul_v3_v3v3(temp, area_no, ss->cache->scale); mul_v3_fl(temp, displace); - add_v3_v3(fc, temp); + add_v3_v3(area_co, temp); #pragma omp parallel for schedule(guided) if ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_OMP_LIMIT) for (n = 0; n < totnode; n++) { @@ -2819,11 +2957,11 @@ static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnod BKE_pbvh_vertex_iter_begin(ss->pbvh, nodes[n], vd, PBVH_ITER_UNIQUE) { if (sculpt_brush_test_sq(&test, vd.co)) { - if (!plane_point_side(vd.co, an, fc)) { + if (!plane_point_side(vd.co, area_no, area_co)) { float intr[3]; float val[3]; - point_plane_project(intr, vd.co, an, fc); + point_plane_project(intr, vd.co, area_no, area_co); sub_v3_v3v3(val, intr, vd.co); @@ -2849,7 +2987,7 @@ static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, fl SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]/*, an[3]*/; + float offset[3]/*, area_no[3]*/; int n; float gravity_vector[3]; @@ -2946,11 +3084,7 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, Unified radius = ss->cache->radius * 1.25f; data.radius_squared = radius * radius; - data.original = ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode); @@ -2981,9 +3115,11 @@ static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, Unified } if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology(ss->pbvh, mode, - ss->cache->location, - ss->cache->radius); + BKE_pbvh_bmesh_update_topology( + ss->pbvh, mode, + ss->cache->location, + (brush->flag & BRUSH_FRONTFACE) ? ss->cache->view_normal : NULL, + ss->cache->radius); } MEM_freeN(nodes); @@ -3005,11 +3141,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe data.ss = ss; data.sd = sd; data.radius_squared = ss->cache->radius_squared; - data.original = ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER) ? true : ss->cache->original; + data.original = sculpt_tool_needs_original(brush->sculpt_tool) ? 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 */ @@ -3024,7 +3156,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe BKE_pbvh_node_mark_update(nodes[n]); } - if (brush_needs_sculpt_normal(brush)) + if (sculpt_brush_needs_normal(brush)) update_sculpt_normal(sd, ob, nodes, totnode); if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) @@ -3144,8 +3276,8 @@ static void sculpt_combine_proxies(Sculpt *sd, Object *ob) BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); /* first line is tools that don't support proxies */ - if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER) || - ss->cache->supports_gravity) + if (ss->cache->supports_gravity || + (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) { /* these brushes start from original coordinates */ const bool use_orco = ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, @@ -3223,7 +3355,7 @@ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - if (ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER)) { + if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't * propagate needed deformation to original base */ @@ -3484,8 +3616,6 @@ static const char *sculpt_tool_name(Sculpt *sd) 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); @@ -3557,7 +3687,7 @@ static void sculpt_omp_start(Sculpt *sd, SculptSession *ss) if (ss->multires) { int i, gridsize, array_mem_size; BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, - &gridsize, NULL, NULL); + &gridsize, NULL); array_mem_size = cache->num_threads * sizeof(void *); @@ -3745,21 +3875,10 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio /* Make copies of the mesh vertex locations and normals for some tools */ if (brush->flag & BRUSH_ANCHORED) { - if (ss->face_normals) { - cache->face_norms = MEM_mallocN(sizeof(float) * 3 * ss->totpoly, "Sculpt face norms"); - for (i = 0; i < ss->totpoly; ++i) { - copy_v3_v3(cache->face_norms[i], ss->face_normals[i]); - } - } - cache->original = 1; } - 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 (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { if (!(brush->flag & BRUSH_ACCUMULATE)) { cache->original = 1; } @@ -4251,6 +4370,9 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st if (sd->flags & SCULPT_DYNTOPO_DETAIL_CONSTANT) { BKE_pbvh_bmesh_detail_size_set(ss->pbvh, sd->constant_detail / 100.0f); } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + } else { BKE_pbvh_bmesh_detail_size_set( ss->pbvh, @@ -4259,7 +4381,7 @@ static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(st (float)(sd->detail_size * U.pixelsize) / 0.4f); } - if (sculpt_stroke_dynamic_topology(ss, brush)) { + if (sculpt_stroke_is_dynamic_topology(ss, brush)) { do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); } @@ -4746,7 +4868,7 @@ static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, co /* exception for shape keys because we can edit those */ for (; md; md = md->next) { - ModifierTypeInfo *mti = modifierType_getInfo(md->type); + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; if (mti->type == eModifierTypeType_Constructive) { @@ -4838,6 +4960,9 @@ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) sd->symmetrize_direction, 0.00001f); sculpt_dynamic_topology_triangulate(ss->bm); + /* bisect operator flags edges (keep tags clean for edge queue) */ + BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); + /* Finish undo */ BM_log_all_added(ss->bm, ss->bm_log); sculpt_undo_push_end(); @@ -4912,7 +5037,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) /* Leave sculptmode */ ob->mode &= ~mode_flag; - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); paint_cursor_delete_textures(); } @@ -4941,16 +5066,16 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) ts->sculpt->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE; } - if (!ts->sculpt->detail_size) { + if (!ts->sculpt->detail_size) ts->sculpt->detail_size = 12; - } - + if (!ts->sculpt->detail_percent) + ts->sculpt->detail_percent = 25; if (ts->sculpt->constant_detail == 0.0f) ts->sculpt->constant_detail = 30.0f; /* Create sculpt mode session data */ if (ob->sculpt) - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); sculpt_init_session(scene, ob); @@ -5033,7 +5158,10 @@ static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) sculpt_undo_push_begin("Dynamic topology flood fill"); sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology(ss->pbvh, PBVH_Collapse | PBVH_Subdivide, bb_min, size)) { + while (BKE_pbvh_bmesh_update_topology( + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, + bb_min, NULL, size)) + { for (i = 0; i < totnodes; i++) BKE_pbvh_node_mark_topology_update(nodes[i]); } @@ -5177,6 +5305,10 @@ static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail"); } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + set_brush_rc_props(&props_ptr, "sculpt", "constant_detail", NULL, 0); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + } else { set_brush_rc_props(&props_ptr, "sculpt", "detail_size", NULL, 0); RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index a61f571fdf6..8f1a4655c37 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -41,14 +41,8 @@ #include "BKE_pbvh.h" struct bContext; -struct Brush; struct KeyBlock; -struct Mesh; -struct MultiresModifierData; struct Object; -struct Scene; -struct Sculpt; -struct SculptStroke; struct SculptUndoNode; int sculpt_mode_poll(struct bContext *C); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index a4adbc6bca8..2f0957c3b60 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -484,7 +484,7 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) BKE_mesh_calc_normals_tessface(mesh->mvert, mesh->totvert, mesh->mface, mesh->totface, NULL); - BKE_free_sculptsession_deformMats(ss); + BKE_sculptsession_free_deformMats(ss); tag_update |= true; } @@ -581,7 +581,7 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, grid_hidden = BKE_pbvh_grid_hidden(pbvh); BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL); unode->grid_hidden = MEM_mapallocN(sizeof(*unode->grid_hidden) * totgrid, "unode->grid_hidden"); @@ -610,7 +610,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, if (node) { BKE_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + &maxgrid, &gridsize, NULL); unode->totvert = totvert; } @@ -842,7 +842,7 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, if (unode->grids) { int totgrid, *grids; BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - NULL, NULL, NULL, NULL); + NULL, NULL, NULL); memcpy(unode->grids, grids, sizeof(int) * totgrid); } else { diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index a9feb9f48de..e01d8a6bd17 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -315,7 +315,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, sqrtf(dist), radius_root); + strength = alpha * BKE_brush_curve_strength_clamped(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)); @@ -379,7 +379,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, sqrtf(dist), radius_root); + strength = alpha * BKE_brush_curve_strength_clamped(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]; @@ -454,7 +454,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, sqrtf(dist), radius_root); + strength = alpha * BKE_brush_curve_strength_clamped(brush, sqrtf(dist), radius_root); normalize_v2(diff); sculptdata->uv[i].uv[0] -= strength * diff[0] * 0.001f; @@ -612,18 +612,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm if (do_island_optimization) { /* We will need island information */ if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, true); + data->elementMap = BM_uv_element_map_create(bm, false, true, true); } else { - data->elementMap = BM_uv_element_map_create(bm, true, true); + data->elementMap = BM_uv_element_map_create(bm, true, true, true); } } else { if (ts->uv_flag & UV_SYNC_SELECTION) { - data->elementMap = BM_uv_element_map_create(bm, false, false); + data->elementMap = BM_uv_element_map_create(bm, false, true, false); } else { - data->elementMap = BM_uv_element_map_create(bm, true, false); + data->elementMap = BM_uv_element_map_create(bm, true, true, false); } } @@ -822,7 +822,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, sqrtf(dist), radius_root); + strength = alpha * BKE_brush_curve_strength_clamped(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/sound/sound_intern.h b/source/blender/editors/sound/sound_intern.h index e8a8ec55ab5..ace173abdee 100644 --- a/source/blender/editors/sound/sound_intern.h +++ b/source/blender/editors/sound/sound_intern.h @@ -32,7 +32,6 @@ #ifndef __SOUND_INTERN_H__ #define __SOUND_INTERN_H__ -struct wmOperatorType; #endif /* __SOUND_INTERN_H__ */ diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index f3c6781b0fa..8a3b48125d0 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -51,6 +51,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" +#include "BKE_library.h" #include "BKE_packedFile.h" #include "BKE_scene.h" #include "BKE_sound.h" @@ -100,13 +101,14 @@ static int sound_open_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); RNA_string_get(op->ptr, "filepath", path); - sound = sound_new_file(bmain, path); + sound = BKE_sound_new_file(bmain, path); if (!op->customdata) sound_open_init(C, op); - if (sound == NULL || sound->playback_handle == NULL) { + if (sound->playback_handle == NULL) { if (op->customdata) MEM_freeN(op->customdata); + BKE_libblock_free(bmain, sound); BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); return OPERATOR_CANCELLED; } @@ -114,7 +116,7 @@ static int sound_open_exec(bContext *C, wmOperator *op) info = AUD_getInfo(sound->playback_handle); if (info.specs.channels == AUD_CHANNELS_INVALID) { - sound_delete(bmain, sound); + BKE_sound_delete(bmain, sound); if (op->customdata) MEM_freeN(op->customdata); BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); return OPERATOR_CANCELLED; @@ -122,11 +124,11 @@ static int sound_open_exec(bContext *C, wmOperator *op) if (RNA_boolean_get(op->ptr, "mono")) { sound->flags |= SOUND_FLAGS_MONO; - sound_load(bmain, sound); + BKE_sound_load(bmain, sound); } if (RNA_boolean_get(op->ptr, "cache")) { - sound_cache(sound); + BKE_sound_cache(sound); } /* hook into UI */ @@ -690,7 +692,7 @@ static int sound_pack_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; sound->packedfile = newPackedFile(op->reports, sound->name, ID_BLEND_PATH(bmain, &sound->id)); - sound_load(bmain, sound); + BKE_sound_load(bmain, sound); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_action/CMakeLists.txt b/source/blender/editors/space_action/CMakeLists.txt index 50cf84079d2..68e56490217 100644 --- a/source/blender/editors/space_action/CMakeLists.txt +++ b/source/blender/editors/space_action/CMakeLists.txt @@ -36,6 +36,7 @@ set(INC_SYS ) set(SRC + action_data.c action_draw.c action_edit.c action_ops.c diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c new file mode 100644 index 00000000000..6e3e456f67d --- /dev/null +++ b/source/blender/editors/space_action/action_data.c @@ -0,0 +1,908 @@ +/* + * ***** 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. + * + * The Original Code is Copyright (C) 2015 Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_action/action_data.c + * \ingroup spaction + */ + + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <float.h> + + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BLF_translation.h" + +#include "DNA_anim_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_mask_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "BKE_animsys.h" +#include "BKE_action.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_nla.h" +#include "BKE_scene.h" +#include "BKE_context.h" +#include "BKE_report.h" + +#include "UI_view2d.h" + +#include "ED_anim_api.h" +#include "ED_gpencil.h" +#include "ED_keyframing.h" +#include "ED_keyframes_edit.h" +#include "ED_screen.h" +#include "ED_markers.h" +#include "ED_mask.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" + +#include "action_intern.h" + +/* ************************************************************************** */ +/* ACTION CREATION */ + +/* Helper function to find the active AnimData block from the Action Editor context */ +AnimData *ED_actedit_animdata_from_context(bContext *C) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Object *ob = CTX_data_active_object(C); + AnimData *adt = NULL; + + /* Get AnimData block to use */ + if (saction->mode == SACTCONT_ACTION) { + /* Currently, "Action Editor" means object-level only... */ + if (ob) { + adt = ob->adt; + } + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + Key *key = BKE_key_from_object(ob); + if (key) { + adt = key->adt; + } + } + + return adt; +} + +/* -------------------------------------------------------------------- */ + +/* Create new action */ +static bAction *action_create_new(bContext *C, bAction *oldact) +{ + ScrArea *sa = CTX_wm_area(C); + bAction *action; + + /* create action - the way to do this depends on whether we've got an + * existing one there already, in which case we make a copy of it + * (which is useful for "versioning" actions within the same file) + */ + if (oldact && GS(oldact->id.name) == ID_AC) { + /* make a copy of the existing action */ + action = BKE_action_copy(oldact); + } + else { + /* just make a new (empty) action */ + action = add_empty_action(CTX_data_main(C), "Action"); + } + + /* when creating new ID blocks, there is already 1 user (as for all new datablocks), + * but the RNA pointer code will assign all the proper users instead, so we compensate + * for that here + */ + BLI_assert(action->id.us == 1); + action->id.us--; + + /* set ID-Root type */ + if (sa->spacetype == SPACE_ACTION) { + SpaceAction *saction = (SpaceAction *)sa->spacedata.first; + + if (saction->mode == SACTCONT_SHAPEKEY) + action->idroot = ID_KE; + else + action->idroot = ID_OB; + } + + return action; +} + +/* Change the active action used by the action editor */ +static void actedit_change_action(bContext *C, bAction *act) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + + PointerRNA ptr, idptr; + PropertyRNA *prop; + + /* create RNA pointers and get the property */ + RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction, &ptr); + prop = RNA_struct_find_property(&ptr, "action"); + + /* NOTE: act may be NULL here, so better to just use a cast here */ + RNA_id_pointer_create((ID *)act, &idptr); + + /* set the new pointer, and force a refresh */ + RNA_property_pointer_set(&ptr, prop, idptr); + RNA_property_update(C, &ptr, prop); +} + +/* ******************** New Action Operator *********************** */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions... + * OR + * The NLA Editor is active (i.e. Animation Data panel -> new action) + * 2) The associated AnimData block must not be in tweakmode + */ +static int action_new_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ + /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Object *ob = CTX_data_active_object(C); + + /* For now, actions are only for the active object, and on object and shapekey levels... */ + if (saction->mode == SACTCONT_ACTION) { + /* XXX: This assumes that actions are assigned to the active object in this mode */ + if (ob) { + if ((ob->adt == NULL) || (ob->adt->flag & ADT_NLA_EDIT_ON) == 0) + return true; + } + } + else if (saction->mode == SACTCONT_SHAPEKEY) { + Key *key = BKE_key_from_object(ob); + if (key) { + if ((key->adt == NULL) || (key->adt->flag & ADT_NLA_EDIT_ON) == 0) + return true; + } + } + } + else if (ED_operator_nla_active(C)) { + if (!(scene->flag & SCE_NLA_EDIT_ON)) { + return true; + } + } + + /* something failed... */ + return false; +} + +static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) +{ + PointerRNA ptr, idptr; + PropertyRNA *prop; + + /* hook into UI */ + UI_context_active_but_prop_get_templateID(C, &ptr, &prop); + + if (prop) { + bAction *action = NULL, *oldact = NULL; + AnimData *adt = NULL; + PointerRNA oldptr; + + oldptr = RNA_property_pointer_get(&ptr, prop); + oldact = (bAction *)oldptr.id.data; + + /* stash the old action to prevent it from being lost */ + if (ptr.type == &RNA_AnimData) { + adt = ptr.data; + } + else if (ptr.type == &RNA_SpaceDopeSheetEditor) { + adt = ED_actedit_animdata_from_context(C); + } + + /* Perform stashing operation - But only if there is an action */ + if (adt && oldact) { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + /* The stash operation will remove the user already + * (and unlink the action from the AnimData action slot). + * Hence, we must unset the ref to the action in the + * action editor too (if this is where we're being called from) + * first before setting the new action once it is created, + * or else the user gets decremented twice! + */ + if (ptr.type == &RNA_SpaceDopeSheetEditor) { + SpaceAction *saction = (SpaceAction *)ptr.data; + saction->action = NULL; + } + } + else { + //printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n", oldact->id.name); + } + } + + /* create action */ + action = action_create_new(C, oldact); + + /* set this new action + * NOTE: we can't use actedit_change_action, as this function is also called from the NLA + */ + RNA_id_pointer_create(&action->id, &idptr); + RNA_property_pointer_set(&ptr, prop, idptr); + RNA_property_update(C, &ptr, prop); + } + + /* set notifier that keyframes have changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); + + return OPERATOR_FINISHED; +} + +void ACTION_OT_new(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Action"; + ot->idname = "ACTION_OT_new"; + ot->description = "Create new action"; + + /* api callbacks */ + ot->exec = action_new_exec; + ot->poll = action_new_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Action Push-Down Operator ******************** */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions + * 2) There must be an action active + * 3) The associated AnimData block must not be in tweakmode + */ +static int action_pushdown_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check for AnimData, Actions, and that tweakmode is off */ + if (adt && saction->action) { + /* NOTE: We check this for the AnimData block in question and not the global flag, + * as the global flag may be left dirty by some of the browsing ops here. + */ + if (!(adt->flag & ADT_NLA_EDIT_ON)) + return true; + } + } + + /* something failed... */ + return false; +} + +static int action_pushdown_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Do the deed... */ + if (adt) { + /* Perform the pushdown operation + * - This will deal with all the AnimData-side usercounts + */ + if (action_has_motion(adt->action) == 0) { + /* action may not be suitable... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* action can be safely added */ + BKE_nla_action_pushdown(adt); + } + + /* Stop displaying this action in this editor + * NOTE: The editor itself doesn't set a user... + */ + saction->action = NULL; + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_push_down(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Push Down Action"; + ot->idname = "ACTION_OT_push_down"; + ot->description = "Push action down on to the NLA stack as a new strip"; + + /* callbacks */ + ot->exec = action_pushdown_exec; + ot->poll = action_pushdown_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ******************* Action Stash Operator ******************** */ + +static int action_stash_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Perform stashing operation */ + if (adt) { + /* don't do anything if this action is empty... */ + if (action_has_motion(adt->action) == 0) { + /* action may not be suitable... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + /* The stash operation will remove the user already, + * so the flushing step later shouldn't double up + * the usercount fixes. Hence, we must unset this ref + * first before setting the new action. + */ + saction->action = NULL; + } + else { + /* action has already been added - simply warn about this, and clear */ + BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); + } + + /* clear action refs from editor, and then also the backing data (not necessary) */ + actedit_change_action(C, NULL); + } + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_stash(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stash Action"; + ot->idname = "ACTION_OT_stash"; + ot->description = "Store this action in the NLA stack as a non-contributing strip for later use"; + + /* callbacks */ + ot->exec = action_stash_exec; + ot->poll = action_pushdown_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_boolean(ot->srna, "create_new", true, "Create New Action", + "Create a new action once the existing one has been safely stored"); +} + +/* ----------------- */ + +/* Criteria: + * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions + * 2) The associated AnimData block must not be in tweakmode + */ +static int action_stash_create_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ + /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ + if (adt) { + if (!(adt->flag & ADT_NLA_EDIT_ON)) + return true; + } + else { + /* There may not be any action/animdata yet, so, just fallback to the global setting + * (which may not be totally valid yet if the action editor was used and things are + * now in an inconsistent state) + */ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + Scene *scene = CTX_data_scene(C); + + if (!(scene->flag & SCE_NLA_EDIT_ON)) { + /* For now, actions are only for the active object, and on object and shapekey levels... */ + return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY); + } + } + } + + /* something failed... */ + return false; +} + +static int action_stash_create_exec(bContext *C, wmOperator *op) +{ + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Check for no action... */ + if (saction->action == NULL) { + /* just create a new action */ + bAction *action = action_create_new(C, NULL); + actedit_change_action(C, action); + } + else if (adt) { + /* Perform stashing operation */ + if (action_has_motion(adt->action) == 0) { + /* don't do anything if this action is empty... */ + BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); + return OPERATOR_CANCELLED; + } + else { + /* stash the action */ + if (BKE_nla_action_stash(adt)) { + bAction *new_action = NULL; + + /* create new action not based on the old one (since the "new" operator already does that) */ + new_action = action_create_new(C, NULL); + + /* The stash operation will remove the user already, + * so the flushing step later shouldn't double up + * the usercount fixes. Hence, we must unset this ref + * first before setting the new action. + */ + saction->action = NULL; + actedit_change_action(C, new_action); + } + else { + /* action has already been added - simply warn about this, and clear */ + BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); + actedit_change_action(C, NULL); + } + } + } + + /* Send notifiers that stuff has changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); + return OPERATOR_FINISHED; +} + +void ACTION_OT_stash_and_create(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Stash Action"; + ot->idname = "ACTION_OT_stash_and_create"; + ot->description = "Store this action in the NLA stack as a non-contributing strip for later use, and create a new action"; + + /* callbacks */ + ot->exec = action_stash_create_exec; + ot->poll = action_stash_create_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************************************** */ +/* ACTION UNLINK */ + +/* ******************* Action Unlink Operator ******************** */ +/* We use a custom unlink operator here, as there are some technicalities which need special care: + * 1) When in Tweak Mode, it shouldn't be possible to unlink the active action, + * or else, everything turns to custard. + * 2) If the Action doesn't have any other users, the user should at least get + * a warning that it is going to get lost. + * 3) We need a convenient way to exit Tweak Mode from the Action Editor + */ + +void ED_animedit_unlink_action(bContext *C, ID *id, AnimData *adt, bAction *act, ReportList *reports) +{ + ScrArea *sa = CTX_wm_area(C); + + /* If the old action only has a single user (that it's about to lose), + * warn user about it + * + * TODO: Maybe we should just save it for them? But then, there's the problem of + * trying to get rid of stuff that's actually unwanted! + */ + if (act->id.us == 1) { + BKE_reportf(reports, RPT_WARNING, + "Action '%s' will not be saved, create Fake User or Stash in NLA Stack to retain", + act->id.name + 2); + } + + /* If in Tweak Mode, don't unlink. Instead, this + * becomes a shortcut to exit Tweak Mode instead + */ + if ((adt) && (adt->flag & ADT_NLA_EDIT_ON)) { + /* Exit Tweak Mode */ + BKE_nla_tweakmode_exit(adt); + + /* Flush this to the Action Editor (if that's where this change was initiated) */ + if (sa->spacetype == SPACE_ACTION) { + actedit_change_action(C, NULL); + } + } + else { + /* Unlink normally - Setting it to NULL should be enough to get the old one unlinked */ + if (sa->spacetype == SPACE_ACTION) { + /* clear action editor -> action */ + actedit_change_action(C, NULL); + } + else { + /* clear AnimData -> action */ + PointerRNA ptr; + PropertyRNA *prop; + + /* create AnimData RNA pointers */ + RNA_pointer_create(id, &RNA_AnimData, adt, &ptr); + prop = RNA_struct_find_property(&ptr, "action"); + + /* clear... */ + RNA_property_pointer_set(&ptr, prop, PointerRNA_NULL); + RNA_property_update(C, &ptr, prop); + } + } +} + +/* -------------------------- */ + +static int action_unlink_poll(bContext *C) +{ + if (ED_operator_action_active(C)) { + SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); + AnimData *adt = ED_actedit_animdata_from_context(C); + + /* Only when there's an active action, in the right modes... */ + if (saction->action && adt) + return true; + } + + /* something failed... */ + return false; +} + +static int action_unlink_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + + if (adt && adt->action) { + ED_animedit_unlink_action(C, NULL, adt, adt->action, op->reports); + } + + return OPERATOR_FINISHED; +} + +void ACTION_OT_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink Action"; + ot->idname = "ACTION_OT_unlink"; + ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)"; + + /* callbacks */ + ot->exec = action_unlink_exec; + ot->poll = action_unlink_poll; +} + +/* ************************************************************************** */ +/* ACTION BROWSING */ + +/* Try to find NLA Strip to use for action layer up/down tool */ +static NlaStrip *action_layer_get_nlastrip(ListBase *strips, float ctime) +{ + NlaStrip *strip; + + for (strip = strips->first; strip; strip = strip->next) { + /* Can we use this? */ + if (IN_RANGE_INCL(ctime, strip->start, strip->end)) { + /* in range - use this one */ + return strip; + } + else if ((ctime < strip->start) && (strip->prev == NULL)) { + /* before first - use this one */ + return strip; + } + else if ((ctime > strip->end) && (strip->next == NULL)) { + /* after last - use this one */ + return strip; + } + } + + /* nothing suitable found... */ + return NULL; +} + +/* Switch NLA Strips/Actions */ +static void action_layer_switch_strip(AnimData *adt, + NlaTrack *old_track, NlaStrip *old_strip, + NlaTrack *nlt, NlaStrip *strip) +{ + /* Exit tweakmode on old strip + * NOTE: We need to manually clear this stuff ourselves, as tweakmode exit doesn't do it + */ + BKE_nla_tweakmode_exit(adt); + + if (old_strip) { + old_strip->flag &= ~(NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT); + } + if (old_track) { + old_track->flag &= ~(NLATRACK_ACTIVE | NLATRACK_SELECTED); + } + + /* Make this one the active one instead */ + strip->flag |= (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT); + nlt->flag |= NLATRACK_ACTIVE; + + /* Copy over "solo" flag - This is useful for stashed actions... */ + if (old_track) { + if (old_track->flag & NLATRACK_SOLO) { + old_track->flag &= ~NLATRACK_SOLO; + nlt->flag |= NLATRACK_SOLO; + } + } + else { + /* NLA muting <==> Solo Tracks */ + if (adt->flag & ADT_NLA_EVAL_OFF) { + /* disable NLA muting */ + adt->flag &= ~ADT_NLA_EVAL_OFF; + + /* mark this track as being solo */ + adt->flag |= ADT_NLA_SOLO_TRACK; + nlt->flag |= NLATRACK_SOLO; + + // TODO: Needs restpose flushing (when we get reference track) + } + } + + /* Enter tweakmode again - hopefully we're now "it" */ + BKE_nla_tweakmode_enter(adt); + BLI_assert(adt->actstrip == strip); +} + +/* ********************** One Layer Up Operator ************************** */ + +static int action_layer_next_poll(bContext *C) +{ + /* Action Editor's action editing modes only */ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + if (adt) { + /* only allow if we're in tweakmode, and there's something above us... */ + if (adt->flag & ADT_NLA_EDIT_ON) { + /* We need to check if there are any tracks above the active one + * since the track the action comes from is not stored in AnimData + */ + if (adt->nla_tracks.last) { + NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.last; + + if (nlt->flag & NLATRACK_DISABLED) { + /* A disabled track will either be the track itself, + * or one of the ones above it. + * + * If this is the top-most one, there is the possibility + * that there is no active action. For now, we let this + * case return true too, so that there is a natural way + * to "move to an empty layer", even though this means + * that we won't actually have an action. + */ + // return (adt->tmpact != NULL); + return true; + } + } + } + } + } + + /* something failed... */ + return false; +} + +static int action_layer_next_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + NlaTrack *act_track; + + Scene *scene = CTX_data_scene(C); + float ctime = BKE_scene_frame_get(scene); + + /* Get active track */ + act_track = BKE_nlatrack_find_tweaked(adt); + + if (act_track == NULL) { + BKE_report(op->reports, RPT_ERROR, "Could not find current NLA Track"); + return OPERATOR_CANCELLED; + } + + /* Find next action, and hook it up */ + if (act_track->next) { + NlaTrack *nlt; + + /* Find next action to use */ + for (nlt = act_track->next; nlt; nlt = nlt->next) { + NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime); + + if (strip) { + action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip); + break; + } + } + } + else { + /* No more actions (strips) - Go back to editing the original active action + * NOTE: This will mean exiting tweakmode... + */ + BKE_nla_tweakmode_exit(adt); + + /* Deal with solo flags... + * Assume: Solo Track == NLA Muting + */ + if (adt->flag & ADT_NLA_SOLO_TRACK) { + /* turn off solo flags on tracks */ + act_track->flag &= ~NLATRACK_SOLO; + adt->flag &= ~ADT_NLA_SOLO_TRACK; + + /* turn on NLA muting (to keep same effect) */ + adt->flag |= ADT_NLA_EVAL_OFF; + + // TODO: Needs restpose flushing (when we get reference track) + } + } + + /* Update the action that this editor now uses + * NOTE: The calls above have already handled the usercount/animdata side of things + */ + actedit_change_action(C, adt->action); + return OPERATOR_FINISHED; +} + +void ACTION_OT_layer_next(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Next Layer"; + ot->idname = "ACTION_OT_layer_next"; + ot->description = "Switch to editing action in animation layer above the current action in the NLA Stack"; + + /* callbacks */ + ot->exec = action_layer_next_exec; + ot->poll = action_layer_next_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************* One Layer Down Operator ************************* */ + +static int action_layer_prev_poll(bContext *C) +{ + /* Action Editor's action editing modes only */ + if (ED_operator_action_active(C)) { + AnimData *adt = ED_actedit_animdata_from_context(C); + if (adt) { + if (adt->flag & ADT_NLA_EDIT_ON) { + /* Tweak Mode: We need to check if there are any tracks below the active one that we can move to */ + if (adt->nla_tracks.first) { + NlaTrack *nlt = (NlaTrack *)adt->nla_tracks.first; + + /* Since the first disabled track is the track being tweaked/edited, + * we can simplify things by only checking the first track: + * - If it is disabled, this is the track being tweaked, + * so there can't be anything below it + * - Otherwise, there is at least 1 track below the tweaking + * track that we can descend to + */ + if ((nlt->flag & NLATRACK_DISABLED) == 0) { + /* not disabled = there are actions below the one being tweaked */ + return true; + } + } + } + else { + /* Normal Mode: If there are any tracks, we can try moving to those */ + return (adt->nla_tracks.first != NULL); + } + } + } + + /* something failed... */ + return false; +} + +static int action_layer_prev_exec(bContext *C, wmOperator *op) +{ + AnimData *adt = ED_actedit_animdata_from_context(C); + NlaTrack *act_track; + NlaTrack *nlt; + + Scene *scene = CTX_data_scene(C); + float ctime = BKE_scene_frame_get(scene); + + /* Sanity Check */ + if (adt == NULL) { + BKE_report(op->reports, RPT_ERROR, "Internal Error: Could not find Animation Data/NLA Stack to use"); + return OPERATOR_CANCELLED; + } + + /* Get active track */ + act_track = BKE_nlatrack_find_tweaked(adt); + + /* If there is no active track, that means we are using the active action... */ + if (act_track) { + /* Active Track - Start from the one below it */ + nlt = act_track->prev; + } + else { + /* Active Action - Use the top-most track */ + nlt = adt->nla_tracks.last; + } + + /* Find previous action and hook it up */ + for (; nlt; nlt = nlt->prev) { + NlaStrip *strip = action_layer_get_nlastrip(&nlt->strips, ctime); + + if (strip) { + action_layer_switch_strip(adt, act_track, adt->actstrip, nlt, strip); + break; + } + } + + /* Update the action that this editor now uses + * NOTE: The calls above have already handled the usercount/animdata side of things + */ + actedit_change_action(C, adt->action); + return OPERATOR_FINISHED; +} + +void ACTION_OT_layer_prev(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Previous Layer"; + ot->idname = "ACTION_OT_layer_prev"; + ot->description = "Switch to editing action in animation layer below the current action in the NLA Stack"; + + /* callbacks */ + ot->exec = action_layer_prev_exec; + ot->poll = action_layer_prev_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ************************************************************************** */ diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index a17cfa4d87c..10748c2fe15 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -93,6 +93,8 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)ACHANNEL_FIRST; for (ale = anim_data.first; ale; ale = ale->next) { @@ -104,11 +106,12 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP; + channel_index++; } } { /* second pass: widgets */ @@ -210,7 +213,7 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar) if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) || IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); int sel = 0; /* determine if any need to draw channel */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 76c955d8bca..90c38426164 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -80,418 +80,6 @@ #include "action_intern.h" -/* ************************************************************************** */ -/* ACTION MANAGEMENT */ - -/* Helper function to find the active AnimData block from the Action Editor context */ -static AnimData *actedit_animdata_from_context(bContext *C) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Object *ob = CTX_data_active_object(C); - AnimData *adt = NULL; - - /* Get AnimData block to use */ - if (saction->mode == SACTCONT_ACTION) { - /* Currently, "Action Editor" means object-level only... */ - adt = ob->adt; - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - adt = key->adt; - } - - return adt; -} - -/* Create new action */ -static bAction *action_create_new(bContext *C, bAction *oldact) -{ - ScrArea *sa = CTX_wm_area(C); - bAction *action; - - /* create action - the way to do this depends on whether we've got an - * existing one there already, in which case we make a copy of it - * (which is useful for "versioning" actions within the same file) - */ - if (oldact && GS(oldact->id.name) == ID_AC) { - /* make a copy of the existing action */ - action = BKE_action_copy(oldact); - } - else { - /* just make a new (empty) action */ - action = add_empty_action(CTX_data_main(C), "Action"); - } - - /* when creating new ID blocks, there is already 1 user (as for all new datablocks), - * but the RNA pointer code will assign all the proper users instead, so we compensate - * for that here - */ - BLI_assert(action->id.us == 1); - action->id.us--; - - /* set ID-Root type */ - if (sa->spacetype == SPACE_ACTION) { - SpaceAction *saction = (SpaceAction *)sa->spacedata.first; - - if (saction->mode == SACTCONT_SHAPEKEY) - action->idroot = ID_KE; - else - action->idroot = ID_OB; - } - - return action; -} - -/* Change the active action used by the action editor */ -static void actedit_change_action(bContext *C, bAction *act) -{ - bScreen *screen = CTX_wm_screen(C); - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - - PointerRNA ptr, idptr; - PropertyRNA *prop; - - /* create RNA pointers and get the property */ - RNA_pointer_create(&screen->id, &RNA_SpaceDopeSheetEditor, saction, &ptr); - prop = RNA_struct_find_property(&ptr, "action"); - - /* NOTE: act may be NULL here, so better to just use a cast here */ - RNA_id_pointer_create((ID *)act, &idptr); - - /* set the new pointer, and force a refresh */ - RNA_property_pointer_set(&ptr, prop, idptr); - RNA_property_update(C, &ptr, prop); -} - -/* ******************** New Action Operator *********************** */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions... - * OR - * The NLA Editor is active (i.e. Animation Data panel -> new action) - * 2) The associated AnimData block must not be in tweakmode - */ -static int action_new_poll(bContext *C) -{ - Scene *scene = CTX_data_scene(C); - - /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ - /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ - if (!(scene->flag & SCE_NLA_EDIT_ON)) { - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - - /* For now, actions are only for the active object, and on object and shapekey levels... */ - return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY); - } - else if (ED_operator_nla_active(C)) { - return true; - } - } - - /* something failed... */ - return false; -} - -static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) -{ - PointerRNA ptr, idptr; - PropertyRNA *prop; - - /* hook into UI */ - UI_context_active_but_prop_get_templateID(C, &ptr, &prop); - - if (prop) { - bAction *action = NULL, *oldact = NULL; - AnimData *adt = NULL; - PointerRNA oldptr; - - oldptr = RNA_property_pointer_get(&ptr, prop); - oldact = (bAction *)oldptr.id.data; - - /* stash the old action to prevent it from being lost */ - if (ptr.type == &RNA_AnimData) { - adt = ptr.data; - } - else if (ptr.type == &RNA_SpaceDopeSheetEditor) { - adt = actedit_animdata_from_context(C); - } - - /* Perform stashing operation - But only if there is an action */ - if (adt && oldact) { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - /* The stash operation will remove the user already - * (and unlink the action from the AnimData action slot). - * Hence, we must unset the ref to the action in the - * action editor too (if this is where we're being called from) - * first before setting the new action once it is created, - * or else the user gets decremented twice! - */ - if (ptr.type == &RNA_SpaceDopeSheetEditor) { - SpaceAction *saction = (SpaceAction *)ptr.data; - saction->action = NULL; - } - } - else { - //printf("WARNING: Failed to stash %s. It may already exist in the NLA stack though\n", oldact->id.name); - } - } - - /* create action */ - action = action_create_new(C, oldact); - - /* set this new action - * NOTE: we can't use actedit_change_action, as this function is also called from the NLA - */ - RNA_id_pointer_create(&action->id, &idptr); - RNA_property_pointer_set(&ptr, prop, idptr); - RNA_property_update(C, &ptr, prop); - } - - /* set notifier that keyframes have changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); - - return OPERATOR_FINISHED; -} - -void ACTION_OT_new(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "New Action"; - ot->idname = "ACTION_OT_new"; - ot->description = "Create new action"; - - /* api callbacks */ - ot->exec = action_new_exec; - ot->poll = action_new_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* Action Push-Down Operator ******************** */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions - * 2) There must be an action active - * 3) The associated AnimData block must not be in tweakmode - */ -static int action_pushdown_poll(bContext *C) -{ - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - - /* Check for actions and that tweakmode is off */ - if ((saction->action) && !(scene->flag & SCE_NLA_EDIT_ON)) { - /* For now, actions are only for the active object, and on object and shapekey levels... */ - if (saction->mode == SACTCONT_ACTION) { - return (ob->adt != NULL); - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - - return (key && key->adt); - } - } - } - - /* something failed... */ - return false; -} - -static int action_pushdown_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Do the deed... */ - if (adt) { - /* Perform the pushdown operation - * - This will deal with all the AnimData-side usercounts - */ - if (action_has_motion(adt->action) == 0) { - /* action may not be suitable... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* action can be safely added */ - BKE_nla_action_pushdown(adt); - } - - /* Stop displaying this action in this editor - * NOTE: The editor itself doesn't set a user... - */ - saction->action = NULL; - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_push_down(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Push Down Action"; - ot->idname = "ACTION_OT_push_down"; - ot->description = "Push action down on to the NLA stack as a new strip"; - - /* callbacks */ - ot->exec = action_pushdown_exec; - ot->poll = action_pushdown_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/* ******************* Action Stash Operator ******************** */ - -static int action_stash_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Perform stashing operation */ - if (adt) { - /* don't do anything if this action is empty... */ - if (action_has_motion(adt->action) == 0) { - /* action may not be suitable... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - /* The stash operation will remove the user already, - * so the flushing step later shouldn't double up - * the usercount fixes. Hence, we must unset this ref - * first before setting the new action. - */ - saction->action = NULL; - } - else { - /* action has already been added - simply warn about this, and clear */ - BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); - } - - /* clear action refs from editor, and then also the backing data (not necessary) */ - actedit_change_action(C, NULL); - } - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_stash(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Stash Action"; - ot->idname = "ACTION_OT_stash"; - ot->description = "Store this action in the NLA stack as a non-contributing strip for later use"; - - /* callbacks */ - ot->exec = action_stash_exec; - ot->poll = action_pushdown_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_boolean(ot->srna, "create_new", true, "Create New Action", - "Create a new action once the existing one has been safely stored"); -} - -/* ----------------- */ - -/* Criteria: - * 1) There must be an dopesheet/action editor, and it must be in a mode which uses actions - * 2) The associated AnimData block must not be in tweakmode - */ -static int action_stash_create_poll(bContext *C) -{ - if (ED_operator_action_active(C)) { - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - Scene *scene = CTX_data_scene(C); - - /* Check tweakmode is off (as you don't want to be tampering with the action in that case) */ - /* NOTE: unlike for pushdown, this operator needs to be run when creating an action from nothing... */ - if (!(scene->flag & SCE_NLA_EDIT_ON)) { - /* For now, actions are only for the active object, and on object and shapekey levels... */ - return ELEM(saction->mode, SACTCONT_ACTION, SACTCONT_SHAPEKEY); - } - } - - /* something failed... */ - return false; -} - -static int action_stash_create_exec(bContext *C, wmOperator *op) -{ - SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = actedit_animdata_from_context(C); - - /* Check for no action... */ - if (saction->action == NULL) { - /* just create a new action */ - bAction *action = action_create_new(C, NULL); - actedit_change_action(C, action); - } - else if (adt) { - /* Perform stashing operation */ - if (action_has_motion(adt->action) == 0) { - /* don't do anything if this action is empty... */ - BKE_report(op->reports, RPT_WARNING, "Action must have at least one keyframe or F-Modifier"); - return OPERATOR_CANCELLED; - } - else { - /* stash the action */ - if (BKE_nla_action_stash(adt)) { - bAction *new_action = NULL; - - /* create new action not based on the old one (since the "new" operator already does that) */ - new_action = action_create_new(C, NULL); - - /* The stash operation will remove the user already, - * so the flushing step later shouldn't double up - * the usercount fixes. Hence, we must unset this ref - * first before setting the new action. - */ - saction->action = NULL; - actedit_change_action(C, new_action); - } - else { - /* action has already been added - simply warn about this, and clear */ - BKE_report(op->reports, RPT_ERROR, "Action has already been stashed"); - actedit_change_action(C, NULL); - } - } - } - - /* Send notifiers that stuff has changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_NLA_ACTCHANGE, NULL); - return OPERATOR_FINISHED; -} - -void ACTION_OT_stash_and_create(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Stash Action"; - ot->idname = "ACTION_OT_stash_and_create"; - ot->description = "Store this action in the NLA stack as a non-contributing strip for later use, and create a new action"; - - /* callbacks */ - ot->exec = action_stash_create_exec; - ot->poll = action_stash_create_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} /* ************************************************************************** */ /* POSE MARKERS STUFF */ @@ -738,7 +326,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min, y = (float)ACHANNEL_FIRST; for (ale = anim_data.first; ale; ale = ale->next) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); /* must be selected... */ if (acf && acf->has_setting(ac, ale, ACHANNEL_SETTING_SELECT) && @@ -837,7 +425,15 @@ static int actkeys_viewsel_exec(bContext *C, wmOperator *UNUSED(op)) /* only selected */ return actkeys_viewall(C, true); } - + +static int actkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + + return OPERATOR_FINISHED; +} + void ACTION_OT_view_all(wmOperatorType *ot) { /* identifiers */ @@ -868,6 +464,21 @@ void ACTION_OT_view_selected(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +void ACTION_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "ACTION_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = actkeys_view_frame_exec; + ot->poll = ED_operator_action_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ************************************************************************** */ /* GENERAL STUFF */ @@ -1068,8 +679,13 @@ static void insert_action_keys(bAnimContext *ac, short mode) else cfra = (float)CFRA; - /* if there's an id */ - if (ale->id) + /* read value from property the F-Curve represents, or from the curve only? + * - ale->id != NULL: Typically, this means that we have enough info to try resolving the path + * - ale->owner != NULL: If this is set, then the path may not be resolvable from the ID alone, + * so it's easier for now to just read the F-Curve directly. + * (TODO: add the full-blown PointerRNA relative parsing case here...) + */ + if (ale->id && !ale->owner) 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); @@ -1142,7 +758,7 @@ static void duplicate_action_keys(bAnimContext *ac) /* loop through filtered data and delete selected keys */ for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_FCURVE) + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) duplicate_fcurve_keys((FCurve *)ale->key_data); else if (ale->type == ANIMTYPE_GPLAYER) ED_gplayer_frames_duplicate((bGPDlayer *)ale->data); @@ -1176,13 +792,6 @@ static int actkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } - -static int actkeys_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - actkeys_duplicate_exec(C, op); - - return OPERATOR_FINISHED; -} void ACTION_OT_duplicate(wmOperatorType *ot) { @@ -1192,7 +801,6 @@ void ACTION_OT_duplicate(wmOperatorType *ot) ot->description = "Make a copy of all selected keyframes"; /* api callbacks */ - ot->invoke = actkeys_duplicate_invoke; ot->exec = actkeys_duplicate_exec; ot->poll = ED_operator_action_active; diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 61d4249ae20..17f1f404225 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -35,10 +35,7 @@ struct bContext; struct bAnimContext; struct SpaceAction; struct ARegion; -struct wmWindowManager; struct wmOperatorType; -struct ActKeysInc; -struct bAnimListElem; /* internal exports only */ @@ -80,6 +77,7 @@ enum eActKeys_ColumnSelect_Mode { void ACTION_OT_previewrange_set(struct wmOperatorType *ot); void ACTION_OT_view_all(struct wmOperatorType *ot); void ACTION_OT_view_selected(struct wmOperatorType *ot); +void ACTION_OT_view_frame(struct wmOperatorType *ot); void ACTION_OT_copy(struct wmOperatorType *ot); void ACTION_OT_paste(struct wmOperatorType *ot); @@ -101,10 +99,15 @@ void ACTION_OT_snap(struct wmOperatorType *ot); void ACTION_OT_mirror(struct wmOperatorType *ot); void ACTION_OT_new(struct wmOperatorType *ot); +void ACTION_OT_unlink(struct wmOperatorType *ot); + void ACTION_OT_push_down(struct wmOperatorType *ot); void ACTION_OT_stash(struct wmOperatorType *ot); void ACTION_OT_stash_and_create(struct wmOperatorType *ot); +void ACTION_OT_layer_next(struct wmOperatorType *ot); +void ACTION_OT_layer_prev(struct wmOperatorType *ot); + void ACTION_OT_markers_make_local(struct wmOperatorType *ot); /* defines for snap keyframes diff --git a/source/blender/editors/space_action/action_ops.c b/source/blender/editors/space_action/action_ops.c index 8c706d8da57..59b147c6f6c 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -79,14 +79,20 @@ void action_operatortypes(void) WM_operatortype_append(ACTION_OT_paste); WM_operatortype_append(ACTION_OT_new); + WM_operatortype_append(ACTION_OT_unlink); + WM_operatortype_append(ACTION_OT_push_down); WM_operatortype_append(ACTION_OT_stash); WM_operatortype_append(ACTION_OT_stash_and_create); + WM_operatortype_append(ACTION_OT_layer_next); + WM_operatortype_append(ACTION_OT_layer_prev); + WM_operatortype_append(ACTION_OT_previewrange_set); WM_operatortype_append(ACTION_OT_view_all); WM_operatortype_append(ACTION_OT_view_selected); - + WM_operatortype_append(ACTION_OT_view_frame); + WM_operatortype_append(ACTION_OT_markers_make_local); } @@ -101,6 +107,7 @@ void ED_operatormacros_action(void) WM_operatortype_macro_define(ot, "ACTION_OT_duplicate"); otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform"); RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE); + RNA_enum_set(otmacro->ptr, "proportional", PROP_EDIT_OFF); } /* ************************** registration - keymaps **********************************/ @@ -197,11 +204,10 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "ACTION_OT_keyframe_type", RKEY, KM_PRESS, 0, 0); /* destructive */ - WM_keymap_add_item(keymap, "ACTION_OT_clean", OKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACTION_OT_sample", OKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_add_item(keymap, "ACTION_OT_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "ACTION_OT_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "DOPESHEET_MT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "DOPESHEET_MT_delete", DELKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACTION_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "ACTION_OT_keyframe_insert", IKEY, KM_PRESS, 0, 0); @@ -223,6 +229,8 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "ACTION_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACTION_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "ACTION_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "ACTION_OT_view_frame", PAD0, KM_PRESS, 0, 0); + /* animation module */ /* channels list @@ -236,6 +244,9 @@ static void action_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* transform system */ transform_keymap_for_space(keyconf, keymap, SPACE_ACTION); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", OKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_proportional_action"); + /* special markers hotkeys for anim editors: see note in definition of this function */ ED_marker_keymap_animedit_conflictfree(keymap); } diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index ddfca98a119..0f94baff082 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -1221,11 +1221,11 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ agrp->flag |= AGRP_SELECTED; ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, agrp, ANIMTYPE_GROUP); } - else if (ale->type == ANIMTYPE_FCURVE) { + else if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) { FCurve *fcu = ale->data; fcu->flag |= FCURVE_SELECTED; - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, fcu, ale->type); } } } diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 73091e7f261..bc42f1dd5bb 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -59,6 +59,7 @@ #include "RNA_access.h" +#include "ED_buttons.h" #include "ED_armature.h" #include "ED_screen.h" #include "ED_physics.h" @@ -1179,3 +1180,33 @@ ID *buttons_context_id_path(const bContext *C) return NULL; } + +void ED_buttons_id_unref(SpaceButs *sbuts, const ID *id) +{ + if (sbuts->pinid == id) { + sbuts->pinid = NULL; + sbuts->flag &= ~SB_PIN_CONTEXT; + } + + if (sbuts->path) { + ButsContextPath *path = sbuts->path; + int i; + + for (i = 0; i < path->len; i++) { + if (path->ptr[i].id.data == id) { + break; + } + } + + if (i == path->len) { + /* pass */ + } + else if (i == 0) { + MEM_SAFE_FREE(sbuts->path); + } + else { + memset(&path->ptr[i], 0, sizeof(path->ptr[i]) * (path->len - i)); + path->len = i; + } + } +} diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index f294729ae97..7fc35a6b1e7 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -34,7 +34,6 @@ #include "DNA_listBase.h" #include "RNA_types.h" -struct ARegion; struct ARegionType; struct ID; struct SpaceButs; diff --git a/source/blender/editors/space_buttons/buttons_ops.c b/source/blender/editors/space_buttons/buttons_ops.c index ba0c22a4ade..8ad4858ff22 100644 --- a/source/blender/editors/space_buttons/buttons_ops.c +++ b/source/blender/editors/space_buttons/buttons_ops.c @@ -97,6 +97,7 @@ void BUTTONS_OT_toolbox(wmOperatorType *ot) typedef struct FileBrowseOp { PointerRNA ptr; PropertyRNA *prop; + bool is_undo; } FileBrowseOp; static int file_browse_exec(bContext *C, wmOperator *op) @@ -142,6 +143,10 @@ static int file_browse_exec(bContext *C, wmOperator *op) RNA_property_update(C, &fbo->ptr, fbo->prop); MEM_freeN(str); + if (fbo->is_undo) { + const char *undostr = RNA_property_identifier(fbo->prop); + ED_undo_push(C, undostr); + } /* special, annoying exception, filesel on redo panel [#26618] */ { @@ -168,6 +173,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) { PointerRNA ptr; PropertyRNA *prop; + bool is_undo; FileBrowseOp *fbo; char *str; @@ -176,7 +182,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - UI_context_active_but_prop_get_filebrowser(C, &ptr, &prop); + UI_context_active_but_prop_get_filebrowser(C, &ptr, &prop, &is_undo); if (!prop) return OPERATOR_CANCELLED; @@ -210,6 +216,7 @@ static int file_browse_invoke(bContext *C, wmOperator *op, const wmEvent *event) fbo = MEM_callocN(sizeof(FileBrowseOp), "FileBrowseOp"); fbo->ptr = ptr; fbo->prop = prop; + fbo->is_undo = is_undo; op->customdata = fbo; RNA_string_set(op->ptr, path_prop, str); @@ -241,7 +248,8 @@ void BUTTONS_OT_file_browse(wmOperatorType *ot) ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; - ot->flag |= OPTYPE_UNDO; + /* conditional undo based on button flag */ + ot->flag = 0; /* properties */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, @@ -261,7 +269,8 @@ void BUTTONS_OT_directory_browse(wmOperatorType *ot) ot->exec = file_browse_exec; ot->cancel = file_browse_cancel; - ot->flag |= OPTYPE_UNDO; + /* conditional undo based on button flag */ + ot->flag = 0; /* properties */ WM_operator_properties_filesel(ot, 0, FILE_SPECIAL, FILE_OPENFILE, diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index 6c55d8d034e..76954ede522 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -43,6 +43,7 @@ #include "BLI_math.h" #include "BLI_string.h" #include "BLI_math_base.h" +#include "BLI_rect.h" #include "BKE_context.h" #include "BKE_image.h" @@ -285,6 +286,7 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar, MovieClip *clip = ED_space_clip_get_clip(sc); int filter = GL_LINEAR; int x, y; + rctf frame; /* find window pixel coordinates of origin */ UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x, &y); @@ -308,10 +310,14 @@ static void draw_movieclip_buffer(const bContext *C, SpaceClip *sc, ARegion *ar, glPixelZoom(zoomx * width / ibuf->x, zoomy * height / ibuf->y); glaDrawImBuf_glsl_ctx(C, ibuf, x, y, filter); - /* reset zoom */ glPixelZoom(1.0f, 1.0f); + BLI_rctf_init(&frame, 0.0f, ibuf->x, 0.0f, ibuf->y); + + if (sc->flag & SC_SHOW_METADATA) + ED_region_image_metadata_draw(x, y, ibuf, frame, zoomx * width / ibuf->x, zoomy * height / ibuf->y); + if (ibuf->planes == 32) glDisable(GL_BLEND); } @@ -1123,7 +1129,9 @@ static void draw_plane_marker_ex(SpaceClip *sc, Scene *scene, MovieTrackingPlane { bool tiny = (sc->flag & SC_SHOW_TINY_MARKER) != 0; bool is_selected_track = (plane_track->flag & SELECT) != 0; - bool draw_plane_quad = plane_track->image == NULL || plane_track->image_opacity == 0.0f; + const bool has_image = plane_track->image != NULL && + BKE_image_has_ibuf(plane_track->image, NULL); + const bool draw_plane_quad = !has_image || plane_track->image_opacity == 0.0f; float px[2]; if (draw_outline) { @@ -1692,29 +1700,29 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar) } if (sc->flag & SC_SHOW_STABLE) { + float translation[2]; + float aspect = clip->tracking.camera.pixel_aspect; float smat[4][4], ismat[4][4]; - ibuf = ED_space_clip_get_stable_buffer(sc, sc->loc, &sc->scale, &sc->angle); - - if (ibuf) { - float translation[2]; - float aspect = clip->tracking.camera.pixel_aspect; + if ((sc->flag & SC_MUTE_FOOTAGE) == 0) { + ibuf = ED_space_clip_get_stable_buffer(sc, sc->loc, + &sc->scale, &sc->angle); + } - if (width != ibuf->x) - mul_v2_v2fl(translation, sc->loc, (float)width / ibuf->x); - else - copy_v2_v2(translation, sc->loc); + if (ibuf != NULL && width != ibuf->x) + mul_v2_v2fl(translation, sc->loc, (float)width / ibuf->x); + else + copy_v2_v2(translation, sc->loc); - BKE_tracking_stabilization_data_to_mat4(width, height, aspect, - translation, sc->scale, sc->angle, sc->stabmat); + BKE_tracking_stabilization_data_to_mat4(width, height, aspect, translation, + sc->scale, sc->angle, sc->stabmat); - unit_m4(smat); - smat[0][0] = 1.0f / width; - smat[1][1] = 1.0f / height; - invert_m4_m4(ismat, smat); + unit_m4(smat); + smat[0][0] = 1.0f / width; + smat[1][1] = 1.0f / height; + invert_m4_m4(ismat, smat); - mul_m4_series(sc->unistabmat, smat, sc->stabmat, ismat); - } + mul_m4_series(sc->unistabmat, smat, sc->stabmat, ismat); } else if ((sc->flag & SC_MUTE_FOOTAGE) == 0) { ibuf = ED_space_clip_get_buffer(sc); diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c index 89693a403fe..dbedfaa1de4 100644 --- a/source/blender/editors/space_clip/clip_editor.c +++ b/source/blender/editors/space_clip/clip_editor.c @@ -52,6 +52,7 @@ #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_context.h" #include "BKE_tracking.h" @@ -64,6 +65,7 @@ #include "ED_screen.h" #include "ED_clip.h" +#include "ED_mask.h" #include "WM_api.h" #include "WM_types.h" @@ -219,6 +221,7 @@ int ED_space_clip_get_clip_frame_number(SpaceClip *sc) { MovieClip *clip = ED_space_clip_get_clip(sc); + /* Caller must ensure space does have a valid clip, otherwise it will crash, see T45017. */ return BKE_movieclip_remap_scene_to_clip_frame(clip, sc->user.framenr); } @@ -329,7 +332,7 @@ void ED_clip_update_frame(const Main *mainp, int cfra) } } -static bool selected_boundbox(SpaceClip *sc, float min[2], float max[2]) +static bool selected_tracking_boundbox(SpaceClip *sc, float min[2], float max[2]) { MovieClip *clip = ED_space_clip_get_clip(sc); MovieTrackingTrack *track; @@ -377,6 +380,29 @@ static bool selected_boundbox(SpaceClip *sc, float min[2], float max[2]) return ok; } +static bool selected_boundbox(const bContext *C, float min[2], float max[2]) +{ + SpaceClip *sc = CTX_wm_space_clip(C); + if (sc->mode == SC_MODE_TRACKING) { + return selected_tracking_boundbox(sc, min, max); + } + else { + if (ED_mask_selected_minmax(C, min, max)) { + MovieClip *clip = ED_space_clip_get_clip(sc); + int width, height; + ED_space_clip_get_size(sc, &width, &height); + BKE_mask_coord_to_movieclip(clip, &sc->user, min, min); + BKE_mask_coord_to_movieclip(clip, &sc->user, max, max); + min[0] *= width; + min[1] *= height; + max[0] *= width; + max[1] *= height; + return true; + } + return false; + } +} + bool ED_clip_view_selection(const bContext *C, ARegion *ar, bool fit) { SpaceClip *sc = CTX_wm_space_clip(C); @@ -388,7 +414,7 @@ bool ED_clip_view_selection(const bContext *C, ARegion *ar, bool fit) if ((frame_width == 0) || (frame_height == 0) || (sc->clip == NULL)) return false; - if (!selected_boundbox(sc, min, max)) + if (!selected_boundbox(C, min, max)) return false; /* center view */ @@ -626,8 +652,9 @@ static bool check_prefetch_break(void) } /* read file for specified frame number to the memory */ -static unsigned char *prefetch_read_file_to_memory(MovieClip *clip, int current_frame, short render_size, - short render_flag, size_t *size_r) +static unsigned char *prefetch_read_file_to_memory( + MovieClip *clip, int current_frame, short render_size, short render_flag, + size_t *r_size) { MovieClipUser user = {0}; char name[FILE_MAX]; @@ -660,7 +687,7 @@ static unsigned char *prefetch_read_file_to_memory(MovieClip *clip, int current_ return NULL; } - *size_r = size; + *r_size = size; close(file); @@ -698,8 +725,9 @@ static int prefetch_find_uncached_frame(MovieClip *clip, int from_frame, int end } /* get memory buffer for first uncached frame within prefetch frame range */ -static unsigned char *prefetch_thread_next_frame(PrefetchQueue *queue, MovieClip *clip, - size_t *size_r, int *current_frame_r) +static unsigned char *prefetch_thread_next_frame( + PrefetchQueue *queue, MovieClip *clip, + size_t *r_size, int *r_current_frame) { unsigned char *mem = NULL; @@ -728,9 +756,9 @@ static unsigned char *prefetch_thread_next_frame(PrefetchQueue *queue, MovieClip int frames_processed; mem = prefetch_read_file_to_memory(clip, current_frame, queue->render_size, - queue->render_flag, size_r); + queue->render_flag, r_size); - *current_frame_r = current_frame; + *r_current_frame = current_frame; queue->current_frame = current_frame; @@ -765,13 +793,15 @@ static void prefetch_task_func(TaskPool *pool, void *task_data, int UNUSED(threa int flag = IB_rect | IB_alphamode_detect; int result; char *colorspace_name = NULL; + const bool use_proxy = (clip->flag & MCLIP_USE_PROXY) && + (queue->render_size != MCLIP_PROXY_RENDER_SIZE_FULL); user.framenr = current_frame; user.render_size = queue->render_size; user.render_flag = queue->render_flag; /* Proxies are stored in the display space. */ - if (queue->render_flag & MCLIP_USE_PROXY) { + if (!use_proxy) { colorspace_name = clip->colorspace_settings.name; } diff --git a/source/blender/editors/space_clip/clip_graph_ops.c b/source/blender/editors/space_clip/clip_graph_ops.c index 2a2f15c94bb..e781d199d35 100644 --- a/source/blender/editors/space_clip/clip_graph_ops.c +++ b/source/blender/editors/space_clip/clip_graph_ops.c @@ -200,10 +200,18 @@ static bool mouse_select_knot(bContext *C, float co[2], bool extend) toggle_selection_cb); } - if (userdata.coord == 0) - userdata.marker->flag |= MARKER_GRAPH_SEL_X; - else - userdata.marker->flag |= MARKER_GRAPH_SEL_Y; + if (userdata.coord == 0) { + if (extend && (userdata.marker->flag & MARKER_GRAPH_SEL_X) != 0) + userdata.marker->flag &= ~MARKER_GRAPH_SEL_X; + else + userdata.marker->flag |= MARKER_GRAPH_SEL_X; + } + else { + if (extend && (userdata.marker->flag & MARKER_GRAPH_SEL_Y) != 0) + userdata.marker->flag &= ~MARKER_GRAPH_SEL_Y; + else + userdata.marker->flag |= MARKER_GRAPH_SEL_Y; + } return true; } @@ -238,10 +246,12 @@ static bool mouse_select_curve(bContext *C, float co[2], bool extend) else if (act_track != userdata.track) { SelectUserData selectdata = {SEL_DESELECT}; MovieTrackingObject *object = BKE_tracking_object_get_active(tracking); - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object); tracking->act_track = userdata.track; - BKE_tracking_track_select(tracksbase, userdata.track, TRACK_AREA_ALL, true); + if ((sc->flag & SC_SHOW_GRAPH_SEL_ONLY) == 0) { + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object); + BKE_tracking_track_select(tracksbase, userdata.track, TRACK_AREA_ALL, false); + } /* deselect all knots on newly selected curve */ clip_graph_tracking_iterate(sc, diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index 322825ccc84..1d398177d9f 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -658,7 +658,7 @@ void CLIP_OT_view_zoom(wmOperatorType *ot) ot->poll = ED_space_clip_view_clip_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; /* properties */ prop = RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, "Factor", @@ -912,7 +912,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - sound_seek_scene(CTX_data_main(C), scene); + BKE_sound_seek_scene(CTX_data_main(C), scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } diff --git a/source/blender/editors/space_clip/clip_utils.c b/source/blender/editors/space_clip/clip_utils.c index 5f919c9b51d..48f8f587106 100644 --- a/source/blender/editors/space_clip/clip_utils.c +++ b/source/blender/editors/space_clip/clip_utils.c @@ -177,9 +177,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) MovieTracking *tracking = &clip->tracking; MovieTrackingStabilization *stab = &tracking->stabilization; MovieTrackingTrack *act_track = BKE_tracking_track_get_active(tracking); - MovieTrackingPlaneTrack *plane_track, *next_plane_track; ListBase *tracksbase = BKE_tracking_get_active_tracks(tracking); - ListBase *plane_tracks_base = BKE_tracking_get_active_plane_tracks(tracking); bool has_bundle = false, update_stab = false; char track_name_escaped[MAX_NAME], prefix[MAX_NAME * 2]; @@ -197,49 +195,7 @@ void clip_delete_track(bContext *C, MovieClip *clip, MovieTrackingTrack *track) has_bundle = true; /* Make sure no plane will use freed track */ - for (plane_track = plane_tracks_base->first; - plane_track; - plane_track = next_plane_track) - { - bool found = false; - int i; - - next_plane_track = plane_track->next; - - for (i = 0; i < plane_track->point_tracksnr; i++) { - if (plane_track->point_tracks[i] == track) { - found = true; - break; - } - } - - if (!found) { - continue; - } - - if (plane_track->point_tracksnr > 4) { - int track_index; - MovieTrackingTrack **new_point_tracks; - - new_point_tracks = MEM_mallocN(sizeof(*new_point_tracks) * plane_track->point_tracksnr, - "new point tracks array"); - - for (i = 0, track_index = 0; i < plane_track->point_tracksnr; i++) { - if (plane_track->point_tracks[i] != track) { - new_point_tracks[track_index++] = plane_track->point_tracks[i]; - } - } - - MEM_freeN(plane_track->point_tracks); - plane_track->point_tracks = new_point_tracks; - plane_track->point_tracksnr--; - } - else { - /* Delete planes with less than 3 point tracks in it. */ - BKE_tracking_plane_track_free(plane_track); - BLI_freelinkN(plane_tracks_base, plane_track); - } - } + BKE_tracking_plane_tracks_remove_point_track(tracking, track); /* Delete f-curves associated with the track (such as weight, i.e.) */ BLI_strescape(track_name_escaped, track->name, sizeof(track_name_escaped)); diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index fc2c0d3d45c..5ba82f7f71f 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -206,7 +206,7 @@ static void clip_scopes_tag_refresh(ScrArea *sa) if (sc->mode != SC_MODE_TRACKING) return; - /* only while proeprties are visible */ + /* only while properties are visible */ for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_UI && ar->flag & RGN_FLAG_HIDDEN) return; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index 742e58d80dd..91e11d30cc2 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -825,12 +825,8 @@ static void apply_mouse_slide(bContext *C, SlideMarkerData *data) plane_track = plane_track->next) { if ((plane_track->flag & PLANE_TRACK_AUTOKEY) == 0) { - int i; - for (i = 0; i < plane_track->point_tracksnr; i++) { - if (plane_track->point_tracks[i] == data->track) { - BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); - break; - } + if (BKE_tracking_plane_track_has_point_track(plane_track, data->track)) { + BKE_tracking_track_plane_from_existing_motion(plane_track, framenr); } } } @@ -1070,7 +1066,7 @@ void CLIP_OT_slide_marker(wmOperatorType *ot) ot->modal = slide_marker_modal; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; /* properties */ RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX, @@ -1430,6 +1426,7 @@ static int track_markers_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS } clip = ED_space_clip_get_clip(sc); + BLI_assert(clip != NULL); framenr = ED_space_clip_get_clip_frame_number(sc); if (WM_jobs_test(CTX_wm_manager(C), sa, WM_JOB_TYPE_ANY)) { @@ -1508,6 +1505,7 @@ void CLIP_OT_track_markers(wmOperatorType *ot) ot->exec = track_markers_exec; ot->invoke = track_markers_invoke; ot->modal = track_markers_modal; + ot->poll = ED_space_clip_tracking_poll; /* flags */ ot->flag = OPTYPE_UNDO; @@ -1883,7 +1881,7 @@ void CLIP_OT_clear_track_path(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* proeprties */ + /* properties */ RNA_def_enum(ot->srna, "action", clear_path_actions, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute"); RNA_def_boolean(ot->srna, "clear_active", 0, "Clear Active", "Clear active track only instead of all selected tracks"); } @@ -2042,7 +2040,7 @@ static void object_solver_inverted_matrix(Scene *scene, Object *ob, float invmat bool found = false; for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); if (!cti) continue; @@ -2073,7 +2071,7 @@ static Object *object_solver_camera(Scene *scene, Object *ob) bConstraint *con; for (con = ob->constraints.first; con; con = con->next) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); if (!cti) continue; @@ -3054,7 +3052,7 @@ static int frame_jump_exec(bContext *C, wmOperator *op) if (CFRA != sc->user.framenr) { CFRA = sc->user.framenr; - sound_seek_scene(CTX_data_main(C), scene); + BKE_sound_seek_scene(CTX_data_main(C), scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } @@ -3117,6 +3115,12 @@ static int join_tracks_exec(bContext *C, wmOperator *op) if (tracking->stabilization.rot_track == track) tracking->stabilization.rot_track = act_track; + /* TODO(sergey): Re-evaluate planes with auto-key. */ + BKE_tracking_plane_tracks_replace_point_track(tracking, + track, + act_track); + + BKE_tracking_track_free(track); BLI_freelinkN(tracksbase, track); } @@ -4181,7 +4185,7 @@ void CLIP_OT_slide_plane_marker(wmOperatorType *ot) ot->modal = slide_plane_marker_modal; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; } /********************** Insert track keyframe operator *********************/ diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c index 0b29c253f3a..8a2bf17c667 100644 --- a/source/blender/editors/space_clip/tracking_select.c +++ b/source/blender/editors/space_clip/tracking_select.c @@ -997,6 +997,6 @@ void CLIP_OT_select_grouped(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* proeprties */ + /* properties */ RNA_def_enum(ot->srna, "group", select_group_items, TRACK_CLEAR_REMAINED, "Action", "Clear action to execute"); } diff --git a/source/blender/editors/space_console/console_intern.h b/source/blender/editors/space_console/console_intern.h index a3746e091ce..d8a4a6fd2bb 100644 --- a/source/blender/editors/space_console/console_intern.h +++ b/source/blender/editors/space_console/console_intern.h @@ -31,7 +31,6 @@ struct ConsoleLine; struct wmOperatorType; -struct ReportList; struct bContext; /* console_draw.c */ diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index fc007a659b4..4c33499da41 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC file_draw.c file_ops.c file_panels.c + file_utils.c filelist.c filesel.c fsmenu.c diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 98c7ddeff77..6347ce7c4e8 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -295,22 +295,29 @@ static void file_draw_icon(uiBlock *block, char *path, int sx, int sy, int icon, static void file_draw_string(int sx, int sy, const char *string, float width, int height, short align) { - uiStyle *style = UI_style_get(); - uiFontStyle fs = style->widgetlabel; + uiStyle *style; + uiFontStyle fs; rcti rect; char fname[FILE_MAXFILE]; + if (string[0] == '\0') { + return; + } + + style = UI_style_get(); + fs = style->widgetlabel; + fs.align = align; BLI_strncpy(fname, string, FILE_MAXFILE); - file_shorten_string(fname, width + 1.0f, 0); + UI_text_clip_middle_ex(&fs, fname, width, UI_DPI_ICON_SIZE, sizeof(fname), '\0'); /* no text clipping needed, UI_fontstyle_draw does it but is a bit too strict (for buttons it works) */ rect.xmin = sx; - rect.xmax = (int)(sx + ceil(width + 4.0f)); + rect.xmax = (int)(sx + ceil(width + 5.0f / UI_DPI_FAC)); rect.ymin = sy - height; rect.ymax = sy; - + UI_fontstyle_draw(&fs, &rect, fname); } @@ -323,35 +330,40 @@ void file_calc_previews(const bContext *C, ARegion *ar) UI_view2d_totRect_set(v2d, sfile->layout->width, sfile->layout->height); } -static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool dropshadow, bool drag) +static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int sy, ImBuf *imb, FileLayout *layout, bool is_icon, bool drag) { uiBut *but; float fx, fy; float dx, dy; int xco, yco; + float ui_imbx, ui_imby; float scaledx, scaledy; float scale; int ex, ey; + bool use_dropshadow = !is_icon && (file->flags & FILE_TYPE_IMAGE); BLI_assert(imb != NULL); - if ((imb->x * UI_DPI_FAC > layout->prv_w) || - (imb->y * UI_DPI_FAC > layout->prv_h)) + ui_imbx = imb->x * UI_DPI_FAC; + ui_imby = imb->y * UI_DPI_FAC; + /* Unlike thumbnails, icons are not scaled up. */ + if (((ui_imbx > layout->prv_w) || (ui_imby > layout->prv_h)) || + (!is_icon && ((ui_imbx < layout->prv_w) || (ui_imby < layout->prv_h)))) { if (imb->x > imb->y) { scaledx = (float)layout->prv_w; - scaledy = ( (float)imb->y / (float)imb->x) * layout->prv_w; + scaledy = ((float)imb->y / (float)imb->x) * layout->prv_w; scale = scaledx / imb->x; } else { scaledy = (float)layout->prv_h; - scaledx = ( (float)imb->x / (float)imb->y) * layout->prv_h; + scaledx = ((float)imb->x / (float)imb->y) * layout->prv_h; scale = scaledy / imb->y; } } else { - scaledx = (float)imb->x * UI_DPI_FAC; - scaledy = (float)imb->y * UI_DPI_FAC; + scaledx = ui_imbx; + scaledy = ui_imby; scale = UI_DPI_FAC; } @@ -367,17 +379,23 @@ static void file_draw_preview(uiBlock *block, struct direntry *file, int sx, int glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); /* shadow */ - if (dropshadow) + if (use_dropshadow) { UI_draw_box_shadow(220, (float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); + } glEnable(GL_BLEND); /* the image */ - glColor4f(1.0, 1.0, 1.0, 1.0); + if (!is_icon && file->flags & FILE_TYPE_FTFONT) { + UI_ThemeColor(TH_TEXT); + } + else { + glColor4f(1.0, 1.0, 1.0, 1.0); + } glaDrawPixelsTexScaled((float)xco, (float)yco, imb->x, imb->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_NEAREST, imb->rect, scale, scale); /* border */ - if (dropshadow) { + if (use_dropshadow) { glColor4f(0.0f, 0.0f, 0.0f, 0.4f); fdrawbox((float)xco, (float)yco, (float)(xco + ex), (float)(yco + ey)); } @@ -525,9 +543,9 @@ void file_draw_list(const bContext *C, ARegion *ar) if (!(file->selflag & FILE_SEL_EDITING)) { - if ((params->active_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) { + if ((params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) || (file->selflag & FILE_SEL_SELECTED)) { int colorid = (file->selflag & FILE_SEL_SELECTED) ? TH_HILITE : TH_BACK; - int shade = (params->active_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 20 : 0; + int shade = (params->highlight_file == i) || (file->selflag & FILE_SEL_HIGHLIGHTED) ? 35 : 0; /* readonly files (".." and ".") must not be drawn as selected - set color back to normal */ if (FILENAME_IS_CURRPAR(file->relname)) { @@ -549,7 +567,7 @@ void file_draw_list(const bContext *C, ARegion *ar) is_icon = 1; } - file_draw_preview(block, file, sx, sy, imb, layout, !is_icon && (file->flags & FILE_TYPE_IMAGE), do_drag); + file_draw_preview(block, file, sx, sy, imb, layout, is_icon, do_drag); } else { file_draw_icon(block, file->path, sx, sy - (UI_UNIT_Y / 6), get_file_icon(file), ICON_DEFAULT_WIDTH_SCALE, ICON_DEFAULT_HEIGHT_SCALE, do_drag); diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index e7a6cd62a33..d7af6c9b812 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -59,9 +59,17 @@ bool file_draw_check_exists(SpaceFile *sfile); /* file_ops.h */ struct wmOperatorType; struct wmOperator; -struct wmEvent; + +typedef enum WalkSelectDirection { + FILE_SELECT_WALK_UP, + FILE_SELECT_WALK_DOWN, + FILE_SELECT_WALK_LEFT, + FILE_SELECT_WALK_RIGHT, +} WalkSelectDirections; + void FILE_OT_highlight(struct wmOperatorType *ot); void FILE_OT_select(struct wmOperatorType *ot); +void FILE_OT_select_walk(struct wmOperatorType *ot); void FILE_OT_select_all_toggle(struct wmOperatorType *ot); void FILE_OT_select_border(struct wmOperatorType *ot); void FILE_OT_select_bookmark(struct wmOperatorType *ot); @@ -102,7 +110,6 @@ void file_operator_to_sfile(struct SpaceFile *sfile, struct wmOperator *op); /* filesel.c */ -float file_shorten_string(char *string, float w, int front); float file_string_width(const char *str); float file_font_pointsize(void); @@ -113,5 +120,8 @@ int autocomplete_file(struct bContext *C, char *str, void *arg_v); /* file_panels.c */ void file_panels_register(struct ARegionType *art); +/* file_utils.c */ +void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds); + #endif /* __FILE_INTERN_H__ */ diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index f8d13bb6adb..8c9233e3ce5 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -176,7 +176,8 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) (selected_idx < numfiles) && (file = filelist_file(sfile->files, selected_idx))) { - params->active_file = selected_idx; + params->highlight_file = selected_idx; + sfile->params->active_file = selected_idx; if (S_ISDIR(file->type)) { const bool is_parent_dir = FILENAME_IS_PARENT(file->relname); @@ -214,6 +215,23 @@ static FileSelect file_select_do(bContext *C, int selected_idx, bool do_diropen) return retval; } +/** + * \warning: loops over all files so better use cautiously + */ +static bool file_is_any_selected(struct FileList *files) +{ + const int numfiles = filelist_numfiles(files); + int i; + + for (i = 0; i < numfiles; ++i) { + if (filelist_is_selected(files, i, CHECK_ALL)) { + return true; + } + } + + return false; +} + static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, bool fill, bool do_diropen) { @@ -229,19 +247,53 @@ static FileSelect file_select(bContext *C, const rcti *rect, FileSelType select, if (sel.first != sel.last) select = 0; /* Do we have a valid selection and are we actually selecting */ - if ((sel.last >= 0) && ((select == FILE_SEL_ADD) || (select == FILE_SEL_TOGGLE))) { + if ((sel.last >= 0) && (select != FILE_SEL_REMOVE)) { /* Check last selection, if selected, act on the file or dir */ if (filelist_is_selected(sfile->files, sel.last, check_type)) { retval = file_select_do(C, sel.last, do_diropen); } } + if (select != FILE_SEL_ADD && !file_is_any_selected(sfile->files)) { + sfile->params->active_file = -1; + } + /* update operator for name change event */ file_draw_check(C); return retval; } +static int file_border_select_find_last_selected( + SpaceFile *sfile, ARegion *ar, const FileSelection *sel, + const int mouse_xy[2]) +{ + FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + rcti bounds_first, bounds_last; + int dist_first, dist_last; + + file_tile_boundbox(ar, layout, sel->first, &bounds_first); + file_tile_boundbox(ar, layout, sel->last, &bounds_last); + + /* are first and last in the same column (horizontal layout)/row (vertical layout)? */ + if ((layout->flag & FILE_LAYOUT_HOR && bounds_first.xmin == bounds_last.xmin) || + (layout->flag & FILE_LAYOUT_VER && bounds_first.ymin != bounds_last.ymin)) + { + /* use vertical distance */ + const int my_loc = mouse_xy[1] - ar->winrct.ymin; + dist_first = BLI_rcti_length_y(&bounds_first, my_loc); + dist_last = BLI_rcti_length_y(&bounds_last, my_loc); + } + else { + /* use horizontal distance */ + const int mx_loc = mouse_xy[0] - ar->winrct.xmin; + dist_first = BLI_rcti_length_x(&bounds_first, mx_loc); + dist_last = BLI_rcti_length_x(&bounds_last, mx_loc); + } + + return dist_first < dist_last ? sel->first : sel->last; +} + static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); @@ -276,17 +328,17 @@ static int file_border_select_modal(bContext *C, wmOperator *op, const wmEvent * file->selflag &= ~FILE_SEL_HIGHLIGHTED; } - /* active_file gets highlighted as well - make sure it is no readonly file */ + /* make sure highlight_file is no readonly file */ if (sel.last == idx) { - params->active_file = idx; + params->highlight_file = idx; } } } params->sel_first = sel.first; params->sel_last = sel.last; - + params->active_file = file_border_select_find_last_selected(sfile, ar, &sel, &event->x); } else { - params->active_file = -1; + params->highlight_file = -1; params->sel_first = params->sel_last = -1; file_deselect_all(sfile, FILE_SEL_HIGHLIGHTED); WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); @@ -361,7 +413,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; if (sfile && sfile->params) { - int idx = sfile->params->active_file; + int idx = sfile->params->highlight_file; if (idx >= 0) { struct direntry *file = filelist_file(sfile->files, idx); @@ -409,35 +461,252 @@ void FILE_OT_select(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } +/** + * \returns true if selection has changed + */ +static bool file_walk_select_selection_set( + bContext *C, SpaceFile *sfile, + const int direction, const int numfiles, + const int active_old, const int active_new, const int other_site, + const bool has_selection, const bool extend, const bool fill) +{ + FileSelectParams *params = sfile->params; + struct FileList *files = sfile->files; + const int last_sel = params->active_file; /* store old value */ + int active = active_old; /* could use active_old instead, just for readability */ + bool deselect = false; + + BLI_assert(params); + + if (has_selection) { + if (extend && + filelist_is_selected(files, active_old, FILE_SEL_SELECTED) && + filelist_is_selected(files, active_new, FILE_SEL_SELECTED)) + { + /* conditions for deselecting: initial file is selected, new file is + * selected and either other_side isn't selected/found or we use fill */ + deselect = (fill || other_site == -1 || !filelist_is_selected(files, other_site, FILE_SEL_SELECTED)); + + /* don't change active here since we either want to deselect active or we want to + * walk through a block of selected files without selecting/deselecting anything */ + params->active_file = active_new; + /* but we want to change active if we use fill (needed to get correct selection bounds) */ + if (deselect && fill) { + active = active_new; + } + } + else { + /* regular selection change */ + params->active_file = active = active_new; + } + } + else { + /* select last file */ + if (ELEM(direction, FILE_SELECT_WALK_UP, FILE_SELECT_WALK_LEFT)) { + params->active_file = active = numfiles - 1; + } + /* select first file */ + else if (ELEM(direction, FILE_SELECT_WALK_DOWN, FILE_SELECT_WALK_RIGHT)) { + params->active_file = active = 1; + } + else { + BLI_assert(0); + } + } + + if (active < 0) { + return false; + } + + /* highlight the active walker file for extended selection for better visual feedback */ + if (extend) { + params->highlight_file = params->active_file; + } + else { + /* deselect all first */ + file_deselect_all(sfile, FILE_SEL_SELECTED); + + /* highlight file under mouse pos */ + params->highlight_file = -1; + WM_event_add_mousemove(C); + } + + /* do the actual selection */ + if (fill) { + FileSelection sel = { MIN2(active, last_sel), MAX2(active, last_sel) }; + + /* fill selection between last and first selected file */ + filelist_select( + files, &sel, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + /* entire sel is cleared here, so select active again */ + if (deselect) { + filelist_select_file(files, active, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + } + else { + filelist_select_file( + files, active, deselect ? FILE_SEL_REMOVE : FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + + BLI_assert(IN_RANGE(active, 0, numfiles)); + + /* selection changed */ + return true; +} + +/** + * \returns true if selection has changed + */ +static bool file_walk_select_do( + bContext *C, SpaceFile *sfile, + FileSelectParams *params, const int direction, + const bool extend, const bool fill) +{ + struct FileList *files = sfile->files; + const int numfiles = filelist_numfiles(files); + const bool has_selection = file_is_any_selected(files); + const int active_old = params->active_file; + int active_new = -1; + int other_site = -1; /* file on the other site of active_old */ + + + /* *** get all needed files for handling selection *** */ + + if (has_selection) { + ARegion *ar = CTX_wm_region(C); + FileLayout *layout = ED_fileselect_get_layout(sfile, ar); + const int idx_shift = (layout->flag & FILE_LAYOUT_HOR) ? layout->rows : layout->columns; + + if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_UP) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_LEFT)) + { + active_new = active_old - 1; + other_site = active_old + 1; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_DOWN) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_RIGHT)) + { + active_new = active_old + 1; + other_site = active_old - 1; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_LEFT) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_UP)) + { + active_new = active_old - idx_shift; + other_site = active_old + idx_shift; + } + else if ((layout->flag & FILE_LAYOUT_HOR && direction == FILE_SELECT_WALK_RIGHT) || + (layout->flag & FILE_LAYOUT_VER && direction == FILE_SELECT_WALK_DOWN)) + { + + active_new = active_old + idx_shift; + other_site = active_old - idx_shift; + } + else { + BLI_assert(0); + } + + if (!IN_RANGE(active_new, 0, numfiles)) { + if (extend) { + /* extend to invalid file -> abort */ + return false; + } + else { + /* select initial file */ + active_new = active_old; + } + } + if (!IN_RANGE(other_site, 0, numfiles)) { + other_site = -1; + } + } + + return file_walk_select_selection_set( + C, sfile, direction, numfiles, active_old, active_new, other_site, has_selection, extend, fill); +} + +static int file_walk_select_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); + FileSelectParams *params = sfile->params; + const int direction = RNA_enum_get(op->ptr, "direction"); + const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool fill = RNA_boolean_get(op->ptr, "fill"); + + if (file_walk_select_do(C, sfile, params, direction, extend, fill)) { + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void FILE_OT_select_walk(wmOperatorType *ot) +{ + static EnumPropertyItem direction_items[] = { + {FILE_SELECT_WALK_UP, "UP", 0, "Prev", ""}, + {FILE_SELECT_WALK_DOWN, "DOWN", 0, "Next", ""}, + {FILE_SELECT_WALK_LEFT, "LEFT", 0, "Left", ""}, + {FILE_SELECT_WALK_RIGHT, "RIGHT", 0, "Right", ""}, + {0, NULL, 0, NULL, NULL} + }; + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Walk Select/Deselect File"; + ot->description = "Select/Deselect files by walking through them"; + ot->idname = "FILE_OT_select_walk"; + + /* api callbacks */ + ot->invoke = file_walk_select_invoke; + ot->poll = ED_operator_file_active; + + /* properties */ + prop = RNA_def_enum(ot->srna, "direction", direction_items, 0, "Walk Direction", + "Select/Deselect file in this direction"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", + "Extend selection instead of deselecting everything first"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "fill", false, "Fill", "Select everything beginning with the last selection"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + static int file_select_all_exec(bContext *C, wmOperator *UNUSED(op)) { ScrArea *sa = CTX_wm_area(C); SpaceFile *sfile = CTX_wm_space_file(C); FileSelection sel; - int numfiles = filelist_numfiles(sfile->files); - int i; - bool is_selected = false; + const int numfiles = filelist_numfiles(sfile->files); + const bool has_selection = file_is_any_selected(sfile->files); sel.first = 0; sel.last = numfiles - 1; - /* Is any file selected ? */ - for (i = 0; i < numfiles; ++i) { - if (filelist_is_selected(sfile->files, i, CHECK_ALL)) { - is_selected = true; - break; - } - } /* select all only if previously no file was selected */ - if (is_selected) { + if (has_selection) { filelist_select(sfile->files, &sel, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + sfile->params->active_file = -1; } else { const FileCheckType check_type = (sfile->params->flag & FILE_DIRSEL_ONLY) ? CHECK_DIRS : CHECK_FILES; + int i; + filelist_select(sfile->files, &sel, FILE_SEL_ADD, FILE_SEL_SELECTED, check_type); + + /* set active_file to first selected */ + for (i = 0; i < numfiles; i++) { + if (filelist_is_selected(sfile->files, i, check_type)) { + sfile->params->active_file = i; + break; + } + } } + file_draw_check(C); + WM_event_add_mousemove(C); ED_area_tag_redraw(sa); + return OPERATOR_FINISHED; } @@ -748,28 +1017,28 @@ int file_highlight_set(SpaceFile *sfile, ARegion *ar, int mx, int my) numfiles = filelist_numfiles(sfile->files); params = ED_fileselect_get_params(sfile); - origfile = params->active_file; + origfile = params->highlight_file; mx -= ar->winrct.xmin; my -= ar->winrct.ymin; if (BLI_rcti_isect_pt(&ar->v2d.mask, mx, my)) { float fx, fy; - int active_file; + int highlight_file; UI_view2d_region_to_view(v2d, mx, my, &fx, &fy); - active_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy)); + highlight_file = ED_fileselect_layout_offset(sfile->layout, (int)(v2d->tot.xmin + fx), (int)(v2d->tot.ymax - fy)); - if ((active_file >= 0) && (active_file < numfiles)) - params->active_file = active_file; + if ((highlight_file >= 0) && (highlight_file < numfiles)) + params->highlight_file = highlight_file; else - params->active_file = -1; + params->highlight_file = -1; } else - params->active_file = -1; + params->highlight_file = -1; - return (params->active_file != origfile); + return (params->highlight_file != origfile); } static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -1669,7 +1938,7 @@ static int file_rename_exec(bContext *C, wmOperator *UNUSED(op)) SpaceFile *sfile = (SpaceFile *)CTX_wm_space_data(C); if (sfile->params) { - int idx = sfile->params->active_file; + int idx = sfile->params->highlight_file; int numfiles = filelist_numfiles(sfile->files); if ( (0 <= idx) && (idx < numfiles) ) { struct direntry *file = filelist_file(sfile->files, idx); @@ -1690,7 +1959,7 @@ static int file_rename_poll(bContext *C) SpaceFile *sfile = CTX_wm_space_file(C); if (sfile && sfile->params) { - int idx = sfile->params->active_file; + int idx = sfile->params->highlight_file; if (idx >= 0) { struct direntry *file = filelist_file(sfile->files, idx); @@ -1699,7 +1968,7 @@ static int file_rename_poll(bContext *C) } } - if (sfile->params->active_file < 0) { + if (sfile->params->highlight_file < 0) { poll = 0; } else { diff --git a/source/blender/editors/space_file/file_utils.c b/source/blender/editors/space_file/file_utils.c new file mode 100644 index 00000000000..8a80e4a69ee --- /dev/null +++ b/source/blender/editors/space_file/file_utils.c @@ -0,0 +1,46 @@ +/* + * ***** 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. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/space_file/file_utils.c + * \ingroup spfile + */ + +#include "BLI_rect.h" + +#include "BKE_context.h" + +#include "ED_screen.h" +#include "ED_fileselect.h" + +#include "WM_types.h" + +#include "file_intern.h" + + +void file_tile_boundbox(const ARegion *ar, FileLayout *layout, const int file, rcti *r_bounds) +{ + int xmin, ymax; + + ED_fileselect_layout_tilepos(layout, file, &xmin, &ymax); + BLI_rcti_init(r_bounds, xmin, xmin + layout->tile_w + layout->tile_border_x, + ar->winy - ymax - layout->tile_h - layout->tile_border_y, ar->winy - ymax); +} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 66acd7d05e3..4048c9fc1a1 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -654,7 +654,7 @@ ImBuf *filelist_geticon(struct FileList *filelist, const int index) if (file->flags & FILE_TYPE_BLENDER) { ibuf = gSpecialFileImages[SPECIAL_IMG_BLENDFILE]; } - else if ((file->flags & FILE_TYPE_MOVIE) || (file->flags & FILE_TYPE_MOVIE_ICON)) { + else if (file->flags & FILE_TYPE_MOVIE) { ibuf = gSpecialFileImages[SPECIAL_IMG_MOVIEFILE]; } else if (file->flags & FILE_TYPE_SOUND) { @@ -900,7 +900,6 @@ static void filelist_setfiletypes(struct FileList *filelist) file = filelist->filelist; for (num = 0; num < filelist->numfiles; num++, file++) { - file->type = file->s.st_mode; /* restore the mess below */ #ifndef __APPLE__ /* Don't check extensions for directories, allow in OSX cause bundles have extensions*/ if (file->type & S_IFDIR) { @@ -1105,7 +1104,7 @@ static void filelist_from_library(struct FileList *filelist) previews = NULL; nprevs = 0; names = BLO_blendhandle_get_linkable_groups(filelist->libfiledata); - nnames = BLI_linklist_length(names); + nnames = BLI_linklist_count(names); } filelist->numfiles = nnames + 1; @@ -1235,7 +1234,7 @@ static void filelist_from_main(struct FileList *filelist) files = filelist->filelist; - if (!filelist->filter_data.hide_parent) { + if (files && !filelist->filter_data.hide_parent) { memset(&(filelist->filelist[0]), 0, sizeof(struct direntry)); filelist->filelist[0].relname = BLI_strdup(FILENAME_PARENT); filelist->filelist[0].type |= S_IFDIR; @@ -1247,7 +1246,7 @@ static void filelist_from_main(struct FileList *filelist) for (id = lb->first; id; id = id->next) { ok = 1; if (ok) { - if (!filelist->filter_data.hide_dot || id->name[2] != '.') { + if (files && (!filelist->filter_data.hide_dot || id->name[2] != '.')) { memset(files, 0, sizeof(struct direntry)); if (id->lib == NULL) { files->relname = BLI_strdup(id->name + 2); @@ -1334,20 +1333,23 @@ static void thumbnails_startjob(void *tjv, short *stop, short *do_update, float tj->do_update = do_update; while ((*stop == 0) && (limg)) { + ThumbSource source = 0; + + BLI_assert(limg->flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | + FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)); if (limg->flags & FILE_TYPE_IMAGE) { - limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_IMAGE); + source = THB_SOURCE_IMAGE; } else if (limg->flags & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_BLEND); + source = THB_SOURCE_BLEND; } else if (limg->flags & FILE_TYPE_MOVIE) { - limg->img = IMB_thumb_manage(limg->path, THB_NORMAL, THB_SOURCE_MOVIE); - if (!limg->img) { - /* remember that file can't be loaded via IMB_open_anim */ - limg->flags &= ~FILE_TYPE_MOVIE; - limg->flags |= FILE_TYPE_MOVIE_ICON; - } + source = THB_SOURCE_MOVIE; } + else if (limg->flags & FILE_TYPE_FTFONT) { + source = THB_SOURCE_FONT; + } + limg->img = IMB_thumb_manage(limg->path, THB_LARGE, source); *do_update = true; PIL_sleep_ms(10); limg = limg->next; @@ -1363,11 +1365,6 @@ static void thumbnails_update(void *tjv) while (limg) { if (!limg->done && limg->img) { tj->filelist->filelist[limg->index].image = IMB_dupImBuf(limg->img); - /* update flag for movie files where thumbnail can't be created */ - if (limg->flags & FILE_TYPE_MOVIE_ICON) { - tj->filelist->filelist[limg->index].flags &= ~FILE_TYPE_MOVIE; - tj->filelist->filelist[limg->index].flags |= FILE_TYPE_MOVIE_ICON; - } limg->done = true; IMB_freeImBuf(limg->img); limg->img = NULL; @@ -1408,7 +1405,7 @@ void thumbnails_start(FileList *filelist, const bContext *C) continue; } if (!filelist->filelist[idx].image) { - if (filelist->filelist[idx].flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | + if (filelist->filelist[idx].flags & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { FileImage *limg = MEM_callocN(sizeof(*limg), __func__); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 797d54a89b1..44e0a5169fa 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -40,12 +40,7 @@ extern "C" { struct BlendHandle; struct FileList; struct FileSelection; -struct FolderList; -struct Main; -struct ReportList; -struct Scene; struct direntry; -struct rcti; struct wmWindowManager; typedef enum FileSelType { diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 14b72eb8f6d..66eb79abae2 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -102,6 +102,8 @@ short ED_fileselect_set_params(SpaceFile *sfile) /* set path to most recently opened .blend */ BLI_split_dirfile(G.main->name, sfile->params->dir, sfile->params->file, sizeof(sfile->params->dir), sizeof(sfile->params->file)); sfile->params->filter_glob[0] = '\0'; + /* set the default thumbnails size */ + sfile->params->thumbnail_size = 128; } params = sfile->params; @@ -248,6 +250,7 @@ short ED_fileselect_set_params(SpaceFile *sfile) /* operator has no setting for this */ params->sort = FILE_SORT_ALPHA; + params->active_file = -1; /* initialize the list with previous folders */ @@ -281,6 +284,7 @@ void ED_fileselect_reset_params(SpaceFile *sfile) sfile->params->type = FILE_UNIX; sfile->params->flag = 0; sfile->params->title[0] = '\0'; + sfile->params->active_file = -1; } int ED_fileselect_layout_numfiles(FileLayout *layout, ARegion *ar) @@ -396,63 +400,23 @@ void ED_fileselect_layout_tilepos(FileLayout *layout, int tile, int *x, int *y) } } -/* Shorten a string to a given width w. - * If front is set, shorten from the front, - * otherwise shorten from the end. */ -float file_shorten_string(char *string, float w, int front) -{ - char temp[FILE_MAX]; - short shortened = 0; - float sw = 0; - float pad = 0; - - if (w <= 0) { - *string = '\0'; - return 0.0; - } +float file_string_width(const char *str) +{ + uiStyle *style = UI_style_get(); + float width; - sw = file_string_width(string); - if (front == 1) { - const char *s = string; - BLI_strncpy(temp, "...", 4); - pad = file_string_width(temp); - while ((*s) && (sw + pad > w)) { - s++; - sw = file_string_width(s); - shortened = 1; - } - if (shortened) { - int slen = strlen(s); - BLI_strncpy(temp + 3, s, slen + 1); - temp[slen + 4] = '\0'; - BLI_strncpy(string, temp, slen + 4); - } + UI_fontstyle_set(&style->widget); + if (style->widget.kerning == 1) { /* for BLF_width */ + BLF_enable(style->widget.uifont_id, BLF_KERNING_DEFAULT); } - else { - const char *s = string; - while (sw > w) { - int slen = strlen(string); - string[slen - 1] = '\0'; - sw = file_string_width(s); - shortened = 1; - } - if (shortened) { - int slen = strlen(string); - if (slen > 3) { - BLI_strncpy(string + slen - 3, "...", 4); - } - } + width = BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + + if (style->widget.kerning == 1) { + BLF_disable(style->widget.uifont_id, BLF_KERNING_DEFAULT); } - - return sw; -} -float file_string_width(const char *str) -{ - uiStyle *style = UI_style_get(); - UI_fontstyle_set(&style->widget); - return BLF_width(style->widget.uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); + return width; } float file_font_pointsize(void) @@ -527,8 +491,8 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *ar) layout->textheight = textheight; if (params->display == FILE_IMGDISPLAY) { - layout->prv_w = 4.8f * UI_UNIT_X; - layout->prv_h = 4.8f * UI_UNIT_Y; + layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; + layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; layout->tile_border_x = 0.3f * UI_UNIT_X; layout->tile_border_y = 0.3f * UI_UNIT_X; layout->prv_border_x = 0.3f * UI_UNIT_X; @@ -642,8 +606,8 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche */ for (i = 0; i < n; i++) { file = filelist_file(sfile->files, i); - /* use same rule as 'FileCheckType.CHECK_FILES' */ - if (S_ISREG(file->type) && (fnmatch(pattern, file->relname, 0) == 0)) { + /* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */ + if (fnmatch(pattern, file->relname, 0) == 0) { file->selflag |= FILE_SEL_SELECTED; if (!match) { BLI_strncpy(matched_file, file->relname, FILE_MAX); @@ -694,13 +658,8 @@ int autocomplete_directory(struct bContext *C, char *str, void *UNUSED(arg_v)) closedir(dir); match = UI_autocomplete_end(autocpl, str); - if (match) { - if (match == AUTOCOMPLETE_FULL_MATCH) { - BLI_add_slash(str); - } - else { - BLI_strncpy(sfile->params->dir, str, sizeof(sfile->params->dir)); - } + if (match == AUTOCOMPLETE_FULL_MATCH) { + BLI_add_slash(str); } } } @@ -740,7 +699,7 @@ void ED_fileselect_clear(struct wmWindowManager *wm, struct SpaceFile *sfile) filelist_free(sfile->files); } - sfile->params->active_file = -1; + sfile->params->highlight_file = -1; WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, NULL); } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index c6ee6875403..fdf7b458865 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -565,7 +565,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) pathString = CFURLCopyFileSystemPath(cfURL, kCFURLPOSIXPathStyle); - if (pathString == NULL || !CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingASCII)) + if (pathString == NULL || !CFStringGetCString(pathString, line, sizeof(line), kCFStringEncodingUTF8)) continue; /* Exclude "all my files" as it makes no sense in blender fileselector */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 782b318b8a2..c7e0e4ad4e9 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -212,7 +212,7 @@ static void file_refresh(const bContext *C, ScrArea *sa) if (!sfile->files) { sfile->files = filelist_new(params->type); filelist_setdir(sfile->files, params->dir); - params->active_file = -1; /* added this so it opens nicer (ton) */ + params->highlight_file = -1; /* added this so it opens nicer (ton) */ } filelist_setsorting(sfile->files, params->sort); filelist_setfilter_options(sfile->files, params->flag & FILE_HIDE_DOT, @@ -377,7 +377,7 @@ static void file_main_area_draw(const bContext *C, ARegion *ar) UI_view2d_view_ortho(v2d); /* on first read, find active file */ - if (params->active_file == -1) { + if (params->highlight_file == -1) { wmEvent *event = CTX_wm_window(C)->eventstate; file_highlight_set(sfile, ar, event->x, event->y); } @@ -397,6 +397,7 @@ static void file_main_area_draw(const bContext *C, ARegion *ar) static void file_operatortypes(void) { WM_operatortype_append(FILE_OT_select); + WM_operatortype_append(FILE_OT_select_walk); WM_operatortype_append(FILE_OT_select_all_toggle); WM_operatortype_append(FILE_OT_select_border); WM_operatortype_append(FILE_OT_select_bookmark); @@ -462,6 +463,49 @@ static void file_keymap(struct wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "fill", true); RNA_boolean_set(kmi->ptr, "open", false); + + /* arrow keys navigation (walk selecting) */ + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP); + RNA_boolean_set(kmi->ptr, "extend", true); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", UPARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_UP); + RNA_boolean_set(kmi->ptr, "extend", true); + RNA_boolean_set(kmi->ptr, "fill", true); + + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN); + RNA_boolean_set(kmi->ptr, "extend", true); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", DOWNARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_DOWN); + RNA_boolean_set(kmi->ptr, "extend", true); + RNA_boolean_set(kmi->ptr, "fill", true); + + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT); + RNA_boolean_set(kmi->ptr, "extend", true); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", LEFTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_LEFT); + RNA_boolean_set(kmi->ptr, "extend", true); + RNA_boolean_set(kmi->ptr, "fill", true); + + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT); + RNA_boolean_set(kmi->ptr, "extend", true); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select_walk", RIGHTARROWKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "direction", FILE_SELECT_WALK_RIGHT); + RNA_boolean_set(kmi->ptr, "extend", true); + RNA_boolean_set(kmi->ptr, "fill", true); + + /* front and back mouse folder navigation */ WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_CLICK, 0, 0); WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_CLICK, 0, 0); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index a2b64afdb15..e0b62722d57 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -165,9 +165,28 @@ static void graph_panel_properties(const bContext *C, Panel *pa) RNA_pointer_create(ale->id, &RNA_FCurve, fcu, &fcu_ptr); /* user-friendly 'name' for F-Curve */ - /* TODO: only show the path if this is invalid? */ col = uiLayoutColumn(layout, false); - icon = getname_anim_fcurve(name, ale->id, fcu); + if (ale->type == ANIMTYPE_FCURVE) { + /* get user-friendly name for F-Curve */ + icon = getname_anim_fcurve(name, ale->id, fcu); + } + else { + /* NLA Control Curve, etc. */ + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + + /* get name */ + if (acf && acf->name) { + acf->name(ale, name); + } + else { + strcpy(name, IFACE_("<invalid>")); + icon = ICON_ERROR; + } + + /* icon */ + if (ale->type == ANIMTYPE_NLACURVE) + icon = ICON_NLA; + } uiItemL(col, name, icon); /* RNA-Path Editing - only really should be enabled when things aren't working */ diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index a5c04ce2cb4..87e7cab4d15 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -481,7 +481,7 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d ChannelDriver *driver; float samplefreq; float stime, etime; - float unitFac; + float unitFac, offset; float dx, dy; short mapping_flag = ANIM_get_normalization_flags(ac); int i, n; @@ -498,7 +498,7 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d fcu->driver = NULL; /* compute unit correction factor */ - unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); + unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); /* Note about sampling frequency: * Ideally, this is chosen such that we have 1-2 pixels = 1 segment @@ -550,7 +550,7 @@ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d n = (etime - stime) / samplefreq + 0.5f; for (i = 0; i <= n; i++) { float ctime = stime + i * samplefreq; - glVertex2f(ctime, evaluate_fcurve(fcu, ctime) * unitFac); + glVertex2f(ctime, (evaluate_fcurve(fcu, ctime) + offset) * unitFac); } glEnd(); @@ -566,13 +566,14 @@ static void draw_fcurve_curve_samples(bAnimContext *ac, ID *id, FCurve *fcu, Vie FPoint *fpt = prevfpt + 1; float fac, v[2]; int b = fcu->totvert - 1; - float unit_scale; + float unit_scale, offset; short mapping_flag = ANIM_get_normalization_flags(ac); /* apply unit mapping */ glPushMatrix(); - unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); + unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); glScalef(1.0f, unit_scale, 1.0f); + glTranslatef(0.0f, offset, 0.0f); glBegin(GL_LINE_STRIP); @@ -665,14 +666,15 @@ static void draw_fcurve_curve_bezts(bAnimContext *ac, ID *id, FCurve *fcu, View2 float fac = 0.0f; int b = fcu->totvert - 1; int resol; - float unit_scale; + float unit_scale, offset; short mapping_flag = ANIM_get_normalization_flags(ac); /* apply unit mapping */ glPushMatrix(); - unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); + unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); glScalef(1.0f, unit_scale, 1.0f); - + glTranslatef(0.0f, offset, 0.0f); + glBegin(GL_LINE_STRIP); /* extrapolate to left? */ @@ -826,7 +828,8 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) ChannelDriver *driver = fcu->driver; View2D *v2d = &ac->ar->v2d; short mapping_flag = ANIM_get_normalization_flags(ac); - float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); + float offset; + float unitfac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); /* for now, only show when debugging driver... */ //if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0) @@ -850,10 +853,10 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu) glBegin(GL_LINES); { t = v2d->cur.xmin; - glVertex2f(t, t * unitfac); + glVertex2f(t, (t + offset) * unitfac); t = v2d->cur.xmax; - glVertex2f(t, t * unitfac); + glVertex2f(t, (t + offset) * unitfac); } glEnd(); @@ -1067,10 +1070,12 @@ void graph_draw_curves(bAnimContext *ac, SpaceIpo *sipo, ARegion *ar, View2DGrid } else if (((fcu->bezt) || (fcu->fpt)) && (fcu->totvert)) { short mapping_flag = ANIM_get_normalization_flags(ac); - float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); glPushMatrix(); glScalef(1.0f, unit_scale, 1.0f); + glTranslatef(0.0f, offset, 0.0f); if (fcu->bezt) { bool do_handles = draw_fcurve_handles_check(sipo, fcu); @@ -1137,6 +1142,8 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) /* loop through channels, and set up drawing depending on their type */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)ACHANNEL_FIRST; for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) { @@ -1148,11 +1155,12 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= ACHANNEL_STEP; + channel_index++; } } { /* second pass: widgets */ diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 2944901663b..3de3ecebc44 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -109,7 +109,7 @@ void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, floa AnimData *adt = ANIM_nla_mapping_get(ac, ale); FCurve *fcu = (FCurve *)ale->key_data; float txmin, txmax, tymin, tymax; - float unitFac; + float unitFac, offset; /* get range */ if (calc_fcurve_bounds(fcu, &txmin, &txmax, &tymin, &tymax, do_sel_only, include_handles)) { @@ -122,7 +122,9 @@ void get_graph_keyframe_extents(bAnimContext *ac, float *xmin, float *xmax, floa } /* apply unit corrections */ - unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); + unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); + tymin += offset; + tymax += offset; tymin *= unitFac; tymax *= unitFac; @@ -256,6 +258,14 @@ static int graphkeys_view_selected_exec(bContext *C, wmOperator *op) return graphkeys_viewall(C, true, include_handles, smooth_viewtx); } +static int graphkeys_view_frame_exec(bContext *C, wmOperator *op) +{ + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + ANIM_center_frame(C, smooth_viewtx); + return OPERATOR_FINISHED; +} + + void GRAPH_OT_view_all(wmOperatorType *ot) { /* identifiers */ @@ -294,6 +304,21 @@ void GRAPH_OT_view_selected(wmOperatorType *ot) "Include handles of keyframes when calculating extents"); } +void GRAPH_OT_view_frame(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "View Frame"; + ot->idname = "GRAPH_OT_view_frame"; + ot->description = "Reset viewable area to show range around current frame"; + + /* api callbacks */ + ot->exec = graphkeys_view_frame_exec; + ot->poll = ED_operator_graphedit_active; /* XXX: unchecked poll to get fsamples working too, but makes modifier damage trickier... */ + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ******************** Create Ghost-Curves Operator *********************** */ /* This operator samples the data of the selected F-Curves to F-Points, storing them * as 'ghost curves' in the active Graph Editor @@ -327,7 +352,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) AnimData *adt = ANIM_nla_mapping_get(ac, ale); ChannelDriver *driver = fcu->driver; FPoint *fpt; - float unitFac; + float unitFac, offset; int cfra; SpaceIpo *sipo = (SpaceIpo *) ac->sl; short mapping_flag = ANIM_get_normalization_flags(ac); @@ -336,7 +361,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) fcu->driver = NULL; /* calculate unit-mapping factor */ - unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); + unitFac = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); /* create samples, but store them in a new curve * - we cannot use fcurve_store_samples() as that will only overwrite the original curve @@ -349,7 +374,7 @@ static void create_ghost_curves(bAnimContext *ac, int start, int end) float cfrae = BKE_nla_tweakedit_remap(adt, cfra, NLATIME_CONVERT_UNMAP); fpt->vec[0] = cfrae; - fpt->vec[1] = fcurve_samplingcb_evalcurve(fcu, NULL, cfrae) * unitFac; + fpt->vec[1] = (fcurve_samplingcb_evalcurve(fcu, NULL, cfrae) + offset) * unitFac; } /* set color of ghost curve @@ -498,12 +523,17 @@ static void insert_graph_keys(bAnimContext *ac, short mode) else cfra = (float)CFRA; - /* if there's an id */ - if (ale->id) + /* read value from property the F-Curve represents, or from the curve only? + * - ale->id != NULL: Typically, this means that we have enough info to try resolving the path + * - ale->owner != NULL: If this is set, then the path may not be resolvable from the ID alone, + * so it's easier for now to just read the F-Curve directly. + * (TODO: add the full-blown PointerRNA relative parsing case here...) + */ + if (ale->id && !ale->owner) 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; } @@ -582,7 +612,7 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op) ListBase anim_data; short mapping_flag = ANIM_get_normalization_flags(&ac); - + float scale, offset; /* get frame and value from props */ frame = RNA_float_get(op->ptr, "frame"); val = RNA_float_get(op->ptr, "value"); @@ -592,16 +622,18 @@ static int graphkeys_click_insert_exec(bContext *C, wmOperator *op) frame = BKE_nla_tweakedit_remap(adt, frame, NLATIME_CONVERT_UNMAP); /* apply inverse unit-mapping to value to get correct value for F-Curves */ - val *= ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, mapping_flag | ANIM_UNITCONV_RESTORE); - + scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, fcu, mapping_flag | ANIM_UNITCONV_RESTORE, &offset); + + val = val * scale - offset; + /* 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 { @@ -851,13 +883,6 @@ static int graphkeys_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -static int graphkeys_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) -{ - graphkeys_duplicate_exec(C, op); - - return OPERATOR_FINISHED; -} - void GRAPH_OT_duplicate(wmOperatorType *ot) { /* identifiers */ @@ -866,7 +891,6 @@ void GRAPH_OT_duplicate(wmOperatorType *ot) ot->description = "Make a copy of all selected keyframes"; /* api callbacks */ - ot->invoke = graphkeys_duplicate_invoke; ot->exec = graphkeys_duplicate_exec; ot->poll = graphop_editable_keyframes_poll; @@ -1155,6 +1179,11 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filepath", path); + if (!BLI_is_file(path)) { + BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", path); + return OPERATOR_CANCELLED; + } + scene = ac.scene; /* current scene */ /* store necessary data for the baking steps */ @@ -1877,7 +1906,8 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) AnimData *adt = ANIM_nla_mapping_get(&ac, ale); short mapping_flag = ANIM_get_normalization_flags(&ac); KeyframeEditData current_ked; - float unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS, &offset); memset(¤t_ked, 0, sizeof(current_ked)); @@ -1891,7 +1921,7 @@ static int graphkeys_framejump_exec(bContext *C, wmOperator *UNUSED(op)) ked.f1 += current_ked.f1; ked.i1 += current_ked.i1; - ked.f2 += current_ked.f2 * unit_scale; + ked.f2 += (current_ked.f2 + offset) * unit_scale; ked.i2 += current_ked.i2; } @@ -1984,9 +2014,10 @@ static void snap_graph_keys(bAnimContext *ac, short mode) /* normalise cursor value (for normalised F-Curves display) */ if (mode == GRAPHKEYS_SNAP_VALUE) { short mapping_flag = ANIM_get_normalization_flags(ac); - float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag, &offset); - ked.f1 = cursor_value / unit_scale; + ked.f1 = (cursor_value / unit_scale) - offset; } /* perform snapping */ @@ -2111,9 +2142,10 @@ static void mirror_graph_keys(bAnimContext *ac, short mode) /* apply unit corrections */ if (mode == GRAPHKEYS_MIRROR_VALUE) { short mapping_flag = ANIM_get_normalization_flags(ac); - float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, ale->key_data, mapping_flag | ANIM_UNITCONV_ONLYKEYS, &offset); - ked.f1 = cursor_value * unit_scale; + ked.f1 = (cursor_value + offset) * unit_scale; } /* perform actual mirroring */ @@ -2243,7 +2275,7 @@ static EnumPropertyItem *graph_fmodifier_itemf(bContext *C, PointerRNA *UNUSED(p /* start from 1 to skip the 'Invalid' modifier type */ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { - FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); + const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); int index; /* check if modifier is valid for this context */ diff --git a/source/blender/editors/space_graph/graph_intern.h b/source/blender/editors/space_graph/graph_intern.h index 50412952139..a478a86a5e2 100644 --- a/source/blender/editors/space_graph/graph_intern.h +++ b/source/blender/editors/space_graph/graph_intern.h @@ -32,11 +32,8 @@ #define __GRAPH_INTERN_H__ struct bContext; -struct wmWindowManager; struct bAnimContext; struct bAnimListElem; -struct FCurve; -struct FModifier; struct SpaceIpo; struct ScrArea; struct ARegion; @@ -94,6 +91,7 @@ void get_graph_keyframe_extents(struct bAnimContext *ac, float *xmin, float *xma void GRAPH_OT_previewrange_set(struct wmOperatorType *ot); void GRAPH_OT_view_all(struct wmOperatorType *ot); void GRAPH_OT_view_selected(struct wmOperatorType *ot); +void GRAPH_OT_view_frame(struct wmOperatorType *ot); void GRAPH_OT_click_insert(struct wmOperatorType *ot); void GRAPH_OT_keyframe_insert(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_graph/graph_ops.c b/source/blender/editors/space_graph/graph_ops.c index da308d0b1f1..f4cce29bda8 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -87,8 +87,9 @@ static void graphview_cursor_apply(bContext *C, wmOperator *op) * NOTE: sync this part of the code with ANIM_OT_change_frame */ CFRA = RNA_int_get(op->ptr, "frame"); + FRAMENUMBER_MIN_CLAMP(CFRA); SUBFRA = 0.f; - sound_seek_scene(bmain, scene); + BKE_sound_seek_scene(bmain, scene); /* set the cursor value */ sipo->cursorVal = RNA_float_get(op->ptr, "value"); @@ -380,6 +381,7 @@ void graphedit_operatortypes(void) WM_operatortype_append(GRAPH_OT_view_all); WM_operatortype_append(GRAPH_OT_view_selected); WM_operatortype_append(GRAPH_OT_properties); + WM_operatortype_append(GRAPH_OT_view_frame); WM_operatortype_append(GRAPH_OT_ghost_curves_create); WM_operatortype_append(GRAPH_OT_ghost_curves_clear); @@ -440,6 +442,7 @@ void ED_operatormacros_graph(void) WM_operatortype_macro_define(ot, "GRAPH_OT_duplicate"); otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_transform"); RNA_enum_set(otmacro->ptr, "mode", TFM_TIME_DUPLICATE); + RNA_enum_set(otmacro->ptr, "proportional", PROP_EDIT_OFF); } @@ -562,15 +565,14 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "GRAPH_OT_easing_type", EKEY, KM_PRESS, KM_CTRL, 0); /* destructive */ - WM_keymap_add_item(keymap, "GRAPH_OT_clean", OKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GRAPH_OT_smooth", OKEY, KM_PRESS, KM_ALT, 0); WM_keymap_add_item(keymap, "GRAPH_OT_sample", OKEY, KM_PRESS, KM_SHIFT, 0); WM_keymap_add_item(keymap, "GRAPH_OT_bake", CKEY, KM_PRESS, KM_ALT, 0); - WM_keymap_add_item(keymap, "GRAPH_OT_delete", XKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPH_OT_delete", DELKEY, KM_PRESS, 0, 0); - + WM_keymap_add_menu(keymap, "GRAPH_MT_delete", XKEY, KM_PRESS, 0, 0); + WM_keymap_add_menu(keymap, "GRAPH_MT_delete", DELKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_duplicate_move", DKEY, KM_PRESS, KM_SHIFT, 0); /* insertkey */ @@ -594,7 +596,8 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) WM_keymap_add_item(keymap, "GRAPH_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GRAPH_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GRAPH_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); - + WM_keymap_add_item(keymap, "GRAPH_OT_view_frame", PAD0, KM_PRESS, 0, 0); + /* F-Modifiers */ kmi = WM_keymap_add_item(keymap, "GRAPH_OT_fmodifier_add", MKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "only_active", false); @@ -608,6 +611,9 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* transform system */ transform_keymap_for_space(keyconf, keymap, SPACE_IPO); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", OKEY, KM_PRESS, 0, 0); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_proportional_fcurve"); + /* pivot point settings */ kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", COMMAKEY, KM_PRESS, 0, 0); RNA_string_set(kmi->ptr, "data_path", "space_data.pivot_point"); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 78dbae7618b..4cf8a1de7ab 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -272,7 +272,8 @@ static void borderselect_graphkeys( for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); /* apply NLA mapping to all the keyframes, since it's easier than trying to * guess when a callback might use something different @@ -282,8 +283,8 @@ static void borderselect_graphkeys( scaled_rectf.xmin = rectf.xmin; scaled_rectf.xmax = rectf.xmax; - scaled_rectf.ymin = rectf.ymin / unit_scale; - scaled_rectf.ymax = rectf.ymax / unit_scale; + scaled_rectf.ymin = rectf.ymin / unit_scale - offset; + scaled_rectf.ymax = rectf.ymax / unit_scale - offset; /* set horizontal range (if applicable) * NOTE: these values are only used for x-range and y-range but not region @@ -1089,6 +1090,8 @@ typedef struct tNearestVertInfo { short hpoint; /* the handle index that we hit (eHandleIndex) */ short sel; /* whether the handle is selected or not */ int dist; /* distance from mouse to vert */ + + eAnim_ChannelType ctype; /* type of animation channel this FCurve comes from */ } tNearestVertInfo; /* Tags for the type of graph vert that we have */ @@ -1116,8 +1119,8 @@ static bool fcurve_handle_sel_check(SpaceIpo *sipo, BezTriple *bezt) /* check if the given vertex is within bounds or not */ // TODO: should we return if we hit something? static void nearest_fcurve_vert_store( - ListBase *matches, View2D *v2d, FCurve *fcu, - BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2], float unit_scale) + ListBase *matches, View2D *v2d, FCurve *fcu, eAnim_ChannelType ctype, + BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2], float unit_scale, float offset) { /* Keyframes or Samples? */ if (bezt) { @@ -1129,7 +1132,7 @@ static void nearest_fcurve_vert_store( * 'vec' matrix */ if (UI_view2d_view_to_region_clip(v2d, - bezt->vec[hpoint + 1][0], bezt->vec[hpoint + 1][1] * unit_scale, + bezt->vec[hpoint + 1][0], (bezt->vec[hpoint + 1][1] + offset) * unit_scale, &screen_co[0], &screen_co[1]) && /* check if distance from mouse cursor to vert in screen space is within tolerance */ ((dist = len_v2v2_int(mval, screen_co)) <= GVERTSEL_TOL)) @@ -1149,6 +1152,8 @@ static void nearest_fcurve_vert_store( /* store values */ nvi->fcu = fcu; + nvi->ctype = ctype; + nvi->bezt = bezt; nvi->hpoint = hpoint; nvi->dist = dist; @@ -1189,30 +1194,31 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L for (ale = anim_data.first; ale; ale = ale->next) { FCurve *fcu = (FCurve *)ale->key_data; AnimData *adt = ANIM_nla_mapping_get(ac, ale); - float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag); - + float offset; + float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, ale->id, fcu, mapping_flag, &offset); + /* apply NLA mapping to all the keyframes */ if (adt) ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 0, 0); - + if (fcu->bezt) { BezTriple *bezt1 = fcu->bezt, *prevbezt = NULL; int i; for (i = 0; i < fcu->totvert; i++, prevbezt = bezt1, bezt1++) { /* keyframe */ - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_KEY, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_KEY, mval, unit_scale, offset); /* handles - only do them if they're visible */ if (fcurve_handle_sel_check(sipo, bezt1) && (fcu->totvert > 1)) { /* first handle only visible if previous segment had handles */ if ((!prevbezt && (bezt1->ipo == BEZT_IPO_BEZ)) || (prevbezt && (prevbezt->ipo == BEZT_IPO_BEZ))) { - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_LEFT, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_LEFT, mval, unit_scale, offset); } /* second handle only visible if this segment is bezier */ if (bezt1->ipo == BEZT_IPO_BEZ) { - nearest_fcurve_vert_store(matches, v2d, fcu, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval, unit_scale); + nearest_fcurve_vert_store(matches, v2d, fcu, ale->type, bezt1, NULL, NEAREST_HANDLE_RIGHT, mval, unit_scale, offset); } } } @@ -1405,7 +1411,7 @@ static void mouse_graph_keys(bAnimContext *ac, const int mval[2], short select_m /* needs to be called with (sipo->flag & SIPO_SELCUVERTSONLY) otherwise the active flag won't be set [#26452] */ if (nvi->fcu->flag & FCURVE_SELECTED) { int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_NODUPLIS); - ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, ANIMTYPE_FCURVE); + ANIM_set_active_channel(ac, ac->data, ac->datatype, filter, nvi->fcu, nvi->ctype); } /* free temp sample data for filtering */ diff --git a/source/blender/editors/space_graph/graph_utils.c b/source/blender/editors/space_graph/graph_utils.c index eea360ced45..0e56dc817e4 100644 --- a/source/blender/editors/space_graph/graph_utils.c +++ b/source/blender/editors/space_graph/graph_utils.c @@ -210,13 +210,18 @@ int graphop_active_fcurve_poll(bContext *C) if (ale == NULL) return 0; - /* free temp data... */ - has_fcurve = ((ale->data) && (ale->type == ANIMTYPE_FCURVE)); + /* do we have a suitable F-Curves? + * - For most cases, NLA Control Curves are sufficiently similar to NLA curves to serve this role too. + * Under the hood, they are F-Curves too. The only problems which will arise here are if these need to be + * in an Action too (but drivers would then also be affected!) + */ + has_fcurve = ((ale->data) && ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)); if (has_fcurve) { FCurve *fcu = (FCurve *)ale->data; has_fcurve = (fcu->flag & FCURVE_VISIBLE) != 0; } + /* free temp data... */ MEM_freeN(ale); /* return success */ diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index fb3c140fddf..fac9369928b 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -44,6 +44,7 @@ #include "BKE_image.h" #include "BKE_node.h" #include "BKE_screen.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -82,9 +83,9 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf, else { if (ima->source == IMA_SRC_MOVIE) { ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Movie"), len - ofs); - if (ima->anim) + if (BKE_image_has_anim(ima)) ofs += BLI_snprintf(str + ofs, len - ofs, IFACE_(" %d frs"), - IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN)); + IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN)); } else { ofs += BLI_strncpy_rlen(str, IFACE_("Image"), len - ofs); @@ -311,10 +312,11 @@ static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void * static const char *ui_imageuser_layer_fake_name(RenderResult *rr) { - if (rr->rectf) { + RenderView *rv = RE_RenderViewGetById(rr, 0); + if (rv->rectf) { return IFACE_("Composite"); } - else if (rr->rect32) { + else if (rv->rect32) { return IFACE_("Sequence"); } else { @@ -358,7 +360,7 @@ static void ui_imageuser_layer_menu(bContext *UNUSED(C), uiLayout *layout, void for (rl = rr->layers.last; rl; rl = rl->prev, nr--) { final: - uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rl->name), 0, 0, + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, rl->name, 0, 0, UI_UNIT_X * 5, UI_UNIT_X, &iuser->layer, (float) nr, 0.0, 0, -1, ""); } @@ -375,7 +377,7 @@ final: static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) { - if (rl == NULL || rl->rectf) { + if (rl == NULL) { return IFACE_("Combined"); } else { @@ -383,9 +385,9 @@ static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) } } -static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *ptrpair_p) +static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) { - void **rnd_data = ptrpair_p; + void **rnd_data = rnd_pt; uiBlock *block = uiLayoutGetBlock(layout); Image *image = rnd_data[0]; ImageUser *iuser = rnd_data[1]; @@ -398,6 +400,7 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * RenderPass *rpass; const char *fake_name; int nr; + int passflag = 0; /* may have been freed since drawing */ rr = BKE_image_acquire_renderresult(scene, image); @@ -419,15 +422,22 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * fake_name = ui_imageuser_pass_fake_name(rl); if (fake_name) { - BLI_strncpy(rpass_fake.name, fake_name, sizeof(rpass_fake.name)); + BLI_strncpy(rpass_fake.internal_name, fake_name, sizeof(rpass_fake.internal_name)); nr += 1; } /* rendered results don't have a Combined pass */ for (rpass = rl ? rl->passes.last : NULL; rpass; rpass = rpass->prev, nr--) { + + /* just show one pass of each kind */ + if (passflag & rpass->passtype) + continue; + + passflag |= rpass->passtype; + final: - uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->name), 0, 0, - UI_UNIT_X * 5, UI_UNIT_X, &iuser->pass, (float) nr, 0.0, 0, -1, ""); + uiDefButI(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->internal_name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->passtype, (float) rpass->passtype, 0.0, 0, -1, ""); } if (fake_name) { @@ -441,21 +451,81 @@ final: BKE_image_release_renderresult(scene, image); } +/**************************** view menus *****************************/ +static void ui_imageuser_view_menu_rr(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + RenderResult *rr; + RenderView *rview; + int nr; + Scene *scene = iuser->scene; + + /* may have been freed since drawing */ + rr = BKE_image_acquire_renderresult(scene, image); + if (UNLIKELY(rr == NULL)) { + return; + } + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = (rr ? BLI_listbase_count(&rr->views) : 0) - 1; + for (rview = rr ? rr->views.last : NULL; rview; rview = rview->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rview->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } + + BKE_image_release_renderresult(scene, image); +} + +static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + int nr; + ImageView *iv; + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = BLI_listbase_count(&image->views) - 1; + for (iv = image->views.last; iv; iv = iv->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(iv->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } +} + /* 5 layer button callbacks... */ static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v) { ImageUser *iuser = iuser_v; - BKE_image_multilayer_index(rr_v, iuser); + BKE_image_multilayer_index(rr_v, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); } + static void image_multi_inclay_cb(bContext *C, void *rr_v, void *iuser_v) { RenderResult *rr = rr_v; ImageUser *iuser = iuser_v; int tot = BLI_listbase_count(&rr->layers); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->layer < tot - 1) { @@ -478,32 +548,75 @@ static void image_multi_incpass_cb(bContext *C, void *rr_v, void *iuser_v) { RenderResult *rr = rr_v; ImageUser *iuser = iuser_v; - RenderLayer *rl = BLI_findlink(&rr->layers, iuser->layer); + RenderLayer *rl; + RenderPass *rp; + RenderPass *next = NULL; + int layer = iuser->layer; - if (rl) { - int tot = BLI_listbase_count(&rl->passes); + if (RE_HasFakeLayer(rr)) + layer -= 1; - if (rr->rectf || rr->rect32) - tot++; /* fake compo/sequencer layer */ + rl = BLI_findlink(&rr->layers, layer); - if (iuser->pass < tot - 1) { - iuser->pass++; - BKE_image_multilayer_index(rr, iuser); + if (rl) { + for (rp = rl->passes.first; rp; rp = rp->next) { + if (rp->passtype == iuser->passtype) { + next = rp->next; + if (next != NULL && next->passtype == rp->passtype) + next = next->next; + break; + } + } + + if (next != NULL && iuser->passtype != next->passtype) { + iuser->passtype = next->passtype; + BKE_image_multilayer_index(rr, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); } } } static void image_multi_decpass_cb(bContext *C, void *rr_v, void *iuser_v) { + RenderResult *rr = rr_v; ImageUser *iuser = iuser_v; + RenderLayer *rl; + RenderPass *rp; + RenderPass *prev = NULL; + int layer = iuser->layer; - if (iuser->pass > 0) { - iuser->pass--; - BKE_image_multilayer_index(rr_v, iuser); - WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); + if (RE_HasFakeLayer(rr)) + layer -= 1; + + rl = BLI_findlink(&rr->layers, layer); + + if (rl) { + for (rp = rl->passes.last; rp; rp = rp->prev) { + if (rp->passtype == iuser->passtype) { + prev = rp->prev; + if (prev != NULL && prev->passtype == rp->passtype) + prev = prev->prev; + break; + } + } + + if (prev != NULL && iuser->passtype != prev->passtype) { + iuser->passtype = prev->passtype; + BKE_image_multilayer_index(rr, iuser); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); + } } } +/* 5 view button callbacks... */ +static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v) +{ + Image *ima = ima_v; + ImageUser *iuser = iuser_v; + + BKE_image_multiview_index(ima, iuser); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); +} + #if 0 static void image_freecache_cb(bContext *C, void *ima_v, void *unused) { @@ -523,13 +636,14 @@ static void image_user_change(bContext *C, void *iuser_v, void *unused) 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 */ + static void *rnd_pt[4]; /* XXX, workaround */ uiBlock *block = uiLayoutGetBlock(layout); uiBut *but; RenderLayer *rl = NULL; - int wmenu1, wmenu2, wmenu3; + int wmenu1, wmenu2, wmenu3, wmenu4; const char *fake_name; - const char *display_name; + const char *display_name = ""; + const bool show_stereo = (iuser->flag & IMA_SHOW_STEREO); uiLayoutRow(layout, true); @@ -537,6 +651,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes wmenu1 = (2 * w) / 5; wmenu2 = (3 * w) / 5; wmenu3 = (3 * w) / 6; + wmenu4 = (3 * w) / 6; rnd_pt[0] = image; rnd_pt[1] = iuser; @@ -558,6 +673,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes if (rr) { RenderPass *rpass; + RenderView *rview; int rpass_index; /* layer */ @@ -566,29 +682,61 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes 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")); - UI_but_func_set(but, image_multi_cb, rr, iuser); - UI_but_type_set_menu_from_pulldown(but); - + if (RE_layers_have_name(rr)) { + display_name = rl ? rl->name : (fake_name ? fake_name : ""); + but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, + 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } /* pass */ fake_name = ui_imageuser_pass_fake_name(rl); - rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL); + rpass = (rl ? RE_pass_find_by_type(rl, iuser->passtype, ((RenderView *)rr->views.first)->name) : NULL); - display_name = rpass ? rpass->name : (fake_name ? fake_name : ""); - but = uiDefMenuBut(block, ui_imageuser_pass_menu, rnd_pt, display_name, 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); + display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : ""); + but = uiDefMenuBut(block, ui_imageuser_pass_menu, rnd_pt, IFACE_(display_name), + 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); UI_but_func_set(but, image_multi_cb, rr, iuser); UI_but_type_set_menu_from_pulldown(but); + + /* view */ + if (BLI_listbase_count_ex(&rr->views, 2) > 1 && !show_stereo) { + rview = BLI_findlink(&rr->views, iuser->view); + display_name = rview ? rview->name : ""; + + but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } + } + + /* stereo image */ + else if (((image->flag & IMA_IS_STEREO) && (!show_stereo)) || + ((image->flag & IMA_IS_MULTIVIEW) && ((image->flag & IMA_IS_STEREO) == 0))) + { + ImageView *iv; + int nr = 0; + + for (iv = image->views.first; iv; iv = iv->next) { + if (nr++ == iuser->view) { + display_name = iv->name; + break; + } + } + + but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multiview_cb, image, iuser); + UI_but_type_set_menu_from_pulldown(but); } } -static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, short *render_slot) +static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, + int menus_width, short *render_slot) { uiBlock *block = uiLayoutGetBlock(layout); uiLayout *row; uiBut *but; - const float dpi_fac = UI_DPI_FAC; row = uiLayoutRow(layout, true); @@ -605,7 +753,7 @@ static void uiblock_layer_pass_arrow_buttons(uiLayout *layout, Image *image, Ren but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_TRIA_RIGHT, 0, 0, 0.90f * UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, TIP_("Next Layer")); UI_but_func_set(but, image_multi_inclay_cb, rr, iuser); - uiblock_layer_pass_buttons(row, image, rr, iuser, 230 * dpi_fac, render_slot); + uiblock_layer_pass_buttons(row, image, rr, iuser, menus_width, render_slot); /* decrease, increase arrows */ but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_TRIA_LEFT, 0, 0, 0.85f * UI_UNIT_X, UI_UNIT_Y, NULL, 0, 0, 0, 0, TIP_("Previous Pass")); @@ -639,7 +787,7 @@ static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg)) RNA_property_update(C, &cb->ptr, cb->prop); } -void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, PointerRNA *userptr, int compact) +void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, PointerRNA *userptr, int compact, int multiview) { PropertyRNA *prop; PointerRNA imaptr; @@ -722,10 +870,17 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char else if (ima->type == IMA_TYPE_R_RESULT) { /* browse layer/passes */ RenderResult *rr; + const float dpi_fac = UI_DPI_FAC; + const int menus_width = 230 * dpi_fac; /* 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, ima, rr, iuser, &ima->render_slot); + if (rr) { + uiblock_layer_pass_arrow_buttons(layout, ima, rr, iuser, menus_width, &ima->render_slot); + } + else { + uiblock_layer_pass_buttons(layout, ima, rr, iuser, menus_width, &ima->render_slot); + } BKE_image_release_renderresult(scene, ima); } } @@ -734,13 +889,13 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (ima->source != IMA_SRC_GENERATED) { row = uiLayoutRow(layout, true); - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) uiItemO(row, "", ICON_PACKAGE, "image.unpack"); else uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); row = uiLayoutRow(row, true); - uiLayoutSetEnabled(row, ima->packedfile == NULL); + uiLayoutSetEnabled(row, BKE_image_has_packedfile(ima) == false); uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); } @@ -758,7 +913,8 @@ 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, ima->rr, iuser, NULL); + const float dpi_fac = UI_DPI_FAC; + uiblock_layer_pass_arrow_buttons(layout, ima, ima->rr, iuser, 230 * dpi_fac, NULL); } else if (ima->source != IMA_SRC_GENERATED) { if (compact == 0) { @@ -784,6 +940,16 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char BKE_image_release_ibuf(ima, ibuf, NULL); } + if (multiview) { + if ((scene->r.scemode & R_MULTIVIEW) != 0) { + uiItemR(layout, &imaptr, "use_multiview", 0, NULL, ICON_NONE); + + if (RNA_boolean_get(&imaptr, "use_multiview")) { + uiTemplateImageViews(layout, &imaptr); + } + } + } + if (has_alpha) { col = uiLayoutColumn(layout, false); uiItemR(col, &imaptr, "use_alpha", 0, NULL, ICON_NONE); @@ -797,8 +963,6 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE); } - uiItemS(layout); - split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); @@ -962,18 +1126,118 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man } } +void uiTemplateImageStereo3d(uiLayout *layout, PointerRNA *stereo3d_format_ptr) +{ + Stereo3dFormat *stereo3d_format = stereo3d_format_ptr->data; + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE); + + switch (stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + { + uiItemR(col, stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_INTERLACE: + { + uiItemR(col, stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE); + uiItemR(col, stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_SIDEBYSIDE: + { + uiItemR(col, stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE); + /* fall-through */ + } + case S3D_DISPLAY_TOPBOTTOM: + { + uiItemR(col, stereo3d_format_ptr, "use_squeezed_frame", 0, NULL, ICON_NONE); + break; + } + } +} + +static void uiTemplateViewsFormat(uiLayout *layout, PointerRNA *ptr, PointerRNA *stereo3d_format_ptr) +{ + uiLayout *col, *box; + + col = uiLayoutColumn(layout, false); + + uiItemL(col, IFACE_("Views Format:"), ICON_NONE); + uiItemR(uiLayoutRow(col, false), ptr, "views_format", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + if (stereo3d_format_ptr) { + box = uiLayoutBox(col); + uiLayoutSetActive(box, RNA_enum_get(ptr, "views_format") == R_IMF_VIEWS_STEREO_3D); + uiTemplateImageStereo3d(box, stereo3d_format_ptr); + } +} + +void uiTemplateImageViews(uiLayout *layout, PointerRNA *imaptr) +{ + Image *ima = imaptr->data; + + if (ima->type != IMA_TYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imaptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imaptr, prop); + + uiTemplateViewsFormat(layout, imaptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(layout, imaptr, NULL); + } +} + +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr) +{ + ImageFormatData *imf = imfptr->data; + + if (ptr == NULL) + return; + + uiItemR(layout, ptr, "use_multiview", 0, NULL, ICON_NONE); + + if (RNA_boolean_get(ptr, "use_multiview")) { + if (imf->imtype != R_IMF_IMTYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imfptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imfptr, prop); + + uiTemplateViewsFormat(layout, imfptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(layout, imfptr, NULL); + } + } +} + void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) { Scene *scene = CTX_data_scene(C); /* render layers and passes */ if (ima && iuser) { - const float dpi_fac = UI_DPI_FAC; RenderResult *rr; + const float dpi_fac = UI_DPI_FAC; + const int menus_width = 160 * dpi_fac; + const bool is_render_result = (ima->type == IMA_TYPE_R_RESULT); /* 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, ima, rr, iuser, 160 * dpi_fac, (ima->type == IMA_TYPE_R_RESULT) ? &ima->render_slot : NULL); + if (rr && is_render_result) { + uiblock_layer_pass_arrow_buttons(layout, ima, rr, iuser, menus_width, &ima->render_slot); + } + else { + uiblock_layer_pass_buttons(layout, ima, rr, iuser, menus_width, + is_render_result ? &ima->render_slot : NULL); + } BKE_image_release_renderresult(scene, ima); } } diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index d03f3c157a9..36419ccd10d 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -106,9 +106,10 @@ static void draw_render_info(const bContext *C, if (re) { int total_tiles; + bool need_free_tiles; rcti *tiles; - RE_engine_get_current_tiles(re, &total_tiles, &tiles); + tiles = RE_engine_get_current_tiles(re, &total_tiles, &need_free_tiles); if (total_tiles) { int i, x, y; @@ -133,7 +134,9 @@ static void draw_render_info(const bContext *C, glaDrawBorderCorners(tile, zoomx, zoomy); } - MEM_freeN(tiles); + if (need_free_tiles) { + MEM_freeN(tiles); + } glPopMatrix(); } @@ -780,7 +783,7 @@ void draw_image_main(const bContext *C, ARegion *ar) Image *ima; ImBuf *ibuf; float zoomx, zoomy; - bool show_viewer, show_render, show_paint; + bool show_viewer, show_render, show_paint, show_stereo3d, show_multilayer; void *lock; /* XXX can we do this in refresh? */ @@ -810,6 +813,8 @@ void draw_image_main(const bContext *C, ARegion *ar) show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0; show_render = (show_viewer && ima->type == IMA_TYPE_R_RESULT) != 0; show_paint = (ima && (sima->mode == SI_MODE_PAINT) && (show_viewer == false) && (show_render == false)); + show_stereo3d = (ima && (ima->flag & IMA_IS_STEREO) && (sima->iuser.flag & IMA_SHOW_STEREO)); + show_multilayer = ima && BKE_image_is_multilayer(ima); if (show_viewer) { /* use locked draw for drawing viewer image buffer since the compositor @@ -820,17 +825,41 @@ void draw_image_main(const bContext *C, ARegion *ar) BLI_lock_thread(LOCK_DRAW_IMAGE); } + if (show_stereo3d) { + if (show_multilayer) + /* update multiindex and pass for the current eye */ + BKE_image_multilayer_index(ima->rr, &sima->iuser); + else + BKE_image_multiview_index(ima, &sima->iuser); + } + ibuf = ED_space_image_acquire_buffer(sima, &lock); /* draw the image or grid */ - if (ibuf == NULL) + if (ibuf == NULL) { ED_region_grid_draw(ar, zoomx, zoomy); - else if (sima->flag & SI_DRAW_TILE) - draw_image_buffer_repeated(C, sima, ar, scene, ima, ibuf, zoomx, zoomy); - else if (ima && (ima->tpageflag & IMA_TILES)) - draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, 0.0f, 0.0, zoomx, zoomy); - else - draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); + } + else { + + if (sima->flag & SI_DRAW_TILE) + draw_image_buffer_repeated(C, sima, ar, scene, ima, ibuf, zoomx, zoomy); + else if (ima && (ima->tpageflag & IMA_TILES)) + draw_image_buffer_tiled(sima, ar, scene, ima, ibuf, 0.0f, 0.0, zoomx, zoomy); + else + draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); + + if (sima->flag & SI_DRAW_METADATA) { + int x, y; + rctf frame; + + BLI_rctf_init(&frame, 0.0f, ibuf->x, 0.0f, ibuf->y); + UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x, &y); + + ED_region_image_metadata_draw(x, y, ibuf, frame, zoomx, zoomy); + } + } + + ED_space_image_release_buffer(sima, ibuf, lock); /* paint helpers */ if (show_paint) @@ -853,8 +882,6 @@ void draw_image_main(const bContext *C, ARegion *ar) } #endif - ED_space_image_release_buffer(sima, ibuf, lock); - if (show_viewer) { BLI_unlock_thread(LOCK_DRAW_IMAGE); } diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 8e2c6b97a5b..38c9604d14b 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -104,7 +104,7 @@ void ED_space_image_set_mask(bContext *C, SpaceImage *sima, Mask *mask) } } -ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **lock_r) +ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock) { ImBuf *ibuf; @@ -114,7 +114,7 @@ ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **lock_r) return BIF_render_spare_imbuf(); else #endif - ibuf = BKE_image_acquire_ibuf(sima->image, &sima->iuser, lock_r); + ibuf = BKE_image_acquire_ibuf(sima->image, &sima->iuser, r_lock); if (ibuf) { if (ibuf->rect || ibuf->rect_float) @@ -124,7 +124,7 @@ ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **lock_r) } } else - *lock_r = NULL; + *r_lock = NULL; return NULL; } @@ -299,7 +299,7 @@ void ED_space_image_scopes_update(const struct bContext *C, struct SpaceImage *s /* scope update can be expensive, don't update during paint modes */ if (sima->mode == SI_MODE_PAINT) return; - if (ob && ((ob->mode & OB_MODE_TEXTURE_PAINT) != 0)) + if (ob && ((ob->mode & (OB_MODE_TEXTURE_PAINT | OB_MODE_EDIT)) != 0)) return; scopes_update(&sima->scopes, ibuf, use_view_settings ? &scene->view_settings : NULL, &scene->display_settings); diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index aecc43f4fdf..6eaad302180 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -38,11 +38,7 @@ struct ARegion; struct ARegionType; struct ScrArea; struct SpaceImage; -struct Object; -struct Image; -struct ImBuf; struct wmOperatorType; -struct Scene; struct bNodeTree; /* space_image.c */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 61667b02140..72e3f21b1be 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -30,14 +30,21 @@ #include <stddef.h> #include <string.h> +#include <fcntl.h> #include <stdlib.h> #include <errno.h> +#ifndef WIN32 +# include <unistd.h> +#else +# include <io.h> +#endif #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utf8.h" #include "BLF_translation.h" @@ -60,6 +67,7 @@ #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "GPU_draw.h" #include "GPU_buffers.h" @@ -68,6 +76,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_moviecache.h" +#include "intern/openexr/openexr_multi.h" #include "RE_pipeline.h" @@ -76,6 +85,7 @@ #include "RNA_enum_types.h" #include "ED_image.h" +#include "ED_mask.h" #include "ED_paint.h" #include "ED_render.h" #include "ED_screen.h" @@ -369,7 +379,7 @@ void IMAGE_OT_view_pan(wmOperatorType *ot) ot->poll = space_image_main_area_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER | OPTYPE_LOCK_BYPASS; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR | OPTYPE_LOCK_BYPASS; /* properties */ RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX, @@ -585,7 +595,7 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot) ot->poll = space_image_main_area_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER | OPTYPE_LOCK_BYPASS; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR | OPTYPE_LOCK_BYPASS; /* properties */ prop = RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX, "Factor", @@ -711,6 +721,9 @@ void IMAGE_OT_view_all(wmOperatorType *ot) ot->exec = image_view_all_exec; ot->poll = space_image_main_area_poll; + /* flags */ + ot->flag = OPTYPE_LOCK_BYPASS; + /* properties */ prop = RNA_def_boolean(ot->srna, "fit_view", 0, "Fit View", "Fit frame to the viewport"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); @@ -742,8 +755,15 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) height = height * aspy; /* get bounds */ - if (!ED_uvedit_minmax(scene, ima, obedit, min, max)) - return OPERATOR_CANCELLED; + if (ED_space_image_show_uvedit(sima, obedit)) { + if (!ED_uvedit_minmax(scene, ima, obedit, min, max)) + return OPERATOR_CANCELLED; + } + else if (ED_space_image_check_show_maskedit(scene, sima)) { + if (!ED_mask_selected_minmax(C, min, max)) { + return OPERATOR_CANCELLED; + } + } /* adjust offset and zoom */ sima->xof = (int)(((min[0] + max[0]) * 0.5f - 0.5f) * width); @@ -763,7 +783,7 @@ static int image_view_selected_exec(bContext *C, wmOperator *UNUSED(op)) static int image_view_selected_poll(bContext *C) { - return (space_image_main_area_poll(C) && ED_operator_uvedit(C)); + return (space_image_main_area_poll(C) && (ED_operator_uvedit(C) || ED_operator_mask(C))); } void IMAGE_OT_view_selected(wmOperatorType *ot) @@ -936,6 +956,7 @@ static void image_filesel(bContext *C, wmOperator *op, const char *path) typedef struct ImageOpenData { PropertyPointerRNA pprop; ImageUser *iuser; + ImageFormatData im_format; } ImageOpenData; typedef struct ImageFrame { @@ -946,7 +967,6 @@ typedef struct ImageFrame { static void image_open_init(bContext *C, wmOperator *op) { ImageOpenData *iod; - op->customdata = iod = MEM_callocN(sizeof(ImageOpenData), __func__); iod->iuser = CTX_data_pointer_get_type(C, "image_user", &RNA_ImageUser).data; UI_context_active_but_prop_get_templateID(C, &iod->pprop.ptr, &iod->pprop.prop); @@ -996,6 +1016,7 @@ static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *p } else { /* different file base name found, is ignored */ + MEM_freeN(filename); MEM_freeN(frame); break; } @@ -1048,7 +1069,7 @@ static int image_open_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); ImageUser *iuser = NULL; - ImageOpenData *iod; + ImageOpenData *iod = op->customdata; PointerRNA idptr; Image *ima = NULL; char path[FILE_MAX]; @@ -1060,15 +1081,27 @@ static int image_open_exec(bContext *C, wmOperator *op) 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")) + if (RNA_struct_property_is_set(op->ptr, "directory") && + RNA_struct_property_is_set(op->ptr, "files")) { - ListBase frames; - - BLI_listbase_clear(&frames); - image_sequence_get_frames(op->ptr, &frames, path, sizeof(path)); - frame_seq_len = image_sequence_get_len(&frames, &frame_ofs); - BLI_freelistN(&frames); + /* only to pass to imbuf */ + char path_full[FILE_MAX]; + BLI_strncpy(path_full, path, sizeof(path_full)); + BLI_path_abs(path_full, G.main->name); + + if (!IMB_isanim(path_full)) { + bool was_relative = BLI_path_is_rel(path); + ListBase frames; + + BLI_listbase_clear(&frames); + image_sequence_get_frames(op->ptr, &frames, path, sizeof(path)); + frame_seq_len = image_sequence_get_len(&frames, &frame_ofs); + BLI_freelistN(&frames); + + if (was_relative) { + BLI_path_rel(path, G.main->name); + } + } } errno = 0; @@ -1085,6 +1118,21 @@ static int image_open_exec(bContext *C, wmOperator *op) if (!op->customdata) image_open_init(C, op); + /* handle multiview images */ + if (RNA_boolean_get(op->ptr, "use_multiview")) { + ImageFormatData *imf = &iod->im_format; + + ima->flag |= IMA_USE_VIEWS; + ima->views_format = imf->views_format; + *ima->stereo3d_format = imf->stereo3d_format; + } + else { + ima->flag &= ~IMA_USE_VIEWS; + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + /* only image path after save, never ibuf */ if (is_relative_path) { if (!exists) { @@ -1128,6 +1176,8 @@ static int image_open_exec(bContext *C, wmOperator *op) iuser->framenr = 1; iuser->offset = frame_ofs - 1; iuser->fie_ima = 2; + iuser->scene = scene; + BKE_image_init_imageuser(ima, iuser); } /* XXX unpackImage frees image buffers */ @@ -1146,7 +1196,8 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */ const char *path = U.textudir; Image *ima = NULL; - + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; if (sima) { ima = sima->image; } @@ -1185,11 +1236,44 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( image_open_init(C, op); + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + image_filesel(C, op, path); return OPERATOR_RUNNING_MODAL; } +static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + +static void image_open_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + ImageOpenData *iod = op->customdata; + ImageFormatData *imf = &iod->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} + /* called by other space types too */ void IMAGE_OT_open(wmOperatorType *ot) { @@ -1202,6 +1286,7 @@ void IMAGE_OT_open(wmOperatorType *ot) ot->exec = image_open_exec; ot->invoke = image_open_invoke; ot->cancel = image_open_cancel; + ot->ui = image_open_draw; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1233,10 +1318,10 @@ static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op)) } - if (!ima || !iuser || !ima->anim) + if (!ima || !iuser || !BKE_image_has_anim(ima)) return OPERATOR_CANCELLED; - iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN); + iuser->frames = IMB_anim_get_duration(((ImageAnim *) ima->anims.first)->anim, IMB_TC_RECORD_RUN); BKE_image_user_frame_calc(iuser, scene->r.cfra, 0); return OPERATOR_FINISHED; @@ -1285,7 +1370,7 @@ static int image_replace_exec(bContext *C, wmOperator *op) /* XXX unpackImage frees image buffers */ ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - BKE_icon_changed(BKE_icon_getid(&sima->image->id)); + BKE_icon_changed(BKE_icon_id_ensure(&sima->image->id)); BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); @@ -1433,6 +1518,10 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } } + /* use the multiview image settings as the default */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + /* color management */ BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); @@ -1468,30 +1557,104 @@ static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op) RNA_string_set(op->ptr, "filepath", simopts->filepath); } +static void save_image_post(wmOperator *op, ImBuf *ibuf, Image *ima, int ok, int save_copy, const char *relbase, int relative, int do_newpath, const char *filepath) +{ + if (ok) { + if (!save_copy) { + ColorManagedColorspaceSettings old_colorspace_settings; + + if (do_newpath) { + BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); + BLI_strncpy(ima->name, filepath, sizeof(ima->name)); + } + + ibuf->userflags &= ~IB_BITMAPDIRTY; + + /* change type? */ + if (ima->type == IMA_TYPE_R_RESULT) { + ima->type = IMA_TYPE_IMAGE; + + /* workaround to ensure the render result buffer is no longer used + * by this image, otherwise can crash when a new render result is + * created. */ + if (ibuf->rect && !(ibuf->mall & IB_rect)) + imb_freerectImBuf(ibuf); + if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) + imb_freerectfloatImBuf(ibuf); + if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) + IMB_freezbufImBuf(ibuf); + if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) + IMB_freezbuffloatImBuf(ibuf); + } + if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + + /* only image path, never ibuf */ + if (relative) { + BLI_path_rel(ima->name, relbase); /* only after saving */ + } + + BKE_color_managed_colorspace_settings_copy(&old_colorspace_settings, + &ima->colorspace_settings); + IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + if (!BKE_color_managed_colorspace_settings_equals(&old_colorspace_settings, + &ima->colorspace_settings)) + { + BKE_image_signal(ima, NULL, IMA_SIGNAL_COLORMANAGE); + } + } + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", filepath); + } +} + +static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) +{ + if (colormanaged_ibuf != ibuf) { + /* This guys might be modified by image buffer write functions, + * need to copy them back from color managed image buffer to an + * original one, so file type of image is being properly updated. + */ + ibuf->ftype = colormanaged_ibuf->ftype; + ibuf->planes = colormanaged_ibuf->planes; + + IMB_freeImBuf(colormanaged_ibuf); + } +} + /** * \return success. * \note ``ima->name`` and ``ibuf->name`` should end up the same. + * \note for multiview the first ``ibuf`` is important to get the settings. */ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath) { Image *ima = ED_space_image(sima); void *lock; ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + Scene *scene; + RenderResult *rr = NULL; bool ok = false; + WM_cursor_wait(1); + if (ibuf) { - ImBuf *colormanaged_ibuf; + ImBuf *colormanaged_ibuf = NULL; const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id); const bool relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); const bool save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); const bool save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); ImageFormatData *imf = &simopts->im_format; + const bool is_multilayer = imf->imtype == R_IMF_IMTYPE_MULTILAYER; + bool is_mono; + /* old global to ensure a 2nd save goes to same dir */ BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima)); - WM_cursor_wait(1); - if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ if (simopts->im_format.planes == R_IMF_PLANES_RGBA) { @@ -1512,83 +1675,194 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI } } - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + /* we need renderresult for exr and rendered multiview */ + scene = CTX_data_scene(C); + rr = BKE_image_acquire_renderresult(scene, ima); + is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : (ima->flag & IMA_IS_MULTIVIEW) == 0; - if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - Scene *scene = CTX_data_scene(C); - RenderResult *rr = BKE_image_acquire_renderresult(scene, ima); - if (rr) { - ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, simopts->im_format.exr_codec); - } - else { + /* error handling */ + if (!rr) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image"); + goto cleanup; } - BKE_image_release_renderresult(scene, ima); } else { - ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy); - } - - if (ok) { - if (!save_copy) { - if (do_newpath) { - BLI_strncpy(ibuf->name, simopts->filepath, sizeof(ibuf->name)); - BLI_strncpy(ima->name, simopts->filepath, sizeof(ima->name)); + if (imf->views_format == R_IMF_VIEWS_STEREO_3D) { + if ((ima->flag & IMA_IS_STEREO) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } - ibuf->userflags &= ~IB_BITMAPDIRTY; - - /* change type? */ - if (ima->type == IMA_TYPE_R_RESULT) { - ima->type = IMA_TYPE_IMAGE; - - /* workaround to ensure the render result buffer is no longer used - * by this image, otherwise can crash when a new render result is - * created. */ - if (ibuf->rect && !(ibuf->mall & IB_rect)) - imb_freerectImBuf(ibuf); - if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) - imb_freerectfloatImBuf(ibuf); - if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) - IMB_freezbufImBuf(ibuf); - if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) - IMB_freezbuffloatImBuf(ibuf); - } - if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; + /* it shouldn't ever happen*/ + if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == NULL) || + (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == NULL)) + { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } + } + BKE_imbuf_stamp_info(rr, ibuf); + } + + /* fancy multiview OpenEXR */ + if ((imf->imtype == R_IMF_IMTYPE_MULTILAYER) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, true, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else if ((imf->imtype == R_IMF_IMTYPE_OPENEXR) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + /* treat special Openexr case separetely (this is the singlelayer multiview OpenEXR */ + BKE_imbuf_write_prepare(ibuf, imf); + ok = BKE_image_save_openexr_multiview(ima, ibuf, simopts->filepath, (IB_rect | IB_zbuf | IB_zbuffloat | IB_multiview)); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* regular mono pipeline */ + else if (is_mono) { + if (is_multilayer) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + } + else { + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + IMB_metadata_copy(colormanaged_ibuf, ibuf); + ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, imf, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + } + save_image_post(op, ibuf, ima, ok, (is_multilayer ? true : save_copy), relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* individual multiview images */ + else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t i; + unsigned char planes = ibuf->planes; + const size_t totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views)); + + if (!is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); + } - /* only image path, never ibuf */ - if (relative) { - BLI_path_rel(ima->name, relbase); /* only after saving */ + for (i = 0; i < totviews; i++) { + char filepath[FILE_MAX]; + bool ok_view = false; + const char *view = rr ? ((RenderView *) BLI_findlink(&rr->views, i))->name : + ((ImageView *) BLI_findlink(&ima->views, i))->name; + + if (is_multilayer) { + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); + ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, false, view); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); + } + else { + /* copy iuser to get the correct ibuf for this view */ + ImageUser iuser = sima->iuser; + iuser.view = i; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) + BKE_image_multilayer_index(rr, &iuser); + else + BKE_image_multiview_index(ima, &iuser); + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + ibuf->planes = planes; + + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); + + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &simopts->im_format, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); + BKE_image_release_ibuf(sima->image, ibuf, lock); } + ok &= ok_view; + } - IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + if (is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); } } - else { - BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", simopts->filepath); - } + /* stereo (multiview) images */ + else if (simopts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else { + ImBuf *ibuf_stereo[2] = {NULL}; + unsigned char planes = ibuf->planes; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + /* we need to get the specific per-view buffers */ + ED_space_image_release_buffer(sima, ibuf, lock); - WM_cursor_wait(0); + for (i = 0; i < 2; i ++) { + ImageUser iuser = sima->iuser; + iuser.flag &= ~IMA_SHOW_STEREO; - if (colormanaged_ibuf != ibuf) { - /* This guys might be modified by image buffer write functions, - * need to copy them back from color managed image buffer to an - * original one, so file type of image is being properly updated. - */ - ibuf->ftype = colormanaged_ibuf->ftype; - ibuf->planes = colormanaged_ibuf->planes; + if (rr) { + int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + iuser.view = id; + BKE_image_multilayer_index(rr, &iuser); + } + else { + iuser.view = i; + BKE_image_multiview_index(ima, &iuser); + } + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + + if (ibuf == NULL) { + BKE_report(op->reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image"); + goto cleanup; + } - IMB_freeImBuf(colormanaged_ibuf); + ibuf->planes = planes; + + /* color manage the ImBuf leaving it ready for saving */ + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, + &imf->view_settings, &imf->display_settings, imf); + + BKE_imbuf_write_prepare(colormanaged_ibuf, imf); + IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); + + /* duplicate buffer to prevent locker issue when using render result */ + ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); + + save_imbuf_post(ibuf, colormanaged_ibuf); + BKE_image_release_ibuf(sima->image, ibuf, lock); + } + + ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]); + + /* save via traditional path */ + ok = BKE_imbuf_write_as(ibuf, simopts->filepath, imf, save_copy); + + IMB_freeImBuf(ibuf); + + for (i = 0; i < 2; i ++) { + IMB_freeImBuf(ibuf_stereo[i]); + } + } } + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + + } + else { +cleanup: + ED_space_image_release_buffer(sima, ibuf, lock); } - ED_space_image_release_buffer(sima, ibuf, lock); + if (rr) { + BKE_image_release_renderresult(scene, ima); + } + + WM_cursor_wait(0); return ok; } @@ -1636,6 +1910,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS Image *ima = ED_space_image(sima); Scene *scene = CTX_data_scene(C); SaveImageOptions simopts; + PropertyRNA *prop; const bool save_as_render = ((ima->source == IMA_SRC_VIEWER) || (ima->flag & IMA_VIEW_AS_RENDER)); if (RNA_struct_property_is_set(op->ptr, "filepath")) @@ -1657,6 +1932,12 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__); memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format)); + /* show multiview save options only if image has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + image_filesel(C, op, simopts.filepath); return OPERATOR_RUNNING_MODAL; @@ -1683,15 +1964,20 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; ImageFormatData *imf = op->customdata; - PointerRNA ptr; + PointerRNA imf_ptr, ptr; + const bool is_multiview = RNA_boolean_get(op->ptr, "show_multiview"); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr); - uiTemplateImageSettings(layout, &ptr, false); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, false); /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, '\0'); + + /* multiview template */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); } static int image_save_as_poll(bContext *C) @@ -1714,8 +2000,6 @@ static int image_save_as_poll(bContext *C) void IMAGE_OT_save_as(wmOperatorType *ot) { -// PropertyRNA *prop; - /* identifiers */ ot->name = "Save As Image"; ot->idname = "IMAGE_OT_save_as"; @@ -1932,6 +2216,7 @@ static int image_new_exec(bContext *C, wmOperator *op) float color[4]; int width, height, floatbuf, gen_type, alpha; int gen_context; + int stereo3d; /* retrieve state */ sima = CTX_wm_space_image(C); @@ -1952,11 +2237,12 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); gen_context = RNA_enum_get(op->ptr, "gen_context"); + stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d"); if (!alpha) color[3] = 1.0f; - ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color); + ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d); if (!ima) return OPERATOR_CANCELLED; @@ -2036,6 +2322,53 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); } +static void image_new_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *split, *col[2]; + uiLayout *layout = op->layout; + PointerRNA ptr; +#if 0 + Scene *scene = CTX_data_scene(C); + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; +#endif + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + /* copy of WM_operator_props_dialog_popup() layout */ + + split = uiLayoutSplit(layout, 0.5f, false); + col[0] = uiLayoutColumn(split, false); + col[1] = uiLayoutColumn(split, false); + + uiItemL(col[0], IFACE_("Name"), ICON_NONE); + uiItemR(col[1], &ptr, "name", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Width"), ICON_NONE); + uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Height"), ICON_NONE); + uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Color"), ICON_NONE); + uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE); + + uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); + uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE); + +#if 0 + if (is_multiview) { + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE); + } +#endif +} + void IMAGE_OT_new(wmOperatorType *ot) { PropertyRNA *prop; @@ -2056,6 +2389,7 @@ void IMAGE_OT_new(wmOperatorType *ot) /* api callbacks */ ot->exec = image_new_exec; ot->invoke = image_new_invoke; + ot->ui = image_new_draw; /* flags */ ot->flag = OPTYPE_UNDO; @@ -2075,7 +2409,8 @@ void IMAGE_OT_new(wmOperatorType *ot) 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); - + prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } #undef IMA_DEF_NAME @@ -2103,7 +2438,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) const bool b = RNA_boolean_get(op->ptr, "invert_b"); const bool a = RNA_boolean_get(op->ptr, "invert_a"); - int i; + size_t i; if (ibuf == NULL) /* TODO: this should actually never happen, but does for render-results -> cleanup */ return OPERATOR_CANCELLED; @@ -2114,13 +2449,13 @@ static int image_invert_exec(bContext *C, wmOperator *op) /* 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(); - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y); + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); } /* TODO: make this into an IMB_invert_channels(ibuf,r,g,b,a) method!? */ if (ibuf->rect_float) { float *fp = (float *) ibuf->rect_float; - for (i = ibuf->x * ibuf->y; i > 0; i--, fp += 4) { + for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, fp += 4) { if (r) fp[0] = 1.0f - fp[0]; if (g) fp[1] = 1.0f - fp[1]; if (b) fp[2] = 1.0f - fp[2]; @@ -2134,7 +2469,7 @@ static int image_invert_exec(bContext *C, wmOperator *op) else if (ibuf->rect) { char *cp = (char *) ibuf->rect; - for (i = ibuf->x * ibuf->y; i > 0; i--, cp += 4) { + for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, cp += 4) { if (r) cp[0] = 255 - cp[0]; if (g) cp[1] = 255 - cp[1]; if (b) cp[2] = 255 - cp[2]; @@ -2200,7 +2535,7 @@ static bool image_pack_test(bContext *C, wmOperator *op) if (!ima) return 0; - if (!as_png && ima->packedfile) + if (!as_png && BKE_image_has_packedfile(ima)) return 0; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2229,7 +2564,7 @@ static int image_pack_exec(bContext *C, wmOperator *op) if (as_png) BKE_image_memorypack(ima); else - ima->packedfile = newPackedFile(op->reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id)); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -2301,7 +2636,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op) if (!ima) ima = CTX_data_edit_image(C); } - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2329,7 +2664,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (RNA_struct_property_is_set(op->ptr, "id")) return image_unpack_exec(C, op); - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2340,7 +2675,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (G.fileflags & G_AUTOPACK) BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save"); - unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", ima->packedfile); + unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", BKE_image_has_packedfile(ima) ? ((ImagePackedFile *)ima->packedfiles.first)->packedfile : NULL); return OPERATOR_FINISHED; } @@ -2609,8 +2944,11 @@ static int image_sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: // XXX hardcoded - image_sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + image_sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: image_sample_apply(C, op, event); break; @@ -2982,7 +3320,7 @@ static void change_frame_apply(bContext *C, wmOperator *op) SUBFRA = 0.0f; /* do updates */ - sound_seek_scene(CTX_data_main(C), scene); + BKE_sound_seek_scene(CTX_data_main(C), scene); WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene); } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index bd3d76bbaba..a9a5e601e10 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -156,7 +156,9 @@ static SpaceLink *image_new(const bContext *UNUSED(C)) simage->iuser.ok = true; simage->iuser.fie_ima = 2; simage->iuser.frames = 100; - + simage->iuser.flag = IMA_SHOW_STEREO; + simage->iuser.passtype = SCE_PASS_COMBINED; + scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; @@ -681,7 +683,7 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) View2D *v2d = &ar->v2d; //View2DScrollers *scrollers; float col[3]; - + /* XXX not supported yet, disabling for now */ scene->r.scemode &= ~R_COMP_CROP; diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index e2895109b48..cd424f45842 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -50,7 +50,7 @@ #include "UI_view2d.h" #include "info_intern.h" -#include "../space_info/textview.h" +#include "textview.h" /* complicates things a bit, so leaving in old simple code */ #define USE_INFO_NEWLINE diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 240d8baa6f2..a1f90f16bbf 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -134,7 +134,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op) G.fileflags &= ~G_AUTOPACK; } else { - packAll(bmain, op->reports); + packAll(bmain, op->reports, true); G.fileflags |= G_AUTOPACK; } @@ -161,7 +161,7 @@ static int pack_all_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - packAll(bmain, op->reports); + packAll(bmain, op->reports, true); G.fileflags |= G_AUTOPACK; return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index a0dfb285a1c..48a39cd79fc 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -56,6 +56,8 @@ #include "ED_info.h" #include "ED_armature.h" +#include "GPU_extensions.h" + #define MAX_INFO_LEN 512 #define MAX_INFO_NUM_LEN 16 @@ -381,6 +383,7 @@ static void stats_string(Scene *scene) Object *ob = (scene->basact) ? scene->basact->object : NULL; uintptr_t mem_in_use, mmap_in_use; char memstr[MAX_INFO_MEM_LEN]; + char gpumemstr[MAX_INFO_MEM_LEN] = ""; char *s; size_t ofs = 0; @@ -416,11 +419,22 @@ static void stats_string(Scene *scene) /* get memory statistics */ - s = memstr; - ofs += BLI_snprintf(s + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_(" | Mem:%.2fM"), + ofs = BLI_snprintf(memstr, MAX_INFO_MEM_LEN, IFACE_(" | Mem:%.2fM"), (double)((mem_in_use - mmap_in_use) >> 10) / 1024.0); if (mmap_in_use) - BLI_snprintf(s + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_(" (%.2fM)"), (double)((mmap_in_use) >> 10) / 1024.0); + BLI_snprintf(memstr + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_(" (%.2fM)"), (double)((mmap_in_use) >> 10) / 1024.0); + + if (GPU_mem_stats_supported()) { + int gpu_free_mem, gpu_tot_memory; + + GPU_mem_stats_get(&gpu_tot_memory, &gpu_free_mem); + + ofs = BLI_snprintf(gpumemstr, MAX_INFO_MEM_LEN, IFACE_(" | Free GPU Mem:%.2fM"), (double)((gpu_free_mem)) / 1024.0); + + if (gpu_tot_memory) { + BLI_snprintf(gpumemstr + ofs, MAX_INFO_MEM_LEN - ofs, IFACE_("/%.2fM"), (double)((gpu_tot_memory)) / 1024.0); + } + } s = stats->infostr; ofs = 0; @@ -447,22 +461,23 @@ static void stats_string(Scene *scene) } ofs += BLI_strncpy_rlen(s + ofs, memstr, MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(s + ofs, gpumemstr, MAX_INFO_LEN - ofs); } else if (ob && (ob->mode & OB_MODE_POSE)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s"), - stats_fmt.totbonesel, stats_fmt.totbone, memstr); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s%s"), + stats_fmt.totbonesel, stats_fmt.totbone, memstr, gpumemstr); } else if (stats_is_object_dynamic_topology_sculpt(ob)) { - ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s"), stats_fmt.totvert, - stats_fmt.tottri); + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s%s"), stats_fmt.totvert, + stats_fmt.tottri, gpumemstr); } else { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, - IFACE_("Verts:%s | Faces:%s | Tris:%s | Objects:%s/%s | Lamps:%s/%s%s"), + IFACE_("Verts:%s | Faces:%s | Tris:%s | Objects:%s/%s | Lamps:%s/%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); + stats_fmt.totlamp, memstr, gpumemstr); } if (ob) diff --git a/source/blender/editors/space_logic/logic_intern.h b/source/blender/editors/space_logic/logic_intern.h index 38b122f64b6..13146621d1b 100644 --- a/source/blender/editors/space_logic/logic_intern.h +++ b/source/blender/editors/space_logic/logic_intern.h @@ -35,12 +35,8 @@ /* internal exports only */ struct bContext; struct ARegion; -struct ARegionType; struct ScrArea; -struct SpaceLogic; -struct Object; struct wmOperatorType; -struct Scene; /* space_logic.c */ struct ARegion *logic_has_buttons_region(struct ScrArea *sa); diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index 7204144ce85..e78100f3ceb 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -903,7 +903,7 @@ static void draw_sensor_internal_header(uiLayout *layout, PointerRNA *ptr) sub = uiLayoutRow(row, false); uiLayoutSetActive(sub, (RNA_boolean_get(ptr, "use_pulse_true_level") || RNA_boolean_get(ptr, "use_pulse_false_level"))); - uiItemR(sub, ptr, "frequency", 0, IFACE_("Freq"), ICON_NONE); + uiItemR(sub, ptr, "tick_skip", 0, IFACE_("Skip"), ICON_NONE); row = uiLayoutRow(split, true); uiItemR(row, ptr, "use_level", UI_ITEM_R_TOGGLE, NULL, ICON_NONE); diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 66023ce1243..1fb7228dd98 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -159,7 +159,9 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p } /* AnimData pointer */ - RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr); + if (adt_ptr) { + RNA_pointer_create(id, &RNA_AnimData, ale->adt, adt_ptr); + } /* set found status to -1, since setting to 1 would break the loop * and potentially skip an active NLA-Track in some cases... @@ -282,7 +284,7 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) /* Active Action Properties ------------------------------------- */ /* action */ row = uiLayoutRow(layout, true); - uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, NULL /*"ACTION_OT_unlink"*/); // XXX: need to make these operators + uiTemplateID(row, (bContext *)C, &adt_ptr, "action", "ACTION_OT_new", NULL, "NLA_OT_action_unlink"); /* extrapolation */ row = uiLayoutRow(layout, true); diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 5476d1c2683..32a8e660f66 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -291,10 +291,12 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe * the case of users trying to use this to change actions * - in tweakmode, clicking here gets us out of tweakmode, as changing selection * while in tweakmode is really evil! + * - we disable "solo" flags too, to make it easier to work with stashed actions + * with less trouble */ if (nlaedit_is_tweakmode_on(ac)) { /* exit tweakmode immediately */ - nlaedit_disable_tweakmode(ac); + nlaedit_disable_tweakmode(ac, true); /* changes to NLA-Action occurred */ notifierFlags |= ND_NLA_ACTCHANGE; @@ -504,6 +506,52 @@ void NLA_OT_action_pushdown(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } +/* ******************** Action Unlink ******************************** */ + +static int nla_action_unlink_poll(bContext *C) +{ + if (ED_operator_nla_active(C)) { + return nla_panel_context(C, NULL, NULL, NULL); + } + + /* something failed... */ + return false; +} + +static int nla_action_unlink_exec(bContext *C, wmOperator *op) +{ + PointerRNA adt_ptr; + AnimData *adt; + + /* check context and also validity of pointer */ + if (!nla_panel_context(C, &adt_ptr, NULL, NULL)) + return OPERATOR_CANCELLED; + + /* get animdata */ + adt = adt_ptr.data; + if (adt == NULL) + return OPERATOR_CANCELLED; + + /* do unlinking */ + if (adt && adt->action) { + ED_animedit_unlink_action(C, adt_ptr.id.data, adt, adt->action, op->reports); + } + + return OPERATOR_FINISHED; +} + +void NLA_OT_action_unlink(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Unlink Action"; + ot->idname = "NLA_OT_action_unlink"; + ot->description = "Unlink this action from the active action slot (and/or exit Tweak Mode)"; + + /* callbacks */ + ot->exec = nla_action_unlink_exec; + ot->poll = nla_action_unlink_poll; +} + /* ******************** Add Tracks Operator ***************************** */ /* Add NLA Tracks to the same AnimData block as a selected track, or above the selected tracks */ @@ -725,7 +773,7 @@ static int nlaedit_objects_add_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { /* ensure that object has AnimData... that's all */ - BKE_id_add_animdata(&ob->id); + BKE_animdata_add_id(&ob->id); } CTX_DATA_END; diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index aae0e38696c..4d37fcc9276 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -186,7 +186,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col } else { /* Action Clip (default/normal type of strip) */ - if ((strip->flag & NLASTRIP_FLAG_ACTIVE) && (adt && (adt->flag & ADT_NLA_EDIT_ON))) { + if (adt && (adt->flag & ADT_NLA_EDIT_ON) && (adt->actstrip == strip)) { /* active strip should be drawn green when it is acting as the tweaking strip. * however, this case should be skipped for when not in EditMode... */ @@ -433,7 +433,7 @@ static void nla_draw_strip_text(AnimData *adt, NlaTrack *nlt, NlaStrip *strip, i /* just print the name and the range */ if (strip->flag & NLASTRIP_FLAG_TEMP_META) { - str_len = BLI_snprintf(str, sizeof(str), "%d) Temp-Meta", index); + str_len = BLI_snprintf_rlen(str, sizeof(str), "%d) Temp-Meta", index); } else { str_len = BLI_strncpy_rlen(str, strip->name, sizeof(str)); @@ -490,11 +490,11 @@ static void nla_draw_strip_frames_text(NlaTrack *UNUSED(nlt), NlaStrip *strip, V * while also preserving some accuracy, since we do use floats */ /* start frame */ - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.1f", strip->start); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.1f", strip->start); UI_view2d_text_cache_add(v2d, strip->start - 1.0f, ymaxc + ytol, numstr, numstr_len, col); /* end frame */ - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.1f", strip->end); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.1f", strip->end); UI_view2d_text_cache_add(v2d, strip->end, ymaxc + ytol, numstr, numstr_len, col); } @@ -650,6 +650,8 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *ar) /* draw channels */ { /* first pass: just the standard GL-drawing for backdrop + text */ + size_t channel_index = 0; + y = (float)(-NLACHANNEL_HEIGHT(snla)); for (ale = anim_data.first; ale; ale = ale->next) { @@ -661,11 +663,12 @@ void draw_nla_channel_list(const bContext *C, bAnimContext *ac, ARegion *ar) IN_RANGE(ymaxc, v2d->cur.ymin, v2d->cur.ymax) ) { /* draw all channels using standard channel-drawing API */ - ANIM_channel_draw(ac, ale, yminc, ymaxc); + ANIM_channel_draw(ac, ale, yminc, ymaxc, channel_index); } /* adjust y-position for next one */ y -= NLACHANNEL_STEP(snla); + channel_index++; } } { /* second pass: UI widgets */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index a883f350f1c..19e6f5a8100 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -111,6 +111,8 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; + + const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action"); bool ok = false; /* get editor data */ @@ -133,6 +135,15 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) /* try entering tweakmode if valid */ ok |= BKE_nla_tweakmode_enter(adt); + + /* mark the active track as being "solo"? */ + if (do_solo && adt->actstrip) { + NlaTrack *nlt = BKE_nlatrack_find_tweaked(adt); + + if (nlt && !(nlt->flag & NLATRACK_SOLO)) { + BKE_nlatrack_solo_toggle(adt, nlt); + } + } } /* free temp data */ @@ -159,6 +170,8 @@ static int nlaedit_enable_tweakmode_exec(bContext *C, wmOperator *op) void NLA_OT_tweakmode_enter(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Enter Tweak Mode"; ot->idname = "NLA_OT_tweakmode_enter"; @@ -170,16 +183,22 @@ void NLA_OT_tweakmode_enter(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "isolate_action", 0, "Isolate Action", + "Enable 'solo' on the NLA Track containing the active strip, " + "to edit it without seeing the effects of the NLA stack"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* ------------- */ /* NLA Editor internal API function for exiting tweakmode */ -bool nlaedit_disable_tweakmode(bAnimContext *ac) +bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo) { ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; - int filter; + int filter; /* get a list of the AnimData blocks being shown in the NLA */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); @@ -195,7 +214,14 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac) for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ale->data; - /* to be sure, just exit tweakmode... */ + /* clear solo flags */ + if ((do_solo) & (adt->flag & ADT_NLA_SOLO_TRACK) && + (adt->flag & ADT_NLA_EDIT_ON)) + { + BKE_nlatrack_solo_toggle(adt, NULL); + } + + /* to be sure that we're doing everything right, just exit tweakmode... */ BKE_nla_tweakmode_exit(adt); } @@ -218,9 +244,11 @@ bool nlaedit_disable_tweakmode(bAnimContext *ac) } /* exit tweakmode operator callback */ -static int nlaedit_disable_tweakmode_exec(bContext *C, wmOperator *UNUSED(op)) +static int nlaedit_disable_tweakmode_exec(bContext *C, wmOperator *op) { bAnimContext ac; + + const bool do_solo = RNA_boolean_get(op->ptr, "isolate_action"); bool ok = false; /* get editor data */ @@ -228,7 +256,7 @@ static int nlaedit_disable_tweakmode_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; /* perform operation */ - ok = nlaedit_disable_tweakmode(&ac); + ok = nlaedit_disable_tweakmode(&ac, do_solo); /* success? */ if (ok) @@ -239,6 +267,8 @@ static int nlaedit_disable_tweakmode_exec(bContext *C, wmOperator *UNUSED(op)) void NLA_OT_tweakmode_exit(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Exit Tweak Mode"; ot->idname = "NLA_OT_tweakmode_exit"; @@ -250,6 +280,12 @@ void NLA_OT_tweakmode_exit(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "isolate_action", 0, "Isolate Action", + "Disable 'solo' on any of the NLA Tracks after exiting tweak mode " + "to get things back to normal"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } /* *********************************************** */ @@ -382,7 +418,7 @@ static bool nla_channels_get_selected_extents(bAnimContext *ac, float *min, floa y = (float)NLACHANNEL_FIRST; for (ale = anim_data.first; ale; ale = ale->next) { - bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); + const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); /* must be selected... */ if (acf && acf->has_setting(ac, ale, ACHANNEL_SETTING_SELECT) && @@ -2189,7 +2225,7 @@ static int nla_fmodifier_add_invoke(bContext *C, wmOperator *UNUSED(op), const w /* start from 1 to skip the 'Invalid' modifier type */ for (i = 1; i < FMODIFIER_NUM_TYPES; i++) { - FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); + const FModifierTypeInfo *fmi = get_fmodifier_typeinfo(i); /* check if modifier is valid for this context */ if (fmi == NULL) diff --git a/source/blender/editors/space_nla/nla_intern.h b/source/blender/editors/space_nla/nla_intern.h index bd96b5a4de5..344580c0d15 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -83,7 +83,7 @@ enum eNlaEdit_Snap_Mode { /* --- */ -bool nlaedit_disable_tweakmode(bAnimContext *ac); +bool nlaedit_disable_tweakmode(bAnimContext *ac, bool do_solo); void NLA_OT_tweakmode_enter(wmOperatorType *ot); void NLA_OT_tweakmode_exit(wmOperatorType *ot); @@ -137,6 +137,7 @@ bool nlaedit_add_tracks_empty(bAnimContext *ac); void NLA_OT_channels_click(wmOperatorType *ot); void NLA_OT_action_pushdown(wmOperatorType *ot); +void NLA_OT_action_unlink(wmOperatorType *ot); void NLA_OT_tracks_add(wmOperatorType *ot); void NLA_OT_tracks_delete(wmOperatorType *ot); diff --git a/source/blender/editors/space_nla/nla_ops.c b/source/blender/editors/space_nla/nla_ops.c index b3a875047db..98da10470f8 100644 --- a/source/blender/editors/space_nla/nla_ops.c +++ b/source/blender/editors/space_nla/nla_ops.c @@ -114,6 +114,7 @@ void nla_operatortypes(void) WM_operatortype_append(NLA_OT_channels_click); WM_operatortype_append(NLA_OT_action_pushdown); + WM_operatortype_append(NLA_OT_action_unlink); WM_operatortype_append(NLA_OT_tracks_add); WM_operatortype_append(NLA_OT_tracks_delete); @@ -305,6 +306,7 @@ static void nla_keymap_main(wmKeyConfig *keyconf, wmKeyMap *keymap) void nla_keymap(wmKeyConfig *keyconf) { wmKeyMap *keymap; + wmKeyMapItem *kmi; /* keymap for all regions ------------------------------------------- */ keymap = WM_keymap_find(keyconf, "NLA Generic", SPACE_NLA, 0); @@ -319,6 +321,16 @@ 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); + /* tweakmode for stashed actions + * - similar to normal tweakmode, except we mark the tracks as being "solo" + * too so that the action can be edited in isolation + */ + kmi = WM_keymap_add_item(keymap, "NLA_OT_tweakmode_enter", TABKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "isolate_action", true); + + kmi = WM_keymap_add_item(keymap, "NLA_OT_tweakmode_exit", TABKEY, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "isolate_action", true); + /* 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); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 6fa164a483f..7f35884cb65 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -703,7 +703,9 @@ static void node_buts_image_user(uiLayout *layout, bContext *C, PointerRNA *ptr, uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE); } - if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) { + if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && + RNA_boolean_get(ptr, "has_layers")) + { col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE); } @@ -829,7 +831,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA static void node_shader_buts_tex_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); } static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -862,14 +864,15 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P if (!(ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER))) { uiLayout *row = uiLayoutRow(layout, true); + const bool is_packed = BKE_image_has_packedfile(ima); - if (ima->packedfile) + if (is_packed) uiItemO(row, "", ICON_PACKAGE, "image.unpack"); else uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); row = uiLayoutRow(row, true); - uiLayoutSetEnabled(row, ima->packedfile == NULL); + uiLayoutSetEnabled(row, !is_packed); uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); } @@ -1214,6 +1217,24 @@ static void node_shader_set_butfunc(bNodeType *ntype) /* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ +static void node_buts_image_views(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr, + PointerRNA *imaptr) +{ + uiLayout *col; + + if (!imaptr->data) + return; + + col = uiLayoutColumn(layout, false); + + if (RNA_boolean_get(ptr, "has_views")) { + if (RNA_enum_get(ptr, "view") == 0) + uiItemR(col, ptr, "view", 0, NULL, ICON_CAMERA_STEREO); + else + uiItemR(col, ptr, "view", 0, NULL, ICON_SCENE); + } +} + static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { bNode *node = ptr->data; @@ -1227,6 +1248,8 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * imaptr = RNA_pointer_get(ptr, "image"); node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr); + + node_buts_image_views(layout, C, ptr, &imaptr); } static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1236,7 +1259,7 @@ static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRN RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 1); } static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1717,8 +1740,8 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { PointerRNA imfptr = RNA_pointer_get(ptr, "format"); - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); - + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + if (multilayer) uiItemL(layout, IFACE_("Path:"), ICON_NONE); else @@ -1727,15 +1750,22 @@ static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C) } static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { + Scene *scene = CTX_data_scene(C); PointerRNA imfptr = RNA_pointer_get(ptr, "format"); PointerRNA active_input_ptr, op_ptr; uiLayout *row, *col; int active_index; - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; node_composit_buts_file_output(layout, C, ptr); uiTemplateImageSettings(layout, &imfptr, false); + /* disable stereo output for multilayer, too much work for something that no one will use */ + /* if someone asks for that we can implement it */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); + uiItemS(layout); uiItemO(layout, IFACE_("Add Input"), ICON_ZOOMIN, "NODE_OT_output_file_add_socket"); @@ -1797,6 +1827,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == false); uiTemplateImageSettings(col, &imfptr, false); + + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); } } } @@ -2090,6 +2123,18 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "check", 0, NULL, ICON_NONE); } +static void node_composit_buts_switch_view_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) +{ + PointerRNA op_ptr; + wmOperatorType *ot = WM_operatortype_find("NODE_OT_switch_view_update", 1); + + BLI_assert(ot != 0); + + WM_operator_properties_create_ptr(&op_ptr, ot); + + uiItemFullO_ptr(layout, ot, "Update Views", ICON_FILE_REFRESH, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); +} + static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *row; @@ -2587,6 +2632,9 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_SWITCH: ntype->draw_buttons = node_composit_buts_switch; break; + case CMP_NODE_SWITCH_VIEW: + ntype->draw_buttons_ex = node_composit_buts_switch_view_ex; + break; case CMP_NODE_MASK_BOX: ntype->draw_buttons = node_composit_buts_boxmask; ntype->draw_backdrop = node_composit_backdrop_boxmask; @@ -2739,7 +2787,7 @@ static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA PointerRNA iuserptr; RNA_pointer_create((ID *)ptr->id.data, &RNA_ImageUser, node->storage, &iuserptr); - uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0); + uiTemplateImage(layout, C, ptr, "image", &iuserptr, 0, 0); } static void node_texture_buts_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2959,6 +3007,7 @@ static void node_file_output_socket_draw(bContext *C, uiLayout *layout, PointerR imfptr = RNA_pointer_get(node_ptr, "format"); imtype = RNA_enum_get(&imfptr, "file_format"); + if (imtype == R_IMF_IMTYPE_MULTILAYER) { NodeImageMultiFileSocket *input = sock->storage; RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 87a64e95e63..34efba00b86 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -123,7 +123,14 @@ void ED_node_tag_update_id(ID *id) bNodeTree *ntree = node_tree_from_ID(id); if (id == NULL || ntree == NULL) return; - + + /* TODO(sergey): With the new dependency graph it + * should be just enough to only tag ntree itself, + * all the users of this tree will have update + * flushed from the tree, + */ + DAG_id_tag_update(&ntree->id, 0); + if (ntree->type == NTREE_SHADER) { DAG_id_tag_update(id, 0); @@ -163,14 +170,14 @@ void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree) ntreeTexCheckCyclics(ntree); } -static int compare_nodes(bNode *a, bNode *b) +static bool compare_nodes(const bNode *a, const bNode *b) { bNode *parent; /* These tell if either the node or any of the parent nodes is selected. * A selected parent means an unselected node is also in foreground! */ - int a_select = (a->flag & NODE_SELECT), b_select = (b->flag & NODE_SELECT); - int a_active = (a->flag & NODE_ACTIVE), b_active = (b->flag & NODE_ACTIVE); + bool a_select = (a->flag & NODE_SELECT) != 0, b_select = (b->flag & NODE_SELECT) != 0; + bool a_active = (a->flag & NODE_ACTIVE) != 0, b_active = (b->flag & NODE_ACTIVE) != 0; /* if one is an ancestor of the other */ /* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */ diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index f79d6e26f22..ffd51bcc44e 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -93,7 +93,6 @@ typedef struct CompoJob { const short *stop; short *do_update; float *progress; - short need_sync; int recalc_flags; } CompoJob; @@ -162,13 +161,12 @@ static int compo_breakjob(void *cjv) ); } -/* called by compo, wmJob sends notifier, old compositor system only */ -static void compo_statsdrawjob(void *cjv, char *UNUSED(str)) +/* called by compo, wmJob sends notifier */ +static void compo_statsdrawjob(void *cjv, const char *UNUSED(str)) { CompoJob *cj = cjv; *(cj->do_update) = true; - cj->need_sync = true; } /* called by compo, wmJob sends notifier */ @@ -202,17 +200,8 @@ static void compo_initjob(void *cjv) } /* called before redraw notifiers, it moves finished previews over */ -static void compo_updatejob(void *cjv) +static void compo_updatejob(void *UNUSED(cjv)) { - CompoJob *cj = cjv; - - if (cj->need_sync) { - /* was used by old compositor system only */ - ntreeLocalSync(cj->localtree, cj->ntree); - - cj->need_sync = false; - } - WM_main_add_notifier(NC_SCENE | ND_COMPO_RESULT, NULL); } @@ -223,13 +212,13 @@ static void compo_progressjob(void *cjv, float progress) *(cj->progress) = progress; } - /* only this runs inside thread */ static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress) { CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; Scene *scene = cj->scene; + SceneRenderView *srv; if (scene->use_nodes == false) return; @@ -249,7 +238,16 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog // XXX BIF_store_spare(); /* 1 is do_previews */ - ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings); + + if ((cj->scene->r.scemode & R_MULTIVIEW) == 0) { + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, ""); + } + else { + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) continue; + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, srv->name); + } + } ntree->test_break = NULL; ntree->stats_draw = NULL; @@ -742,6 +740,34 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) } } +void ED_node_id_unref(SpaceNode *snode, const ID *id) +{ + if (GS(id->name) == ID_SCE) { + if (snode->id == id) { + /* nasty DNA logic for SpaceNode: + * ideally should be handled by editor code, but would be bad level call + */ + bNodeTreePath *path, *path_next; + for (path = snode->treepath.first; path; path = path_next) { + path_next = path->next; + MEM_freeN(path); + } + BLI_listbase_clear(&snode->treepath); + + snode->id = NULL; + snode->from = NULL; + snode->nodetree = NULL; + snode->edittree = NULL; + } + } + else if (GS(id->name) == ID_OB) { + if (snode->from == id) { + snode->flag &= ~SNODE_PIN; + snode->from = NULL; + } + } +} + void ED_node_post_apply_transform(bContext *UNUSED(C), bNodeTree *UNUSED(ntree)) { /* XXX This does not work due to layout functions relying on node->block, @@ -1671,6 +1697,54 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** Switch View ******************* */ + +static int node_switch_view_poll(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (snode && snode->edittree) + return true; + + return false; +} + +static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node, *next; + + for (node = snode->edittree->nodes.first; node; node = next) { + next = node->next; + if (node->flag & SELECT) { + /* call the update function from the Switch View node */ + node->update = NODE_UPDATE_OPERATOR; + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_switch_view_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Views"; + ot->description = "Update views of selected node"; + ot->idname = "NODE_OT_switch_view_update"; + + /* api callbacks */ + ot->exec = node_switch_view_exec; + ot->poll = node_switch_view_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ****************** Delete with reconnect ******************* */ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c index b69808d4e81..f1d9d4efcc6 100644 --- a/source/blender/editors/space_node/node_group.c +++ b/source/blender/editors/space_node/node_group.c @@ -456,8 +456,8 @@ static int node_group_separate_selected(bNodeTree *ntree, bNodeTree *ngroup, flo /* add internal links to the ntree */ for (link = ngroup->links.first; link; link = link_next) { - int fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT)); - int toselect = (link->tonode && (link->tonode->flag & NODE_SELECT)); + const bool fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT)); + const bool toselect = (link->tonode && (link->tonode->flag & NODE_SELECT)); link_next = link->next; if (make_copy) { diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 27c3ab813ae..b15e9025a82 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -42,12 +42,9 @@ struct ARegionType; struct View2D; struct bContext; struct wmWindow; -struct wmWindowManager; -struct wmEvent; struct bNode; struct bNodeSocket; struct bNodeLink; -struct Main; struct wmKeyConfig; /* temp data to pass on to modal */ @@ -203,6 +200,8 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot); void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot); void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot); +void NODE_OT_switch_view_update (struct wmOperatorType *ot); + /* Note: clipboard_cut is a simple macro of copy + delete */ void NODE_OT_clipboard_copy(struct wmOperatorType *ot); void NODE_OT_clipboard_paste(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index e2d83c243a2..474ad4db4af 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -123,6 +123,8 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_viewer_border); WM_operatortype_append(NODE_OT_clear_viewer_border); + WM_operatortype_append(NODE_OT_switch_view_update); + WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_remove); WM_operatortype_append(NODE_OT_tree_socket_move); diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 25f9d56c8f0..78302fedd97 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -249,9 +249,9 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo bool changed = false; const unsigned int delims[] = {'.', '-', '_', '\0'}; size_t pref_len_act, pref_len_curr; - char *sep, *suf_act, *suf_curr; + const char *sep, *suf_act, *suf_curr; - pref_len_act = BLI_str_partition_ex_utf8(node_act->name, delims, &sep, &suf_act, from_right); + pref_len_act = BLI_str_partition_ex_utf8(node_act->name, NULL, 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)) { @@ -263,7 +263,7 @@ static bool node_select_grouped_name(SpaceNode *snode, bNode *node_act, const bo if (node->flag & SELECT) { continue; } - pref_len_curr = BLI_str_partition_ex_utf8(node->name, delims, &sep, &suf_curr, from_right); + pref_len_curr = BLI_str_partition_ex_utf8(node->name, NULL, delims, &sep, &suf_curr, from_right); /* Same as with active node name! */ if (from_right && !(sep && suf_curr)) { diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 8b68ac013c2..a7fd624d2aa 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -24,6 +24,7 @@ * \ingroup edinterface */ +#include <stdlib.h> #include <string.h> #include "MEM_guardedalloc.h" @@ -214,8 +215,22 @@ static void node_socket_add_replace(const bContext *C, bNodeTree *ntree, bNode * } else if (!node_from) { node_from = nodeAddStaticNode(C, ntree, type); - node_from->locx = node_to->locx - (node_from->typeinfo->width + 50); - node_from->locy = node_to->locy; + if (node_prev != NULL) { + /* If we're replacing existing node, use it's location. */ + node_from->locx = node_prev->locx; + node_from->locy = node_prev->locy; + node_from->offsetx = node_prev->offsetx; + node_from->offsety = node_prev->offsety; + } + else { + /* Avoid exact intersection of nodes. + * TODO(sergey): Still not ideal, but better than nothing. + */ + int index = BLI_findindex(&node_to->inputs, sock_to); + BLI_assert(index != -1); + node_from->locx = node_to->locx - (node_from->typeinfo->width + 50); + node_from->locy = node_to->locy - (node_from->typeinfo->height * index); + } node_link_item_apply(node_from, item); } diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index 3491ecc86af..8c5d2d82468 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -292,7 +292,7 @@ void NODE_OT_backimage_move(wmOperatorType *ot) ot->cancel = snode_bg_viewmove_cancel; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; } static int backimage_zoom_exec(bContext *C, wmOperator *op) @@ -609,8 +609,11 @@ static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: // XXX hardcoded - sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: sample_apply(C, op, event); break; diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 62cb0bc73cd..de90c417bdd 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -203,12 +203,10 @@ void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_lengt value[0] = '\0'; for (path = snode->treepath.first, i = 0; path; path = path->next, ++i) { if (i == 0) { - BLI_strncpy(value, path->node_name, max_length); - size = strlen(path->node_name); + size = BLI_strncpy_rlen(value, path->node_name, max_length); } else { - BLI_snprintf(value, max_length, "/%s", path->node_name); - size = strlen(path->node_name) + 1; + size = BLI_snprintf_rlen(value, max_length, "/%s", path->node_name); } max_length -= size; if (max_length <= 0) diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 2c61e69d611..420b73cee86 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -198,11 +198,11 @@ static void restrictbutton_recursive_child(bContext *C, Scene *scene, Object *ob ID *id; bAction *action; FCurve *fcu; - bool driven; + bool driven, special; RNA_id_pointer_create(&ob->id, &ptr); prop = RNA_struct_find_property(&ptr, rnapropname); - fcu = rna_get_fcurve_context_ui(C, &ptr, prop, 0, NULL, &action, &driven); + fcu = rna_get_fcurve_context_ui(C, &ptr, prop, 0, NULL, &action, &driven, &special); if (fcu && !driven) { id = ptr.id.data; @@ -290,8 +290,7 @@ static void restrictbutton_modifier_cb(bContext *C, void *UNUSED(poin), void *po Object *ob = (Object *)poin2; 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_MODIFIER, ob); } static void restrictbutton_bone_visibility_cb(bContext *C, void *poin, void *poin2) @@ -1122,6 +1121,7 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto UI_icon_draw(x, y, ICON_MOD_BEVEL); break; case eModifierType_Smooth: case eModifierType_LaplacianSmooth: + case eModifierType_CorrectiveSmooth: UI_icon_draw(x, y, ICON_MOD_SMOOTH); break; case eModifierType_SimpleDeform: UI_icon_draw(x, y, ICON_MOD_SIMPLEDEFORM); break; diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 798dae2cef3..0821304abf3 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -39,20 +39,24 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_mempool.h" #include "BLF_translation.h" #include "BKE_animsys.h" #include "BKE_context.h" #include "BKE_depsgraph.h" +#include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_outliner_treehash.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_material.h" #include "BKE_group.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_screen.h" #include "ED_keyframing.h" @@ -785,7 +789,7 @@ static void outliner_find_panel(Scene *UNUSED(scene), ARegion *ar, SpaceOops *so else { /* pop up panel - no previous, or user didn't want search after previous */ name[0] = '\0'; -// XXX if (sbutton(name, 0, sizeof(name)-1, "Find: ") && name[0]) { +// XXX if (sbutton(name, 0, sizeof(name) - 1, "Find: ") && name[0]) { // te = outliner_find_name(soops, &soops->tree, name, flags, NULL, &prevFound); // } // else return; /* XXX RETURN! XXX */ @@ -1952,3 +1956,36 @@ void OUTLINER_OT_group_link(wmOperatorType *ot) /* properties */ RNA_def_string(ot->srna, "object", "Object", MAX_ID_NAME, "Object", "Target Object"); } + +/******** Utils to clear any ref to freed ID... **********/ + +void ED_outliner_id_unref(SpaceOops *so, const ID *id) +{ + /* Some early out checks. */ + if (!TREESTORE_ID_TYPE(id)) { + return; /* ID type is not used by outilner... */ + } + + if (so->search_tse.id == id) { + so->search_tse.id = NULL; + } + + if (so->treestore) { + TreeStoreElem *tselem; + BLI_mempool_iter iter; + bool changed = false; + + BLI_mempool_iternew(so->treestore, &iter); + while ((tselem = BLI_mempool_iterstep(&iter))) { + if (tselem->id == id) { + tselem->id = NULL; + changed = true; + } + } + if (so->treehash && changed) { + /* rebuild hash table, because it depends on ids too */ + /* postpone a full rebuild because this can be called many times on-free */ + so->storeflag |= SO_TREESTORE_REBUILD; + } + } +} diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 50fecebb742..24842d20573 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -36,12 +36,10 @@ /* internal exports only */ -struct wmWindowManager; struct wmOperatorType; struct TreeStoreElem; struct bContext; struct Scene; -struct ARegion; struct ID; struct Object; @@ -59,6 +57,11 @@ typedef struct TreeElement { PointerRNA rnaptr; // RNA Pointer } TreeElement; +#define TREESTORE_ID_TYPE(_id) \ + (ELEM(GS((_id)->name), ID_SCE, ID_LI, ID_OB, ID_ME, ID_CU, ID_MB, ID_MA, ID_TE, ID_IM, ID_LT, ID_LA, ID_CA) || \ + ELEM(GS((_id)->name), ID_KE, ID_WO, ID_SPK, ID_GR, ID_AR, ID_AC, ID_BR, ID_PA, ID_GD, ID_LS) || \ + ELEM(GS((_id)->name), ID_SCR, ID_WM)) /* Only in 'blendfile' mode ... :/ */ + /* TreeElement->flag */ #define TE_ACTIVE 1 #define TE_ICONROW 2 diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 730ee02f448..e52c68b57e9 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -554,10 +554,12 @@ static eOLDrawState tree_element_active_bone( Object *ob = OBACT; if (ob) { if (set != OL_SETSEL_EXTEND) { - bPoseChannel *pchannel; /* single select forces all other bones to get unselected */ - for (pchannel = ob->pose->chanbase.first; pchannel; pchannel = pchannel->next) - pchannel->bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + Bone *bone; + for (bone = arm->bonebase.first; bone != NULL; bone = bone->next) { + bone->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); + do_outliner_bone_select_recursive(arm, bone, false); + } } } @@ -618,7 +620,7 @@ static eOLDrawState tree_element_active_ebone( if (set != OL_SETSEL_NONE) { if (set == OL_SETSEL_NORMAL) { if (!(ebone->flag & BONE_HIDDEN_A)) { - ED_armature_deselect_all(scene->obedit, 0); // deselect + ED_armature_deselect_all(scene->obedit); tree_element_active_ebone__sel(C, scene, arm, ebone, true); status = OL_DRAWSEL_NORMAL; } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 64e00589712..66863b8061b 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -427,8 +427,7 @@ static void group_instance_cb(bContext *C, Scene *scene, TreeElement *UNUSED(te) { Group *group = (Group *)tselem->id; - Object *ob = ED_object_add_type(C, OB_EMPTY, scene->cursor, NULL, false, scene->layact); - rename_id(&ob->id, group->id.name + 2); + Object *ob = ED_object_add_type(C, OB_EMPTY, group->id.name + 2, scene->cursor, NULL, false, scene->layact); ob->dup_group = group; ob->transflag |= OB_DUPLIGROUP; id_lib_extern(&group->id); @@ -467,7 +466,7 @@ 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); + BKE_animdata_free(tselem->id); } @@ -694,23 +693,26 @@ static void outliner_do_data_operation(SpaceOops *soops, int type, int event, Li } } -static void outline_delete_hierarchy(bContext *C, Scene *scene, Base *base) +static Base *outline_delete_hierarchy(bContext *C, Scene *scene, Base *base) { - Base *child_base; + Base *child_base, *base_next; Object *parent; if (!base) { - return; + return NULL; } - for (child_base = scene->base.first; child_base; child_base = child_base->next) { + for (child_base = scene->base.first; child_base; child_base = base_next) { + base_next = child_base->next; for (parent = child_base->object->parent; parent && (parent != base->object); parent = parent->parent); if (parent) { - outline_delete_hierarchy(C, scene, child_base); + base_next = outline_delete_hierarchy(C, scene, child_base); } } + base_next = base->next; ED_base_object_free_and_unlink(CTX_data_main(C), scene, base); + return base_next; } static void object_delete_hierarchy_cb( @@ -943,16 +945,15 @@ static int outliner_group_operation_exec(bContext *C, wmOperator *op) default: BLI_assert(0); } - if (event == 3) { /* instance */ /* works without this except if you try render right after, see: 22027 */ DAG_relations_tag_update(CTX_data_main(C)); } - - ED_undo_push(C, prop_group_op_types[event].name); + + ED_undo_push(C, prop_group_op_types[event - 1].name); WM_event_add_notifier(C, NC_GROUP, NULL); - + return OPERATOR_FINISHED; } @@ -1652,9 +1653,14 @@ static int outliner_operation(bContext *C, wmOperator *UNUSED(op), const wmEvent Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); SpaceOops *soops = CTX_wm_space_outliner(C); + uiBut *but = UI_context_active_but_get(C); TreeElement *te; float fmval[2]; + if (but) { + UI_but_tooltip_timer_remove(C, but); + } + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fmval[0], &fmval[1]); for (te = soops->tree.first; te; te = te->next) { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 7aac6a7797c..d4bef06cca9 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -66,7 +66,7 @@ #include "BKE_modifier.h" #include "BKE_sequencer.h" #include "BKE_idcode.h" -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "ED_armature.h" #include "ED_screen.h" @@ -104,6 +104,8 @@ static void outliner_storage_cleanup(SpaceOops *soops) /* cleanup only after reading file or undo step, and always for * RNA datablocks view in order to save memory */ if (soops->storeflag & SO_TREESTORE_CLEANUP) { + soops->storeflag &= ~SO_TREESTORE_CLEANUP; + BLI_mempool_iternew(ts, &iter); while ((tselem = BLI_mempool_iterstep(&iter))) { if (tselem->id == NULL) unused++; @@ -114,7 +116,7 @@ static void outliner_storage_cleanup(SpaceOops *soops) BLI_mempool_destroy(ts); soops->treestore = NULL; if (soops->treehash) { - BKE_treehash_free(soops->treehash); + BKE_outliner_treehash_free(soops->treehash); soops->treehash = NULL; } } @@ -133,7 +135,7 @@ static void outliner_storage_cleanup(SpaceOops *soops) soops->treestore = new_ts; if (soops->treehash) { /* update hash table to fix broken pointers */ - BKE_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); + BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); } } } @@ -151,12 +153,12 @@ static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short ty } if (soops->treehash == NULL) { - soops->treehash = BKE_treehash_create_from_treestore(soops->treestore); + soops->treehash = BKE_outliner_treehash_create_from_treestore(soops->treestore); } /* find any unused tree element in treestore and mark it as used * (note that there may be multiple unused elements in case of linked objects) */ - tselem = BKE_treehash_lookup_unused(soops->treehash, type, nr, id); + tselem = BKE_outliner_treehash_lookup_unused(soops->treehash, type, nr, id); if (tselem) { te->store_elem = tselem; tselem->used = 1; @@ -171,7 +173,7 @@ static void check_persistent(SpaceOops *soops, TreeElement *te, ID *id, short ty tselem->used = 0; tselem->flag = TSE_CLOSED; te->store_elem = tselem; - BKE_treehash_add_element(soops->treehash, tselem); + BKE_outliner_treehash_add_element(soops->treehash, tselem); } /* ********************************************************* */ @@ -216,7 +218,7 @@ TreeElement *outliner_find_tse(SpaceOops *soops, TreeStoreElem *tse) if (tse->id == NULL) return NULL; /* check if 'tse' is in treestore */ - tselem = BKE_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); + tselem = BKE_outliner_treehash_lookup_any(soops->treehash, tse->type, tse->nr, tse->id); if (tselem) return outliner_find_tree_element(&soops->tree, tselem); @@ -769,16 +771,16 @@ static void outliner_add_id_contents(SpaceOops *soops, TreeElement *te, TreeStor ten = outliner_add_element(soops, &te->subtree, id, te, TSE_EBONE, a); ten->directdata = ebone; ten->name = ebone->name; - ebone->temp = ten; + ebone->temp.p = ten; } /* make hierarchy */ - ten = arm->edbo->first ? ((EditBone *)arm->edbo->first)->temp : NULL; + ten = arm->edbo->first ? ((EditBone *)arm->edbo->first)->temp.p : NULL; while (ten) { TreeElement *nten = ten->next, *par; ebone = (EditBone *)ten->directdata; if (ebone->parent) { BLI_remlink(&te->subtree, ten); - par = ebone->parent->temp; + par = ebone->parent->temp.p; BLI_addtail(&par->subtree, ten); ten->parent = par; } @@ -855,6 +857,11 @@ static TreeElement *outliner_add_element(SpaceOops *soops, ListBase *lb, void *i return NULL; } + if (type == 0) { + /* Zero type means real ID, ensure we do not get non-outliner ID types here... */ + BLI_assert(TREESTORE_ID_TYPE(id)); + } + te = MEM_callocN(sizeof(TreeElement), "tree elem"); /* add to the visual tree */ BLI_addtail(lb, te); @@ -1573,6 +1580,11 @@ void outliner_build_tree(Main *mainvar, Scene *scene, SpaceOops *soops) else soops->search_flags &= ~SO_SEARCH_RECURSIVE; + if (soops->treehash && (soops->storeflag & SO_TREESTORE_REBUILD)) { + soops->storeflag &= ~SO_TREESTORE_REBUILD; + BKE_outliner_treehash_rebuild_from_treestore(soops->treehash, soops->treestore); + } + if (soops->tree.first && (soops->storeflag & SO_TREESTORE_REDRAW)) return; diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index e1aea8892b5..7b1ec174a6b 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -41,7 +41,7 @@ #include "BKE_context.h" #include "BKE_screen.h" #include "BKE_scene.h" -#include "BKE_treehash.h" +#include "BKE_outliner_treehash.h" #include "ED_space_api.h" #include "ED_screen.h" @@ -464,7 +464,7 @@ static void outliner_free(SpaceLink *sl) BLI_mempool_destroy(soutliner->treestore); } if (soutliner->treehash) { - BKE_treehash_free(soutliner->treehash); + BKE_outliner_treehash_free(soutliner->treehash); } } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 38a01bcd78c..86d1fe71ade 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -30,6 +30,7 @@ #include <stdlib.h> #include <math.h> #include <string.h> +#include <ctype.h> #include "MEM_guardedalloc.h" @@ -60,6 +61,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" +#include "UI_interface.h" #include "BKE_sound.h" @@ -70,6 +72,10 @@ /* own include */ #include "sequencer_intern.h" +typedef struct SequencerAddData { + ImageFormatData im_format; +} SequencerAddData; + /* Generic functions, reused by add strip operators */ /* avoid passing multiple args and be more verbose */ @@ -222,6 +228,19 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, wmOperator *op) } RNA_PROP_END; } + + if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) && RNA_property_boolean_get(op->ptr, prop)) { + if (op->customdata) { + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + + seq_load->views_format = imf->views_format; + seq_load->flag |= SEQ_USE_VIEWS; + + /* operator custom data is always released after the SeqLoadInfo, no need to handle the memory here */ + seq_load->stereo3d_format = &imf->stereo3d_format; + } + } } /** @@ -287,7 +306,7 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) BLI_strncpy(seq->name + 2, sce_seq->id.name + 2, sizeof(seq->name) - 2); BKE_sequence_base_unique_name_recursive(&ed->seqbase, seq); - seq->scene_sound = sound_scene_add_scene_sound(scene, seq, start_frame, start_frame + seq->len, 0); + seq->scene_sound = BKE_sound_scene_add_scene_sound(scene, seq, start_frame, start_frame + seq->len, 0); BKE_sequence_calc_disp(scene, seq); BKE_sequencer_sort(scene); @@ -573,6 +592,9 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_CANCELLED; } + if (op->customdata) + MEM_freeN(op->customdata); + BKE_sequencer_sort(scene); BKE_sequencer_update_muting(ed); @@ -581,15 +603,40 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_FINISHED; } +/* add sequencer operators */ +static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) +{ + op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__); +} + +static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) +{ + if (op->customdata) + MEM_freeN(op->customdata); + op->customdata = NULL; +} + +static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + /* add movie operator */ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) { return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_movie_strip); } - static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* This is for drag and drop */ if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) @@ -599,13 +646,37 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const w } sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE); - + + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; //return sequencer_add_movie_strip_exec(C, op); } +static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) { @@ -618,9 +689,11 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_movie_strip_invoke; ot->exec = sequencer_add_movie_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -679,11 +752,74 @@ void SEQUENCER_OT_sound_strip_add(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "cache", false, "Cache", "Cache the sound in memory"); } +int sequencer_image_seq_get_minmax_frame(wmOperator *op, int sfra, int *r_minframe, int *r_numdigits) +{ + int minframe = INT32_MAX, maxframe = INT32_MIN; + int numdigits = 0; + + RNA_BEGIN (op->ptr, itemptr, "files") + { + char *filename; + int frame; + /* just get the first filename */ + filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + + if (filename) { + if (BLI_path_frame_get(filename, &frame, &numdigits)) { + minframe = min_ii(minframe, frame); + maxframe = max_ii(maxframe, frame); + } + + MEM_freeN(filename); + } + } + RNA_END; + + if (minframe == INT32_MAX) { + minframe = sfra; + maxframe = minframe + 1; + } + + *r_minframe = minframe; + *r_numdigits = numdigits; + + return maxframe - minframe + 1; +} + +void sequencer_image_seq_reserve_frames(wmOperator *op, StripElem *se, int len, int minframe, int numdigits) +{ + int i; + char *filename = NULL; + RNA_BEGIN (op->ptr, itemptr, "files") + { + /* just get the first filename */ + filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + break; + } + RNA_END; + + if (filename) { + char ext[PATH_MAX]; + char filename_stripped[PATH_MAX]; + /* strip the frame from filename and substitute with # */ + BLI_path_frame_strip(filename, true, ext); + + for (i = 0; i < len; i++, se++) { + BLI_strncpy(filename_stripped, filename, sizeof(filename_stripped)); + BLI_path_frame(filename_stripped, minframe + i, numdigits); + BLI_snprintf(se->name, sizeof(se->name), "%s%s", filename_stripped, ext); + } + + MEM_freeN(filename); + } +} + + /* add image operator */ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) { + int minframe, numdigits; /* cant use the generic function for this */ - Scene *scene = CTX_data_scene(C); /* only for sound */ Editing *ed = BKE_sequencer_editing_get(scene, true); SeqLoadInfo seq_load; @@ -691,11 +827,17 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) Strip *strip; StripElem *se; + const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); seq_load_operator_info(&seq_load, op); /* images are unique in how they handle this - 1 per strip elem */ - seq_load.len = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); + if (use_placeholders) { + seq_load.len = sequencer_image_seq_get_minmax_frame(op, seq_load.start_frame, &minframe, &numdigits); + } + else { + seq_load.len = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); + } if (seq_load.len == 0) return OPERATOR_CANCELLED; @@ -703,20 +845,24 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) if (seq_load.flag & SEQ_LOAD_REPLACE_SEL) ED_sequencer_deselect_all(scene); - /* main adding function */ seq = BKE_sequencer_add_image_strip(C, ed->seqbasep, &seq_load); strip = seq->strip; se = strip->stripdata; - RNA_BEGIN (op->ptr, itemptr, "files") - { - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); - BLI_strncpy(se->name, filename, sizeof(se->name)); - MEM_freeN(filename); - se++; + if (use_placeholders) { + sequencer_image_seq_reserve_frames(op, se, seq_load.len, minframe, numdigits); + } + else { + RNA_BEGIN (op->ptr, itemptr, "files") + { + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + BLI_strncpy(se->name, filename, sizeof(se->name)); + MEM_freeN(filename); + se++; + } + RNA_END; } - RNA_END; if (seq_load.len == 1) { if (seq_load.start_frame < seq_load.end_frame) { @@ -735,6 +881,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) sequencer_add_apply_overlap(C, op, seq); + if (op->customdata) + MEM_freeN(op->customdata); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -742,6 +891,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* drag drop has set the names */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE); @@ -750,6 +902,12 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const w sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE); + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; } @@ -766,6 +924,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_image_strip_invoke; ot->exec = sequencer_add_image_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; @@ -775,6 +935,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY); sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME); + + RNA_def_boolean(ot->srna, "use_placeholders", false, "Use Placeholders", "Use placeholders for missing frames of the strip"); } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b63c46cba01..27eb28bec8f 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -50,6 +50,7 @@ #include "BKE_main.h" #include "BKE_sequencer.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -62,6 +63,7 @@ #include "ED_markers.h" #include "ED_mask.h" #include "ED_sequencer.h" +#include "ED_screen.h" #include "ED_space_api.h" #include "UI_interface.h" @@ -424,12 +426,12 @@ static void draw_seq_handle(View2D *v2d, Sequence *seq, const float handsize_cla size_t numstr_len; if (direction == SEQ_LEFTHANDLE) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", seq->startdisp); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->startdisp); x1 = rx1; y1 -= 0.45f; } else { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", seq->enddisp - 1); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", seq->enddisp - 1); x1 = x2 - handsize_clamped * 0.75f; y1 = y2 + 0.05f; } @@ -860,7 +862,7 @@ void ED_sequencer_special_preview_clear(void) sequencer_special_update_set(NULL); } -ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs) +ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname) { SeqRenderData context; ImBuf *ibuf; @@ -888,6 +890,7 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int bmain->eval_ctx, bmain, scene, rectx, recty, proxy_size, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled * by Esc pressed somewhere in the past @@ -978,6 +981,76 @@ static void sequencer_display_size(Scene *scene, SpaceSeq *sseq, float r_viewrec } } +static void sequencer_draw_gpencil(const bContext *C) +{ + /* draw grease-pencil (image aligned) */ + ED_gpencil_draw_2dimage(C); + + /* ortho at pixel level */ + UI_view2d_view_restore(C); + + /* draw grease-pencil (screen aligned) */ + ED_gpencil_draw_view2d(C, 0); +} + +/* draws content borders plus safety borders if needed */ +static void sequencer_draw_borders(const SpaceSeq *sseq, const View2D *v2d, const Scene *scene) +{ + float x1 = v2d->tot.xmin; + float y1 = v2d->tot.ymin; + float x2 = v2d->tot.xmax; + float y2 = v2d->tot.ymax; + + /* border */ + setlinestyle(3); + + UI_ThemeColorBlendShade(TH_WIRE, TH_BACK, 1.0, 0); + + glBegin(GL_LINE_LOOP); + glVertex2f(x1 - 0.5f, y1 - 0.5f); + glVertex2f(x1 - 0.5f, y2 + 0.5f); + glVertex2f(x2 + 0.5f, y2 + 0.5f); + glVertex2f(x2 + 0.5f, y1 - 0.5f); + glEnd(); + + /* safety border */ + if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title, + scene->safe_areas.action); + + if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { + UI_draw_safe_areas( + x1, x2, y1, y2, + scene->safe_areas.title_center, + scene->safe_areas.action_center); + } + } + + setlinestyle(0); +} + +/* draws checkerboard background for transparent content */ +static void sequencer_draw_background(const SpaceSeq *sseq, View2D *v2d, const float viewrect[2]) +{ + /* setting up the view */ + UI_view2d_totRect_set(v2d, viewrect[0] + 0.5f, viewrect[1] + 0.5f); + UI_view2d_curRect_validate(v2d); + UI_view2d_view_ortho(v2d); + + /* only draw alpha for main buffer */ + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + if (sseq->flag & SEQ_USE_ALPHA) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + fdrawcheckerboard(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax); + glColor4f(1.0, 1.0, 1.0, 1.0); + } + } +} + void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq, int cfra, int frame_ofs, bool draw_overlay, bool draw_backdrop) { struct Main *bmain = CTX_data_main(C); @@ -994,6 +1067,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq const bool is_imbuf = ED_space_sequencer_check_show_imbuf(sseq); int format, type; bool glsl_used = false; + const bool draw_gpencil = ((sseq->flag & SEQ_SHOW_GPENCIL) && sseq->gpd); + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + bool draw_metadata = false; if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) { /* stop all running jobs, except screen one. currently previews frustrate Render @@ -1015,6 +1091,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq glClear(GL_COLOR_BUFFER_BIT); } + /* without this colors can flicker from previous opengl state */ + glColor4ub(255, 255, 255, 255); + /* only initialize the preview if a render is in progress */ if (G.is_rendering) return; @@ -1023,17 +1102,27 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq return; } - ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs); - - if (ibuf == NULL) - return; + /* for now we only support Left/Right */ + ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); + + if ((ibuf == NULL) || + (ibuf->rect == NULL && ibuf->rect_float == NULL)) + { + /* gpencil can also be drawn without a valid imbuf */ + if (draw_gpencil && is_imbuf) { + sequencer_display_size(scene, sseq, viewrect); + + sequencer_draw_background(sseq, v2d, viewrect); + sequencer_draw_borders(sseq, v2d, scene); - if (ibuf->rect == NULL && ibuf->rect_float == NULL) + sequencer_draw_gpencil(C); + } return; + } sequencer_display_size(scene, sseq, viewrect); - if (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0) { + if (!draw_backdrop && (sseq->mainb != SEQ_DRAW_IMG_IMBUF || sseq->zebra != 0)) { SequencerScopes *scopes = &sseq->scopes; sequencer_check_scopes(scopes, ibuf); @@ -1079,36 +1168,23 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq /* future files may have new scopes we don't catch above */ if (scope) { scopes->reference_ibuf = ibuf; - viewrect[0] = scope->x; - viewrect[1] = scope->y; + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + /* scopes drawn in image preview use viewrect from orig ibuf - currently that's only zebra */ + } + else { + viewrect[0] = scope->x; + viewrect[1] = scope->y; + } } else { scopes->reference_ibuf = NULL; } } - /* without this colors can flicker from previous opengl state */ - glColor4ub(255, 255, 255, 255); - if (!draw_backdrop) { - UI_view2d_totRect_set(v2d, viewrect[0] + 0.5f, viewrect[1] + 0.5f); - UI_view2d_curRect_validate(v2d); - - /* setting up the view - actual drawing starts here */ - UI_view2d_view_ortho(v2d); - - /* only draw alpha for main buffer */ - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { - if (sseq->flag & SEQ_USE_ALPHA) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - fdrawcheckerboard(v2d->tot.xmin, v2d->tot.ymin, v2d->tot.xmax, v2d->tot.ymax); - glColor4f(1.0, 1.0, 1.0, 1.0); - } - } + sequencer_draw_background(sseq, v2d, viewrect); } - + if (scope) { IMB_freeImBuf(ibuf); ibuf = scope; @@ -1229,10 +1305,12 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq } } else if (draw_backdrop) { - float aspect = BLI_rcti_size_x(&ar->winrct) / (float)BLI_rcti_size_y(&ar->winrct); + float aspect; float image_aspect = viewrect[0] / viewrect[1]; float imagex, imagey; - + + aspect = BLI_rcti_size_x(&ar->winrct) / (float)BLI_rcti_size_y(&ar->winrct); + if (aspect >= image_aspect) { imagex = image_aspect / aspect; imagey = 1.0f; @@ -1241,28 +1319,22 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq imagex = 1.0f; imagey = aspect / image_aspect; } - + glTexCoord2f(0.0f, 0.0f); glVertex2f(-imagex, -imagey); glTexCoord2f(0.0f, 1.0f); glVertex2f(-imagex, imagey); glTexCoord2f(1.0f, 1.0f); glVertex2f(imagex, imagey); glTexCoord2f(1.0f, 0.0f); glVertex2f(imagex, -imagey); } else { + draw_metadata = ((sseq->flag & SEQ_SHOW_METADATA) != 0); + glTexCoord2f(0.0f, 0.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymin); glTexCoord2f(0.0f, 1.0f); glVertex2f(v2d->tot.xmin, v2d->tot.ymax); glTexCoord2f(1.0f, 1.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymax); glTexCoord2f(1.0f, 0.0f); glVertex2f(v2d->tot.xmax, v2d->tot.ymin); } glEnd(); - - if (draw_backdrop) { - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glMatrixMode(GL_MODELVIEW); - - } - + glBindTexture(GL_TEXTURE_2D, last_texid); glDisable(GL_TEXTURE_2D); if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) @@ -1277,63 +1349,29 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq if (!scope) IMB_freeImBuf(ibuf); - + + if (draw_metadata) { + ED_region_image_metadata_draw(0.0, 0.0, ibuf, v2d->tot, 1.0, 1.0); + } + if (draw_backdrop) { + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); return; } - - if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { - - float x1 = v2d->tot.xmin; - float y1 = v2d->tot.ymin; - float x2 = v2d->tot.xmax; - float y2 = v2d->tot.ymax; - - /* border */ - setlinestyle(3); - - UI_ThemeColorBlendShade(TH_WIRE, TH_BACK, 1.0, 0); - - glBegin(GL_LINE_LOOP); - glVertex2f(x1 - 0.5f, y1 - 0.5f); - glVertex2f(x1 - 0.5f, y2 + 0.5f); - glVertex2f(x2 + 0.5f, y2 + 0.5f); - glVertex2f(x2 + 0.5f, y1 - 0.5f); - glEnd(); - - /* safety border */ - if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { - UI_draw_safe_areas( - x1, x2, y1, y2, - scene->safe_areas.title, - scene->safe_areas.action); - - if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { - UI_draw_safe_areas( - x1, x2, y1, y2, - scene->safe_areas.title_center, - scene->safe_areas.action_center); - } - } - setlinestyle(0); + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF) { + sequencer_draw_borders(sseq, v2d, scene); } - - if (sseq->flag & SEQ_SHOW_GPENCIL) { - if (is_imbuf) { - /* draw grease-pencil (image aligned) */ - ED_gpencil_draw_2dimage(C); - } + + if (draw_gpencil && is_imbuf) { + sequencer_draw_gpencil(C); } - - /* ortho at pixel level */ - UI_view2d_view_restore(C); - - if (sseq->flag & SEQ_SHOW_GPENCIL) { - if (is_imbuf) { - /* draw grease-pencil (screen aligned) */ - ED_gpencil_draw_view2d(C, 0); - } + else { + /* ortho at pixel level */ + UI_view2d_view_restore(C); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index aaf398bf3a1..7c43720ae2b 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -180,8 +180,6 @@ static void seq_proxy_build_job(const bContext *C) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); ScrArea *sa = CTX_wm_area(C); - struct SeqIndexBuildContext *context; - LinkData *link; Sequence *seq; GSet *file_list; @@ -209,9 +207,7 @@ static void seq_proxy_build_job(const bContext *C) SEQP_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { - context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list); - link = BLI_genericNodeN(context); - BLI_addtail(&pj->queue, link); + BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list, &pj->queue); } } SEQ_END @@ -988,11 +984,11 @@ static void UNUSED_FUNCTION(seq_remap_paths) (Scene *scene) return; BLI_strncpy(from, last_seq->strip->dir, sizeof(from)); -// XXX if (0 == sbutton(from, 0, sizeof(from)-1, "From: ")) +// XXX if (0 == sbutton(from, 0, sizeof(from) - 1, "From: ")) // return; BLI_strncpy(to, from, sizeof(to)); -// XXX if (0 == sbutton(to, 0, sizeof(to)-1, "To: ")) +// XXX if (0 == sbutton(to, 0, sizeof(to) - 1, "To: ")) // return; if (STREQ(to, from)) @@ -1742,7 +1738,7 @@ void SEQUENCER_OT_mute(struct wmOperatorType *ot) /* identifiers */ ot->name = "Mute Strips"; ot->idname = "SEQUENCER_OT_mute"; - ot->description = "Mute selected strips"; + ot->description = "Mute (un)selected strips"; /* api callbacks */ ot->exec = sequencer_mute_exec; @@ -1793,7 +1789,7 @@ void SEQUENCER_OT_unmute(struct wmOperatorType *ot) /* identifiers */ ot->name = "Un-Mute Strips"; ot->idname = "SEQUENCER_OT_unmute"; - ot->description = "Un-Mute unselected rather than selected strips"; + ot->description = "Unmute (un)selected strips"; /* api callbacks */ ot->exec = sequencer_unmute_exec; @@ -1802,7 +1798,7 @@ void SEQUENCER_OT_unmute(struct wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "UnMute unselected rather than selected strips"); + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Unmute unselected rather than selected strips"); } @@ -3165,7 +3161,7 @@ static void seq_copy_del_sound(Scene *scene, Sequence *seq) } } else if (seq->scene_sound) { - sound_remove_scene_sound(scene, seq->scene_sound); + BKE_sound_remove_scene_sound(scene, seq->scene_sound); seq->scene_sound = NULL; } } @@ -3322,10 +3318,10 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op) } if (seq_act->scene_sound) - sound_remove_scene_sound(scene, seq_act->scene_sound); + BKE_sound_remove_scene_sound(scene, seq_act->scene_sound); if (seq_other->scene_sound) - sound_remove_scene_sound(scene, seq_other->scene_sound); + BKE_sound_remove_scene_sound(scene, seq_other->scene_sound); seq_act->scene_sound = NULL; seq_other->scene_sound = NULL; @@ -3333,8 +3329,8 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op) BKE_sequence_calc(scene, seq_act); BKE_sequence_calc(scene, seq_other); - if (seq_act->sound) sound_add_scene_sound_defaults(scene, seq_act); - if (seq_other->sound) sound_add_scene_sound_defaults(scene, seq_other); + if (seq_act->sound) BKE_sound_add_scene_sound_defaults(scene, seq_act); + if (seq_other->sound) BKE_sound_add_scene_sound_defaults(scene, seq_other); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -3442,12 +3438,18 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) SEQP_BEGIN(ed, seq) { if ((seq->flag & SELECT)) { - struct SeqIndexBuildContext *context; + ListBase queue = {NULL, NULL}; + LinkData *link; short stop = 0, do_update; float progress; - context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list); - BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); - BKE_sequencer_proxy_rebuild_finish(context, 0); + + BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list, &queue); + + for (link = queue.first; link; link = link->next) { + struct SeqIndexBuildContext *context = link->data; + BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); + BKE_sequencer_proxy_rebuild_finish(context, 0); + } BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); } } @@ -3692,12 +3694,21 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) Editing *ed = BKE_sequencer_editing_get(scene, false); Sequence *seq = BKE_sequencer_active_get(scene); const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); + const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); + int minframe, numdigits; if (seq->type == SEQ_TYPE_IMAGE) { char directory[FILE_MAX]; - const int len = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); + int len; StripElem *se; + /* need to find min/max frame for placeholders */ + if (use_placeholders) { + len = sequencer_image_seq_get_minmax_frame(op, seq->sfra, &minframe, &numdigits); + } + else { + len = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); + } if (len == 0) return OPERATOR_CANCELLED; @@ -3715,14 +3726,19 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) } seq->strip->stripdata = se = MEM_callocN(len * sizeof(StripElem), "stripelem"); - RNA_BEGIN (op->ptr, itemptr, "files") - { - char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); - BLI_strncpy(se->name, filename, sizeof(se->name)); - MEM_freeN(filename); - se++; + if (use_placeholders) { + sequencer_image_seq_reserve_frames(op, se, len, minframe, numdigits); + } + else { + RNA_BEGIN (op->ptr, itemptr, "files") + { + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); + BLI_strncpy(se->name, filename, sizeof(se->name)); + MEM_freeN(filename); + se++; + } + RNA_END; } - RNA_END; /* reset these else we wont see all the images */ seq->anim_startofs = seq->anim_endofs = 0; @@ -3797,4 +3813,5 @@ void SEQUENCER_OT_change_path(struct wmOperatorType *ot) WM_operator_properties_filesel(ot, FILE_TYPE_FOLDER | FILE_TYPE_IMAGE | FILE_TYPE_MOVIE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_DIRECTORY | WM_FILESEL_RELPATH | WM_FILESEL_FILEPATH | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY); + RNA_def_boolean(ot->srna, "use_placeholders", false, "Use Placeholders", "Use placeholders for missing frames of the strip"); } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 461c72961c2..eaac43095e8 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -45,7 +45,8 @@ struct ARegion; struct ARegionType; struct Scene; struct Main; -struct SequencePreview; +struct wmOperator; +struct StripElem; /* space_sequencer.c */ struct ARegion *sequencer_has_buttons_region(struct ScrArea *sa); @@ -63,7 +64,7 @@ void sequencer_special_update_set(Sequence *seq); /* UNUSED */ // void seq_reset_imageofs(struct SpaceSeq *sseq); -struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs); +struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname); /* sequencer_edit.c */ struct View2D; @@ -196,6 +197,7 @@ void SEQUENCER_OT_properties(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_add(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_remove(struct wmOperatorType *ot); void SEQUENCER_OT_strip_modifier_move(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_modifier_copy(struct wmOperatorType *ot); /* sequencer_view.c */ void SEQUENCER_OT_sample(struct wmOperatorType *ot); @@ -203,5 +205,9 @@ void SEQUENCER_OT_sample(struct wmOperatorType *ot); /* sequencer_preview.c */ void sequencer_preview_add_sound(const struct bContext *C, struct Sequence *seq); +/* sequencer_add */ +int sequencer_image_seq_get_minmax_frame(struct wmOperator *op, int sfra, int *r_minframe, int *r_numdigits); +void sequencer_image_seq_reserve_frames(struct wmOperator *op, struct StripElem *se, int len, int minframe, int numdigits); + #endif /* __SEQUENCER_INTERN_H__ */ diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index 83f3f9bc961..fad317fdd4e 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -207,3 +207,58 @@ void SEQUENCER_OT_strip_modifier_move(wmOperatorType *ot) RNA_def_string(ot->srna, "name", "Name", MAX_NAME, "Name", "Name of modifier to remove"); RNA_def_enum(ot->srna, "direction", direction_items, SEQ_MODIFIER_MOVE_UP, "Type", ""); } + +static int strip_modifier_copy_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = scene->ed; + Sequence *seq = BKE_sequencer_active_get(scene); + Sequence *seq_iter; + + if (!seq || !seq->modifiers.first) + return OPERATOR_CANCELLED; + + SEQP_BEGIN(ed, seq_iter) + { + if (seq_iter->flag & SELECT) { + if (seq_iter == seq) + continue; + + if (seq_iter->modifiers.first) { + SequenceModifierData *smd_tmp, *smd = seq_iter->modifiers.first; + + while (smd) { + smd_tmp = smd->next; + BLI_remlink(&seq_iter->modifiers, smd); + BKE_sequence_modifier_free(smd); + smd = smd_tmp; + } + BLI_listbase_clear(&seq_iter->modifiers); + } + + BKE_sequence_modifier_list_copy(seq_iter, seq); + } + } + SEQ_END + + BKE_sequence_invalidate_cache(scene, seq); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; +} + +void SEQUENCER_OT_strip_modifier_copy(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Copy to Selected Strips"; + ot->idname = "SEQUENCER_OT_strip_modifier_copy"; + ot->description = "Copy modifiers of the active strip to all selected strips"; + + /* api callbacks */ + ot->exec = strip_modifier_copy_exec; + ot->poll = strip_modifier_active_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 33a8a1c5b41..dadd187f36c 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -121,6 +121,7 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_strip_modifier_add); WM_operatortype_append(SEQUENCER_OT_strip_modifier_remove); WM_operatortype_append(SEQUENCER_OT_strip_modifier_move); + WM_operatortype_append(SEQUENCER_OT_strip_modifier_copy); /* sequencer_view.h */ WM_operatortype_append(SEQUENCER_OT_sample); @@ -365,5 +366,5 @@ void ED_operatormacros_sequencer(void) "Duplicate selected strips and move them", OPTYPE_UNDO | OPTYPE_REGISTER); WM_operatortype_macro_define(ot, "SEQUENCER_OT_duplicate"); - WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); + WM_operatortype_macro_define(ot, "TRANSFORM_OT_seq_slide"); } diff --git a/source/blender/editors/space_sequencer/sequencer_preview.c b/source/blender/editors/space_sequencer/sequencer_preview.c index c8834d394f5..3f908dc7096 100644 --- a/source/blender/editors/space_sequencer/sequencer_preview.c +++ b/source/blender/editors/space_sequencer/sequencer_preview.c @@ -85,7 +85,7 @@ static void preview_startjob(void *data, short *stop, short *do_update, float *p PreviewJobAudio *preview_next; bSound *sound = previewjb->sound; - sound_read_waveform(sound, stop); + BKE_sound_read_waveform(sound, stop); if (*stop || G.is_break) { BLI_mutex_lock(pj->mutex); diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 24b3776da6d..c197aabedfd 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -29,9 +29,9 @@ #include <math.h> #include <string.h> -#include "BLI_math_color.h" #include "BLI_utildefines.h" +#include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -167,7 +167,7 @@ static ImBuf *make_waveform_view_from_ibuf_byte(ImBuf *ibuf) for (x = 0; x < ibuf->x; x++) { const unsigned char *rgb = src + 4 * (ibuf->x * y + x); - float v = (float)rgb_to_luma_byte(rgb) / 255.0f; + float v = (float)IMB_colormanagement_get_luminance_byte(rgb) / 255.0f; unsigned char *p = tgt; p += 4 * (w * ((int) (v * (h - 3)) + 1) + x + 1); @@ -207,7 +207,7 @@ static ImBuf *make_waveform_view_from_ibuf_float(ImBuf *ibuf) for (x = 0; x < ibuf->x; x++) { const float *rgb = src + 4 * (ibuf->x * y + x); - float v = rgb_to_luma(rgb); + float v = IMB_colormanagement_get_luminance(rgb); unsigned char *p = tgt; CLAMP(v, 0.0f, 1.0f); @@ -647,13 +647,13 @@ static ImBuf *make_vectorscope_view_from_ibuf_byte(ImBuf *ibuf) wtable[x] = (unsigned char) (pow(((float) x + 1) / 256, scope_gamma) * 255); } - for (x = 0; x <= 255; x++) { - vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); - vectorscope_put_cross(255, x, 0, tgt, w, h, 1); - vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); - vectorscope_put_cross(0, 255, x, tgt, w, h, 1); - vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); - vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); + for (x = 0; x < 256; x++) { + vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); + vectorscope_put_cross(255, x, 0, tgt, w, h, 1); + vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); + vectorscope_put_cross(0, 255, x, tgt, w, h, 1); + vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); + vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); } for (y = 0; y < ibuf->y; y++) { @@ -694,12 +694,12 @@ static ImBuf *make_vectorscope_view_from_ibuf_float(ImBuf *ibuf) } for (x = 0; x <= 255; x++) { - vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); - vectorscope_put_cross(255, x, 0, tgt, w, h, 1); - vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); - vectorscope_put_cross(0, 255, x, tgt, w, h, 1); - vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); - vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); + vectorscope_put_cross(255, 0, 255 - x, tgt, w, h, 1); + vectorscope_put_cross(255, x, 0, tgt, w, h, 1); + vectorscope_put_cross(255 - x, 255, 0, tgt, w, h, 1); + vectorscope_put_cross(0, 255, x, tgt, w, h, 1); + vectorscope_put_cross(0, 255 - x, 255, tgt, w, h, 1); + vectorscope_put_cross(x, 0, 255, tgt, w, h, 1); } for (y = 0; y < ibuf->y; y++) { diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 7fdbc9cc7de..4d6ea865b40 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -94,7 +94,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) Scene *scene = CTX_data_scene(C); SpaceSeq *sseq = (SpaceSeq *) CTX_wm_space_data(C); ARegion *ar = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0); + ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0, NULL); ImageSampleInfo *info = op->customdata; float fx, fy; @@ -201,8 +201,11 @@ static int sample_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case LEFTMOUSE: case RIGHTMOUSE: /* XXX hardcoded */ - sample_exit(C, op); - return OPERATOR_CANCELLED; + if (event->val == KM_RELEASE) { + sample_exit(C, op); + return OPERATOR_CANCELLED; + } + break; case MOUSEMOVE: sample_apply(C, op, event); break; diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 7a7f189b187..769718e1567 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -564,7 +564,10 @@ static void sequencer_preview_area_draw(const bContext *C, ARegion *ar) SpaceSeq *sseq = sa->spacedata.first; Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - int show_split = scene->ed && scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW && sseq->mainb == SEQ_DRAW_IMG_IMBUF; + const bool show_split = ( + scene->ed && + (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + (sseq->mainb == SEQ_DRAW_IMG_IMBUF)); /* XXX temp fix for wrong setting in sseq->mainb */ if (sseq->mainb == SEQ_DRAW_SEQUENCE) sseq->mainb = SEQ_DRAW_IMG_IMBUF; diff --git a/source/blender/editors/space_text/text_intern.h b/source/blender/editors/space_text/text_intern.h index f577714c480..7ae2617f375 100644 --- a/source/blender/editors/space_text/text_intern.h +++ b/source/blender/editors/space_text/text_intern.h @@ -34,15 +34,12 @@ /* internal exports only */ struct ARegion; -struct ARegionType; struct bContext; -struct ReportList; struct ScrArea; struct SpaceText; struct Text; struct TextLine; struct wmOperatorType; -struct wmWindowManager; /* text_draw.c */ void draw_text_main(struct SpaceText *st, struct ARegion *ar); diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index ad8050a50e8..fce864d975f 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2233,7 +2233,7 @@ void TEXT_OT_scroll(wmOperatorType *ot) ot->poll = text_scroll_poll; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER | OPTYPE_INTERNAL; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR | OPTYPE_INTERNAL; /* properties */ RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll", -100, 100); diff --git a/source/blender/editors/space_time/time_intern.h b/source/blender/editors/space_time/time_intern.h index ce52cbbd65e..ced36b2ac2d 100644 --- a/source/blender/editors/space_time/time_intern.h +++ b/source/blender/editors/space_time/time_intern.h @@ -34,7 +34,6 @@ /* internal exports only */ -struct wmWindowManager; /* time_ops.c */ void time_operatortypes(void); diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index ab69e67361d..d8d29d63686 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -30,6 +30,7 @@ set(INC ../../makesrna ../../render/extern/include ../../windowmanager + ../../depsgraph ../../../../intern/guardedalloc ../../../../intern/glew-mx ../../../../intern/smoke/extern diff --git a/source/blender/editors/space_view3d/SConscript b/source/blender/editors/space_view3d/SConscript index a21da940906..78f24948070 100644 --- a/source/blender/editors/space_view3d/SConscript +++ b/source/blender/editors/space_view3d/SConscript @@ -48,6 +48,7 @@ incs = [ '../../makesrna', '../../render/extern/include', '../../windowmanager', + '../../depsgraph', ] if env['WITH_BF_PYTHON']: diff --git a/source/blender/editors/space_view3d/drawanimviz.c b/source/blender/editors/space_view3d/drawanimviz.c index d8b18140cde..d753ad68fcc 100644 --- a/source/blender/editors/space_view3d/drawanimviz.c +++ b/source/blender/editors/space_view3d/drawanimviz.c @@ -51,7 +51,6 @@ #include "BIF_gl.h" -#include "ED_armature.h" #include "ED_keyframes_draw.h" diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c index 755c633531d..96ea5d810bf 100644 --- a/source/blender/editors/space_view3d/drawmesh.c +++ b/source/blender/editors/space_view3d/drawmesh.c @@ -579,7 +579,7 @@ static DMDrawOption draw_tface__set_draw(MTFace *tface, const bool UNUSED(has_mc { Material *ma = give_current_material(Gtexdraw.ob, matnr + 1); - if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return 0; + if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return DM_DRAW_OPTION_SKIP; if (tface || Gtexdraw.is_texpaint) set_draw_settings_cached(0, tface, ma, Gtexdraw); @@ -588,15 +588,19 @@ static DMDrawOption draw_tface__set_draw(MTFace *tface, const bool UNUSED(has_mc return DM_DRAW_OPTION_NORMAL; } -static void update_tface_color_layer(DerivedMesh *dm) +static void update_tface_color_layer(DerivedMesh *dm, bool use_mcol) { MTFace *tface = DM_get_tessface_data_layer(dm, CD_MTFACE); MFace *mface = dm->getTessFaceArray(dm); MCol *finalCol; int i, j; - MCol *mcol = dm->getTessFaceDataArray(dm, CD_PREVIEW_MCOL); - if (!mcol) - mcol = dm->getTessFaceDataArray(dm, CD_MCOL); + MCol *mcol = NULL; + + if (use_mcol) { + mcol = dm->getTessFaceDataArray(dm, CD_PREVIEW_MCOL); + if (!mcol) + mcol = dm->getTessFaceDataArray(dm, CD_MCOL); + } if (CustomData_has_layer(&dm->faceData, CD_TEXTURE_MCOL)) { finalCol = CustomData_get_layer(&dm->faceData, CD_TEXTURE_MCOL); @@ -771,7 +775,7 @@ static void draw_mesh_text(Scene *scene, Object *ob, int glsl) for (a = 0, mp = mface; a < totpoly; a++, mtpoly++, mp++) { short matnr = mp->mat_nr; - int mf_smooth = mp->flag & ME_SMOOTH; + const bool mf_smooth = (mp->flag & ME_SMOOTH) != 0; Material *mat = (me->mat) ? me->mat[matnr] : NULL; int mode = mat ? mat->game.flag : GEMAT_INVISIBLE; @@ -937,7 +941,7 @@ static void draw_mesh_textured_old(Scene *scene, View3D *v3d, RegionView3D *rv3d else { drawTFace_userData userData; - update_tface_color_layer(dm); + update_tface_color_layer(dm, !(ob->mode & OB_MODE_TEXTURE_PAINT)); userData.mf = DM_get_tessface_data_layer(dm, CD_MFACE); userData.tf = DM_get_tessface_data_layer(dm, CD_MTFACE); @@ -1103,7 +1107,7 @@ void draw_mesh_textured(Scene *scene, View3D *v3d, RegionView3D *rv3d, Mesh *me = ob->data; TexMatCallback data = {scene, ob, me, dm}; bool (*set_face_cb)(void *, int); - int glsl, picking = (G.f & G_PICKSEL); + bool glsl, picking = (G.f & G_PICKSEL) != 0; /* face hiding callback depending on mode */ if (ob == scene->obedit) diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index 61c9891e201..8cca1c8b1ef 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -52,6 +52,7 @@ #include "BKE_anim.h" /* for the where_on_path function */ #include "BKE_armature.h" #include "BKE_camera.h" +#include "BKE_colortools.h" #include "BKE_constraint.h" /* for the get_constraint_target function */ #include "BKE_curve.h" #include "BKE_DerivedMesh.h" @@ -213,6 +214,7 @@ static void drawcube_size(float size); static void drawcircle_size(float size); static void draw_empty_sphere(float size); static void draw_empty_cone(float size); +static void draw_box(float vec[8][3], bool solid); static void ob_wire_color_blend_theme_id(const unsigned char ob_wire_col[4], const int theme_id, float fac) { @@ -299,11 +301,12 @@ bool draw_glsl_material(Scene *scene, Object *ob, View3D *v3d, const char dt) if (v3d->flag2 & V3D_SHOW_SOLID_MATCAP) return true; - if (BKE_scene_use_new_shading_nodes(scene)) + if (v3d->drawtype == OB_TEXTURE) + return (scene->gm.matmode == GAME_MAT_GLSL && !BKE_scene_use_new_shading_nodes(scene)); + else if (v3d->drawtype == OB_MATERIAL && dt > OB_SOLID) + return true; + else return false; - - 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) @@ -402,13 +405,13 @@ static const float cosval[CIRCLE_RESOL] = { static void draw_xyz_wire(const float c[3], float size, int axis) { - float v1[3] = {0.f, 0.f, 0.f}, v2[3] = {0.f, 0.f, 0.f}; + float v1[3] = {0.0f, 0.0f, 0.0f}, v2[3] = {0.0f, 0.0f, 0.0f}; float dim = size * 0.1f; float dx[3], dy[3], dz[3]; - dx[0] = dim; dx[1] = 0.f; dx[2] = 0.f; - dy[0] = 0.f; dy[1] = dim; dy[2] = 0.f; - dz[0] = 0.f; dz[1] = 0.f; dz[2] = dim; + dx[0] = dim; dx[1] = 0.0f; dx[2] = 0.0f; + dy[0] = 0.0f; dy[1] = dim; dy[2] = 0.0f; + dz[0] = 0.0f; dz[1] = 0.0f; dz[2] = dim; switch (axis) { case 0: /* x axis */ @@ -424,7 +427,7 @@ static void draw_xyz_wire(const float c[3], float size, int axis) glVertex3fv(v2); /* top left to bottom right */ - mul_v3_fl(dy, 2.f); + mul_v3_fl(dy, 2.0f); add_v3_v3(v1, dy); sub_v3_v3(v2, dy); @@ -447,7 +450,7 @@ static void draw_xyz_wire(const float c[3], float size, int axis) glVertex3fv(v2); /* top left to center */ - mul_v3_fl(dy, 2.f); + mul_v3_fl(dy, 2.0f); add_v3_v3(v1, dy); copy_v3_v3(v2, c); @@ -465,12 +468,12 @@ static void draw_xyz_wire(const float c[3], float size, int axis) glVertex3fv(v1); - mul_v3_fl(dx, 2.f); + mul_v3_fl(dx, 2.0f); add_v3_v3(v1, dx); glVertex3fv(v1); - mul_v3_fl(dz, 2.f); + mul_v3_fl(dz, 2.0f); sub_v3_v3(v1, dx); sub_v3_v3(v1, dz); @@ -483,7 +486,6 @@ static void draw_xyz_wire(const float c[3], float size, int axis) glEnd(); break; } - } void drawaxes(float size, char drawtype) @@ -597,10 +599,10 @@ void drawaxes(float size, char drawtype) } -/* Function to draw an Image on a empty Object */ +/* Function to draw an Image on an empty Object */ static void draw_empty_image(Object *ob, const short dflag, const unsigned char ob_wire_col[4]) { - Image *ima = (Image *)ob->data; + Image *ima = ob->data; ImBuf *ibuf = BKE_image_acquire_ibuf(ima, ob->iuser, NULL); float scale, ofs_x, ofs_y, sca_x, sca_y; @@ -640,16 +642,13 @@ static void draw_empty_image(Object *ob, const short dflag, const unsigned char sca_y = 1.0f; } - /* Calculate the scale center based on objects origin */ + /* Calculate the scale center based on object's origin */ ofs_x = ob->ima_ofs[0] * ima_x; ofs_y = ob->ima_ofs[1] * ima_y; glMatrixMode(GL_MODELVIEW); glPushMatrix(); - /* Make sure we are drawing at the origin */ - glTranslatef(0.0f, 0.0f, 0.0f); - /* Calculate Image scale */ scale = (ob->empty_drawsize / max_ff((float)ima_x * sca_x, (float)ima_y * sca_y)); @@ -943,7 +942,7 @@ void view3d_cached_text_draw_end(View3D *v3d, ARegion *ar, bool depth_write, flo /* ******************** primitive drawing ******************* */ -/* draws a cube on given the scaling of the cube, assuming that +/* draws a cube given the scaling of the cube, assuming that * all required matrices have been set (used for drawing empties) */ static void drawcube_size(float size) @@ -1025,8 +1024,6 @@ static void drawshadbuflimits(Lamp *la, float mat[4][4]) glPointSize(1.0); } - - static void spotvolume(float lvec[3], float vvec[3], const float inp) { /* camera is at 0,0,0 */ @@ -1035,8 +1032,8 @@ static void spotvolume(float lvec[3], float vvec[3], const float inp) normalize_v3(lvec); normalize_v3(vvec); /* is this the correct vector ? */ - cross_v3_v3v3(temp, vvec, lvec); /* equation for a plane through vvec en lvec */ - cross_v3_v3v3(plane, lvec, temp); /* a plane perpendicular to this, parrallel with lvec */ + cross_v3_v3v3(temp, vvec, lvec); /* equation for a plane through vvec and lvec */ + cross_v3_v3v3(plane, lvec, temp); /* a plane perpendicular to this, parallel with lvec */ /* vectors are exactly aligned, use the X axis, this is arbitrary */ if (normalize_v3(plane) == 0.0f) @@ -1149,6 +1146,52 @@ static void draw_transp_spot_volume(Lamp *la, float x, float z) glCullFace(GL_BACK); } +#ifdef WITH_GAMEENGINE +static void draw_transp_sun_volume(Lamp *la) +{ + float box[8][3]; + + /* construct box */ + box[0][0] = box[1][0] = box[2][0] = box[3][0] = -la->shadow_frustum_size; + box[4][0] = box[5][0] = box[6][0] = box[7][0] = +la->shadow_frustum_size; + box[0][1] = box[1][1] = box[4][1] = box[5][1] = -la->shadow_frustum_size; + box[2][1] = box[3][1] = box[6][1] = box[7][1] = +la->shadow_frustum_size; + box[0][2] = box[3][2] = box[4][2] = box[7][2] = -la->clipend; + box[1][2] = box[2][2] = box[5][2] = box[6][2] = -la->clipsta; + + /* draw edges */ + draw_box(box, false); + + /* draw faces */ + glEnable(GL_CULL_FACE); + glEnable(GL_BLEND); + glDepthMask(0); + + /* draw backside darkening */ + glCullFace(GL_FRONT); + + glBlendFunc(GL_ZERO, GL_SRC_ALPHA); + glColor4f(0.0f, 0.0f, 0.0f, 0.4f); + + draw_box(box, true); + + /* draw front side lighting */ + glCullFace(GL_BACK); + + glBlendFunc(GL_ONE, GL_ONE); + glColor4f(0.2f, 0.2f, 0.2f, 1.0f); + + draw_box(box, true); + + /* restore state */ + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_BLEND); + glDepthMask(1); + glDisable(GL_CULL_FACE); + glCullFace(GL_BACK); +} +#endif + static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, const char dt, const short dflag, const unsigned char ob_wire_col[4], const bool is_obact) { @@ -1161,7 +1204,7 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, unsigned char curcol[4]; unsigned char col[4]; - /* cone can't be drawn for duplicated lamps, because duplilist would be freed to */ + /* cone can't be drawn for duplicated lamps, because duplilist would be freed */ /* the moment of view3d_draw_transp() call */ const bool is_view = (rv3d->persp == RV3D_CAMOB && v3d->camera == base->object); const bool drawcone = ((dt > OB_WIRE) && @@ -1171,7 +1214,22 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, !(base->flag & OB_FROMDUPLI) && !is_view); - if (drawcone && !v3d->transp) { +#ifdef WITH_GAMEENGINE + const bool drawshadowbox = ( + (rv3d->rflag & RV3D_IS_GAME_ENGINE) && + (dt > OB_WIRE) && + !(G.f & G_PICKSEL) && + (la->type == LA_SUN) && + ((la->mode & LA_SHAD_BUF) || + (la->mode & LA_SHAD_RAY)) && + (la->mode & LA_SHOW_SHADOW_BOX) && + !(base->flag & OB_FROMDUPLI) && + !is_view); +#else + const bool drawshadowbox = false; +#endif + + if ((drawcone || drawshadowbox) && !v3d->transp) { /* in this case we need to draw delayed */ ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag); return; @@ -1310,7 +1368,7 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, x *= y; /* draw the circle/square at the end of the cone */ - glTranslatef(0.0, 0.0, x); + glTranslatef(0.0, 0.0, x); if (la->mode & LA_SQUARE) { float tvec[3]; float z_abs = fabsf(z); @@ -1416,6 +1474,13 @@ static void drawlamp(View3D *v3d, RegionView3D *rv3d, Base *base, dir = -dir; } } + +#ifdef WITH_GAMEENGINE + if (drawshadowbox) { + draw_transp_sun_volume(la); + } +#endif + } else if (la->type == LA_AREA) { setlinestyle(3); @@ -1499,10 +1564,10 @@ static void draw_limit_line(float sta, float end, const short dflag, unsigned in static void draw_focus_cross(float dist, float size) { glBegin(GL_LINES); - glVertex3f(-size, 0.f, -dist); - glVertex3f(size, 0.f, -dist); - glVertex3f(0.f, -size, -dist); - glVertex3f(0.f, size, -dist); + glVertex3f(-size, 0.0f, -dist); + glVertex3f(size, 0.0f, -dist); + glVertex3f(0.0f, -size, -dist); + glVertex3f(0.0f, size, -dist); glEnd(); } @@ -1555,8 +1620,7 @@ static void draw_viewport_object_reconstruction(Scene *scene, Base *base, View3D BKE_tracking_get_camera_object_matrix(scene, base->object, mat); /* we're compensating camera size for bundles size, - * to make it so bundles are always displayed with the same size - */ + * to make it so bundles are always displayed with the same size */ copy_v3_v3(camera_size, base->object->size); if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) mul_v3_fl(camera_size, tracking_object->scale); @@ -1624,7 +1688,7 @@ static void draw_viewport_object_reconstruction(Scene *scene, Base *base, View3D glColor3ubv(ob_wire_col); } - glLineWidth(2.f); + glLineWidth(2.0f); glDisable(GL_LIGHTING); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -1632,7 +1696,7 @@ static void draw_viewport_object_reconstruction(Scene *scene, Base *base, View3D glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_LIGHTING); - glLineWidth(1.f); + glLineWidth(1.0f); } if ((dflag & DRAW_CONSTCOLOR) == 0) { @@ -1746,6 +1810,238 @@ static void draw_viewport_reconstruction(Scene *scene, Base *base, View3D *v3d, GPU_select_load_id(base->selcol); } +static void drawcamera_volume(float near_plane[4][3], float far_plane[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(near_plane[0]); + glVertex3fv(far_plane[0]); + glVertex3fv(far_plane[1]); + glVertex3fv(near_plane[1]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glVertex3fv(near_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[2]); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(far_plane[0]); + glVertex3fv(near_plane[0]); + glVertex3fv(near_plane[3]); + glVertex3fv(far_plane[3]); + glEnd(); +} + +/* camera frame */ +static void drawcamera_frame(float vec[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(vec[0]); + glVertex3fv(vec[1]); + glVertex3fv(vec[2]); + glVertex3fv(vec[3]); + glEnd(); +} + +/* center point to camera frame */ +static void drawcamera_framelines(float vec[4][3], float origin[3]) +{ + glBegin(GL_LINE_STRIP); + glVertex3fv(vec[1]); + glVertex3fv(origin); + glVertex3fv(vec[0]); + glVertex3fv(vec[3]); + glVertex3fv(origin); + glVertex3fv(vec[2]); + glEnd(); +} + +static bool drawcamera_is_stereo3d(Scene *scene, View3D *v3d, Object *ob) +{ + return (ob == v3d->camera) && + (scene->r.scemode & R_MULTIVIEW) != 0 && + (v3d->stereo3d_flag); +} + +static void drawcamera_stereo3d( + Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const Camera *cam, + float vec[4][3], float drawsize, const float scale[3]) +{ + int i, j; + float obmat[4][4]; + float vec_lr[2][4][3]; + const float fac = (cam->stereo.pivot == CAM_S3D_PIVOT_CENTER) ? 2.0f : 1.0f; + float origin[2][3] = {{0}}; + float tvec[3]; + const Camera *cam_lr[2]; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME); + + zero_v3(tvec); + + glPushMatrix(); + + for (i = 0; i < 2; i++) { + ob = BKE_camera_multiview_render(scene, ob, names[i]); + cam_lr[i] = ob->data; + + glLoadMatrixf(rv3d->viewmat); + BKE_camera_multiview_model_matrix(&scene->r, ob, names[i], obmat); + glMultMatrixf(obmat); + + copy_m3_m3(vec_lr[i], vec); + copy_v3_v3(vec_lr[i][3], vec[3]); + + if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { + const float shift_x = + ((BKE_camera_multiview_shift_x(&scene->r, ob, names[i]) - cam->shiftx) * + (drawsize * scale[0] * fac)); + + for (j = 0; j < 4; j++) { + vec_lr[i][j][0] += shift_x; + } + } + + if (is_stereo3d_cameras) { + /* camera frame */ + drawcamera_frame(vec_lr[i], GL_LINE_LOOP); + + /* center point to camera frame */ + drawcamera_framelines(vec_lr[i], tvec); + } + + /* connecting line */ + mul_m4_v3(obmat, origin[i]); + + /* convergence plane */ + if (is_stereo3d_plane || is_stereo3d_volume) { + for (j = 0; j < 4; j++) { + mul_m4_v3(obmat, vec_lr[i][j]); + } + } + } + + + /* the remaining drawing takes place in the view space */ + glLoadMatrixf(rv3d->viewmat); + + if (is_stereo3d_cameras) { + /* draw connecting lines */ + glPushAttrib(GL_ENABLE_BIT); + + glLineStipple(2, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + + glBegin(GL_LINES); + glVertex3fv(origin[0]); + glVertex3fv(origin[1]); + glEnd(); + glPopAttrib(); + } + + /* draw convergence plane*/ + if (is_stereo3d_plane) { + float axis_center[3], screen_center[3]; + float world_plane[4][3]; + float local_plane[4][3]; + float offset; + + mid_v3_v3v3(axis_center, origin[0], origin[1]); + + for (i = 0; i < 4; i++) { + mid_v3_v3v3(world_plane[i], vec_lr[0][i], vec_lr[1][i]); + sub_v3_v3v3(local_plane[i], world_plane[i], axis_center); + } + + mid_v3_v3v3(screen_center, world_plane[0], world_plane[2]); + offset = cam->stereo.convergence_distance / len_v3v3(screen_center, axis_center); + + for (i = 0; i < 4; i++) { + mul_v3_fl(local_plane[i], offset); + add_v3_v3(local_plane[i], axis_center); + } + + glColor3f(0.0f, 0.0f, 0.0f); + + /* camera frame */ + drawcamera_frame(local_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_convergence_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + glColor4f(0.0f, 0.0f, 0.0f, v3d->stereo3d_convergence_alpha); + + drawcamera_frame(local_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + + /* draw convergence plane*/ + if (is_stereo3d_volume) { + float screen_center[3]; + float near_plane[4][3], far_plane[4][3]; + float offset; + int j; + + for (i = 0; i < 2; i++) { + mid_v3_v3v3(screen_center, vec_lr[i][0], vec_lr[i][2]); + + offset = len_v3v3(screen_center, origin[i]); + + for (j = 0; j < 4; j++) { + sub_v3_v3v3(near_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(near_plane[j], cam_lr[i]->clipsta / offset); + add_v3_v3(near_plane[j], origin[i]); + + sub_v3_v3v3(far_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(far_plane[j], cam_lr[i]->clipend / offset); + add_v3_v3(far_plane[j], origin[i]); + } + + /* camera frame */ + glColor3f(0.0f, 0.0f, 0.0f); + + drawcamera_frame(near_plane, GL_LINE_LOOP); + drawcamera_frame(far_plane, GL_LINE_LOOP); + drawcamera_volume(near_plane, far_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_volume_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + if (i == 0) + glColor4f(0.0f, 1.0f, 1.0f, v3d->stereo3d_volume_alpha); + else + glColor4f(1.0f, 0.0f, 0.0f, v3d->stereo3d_volume_alpha); + + drawcamera_frame(near_plane, GL_QUADS); + drawcamera_frame(far_plane, GL_QUADS); + drawcamera_volume(near_plane, far_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + } + + glPopMatrix(); +} + /* flag similar to draw_object() */ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, const short dflag, const unsigned char ob_wire_col[4]) @@ -1757,9 +2053,20 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base float vec[4][3], asp[2], shift[2], scale[3]; int i; float drawsize; - const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera); MovieClip *clip = BKE_object_movieclip_get(scene, base->object, false); + const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera); + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + const bool is_stereo3d = drawcamera_is_stereo3d(scene, v3d, ob); + const bool is_stereo3d_view = (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_cameras = (ob == scene->camera) && + is_multiview && + is_stereo3d_view && + (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS); + const bool is_selection_camera_stereo = (G.f & G_PICKSEL) && + is_view && is_multiview && + is_stereo3d_view; + /* draw data for movie clip set as active for scene */ if (clip) { draw_viewport_reconstruction(scene, base, v3d, clip, dflag, ob_wire_col, false); @@ -1785,9 +2092,17 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base cam = ob->data; - scale[0] = 1.0f / len_v3(ob->obmat[0]); - scale[1] = 1.0f / len_v3(ob->obmat[1]); - scale[2] = 1.0f / len_v3(ob->obmat[2]); + /* BKE_camera_multiview_model_matrix already accounts for scale, don't do it here */ + if (is_selection_camera_stereo) { + scale[0] = 1.0f; + scale[1] = 1.0f; + scale[2] = 1.0f; + } + else { + scale[0] = 1.0f / len_v3(ob->obmat[0]); + scale[1] = 1.0f / len_v3(ob->obmat[1]); + scale[2] = 1.0f / len_v3(ob->obmat[2]); + } BKE_camera_view_frame_ex(scene, cam, cam->drawsize, is_view, scale, asp, shift, &drawsize, vec); @@ -1796,12 +2111,24 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glDisable(GL_CULL_FACE); /* camera frame */ - glBegin(GL_LINE_LOOP); - glVertex3fv(vec[0]); - glVertex3fv(vec[1]); - glVertex3fv(vec[2]); - glVertex3fv(vec[3]); - glEnd(); + if (!is_stereo3d_cameras) { + /* make sure selection uses the same matrix for camera as the one used while viewing */ + if (is_selection_camera_stereo) { + float obmat[4][4]; + bool is_left = v3d->multiview_eye == STEREO_LEFT_ID; + + glPushMatrix(); + glLoadMatrixf(rv3d->viewmat); + BKE_camera_multiview_model_matrix(&scene->r, ob, is_left ? STEREO_LEFT_NAME : STEREO_RIGHT_NAME, obmat); + glMultMatrixf(obmat); + + drawcamera_frame(vec, GL_LINE_LOOP); + glPopMatrix(); + } + else { + drawcamera_frame(vec, GL_LINE_LOOP); + } + } if (is_view) return; @@ -1809,20 +2136,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base zero_v3(tvec); /* center point to camera frame */ - glBegin(GL_LINE_STRIP); - glVertex3fv(vec[1]); - glVertex3fv(tvec); - glVertex3fv(vec[0]); - glVertex3fv(vec[3]); - glVertex3fv(tvec); - glVertex3fv(vec[2]); - glEnd(); - + if (!is_stereo3d_cameras) + drawcamera_framelines(vec, tvec); /* arrow on top */ tvec[2] = vec[1][2]; /* copy the depth */ - /* draw an outline arrow for inactive cameras and filled * for active cameras. We actually draw both outline+filled * for active cameras so the wire can be seen side-on */ @@ -1872,14 +2191,17 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glPopMatrix(); } } + + /* stereo cameras drawing */ + if (is_stereo3d) { + drawcamera_stereo3d(scene, v3d, rv3d, ob, cam, vec, drawsize, scale); + } } /* flag similar to draw_object() */ static void drawspeaker(Scene *UNUSED(scene), View3D *UNUSED(v3d), RegionView3D *UNUSED(rv3d), Object *UNUSED(ob), int UNUSED(flag)) { - //Speaker *spk = ob->data; - float vec[3]; int i, j; @@ -1990,9 +2312,9 @@ static void ensure_curve_cache(Scene *scene, Object *object) * here, we also need to check whether display list is * empty or not. * - * The trick below tries to optimie calls to displist + * The trick below tries to optimize calls to displist * creation for cases curve is empty. Meaning, if the curve - * is empty (without splies) bevel list would also be empty. + * is empty (without splines) 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. @@ -2270,8 +2592,7 @@ static void draw_dm_verts__mapFunc(void *userData, int index, const float co[3], BMVert *eve = BM_vert_at_index(data->bm, index); if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN) && BM_elem_flag_test(eve, BM_ELEM_SELECT) == data->sel) { - /* skin nodes: draw a red circle around the root - * node(s) */ + /* skin nodes: draw a red circle around the root node(s) */ if (data->cd_vskin_offset != -1) { const MVertSkin *vs = BM_ELEM_CD_GET_VOID_P(eve, data->cd_vskin_offset); if (vs->flag & MVERT_SKIN_ROOT) { @@ -2337,7 +2658,6 @@ static void draw_dm_verts(BMEditMesh *em, DerivedMesh *dm, const char sel, BMVer static DMDrawOption draw_dm_edges_sel__setDrawOptions(void *userData, int index) { BMEdge *eed; - //unsigned char **cols = userData, *col; drawDMEdgesSel_userData *data = userData; unsigned char *col; @@ -2354,7 +2674,7 @@ static DMDrawOption draw_dm_edges_sel__setDrawOptions(void *userData, int index) else { col = data->baseCol; } - /* no alpha, this is used so a transparent color can disable drawing unselected edges in editmode */ + /* no alpha, this is used so a transparent color can disable drawing unselected edges in editmode */ if (col[3] == 0) return DM_DRAW_OPTION_SKIP; @@ -2751,7 +3071,7 @@ static void draw_dm_faces_sel(BMEditMesh *em, DerivedMesh *dm, unsigned char *ba data.orig_index_mf_to_mpoly = data.orig_index_mp_to_orig = NULL; } - dm->drawMappedFaces(dm, draw_dm_faces_sel__setDrawOptions, GPU_enable_material, draw_dm_faces_sel__compareDrawOptions, &data, 0); + dm->drawMappedFaces(dm, draw_dm_faces_sel__setDrawOptions, NULL, draw_dm_faces_sel__compareDrawOptions, &data, 0); } static DMDrawOption draw_dm_creases__setDrawOptions(void *userData, int index) @@ -3044,7 +3364,7 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe } BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - /* draw selected edges, or edges next to selected verts while draging */ + /* draw selected edges, or edges next to selected verts while dragging */ if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || BM_elem_flag_test(eed->v2, BM_ELEM_SELECT)))) @@ -3082,7 +3402,7 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe unit->system, B_UNIT_LENGTH, do_split, false); } else { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), conv_float, len_v3v3(v1, v2)); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, len_v3v3(v1, v2)); } view3d_cached_text_draw_add(vmid, numstr, numstr_len, 0, txt_flag, col); @@ -3101,17 +3421,15 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); } - // invert_m4_m4(ob->imat, ob->obmat); // this is already called - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { BMLoop *l_a, *l_b; if (BM_edge_loop_pair(eed, &l_a, &l_b)) { - /* draw selected edges, or edges next to selected verts while draging */ + /* draw selected edges, or edges next to selected verts while dragging */ if (BM_elem_flag_test(eed, BM_ELEM_SELECT) || (do_moving && (BM_elem_flag_test(eed->v1, BM_ELEM_SELECT) || BM_elem_flag_test(eed->v2, BM_ELEM_SELECT) || - /* special case, this is useful to show when vertes connected to this edge via a - * face are being transformed */ + /* special case, this is useful to show when verts connected to + * this edge via a face are being transformed */ BM_elem_flag_test(l_a->next->next->v, BM_ELEM_SELECT) || BM_elem_flag_test(l_a->prev->v, BM_ELEM_SELECT) || BM_elem_flag_test(l_b->next->next->v, BM_ELEM_SELECT) || @@ -3161,7 +3479,7 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe angle = angle_normalized_v3v3(no_a, no_b); - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.3f", is_rad ? angle : RAD2DEGF(angle)); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.3f", is_rad ? angle : RAD2DEGF(angle)); view3d_cached_text_draw_add(vmid, numstr, numstr_len, 0, txt_flag, col); } @@ -3186,7 +3504,7 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe 3, unit->system, B_UNIT_AREA, do_split, false); \ } \ else { \ - numstr_len = BLI_snprintf(numstr, sizeof(numstr), conv_float, area); \ + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), conv_float, area); \ } \ view3d_cached_text_draw_add(vmid, numstr, numstr_len, 0, txt_flag, col); \ } (void)0 @@ -3304,7 +3622,7 @@ static void draw_em_measure_stats(ARegion *ar, View3D *v3d, Object *ob, BMEditMe angle = angle_v3v3v3(v1, v2, v3); - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%.3f", is_rad ? angle : RAD2DEGF(angle)); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%.3f", is_rad ? angle : RAD2DEGF(angle)); interp_v3_v3v3(fvec, vmid, v2_local, 0.8f); view3d_cached_text_draw_add(fvec, numstr, numstr_len, 0, txt_flag, col); } @@ -3335,7 +3653,7 @@ static void draw_em_indices(BMEditMesh *em) UI_GetThemeColor3ubv(TH_DRAWEXTRA_FACEANG, col); BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", i); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); view3d_cached_text_draw_add(v->co, numstr, numstr_len, 0, txt_flag, col); } i++; @@ -3347,7 +3665,7 @@ static void draw_em_indices(BMEditMesh *em) UI_GetThemeColor3ubv(TH_DRAWEXTRA_EDGELEN, col); BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", i); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); mid_v3_v3v3(pos, e->v1->co, e->v2->co); view3d_cached_text_draw_add(pos, numstr, numstr_len, 0, txt_flag, col); } @@ -3361,7 +3679,7 @@ static void draw_em_indices(BMEditMesh *em) BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { if (BM_elem_flag_test(f, BM_ELEM_SELECT)) { BM_face_calc_center_mean(f, pos); - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%d", i); + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%d", i); view3d_cached_text_draw_add(pos, numstr, numstr_len, 0, txt_flag, col); } i++; @@ -3417,7 +3735,7 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, if (em->bm->selected.last) { BMEditSelection *ese = em->bm->selected.last; - /* face is handeled above */ + /* face is handled above */ #if 0 if (ese->type == BM_FACE) { efa_act = (BMFace *)ese->data; @@ -3532,11 +3850,14 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, } else if (efa_act) { /* even if draw faces is off it would be nice to draw the stipple face - * Make all other faces zero alpha except for the active - * */ - /* col4 is only used by WITH_FREESTYLE, but keeping it here spares some #ifdef's... */ - unsigned char col1[4], col2[4], col3[4], col4[4]; - col1[3] = col2[3] = col4[3] = 0; /* don't draw */ + * Make all other faces zero alpha except for the active */ + unsigned char col1[4], col2[4], col3[4]; +#ifdef WITH_FREESTYLE + unsigned char col4[4]; + col4[3] = 0; /* don't draw */ +#endif + col1[3] = col2[3] = 0; /* don't draw */ + UI_GetThemeColor4ubv(TH_EDITMESH_ACTIVE, col3); glEnable(GL_BLEND); @@ -3550,7 +3871,6 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d, glDisable(GL_BLEND); glDepthMask(1); /* restore write in zbuffer */ - } /* here starts all fancy draw-extra over */ @@ -3691,7 +4011,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D #endif Mesh *me = ob->data; eWireDrawMode draw_wire = OBDRAW_WIRE_OFF; - int /* totvert,*/ totedge, totface; + bool /* no_verts,*/ no_edges, no_faces; DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask); const bool is_obact = (ob == OBACT); int draw_flags = (is_obact && BKE_paint_select_face_test(ob)) ? DRAW_FACE_SELECT : 0; @@ -3714,9 +4034,9 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D draw_wire = OBDRAW_WIRE_ON_DEPTH; /* draw wire after solid using zoffset and depth buffer adjusment */ } - /* totvert = dm->getNumVerts(dm); */ /*UNUSED*/ - totedge = dm->getNumEdges(dm); - totface = dm->getNumTessFaces(dm); + /* check polys instead of tessfaces because of dyntopo where tessfaces don't exist */ + no_edges = (dm->getNumEdges(dm) == 0); + no_faces = (dm->getNumPolys(dm) == 0); /* vertexpaint, faceselect wants this, but it doesnt work for shaded? */ glFrontFace((ob->transflag & OB_NEG_SCALE) ? GL_CW : GL_CCW); @@ -3725,15 +4045,15 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D if (((v3d->flag2 & V3D_RENDER_OVERRIDE) && v3d->drawtype >= OB_WIRE) == 0) draw_bounding_volume(ob, ob->boundtype); } - else if ((totface == 0 && totedge == 0) || + else if ((no_faces && no_edges) || ((!is_obact || (ob->mode == OB_MODE_OBJECT)) && object_is_halo(scene, ob))) { glPointSize(1.5); dm->drawVerts(dm); glPointSize(1.0); } - else if (dt == OB_WIRE || totface == 0) { - draw_wire = OBDRAW_WIRE_ON; /* draw wire only, no depth buffer stuff */ + else if ((dt == OB_WIRE) || no_faces) { + draw_wire = OBDRAW_WIRE_ON; /* draw wire only, no depth buffer stuff */ } else if (((is_obact && ob->mode & OB_MODE_TEXTURE_PAINT)) || check_object_draw_texture(scene, v3d, dt)) @@ -3775,8 +4095,10 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D else dm->drawFacesGLSL(dm, GPU_enable_material); -// if (BKE_bproperty_object_get(ob, "Text")) -// XXX draw_mesh_text(ob, 1); +#if 0 /* XXX */ + if (BKE_bproperty_object_get(ob, "Text")) + draw_mesh_text(ob, 1); +#endif GPU_disable_material(); glFrontFace(GL_CCW); @@ -3894,7 +4216,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D } if ((draw_wire != OBDRAW_WIRE_OFF) && /* draw extra wire */ - /* when overriding with render only, don't bother */ + /* when overriding with render only, don't bother */ (((v3d->flag2 & V3D_RENDER_OVERRIDE) && v3d->drawtype >= OB_SOLID) == 0)) { /* When using wireframe object draw in particle edit mode @@ -3924,7 +4246,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D glDepthMask(0); /* disable write in zbuffer, selected edge wires show better */ } - dm->drawEdges(dm, (dt == OB_WIRE || totface == 0), (ob->dtx & OB_DRAW_ALL_EDGES) != 0); + dm->drawEdges(dm, ((dt == OB_WIRE) || no_faces), (ob->dtx & OB_DRAW_ALL_EDGES) != 0); if (dt != OB_WIRE && (draw_wire == OBDRAW_WIRE_ON_DEPTH)) { glDepthMask(1); @@ -3933,7 +4255,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D } if (is_obact && BKE_paint_select_vert_test(ob)) { - const int use_depth = (v3d->flag & V3D_ZBUF_SELECT); + const bool use_depth = (v3d->flag & V3D_ZBUF_SELECT) != 0; glColor3f(0.0f, 0.0f, 0.0f); glPointSize(UI_GetThemeValuef(TH_VERTEX_SIZE)); @@ -3948,7 +4270,7 @@ static void draw_mesh_fancy(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D dm->release(dm); } -/* returns 1 if nothing was drawn, for detecting to draw an object center */ +/* returns true if nothing was drawn, for detecting to draw an object center */ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, Base *base, const char dt, const unsigned char ob_wire_col[4], const short dflag) { @@ -3992,7 +4314,9 @@ static bool draw_mesh_object(Scene *scene, ARegion *ar, View3D *v3d, RegionView3 scene->customdata_mask); DM_update_materials(finalDM, ob); - DM_update_materials(cageDM, ob); + if (cageDM != finalDM) { + DM_update_materials(cageDM, ob); + } if (dt > OB_WIRE) { const bool glsl = draw_glsl_material(scene, ob, v3d, dt); @@ -4328,7 +4652,7 @@ static bool drawCurveDerivedMesh(Scene *scene, View3D *v3d, RegionView3D *rv3d, /** * Only called by #drawDispList - * \return 1 when nothing was drawn + * \return true when nothing was drawn */ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, const char dt, const short dflag, const unsigned char ob_wire_col[4]) @@ -4600,7 +4924,7 @@ static void draw_particle(ParticleKey *state, int draw_as, short draw, float pix copy_v3_v3(pdd->vd, vec2); pdd->vd += 3; vec[2] = 2.0f * pixsize; - vec[0] = vec[1] = 0.0; + vec[0] = vec[1] = 0.0f; mul_qt_v3(state->rot, vec); if (draw_as == PART_DRAW_AXIS) { copy_v3_v3(vec2, state->co); @@ -4683,7 +5007,7 @@ static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d, if (psys->parent) mul_m4_v3(psys->parent->obmat, state->co); - /* create actiual particle data */ + /* create actual particle data */ if (draw_as == PART_DRAW_BB) { bb->offset[0] = part->bb_offset[0]; bb->offset[1] = part->bb_offset[1]; @@ -4708,16 +5032,17 @@ static void draw_particle_data(ParticleSystem *psys, RegionView3D *rv3d, draw_particle(state, draw_as, part->draw, pixsize, imat, part->draw_line, bb, pdd); } -/* unified drawing of all new particle systems draw types except dupli ob & group */ -/* mostly tries to use vertex arrays for speed */ - -/* 1. check that everything is ok & updated */ -/* 2. start initializing things */ -/* 3. initialize according to draw type */ -/* 4. allocate drawing data arrays */ -/* 5. start filling the arrays */ -/* 6. draw the arrays */ -/* 7. clean up */ +/* unified drawing of all new particle systems draw types except dupli ob & group + * mostly tries to use vertex arrays for speed + * + * 1. check that everything is ok & updated + * 2. start initializing things + * 3. initialize according to draw type + * 4. allocate drawing data arrays + * 5. start filling the arrays + * 6. draw the arrays + * 7. clean up + */ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, ParticleSystem *psys, const char ob_dt, const short dflag) @@ -4762,6 +5087,12 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (draw_as == PART_DRAW_NOT) return; + /* prepare curvemapping tables */ + if ((psys->part->child_flag & PART_CHILD_USE_CLUMP_CURVE) && psys->part->clumpcurve) + curvemapping_changed_all(psys->part->clumpcurve); + if ((psys->part->child_flag & PART_CHILD_USE_ROUGH_CURVE) && psys->part->roughcurve) + curvemapping_changed_all(psys->part->roughcurve); + /* 2. */ sim.scene = scene; sim.ob = ob; @@ -4907,7 +5238,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv int create_ndata = 0; if (!pdd) - pdd = psys->pdd = MEM_callocN(sizeof(ParticleDrawData), "ParticlDrawData"); + pdd = psys->pdd = MEM_callocN(sizeof(ParticleDrawData), "ParticleDrawData"); if (part->draw_as == PART_DRAW_REND && part->trail_count > 1) { tot_vec_size *= part->trail_count; @@ -5010,7 +5341,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv BLI_assert(0); break; } - CLAMP(intensity, 0.f, 1.f); + CLAMP(intensity, 0.0f, 1.0f); weight_to_rgb(ma_col, intensity); } } @@ -5069,8 +5400,8 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv } if (drawn) { - /* additional things to draw for each particle */ - /* (velocity, size and number) */ + /* additional things to draw for each particle + * (velocity, size and number) */ if ((part->draw & PART_DRAW_VEL) && pdd && pdd->vedata) { copy_v3_v3(pdd->ved, state.co); pdd->ved += 3; @@ -5098,21 +5429,21 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (part->draw & PART_DRAW_NUM) { if (a < totpart && (part->draw & PART_DRAW_HEALTH) && (part->phystype == PART_PHYS_BOIDS)) { - numstr_len = BLI_snprintf(val_pos, sizeof(numstr), "%d:%.2f", a, pa_health); + numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%d:%.2f", a, pa_health); } else { - numstr_len = BLI_snprintf(val_pos, sizeof(numstr), "%d", a); + numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%d", a); } } else { if (a < totpart && (part->draw & PART_DRAW_HEALTH) && (part->phystype == PART_PHYS_BOIDS)) { - numstr_len = BLI_snprintf(val_pos, sizeof(numstr), "%.2f", pa_health); + numstr_len = BLI_snprintf_rlen(val_pos, sizeof(numstr), "%.2f", pa_health); } } if (numstr[0]) { - /* in path drawing state.co is the end point */ - /* use worldspace beause object matrix is already applied */ + /* in path drawing state.co is the end point + * use worldspace because object matrix is already applied */ mul_v3_m4v3(vec_txt, ob->imat, state.co); view3d_cached_text_draw_add(vec_txt, numstr, numstr_len, 10, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, tcol); @@ -5129,7 +5460,7 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv if (draw_as == PART_DRAW_PATH) { ParticleCacheKey **cache, *path; - float /* *cd2=NULL, */ /* UNUSED */ *cdata2 = NULL; + float *cdata2 = NULL; /* setup gl flags */ if (1) { //ob_dt > OB_WIRE) { @@ -5193,11 +5524,13 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv HairKey *hkey = pa->hair; glVertexPointer(3, GL_FLOAT, sizeof(HairKey), hkey->world_co); - - // XXX use proper theme color here -// UI_ThemeColor(TH_NORMAL); + +#if 0 /* XXX use proper theme color here */ + UI_ThemeColor(TH_NORMAL); +#else glColor3f(0.58f, 0.67f, 1.0f); - +#endif + glDrawArrays(GL_LINE_STRIP, 0, pa->totkey); } } @@ -5274,22 +5607,22 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv UI_ThemeColorShadeAlpha(TH_WIRE, 0, -100); glEnable(GL_BLEND); glBegin(GL_LINES); - for (i = 1; i < res[0]-1; ++i) { - float f = interpf(b[0], a[0], (float)i / (float)(res[0]-1)); + for (i = 1; i < res[0] - 1; ++i) { + float f = interpf(b[0], a[0], (float)i / (float)(res[0] - 1)); glVertex3f(f, a[1], a[2]); glVertex3f(f, b[1], a[2]); glVertex3f(f, b[1], a[2]); glVertex3f(f, b[1], b[2]); glVertex3f(f, b[1], b[2]); glVertex3f(f, a[1], b[2]); glVertex3f(f, a[1], b[2]); glVertex3f(f, a[1], a[2]); } - for (i = 1; i < res[1]-1; ++i) { - float f = interpf(b[1], a[1], (float)i / (float)(res[1]-1)); + for (i = 1; i < res[1] - 1; ++i) { + float f = interpf(b[1], a[1], (float)i / (float)(res[1] - 1)); glVertex3f(a[0], f, a[2]); glVertex3f(b[0], f, a[2]); glVertex3f(b[0], f, a[2]); glVertex3f(b[0], f, b[2]); glVertex3f(b[0], f, b[2]); glVertex3f(a[0], f, b[2]); glVertex3f(a[0], f, b[2]); glVertex3f(a[0], f, a[2]); } - for (i = 1; i < res[2]-1; ++i) { - float f = interpf(b[2], a[2], (float)i / (float)(res[2]-1)); + for (i = 1; i < res[2] - 1; ++i) { + float f = interpf(b[2], a[2], (float)i / (float)(res[2] - 1)); glVertex3f(a[0], a[1], f); glVertex3f(b[0], a[1], f); glVertex3f(b[0], a[1], f); glVertex3f(b[0], b[1], f); glVertex3f(b[0], b[1], f); glVertex3f(a[0], b[1], f); @@ -5326,7 +5659,6 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv glDrawArrays(GL_LINE_STRIP, 0, path->segments + 1); } - /* restore & clean up */ if (1) { //ob_dt > OB_WIRE) { if (part->draw_col == PART_DRAW_COL_MAT) @@ -5334,9 +5666,10 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv glDisable(GL_COLOR_MATERIAL); } - if (cdata2) + if (cdata2) { MEM_freeN(cdata2); - /* cd2 = */ /* UNUSED */ cdata2 = NULL; + cdata2 = NULL; + } glLineWidth(1.0f); @@ -5345,8 +5678,8 @@ static void draw_new_particle_system(Scene *scene, View3D *v3d, RegionView3D *rv for (a = 0, pa = psys->particles; a < totpart; a++, pa++) { float vec_txt[3]; - numstr_len = BLI_snprintf(numstr, sizeof(numstr), "%i", a); - /* use worldspace beause object matrix is already applied */ + numstr_len = BLI_snprintf_rlen(numstr, sizeof(numstr), "%i", a); + /* use worldspace because object matrix is already applied */ mul_v3_m4v3(vec_txt, ob->imat, cache[a]->co); view3d_cached_text_draw_add(vec_txt, numstr, numstr_len, 10, V3D_CACHE_TEXT_WORLDSPACE | V3D_CACHE_TEXT_ASCII, tcol); @@ -5474,7 +5807,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) PTCacheEditPoint *point; PTCacheEditKey *key; ParticleEditSettings *pset = PE_settings(scene); - int i, k, totpoint = edit->totpoint, timed = pset->flag & PE_FADE_TIME ? pset->fade_frames : 0; + int i, k, totpoint = edit->totpoint, timed = (pset->flag & PE_FADE_TIME) ? pset->fade_frames : 0; int totkeys = 1; float sel_col[3]; float nosel_col[3]; @@ -5614,7 +5947,7 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) glColor3fv(nosel_col); /* has to be like this.. otherwise selection won't work, have try glArrayElement later..*/ glBegin(GL_POINTS); - glVertex3fv(key->flag & PEK_USE_WCO ? key->world_co : key->co); + glVertex3fv((key->flag & PEK_USE_WCO) ? key->world_co : key->co); glEnd(); } } @@ -5630,9 +5963,9 @@ static void draw_ptcache_edit(Scene *scene, View3D *v3d, PTCacheEdit *edit) glShadeModel(GL_FLAT); if (v3d->zbuf) glEnable(GL_DEPTH_TEST); glLineWidth(1.0f); - glPointSize(1.0); + glPointSize(1.0f); } -//static void ob_draw_RE_motion(float com[3],float rotscale[3][3],float tw,float th) + static void ob_draw_RE_motion(float com[3], float rotscale[3][3], float itw, float ith, float drw_size) { float tr[3][3]; @@ -6036,7 +6369,7 @@ static void draw_editnurb_splines(Object *ob, Nurb *nurb, const bool sel) Nurb *nu; BPoint *bp, *bp1; int a, b, ofs, index; - Curve *cu = (Curve *)ob->data; + Curve *cu = ob->data; index = 0; nu = nurb; @@ -6396,7 +6729,7 @@ static void draw_empty_sphere(float size) static GLuint displist = 0; if (displist == 0) { - GLUquadricObj *qobj; + GLUquadricObj *qobj; displist = glGenLists(1); glNewList(displist, GL_COMPILE); @@ -6427,16 +6760,13 @@ static void draw_empty_sphere(float size) /* draw a cone for use as an empty drawtype */ static void draw_empty_cone(float size) { - float cent = 0; - float radius; + const float radius = size; + GLUquadricObj *qobj = gluNewQuadric(); gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); - glPushMatrix(); - radius = size; - glTranslatef(cent, cent, cent); glScalef(radius, size * 2.0f, radius); glRotatef(-90.0, 1.0, 0.0, 0.0); gluCylinder(qobj, 1.0, 0.0, 1.0, 8, 1); @@ -6449,7 +6779,7 @@ static void draw_empty_cone(float size) static void drawspiral(const float cent[3], float rad, float tmat[4][4], int start) { float vec[3], vx[3], vy[3]; - const float tot_inv = (1.0f / (float)CIRCLE_RESOL); + const float tot_inv = 1.0f / (float)CIRCLE_RESOL; int a; bool inverse = false; float x, y, fac; @@ -6513,8 +6843,7 @@ static void drawspiral(const float cent[3], float rad, float tmat[4][4], int sta } /* draws a circle on x-z plane given the scaling of the circle, assuming that - * all required matrices have been set (used for drawing empties) - */ + * all required matrices have been set (used for drawing empties) */ static void drawcircle_size(float size) { float x, y; @@ -6522,7 +6851,7 @@ static void drawcircle_size(float size) glBegin(GL_LINE_LOOP); - /* coordinates are: cos(degrees * 11.25) = x, sin(degrees*11.25) = y, 0.0f = z */ + /* coordinates are: cos(degrees * 11.25) = x, sin(degrees * 11.25) = y, 0.0f = z */ for (degrees = 0; degrees < CIRCLE_RESOL; degrees++) { x = cosval[degrees]; y = sinval[degrees]; @@ -6666,31 +6995,23 @@ static void draw_forcefield(Object *ob, RegionView3D *rv3d, PartDeflect *pd = ob->pd; float imat[4][4], tmat[4][4]; float vec[3] = {0.0, 0.0, 0.0}; - float size; - /* scale size of circle etc with the empty drawsize */ - if (ob->type == OB_EMPTY) size = ob->empty_drawsize; - else size = 1.0; + const float size = (ob->type == OB_EMPTY) ? ob->empty_drawsize : 1.0f; /* calculus here, is reused in PFIELD_FORCE */ invert_m4_m4(imat, rv3d->viewmatob); -// normalize_v3(imat[0]); /* we don't do this because field doesnt scale either... apart from wind! */ -// normalize_v3(imat[1]); +#if 0 + normalize_v3(imat[0]); /* we don't do this because field doesnt scale either... apart from wind! */ + normalize_v3(imat[1]); +#endif if (pd->forcefield == PFIELD_WIND) { - float force_val; + float force_val = pd->f_strength; if ((dflag & DRAW_CONSTCOLOR) == 0) { ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.5f); } - //if (has_ipo_code(ob->ipo, OB_PD_FSTR)) - // force_val = IPO_GetFloatValue(ob->ipo, OB_PD_FSTR, scene->r.cfra); - //else - { - force_val = pd->f_strength; - } - unit_m4(tmat); force_val *= 0.1f; drawcircball(GL_LINE_LOOP, vec, size, tmat); @@ -6704,14 +7025,7 @@ static void draw_forcefield(Object *ob, RegionView3D *rv3d, } else if (pd->forcefield == PFIELD_FORCE) { - float ffall_val; - - //if (has_ipo_code(ob->ipo, OB_PD_FFALL)) - // ffall_val = IPO_GetFloatValue(ob->ipo, OB_PD_FFALL, scene->r.cfra); - //else - { - ffall_val = pd->f_power; - } + float ffall_val = pd->f_power; if ((dflag & DRAW_CONSTCOLOR) == 0) ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.5f); drawcircball(GL_LINE_LOOP, vec, size, imat); @@ -6721,20 +7035,9 @@ static void draw_forcefield(Object *ob, RegionView3D *rv3d, drawcircball(GL_LINE_LOOP, vec, size * 2.0f, imat); } else if (pd->forcefield == PFIELD_VORTEX) { - float /*ffall_val,*/ force_val; + float force_val = pd->f_strength; unit_m4(tmat); - //if (has_ipo_code(ob->ipo, OB_PD_FFALL)) - // ffall_val = IPO_GetFloatValue(ob->ipo, OB_PD_FFALL, scene->r.cfra); - //else - // ffall_val = pd->f_power; - - //if (has_ipo_code(ob->ipo, OB_PD_FSTR)) - // force_val = IPO_GetFloatValue(ob->ipo, OB_PD_FSTR, scene->r.cfra); - //else - { - force_val = pd->f_strength; - } if ((dflag & DRAW_CONSTCOLOR) == 0) { ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.7f); @@ -6752,25 +7055,19 @@ static void draw_forcefield(Object *ob, RegionView3D *rv3d, else if (pd->forcefield == PFIELD_GUIDE && ob->type == OB_CURVE) { Curve *cu = ob->data; if ((cu->flag & CU_PATH) && ob->curve_cache->path && ob->curve_cache->path->data) { - float mindist, guidevec1[4], guidevec2[3]; - - //if (has_ipo_code(ob->ipo, OB_PD_FSTR)) - // mindist = IPO_GetFloatValue(ob->ipo, OB_PD_FSTR, scene->r.cfra); - //else - { - mindist = pd->f_strength; - } + float guidevec1[4], guidevec2[3]; + float mindist = pd->f_strength; if ((dflag & DRAW_CONSTCOLOR) == 0) { ob_wire_color_blend_theme_id(ob_wire_col, TH_BACK, 0.5f); } - /*path end*/ + /* path end */ setlinestyle(3); where_on_path(ob, 1.0f, guidevec1, guidevec2, NULL, NULL, NULL); drawcircball(GL_LINE_LOOP, guidevec1, mindist, imat); - /*path beginning*/ + /* path beginning */ setlinestyle(0); where_on_path(ob, 0.0f, guidevec1, guidevec2, NULL, NULL, NULL); drawcircball(GL_LINE_LOOP, guidevec1, mindist, imat); @@ -6841,19 +7138,31 @@ static void draw_forcefield(Object *ob, RegionView3D *rv3d, setlinestyle(0); } -static void draw_box(float vec[8][3]) +static void draw_box(float vec[8][3], bool solid) { - glBegin(GL_LINE_STRIP); - glVertex3fv(vec[0]); glVertex3fv(vec[1]); glVertex3fv(vec[2]); glVertex3fv(vec[3]); - glVertex3fv(vec[0]); glVertex3fv(vec[4]); glVertex3fv(vec[5]); glVertex3fv(vec[6]); - glVertex3fv(vec[7]); glVertex3fv(vec[4]); - glEnd(); + if (!solid) { + glBegin(GL_LINE_STRIP); + glVertex3fv(vec[0]); glVertex3fv(vec[1]); glVertex3fv(vec[2]); glVertex3fv(vec[3]); + glVertex3fv(vec[0]); glVertex3fv(vec[4]); glVertex3fv(vec[5]); glVertex3fv(vec[6]); + glVertex3fv(vec[7]); glVertex3fv(vec[4]); + glEnd(); - glBegin(GL_LINES); - glVertex3fv(vec[1]); glVertex3fv(vec[5]); - glVertex3fv(vec[2]); glVertex3fv(vec[6]); - glVertex3fv(vec[3]); glVertex3fv(vec[7]); - glEnd(); + glBegin(GL_LINES); + glVertex3fv(vec[1]); glVertex3fv(vec[5]); + glVertex3fv(vec[2]); glVertex3fv(vec[6]); + glVertex3fv(vec[3]); glVertex3fv(vec[7]); + glEnd(); + } + else { + glBegin(GL_QUADS); + glVertex3fv(vec[0]); glVertex3fv(vec[1]); glVertex3fv(vec[2]); glVertex3fv(vec[3]); + glVertex3fv(vec[7]); glVertex3fv(vec[6]); glVertex3fv(vec[5]); glVertex3fv(vec[4]); + glVertex3fv(vec[4]); glVertex3fv(vec[5]); glVertex3fv(vec[1]); glVertex3fv(vec[0]); + glVertex3fv(vec[3]); glVertex3fv(vec[2]); glVertex3fv(vec[6]); glVertex3fv(vec[7]); + glVertex3fv(vec[3]); glVertex3fv(vec[7]); glVertex3fv(vec[4]); glVertex3fv(vec[0]); + glVertex3fv(vec[1]); glVertex3fv(vec[5]); glVertex3fv(vec[6]); glVertex3fv(vec[2]); + glEnd(); + } } static void draw_bb_quadric(BoundBox *bb, char type, bool around_origin) @@ -6947,7 +7256,7 @@ static void draw_bounding_volume(Object *ob, char type) vec[0][2] = vec[3][2] = vec[4][2] = vec[7][2] = -size[2]; vec[1][2] = vec[2][2] = vec[5][2] = vec[6][2] = +size[2]; - draw_box(vec); + draw_box(vec, false); } else { draw_bb_quadric(bb, type, true); @@ -6955,7 +7264,7 @@ static void draw_bounding_volume(Object *ob, char type) } else { if (type == OB_BOUND_BOX) - draw_box(bb->vec); + draw_box(bb->vec, false); else draw_bb_quadric(bb, type, false); } @@ -6991,7 +7300,7 @@ static void drawtexspace(Object *ob) setlinestyle(2); - draw_box(vec); + draw_box(vec, false); setlinestyle(0); } @@ -7178,7 +7487,7 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_ else { if (ob->flag & OB_FROMGROUP) { if (base->flag & (SELECT + BA_WAS_SEL)) { - /* uses darker active color for non-active + selected*/ + /* uses darker active color for non-active + selected */ theme_id = TH_GROUP_ACTIVE; if (scene->basact != base) { @@ -7232,8 +7541,11 @@ static void draw_object_matcap_check(View3D *v3d, Object *ob) v3d->defmaterial->preview = NULL; } /* first time users */ - if (v3d->matcap_icon == 0) + if (v3d->matcap_icon < ICON_MATCAP_01 || + v3d->matcap_icon > ICON_MATCAP_24) + { v3d->matcap_icon = ICON_MATCAP_01; + } if (v3d->defmaterial->preview == NULL) v3d->defmaterial->preview = UI_icon_to_preview(v3d->matcap_icon); @@ -7265,7 +7577,7 @@ static void draw_rigidbody_shape(Object *ob) vec[0][2] = vec[3][2] = vec[4][2] = vec[7][2] = -size[2]; vec[1][2] = vec[2][2] = vec[5][2] = vec[6][2] = +size[2]; - draw_box(vec); + draw_box(vec, false); break; case RB_SHAPE_SPHERE: draw_bb_quadric(bb, OB_BOUND_SPHERE, true); @@ -7302,7 +7614,8 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short 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. */ + bool skip_object = false; /* Draw particles but not their emitter object. */ + SmokeModifierData *smd = NULL; if (ob != scene->obedit) { if (ob->restrictflag & OB_RESTRICT_VIEW) @@ -7326,17 +7639,37 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (ob->mode == OB_MODE_OBJECT) { ParticleSystem *psys; - particle_skip_object = render_override; + skip_object = render_override; for (psys = ob->particlesystem.first; psys; psys = psys->next) { /* Once we have found a psys which renders its emitter object, we are done. */ if (psys->part->draw & PART_DRAW_EMITTER) { - particle_skip_object = false; + skip_object = false; break; } } } } + if ((md = modifiers_findByType(ob, eModifierType_Smoke)) && (modifier_isEnabled(scene, md, eModifierMode_Realtime))) { + smd = (SmokeModifierData *)md; + + if (smd->domain) { + if (!v3d->transp && (dflag & DRAW_PICKING) == 0) { + if (!v3d->xray && !(ob->dtx & OB_DRAWXRAY)) { + /* object has already been drawn so skip drawing it */ + ED_view3d_after_add(&v3d->afterdraw_transp, base, dflag); + return; + } + else if (v3d->xray) { + /* object has already been drawn so skip drawing it */ + ED_view3d_after_add(&v3d->afterdraw_xraytransp, base, dflag); + return; + } + } + } + } + + /* xray delay? */ if ((dflag & DRAW_PICKING) == 0 && (base->flag & OB_FROMDUPLI) == 0 && (v3d->flag2 & V3D_RENDER_SHADOW) == 0) { /* don't do xray in particle mode, need the z-buffer */ @@ -7435,13 +7768,12 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (dt >= OB_BOUNDBOX) { dtx = ob->dtx; if (ob->mode & OB_MODE_EDIT) { - // the only 2 extra drawtypes alowed in editmode + /* the only 2 extra drawtypes alowed in editmode */ dtx = dtx & (OB_DRAWWIRE | OB_TEXSPACE); } - } - if (!particle_skip_object) { + if (!skip_object) { /* draw outline for selected objects, mesh does itself */ if ((v3d->flag & V3D_SELECT_OUTLINE) && !render_override && ob->type != OB_MESH) { if (dt > OB_WIRE && (ob->mode & OB_MODE_EDIT) == 0 && (dflag & DRAW_SCENESET) == 0) { @@ -7496,9 +7828,6 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } else if (ED_view3d_boundbox_clip(rv3d, ob->bb)) { empty_object = drawDispList(scene, v3d, rv3d, base, dt, dflag, ob_wire_col); - -//XXX old animsys if (cu->path) -// curve_draw_speed(scene, ob); } break; case OB_MBALL: @@ -7663,12 +7992,10 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } /* draw code for smoke */ - if ((md = modifiers_findByType(ob, eModifierType_Smoke)) && (modifier_isEnabled(scene, md, eModifierMode_Realtime))) { - SmokeModifierData *smd = (SmokeModifierData *)md; - - // draw collision objects - if ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll) { + if (smd) { #if 0 + /* draw collision objects */ + if ((smd->type & MOD_SMOKE_TYPE_COLL) && smd->coll) { SmokeCollSettings *scs = smd->coll; if (scs->points) { size_t i; @@ -7695,30 +8022,30 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short glDisable(GL_BLEND); glDepthMask(GL_TRUE); if (col) cpack(col); - } -#endif } +#endif /* only draw domains */ if (smd->domain) { SmokeDomainSettings *sds = smd->domain; - float p0[3], p1[3], viewnormal[3]; - BoundBox bb; + float viewnormal[3]; glLoadMatrixf(rv3d->viewmat); glMultMatrixf(ob->obmat); /* draw adaptive domain bounds */ - if (sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) { + if ((sds->flags & MOD_SMOKE_ADAPTIVE_DOMAIN) && !render_override) { + float p0[3], p1[3]; + BoundBox bb; /* draw domain max bounds */ VECSUBFAC(p0, sds->p0, sds->cell_size, sds->adapt_res); VECADDFAC(p1, sds->p1, sds->cell_size, sds->adapt_res); BKE_boundbox_init_from_minmax(&bb, p0, p1); - draw_box(bb.vec); + draw_box(bb.vec, false); - /* draw base resolution bounds */ #if 0 + /* draw base resolution bounds */ BKE_boundbox_init_from_minmax(&bb, sds->p0, sds->p1); draw_box(bb.vec); #endif @@ -7726,16 +8053,16 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short /* don't show smoke before simulation starts, this could be made an option in the future */ if (smd->domain->fluid && CFRA >= smd->domain->point_cache[0]->startframe) { + float p0[3], p1[3]; - // get view vector - copy_v3_v3(viewnormal, rv3d->viewinv[2]); + /* get view vector */ invert_m4_m4(ob->imat, ob->obmat); - mul_mat3_m4_v3(ob->imat, viewnormal); + mul_v3_mat3_m4v3(viewnormal, ob->imat, rv3d->viewinv[2]); normalize_v3(viewnormal); /* set dynamic boundaries to draw the volume - * also scale cube to global space to equalize volume slicing on all axises - * (its scaled back before drawing) */ + * also scale cube to global space to equalize volume slicing on all axes + * (it's scaled back before drawing) */ p0[0] = (sds->p0[0] + sds->cell_size[0] * sds->res_min[0] + sds->obj_shift_f[0]) * fabsf(ob->size[0]); p0[1] = (sds->p0[1] + sds->cell_size[1] * sds->res_min[1] + sds->obj_shift_f[1]) * fabsf(ob->size[1]); p0[2] = (sds->p0[2] + sds->cell_size[2] * sds->res_min[2] + sds->obj_shift_f[2]) * fabsf(ob->size[2]); @@ -7819,7 +8146,6 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short view3d_cached_text_draw_add(zero, ob->id.name + 2, strlen(ob->id.name + 2), 10, 0, ob_wire_col); } } - /*if (dtx & OB_DRAWIMAGE) drawDispListwire(&ob->disp);*/ if ((dtx & OB_DRAWWIRE) && dt >= OB_SOLID) { if ((dflag & DRAW_CONSTCOLOR) == 0) { draw_wire_extra(scene, rv3d, ob, ob_wire_col); @@ -7935,7 +8261,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) { /* special case for object solver and follow track constraints because they don't fill * constraint targets properly (design limitation -- scene is needed for their target - * but it can't be accessed from get_targets callvack) */ + * but it can't be accessed from get_targets callback) */ Object *camob = NULL; @@ -7960,7 +8286,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short } } else { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); if ((cti && cti->get_constraint_targets) && (curcon->flag & CONSTRAINT_EXPAND)) { ListBase targets = {NULL, NULL}; @@ -8192,7 +8518,7 @@ static void bbs_mesh_solid_verts(Scene *scene, Object *ob) static void bbs_mesh_solid_faces(Scene *scene, Object *ob) { DerivedMesh *dm = mesh_get_derived_final(scene, ob, scene->customdata_mask); - Mesh *me = (Mesh *)ob->data; + Mesh *me = ob->data; glColor3ub(0, 0, 0); diff --git a/source/blender/editors/space_view3d/drawsimdebug.c b/source/blender/editors/space_view3d/drawsimdebug.c index 6113bfd4143..46320ba6763 100644 --- a/source/blender/editors/space_view3d/drawsimdebug.c +++ b/source/blender/editors/space_view3d/drawsimdebug.c @@ -29,28 +29,21 @@ * \ingroup spview3d */ -#include "MEM_guardedalloc.h" - #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "DNA_object_types.h" -#include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BKE_effect.h" -#include "BKE_global.h" -#include "BKE_modifier.h" #include "view3d_intern.h" #include "BIF_gl.h" -#include "BIF_glutil.h" -#include "UI_resources.h" static void draw_sim_debug_elements(SimDebugData *debug_data, float imat[4][4]) { diff --git a/source/blender/editors/space_view3d/drawvolume.c b/source/blender/editors/space_view3d/drawvolume.c index a220e1e39b0..d6691f431dd 100644 --- a/source/blender/editors/space_view3d/drawvolume.c +++ b/source/blender/editors/space_view3d/drawvolume.c @@ -48,8 +48,6 @@ #include "GPU_extensions.h" -#include "ED_mesh.h" - #include "view3d_intern.h" // own include struct GPUTexture; @@ -107,7 +105,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, float cor[3] = {1.0f, 1.0f, 1.0f}; int gl_depth = 0, gl_blend = 0; - int use_fire = (sds->active_fields & SM_ACTIVE_FIRE); + const bool use_fire = (sds->active_fields & SM_ACTIVE_FIRE) != 0; /* draw slices of smoke is adapted from c++ code authored * by: Johannes Schmid and Ingemar Rask, 2006, johnny@grob.org */ @@ -137,76 +135,8 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, unsigned char *spec_data; float *spec_pixels; GPUTexture *tex_spec; - - /* Fragment program to calculate the view3d of smoke */ - /* using 4 textures, density, shadow, flame and flame spectrum */ - const char *shader_basic = - "!!ARBfp1.0\n" - "PARAM dx = program.local[0];\n" - "PARAM darkness = program.local[1];\n" - "PARAM render = program.local[2];\n" - "PARAM f = {1.442695041, 1.442695041, 1.442695041, 0.01};\n" - "TEMP temp, shadow, flame, spec, value;\n" - "TEX temp, fragment.texcoord[0], texture[0], 3D;\n" - "TEX shadow, fragment.texcoord[0], texture[1], 3D;\n" - "TEX flame, fragment.texcoord[0], texture[2], 3D;\n" - "TEX spec, flame.r, texture[3], 1D;\n" - /* calculate shading factor from density */ - "MUL value.r, temp.a, darkness.a;\n" - "MUL value.r, value.r, dx.r;\n" - "MUL value.r, value.r, f.r;\n" - "EX2 temp, -value.r;\n" - /* alpha */ - "SUB temp.a, 1.0, temp.r;\n" - /* shade colors */ - "MUL temp.r, temp.r, shadow.r;\n" - "MUL temp.g, temp.g, shadow.r;\n" - "MUL temp.b, temp.b, shadow.r;\n" - "MUL temp.r, temp.r, darkness.r;\n" - "MUL temp.g, temp.g, darkness.g;\n" - "MUL temp.b, temp.b, darkness.b;\n" - /* for now this just replace smoke shading if rendering fire */ - "CMP result.color, render.r, temp, spec;\n" - "END\n"; - - /* color shader */ - const char *shader_color = - "!!ARBfp1.0\n" - "PARAM dx = program.local[0];\n" - "PARAM darkness = program.local[1];\n" - "PARAM render = program.local[2];\n" - "PARAM f = {1.442695041, 1.442695041, 1.442695041, 1.442695041};\n" - "TEMP temp, shadow, flame, spec, value;\n" - "TEX temp, fragment.texcoord[0], texture[0], 3D;\n" - "TEX shadow, fragment.texcoord[0], texture[1], 3D;\n" - "TEX flame, fragment.texcoord[0], texture[2], 3D;\n" - "TEX spec, flame.r, texture[3], 1D;\n" - /* unpremultiply volume texture */ - "RCP value.r, temp.a;\n" - "MUL temp.r, temp.r, value.r;\n" - "MUL temp.g, temp.g, value.r;\n" - "MUL temp.b, temp.b, value.r;\n" - /* calculate shading factor from density */ - "MUL value.r, temp.a, darkness.a;\n" - "MUL value.r, value.r, dx.r;\n" - "MUL value.r, value.r, f.r;\n" - "EX2 value.r, -value.r;\n" - /* alpha */ - "SUB temp.a, 1.0, value.r;\n" - /* shade colors */ - "MUL temp.r, temp.r, shadow.r;\n" - "MUL temp.g, temp.g, shadow.r;\n" - "MUL temp.b, temp.b, shadow.r;\n" - "MUL temp.r, temp.r, value.r;\n" - "MUL temp.g, temp.g, value.r;\n" - "MUL temp.b, temp.b, value.r;\n" - /* for now this just replace smoke shading if rendering fire */ - "CMP result.color, render.r, temp, spec;\n" - "END\n"; - - GLuint prog; - - + GPUProgram *smoke_program; + int progtype = (sds->active_fields & SM_ACTIVE_COLORS) ? GPU_PROGRAM_SMOKE_COLORED : GPU_PROGRAM_SMOKE; float size[3]; if (!tex) { @@ -323,8 +253,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, glGetBooleanv(GL_BLEND, (GLboolean *)&gl_blend); glGetBooleanv(GL_DEPTH_TEST, (GLboolean *)&gl_depth); - glDepthMask(GL_FALSE); - glDisable(GL_DEPTH_TEST); + glEnable(GL_DEPTH_TEST); glEnable(GL_BLEND); /* find cube vertex that is closest to the viewer */ @@ -351,24 +280,17 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, // printf("i: %d\n", i); // printf("point %f, %f, %f\n", cv[i][0], cv[i][1], cv[i][2]); - if (GL_TRUE == glewIsSupported("GL_ARB_fragment_program")) { - glEnable(GL_FRAGMENT_PROGRAM_ARB); - glGenProgramsARB(1, &prog); - - glBindProgramARB(GL_FRAGMENT_PROGRAM_ARB, prog); - /* set shader */ - if (sds->active_fields & SM_ACTIVE_COLORS) - glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(shader_color), shader_color); - else - glProgramStringARB(GL_FRAGMENT_PROGRAM_ARB, GL_PROGRAM_FORMAT_ASCII_ARB, (GLsizei)strlen(shader_basic), shader_basic); + smoke_program = GPU_shader_get_builtin_program(progtype); + if (smoke_program) { + GPU_program_bind(smoke_program); /* cell spacing */ - glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 0, dx, dx, dx, 1.0); + GPU_program_parameter_4f(smoke_program, 0, dx, dx, dx, 1.0); /* custom parameter for smoke style (higher = thicker) */ if (sds->active_fields & SM_ACTIVE_COLORS) - glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, 1.0, 1.0, 1.0, 10.0); + GPU_program_parameter_4f(smoke_program, 1, 1.0, 1.0, 1.0, 10.0); else - glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0); + GPU_program_parameter_4f(smoke_program, 1, sds->active_color[0], sds->active_color[1], sds->active_color[2], 10.0); } else printf("Your gfx card does not support 3D View smoke drawing.\n"); @@ -448,7 +370,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, else glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, 1.0, 0.0, 0.0, 0.0); + GPU_program_parameter_4f(smoke_program, 2, 1.0, 0.0, 0.0, 0.0); glBegin(GL_POLYGON); glColor3f(1.0, 1.0, 1.0); for (i = 0; i < numpoints; i++) { @@ -468,7 +390,7 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, else glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glProgramLocalParameter4fARB(GL_FRAGMENT_PROGRAM_ARB, 2, -1.0, 0.0, 0.0, 0.0); + GPU_program_parameter_4f(smoke_program, 2, -1.0, 0.0, 0.0, 0.0); glBegin(GL_POLYGON); glColor3f(1.0, 1.0, 1.0); for (i = 0; i < numpoints; i++) { @@ -501,10 +423,8 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, free(spec_data); free(spec_pixels); - if (GLEW_ARB_fragment_program) { - glDisable(GL_FRAGMENT_PROGRAM_ARB); - glDeleteProgramsARB(1, &prog); - } + if (smoke_program) + GPU_program_unbind(smoke_program); MEM_freeN(points); @@ -516,8 +436,6 @@ void draw_smoke_volume(SmokeDomainSettings *sds, Object *ob, if (gl_depth) { glEnable(GL_DEPTH_TEST); } - - glDepthMask(GL_TRUE); } #ifdef SMOKE_DEBUG_VELOCITY diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 35fb2bce115..096d9e8a40a 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -74,6 +74,8 @@ # include "BPY_extern.h" #endif +#include "DEG_depsgraph.h" + #include "view3d_intern.h" /* own include */ /* ******************** manage regions ********************* */ @@ -266,7 +268,7 @@ void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d) } #endif -static void view3d_stop_render_preview(wmWindowManager *wm, ARegion *ar) +void ED_view3d_stop_render_preview(wmWindowManager *wm, ARegion *ar) { RegionView3D *rv3d = ar->regiondata; @@ -297,7 +299,7 @@ void ED_view3d_shade_update(Main *bmain, Scene *scene, View3D *v3d, ScrArea *sa) for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->regiondata) - view3d_stop_render_preview(wm, ar); + ED_view3d_stop_render_preview(wm, ar); } } else if (scene->obedit != NULL && scene->obedit->type == OB_MESH) { @@ -344,7 +346,13 @@ static SpaceLink *view3d_new(const bContext *C) v3d->bundle_size = 0.2f; v3d->bundle_drawtype = OB_PLAINAXES; - + + /* stereo */ + v3d->stereo3d_camera = STEREO_3D_ID; + v3d->stereo3d_flag |= V3D_S3D_DISPPLANE; + v3d->stereo3d_convergence_alpha = 0.15f; + v3d->stereo3d_volume_alpha = 0.05f; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for view3d"); @@ -563,7 +571,7 @@ static void view3d_main_area_exit(wmWindowManager *wm, ARegion *ar) { RegionView3D *rv3d = ar->regiondata; - view3d_stop_render_preview(wm, ar); + ED_view3d_stop_render_preview(wm, ar); if (rv3d->gpuoffscreen) { GPU_offscreen_free(rv3d->gpuoffscreen); @@ -925,7 +933,8 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN (ob && (ob->mode == OB_MODE_TEXTURE_PAINT)) || (v3d->drawtype == OB_TEXTURE && (scene->gm.matmode == GAME_MAT_GLSL || - BKE_scene_use_new_shading_nodes(scene)))) + BKE_scene_use_new_shading_nodes(scene))) || + !DEG_depsgraph_use_legacy()) { ED_region_tag_redraw(ar); } @@ -948,7 +957,8 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN switch (wmn->data) { case ND_LIGHTING: if ((v3d->drawtype == OB_MATERIAL) || - (v3d->drawtype == OB_TEXTURE && (scene->gm.matmode == GAME_MAT_GLSL))) + (v3d->drawtype == OB_TEXTURE && (scene->gm.matmode == GAME_MAT_GLSL)) || + !DEG_depsgraph_use_legacy()) { ED_region_tag_redraw(ar); } @@ -1005,7 +1015,7 @@ static void view3d_main_area_listener(bScreen *sc, ScrArea *sa, ARegion *ar, wmN break; case NC_GPENCIL: - if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) { + if (wmn->data == ND_DATA || ELEM(wmn->action, NA_EDITED, NA_SELECTED)) { 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 88f43ab340f..1f8a69adba0 100644 --- a/source/blender/editors/space_view3d/view3d_buttons.c +++ b/source/blender/editors/space_view3d/view3d_buttons.c @@ -68,7 +68,6 @@ #include "RNA_access.h" #include "ED_armature.h" -#include "ED_gpencil.h" #include "ED_mesh.h" #include "ED_screen.h" @@ -200,7 +199,7 @@ static void v3d_editvertex_buts(uiLayout *layout, View3D *v3d, Object *ob, float bool has_skinradius = false; PointerRNA data_ptr; - fill_vn_fl(median, NBR_TRANSFORM_PROPERTIES, 0.0f); + copy_vn_fl(median, NBR_TRANSFORM_PROPERTIES, 0.0f); tot = totedgedata = totcurvedata = totlattdata = totcurvebweight = 0; /* make sure we got storage */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 58ef9184dd0..fb0f437884a 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -50,11 +50,13 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_endian_switch.h" +#include "BLI_threads.h" #include "BKE_anim.h" #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" #include "BKE_image.h" #include "BKE_key.h" #include "BKE_main.h" @@ -99,6 +101,11 @@ #include "view3d_intern.h" /* own include */ +/* prototypes */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar); +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname); + /* handy utility for drawing shapes in the viewport for arbitrary code. * could add lines and points too */ // #define DEBUG_DRAW @@ -207,7 +214,7 @@ void ED_view3d_clipping_enable(void) } } -static bool view3d_clipping_test(const float co[3], float clip[6][4]) +static bool view3d_clipping_test(const float co[3], const float clip[6][4]) { if (plane_point_side_v3(clip[0], co) > 0.0f) if (plane_point_side_v3(clip[1], co) > 0.0f) @@ -220,7 +227,7 @@ static bool view3d_clipping_test(const float co[3], float clip[6][4]) /* for 'local' ED_view3d_clipping_local must run first * then all comparisons can be done in localspace */ -bool ED_view3d_clipping_test(RegionView3D *rv3d, const float co[3], const bool is_local) +bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const bool is_local) { return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip); } @@ -465,7 +472,7 @@ float ED_view3d_grid_scale(Scene *scene, View3D *v3d, const char **grid_unit) return v3d->grid * ED_scene_grid_scale(scene, grid_unit); } -static void drawfloor(Scene *scene, View3D *v3d, const char **grid_unit) +static void drawfloor(Scene *scene, View3D *v3d, const char **grid_unit, bool write_depth) { float grid, grid_scale; unsigned char col_grid[3]; @@ -477,7 +484,8 @@ static void drawfloor(Scene *scene, View3D *v3d, const char **grid_unit) grid_scale = ED_view3d_grid_scale(scene, v3d, grid_unit); grid = gridlines * grid_scale; - glDepthMask(GL_FALSE); + if (!write_depth) + glDepthMask(GL_FALSE); UI_GetThemeColor3ubv(TH_GRID, col_grid); @@ -650,7 +658,7 @@ static void draw_rotation_guide(RegionView3D *rv3d) glEnable(GL_POINT_SMOOTH); glDepthMask(0); /* don't overwrite zbuf */ - if (rv3d->rot_angle != 0.f) { + if (rv3d->rot_angle != 0.0f) { /* -- draw rotation axis -- */ float scaled_axis[3]; const float scale = rv3d->dist; @@ -658,19 +666,21 @@ static void draw_rotation_guide(RegionView3D *rv3d) glBegin(GL_LINE_STRIP); - color[3] = 0.f; /* more transparent toward the ends */ + color[3] = 0.0f; /* more transparent toward the ends */ glColor4fv(color); add_v3_v3v3(end, o, scaled_axis); glVertex3fv(end); - // color[3] = 0.2f + fabsf(rv3d->rot_angle); /* modulate opacity with angle */ - // ^^ neat idea, but angle is frame-rate dependent, so it's usually close to 0.2 +#if 0 + color[3] = 0.2f + fabsf(rv3d->rot_angle); /* modulate opacity with angle */ + /* ^^ neat idea, but angle is frame-rate dependent, so it's usually close to 0.2 */ +#endif color[3] = 0.5f; /* more opaque toward the center */ glColor4fv(color); glVertex3fv(o); - color[3] = 0.f; + color[3] = 0.0f; glColor4fv(color); sub_v3_v3v3(end, o, scaled_axis); glVertex3fv(end); @@ -681,14 +691,14 @@ static void draw_rotation_guide(RegionView3D *rv3d) #define ROT_AXIS_DETAIL 13 const float s = 0.05f * scale; - const float step = 2.f * (float)(M_PI / ROT_AXIS_DETAIL); + const float step = 2.0f * (float)(M_PI / ROT_AXIS_DETAIL); float angle; int i; float q[4]; /* rotate ring so it's perpendicular to axis */ const int upright = fabsf(rv3d->rot_axis[2]) >= 0.95f; if (!upright) { - const float up[3] = {0.f, 0.f, 1.f}; + const float up[3] = {0.0f, 0.0f, 1.0f}; float vis_angle, vis_axis[3]; cross_v3_v3v3(vis_axis, up, rv3d->rot_axis); @@ -699,7 +709,7 @@ static void draw_rotation_guide(RegionView3D *rv3d) color[3] = 0.25f; /* somewhat faint */ glColor4fv(color); glBegin(GL_LINE_LOOP); - for (i = 0, angle = 0.f; i < ROT_AXIS_DETAIL; ++i, angle += step) { + for (i = 0, angle = 0.0f; i < ROT_AXIS_DETAIL; ++i, angle += step) { float p[3] = {s * cosf(angle), s * sinf(angle), 0.0f}; if (!upright) { @@ -725,12 +735,12 @@ static void draw_rotation_guide(RegionView3D *rv3d) glVertex3fv(o); glEnd(); - /* find screen coordinates for rotation center, then draw pretty icon */ #if 0 + /* find screen coordinates for rotation center, then draw pretty icon */ mul_m4_v3(rv3d->persinv, rot_center); UI_icon_draw(rot_center[0], rot_center[1], ICON_NDOF_TURN); -#endif /* ^^ just playing around, does not work */ +#endif glDisable(GL_BLEND); glDisable(GL_POINT_SMOOTH); @@ -949,8 +959,9 @@ static void draw_selected_name(Scene *scene, Object *ob, rcti *rect) BLF_draw_default(offset, 0.5f * U.widget_unit, 0.0f, info, sizeof(info)); } -static void view3d_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, - rctf *r_viewborder, const bool no_shift, const bool no_zoom) +static void view3d_camera_border( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + rctf *r_viewborder, const bool no_shift, const bool no_zoom) { CameraParams params; rctf rect_view, rect_camera; @@ -983,7 +994,9 @@ static void view3d_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionV r_viewborder->ymax = ((rect_camera.ymax - rect_view.ymin) / BLI_rctf_size_y(&rect_view)) * ar->winy; } -void ED_view3d_calc_camera_border_size(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, float r_size[2]) +void ED_view3d_calc_camera_border_size( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + float r_size[2]) { rctf viewborder; @@ -992,8 +1005,9 @@ void ED_view3d_calc_camera_border_size(Scene *scene, ARegion *ar, View3D *v3d, R r_size[1] = BLI_rctf_size_y(&viewborder); } -void ED_view3d_calc_camera_border(Scene *scene, ARegion *ar, View3D *v3d, RegionView3D *rv3d, - rctf *r_viewborder, const bool no_shift) +void ED_view3d_calc_camera_border( + const Scene *scene, const ARegion *ar, const View3D *v3d, const RegionView3D *rv3d, + rctf *r_viewborder, const bool no_shift) { view3d_camera_border(scene, ar, v3d, rv3d, r_viewborder, no_shift, false); } @@ -1314,12 +1328,12 @@ static void backdrawview3d(Scene *scene, ARegion *ar, View3D *v3d) /* do nothing */ } else if ((base && (base->object->mode & OB_MODE_PARTICLE_EDIT)) && - v3d->drawtype > OB_WIRE && (v3d->flag & V3D_ZBUF_SELECT)) + V3D_IS_ZBUF(v3d)) { /* do nothing */ } - else if (scene->obedit && v3d->drawtype > OB_WIRE && - (v3d->flag & V3D_ZBUF_SELECT)) + else if (scene->obedit && + V3D_IS_ZBUF(v3d)) { /* do nothing */ } @@ -1441,14 +1455,23 @@ static void view3d_opengl_read_Z_pixels(ARegion *ar, int x, int y, int w, int h, glReadPixels(ar->winrct.xmin + x, ar->winrct.ymin + y, w, h, format, type, data); } -void view3d_validate_backbuf(ViewContext *vc) +void ED_view3d_backbuf_validate(ViewContext *vc) { if (vc->v3d->flag & V3D_INVALID_BACKBUF) backdrawview3d(vc->scene, vc->ar, vc->v3d); } +/** + * allow for small values [0.5 - 2.5], + * and large values, FLT_MAX by clamping by the area size + */ +int ED_view3d_backbuf_sample_size_clamp(ARegion *ar, const float dist) +{ + return (int)min_ff(ceilf(dist), (float)max_ii(ar->winx, ar->winx)); +} + /* samples a single pixel (copied from vpaint) */ -unsigned int view3d_sample_backbuf(ViewContext *vc, int x, int y) +unsigned int ED_view3d_backbuf_sample(ViewContext *vc, int x, int y) { unsigned int col; @@ -1456,7 +1479,7 @@ unsigned int view3d_sample_backbuf(ViewContext *vc, int x, int y) return 0; } - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); view3d_opengl_read_pixels(vc->ar, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col); glReadBuffer(GL_BACK); @@ -1469,7 +1492,7 @@ unsigned int view3d_sample_backbuf(ViewContext *vc, int x, int y) } /* reads full rect, converts indices */ -ImBuf *view3d_read_backbuf(ViewContext *vc, short xmin, short ymin, short xmax, short ymax) +ImBuf *ED_view3d_backbuf_read(ViewContext *vc, short xmin, short ymin, short xmax, short ymax) { unsigned int *dr, *rd; struct ImBuf *ibuf, *ibuf1; @@ -1477,17 +1500,18 @@ ImBuf *view3d_read_backbuf(ViewContext *vc, short xmin, short ymin, short xmax, short xminc, yminc, xmaxc, ymaxc, xs, ys; /* clip */ - if (xmin < 0) xminc = 0; else xminc = xmin; - if (xmax >= vc->ar->winx) xmaxc = vc->ar->winx - 1; else xmaxc = xmax; - if (xminc > xmaxc) return NULL; + xminc = max_ii(xmin, 0); + yminc = max_ii(ymin, 0); + xmaxc = min_ii(xmax, vc->ar->winx - 1); + ymaxc = min_ii(ymax, vc->ar->winy - 1); + + if (UNLIKELY((xminc > xmaxc) || (yminc > ymaxc))) { + return NULL; + } - if (ymin < 0) yminc = 0; else yminc = ymin; - if (ymax >= vc->ar->winy) ymaxc = vc->ar->winy - 1; else ymaxc = ymax; - if (yminc > ymaxc) return NULL; - ibuf = IMB_allocImBuf((xmaxc - xminc + 1), (ymaxc - yminc + 1), 32, IB_rect); - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); view3d_opengl_read_pixels(vc->ar, xminc, yminc, @@ -1527,23 +1551,21 @@ ImBuf *view3d_read_backbuf(ViewContext *vc, short xmin, short ymin, short xmax, } /* smart function to sample a rect spiralling outside, nice for backbuf selection */ -unsigned int view3d_sample_backbuf_rect(ViewContext *vc, const int mval[2], int size, - unsigned int min, unsigned int max, float *r_dist, short strict, - void *handle, bool (*indextest)(void *handle, unsigned int index)) +unsigned int ED_view3d_backbuf_sample_rect( + ViewContext *vc, const int mval[2], int size, + unsigned int min, unsigned int max, float *r_dist) { struct ImBuf *buf; - unsigned int *bufmin, *bufmax, *tbuf; + const unsigned int *bufmin, *bufmax, *tbuf; int minx, miny; int a, b, rc, nr, amount, dirvec[4][2]; - int distance = 0; unsigned int index = 0; - bool indexok = false; amount = (size - 1) / 2; minx = mval[0] - (amount + 1); miny = mval[1] - (amount + 1); - buf = view3d_read_backbuf(vc, minx, miny, minx + size - 1, miny + size - 1); + buf = ED_view3d_backbuf_read(vc, minx, miny, minx + size - 1, miny + size - 1); if (!buf) return 0; rc = 0; @@ -1561,21 +1583,19 @@ unsigned int view3d_sample_backbuf_rect(ViewContext *vc, const int mval[2], int for (nr = 1; nr <= size; nr++) { for (a = 0; a < 2; a++) { - for (b = 0; b < nr; b++, distance++) { - if (*tbuf && *tbuf >= min && *tbuf < max) { /* we got a hit */ - if (strict) { - indexok = indextest(handle, *tbuf - min + 1); - if (indexok) { - *r_dist = sqrtf((float)distance); - index = *tbuf - min + 1; - goto exit; - } - } - else { - *r_dist = sqrtf((float)distance); /* XXX, this distance is wrong - */ - index = *tbuf - min + 1; /* messy yah, but indices start at 1 */ - goto exit; - } + for (b = 0; b < nr; b++) { + if (*tbuf && *tbuf >= min && *tbuf < max) { + /* we got a hit */ + + /* get x,y pixel coords from the offset + * (manhatten distance in keeping with other screen-based selection) */ + *r_dist = (float)( + abs(((int)(tbuf - buf->rect) % size) - (size / 2)) + + abs(((int)(tbuf - buf->rect) / size) - (size / 2))); + + /* indices start at 1 here */ + index = (*tbuf - min) + 1; + goto exit; } tbuf += (dirvec[rc][0] + dirvec[rc][1]); @@ -1597,6 +1617,26 @@ exit: /* ************************************************************* */ +static void view3d_stereo_bgpic_setup(Scene *scene, View3D *v3d, Image *ima, ImageUser *iuser) +{ + if ((ima->flag & IMA_IS_STEREO)) { + iuser->flag |= IMA_SHOW_STEREO; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) { + iuser->multiview_eye = STEREO_LEFT_ID; + } + else if (v3d->stereo3d_camera != STEREO_3D_ID) { + /* show only left or right camera */ + iuser->multiview_eye = v3d->stereo3d_camera; + } + + BKE_image_multiview_index(ima, iuser); + } + else { + iuser->flag &= ~IMA_SHOW_STEREO; + } +} + static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, const bool do_foreground, const bool do_camera_frame) { @@ -1620,6 +1660,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ImBuf *ibuf = NULL, *freeibuf, *releaseibuf; void *lock; + rctf clip_rect; Image *ima = NULL; MovieClip *clip = NULL; @@ -1639,6 +1680,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ibuf = NULL; /* frame is out of range, dont show */ } else { + view3d_stereo_bgpic_setup(scene, v3d, ima, &bgpic->iuser); ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock); releaseibuf = ibuf; } @@ -1782,8 +1824,12 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, } /* complete clip? */ + BLI_rctf_init(&clip_rect, x1, x2, y1, y2); + if (bgpic->rotation) { + BLI_rctf_rotate_expand(&clip_rect, &clip_rect, bgpic->rotation); + } - if (x2 < 0 || y2 < 0 || x1 > ar->winx || y1 > ar->winy) { + if (clip_rect.xmax < 0 || clip_rect.ymax < 0 || clip_rect.xmin > ar->winx || clip_rect.ymin > ar->winy) { if (freeibuf) IMB_freeImBuf(freeibuf); if (releaseibuf) @@ -1830,9 +1876,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ED_region_pixelspace(ar); glTranslatef(centx, centy, 0.0); - if (rv3d->persp != RV3D_CAMOB) { - glRotatef(RAD2DEGF(-bgpic->rotation), 0.0f, 0.0f, 1.0f); - } + glRotatef(RAD2DEGF(-bgpic->rotation), 0.0f, 0.0f, 1.0f); if (bgpic->flag & V3D_BGPIC_FLIP_X) { zoomx *= -1.0f; @@ -2044,7 +2088,7 @@ static void draw_dupli_objects_color( lb = object_duplilist(G.main->eval_ctx, scene, base->object); // BLI_listbase_sort(lb, dupli_ob_sort); /* might be nice to have if we have a dupli list with mixed objects. */ - apply_data = duplilist_apply(base->object, lb); + apply_data = duplilist_apply(base->object, scene, lb); dob = dupli_step(lb->first); if (dob) dob_next = dupli_step(dob->next); @@ -2479,13 +2523,14 @@ static void gpu_render_lamp_update(Scene *scene, View3D *v3d, Object *ob, Object } } -static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) +static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d) { ListBase shadows; View3DShadow *shadow; Scene *sce_iter; Base *base; Object *ob; + World *world = scene->world; SceneRenderLayer *srl = v3d->scenelock ? BLI_findlink(&scene->r.layers, scene->r.actlay) : NULL; BLI_listbase_clear(&shadows); @@ -2541,7 +2586,7 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) ED_view3d_draw_offscreen( scene, v3d, &ar, winsize, winsize, viewmat, winmat, false, false, true, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); GPU_lamp_shadow_buffer_unbind(shadow->lamp); v3d->drawtype = drawtype; @@ -2550,11 +2595,19 @@ static void gpu_update_lamps_shadows(Scene *scene, View3D *v3d) } BLI_freelistN(&shadows); + + /* update world values */ + if (world) { + GPU_mist_update_enable(world->mode & WO_MIST); + GPU_mist_update_values(world->mistype, world->miststa, world->mistdist, world->misi, &world->horr); + GPU_horizon_update_color(&world->horr); + GPU_ambient_update_color(&world->ambr); + } } /* *********************** customdata **************** */ -CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) +CustomDataMask ED_view3d_datamask(const Scene *scene, const View3D *v3d) { CustomDataMask mask = 0; @@ -2580,16 +2633,16 @@ CustomDataMask ED_view3d_datamask(Scene *scene, View3D *v3d) } /* goes over all modes and view3d settings */ -CustomDataMask ED_view3d_screen_datamask(bScreen *screen) +CustomDataMask ED_view3d_screen_datamask(const bScreen *screen) { - Scene *scene = screen->scene; + const Scene *scene = screen->scene; CustomDataMask mask = CD_MASK_BAREMESH; - ScrArea *sa; + const ScrArea *sa; /* check if we need tfaces & mcols due to view mode */ for (sa = screen->areabase.first; sa; sa = sa->next) { if (sa->spacetype == SPACE_VIEW3D) { - mask |= ED_view3d_datamask(scene, (View3D *)sa->spacedata.first); + mask |= ED_view3d_datamask(scene, sa->spacedata.first); } } @@ -2674,7 +2727,7 @@ static void view3d_draw_objects( const bool draw_grids = !draw_offscreen && (v3d->flag2 & V3D_RENDER_OVERRIDE) == 0; const bool draw_floor = (rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO); /* only draw grids after in solid modes, else it hovers over mesh wires */ - const bool draw_grids_after = draw_grids && draw_floor && (v3d->drawtype > OB_WIRE); + const bool draw_grids_after = draw_grids && draw_floor && (v3d->drawtype > OB_WIRE) && fx; bool do_composite_xray = false; bool xrayclear = true; @@ -2723,8 +2776,8 @@ static void view3d_draw_objects( glMatrixMode(GL_MODELVIEW); glLoadMatrixf(rv3d->viewmat); } - else { - drawfloor(scene, v3d, grid_unit); + else if (!draw_grids_after) { + drawfloor(scene, v3d, grid_unit, true); } } @@ -2802,7 +2855,7 @@ static void view3d_draw_objects( /* perspective floor goes last to use scene depth and avoid writing to depth buffer */ if (draw_grids_after) { - drawfloor(scene, v3d, grid_unit); + drawfloor(scene, v3d, grid_unit, false); } /* must be before xray draw which clears the depth buffer */ @@ -2873,7 +2926,7 @@ void ED_view3d_draw_offscreen_init(Scene *scene, View3D *v3d) { /* shadow buffers, before we setup matrices */ if (draw_glsl_material(scene, NULL, v3d, v3d->drawtype)) - gpu_update_lamps_shadows(scene, v3d); + gpu_update_lamps_shadows_world(scene, v3d); } /* @@ -2887,21 +2940,41 @@ static void view3d_main_area_clear(Scene *scene, View3D *v3d, ARegion *ar) if (glsl) { RegionView3D *rv3d = ar->regiondata; GPUMaterial *gpumat = GPU_material_world(scene, scene->world); + bool material_not_bound; /* calculate full shader for background */ GPU_material_bind(gpumat, 1, 1, 1.0, false, rv3d->viewmat, rv3d->viewinv, rv3d->viewcamtexcofac, (v3d->scenelock != 0)); + material_not_bound = !GPU_material_bound(gpumat); + + if (material_not_bound) { + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + glColor4f(0.0f, 0.0f, 0.0f, 1.0f); + } + glEnable(GL_DEPTH_TEST); glDepthFunc(GL_ALWAYS); glShadeModel(GL_SMOOTH); - glBegin(GL_QUADS); + glBegin(GL_TRIANGLE_STRIP); glVertex3f(-1.0, -1.0, 1.0); glVertex3f(1.0, -1.0, 1.0); - glVertex3f(1.0, 1.0, 1.0); glVertex3f(-1.0, 1.0, 1.0); + glVertex3f(1.0, 1.0, 1.0); glEnd(); glShadeModel(GL_FLAT); + if (material_not_bound) { + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + } + GPU_material_unbind(gpumat); glDepthFunc(GL_LEQUAL); @@ -3079,7 +3152,8 @@ void ED_view3d_draw_offscreen( float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, GPUOffScreen *ofs, - GPUFX *fx, GPUFXSettings *fx_settings) + GPUFX *fx, GPUFXSettings *fx_settings, + const char *viewname) { struct bThemeState theme_state; int bwinx, bwiny; @@ -3114,11 +3188,24 @@ void ED_view3d_draw_offscreen( } /* setup view matrices before fx or unbinding the offscreen buffers will cause issues */ - view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + if ((viewname != NULL && viewname[0] != '\0') && (viewmat == NULL) && rv3d->persp == RV3D_CAMOB && v3d->camera) + view3d_stereo3d_setup_offscreen(scene, v3d, ar, winmat, viewname); + else + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); /* framebuffer fx needed, we need to draw offscreen first */ if (v3d->fx_settings.fx_flag && fx) { + GPUSSAOSettings *ssao = NULL; + + if (v3d->drawtype < OB_SOLID) { + ssao = v3d->fx_settings.ssao; + v3d->fx_settings.ssao = NULL; + } + do_compositing = GPU_fx_compositor_initialize_passes(fx, &ar->winrct, NULL, fx_settings); + + if (ssao) + v3d->fx_settings.ssao = ssao; } /* clear opengl buffers */ @@ -3168,13 +3255,16 @@ void ED_view3d_draw_offscreen( /* utility func for ED_view3d_draw_offscreen */ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]) + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]) { RegionView3D *rv3d = ar->regiondata; ImBuf *ibuf; GPUOffScreen *ofs; bool draw_sky = (alpha_mode == R_ADDSKY) && v3d && (v3d->flag3 & V3D_SHOW_WORLD); + if (UNLIKELY(v3d == NULL)) + return NULL; + /* state changes make normal drawing go weird otherwise */ glPushAttrib(GL_LIGHTING_BIT); @@ -3193,13 +3283,14 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in if (rv3d->persp == RV3D_CAMOB && v3d->camera) { CameraParams params; GPUFXSettings fx_settings = {NULL}; - Object *camera = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); BKE_camera_params_init(¶ms); /* fallback for non camera objects */ params.clipsta = v3d->near; params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, sizex, sizey, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); @@ -3208,13 +3299,13 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background, draw_sky, !params.is_ortho, - ofs, NULL, &fx_settings); + ofs, NULL, &fx_settings, viewname); } else { ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background, draw_sky, true, - ofs, NULL, NULL); + ofs, NULL, NULL, viewname); } /* read in pixels & stamp */ @@ -3239,7 +3330,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in /* creates own 3d views, used by the sequencer */ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]) + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, + const char *viewname, char err_out[256]) { View3D v3d = {NULL}; ARegion ar = {NULL}; @@ -3269,9 +3361,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w { CameraParams params; + Object *camera = BKE_camera_multiview_render(scene, v3d.camera, viewname); BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, v3d.camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, width, height, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); @@ -3285,7 +3379,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w invert_m4_m4(rv3d.persinv, rv3d.viewinv); return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag, - draw_background, alpha_mode, err_out); + draw_background, alpha_mode, viewname, err_out); // seq_view3d_cb(scene, cfra, render_size, seqrectx, seqrecty); } @@ -3490,6 +3584,104 @@ static void view3d_main_area_draw_engine_info(View3D *v3d, RegionView3D *rv3d, A ED_region_info_draw(ar, rv3d->render_engine->text, 1, fill_color); } +static bool view3d_stereo3d_active(const bContext *C, Scene *scene, View3D *v3d, RegionView3D *rv3d) +{ + wmWindow *win = CTX_wm_window(C); + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if (WM_stereo3d_enabled(win, true) == false) + return false; + + if ((v3d->camera == NULL) || (v3d->camera->type != OB_CAMERA) || rv3d->persp != RV3D_CAMOB) + return false; + + if (scene->r.views_format & SCE_VIEWS_FORMAT_MULTIVIEW) { + if (v3d->stereo3d_camera == STEREO_MONO_ID) + return false; + + return BKE_scene_multiview_is_stereo3d(&scene->r); + } + + return true; +} + +/* setup the view and win matrices for the multiview cameras + * + * unlike view3d_stereo3d_setup_offscreen, when view3d_stereo3d_setup is called + * we have no winmatrix (i.e., projection matrix) defined at that time. + * Since the camera and the camera shift are needed for the winmat calculation + * we do a small hack to replace it temporarily so we don't need to change the + * view3d)main_area_setup_view() code to account for that. + */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar) +{ + bool is_left; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + const char *viewname; + + /* show only left or right camera */ + if (v3d->stereo3d_camera != STEREO_3D_ID) + v3d->multiview_eye = v3d->stereo3d_camera; + + is_left = v3d->multiview_eye == STEREO_LEFT_ID; + viewname = names[is_left ? STEREO_LEFT_ID : STEREO_RIGHT_ID]; + + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + Camera *data; + float viewmat[4][4]; + float shiftx; + + data = (Camera *)v3d->camera->data; + shiftx = data->shiftx; + + BLI_lock_thread(LOCK_VIEW3D); + data->shiftx = BKE_camera_multiview_shift_x(&scene->r, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + data->shiftx = shiftx; + BLI_unlock_thread(LOCK_VIEW3D); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *view_ob = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BLI_lock_thread(LOCK_VIEW3D); + v3d->camera = camera; + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + v3d->camera = view_ob; + BLI_unlock_thread(LOCK_VIEW3D); + } +} + +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname) +{ + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + float viewmat[4][4]; + const bool is_left = STREQ(viewname, STEREO_LEFT_NAME); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } +} + #ifdef WITH_GAMEENGINE static void update_lods(Scene *scene, float camera_pos[3]) { @@ -3515,16 +3707,19 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 /* shadow buffers, before we setup matrices */ if (draw_glsl_material(scene, NULL, v3d, v3d->drawtype)) - gpu_update_lamps_shadows(scene, v3d); - + gpu_update_lamps_shadows_world(scene, v3d); + /* reset default OpenGL lights if needed (i.e. after preferences have been altered) */ if (rv3d->rflag & RV3D_GPULIGHT_UPDATE) { rv3d->rflag &= ~RV3D_GPULIGHT_UPDATE; GPU_default_lights(); } - /* setup view matrices */ - view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); + /* setup the view matrix */ + if (view3d_stereo3d_active(C, scene, v3d, rv3d)) + view3d_stereo3d_setup(scene, v3d, ar); + else + view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); rv3d->rflag &= ~RV3D_IS_GAME_ENGINE; #ifdef WITH_GAMEENGINE @@ -3537,7 +3732,7 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 #endif /* framebuffer fx needed, we need to draw offscreen first */ - if (v3d->fx_settings.fx_flag) { + if (v3d->fx_settings.fx_flag && v3d->drawtype >= OB_SOLID) { GPUFXSettings fx_settings; BKE_screen_gpu_fx_validate(&v3d->fx_settings); fx_settings = v3d->fx_settings; @@ -3549,6 +3744,7 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 else { fx_settings.dof = NULL; } + do_compositing = GPU_fx_compositor_initialize_passes(rv3d->compositor, &ar->winrct, &ar->drawrct, &fx_settings); } @@ -3606,7 +3802,7 @@ static bool is_cursor_visible(Scene *scene) else if (ob->mode & OB_MODE_TEXTURE_PAINT) { const Paint *p = BKE_paint_get_active(scene); - if (p && p->brush->imagepaint_tool == PAINT_TOOL_CLONE) { + if (p && p->brush && p->brush->imagepaint_tool == PAINT_TOOL_CLONE) { if ((scene->toolsettings->imapaint.flag & IMAGEPAINT_PROJECT_LAYER_CLONE) == 0) { return true; } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 51915e05a9e..d38de4a426e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -28,7 +28,6 @@ * \ingroup spview3d */ - #include <string.h> #include <stdio.h> #include <math.h> @@ -38,7 +37,6 @@ #include "DNA_curve_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" -#include "DNA_camera_types.h" #include "MEM_guardedalloc.h" @@ -75,7 +73,6 @@ #include "ED_transform.h" #include "ED_mesh.h" #include "ED_view3d.h" -#include "ED_sculpt.h" #include "UI_resources.h" @@ -83,7 +80,7 @@ #include "view3d_intern.h" /* own include */ -bool ED_view3d_offset_lock_check(struct View3D *v3d, struct RegionView3D *rv3d) +bool ED_view3d_offset_lock_check(const View3D *v3d, const RegionView3D *rv3d) { return (rv3d->persp != RV3D_CAMOB) && (v3d->ob_centre_cursor || v3d->ob_centre); } @@ -103,7 +100,7 @@ static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op) /* ********************** view3d_edit: view manipulations ********************* */ -bool ED_view3d_camera_lock_check(View3D *v3d, RegionView3D *rv3d) +bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d) { return ((v3d->camera) && (v3d->camera->id.lib == NULL) && @@ -520,6 +517,7 @@ void ED_view3d_quadview_update(ScrArea *sa, ARegion *ar, bool do_clip) typedef struct ViewOpsData { /* context pointers (assigned by viewops_data_alloc) */ + Scene *scene; ScrArea *sa; ARegion *ar; View3D *v3d; @@ -592,6 +590,7 @@ static void viewops_data_alloc(bContext *C, wmOperator *op) /* store data */ op->customdata = vod; + vod->scene = CTX_data_scene(C); vod->sa = CTX_wm_area(C); vod->ar = CTX_wm_region(C); vod->v3d = vod->sa->spacedata.first; @@ -1313,7 +1312,7 @@ void VIEW3D_OT_rotate(wmOperatorType *ot) ot->cancel = viewrotate_cancel; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; } /** \name NDOF Utility Functions @@ -2056,7 +2055,7 @@ void VIEW3D_OT_move(wmOperatorType *ot) ot->cancel = viewmove_cancel; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; } /* ************************ viewzoom ******************************** */ @@ -2096,16 +2095,63 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "VIEW3D_OT_zoom"); } -static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my) +static void view_zoom_mouseloc_camera( + Scene *scene, View3D *v3d, + ARegion *ar, float dfac, int mx, int my) { RegionView3D *rv3d = ar->regiondata; + const float zoomfac = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom); + const float zoomfac_new = CLAMPIS(zoomfac * (1.0f / dfac), RV3D_CAMZOOM_MIN_FACTOR, RV3D_CAMZOOM_MAX_FACTOR); + const float camzoom_new = BKE_screen_view3d_zoom_from_fac(zoomfac_new); + + + if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) { + float zoomfac_px; + rctf camera_frame_old; + rctf camera_frame_new; + + const float pt_src[2] = {mx, my}; + float pt_dst[2]; + float delta_px[2]; + + ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_old, false); + BLI_rctf_translate(&camera_frame_old, ar->winrct.xmin, ar->winrct.ymin); + + rv3d->camzoom = camzoom_new; + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + + ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &camera_frame_new, false); + BLI_rctf_translate(&camera_frame_new, ar->winrct.xmin, ar->winrct.ymin); + + BLI_rctf_transform_pt_v(&camera_frame_new, &camera_frame_old, pt_dst, pt_src); + sub_v2_v2v2(delta_px, pt_dst, pt_src); + + /* translate the camera offset using pixel space delta + * mapped back to the camera (same logic as panning in camera view) */ + zoomfac_px = BKE_screen_view3d_zoom_to_fac(rv3d->camzoom) * 2.0f; + + rv3d->camdx += delta_px[0] / (ar->winx * zoomfac_px); + rv3d->camdy += delta_px[1] / (ar->winy * zoomfac_px); + CLAMP(rv3d->camdx, -1.0f, 1.0f); + CLAMP(rv3d->camdy, -1.0f, 1.0f); + } + else { + rv3d->camzoom = camzoom_new; + CLAMP(rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); + } +} + +static void view_zoom_mouseloc_3d(ARegion *ar, float dfac, int mx, int my) +{ + RegionView3D *rv3d = ar->regiondata; + const float dist_new = rv3d->dist * dfac; if (U.uiflag & USER_ZOOM_TO_MOUSEPOS) { float dvec[3]; float tvec[3]; float tpos[3]; float mval_f[2]; - float new_dist; + float zfac; negate_v3_v3(tpos, rv3d->ofs); @@ -2122,105 +2168,133 @@ static void view_zoom_mouseloc(ARegion *ar, float dfac, int mx, int my) negate_v3(tvec); /* Offset to target position and dolly */ - new_dist = rv3d->dist * dfac; - copy_v3_v3(rv3d->ofs, tvec); - rv3d->dist = new_dist; + rv3d->dist = dist_new; /* Calculate final offset */ madd_v3_v3v3fl(rv3d->ofs, tvec, dvec, dfac); } else { - rv3d->dist *= dfac; + rv3d->dist = dist_new; } } - -static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzoom, const short zoom_invert) +static float viewzoom_scale_value( + const rcti *winrct, + const short viewzoom, + const bool zoom_invert, const bool zoom_invert_force, + const int xy[2], const int xy_orig[2], + const float val, const float val_orig, + double *r_timer_lastdraw) { - 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; - vod->rv3d->camzoom = vod->camzoom_prev + (zoom_invert ? -delta : delta); - - CLAMP(vod->rv3d->camzoom, RV3D_CAMZOOM_MIN, RV3D_CAMZOOM_MAX); - } + float zfac; if (viewzoom == USER_ZOOM_CONT) { double time = PIL_check_seconds_timer(); - float time_step = (float)(time - vod->timer_lastdraw); + float time_step = (float)(time - *r_timer_lastdraw); float fac; if (U.uiflag & USER_ZOOM_HORIZ) { - fac = (float)(vod->origx - xy[0]); + fac = (float)(xy_orig[0] - xy[0]); } else { - fac = (float)(vod->origy - xy[1]); + fac = (float)(xy_orig[1] - xy[1]); } - if (zoom_invert) { + if (zoom_invert != zoom_invert_force) { fac = -fac; } /* oldstyle zoom */ zfac = 1.0f + ((fac / 20.0f) * time_step); - vod->timer_lastdraw = time; + *r_timer_lastdraw = time; } else if (viewzoom == USER_ZOOM_SCALE) { /* method which zooms based on how far you move the mouse */ const int ctr[2] = { - BLI_rcti_cent_x(&vod->ar->winrct), - BLI_rcti_cent_y(&vod->ar->winrct), + BLI_rcti_cent_x(winrct), + BLI_rcti_cent_y(winrct), }; - const float len_new = 5 + len_v2v2_int(ctr, xy); - const float len_old = 5 + len_v2v2_int(ctr, &vod->origx); - zfac = vod->dist_prev * ((len_old + 5) / (len_new + 5)) / vod->rv3d->dist; + float len_new = 5 + len_v2v2_int(ctr, xy); + float len_old = 5 + len_v2v2_int(ctr, xy_orig); + + /* intentionally ignore 'zoom_invert' for scale */ + if (zoom_invert_force) { + SWAP(float, len_new, len_old); + } + + zfac = val_orig * (len_old / max_ff(len_new, 1.0f)) / val; } else { /* USER_ZOOM_DOLLY */ - float len1, len2; - + float len_new = 5; + float len_old = 5; + if (U.uiflag & USER_ZOOM_HORIZ) { - len1 = (vod->ar->winrct.xmax - xy[0]) + 5; - len2 = (vod->ar->winrct.xmax - vod->origx) + 5; + len_new += (winrct->xmax - xy[0]); + len_old += (winrct->xmax - xy_orig[0]); } else { - len1 = (vod->ar->winrct.ymax - xy[1]) + 5; - len2 = (vod->ar->winrct.ymax - vod->origy) + 5; - } - if (zoom_invert) { - SWAP(float, len1, len2); + len_new += (winrct->ymax - xy[1]); + len_old += (winrct->ymax - xy_orig[1]); } - - if (use_cam_zoom) { - /* zfac is ignored in this case, see below */ -#if 0 - zfac = vod->camzoom_prev * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->camzoom; -#endif - } - else { - zfac = vod->dist_prev * (2.0f * ((len1 / len2) - 1.0f) + 1.0f) / vod->rv3d->dist; + + if (zoom_invert != zoom_invert_force) { + SWAP(float, len_new, len_old); } + + zfac = val_orig * (2.0f * ((len_new / max_ff(len_old, 1.0f)) - 1.0f) + 1.0f) / val; } - if (!use_cam_zoom) { - 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); - } - } + return zfac; +} + +static void viewzoom_apply_camera( + ViewOpsData *vod, const int xy[2], + const short viewzoom, const bool zoom_invert) +{ + float zfac; + float zoomfac_prev = BKE_screen_view3d_zoom_to_fac(vod->camzoom_prev) * 2.0f; + float zoomfac = BKE_screen_view3d_zoom_to_fac(vod->rv3d->camzoom) * 2.0f; + + zfac = viewzoom_scale_value( + &vod->ar->winrct, viewzoom, zoom_invert, true, xy, &vod->origx, + zoomfac, zoomfac_prev, + &vod->timer_lastdraw); + + if (zfac != 1.0f && zfac != 0.0f) { + /* calculate inverted, then invert again (needed because of camera zoom scaling) */ + zfac = 1.0f / zfac; + view_zoom_mouseloc_camera( + vod->scene, vod->v3d, + vod->ar, zfac, vod->oldx, vod->oldy); + } + + ED_region_tag_redraw(vod->ar); +} + +static void viewzoom_apply_3d( + ViewOpsData *vod, const int xy[2], + const short viewzoom, const bool zoom_invert) +{ + float zfac; + float dist_range[2]; + + ED_view3d_dist_range_get(vod->v3d, dist_range); + + zfac = viewzoom_scale_value( + &vod->ar->winrct, viewzoom, zoom_invert, false, xy, &vod->origx, + vod->rv3d->dist, vod->dist_prev, + &vod->timer_lastdraw); + + 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); + + view_zoom_mouseloc_3d( + vod->ar, zfac, vod->oldx, vod->oldy); } /* these limits were in old code too */ @@ -2234,6 +2308,19 @@ static void viewzoom_apply(ViewOpsData *vod, const int xy[2], const short viewzo ED_region_tag_redraw(vod->ar); } +static void viewzoom_apply( + ViewOpsData *vod, const int xy[2], + const short viewzoom, const bool zoom_invert) +{ + if ((vod->rv3d->persp == RV3D_CAMOB) && + (vod->rv3d->is_persp && ED_view3d_camera_lock_check(vod->v3d, vod->rv3d)) == 0) + { + viewzoom_apply_camera(vod, xy, viewzoom, zoom_invert); + } + else { + viewzoom_apply_3d(vod, xy, viewzoom, zoom_invert); + } +} static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -2294,6 +2381,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) static int viewzoom_exec(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); View3D *v3d; RegionView3D *rv3d; ScrArea *sa; @@ -2326,22 +2414,26 @@ static int viewzoom_exec(bContext *C, wmOperator *op) ED_view3d_dist_range_get(v3d, dist_range); if (delta < 0) { + const float step = 1.2f; /* 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; + view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my); } - else if (rv3d->dist < dist_range[1]) { - view_zoom_mouseloc(ar, 1.2f, mx, my); + else { + if (rv3d->dist < dist_range[1]) { + view_zoom_mouseloc_3d(ar, step, mx, my); + } } } else { + const float step = 1.0f / 1.2f; if (use_cam_zoom) { - rv3d->camzoom += 10.0f; - if (rv3d->camzoom > RV3D_CAMZOOM_MAX) rv3d->camzoom = RV3D_CAMZOOM_MAX; + view_zoom_mouseloc_camera(scene, v3d, ar, step, mx, my); } - else if (rv3d->dist > dist_range[0]) { - view_zoom_mouseloc(ar, 0.83333f, mx, my); + else { + if (rv3d->dist > dist_range[0]) { + view_zoom_mouseloc_3d(ar, step, mx, my); + } } } @@ -2471,7 +2563,7 @@ void VIEW3D_OT_zoom(wmOperatorType *ot) ot->cancel = viewzoom_cancel; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX); prop = RNA_def_int(ot->srna, "mx", 0, 0, INT_MAX, "Zoom Position X", "", 0, INT_MAX); @@ -2725,7 +2817,7 @@ void VIEW3D_OT_dolly(wmOperatorType *ot) ot->cancel = viewdolly_cancel; /* flags */ - ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER; + ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR; 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); @@ -2748,29 +2840,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, size = max_fff(afm[0], afm[1], afm[2]); if (ok_dist) { - /* fix up zoom distance if needed */ + char persp; if (rv3d->is_persp) { - float lens, sensor_size; - /* offset the view based on the lens */ if (rv3d->persp == RV3D_CAMOB && ED_view3d_camera_lock_check(v3d, rv3d)) { - CameraParams params; - BKE_camera_params_init(¶ms); - params.clipsta = v3d->near; - params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); - - lens = params.lens; - sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + persp = RV3D_CAMOB; } else { - lens = v3d->lens; - sensor_size = DEFAULT_SENSOR_WIDTH; + persp = RV3D_PERSP; } - size = ED_view3d_radius_to_persp_dist(focallength_to_fov(lens, sensor_size), size / 2.0f) * VIEW3D_MARGIN; - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); } else { /* ortho */ if (size < 0.0001f) { @@ -2779,7 +2857,15 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, } else { /* adjust zoom so it looks nicer */ - size = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; + persp = RV3D_ORTHO; + } + } + + if (ok_dist) { + new_dist = ED_view3d_radius_to_dist(v3d, ar, persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->is_persp) { + /* don't zoom closer than the near clipping plane */ + new_dist = max_ff(new_dist, v3d->near * 1.5f); } } } @@ -2787,15 +2873,6 @@ static void view3d_from_minmax(bContext *C, View3D *v3d, ARegion *ar, mid_v3_v3v3(new_ofs, min, max); negate_v3(new_ofs); - new_dist = size; - - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - size = (float)ar->winx / (float)ar->winy; - if (size < 1.0f) size = 1.0f / size; - new_dist *= size; - } - if (rv3d->persp == RV3D_CAMOB && !ED_view3d_camera_lock_check(v3d, rv3d)) { rv3d->persp = RV3D_PERSP; ED_view3d_smooth_view(C, v3d, ar, v3d->camera, NULL, @@ -3877,7 +3954,7 @@ static int vieworbit_exec(bContext *C, wmOperator *op) char view_opposite; PropertyRNA *prop_angle = RNA_struct_find_property(op->ptr, "angle"); float angle = RNA_property_is_set(op->ptr, prop_angle) ? - RNA_property_float_get(op->ptr, prop_angle) : DEG2RADF((float)U.pad_rot_angle); + RNA_property_float_get(op->ptr, prop_angle) : DEG2RADF(U.pad_rot_angle); /* no NULL check is needed, poll checks */ v3d = CTX_wm_view3d(C); @@ -4106,7 +4183,7 @@ static int viewroll_exec(bContext *C, wmOperator *op) rv3d = ar->regiondata; if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) { 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 angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle); float mousevec[3]; float quat_new[4]; @@ -4530,7 +4607,7 @@ void ED_view3d_cursor3d_position(bContext *C, float fp[3], const int mval[2]) Scene *scene = CTX_data_scene(C); ARegion *ar = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); - RegionView3D *rv3d = CTX_wm_region_view3d(C); + RegionView3D *rv3d = ar->regiondata; bool flip; bool depth_used = false; @@ -4565,11 +4642,30 @@ 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, mval); + float *fp_curr = ED_view3d_cursor3d_get(scene, v3d); + float fp_prev[3]; + + copy_v3_v3(fp_prev, fp_curr); - if (v3d && v3d->localvd) + ED_view3d_cursor3d_position(C, fp_curr, mval); + + /* offset the cursor lock to avoid jumping to new offset */ + if (v3d->ob_centre_cursor) { + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + float co_curr[2], co_prev[2]; + + if ((ED_view3d_project_float_global(ar, fp_prev, co_prev, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) && + (ED_view3d_project_float_global(ar, fp_curr, co_curr, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK)) + { + rv3d->ofs_lock[0] += (co_curr[0] - co_prev[0]) / (ar->winx * 0.5f); + rv3d->ofs_lock[1] += (co_curr[1] - co_prev[1]) / (ar->winy * 0.5f); + } + } + + if (v3d->localvd) WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); else WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); @@ -4579,7 +4675,7 @@ static int view3d_cursor3d_invoke(bContext *C, wmOperator *UNUSED(op), const wmE { ED_view3d_cursor3d_update(C, event->mval); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } void VIEW3D_OT_cursor3d(wmOperatorType *ot) @@ -4593,7 +4689,7 @@ void VIEW3D_OT_cursor3d(wmOperatorType *ot) /* api callbacks */ ot->invoke = view3d_cursor3d_invoke; - ot->poll = ED_operator_view3d_active; + ot->poll = ED_operator_region_view3d_active; /* flags */ // ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO; @@ -4719,16 +4815,24 @@ bool ED_view3d_autodist(Scene *scene, ARegion *ar, View3D *v3d, bglMats mats; /* ZBuffer depth vars */ float depth_close; double cent[2], p[3]; + int margin_arr[] = {0, 2, 4}; + int i; + bool depth_ok = false; /* Get Z Depths, needed for perspective, nice for ortho */ bgl_get_mats(&mats); ED_view3d_draw_depth(scene, ar, v3d, alphaoverride); - depth_close = view_autodist_depth_margin(ar, mval, 4); + /* Attempt with low margin's first */ + i = 0; + do { + depth_close = view_autodist_depth_margin(ar, mval, margin_arr[i++] * U.pixelsize); + depth_ok = (depth_close != FLT_MAX); + } while ((depth_ok == false) && (i < ARRAY_SIZE(margin_arr))); - if (depth_close != FLT_MAX) { - cent[0] = (double)mval[0]; - cent[1] = (double)mval[1]; + if (depth_ok) { + cent[0] = (double)mval[0] + 0.5; + cent[1] = (double)mval[1] + 0.5; if (gluUnProject(cent[0], cent[1], depth_close, mats.modelview, mats.projection, (GLint *)mats.viewport, &p[0], &p[1], &p[2])) @@ -4779,8 +4883,8 @@ bool ED_view3d_autodist_simple(ARegion *ar, const int mval[2], float mouse_world if (depth == FLT_MAX) return false; - cent[0] = (double)mval[0]; - cent[1] = (double)mval[1]; + cent[0] = (double)mval[0] + 0.5; + cent[1] = (double)mval[1] + 0.5; bgl_get_mats(&mats); diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c index da77c4f75f7..469a7e63903 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -296,10 +296,10 @@ static void fly_update_header(bContext *C, FlyInfo *fly) "Ctrl: free look, " "X: Upright x axis (%s), " "Z: Upright z axis (%s), " - "(+/- | Wheel): speed"), + "(+/- | Wheel): speed"), WM_bool_as_string(fly->xlock != FLY_AXISLOCK_STATE_OFF), - WM_bool_as_string(fly->zlock != FLY_AXISLOCK_STATE_OFF)); + WM_bool_as_string(fly->zlock != FLY_AXISLOCK_STATE_OFF)); ED_area_headerprint(CTX_wm_area(C), header); #undef HEADER_LENGTH @@ -806,7 +806,7 @@ static int flyApply(bContext *C, FlyInfo *fly) copy_v3_fl3(upvec, 1.0f, 0.0f, 0.0f); mul_m3_v3(mat, upvec); /* Rotate about the relative up vec */ - axis_angle_to_quat(tmp_quat, upvec, (float)moffset[1] * time_redraw * -FLY_ROTATE_FAC); + axis_angle_to_quat(tmp_quat, upvec, moffset[1] * time_redraw * -FLY_ROTATE_FAC); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); if (fly->xlock != FLY_AXISLOCK_STATE_OFF) @@ -836,7 +836,7 @@ static int flyApply(bContext *C, FlyInfo *fly) } /* Rotate about the relative up vec */ - axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); + axis_angle_to_quat(tmp_quat, upvec, moffset[0] * time_redraw * FLY_ROTATE_FAC); mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat); if (fly->xlock != FLY_AXISLOCK_STATE_OFF) diff --git a/source/blender/editors/space_view3d/view3d_header.c b/source/blender/editors/space_view3d/view3d_header.c index 11ed9867e2f..f8912345e1c 100644 --- a/source/blender/editors/space_view3d/view3d_header.c +++ b/source/blender/editors/space_view3d/view3d_header.c @@ -105,8 +105,8 @@ static void view3d_layers_editmode_ensure(Scene *scene, View3D *v3d) if (scene->obedit && (scene->obedit->lay & v3d->lay) == 0) { int bit; for (bit = 0; bit < 32; bit++) { - if (scene->obedit->lay & (1 << bit)) { - v3d->lay |= 1 << bit; + if (scene->obedit->lay & (1u << bit)) { + v3d->lay |= (1u << bit); break; } } @@ -161,8 +161,8 @@ static int view3d_layers_exec(bContext *C, wmOperator *op) v3d->layact = 1 << nr; else if ((v3d->lay & v3d->layact) == 0) { for (bit = 0; bit < 32; bit++) { - if (v3d->lay & (1 << bit)) { - v3d->layact = 1 << bit; + if (v3d->lay & (1u << bit)) { + v3d->layact = (1u << bit); break; } } diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index 25dbc8830fe..95c0ef92680 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -41,14 +41,11 @@ struct BoundBox; struct DerivedMesh; struct Object; struct SmokeDomainSettings; -struct ViewContext; struct bAnimVizSettings; struct bContext; struct bMotionPath; struct bPoseChannel; -struct bScreen; struct Mesh; -struct SimDebugData; struct wmNDOFMotionData; struct wmOperatorType; struct wmWindowManager; @@ -208,7 +205,7 @@ void VIEW3D_OT_localview(struct wmOperatorType *ot); void VIEW3D_OT_game_start(struct wmOperatorType *ot); -bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const struct BoundBox *bb, float obmat[4][4]); +bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const struct BoundBox *bb, float obmat[4][4]); bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const struct BoundBox *bb); void ED_view3d_smooth_view_ex( @@ -225,8 +222,8 @@ 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, const rctf *rect); -void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d); +void view3d_winmatrix_set(ARegion *ar, const View3D *v3d, const rctf *rect); +void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d); void fly_modal_keymap(struct wmKeyConfig *keyconf); void walk_modal_keymap(struct wmKeyConfig *keyconf); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 8c668b2b8e0..6a2c948aa8d 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -236,6 +236,7 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "VIEW3D_OT_navigate", FKEY, KM_PRESS, KM_SHIFT, 0); + /* value is set to KM_NOTHING to avoid conflicts with click type (see T44251) */ WM_keymap_verify_item(keymap, "VIEW3D_OT_smoothview", TIMER1, KM_ANY, KM_ANY, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_rotate", MOUSEPAN, 0, 0, 0); diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c index 74e3fde0eec..ba0626c58ea 100644 --- a/source/blender/editors/space_view3d/view3d_project.c +++ b/source/blender/editors/space_view3d/view3d_project.c @@ -605,6 +605,14 @@ void ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, Object *ob, float pm mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat); } +void ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, float obmat[4][4], float pmat[4][4]) +{ + float vmat[4][4]; + + mul_m4_m4m4(vmat, (float (*)[4])rv3d->viewmat, obmat); + mul_m4_m4m4(pmat, (float (*)[4])rv3d->winmat, vmat); +} + /** * Uses window coordinates (x,y) and depth component z to find a point in * modelspace */ diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index eba31866f54..98b1e846c70 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -187,8 +187,9 @@ typedef struct RulerInfo { /* wm state */ wmWindow *win; - ARegion *ar; + ScrArea *sa; void *draw_handle_pixel; + ARegion *ar; /* re-assigned every modal update */ } RulerInfo; /* -------------------------------------------------------------------- */ @@ -435,7 +436,7 @@ static void ruler_info_draw_pixel(const struct bContext *C, ARegion *ar, void *a UnitSettings *unit = &scene->unit; RulerItem *ruler_item; RulerInfo *ruler_info = arg; - RegionView3D *rv3d = ruler_info->ar->regiondata; + RegionView3D *rv3d = ar->regiondata; // ARegion *ar = ruler_info->ar; const float cap_size = 4.0f; const float bg_margin = 4.0f * U.pixelsize; @@ -798,12 +799,14 @@ static int view3d_ruler_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE op->customdata = ruler_info; ruler_info->win = win; - ruler_info->ar = ar; + ruler_info->sa = sa; ruler_info->draw_handle_pixel = ED_region_draw_cb_activate(ar->type, ruler_info_draw_pixel, ruler_info, REGION_DRAW_POST_PIXEL); view3d_ruler_header_update(sa); + op->flag |= OP_IS_MODAL_CURSOR_REGION; + WM_cursor_modal_set(win, BC_CROSSCURSOR); WM_event_add_modal_handler(C, op); @@ -825,15 +828,17 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) int exit_code = OPERATOR_RUNNING_MODAL; RulerInfo *ruler_info = op->customdata; ScrArea *sa = CTX_wm_area(C); - ARegion *ar = ruler_info->ar; + ARegion *ar = CTX_wm_region(C); RegionView3D *rv3d = ar->regiondata; /* its possible to change spaces while running the operator [#34894] */ - if (UNLIKELY(ar != CTX_wm_region(C))) { + if (UNLIKELY(sa != ruler_info->sa)) { exit_code = OPERATOR_FINISHED; goto exit; } + ruler_info->ar = ar; + switch (event->type) { case LEFTMOUSE: if (event->val == KM_RELEASE) { @@ -1019,6 +1024,13 @@ static int view3d_ruler_modal(bContext *C, wmOperator *op, const wmEvent *event) } + if (ruler_info->state == RULER_STATE_DRAG) { + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } + else { + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + if (do_draw) { view3d_ruler_header_update(sa); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index d0f22ba58c5..49e42cf164a 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -725,7 +725,7 @@ static void do_lasso_select_meshobject__doSelectVert(void *userData, MVert *mv, } static void do_lasso_select_paintvert(ViewContext *vc, const int mcords[][2], short moves, bool extend, bool select) { - const int use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT); + const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; Object *ob = vc->obact; Mesh *me = ob->data; rcti rect; @@ -1641,7 +1641,7 @@ static void do_paintvert_box_select__doSelectVert(void *userData, MVert *mv, con } static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, bool extend) { - const int use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT); + const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; Mesh *me; MVert *mvert; struct ImBuf *ibuf; @@ -1662,7 +1662,7 @@ static int do_paintvert_box_select(ViewContext *vc, rcti *rect, bool select, boo if (use_zbuf) { selar = MEM_callocN(me->totvert + 1, "selar"); - view3d_validate_backbuf(vc); + ED_view3d_backbuf_validate(vc); ibuf = IMB_allocImBuf(sx, sy, 32, IB_rect); rt = ibuf->rect; @@ -2185,7 +2185,7 @@ void VIEW3D_OT_select_border(wmOperatorType *ot) static bool mouse_weight_paint_vertex_select(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, Object *obact) { View3D *v3d = CTX_wm_view3d(C); - const int use_zbuf = (v3d->flag & V3D_ZBUF_SELECT); + const bool use_zbuf = (v3d->flag & V3D_ZBUF_SELECT) != 0; Mesh *me = obact->data; /* already checked for NULL */ unsigned int index = 0; @@ -2453,7 +2453,7 @@ static void paint_vertsel_circle_select_doSelectVert(void *userData, MVert *mv, } static void paint_vertsel_circle_select(ViewContext *vc, const bool select, const int mval[2], float rad) { - const int use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT); + const bool use_zbuf = (vc->v3d->flag & V3D_ZBUF_SELECT) != 0; Object *ob = vc->obact; Mesh *me = ob->data; bool bbsel; diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 0608c35129d..8bb84d00c83 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -31,9 +31,7 @@ #include "DNA_armature_types.h" -#include "DNA_curve_types.h" #include "DNA_object_types.h" -#include "DNA_meta_types.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" @@ -43,11 +41,9 @@ #include "BKE_armature.h" #include "BKE_context.h" #include "BKE_depsgraph.h" -#include "BKE_lattice.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_object.h" -#include "BKE_editmesh.h" #include "BKE_tracking.h" #include "WM_api.h" @@ -60,7 +56,6 @@ #include "ED_transverts.h" #include "ED_keyframing.h" #include "ED_screen.h" -#include "ED_curve.h" #include "view3d_intern.h" diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index 4888c6846d2..529e5fcf756 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -688,7 +688,7 @@ static bool view3d_boundbox_clip_m4(const BoundBox *bb, float persmatob[4][4]) return false; } -bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const BoundBox *bb, float obmat[4][4]) +bool ED_view3d_boundbox_clip_ex(const RegionView3D *rv3d, const BoundBox *bb, float obmat[4][4]) { /* return 1: draw */ @@ -697,7 +697,7 @@ bool ED_view3d_boundbox_clip_ex(RegionView3D *rv3d, const BoundBox *bb, float ob if (bb == NULL) return true; if (bb->flag & BOUNDBOX_DISABLED) return true; - mul_m4_m4m4(persmatob, rv3d->persmat, obmat); + mul_m4_m4m4(persmatob, (float(*)[4])rv3d->persmat, obmat); return view3d_boundbox_clip_m4(bb, persmatob); } @@ -710,7 +710,7 @@ bool ED_view3d_boundbox_clip(RegionView3D *rv3d, const BoundBox *bb) return view3d_boundbox_clip_m4(bb, rv3d->persmatob); } -float ED_view3d_depth_read_cached(ViewContext *vc, int x, int y) +float ED_view3d_depth_read_cached(const ViewContext *vc, int x, int y) { ViewDepths *vd = vc->rv3d->depths; @@ -729,16 +729,19 @@ 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]) +void ED_view3d_dist_range_get( + const 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) +bool ED_view3d_clip_range_get( + const View3D *v3d, const RegionView3D *rv3d, + float *r_clipsta, float *r_clipend, + const bool use_ortho_factor) { CameraParams params; @@ -758,8 +761,9 @@ bool ED_view3d_clip_range_get(View3D *v3d, RegionView3D *rv3d, float *r_clipsta, } /* also exposed in previewrender.c */ -bool ED_view3d_viewplane_get(View3D *v3d, RegionView3D *rv3d, int winx, int winy, - rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize) +bool ED_view3d_viewplane_get( + const View3D *v3d, const RegionView3D *rv3d, int winx, int winy, + rctf *r_viewplane, float *r_clipsta, float *r_clipend, float *r_pixsize) { CameraParams params; @@ -797,7 +801,7 @@ void ED_view3d_polygon_offset(const RegionView3D *rv3d, const float dist) /** * \param rect optional for picking (can be NULL). */ -void view3d_winmatrix_set(ARegion *ar, View3D *v3d, const rctf *rect) +void view3d_winmatrix_set(ARegion *ar, const View3D *v3d, const rctf *rect) { RegionView3D *rv3d = ar->regiondata; rctf viewplane; @@ -917,7 +921,7 @@ bool ED_view3d_lock(RegionView3D *rv3d) } /* don't set windows active in here, is used by renderwin too */ -void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) +void view3d_viewmatrix_set(Scene *scene, const View3D *v3d, RegionView3D *rv3d) { if (rv3d->persp == RV3D_CAMOB) { /* obs/camera */ if (v3d->camera) { @@ -956,7 +960,7 @@ void view3d_viewmatrix_set(Scene *scene, View3D *v3d, RegionView3D *rv3d) } else if (v3d->ob_centre_cursor) { float vec[3]; - copy_v3_v3(vec, ED_view3d_cursor3d_get(scene, v3d)); + copy_v3_v3(vec, ED_view3d_cursor3d_get(scene, (View3D *)v3d)); translate_m4(rv3d->viewmat, -vec[0], -vec[1], -vec[2]); use_lock_ofs = true; } @@ -1214,7 +1218,7 @@ static bool view3d_localview_init( View3D *v3d = sa->spacedata.first; Base *base; float min[3], max[3], box[3], mid[3]; - float size = 0.0f, size_persp = 0.0f, size_ortho = 0.0f; + float size = 0.0f; unsigned int locallay; bool ok = false; @@ -1252,13 +1256,6 @@ static bool view3d_localview_init( sub_v3_v3v3(box, max, min); size = max_fff(box[0], box[1], box[2]); - - /* do not zoom closer than the near clipping plane */ - size = max_ff(size, v3d->near * 1.5f); - - /* perspective size (we always switch out of camera view so no need to use its lens size) */ - size_persp = ED_view3d_radius_to_persp_dist(focallength_to_fov(v3d->lens, DEFAULT_SENSOR_WIDTH), size / 2.0f) * VIEW3D_MARGIN; - size_ortho = ED_view3d_radius_to_ortho_dist(v3d->lens, size / 2.0f) * VIEW3D_MARGIN; } if (ok == true) { @@ -1275,6 +1272,7 @@ static bool view3d_localview_init( for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = ar->regiondata; + bool ok_dist = true; /* new view values */ Object *camera_old = NULL; @@ -1290,25 +1288,24 @@ static bool view3d_localview_init( camera_old = v3d->camera; } - /* perspective should be a bit farther away to look nice */ - if (rv3d->persp != RV3D_ORTHO) { - dist_new = size_persp; - } - else { - dist_new = size_ortho; + if (rv3d->persp == RV3D_ORTHO) { + if (size < 0.0001f) { + ok_dist = false; + } } - /* correction for window aspect ratio */ - if (ar->winy > 2 && ar->winx > 2) { - float asp = (float)ar->winx / (float)ar->winy; - if (asp < 1.0f) asp = 1.0f / asp; - dist_new *= asp; + if (ok_dist) { + dist_new = ED_view3d_radius_to_dist(v3d, ar, rv3d->persp, true, (size / 2) * VIEW3D_MARGIN); + if (rv3d->persp == RV3D_PERSP) { + /* don't zoom closer than the near clipping plane */ + dist_new = max_ff(dist_new, v3d->near * 1.5f); + } } ED_view3d_smooth_view_ex( wm, win, sa, v3d, ar, camera_old, NULL, - ofs_new, NULL, &dist_new, NULL, + ofs_new, NULL, ok_dist ? &dist_new : NULL, NULL, smooth_viewtx); } } @@ -1715,21 +1712,116 @@ void VIEW3D_OT_game_start(wmOperatorType *ot) /* ************************************** */ -float ED_view3d_pixel_size(RegionView3D *rv3d, const float co[3]) +float ED_view3d_pixel_size(const RegionView3D *rv3d, const float co[3]) { - return mul_project_m4_v3_zfac(rv3d->persmat, co) * rv3d->pixsize * U.pixelsize; + return mul_project_m4_v3_zfac((float(*)[4])rv3d->persmat, co) * rv3d->pixsize * U.pixelsize; } -float ED_view3d_radius_to_persp_dist(const float angle, const float radius) +float ED_view3d_radius_to_dist_persp(const float angle, const float radius) { - return (radius / 2.0f) * fabsf(1.0f / cosf((((float)M_PI) - angle) / 2.0f)); + return radius * (1.0f / tanf(angle / 2.0f)); } -float ED_view3d_radius_to_ortho_dist(const float lens, const float radius) +float ED_view3d_radius_to_dist_ortho(const float lens, const float radius) { return radius / (DEFAULT_SENSOR_WIDTH / lens); } +/** + * Return a new RegionView3D.dist value to fit the \a radius. + * + * \note Depth isn't taken into account, this will fit a flat plane exactly, + * but points towards the view (with a perspective projection), + * may be within the radius but outside the view. eg: + * + * <pre> + * + + * pt --> + /^ radius + * / | + * / | + * view + + + * \ | + * \ | + * \| + * + + * </pre> + * + * \param ar Can be NULL if \a use_aspect is false. + * \param persp Allow the caller to tell what kind of perspective to use (ortho/view/camera) + * \param use_aspect Increase the distance to account for non 1:1 view aspect. + * \param radius The radius will be fitted exactly, typically pre-scaled by a margin (#VIEW3D_MARGIN). + */ +float ED_view3d_radius_to_dist( + const View3D *v3d, const ARegion *ar, + const char persp, const bool use_aspect, + const float radius) +{ + float dist; + + BLI_assert(ELEM(persp, RV3D_ORTHO, RV3D_PERSP, RV3D_CAMOB)); + BLI_assert((persp != RV3D_CAMOB) || v3d->camera); + + if (persp == RV3D_ORTHO) { + dist = ED_view3d_radius_to_dist_ortho(v3d->lens, radius); + } + else { + float lens, sensor_size, zoom; + float angle; + + if (persp == RV3D_CAMOB) { + CameraParams params; + BKE_camera_params_init(¶ms); + params.clipsta = v3d->near; + params.clipend = v3d->far; + BKE_camera_params_from_object(¶ms, v3d->camera); + + lens = params.lens; + sensor_size = BKE_camera_sensor_size(params.sensor_fit, params.sensor_x, params.sensor_y); + + /* ignore 'rv3d->camzoom' because we wan't to fit to the cameras frame */ + zoom = CAMERA_PARAM_ZOOM_INIT_CAMOB; + } + else { + lens = v3d->lens; + sensor_size = DEFAULT_SENSOR_WIDTH; + zoom = CAMERA_PARAM_ZOOM_INIT_PERSP; + } + + angle = focallength_to_fov(lens, sensor_size); + + /* zoom influences lens, correct this by scaling the angle as a distance (by the zoom-level) */ + angle = atanf(tanf(angle / 2.0f) * zoom) * 2.0f; + + dist = ED_view3d_radius_to_dist_persp(angle, radius); + } + + if (use_aspect) { + const RegionView3D *rv3d = ar->regiondata; + + float winx, winy; + + if (persp == RV3D_CAMOB) { + /* camera frame x/y in pixels */ + winx = ar->winx / rv3d->viewcamtexcofac[0]; + winy = ar->winy / rv3d->viewcamtexcofac[1]; + } + else { + winx = ar->winx; + winy = ar->winy; + } + + if (winx && winy) { + float aspect = winx / winy; + if (aspect < 1.0f) { + aspect = 1.0f / aspect; + } + dist *= aspect; + } + } + + return dist; +} + /* view matrix properties utilities */ /* unused */ diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 191eeb05c71..1d5c2a3a169 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -26,8 +26,6 @@ /* defines VIEW3D_OT_navigate - walk modal operator */ -//#define NDOF_WALK_DEBUG -//#define NDOF_WALK_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -58,6 +56,11 @@ #include "view3d_intern.h" /* own include */ +//#define NDOF_WALK_DEBUG +//#define NDOF_WALK_DRAW_TOOMUCH /* is this needed for ndof? - commented so redraw doesnt thrash - campbell */ + +#define USE_TABLET_SUPPORT + /* prototypes */ static float getVelocityZeroTime(const float gravity, const float velocity); @@ -220,8 +223,7 @@ void walk_modal_keymap(wmKeyConfig *keyconf) } -typedef struct WalkTeleport -{ +typedef struct WalkTeleport { eWalkTeleportState state; float duration; /* from user preferences */ float origin[3]; @@ -277,6 +279,14 @@ typedef struct WalkInfo { /* mouse reverse */ bool is_reversed; +#ifdef USE_TABLET_SUPPORT + /* check if we had a cursor event before */ + bool is_cursor_first; + + /* tablet devices (we can't relocate the cursor) */ + bool is_cursor_absolute; +#endif + /* gravity system */ eWalkGravityState gravity_state; float gravity; @@ -345,7 +355,7 @@ static void walk_update_header(bContext *C, WalkInfo *walk) char header[HEADER_LENGTH]; BLI_snprintf(header, HEADER_LENGTH, IFACE_("LMB/Return: confirm, Esc/RMB: cancel, " - "Tab: gravity (%s), " + "Tab: gravity (%s), " "WASD: move around, " "Shift: fast, Alt: slow, " "QE: up and down, MMB/Space: teleport, V: jump, " @@ -520,6 +530,12 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->is_reversed = ((U.walk_navigation.flag & USER_WALK_MOUSE_REVERSE) != 0); +#ifdef USE_TABLET_SUPPORT + walk->is_cursor_first = true; + + walk->is_cursor_absolute = false; +#endif + walk->active_directions = 0; #ifdef NDOF_WALK_DRAW_TOOMUCH @@ -587,10 +603,16 @@ static int walkEnd(bContext *C, WalkInfo *walk) /* restore the cursor */ WM_cursor_modal_restore(win); - /* center the mouse */ - WM_cursor_warp(win, - walk->ar->winrct.xmin + walk->center_mval[0], - walk->ar->winrct.ymin + walk->center_mval[1]); +#ifdef USE_TABLET_SUPPORT + if (walk->is_cursor_absolute == false) +#endif + { + /* center the mouse */ + WM_cursor_warp( + win, + walk->ar->winrct.xmin + walk->center_mval[0], + walk->ar->winrct.ymin + walk->center_mval[1]); + } if (walk->state == WALK_CONFIRM) { MEM_freeN(walk); @@ -618,6 +640,27 @@ static void walkEvent(bContext *C, wmOperator *UNUSED(op), WalkInfo *walk, const } else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { +#ifdef USE_TABLET_SUPPORT + if (walk->is_cursor_first) { + /* wait until we get the 'warp' event */ + if ((walk->center_mval[0] == event->mval[0]) && + (walk->center_mval[1] == event->mval[1])) + { + walk->is_cursor_first = false; + } + return; + } + + if ((walk->is_cursor_absolute == false) && WM_event_is_absolute(event)) { + walk->is_cursor_absolute = true; + copy_v2_v2_int(walk->prev_mval, event->mval); + copy_v2_v2_int(walk->center_mval, event->mval); + /* without this we can't turn 180d */ + CLAMP_MIN(walk->mouse_speed, 4.0f); + } +#endif /* USE_TABLET_SUPPORT */ + + walk->moffset[0] += event->mval[0] - walk->prev_mval[0]; walk->moffset[1] += event->mval[1] - walk->prev_mval[1]; @@ -628,6 +671,12 @@ static void walkEvent(bContext *C, wmOperator *UNUSED(op), WalkInfo *walk, const { walk->redraw = true; +#ifdef USE_TABLET_SUPPORT + if (walk->is_cursor_absolute) { + /* pass */ + } + else +#endif if (wm_event_is_last_mousemove(event)) { wmWindow *win = CTX_wm_window(C); diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index 0c360474b78..0bc38f81dd7 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -55,6 +55,10 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() +if(WITH_LEGACY_DEPSGRAPH) + add_definitions(-DWITH_LEGACY_DEPSGRAPH) +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 e249856b747..1a2e9ab074a 100644 --- a/source/blender/editors/transform/SConscript +++ b/source/blender/editors/transform/SConscript @@ -50,4 +50,7 @@ defs = env['BF_GL_DEFINITIONS'] if env['WITH_BF_INTERNATIONAL']: defs.append('WITH_INTERNATIONAL') +if env['WITH_BF_LEGACY_DEPSGRAPH']: + defs.append('WITH_LEGACY_DEPSGRAPH') + env.BlenderLib ( 'bf_editors_transform', sources, incs, defs, libtype=['core'], priority=[40] ) diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 061d00424b8..68d77dec619 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -54,6 +54,7 @@ #include "BLI_memarena.h" #include "BKE_nla.h" +#include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" #include "BKE_context.h" #include "BKE_constraint.h" @@ -98,11 +99,13 @@ static void drawTransformApply(const struct bContext *C, ARegion *ar, void *arg) static void doEdgeSlide(TransInfo *t, float perc); static void doVertSlide(TransInfo *t, float perc); -static void drawEdgeSlide(const struct bContext *C, TransInfo *t); -static void drawVertSlide(const struct bContext *C, TransInfo *t); +static void drawEdgeSlide(TransInfo *t); +static void drawVertSlide(TransInfo *t); static void len_v3_ensure(float v[3], const float length); static void postInputRotation(TransInfo *t, float values[3]); +static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around); + /* Transform Callbacks */ static void initBend(TransInfo *t); @@ -164,6 +167,7 @@ static void applyBoneEnvelope(TransInfo *t, const int mval[2]); static void initBoneRoll(TransInfo *t); static void applyBoneRoll(TransInfo *t, const int mval[2]); +static void initEdgeSlide_ex(TransInfo *t, bool use_double_side); static void initEdgeSlide(TransInfo *t); static eRedrawFlag handleEventEdgeSlide(TransInfo *t, const struct wmEvent *event); static void applyEdgeSlide(TransInfo *t, const int mval[2]); @@ -235,6 +239,38 @@ void setTransformViewMatrices(TransInfo *t) calculateCenter2D(t); } +void setTransformViewAspect(TransInfo *t, float r_aspect[3]) +{ + copy_v3_fl(r_aspect, 1.0f); + + if (t->spacetype == SPACE_IMAGE) { + SpaceImage *sima = t->sa->spacedata.first; + + if (t->options & CTX_MASK) { + ED_space_image_get_aspect(sima, &r_aspect[0], &r_aspect[1]); + } + else if (t->options & CTX_PAINT_CURVE) { + /* pass */ + } + else { + ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]); + } + } + else if (t->spacetype == SPACE_CLIP) { + SpaceClip *sclip = t->sa->spacedata.first; + + if (t->options & CTX_MOVIECLIP) { + ED_space_clip_get_aspect_dimension_aware(sclip, &r_aspect[0], &r_aspect[1]); + } + else { + ED_space_clip_get_aspect(sclip, &r_aspect[0], &r_aspect[1]); + } + } + else if (t->spacetype == SPACE_IPO) { + /* depemds on context of usage */ + } +} + static void convertViewVec2D(View2D *v2d, float r_vec[3], int dx, int dy) { float divx, divy; @@ -288,25 +324,19 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) } } 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); } - r_vec[0] *= aspx; - r_vec[1] *= aspy; + r_vec[0] *= t->aspect[0]; + r_vec[1] *= t->aspect[1]; } else if (ELEM(t->spacetype, SPACE_IPO, SPACE_NLA)) { convertViewVec2D(t->view, r_vec, dx, dy); @@ -315,8 +345,6 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) convertViewVec2D(&t->ar->v2d, r_vec, dx, dy); } else if (t->spacetype == SPACE_CLIP) { - float aspx, aspy; - if (t->options & CTX_MASK) { convertViewVec2D_mask(t->view, r_vec, dx, dy); } @@ -324,21 +352,8 @@ void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy) convertViewVec2D(t->view, r_vec, dx, dy); } - if (t->options & CTX_MOVIECLIP) { - ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy); - } - else if (t->options & CTX_MASK) { - /* TODO - NOT WORKING, this isnt so bad since its only display aspect */ - ED_space_clip_get_aspect(t->sa->spacedata.first, &aspx, &aspy); - } - else { - /* should never happen, quiet warnings */ - BLI_assert(0); - aspx = aspy = 1.0f; - } - - r_vec[0] *= aspx; - r_vec[1] *= aspy; + r_vec[0] *= t->aspect[0]; + r_vec[1] *= t->aspect[1]; } else { printf("%s: called in an invalid context\n", __func__); @@ -360,15 +375,10 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr SpaceImage *sima = t->sa->spacedata.first; if (t->options & CTX_MASK) { - float aspx, aspy; float v[2]; - ED_space_image_get_aspect(sima, &aspx, &aspy); - - copy_v2_v2(v, vec); - - v[0] = v[0] / aspx; - v[1] = v[1] / aspy; + v[0] = vec[0] / t->aspect[0]; + v[1] = vec[1] / t->aspect[1]; BKE_mask_coord_to_image(sima->image, &sima->iuser, v, v); @@ -382,11 +392,10 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr adr[1] = vec[1]; } else { - float aspx, aspy, v[2]; + float v[2]; - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); - v[0] = vec[0] / aspx; - v[1] = vec[1] / aspy; + v[0] = vec[0] / t->aspect[0]; + v[1] = vec[1] / t->aspect[1]; UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]); } @@ -431,15 +440,10 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr MovieClip *clip = ED_space_clip_get_clip(sc); if (clip) { - float aspx, aspy; float v[2]; - ED_space_clip_get_aspect(sc, &aspx, &aspy); - - copy_v2_v2(v, vec); - - v[0] = v[0] / aspx; - v[1] = v[1] / aspy; + v[0] = vec[0] / t->aspect[0]; + v[1] = vec[1] / t->aspect[1]; BKE_mask_coord_to_movieclip(sc->clip, &sc->user, v, v); @@ -454,13 +458,10 @@ void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DPr } } else if (t->options & CTX_MOVIECLIP) { - float v[2], aspx, aspy; - - copy_v2_v2(v, vec); - ED_space_clip_get_aspect_dimension_aware(t->sa->spacedata.first, &aspx, &aspy); + float v[2]; - v[0] /= aspx; - v[1] /= aspy; + v[0] = vec[0] / t->aspect[0]; + v[1] = vec[1] / t->aspect[1]; UI_view2d_view_to_region(t->view, v[0], v[1], &adr[0], &adr[1]); } @@ -516,7 +517,6 @@ void applyAspectRatio(TransInfo *t, float vec[2]) { if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION) && !(t->options & CTX_PAINT_CURVE)) { SpaceImage *sima = t->sa->spacedata.first; - float aspx, aspy; if ((sima->flag & SI_COORDFLOATS) == 0) { int width, height; @@ -526,28 +526,13 @@ void applyAspectRatio(TransInfo *t, float vec[2]) vec[1] *= height; } - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - vec[0] /= aspx; - vec[1] /= aspy; + vec[0] /= t->aspect[0]; + vec[1] /= t->aspect[1]; } else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) { if (t->options & (CTX_MOVIECLIP | CTX_MASK)) { - SpaceClip *sc = t->sa->spacedata.first; - float aspx, aspy; - - - if (t->options & CTX_MOVIECLIP) { - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); - - vec[0] /= aspx; - vec[1] /= aspy; - } - else if (t->options & CTX_MASK) { - ED_space_clip_get_aspect(sc, &aspx, &aspy); - - vec[0] /= aspx; - vec[1] /= aspy; - } + vec[0] /= t->aspect[0]; + vec[1] /= t->aspect[1]; } } } @@ -556,7 +541,6 @@ void removeAspectRatio(TransInfo *t, float vec[2]) { if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { SpaceImage *sima = t->sa->spacedata.first; - float aspx, aspy; if ((sima->flag & SI_COORDFLOATS) == 0) { int width, height; @@ -566,24 +550,13 @@ void removeAspectRatio(TransInfo *t, float vec[2]) vec[1] /= height; } - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - vec[0] *= aspx; - vec[1] *= aspy; + vec[0] *= t->aspect[0]; + vec[1] *= t->aspect[1]; } else if ((t->spacetype == SPACE_CLIP) && (t->mode == TFM_TRANSLATION)) { if (t->options & (CTX_MOVIECLIP | CTX_MASK)) { - SpaceClip *sc = t->sa->spacedata.first; - float aspx = 1.0f, aspy = 1.0f; - - if (t->options & CTX_MOVIECLIP) { - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); - } - else if (t->options & CTX_MASK) { - ED_space_clip_get_aspect(sc, &aspx, &aspy); - } - - vec[0] *= aspx; - vec[1] *= aspy; + vec[0] *= t->aspect[0]; + vec[1] *= t->aspect[1]; } } } @@ -729,7 +702,6 @@ static void view_editmove(unsigned short UNUSED(event)) switch (event) { case WHEELUPMOUSE: - if (G.qual & LR_SHIFTKEY) { if (G.qual & LR_ALTKEY) { G.qual &= ~LR_SHIFTKEY; @@ -795,35 +767,38 @@ static void view_editmove(unsigned short UNUSED(event)) /* ************************************************* */ /* NOTE: these defines are saved in keymap files, do not change values but just add new ones */ -#define TFM_MODAL_CANCEL 1 -#define TFM_MODAL_CONFIRM 2 -#define TFM_MODAL_TRANSLATE 3 -#define TFM_MODAL_ROTATE 4 -#define TFM_MODAL_RESIZE 5 -#define TFM_MODAL_SNAP_INV_ON 6 -#define TFM_MODAL_SNAP_INV_OFF 7 -#define TFM_MODAL_SNAP_TOGGLE 8 -#define TFM_MODAL_AXIS_X 9 -#define TFM_MODAL_AXIS_Y 10 -#define TFM_MODAL_AXIS_Z 11 -#define TFM_MODAL_PLANE_X 12 -#define TFM_MODAL_PLANE_Y 13 -#define TFM_MODAL_PLANE_Z 14 -#define TFM_MODAL_CONS_OFF 15 -#define TFM_MODAL_ADD_SNAP 16 -#define TFM_MODAL_REMOVE_SNAP 17 -/* 18 and 19 used by numinput, defined in transform.h - * */ -#define TFM_MODAL_PROPSIZE_UP 20 -#define TFM_MODAL_PROPSIZE_DOWN 21 -#define TFM_MODAL_AUTOIK_LEN_INC 22 -#define TFM_MODAL_AUTOIK_LEN_DEC 23 - -#define TFM_MODAL_EDGESLIDE_UP 24 -#define TFM_MODAL_EDGESLIDE_DOWN 25 +enum { + TFM_MODAL_CANCEL = 1, + TFM_MODAL_CONFIRM = 2, + TFM_MODAL_TRANSLATE = 3, + TFM_MODAL_ROTATE = 4, + TFM_MODAL_RESIZE = 5, + TFM_MODAL_SNAP_INV_ON = 6, + TFM_MODAL_SNAP_INV_OFF = 7, + TFM_MODAL_SNAP_TOGGLE = 8, + TFM_MODAL_AXIS_X = 9, + TFM_MODAL_AXIS_Y = 10, + TFM_MODAL_AXIS_Z = 11, + TFM_MODAL_PLANE_X = 12, + TFM_MODAL_PLANE_Y = 13, + TFM_MODAL_PLANE_Z = 14, + TFM_MODAL_CONS_OFF = 15, + TFM_MODAL_ADD_SNAP = 16, + TFM_MODAL_REMOVE_SNAP = 17, + +/* 18 and 19 used by numinput, defined in transform.h */ + + TFM_MODAL_PROPSIZE_UP = 20, + TFM_MODAL_PROPSIZE_DOWN = 21, + TFM_MODAL_AUTOIK_LEN_INC = 22, + TFM_MODAL_AUTOIK_LEN_DEC = 23, + + TFM_MODAL_EDGESLIDE_UP = 24, + TFM_MODAL_EDGESLIDE_DOWN = 25, /* for analog input, like trackpad */ -#define TFM_MODAL_PROPSIZE 26 + TFM_MODAL_PROPSIZE = 26, +}; /* called in transform_ops.c, on each regeneration of keymaps */ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) @@ -868,8 +843,8 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) /* items for modal map */ WM_modalkeymap_add_item(keymap, ESCKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CANCEL); WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); - WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, RETKEY, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_PRESS, KM_ANY, 0, TFM_MODAL_CONFIRM); WM_modalkeymap_add_item(keymap, GKEY, KM_PRESS, 0, 0, TFM_MODAL_TRANSLATE); WM_modalkeymap_add_item(keymap, RKEY, KM_PRESS, 0, 0, TFM_MODAL_ROTATE); @@ -888,8 +863,12 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP); WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN); + WM_modalkeymap_add_item(keymap, PAGEUPKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP); + WM_modalkeymap_add_item(keymap, PAGEDOWNKEY, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN); WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_UP); WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, 0, 0, TFM_MODAL_PROPSIZE_DOWN); + WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_UP); + WM_modalkeymap_add_item(keymap, WHEELUPMOUSE, KM_PRESS, KM_SHIFT, 0, TFM_MODAL_PROPSIZE_DOWN); WM_modalkeymap_add_item(keymap, MOUSEPAN, 0, 0, 0, TFM_MODAL_PROPSIZE); WM_modalkeymap_add_item(keymap, WHEELDOWNMOUSE, KM_PRESS, KM_ALT, 0, TFM_MODAL_EDGESLIDE_UP); @@ -907,7 +886,7 @@ static void transform_event_xyz_constraint(TransInfo *t, short key_type, char cm { if (!(t->flag & T_NO_CONSTRAINT)) { int constraint_axis, constraint_plane; - int edit_2d = (t->flag & T_2D_EDIT); + const bool edit_2d = (t->flag & T_2D_EDIT) != 0; const char *msg1 = "", *msg2 = "", *msg3 = ""; char axis; @@ -1230,7 +1209,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_PROPSIZE_UP: if (t->flag & T_PROP_EDIT) { - t->prop_size *= 1.1f; + t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far); calculatePropRatio(t); @@ -1240,17 +1219,12 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_PROPSIZE_DOWN: if (t->flag & T_PROP_EDIT) { - t->prop_size *= 0.90909090f; + t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw |= TREDRAW_HARD; handled = true; } break; - case TFM_MODAL_EDGESLIDE_UP: - case TFM_MODAL_EDGESLIDE_DOWN: - t->redraw |= TREDRAW_HARD; - handled = true; - break; case TFM_MODAL_AUTOIK_LEN_INC: if (t->flag & T_AUTOIK) { transform_autoik_update(t, 1); @@ -1265,6 +1239,9 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } break; + /* Those two are only handled in transform's own handler, see T44634! */ + case TFM_MODAL_EDGESLIDE_UP: + case TFM_MODAL_EDGESLIDE_DOWN: default: break; } @@ -1413,7 +1390,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case PADPLUSKEY: if (event->alt && t->flag & T_PROP_EDIT) { - t->prop_size *= 1.1f; + t->prop_size *= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) t->prop_size = min_ff(t->prop_size, ((View3D *)t->view)->far); calculatePropRatio(t); @@ -1434,7 +1411,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case PADMINUS: if (event->alt && t->flag & T_PROP_EDIT) { - t->prop_size *= 0.90909090f; + t->prop_size /= (t->modifiers & MOD_PRECISION) ? 1.01f : 1.1f; calculatePropRatio(t); t->redraw = TREDRAW_HARD; handled = true; @@ -1571,7 +1548,7 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa if (cent3d) { // Copy center from constraint center. Transform center can be local - copy_v3_v3(cent3d, t->con.center); + copy_v3_v3(cent3d, t->center_global); } } @@ -1824,8 +1801,8 @@ static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), voi drawSnapping(C, t); /* edge slide, vert slide */ - drawEdgeSlide(C, t); - drawVertSlide(C, t); + drawEdgeSlide(t); + drawVertSlide(t); } /* just draw a little warning message in the top-right corner of the viewport to warn that autokeying is enabled */ @@ -1934,7 +1911,11 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) if ((prop = RNA_struct_find_property(op->ptr, "proportional")) && !RNA_property_is_set(op->ptr, prop)) { - if (t->obedit) + if (t->spacetype == SPACE_IPO) + ts->proportional_fcurve = proportional; + else if (t->spacetype == SPACE_ACTION) + ts->proportional_action = proportional; + else if (t->obedit) ts->proportional = proportional; else if (t->options & CTX_MASK) ts->proportional_mask = (proportional != PROP_EDIT_OFF); @@ -1985,7 +1966,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } if ((prop = RNA_struct_find_property(op->ptr, "mirror"))) { - RNA_property_boolean_set(op->ptr, prop, t->flag & T_MIRROR); + RNA_property_boolean_set(op->ptr, prop, (t->flag & T_MIRROR) != 0); } if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) { @@ -2013,6 +1994,18 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) RNA_property_boolean_set_array(op->ptr, prop, constraint_axis); } + + { + const char *prop_id = NULL; + if (t->mode == TFM_SHRINKFATTEN) { + prop_id = "use_even_offset"; + } + + if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) { + + RNA_property_boolean_set(op->ptr, prop, (t->flag & T_ALT_TRANSFORM) != 0); + } + } } /* note: caller needs to free 't' on a 0 return */ @@ -2085,6 +2078,18 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t); } + else if (t->spacetype == SPACE_IPO) { + unit_m3(t->spacemtx); + t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); + //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t); + } + else if (t->spacetype == SPACE_ACTION) { + unit_m3(t->spacemtx); + t->draw_handle_view = ED_region_draw_cb_activate(t->ar->type, drawTransformView, t, REGION_DRAW_POST_VIEW); + //t->draw_handle_pixel = ED_region_draw_cb_activate(t->ar->type, drawTransformPixel, t, REGION_DRAW_POST_PIXEL); + t->draw_handle_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), helpline_poll, drawHelpline, t); + } else unit_m3(t->spacemtx); @@ -2184,18 +2189,28 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */ bArmature *arm = t->poseobj->data; - if (arm->drawtype == ARM_ENVELOPE) + if (arm->drawtype == ARM_ENVELOPE) { initBoneEnvelope(t); - else + t->mode = TFM_BONE_ENVELOPE_DIST; + } + else { initBoneSize(t); + } break; } case TFM_BONE_ENVELOPE: initBoneEnvelope(t); break; + case TFM_BONE_ENVELOPE_DIST: + initBoneEnvelope(t); + t->mode = TFM_BONE_ENVELOPE_DIST; + break; case TFM_EDGE_SLIDE: - initEdgeSlide(t); + { + const bool use_double_side = (op ? !RNA_boolean_get(op->ptr, "single_side") : true); + initEdgeSlide_ex(t, use_double_side); break; + } case TFM_VERT_SLIDE: initVertSlide(t); break; @@ -2254,7 +2269,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve return 0; } - /* overwrite initial values if operator supplied a non-null vector */ if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) { float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */ @@ -2512,8 +2526,8 @@ static void protectedQuaternionBits(short protectflag, float quat[4], const floa static void constraintTransLim(TransInfo *t, TransData *td) { if (td->con) { - bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT); - bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT); + const bConstraintTypeInfo *ctiLoc = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_LOCLIMIT); + const bConstraintTypeInfo *ctiDist = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_DISTLIMIT); bConstraintOb cob = {NULL}; bConstraint *con; @@ -2528,7 +2542,7 @@ static void constraintTransLim(TransInfo *t, TransData *td) /* Evaluate valid constraints */ for (con = td->con; con; con = con->next) { - bConstraintTypeInfo *cti = NULL; + const bConstraintTypeInfo *cti = NULL; ListBase targets = {NULL, NULL}; /* only consider constraint if enabled */ @@ -2615,7 +2629,7 @@ static void constraintob_from_transdata(bConstraintOb *cob, TransData *td) static void constraintRotLim(TransInfo *UNUSED(t), TransData *td) { if (td->con) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_ROTLIMIT); bConstraintOb cob; bConstraint *con; bool do_limit = false; @@ -2682,7 +2696,7 @@ static void constraintRotLim(TransInfo *UNUSED(t), TransData *td) static void constraintSizeLim(TransInfo *t, TransData *td) { if (td->con && td->ext) { - bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT); + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_from_type(CONSTRAINT_TYPE_SIZELIMIT); bConstraintOb cob = {NULL}; bConstraint *con; float size_sign[3], size_abs[3]; @@ -2806,10 +2820,11 @@ static void initBend(TransInfo *t) t->num.unit_type[0] = B_UNIT_ROTATION; t->num.unit_type[1] = B_UNIT_LENGTH; - t->flag |= T_NO_CONSTRAINT; + t->flag |= T_NO_CONSTRAINT | T_FREE_CUSTOMDATA; //copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view)); calculateCenterCursor(t, t->center); + calculateCenterGlobal(t); t->val = 0.0f; @@ -2884,6 +2899,8 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) values.scale = values.scale / data->warp_init_dist; } + copy_v2_v2(t->values, values.vector); + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN * 2]; @@ -2901,8 +2918,6 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) WM_bool_as_string(is_clamp)); } - copy_v2_v2(t->values, values.vector); - values.angle *= -1.0f; values.scale *= data->warp_init_dist; @@ -2957,6 +2972,13 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) add_v3_v3(vec, pivot); mul_m3_v3(td->smtx, vec); + + /* rotation */ + if ((t->flag & T_POINTS) == 0) { + ElementRotation(t, td, mat, V3D_LOCAL); + } + + /* location */ copy_v3_v3(td->loc, vec); } @@ -3052,6 +3074,8 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &value); + t->values[0] = value; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -3065,8 +3089,6 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Shear: %.3f %s (Press X or Y to set shear axis)"), value, t->proptext); } - t->values[0] = value; - unit_m3(smat); // Custom data signals shear direction @@ -3329,7 +3351,7 @@ static void applyResize(TransInfo *t, const int mval[2]) ratio = t->values[0]; } - size[0] = size[1] = size[2] = ratio; + copy_v3_fl(size, ratio); snapGridIncrement(t, size); @@ -3435,12 +3457,10 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) { TransData *td; float size[3], mat[3][3]; - float ratio; int i; char str[MAX_INFO_LEN]; - ratio = t->values[0]; - size[0] = size[1] = size[2] = ratio; + copy_v3_fl(size, t->values[0]); snapGridIncrement(t, size); @@ -3545,10 +3565,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &ratio); - if (ratio < 0) - ratio = 0.0f; - else if (ratio > 1) - ratio = 1.0f; + CLAMP(ratio, 0.0f, 1.0f); t->values[0] = ratio; @@ -3917,6 +3934,8 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) final = angle_wrap_rad(final); } + t->values[0] = final; + if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -3933,8 +3952,6 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } - t->values[0] = final; - applyRotationValue(t, final, t->axis); recalcData(t); @@ -4006,7 +4023,9 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) char str[MAX_INFO_LEN]; size_t ofs = 0; float axis1[3], axis2[3]; +#if 0 /* UNUSED */ float mat[3][3], totmat[3][3], smat[3][3]; +#endif float phi[2]; copy_v3_v3(axis1, t->persinv[0]); @@ -4014,13 +4033,14 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) normalize_v3(axis1); normalize_v3(axis2); - phi[0] = t->values[0]; - phi[1] = t->values[1]; + copy_v2_v2(phi, t->values); snapGridIncrement(t, phi); applyNumInput(&t->num, phi); + copy_v2_v2(t->values, phi); + if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN * 2]; @@ -4038,6 +4058,7 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); } +#if 0 /* UNUSED */ axis_angle_normalized_to_mat3(smat, axis1, phi[0]); axis_angle_normalized_to_mat3(totmat, axis2, phi[1]); @@ -4045,6 +4066,7 @@ static void applyTrackball(TransInfo *t, const int UNUSED(mval[2])) // TRANSFORM_FIX_ME //copy_m3_m3(t->mat, mat); // used in manipulator +#endif applyTrackballValue(t, axis1, axis2, phi); @@ -4370,6 +4392,8 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &distance); + t->values[0] = -distance; + /* header print for NumInput */ ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Shrink/Fatten:"), MAX_INFO_LEN - ofs); if (hasNumInput(&t->num)) { @@ -4397,9 +4421,6 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); /* done with header string */ - - t->values[0] = -distance; - for (i = 0; i < t->total; i++, td++) { float tdistance; /* temp dist */ if (td->flag & TD_NOACTION) @@ -4466,6 +4487,8 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &final); + t->values[0] = final; + if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4543,6 +4566,8 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &ratio); + t->values[0] = ratio; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4621,6 +4646,8 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &ratio); + t->values[0] = ratio; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4719,6 +4746,8 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &ratio); + t->values[0] = ratio; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4791,6 +4820,8 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &distance); + t->values[0] = distance; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4804,8 +4835,6 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Push/Pull: %.4f%s %s"), distance, t->con.text, t->proptext); } - t->values[0] = distance; - if (t->con.applyRot && t->con.mode & CON_APPLY) { t->con.applyRot(t, NULL, axis, NULL); } @@ -4878,12 +4907,14 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) weight = t->values[0]; - if (weight > 1.0f) weight = 1.0f; + CLAMP_MAX(weight, 1.0f); snapGridIncrement(t, &weight); applyNumInput(&t->num, &weight); + t->values[0] = weight; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -4956,12 +4987,14 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) crease = t->values[0]; - if (crease > 1.0f) crease = 1.0f; + CLAMP_MAX(crease, 1.0f); snapGridIncrement(t, &crease); applyNumInput(&t->num, &crease); + t->values[0] = crease; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -5095,7 +5128,7 @@ static void applyBoneSize(TransInfo *t, const int mval[2]) ratio = t->values[0]; } - size[0] = size[1] = size[2] = ratio; + copy_v3_fl(size, ratio); snapGridIncrement(t, size); @@ -5103,6 +5136,8 @@ static void applyBoneSize(TransInfo *t, const int mval[2]) constraintNumInput(t, size); } + copy_v3_v3(t->values, size); + size_to_mat3(mat, size); if (t->con.applySize) { @@ -5169,6 +5204,8 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &ratio); + t->values[0] = ratio; + /* header print for NumInput */ if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -5256,11 +5293,22 @@ static void slide_origdata_create_data_vert( loop_weights = BLI_array_alloca(loop_weights, l_num); for (j = 0; j < l_num; j++) { BMLoop *l = BM_iter_step(&liter); - if (!BLI_ghash_haskey(sod->origfaces, l->f)) { + BMLoop *l_prev, *l_next; + void **val_p; + if (!BLI_ghash_ensure_p(sod->origfaces, l->f, &val_p)) { BMFace *f_copy = BM_face_copy(sod->bm_origfaces, bm, l->f, true, true); - BLI_ghash_insert(sod->origfaces, l->f, f_copy); + *val_p = f_copy; } - loop_weights[j] = BM_loop_calc_face_angle(l); + + if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && + (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) + { + loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); + } + else { + loop_weights[j] = 0.0f; + } + } /* store cd_loop_groups */ @@ -5328,6 +5376,13 @@ static void slide_origdata_interp_data_vert( int j, l_num; float *loop_weights; const bool do_loop_weight = (len_squared_v3v3(sv->v->co, sv->co_orig_3d) > FLT_EPSILON); + const float *v_proj_axis = sv->v->no; + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + float v_proj[3][3]; + + if (do_loop_weight) { + project_plane_v3_v3v3(v_proj[1], sv->co_orig_3d, v_proj_axis); + } // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, sv->v); @@ -5348,11 +5403,44 @@ static void slide_origdata_interp_data_vert( /* weight the loop */ if (do_loop_weight) { - const float *v_prev = slide_origdata_orig_vert_co(sod, l->prev->v); - const float *v_next = slide_origdata_orig_vert_co(sod, l->next->v); - const float dist = dist_signed_squared_to_corner_v3v3v3(sv->v->co, v_prev, sv->co_orig_3d, v_next, f_copy->no); - const float eps = 0.00001f; - loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + const float eps = 1.0e-8f; + const BMLoop *l_prev = l->prev; + const BMLoop *l_next = l->next; + const float *co_prev = slide_origdata_orig_vert_co(sod, l_prev->v); + const float *co_next = slide_origdata_orig_vert_co(sod, l_next->v); + bool co_prev_ok; + bool co_next_ok; + + + /* In the unlikely case that we're next to a zero length edge - walk around the to the next. + * Since we only need to check if the vertex is in this corner, + * its not important _which_ loop - as long as its not overlapping 'sv->co_orig_3d', see: T45096. */ + project_plane_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && + ((l_prev = l_prev->prev) != l->next))) + { + co_prev = slide_origdata_orig_vert_co(sod, l_prev->v); + project_plane_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + } + project_plane_v3_v3v3(v_proj[2], co_next, v_proj_axis); + while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && + ((l_next = l_next->next) != l->prev))) + { + co_next = slide_origdata_orig_vert_co(sod, l_next->v); + project_plane_v3_v3v3(v_proj[2], co_next, v_proj_axis); + } + + if (co_prev_ok && co_next_ok) { + const float dist = dist_signed_squared_to_corner_v3v3v3(sv->v->co, UNPACK3(v_proj), v_proj_axis); + + loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + if (UNLIKELY(!isfinite(loop_weights[j]))) { + loop_weights[j] = 0.0f; + } + } + else { + loop_weights[j] = 0.0f; + } } } @@ -5423,6 +5511,19 @@ static void slide_origdata_free_date( /** \name Transform Edge Slide * \{ */ +static void calcEdgeSlideCustomPoints(struct TransInfo *t) +{ + EdgeSlideData *sld = t->customData; + + setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start); + + /* setCustomPoints isn't normally changing as the mouse moves, + * in this case apply mouse input immediatly so we don't refresh + * with the value from the previous points */ + applyMouseInput(t, &t->mouse, t->mval, t->values); +} + + static BMEdge *get_other_edge(BMVert *v, BMEdge *e) { BMIter iter; @@ -5438,7 +5539,7 @@ static BMEdge *get_other_edge(BMVert *v, BMEdge *e) } /* interpoaltes along a line made up of 2 segments (used for edge slide) */ -static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float t) +static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], float t) { float t_mid, t_delta; @@ -5446,17 +5547,28 @@ static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[ t_mid = line_point_factor_v3(v2, v1, v3); t_delta = t - t_mid; - if (fabsf(t_delta) < FLT_EPSILON) { - copy_v3_v3(p, v2); - } - else if (t_delta < 0.0f) { - interp_v3_v3v3(p, v1, v2, t / t_mid); + if (t_delta < 0.0f) { + if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) { + copy_v3_v3(p, v2); + } + else { + interp_v3_v3v3(p, v1, v2, t / t_mid); + } } else { - interp_v3_v3v3(p, v2, v3, (t - t_mid) / (1.0f - t_mid)); + t = t - t_mid; + t_mid = 1.0f - t_mid; + + if (UNLIKELY(fabsf(t_mid) < FLT_EPSILON)) { + copy_v3_v3(p, v3); + } + else { + interp_v3_v3v3(p, v2, v3, t / t_mid); + } } } + static void len_v3_ensure(float v[3], const float length) { normalize_v3(v); @@ -5601,7 +5713,171 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, return NULL; } -static void calcNonProportionalEdgeSlide(TransInfo *t, EdgeSlideData *sld, const float mval[2]) +/** + * Calculate screenspace `mval_start` / `mval_end`, optionally slide direction. + */ +static void calcEdgeSlide_mval_range( + TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr, + const float mval[2], const bool use_btree_disp, const bool use_calc_direction) +{ + TransDataEdgeSlideVert *sv_array = sld->sv; + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + ARegion *ar = t->ar; + View3D *v3d = NULL; + RegionView3D *rv3d = NULL; + float projectMat[4][4]; + BMBVHTree *btree; + + /* only for use_calc_direction */ + float (*loop_dir)[3] = NULL, *loop_maxdist = NULL; + + float mval_start[2], mval_end[2]; + float mval_dir[3], dist_best_sq; + BMIter iter; + BMEdge *e; + + if (t->spacetype == SPACE_VIEW3D) { + /* background mode support */ + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + } + + if (!rv3d) { + /* ok, let's try to survive this */ + unit_m4(projectMat); + } + else { + ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); + } + + if (use_btree_disp) { + btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); + } + else { + btree = NULL; + } + + /* find mouse vectors, the global one, and one per loop in case we have + * multiple loops selected, in case they are oriented different */ + zero_v3(mval_dir); + dist_best_sq = -1.0f; + + if (use_calc_direction) { + loop_dir = MEM_callocN(sizeof(float[3]) * loop_nr, "sv loop_dir"); + loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"); + copy_vn_fl(loop_maxdist, loop_nr, -1.0f); + } + + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + int i; + + /* search cross edges for visible edge to the mouse cursor, + * then use the shared vertex to calculate screen vector*/ + for (i = 0; i < 2; i++) { + BMIter iter_other; + BMEdge *e_other; + + BMVert *v = i ? e->v1 : e->v2; + BM_ITER_ELEM (e_other, &iter_other, v, BM_EDGES_OF_VERT) { + /* screen-space coords */ + float sco_a[3], sco_b[3]; + float dist_sq; + int j, l_nr; + + if (BM_elem_flag_test(e_other, BM_ELEM_SELECT)) + continue; + + /* This test is only relevant if object is not wire-drawn! See [#32068]. */ + if (use_btree_disp && !BMBVH_EdgeVisible(btree, e_other, ar, v3d, t->obedit)) { + continue; + } + + BLI_assert(sv_table[BM_elem_index_get(v)] != -1); + j = sv_table[BM_elem_index_get(v)]; + + if (sv_array[j].v_side[1]) { + ED_view3d_project_float_v3_m4(ar, sv_array[j].v_side[1]->co, sco_b, projectMat); + } + else { + add_v3_v3v3(sco_b, v->co, sv_array[j].dir_side[1]); + ED_view3d_project_float_v3_m4(ar, sco_b, sco_b, projectMat); + } + + if (sv_array[j].v_side[0]) { + ED_view3d_project_float_v3_m4(ar, sv_array[j].v_side[0]->co, sco_a, projectMat); + } + else { + add_v3_v3v3(sco_a, v->co, sv_array[j].dir_side[0]); + ED_view3d_project_float_v3_m4(ar, sco_a, sco_a, projectMat); + } + + /* global direction */ + dist_sq = dist_squared_to_line_segment_v2(mval, sco_b, sco_a); + if ((dist_best_sq == -1.0f) || + /* intentionally use 2d size on 3d vector */ + (dist_sq < dist_best_sq && (len_squared_v2v2(sco_b, sco_a) > 0.1f))) + { + dist_best_sq = dist_sq; + sub_v3_v3v3(mval_dir, sco_b, sco_a); + } + + if (use_calc_direction) { + /* per loop direction */ + l_nr = sv_array[j].loop_nr; + if (loop_maxdist[l_nr] == -1.0f || dist_sq < loop_maxdist[l_nr]) { + loop_maxdist[l_nr] = dist_sq; + sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a); + } + } + } + } + } + } + + if (use_calc_direction) { + int i; + sv_array = sld->sv; + for (i = 0; i < sld->totsv; i++, sv_array++) { + /* switch a/b if loop direction is different from global direction */ + int l_nr = sv_array->loop_nr; + if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) { + swap_v3_v3(sv_array->dir_side[0], sv_array->dir_side[1]); + SWAP(BMVert *, sv_array->v_side[0], sv_array->v_side[1]); + } + } + + MEM_freeN(loop_dir); + MEM_freeN(loop_maxdist); + } + + /* possible all of the edge loops are pointing directly at the view */ + if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) { + mval_dir[0] = 0.0f; + mval_dir[1] = 100.0f; + } + + /* zero out start */ + zero_v2(mval_start); + + /* dir holds a vector along edge loop */ + copy_v2_v2(mval_end, mval_dir); + mul_v2_fl(mval_end, 0.5f); + + sld->mval_start[0] = t->mval[0] + mval_start[0]; + sld->mval_start[1] = t->mval[1] + mval_start[1]; + + sld->mval_end[0] = t->mval[0] + mval_end[0]; + sld->mval_end[1] = t->mval[1] + mval_end[1]; + + if (btree) { + BKE_bmbvh_free(btree); + } +} + +static void calcEdgeSlide_non_proportional( + TransInfo *t, EdgeSlideData *sld, const float mval[2]) { TransDataEdgeSlideVert *sv = sld->sv; @@ -5631,7 +5907,7 @@ static void calcNonProportionalEdgeSlide(TransInfo *t, EdgeSlideData *sld, const for (i = 0; i < sld->totsv; i++, sv++) { /* Set length */ - sv->edge_len = len_v3v3(sv->dir_a, sv->dir_b); + sv->edge_len = len_v3v3(sv->dir_side[0], sv->dir_side[1]); ED_view3d_project_float_v2_m4(ar, sv->v->co, v_proj, projectMat); dist_sq = len_squared_v2v2(mval, v_proj); @@ -5646,7 +5922,7 @@ static void calcNonProportionalEdgeSlide(TransInfo *t, EdgeSlideData *sld, const } } -static bool createEdgeSlideVerts(TransInfo *t) +static bool createEdgeSlideVerts_double_side(TransInfo *t) { BMEditMesh *em = BKE_editmesh_from_object(t->obedit); BMesh *bm = em->bm; @@ -5655,24 +5931,13 @@ static bool createEdgeSlideVerts(TransInfo *t) BMVert *v; TransDataEdgeSlideVert *sv_array; int sv_tot; - BMBVHTree *btree; int *sv_table; /* BMVert -> sv_array index */ EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); + float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; + int numsel, i, j, loop_nr; + bool use_btree_disp = false; View3D *v3d = NULL; RegionView3D *rv3d = NULL; - ARegion *ar = t->ar; - float projectMat[4][4]; - float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; - float mval_start[2], mval_end[2]; - float mval_dir[3], maxdist, (*loop_dir)[3], *loop_maxdist; - int numsel, i, j, loop_nr, l_nr; - int use_btree_disp; - - if (t->spacetype == SPACE_VIEW3D) { - /* background mode support */ - v3d = t->sa ? t->sa->spacedata.first : NULL; - rv3d = t->ar ? t->ar->regiondata : NULL; - } slide_origdata_init_flag(t, &sld->orig_data); @@ -5680,14 +5945,6 @@ static bool createEdgeSlideVerts(TransInfo *t) sld->curr_sv_index = 0; sld->flipped_vtx = false; - if (!rv3d) { - /* ok, let's try to survive this */ - unit_m4(projectMat); - } - else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); - } - /*ensure valid selection*/ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { @@ -5859,14 +6116,14 @@ static bool createEdgeSlideVerts(TransInfo *t) 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); + sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v); + copy_v3_v3(sv->dir_side[0], vec_a); } 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); + sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v); + copy_v3_v3(sv->dir_side[1], vec_b); } v_prev = v; @@ -5885,23 +6142,23 @@ 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); + sv->v_side[0] = BM_edge_other_vert(l_tmp->e, v); if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_a); + get_next_loop(v, l_a, e_prev, l_tmp->e, sv->dir_side[0]); } else { - sub_v3_v3v3(sv->dir_a, BM_edge_other_vert(l_tmp->e, v)->co, v->co); + sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co); } } 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); + sv->v_side[1] = BM_edge_other_vert(l_tmp->e, v); if (EDGESLIDE_VERT_IS_INNER(v, l_tmp->e)) { - get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_b); + get_next_loop(v, l_b, e_prev, l_tmp->e, sv->dir_side[1]); } else { - sub_v3_v3v3(sv->dir_b, BM_edge_other_vert(l_tmp->e, v)->co, v->co); + sub_v3_v3v3(sv->dir_side[1], sv->v_side[1]->co, v->co); } } @@ -5972,142 +6229,228 @@ static bool createEdgeSlideVerts(TransInfo *t) #undef EDGESLIDE_VERT_IS_INNER } - /* use for visibility checks */ - use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ - if (use_btree_disp) { - btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false); - } - else { - btree = NULL; + sld->sv = sv_array; + sld->totsv = sv_tot; + + /* use for visibility checks */ + if (t->spacetype == SPACE_VIEW3D) { + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, true); - /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + /* create copies of faces for customdata projection */ + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + slide_origdata_init_data(t, &sld->orig_data); + slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); - sld->sv = sv_array; - sld->totsv = sv_tot; + if (rv3d) { + calcEdgeSlide_non_proportional(t, sld, mval); + } + + sld->em = em; - /* find mouse vectors, the global one, and one per loop in case we have - * multiple loops selected, in case they are oriented different */ - zero_v3(mval_dir); - maxdist = -1.0f; + sld->perc = 0.0f; + + t->customData = sld; + + MEM_freeN(sv_table); - loop_dir = MEM_callocN(sizeof(float) * 3 * loop_nr, "sv loop_dir"); - loop_maxdist = MEM_mallocN(sizeof(float) * loop_nr, "sv loop_maxdist"); - fill_vn_fl(loop_maxdist, loop_nr, -1.0f); + return true; +} - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - BMIter iter2; - BMEdge *e2; - float d; +/** + * A simple version of #createEdgeSlideVerts + * Which assumes the longest unselected. + */ +static bool createEdgeSlideVerts_single_side(TransInfo *t) +{ + BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMesh *bm = em->bm; + BMIter iter; + BMEdge *e; + BMVert *v; + TransDataEdgeSlideVert *sv_array; + int sv_tot; + int *sv_table; /* BMVert -> sv_array index */ + EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); + float mval[2] = {(float)t->mval[0], (float)t->mval[1]}; + int i, j, loop_nr; + bool use_btree_disp = false; + View3D *v3d = NULL; + RegionView3D *rv3d = NULL; - /* search cross edges for visible edge to the mouse cursor, - * then use the shared vertex to calculate screen vector*/ - for (i = 0; i < 2; i++) { - v = i ? e->v1 : e->v2; - BM_ITER_ELEM (e2, &iter2, v, BM_EDGES_OF_VERT) { - /* screen-space coords */ - float sco_a[3], sco_b[3]; + if (t->spacetype == SPACE_VIEW3D) { + /* background mode support */ + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + } - if (BM_elem_flag_test(e2, BM_ELEM_SELECT)) - continue; + slide_origdata_init_flag(t, &sld->orig_data); - /* This test is only relevant if object is not wire-drawn! See [#32068]. */ - if (use_btree_disp && !BMBVH_EdgeVisible(btree, e2, ar, v3d, t->obedit)) { - continue; + sld->is_proportional = true; + sld->curr_sv_index = 0; + /* heppans to be best for single-sided */ + sld->flipped_vtx = true; + + /* ensure valid selection */ + j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + float len_sq_max = -1.0f; + BMIter iter2; + BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_SELECT)) { + float len_sq = BM_edge_calc_length_squared(e); + if (len_sq > len_sq_max) { + len_sq_max = len_sq; + v->e = e; } + } + } - BLI_assert(sv_table[BM_elem_index_get(v)] != -1); - j = sv_table[BM_elem_index_get(v)]; + if (len_sq_max != -1.0f) { + j++; + } + } + BM_elem_index_set(v, i); /* set_inline */ + } + bm->elem_index_dirty &= ~BM_VERT; - if (sv_array[j].v_b) { - ED_view3d_project_float_v3_m4(ar, sv_array[j].v_b->co, sco_b, projectMat); - } - else { - add_v3_v3v3(sco_b, v->co, sv_array[j].dir_b); - ED_view3d_project_float_v3_m4(ar, sco_b, sco_b, projectMat); - } - - if (sv_array[j].v_a) { - ED_view3d_project_float_v3_m4(ar, sv_array[j].v_a->co, sco_a, projectMat); + if (!j) { + return false; + } + + + sv_tot = j; + BLI_assert(sv_tot != 0); + /* over alloc */ + sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * bm->totvertsel, "sv_array"); + + /* same loop for all loops, weak but we dont connect loops in this case */ + loop_nr = 1; + + sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__); + + j = 0; + BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) { + sv_table[i] = -1; + if ((v->e != NULL) && (BM_elem_flag_test(v, BM_ELEM_SELECT))) { + if (BM_elem_flag_test(v->e, BM_ELEM_SELECT) == 0) { + TransDataEdgeSlideVert *sv; + sv = &sv_array[j]; + sv->v = v; + copy_v3_v3(sv->v_co_orig, v->co); + sv->v_side[0] = BM_edge_other_vert(v->e, v); + sub_v3_v3v3(sv->dir_side[0], sv->v_side[0]->co, v->co); + sv->loop_nr = 0; + sv_table[i] = j; + j += 1; + } + } + } + + /* check for wire vertices, + * interpolate the directions of wire verts between non-wire verts */ + if (sv_tot != bm->totvert) { + const int sv_tot_nowire = sv_tot; + TransDataEdgeSlideVert *sv_iter = sv_array; + int i; + for (i = 0; i < sv_tot_nowire; i++, sv_iter++) { + BMIter eiter; + BM_ITER_ELEM (e, &eiter, sv_iter->v, BM_EDGES_OF_VERT) { + /* walk over wire */ + TransDataEdgeSlideVert *sv_end = NULL; + BMEdge *e_step = e; + BMVert *v = sv_iter->v; + + j = sv_tot; + + while (1) { + BMVert *v_other = BM_edge_other_vert(e_step, v); + int endpoint = ( + (sv_table[BM_elem_index_get(v_other)] != -1) + + (BM_vert_is_edge_pair(v_other) == false)); + + if ((BM_elem_flag_test(e_step, BM_ELEM_SELECT) && + BM_elem_flag_test(v_other, BM_ELEM_SELECT)) && + (endpoint == 0)) + { + /* scan down the list */ + TransDataEdgeSlideVert *sv; + BLI_assert(sv_table[BM_elem_index_get(v_other)] == -1); + sv_table[BM_elem_index_get(v_other)] = j; + sv = &sv_array[j]; + sv->v = v_other; + copy_v3_v3(sv->v_co_orig, v_other->co); + copy_v3_v3(sv->dir_side[0], sv_iter->dir_side[0]); + j++; + + /* advance! */ + v = v_other; + e_step = BM_DISK_EDGE_NEXT(e_step, v_other); } else { - add_v3_v3v3(sco_a, v->co, sv_array[j].dir_a); - ED_view3d_project_float_v3_m4(ar, sco_a, sco_a, projectMat); - } - - /* global direction */ - d = dist_to_line_segment_v2(mval, sco_b, sco_a); - if ((maxdist == -1.0f) || - /* intentionally use 2d size on 3d vector */ - (d < maxdist && (len_squared_v2v2(sco_b, sco_a) > 0.1f))) - { - maxdist = d; - sub_v3_v3v3(mval_dir, sco_b, sco_a); + if ((endpoint == 2) && (sv_tot != j)) { + BLI_assert(BM_elem_index_get(v_other) != -1); + sv_end = &sv_array[sv_table[BM_elem_index_get(v_other)]]; + } + break; } + } - /* per loop direction */ - l_nr = sv_array[j].loop_nr; - if (loop_maxdist[l_nr] == -1.0f || d < loop_maxdist[l_nr]) { - loop_maxdist[l_nr] = d; - sub_v3_v3v3(loop_dir[l_nr], sco_b, sco_a); + if (sv_end) { + int sv_tot_prev = sv_tot; + const float *co_src = sv_iter->v->co; + const float *co_dst = sv_end->v->co; + const float *dir_src = sv_iter->dir_side[0]; + const float *dir_dst = sv_end->dir_side[0]; + sv_tot = j; + + while (j-- != sv_tot_prev) { + float factor; + factor = line_point_factor_v3(sv_array[j].v->co, co_src, co_dst); + interp_v3_v3v3(sv_array[j].dir_side[0], dir_src, dir_dst, factor); } } } } } - /* possible all of the edge loops are pointing directly at the view */ - if (UNLIKELY(len_squared_v2(mval_dir) < 0.1f)) { - mval_dir[0] = 0.0f; - mval_dir[1] = 100.0f; + /* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */ + + sld->sv = sv_array; + sld->totsv = sv_tot; + + /* use for visibility checks */ + if (t->spacetype == SPACE_VIEW3D) { + v3d = t->sa ? t->sa->spacedata.first : NULL; + rv3d = t->ar ? t->ar->regiondata : NULL; + use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } + calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false); + + /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); slide_origdata_init_data(t, &sld->orig_data); slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); - /*create copies of faces for customdata projection*/ - sv_array = sld->sv; - for (i = 0; i < sld->totsv; i++, sv_array++) { - /* switch a/b if loop direction is different from global direction */ - l_nr = sv_array->loop_nr; - if (dot_v3v3(loop_dir[l_nr], mval_dir) < 0.0f) { - swap_v3_v3(sv_array->dir_a, sv_array->dir_b); - SWAP(BMVert *, sv_array->v_a, sv_array->v_b); - } + if (rv3d) { + calcEdgeSlide_non_proportional(t, sld, mval); } - if (rv3d) - calcNonProportionalEdgeSlide(t, sld, mval); - sld->em = em; - /*zero out start*/ - zero_v2(mval_start); - - /*dir holds a vector along edge loop*/ - copy_v2_v2(mval_end, mval_dir); - mul_v2_fl(mval_end, 0.5f); - - sld->mval_start[0] = t->mval[0] + mval_start[0]; - sld->mval_start[1] = t->mval[1] + mval_start[1]; - - sld->mval_end[0] = t->mval[0] + mval_end[0]; - sld->mval_end[1] = t->mval[1] + mval_end[1]; - sld->perc = 0.0f; - + t->customData = sld; - + MEM_freeN(sv_table); - if (btree) { - BKE_bmbvh_free(btree); - } - MEM_freeN(loop_dir); - MEM_freeN(loop_maxdist); return true; } @@ -6148,15 +6491,23 @@ void freeEdgeSlideVerts(TransInfo *t) recalcData(t); } -static void initEdgeSlide(TransInfo *t) +static void initEdgeSlide_ex(TransInfo *t, bool use_double_side) { EdgeSlideData *sld; + bool ok; t->mode = TFM_EDGE_SLIDE; t->transform = applyEdgeSlide; t->handleEvent = handleEventEdgeSlide; - if (!createEdgeSlideVerts(t)) { + if (use_double_side) { + ok = createEdgeSlideVerts_double_side(t); + } + else { + ok = createEdgeSlideVerts_single_side(t); + } + + if (!ok) { t->state = TRANS_CANCEL; return; } @@ -6169,7 +6520,7 @@ static void initEdgeSlide(TransInfo *t) t->customFree = freeEdgeSlideVerts; /* set custom point first if you want value to be initialized by init */ - setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start); + calcEdgeSlideCustomPoints(t); initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO_FLIP); t->idx_max = 0; @@ -6185,6 +6536,11 @@ static void initEdgeSlide(TransInfo *t) t->flag |= T_NO_CONSTRAINT | T_NO_PROJECT; } +static void initEdgeSlide(TransInfo *t) +{ + initEdgeSlide_ex(t, true); +} + static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_EDGE_SLIDE) { @@ -6195,35 +6551,40 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven case EKEY: if (event->val == KM_PRESS) { sld->is_proportional = !sld->is_proportional; + calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } break; case FKEY: - { if (event->val == KM_PRESS) { if (sld->is_proportional == false) { sld->flipped_vtx = !sld->flipped_vtx; } + calcEdgeSlideCustomPoints(t); + return TREDRAW_HARD; + } + break; + case CKEY: + /* use like a modifier key */ + if (event->val == KM_PRESS) { + t->flag ^= T_ALT_TRANSFORM; + calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } break; - } case EVT_MODAL_MAP: - { switch (event->val) { case TFM_MODAL_EDGESLIDE_DOWN: - { sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv; - break; - } + return TREDRAW_HARD; case TFM_MODAL_EDGESLIDE_UP: - { sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv; - break; - } + return TREDRAW_HARD; } break; - } + case MOUSEMOVE: + calcEdgeSlideCustomPoints(t); + break; default: break; } @@ -6232,23 +6593,16 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven return TREDRAW_NOTHING; } -static void drawEdgeSlide(const struct bContext *C, TransInfo *t) +static void drawEdgeSlide(TransInfo *t) { - if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = (EdgeSlideData *)t->customData; + if ((t->mode == TFM_EDGE_SLIDE) && t->customData) { + EdgeSlideData *sld = t->customData; + const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); + /* Non-Prop mode */ - if (sld && sld->is_proportional == false) { - View3D *v3d = CTX_wm_view3d(C); - float co_a[3], co_b[3], co_mark[3]; - TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - const float fac = (sld->perc + 1.0f) / 2.0f; - const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; - const float guide_size = ctrl_size - 0.5f; + if ((sld->is_proportional == false) || (is_clamp == false)) { + View3D *v3d = t->view; const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; - const int alpha_shade = -30; - - add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_a); - add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_b); if (v3d && v3d->zbuf) glDisable(GL_DEPTH_TEST); @@ -6261,42 +6615,88 @@ static void drawEdgeSlide(const struct bContext *C, TransInfo *t) glMultMatrixf(t->obedit->obmat); - glLineWidth(line_size); - UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); - glBegin(GL_LINES); - if (curr_sv->v_a) { - glVertex3fv(curr_sv->v_a->co); - glVertex3fv(curr_sv->v_co_orig); - } - if (curr_sv->v_b) { - glVertex3fv(curr_sv->v_b->co); - glVertex3fv(curr_sv->v_co_orig); - } - bglEnd(); + if (sld->is_proportional == false) { + float co_a[3], co_b[3], co_mark[3]; + TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; + const float fac = (sld->perc + 1.0f) / 2.0f; + const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; + const float guide_size = ctrl_size - 0.5f; + const int alpha_shade = -30; + + add_v3_v3v3(co_a, curr_sv->v_co_orig, curr_sv->dir_side[0]); + add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]); + + glLineWidth(line_size); + UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); + glBegin(GL_LINES); + if (curr_sv->v_side[0]) { + glVertex3fv(curr_sv->v_side[0]->co); + glVertex3fv(curr_sv->v_co_orig); + } + if (curr_sv->v_side[1]) { + glVertex3fv(curr_sv->v_side[1]->co); + glVertex3fv(curr_sv->v_co_orig); + } + glEnd(); + UI_ThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade); + glPointSize(ctrl_size); + bglBegin(GL_POINTS); + if (sld->flipped_vtx) { + if (curr_sv->v_side[1]) bglVertex3fv(curr_sv->v_side[1]->co); + } + else { + if (curr_sv->v_side[0]) bglVertex3fv(curr_sv->v_side[0]->co); + } + bglEnd(); - UI_ThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade); - glPointSize(ctrl_size); - bglBegin(GL_POINTS); - if (sld->flipped_vtx) { - if (curr_sv->v_b) bglVertex3fv(curr_sv->v_b->co); + UI_ThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade); + glPointSize(guide_size); + bglBegin(GL_POINTS); +#if 0 + interp_v3_v3v3(co_mark, co_b, co_a, fac); + bglVertex3fv(co_mark); +#endif + interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac); + bglVertex3fv(co_mark); + bglEnd(); } else { - if (curr_sv->v_a) bglVertex3fv(curr_sv->v_a->co); - } - bglEnd(); + if (is_clamp == false) { + const int side_index = sld->curr_side_unclamp; + TransDataEdgeSlideVert *sv; + int i; + const int alpha_shade = -160; + + glLineWidth(line_size); + UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); + glBegin(GL_LINES); + + sv = sld->sv; + for (i = 0; i < sld->totsv; i++, sv++) { + float a[3], b[3]; + + if (!is_zero_v3(sv->dir_side[side_index])) { + copy_v3_v3(a, sv->dir_side[side_index]); + } + else { + copy_v3_v3(a, sv->dir_side[!side_index]); + } - UI_ThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade); - glPointSize(guide_size); - bglBegin(GL_POINTS); -#if 0 - interp_v3_v3v3(co_mark, co_b, co_a, fac); - bglVertex3fv(co_mark); -#endif - interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac); - bglVertex3fv(co_mark); - bglEnd(); + mul_v3_fl(a, 100.0f); + negate_v3_v3(b, a); + add_v3_v3(a, sv->v_co_orig); + add_v3_v3(b, sv->v_co_orig); + glVertex3fv(a); + glVertex3fv(b); + } + glEnd(); + } + else { + BLI_assert(0); + } + } glPopMatrix(); glPopAttrib(); @@ -6319,17 +6719,30 @@ static void doEdgeSlide(TransInfo *t, float perc) sv = svlist; if (sld->is_proportional == true) { - for (i = 0; i < sld->totsv; i++, sv++) { - float vec[3]; - if (perc > 0.0f) { - copy_v3_v3(vec, sv->dir_a); - mul_v3_fl(vec, perc); - add_v3_v3v3(sv->v->co, sv->v_co_orig, vec); + const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); + if (is_clamp) { + const int side_index = (perc < 0.0f); + const float perc_final = fabsf(perc); + for (i = 0; i < sld->totsv; i++, sv++) { + madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final); } - else { - copy_v3_v3(vec, sv->dir_b); - mul_v3_fl(vec, -perc); - add_v3_v3v3(sv->v->co, sv->v_co_orig, vec); + + sld->curr_side_unclamp = side_index; + } + else { + const int side_index = sld->curr_side_unclamp; + const float perc_init = fabsf(perc) * ((sld->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1); + for (i = 0; i < sld->totsv; i++, sv++) { + float dir_flip[3]; + float perc_final = perc_init; + if (!is_zero_v3(sv->dir_side[side_index])) { + copy_v3_v3(dir_flip, sv->dir_side[side_index]); + } + else { + copy_v3_v3(dir_flip, sv->dir_side[!side_index]); + perc_final *= -1; + } + madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final); } } } @@ -6339,7 +6752,7 @@ static void doEdgeSlide(TransInfo *t, float perc) * a/b verts, this could be changed/improved so the distance is still met but the verts are moved along * their original path (which may not be straight), however how it works now is OK and matches 2.4x - Campbell * - * \note len_v3v3(curr_sv->dir_a, curr_sv->dir_b) + * \note len_v3v3(curr_sv->dir_side[0], curr_sv->dir_side[1]) * is the same as the distance between the original vert locations, same goes for the lines below. */ TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; @@ -6352,8 +6765,8 @@ static void doEdgeSlide(TransInfo *t, float perc) if (sv->edge_len > FLT_EPSILON) { const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len; - add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_a); - add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_b); + add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]); + add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]); if (sld->flipped_vtx) { interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac); @@ -6371,46 +6784,43 @@ static void doEdgeSlide(TransInfo *t, float perc) static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) { char str[MAX_INFO_LEN]; + size_t ofs = 0; float final; EdgeSlideData *sld = t->customData; bool flipped = sld->flipped_vtx; bool is_proportional = sld->is_proportional; + const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); + const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num)); final = t->values[0]; snapGridIncrement(t, &final); /* only do this so out of range values are not displayed */ - CLAMP(final, -1.0f, 1.0f); + if (is_constrained) { + CLAMP(final, -1.0f, 1.0f); + } applyNumInput(&t->num, &final); + t->values[0] = final; + + /* header string */ + ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Edge Slide: "), MAX_INFO_LEN - ofs); if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; - outputNumInput(&(t->num), c, &t->scene->unit); - - if (is_proportional) { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Edge Slide: %s (E)ven: %s"), - &c[0], WM_bool_as_string(!is_proportional)); - } - else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Edge Slide: %s (E)ven: %s, (F)lipped: %s"), - &c[0], WM_bool_as_string(!is_proportional), WM_bool_as_string(flipped)); - } - } - else if (is_proportional) { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Edge Slide: %.4f (E)ven: %s"), - final, WM_bool_as_string(!is_proportional)); + ofs += BLI_strncpy_rlen(str + ofs, &c[0], MAX_INFO_LEN - ofs); } else { - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("Edge Slide: %.4f (E)ven: %s, (F)lipped: %s"), - final, WM_bool_as_string(!is_proportional), WM_bool_as_string(flipped)); + ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, "%.4f ", final); } - - CLAMP(final, -1.0f, 1.0f); - - t->values[0] = final; + ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(E)ven: %s, "), WM_bool_as_string(!is_proportional)); + if (!is_proportional) { + ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("(F)lipped: %s, "), WM_bool_as_string(flipped)); + } + ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_("Alt or (C)lamp: %s"), WM_bool_as_string(is_clamp)); + /* done with header string */ /* do stuff here */ doEdgeSlide(t, final); @@ -6432,10 +6842,20 @@ static void calcVertSlideCustomPoints(struct TransInfo *t) { VertSlideData *sld = t->customData; TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index]; - const float *co_orig = sv->co_orig_2d; - const float *co_curr = sv->co_link_orig_2d[sv->co_link_curr]; - const int mval_start[2] = {co_orig[0], co_orig[1]}; - const int mval_end[2] = {co_curr[0], co_curr[1]}; + + const float *co_orig_3d = sv->co_orig_3d; + const float *co_curr_3d = sv->co_link_orig_3d[sv->co_link_curr]; + + float co_curr_2d[2], co_orig_2d[2]; + + int mval_ofs[2], mval_start[2], mval_end[2]; + + ED_view3d_project_float_v2_m4(t->ar, co_orig_3d, co_orig_2d, sld->proj_mat); + ED_view3d_project_float_v2_m4(t->ar, co_curr_3d, co_curr_2d, sld->proj_mat); + + ARRAY_SET_ITEMS(mval_ofs, t->imval[0] - co_orig_2d[0], t->imval[1] - co_orig_2d[1]); + ARRAY_SET_ITEMS(mval_start, co_orig_2d[0] + mval_ofs[0], co_orig_2d[1] + mval_ofs[1]); + ARRAY_SET_ITEMS(mval_end, co_curr_2d[0] + mval_ofs[0], co_curr_2d[1] + mval_ofs[1]); if (sld->flipped_vtx && sld->is_proportional == false) { setCustomPoints(t, &t->mouse, mval_start, mval_end); @@ -6443,6 +6863,11 @@ static void calcVertSlideCustomPoints(struct TransInfo *t) else { setCustomPoints(t, &t->mouse, mval_end, mval_start); } + + /* setCustomPoints isn't normally changing as the mouse moves, + * in this case apply mouse input immediatly so we don't refresh + * with the value from the previous points */ + applyMouseInput(t, &t->mouse, t->mval, t->values); } /** @@ -6460,28 +6885,39 @@ static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2]) int i; for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) { - dist_sq = len_squared_v2v2(mval_fl, sv->co_orig_2d); + float co_2d[2]; + + ED_view3d_project_float_v2_m4(t->ar, sv->co_orig_3d, co_2d, sld->proj_mat); + + dist_sq = len_squared_v2v2(mval_fl, co_2d); if (dist_sq < dist_min_sq) { dist_min_sq = dist_sq; sld->curr_sv_index = i; } } } + /** * Run while moving the mouse to slide along the edge matching the mouse direction */ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2]) { VertSlideData *sld = t->customData; - float mval_fl[2] = {UNPACK2(mval)}; + float imval_fl[2] = {UNPACK2(t->imval)}; + float mval_fl[2] = {UNPACK2(mval)}; - float dir[2]; + float dir[3]; TransDataVertSlideVert *sv; int i; - /* first get the direction of the original vertex */ - sub_v2_v2v2(dir, sld->sv[sld->curr_sv_index].co_orig_2d, mval_fl); - normalize_v2(dir); + /* first get the direction of the original mouse position */ + sub_v2_v2v2(dir, imval_fl, mval_fl); + ED_view3d_win_to_delta(t->ar, dir, dir, t->zfac); + + invert_m4_m4(t->obedit->imat, t->obedit->obmat); + mul_mat3_m4_v3(t->obedit->imat, dir); + + normalize_v3(dir); for (i = 0, sv = sld->sv; i < sld->totsv; i++, sv++) { if (sv->co_link_tot > 1) { @@ -6490,11 +6926,14 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] int j; for (j = 0; j < sv->co_link_tot; j++) { - float tdir[2]; + float tdir[3]; float dir_dot; - sub_v2_v2v2(tdir, sv->co_orig_2d, sv->co_link_orig_2d[j]); - normalize_v2(tdir); - dir_dot = dot_v2v2(dir, tdir); + + sub_v3_v3v3(tdir, sv->co_orig_3d, sv->co_link_orig_3d[j]); + project_plane_v3_v3v3(tdir, tdir, t->viewinv[2]); + + normalize_v3(tdir); + dir_dot = dot_v3v3(dir, tdir); if (dir_dot > dir_dot_best) { dir_dot_best = dir_dot; co_link_curr_best = j; @@ -6518,32 +6957,14 @@ static bool createVertSlideVerts(TransInfo *t) BMVert *v; TransDataVertSlideVert *sv_array; VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); -// View3D *v3d = NULL; - RegionView3D *rv3d = NULL; - ARegion *ar = t->ar; - float projectMat[4][4]; int j; - if (t->spacetype == SPACE_VIEW3D) { - /* background mode support */ -// v3d = t->sa ? t->sa->spacedata.first : NULL; - rv3d = ar ? ar->regiondata : NULL; - } - slide_origdata_init_flag(t, &sld->orig_data); sld->is_proportional = true; sld->curr_sv_index = 0; sld->flipped_vtx = false; - if (!rv3d) { - /* ok, let's try to survive this */ - unit_m4(projectMat); - } - else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); - } - j = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { bool ok = false; @@ -6587,7 +7008,6 @@ static bool createVertSlideVerts(TransInfo *t) } sv_array[j].co_link_orig_3d = MEM_mallocN(sizeof(*sv_array[j].co_link_orig_3d) * k, __func__); - sv_array[j].co_link_orig_2d = MEM_mallocN(sizeof(*sv_array[j].co_link_orig_2d) * k, __func__); sv_array[j].co_link_tot = k; k = 0; @@ -6595,31 +7015,9 @@ static bool createVertSlideVerts(TransInfo *t) if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { BMVert *v_other = BM_edge_other_vert(e, v); copy_v3_v3(sv_array[j].co_link_orig_3d[k], v_other->co); - if (ar) { - ED_view3d_project_float_v2_m4(ar, - sv_array[j].co_link_orig_3d[k], - sv_array[j].co_link_orig_2d[k], - projectMat); - } - else { - copy_v2_v2(sv_array[j].co_link_orig_2d[k], - sv_array[j].co_link_orig_3d[k]); - } k++; } } - - if (ar) { - ED_view3d_project_float_v2_m4(ar, - sv_array[j].co_orig_3d, - sv_array[j].co_orig_2d, - projectMat); - } - else { - copy_v2_v2(sv_array[j].co_orig_2d, - sv_array[j].co_orig_3d); - } - j++; } } @@ -6637,7 +7035,19 @@ static bool createVertSlideVerts(TransInfo *t) t->customData = sld; - if (rv3d) { + /* most likely will be set below */ + unit_m4(sld->proj_mat); + + if (t->spacetype == SPACE_VIEW3D) { + /* view vars */ + RegionView3D *rv3d = NULL; + ARegion *ar = t->ar; + + rv3d = ar ? ar->regiondata : NULL; + if (rv3d) { + ED_view3d_ob_project_mat_get(rv3d, t->obedit, sld->proj_mat); + } + calcVertSlideMouseActiveVert(t, t->mval); calcVertSlideMouseActiveEdges(t, t->mval); } @@ -6677,7 +7087,6 @@ void freeVertSlideVerts(TransInfo *t) TransDataVertSlideVert *sv = sld->sv; int i = 0; for (i = 0; i < sld->totsv; i++, sv++) { - MEM_freeN(sv->co_link_orig_2d); MEM_freeN(sv->co_link_orig_3d); } } @@ -6744,16 +7153,13 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven } break; case FKEY: - { if (event->val == KM_PRESS) { sld->flipped_vtx = !sld->flipped_vtx; calcVertSlideCustomPoints(t); return TREDRAW_HARD; } break; - } case CKEY: - { /* use like a modifier key */ if (event->val == KM_PRESS) { t->flag ^= T_ALT_TRANSFORM; @@ -6761,23 +7167,17 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven return TREDRAW_HARD; } break; - } #if 0 case EVT_MODAL_MAP: - { switch (event->val) { case TFM_MODAL_EDGESLIDE_DOWN: - { sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv; break; - } case TFM_MODAL_EDGESLIDE_UP: - { sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv; break; - } } - } + break; #endif case MOUSEMOVE: { @@ -6797,19 +7197,20 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven return TREDRAW_NOTHING; } -static void drawVertSlide(const struct bContext *C, TransInfo *t) +static void drawVertSlide(TransInfo *t) { - if (t->mode == TFM_VERT_SLIDE) { - VertSlideData *sld = (VertSlideData *)t->customData; + if ((t->mode == TFM_VERT_SLIDE) && t->customData) { + VertSlideData *sld = t->customData; + const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); + /* Non-Prop mode */ - if (sld) { - View3D *v3d = CTX_wm_view3d(C); + { + View3D *v3d = t->view; TransDataVertSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; TransDataVertSlideVert *sv; const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; const int alpha_shade = -160; - const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); int i; if (v3d && v3d->zbuf) @@ -6857,11 +7258,40 @@ static void drawVertSlide(const struct bContext *C, TransInfo *t) curr_sv->co_orig_3d); bglEnd(); + glDisable(GL_BLEND); + + /* direction from active vertex! */ + if ((t->mval[0] != t->imval[0]) || + (t->mval[1] != t->imval[1])) + { + float zfac = ED_view3d_calc_zfac(t->ar->regiondata, curr_sv->co_orig_3d, NULL); + float mval_ofs[2]; + float co_dest_3d[3]; + + mval_ofs[0] = t->mval[0] - t->imval[0]; + mval_ofs[1] = t->mval[1] - t->imval[1]; + + ED_view3d_win_to_delta(t->ar, mval_ofs, co_dest_3d, zfac); + + invert_m4_m4(t->obedit->imat, t->obedit->obmat); + mul_mat3_m4_v3(t->obedit->imat, co_dest_3d); + + add_v3_v3(co_dest_3d, curr_sv->co_orig_3d); + + glLineWidth(1); + setlinestyle(1); + + cpack(0xffffff); + glBegin(GL_LINES); + glVertex3fv(curr_sv->co_orig_3d); + glVertex3fv(co_dest_3d); + + glEnd(); + } + glPopMatrix(); glPopAttrib(); - glDisable(GL_BLEND); - if (v3d && v3d->zbuf) glEnable(GL_DEPTH_TEST); } @@ -6933,6 +7363,8 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &final); + t->values[0] = final; + /* header string */ ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Vert Slide: "), MAX_INFO_LEN - ofs); if (hasNumInput(&t->num)) { @@ -7001,6 +7433,8 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) applyNumInput(&t->num, &final); + t->values[0] = final; + if (hasNumInput(&t->num)) { char c[NUM_STR_REP_LEN]; @@ -7273,7 +7707,7 @@ static void initSeqSlide(TransInfo *t) t->num.idx_max = t->idx_max; t->snap[0] = 0.0f; - t->snap[1] = floor(t->scene->r.frs_sec / t->scene->r.frs_sec_base); + t->snap[1] = floorf(t->scene->r.frs_sec / t->scene->r.frs_sec_base); t->snap[2] = 10.0f; copy_v3_fl(t->num.val_inc, t->snap[1]); @@ -7307,40 +7741,28 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[MAX_INFO_L WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); } -static void applySeqSlideValue(TransInfo *t, const float val[2], int frame) +static void applySeqSlideValue(TransInfo *t, const float val[2]) { TransData *td = t->data; int i; - TransSeq *ts = t->customData; for (i = 0; i < t->total; i++, td++) { - float tvec[2]; - if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - copy_v2_v2(tvec, val); - - mul_v2_fl(tvec, td->factor); - - if (t->modifiers & MOD_SNAP_INVERT) { - td->loc[0] = frame + td->factor * (td->iloc[0] - ts->min); - } - else { - td->loc[0] = td->iloc[0] + tvec[0]; - } - - td->loc[1] = td->iloc[1] + tvec[1]; + madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); } } static void applySeqSlide(TransInfo *t, const int mval[2]) { char str[MAX_INFO_LEN]; - int snap_frame = 0; + + snapSequenceBounds(t, mval); + if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; float tvec[3]; @@ -7348,16 +7770,15 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) copy_v3_v3(t->values, tvec); } else { - snap_frame = snapSequenceBounds(t, mval); // snapGridIncrement(t, t->values); applyNumInput(&t->num, t->values); } - t->values[0] = floor(t->values[0] + 0.5f); - t->values[1] = floor(t->values[1] + 0.5f); + t->values[0] = floorf(t->values[0] + 0.5f); + t->values[1] = floorf(t->values[1] + 0.5f); headerSeqSlide(t, t->values, str); - applySeqSlideValue(t, t->values, snap_frame); + applySeqSlideValue(t, t->values); recalcData(t); @@ -7520,6 +7941,7 @@ static void initTimeTranslate(TransInfo *t) static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) { char tvec[NUM_STR_REP_LEN * 3]; + int ofs = 0; /* if numeric input is active, use results from that, otherwise apply snapping to result */ if (hasNumInput(&t->num)) { @@ -7555,10 +7977,14 @@ static void headerTimeTranslate(TransInfo *t, char str[MAX_INFO_LEN]) BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", val); } - BLI_snprintf(str, MAX_INFO_LEN, IFACE_("DeltaX: %s"), &tvec[0]); + ofs += BLI_snprintf(str, MAX_INFO_LEN, IFACE_("DeltaX: %s"), &tvec[0]); + + if (t->flag & T_PROP_EDIT_ALL) { + ofs += BLI_snprintf(str + ofs, MAX_INFO_LEN - ofs, IFACE_(" Proportional size: %.2f"), t->prop_size); + } } -static void applyTimeTranslateValue(TransInfo *t, float UNUSED(sval)) +static void applyTimeTranslateValue(TransInfo *t) { TransData *td = t->data; TransData2D *td2d = t->data2d; @@ -7588,11 +8014,11 @@ static void applyTimeTranslateValue(TransInfo *t, float UNUSED(sval)) deltax = (float)(floor(((double)deltax / secf) + 0.5) * secf); } else if (autosnap == SACTSNAP_STEP) { - deltax = (float)(floor(deltax + 0.5f)); + deltax = floorf(deltax + 0.5f); } val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP); - val += deltax; + val += deltax * td->factor; *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); } else { @@ -7602,7 +8028,7 @@ static void applyTimeTranslateValue(TransInfo *t, float UNUSED(sval)) val = (float)(floor(((double)deltax / secf) + 0.5) * secf); } else if (autosnap == SACTSNAP_STEP) { - val = (float)(floor(val + 0.5f)); + val = floorf(val + 0.5f); } *(td->val) = td->ival + val; @@ -7616,15 +8042,17 @@ static void applyTimeTranslateValue(TransInfo *t, float UNUSED(sval)) static void applyTimeTranslate(TransInfo *t, const int mval[2]) { View2D *v2d = (View2D *)t->view; - float cval[2], sval[2]; char str[MAX_INFO_LEN]; /* calculate translation amount from mouse movement - in 'time-grid space' */ - UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]); - UI_view2d_region_to_view(v2d, t->imval[0], t->imval[0], &sval[0], &sval[1]); + if (t->flag & T_MODAL) { + float cval[2], sval[2]; + UI_view2d_region_to_view(v2d, mval[0], mval[0], &cval[0], &cval[1]); + UI_view2d_region_to_view(v2d, t->imval[0], t->imval[0], &sval[0], &sval[1]); - /* we only need to calculate effect for time (applyTimeTranslate only needs that) */ - t->values[0] = cval[0] - sval[0]; + /* we only need to calculate effect for time (applyTimeTranslate only needs that) */ + t->values[0] = cval[0] - sval[0]; + } /* handle numeric-input stuff */ t->vec[0] = t->values[0]; @@ -7632,7 +8060,7 @@ static void applyTimeTranslate(TransInfo *t, const int mval[2]) t->values[0] = t->vec[0]; headerTimeTranslate(t, str); - applyTimeTranslateValue(t, sval[0]); + applyTimeTranslateValue(t); recalcData(t); @@ -7765,7 +8193,7 @@ static void applyTimeSlide(TransInfo *t, const int mval[2]) /* t->values[0] stores cval[0], which is the current mouse-pointer location (in frames) */ // XXX Need to be able to repeat this - t->values[0] = cval[0]; + /* t->values[0] = cval[0]; */ /* UNUSED (reset again later). */ /* handle numeric-input stuff */ t->vec[0] = 2.0f * (cval[0] - sval[0]) / (maxx - minx); @@ -7866,7 +8294,7 @@ static void applyTimeScaleValue(TransInfo *t) fac = (float)(floor((double)fac / secf + 0.5) * secf); } else if (autosnap == SACTSNAP_STEP) { - fac = (float)(floor(fac + 0.5f)); + fac = floorf(fac + 0.5f); } /* check if any need to apply nla-mapping */ diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index 8d6c693b14a..8863d337cff 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -39,9 +39,6 @@ #include "DNA_listBase.h" -#include "BLI_smallhash.h" -#include "BKE_editmesh.h" - /* ************************** Types ***************************** */ struct TransInfo; @@ -107,8 +104,6 @@ typedef struct TransCon { float mtx[3][3]; /* Matrix of the Constraint space */ float imtx[3][3]; /* Inverse Matrix of the Constraint space */ float pmtx[3][3]; /* Projection Constraint Matrix (same as imtx with some axis == 0) */ - float center[3]; /* transformation center to define where to draw the view widget - * ALWAYS in global space. Unlike the transformation center */ int imval[2]; /* initial mouse value for visual calculation */ /* the one in TransInfo is not garanty to stay the same (Rotates change it) */ int mode; /* Mode flags of the Constraint */ @@ -216,10 +211,10 @@ typedef struct TransDataEdgeSlideVert { float edge_len; - struct BMVert *v_a, *v_b; + struct BMVert *v_side[2]; /* add origvert.co to get the original locations */ - float dir_a[3], dir_b[3]; + float dir_side[2][3]; int loop_nr; } TransDataEdgeSlideVert; @@ -256,19 +251,20 @@ typedef struct EdgeSlideData { bool flipped_vtx; int curr_sv_index; + + /** when un-clamped - use this index: #TransDataEdgeSlideVert.dir_side */ + int curr_side_unclamp; } EdgeSlideData; typedef struct TransDataVertSlideVert { /* TransDataGenericSlideVert */ - BMVert *v; + struct BMVert *v; struct LinkNode **cd_loop_groups; float co_orig_3d[3]; /* end generic */ - float co_orig_2d[2]; float (*co_link_orig_3d)[3]; - float (*co_link_orig_2d)[2]; int co_link_tot; int co_link_curr; } TransDataVertSlideVert; @@ -287,6 +283,9 @@ typedef struct VertSlideData { bool flipped_vtx; int curr_sv_index; + + /* result of ED_view3d_ob_project_mat_get */ + float proj_mat[4][4]; } VertSlideData; typedef struct BoneInitData { @@ -356,7 +355,10 @@ typedef struct TransInfo { eRedrawFlag redraw; /* redraw flag */ float prop_size; /* proportional circle radius */ char proptext[20]; /* proportional falloff text */ - float center[3]; /* center of transformation */ + float aspect[3]; /* spaces using non 1:1 aspect, (uv's, f-curve, movie-clip... etc) + * use for conversion and snapping. */ + float center[3]; /* center of transformation (in local-space) */ + float center_global[3]; /* center of transformation (in global-space) */ float center2d[2]; /* center in screen coordinates */ int imval[2]; /* initial mouse position */ short event_type; /* event->type used to invoke transform */ @@ -541,6 +543,7 @@ void transformApply(struct bContext *C, TransInfo *t); int transformEnd(struct bContext *C, TransInfo *t); void setTransformViewMatrices(TransInfo *t); +void setTransformViewAspect(TransInfo *t, float r_aspect[3]); void convertViewVec(TransInfo *t, float r_vec[3], int dx, int dy); void projectIntViewEx(TransInfo *t, const float vec[3], int adr[2], const eV3DProjTest flag); void projectIntView(TransInfo *t, const float vec[3], int adr[2]); @@ -626,7 +629,7 @@ typedef enum { void snapGridIncrement(TransInfo *t, float *val); void snapGridIncrementAction(TransInfo *t, float *val, GearsType action); -int snapSequenceBounds(TransInfo *t, const int mval[2]); +void snapSequenceBounds(TransInfo *t, const int mval[2]); bool activeSnap(TransInfo *t); bool validSnap(TransInfo *t); @@ -690,6 +693,7 @@ void restoreTransObjects(TransInfo *t); void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); +void calculateCenterGlobal(TransInfo *t); void calculateCenter(TransInfo *t); diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index ccb81c7342b..3063ee22c55 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -46,6 +46,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_string.h" +#include "BLI_rect.h" #include "BKE_context.h" @@ -202,7 +203,7 @@ static void axisProjection(TransInfo *t, const float axis[3], const float in[3], return; } - copy_v3_v3(t_con_center, t->con.center); + copy_v3_v3(t_con_center, t->center_global); /* checks for center being too close to the view center */ viewAxisCorrectCenter(t, t_con_center); @@ -276,7 +277,7 @@ static void planeProjection(TransInfo *t, const float in[3], float out[3]) { float vec[3], factor, norm[3]; - add_v3_v3v3(vec, in, t->con.center); + add_v3_v3v3(vec, in, t->center_global); getViewVector(t, vec, norm); sub_v3_v3v3(vec, out, in); @@ -309,7 +310,7 @@ static void applyAxisConstraintVec(TransInfo *t, TransData *td, const float in[3 mul_m3_v3(t->con.pmtx, out); // With snap, a projection is alright, no need to correct for view alignment - if (!(t->tsnap.mode != SCE_SNAP_MODE_INCREMENT && activeSnap(t))) { + if (ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) || activeSnap(t)) { if (getConstraintSpaceDimension(t) == 2) { if (out[0] != 0.0f || out[1] != 0.0f || out[2] != 0.0f) { planeProjection(t, in, out); @@ -687,11 +688,11 @@ void drawConstraint(TransInfo *t) int depth_test_enabled; convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1])); - add_v3_v3(vec, tc->center); + add_v3_v3(vec, t->center_global); - drawLine(t, tc->center, tc->mtx[0], 'X', 0); - drawLine(t, tc->center, tc->mtx[1], 'Y', 0); - drawLine(t, tc->center, tc->mtx[2], 'Z', 0); + drawLine(t, t->center_global, tc->mtx[0], 'X', 0); + drawLine(t, t->center_global, tc->mtx[1], 'Y', 0); + drawLine(t, t->center_global, tc->mtx[2], 'Z', 0); glColor3ubv((GLubyte *)col2); @@ -701,7 +702,7 @@ void drawConstraint(TransInfo *t) setlinestyle(1); glBegin(GL_LINE_STRIP); - glVertex3fv(tc->center); + glVertex3fv(t->center_global); glVertex3fv(vec); glEnd(); setlinestyle(0); @@ -711,13 +712,13 @@ void drawConstraint(TransInfo *t) } if (tc->mode & CON_AXIS0) { - drawLine(t, tc->center, tc->mtx[0], 'X', DRAWLIGHT); + drawLine(t, t->center_global, tc->mtx[0], 'X', DRAWLIGHT); } if (tc->mode & CON_AXIS1) { - drawLine(t, tc->center, tc->mtx[1], 'Y', DRAWLIGHT); + drawLine(t, t->center_global, tc->mtx[1], 'Y', DRAWLIGHT); } if (tc->mode & CON_AXIS2) { - drawLine(t, tc->center, tc->mtx[2], 'Z', DRAWLIGHT); + drawLine(t, t->center_global, tc->mtx[2], 'Z', DRAWLIGHT); } } } @@ -728,7 +729,6 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) if (t->flag & T_PROP_EDIT) { RegionView3D *rv3d = CTX_wm_region_view3d(C); float tmat[4][4], imat[4][4]; - float center[3]; int depth_test_enabled; UI_ThemeColor(TH_GRID); @@ -744,25 +744,21 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) glPushMatrix(); - copy_v3_v3(center, t->center); - - if ((t->spacetype == SPACE_VIEW3D) && t->obedit) { - mul_m4_v3(t->obedit->obmat, center); /* because t->center is in local space */ + if (t->spacetype == SPACE_VIEW3D) { + /* pass */ } else if (t->spacetype == SPACE_IMAGE) { - float aspx, aspy; - - if (t->options & CTX_MASK) { - /* 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); - } - glScalef(1.0f / aspx, 1.0f / aspy, 1.0); + glScalef(1.0f / t->aspect[0], 1.0f / t->aspect[1], 1.0f); + } + else if (ELEM(t->spacetype, SPACE_IPO, SPACE_ACTION)) { + /* only scale y */ + rcti *mask = &t->ar->v2d.mask; + rctf *datamask = &t->ar->v2d.cur; + float xsize = BLI_rctf_size_x(datamask); + float ysize = BLI_rctf_size_y(datamask); + float xmask = BLI_rcti_size_x(mask); + float ymask = BLI_rcti_size_y(mask); + glScalef(1.0f, (ysize / xsize) * (xmask / ymask), 1.0f); } depth_test_enabled = glIsEnabled(GL_DEPTH_TEST); @@ -770,7 +766,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) glDisable(GL_DEPTH_TEST); set_inverted_drawing(1); - drawcircball(GL_LINE_LOOP, center, t->prop_size, imat); + drawcircball(GL_LINE_LOOP, t->center_global, t->prop_size, imat); set_inverted_drawing(0); if (depth_test_enabled) @@ -961,7 +957,7 @@ static void setNearestAxis3d(TransInfo *t) mul_v3_fl(axis, zfac); /* now we can project to get window coordinate */ - add_v3_v3(axis, t->con.center); + add_v3_v3(axis, t->center_global); projectFloatView(t, axis, axis_2d); sub_v2_v2v2(axis, axis_2d, t->center2d); diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index b50f56aae3e..c9e7e085a96 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -31,6 +31,7 @@ #include <string.h> #include <math.h> +#include <limits.h> #include "DNA_anim_types.h" #include "DNA_brush_types.h" @@ -58,6 +59,7 @@ #include "BLI_linklist_stack.h" #include "BLI_string.h" #include "BLI_bitmap.h" +#include "BLI_rect.h" #include "BKE_DerivedMesh.h" #include "BKE_action.h" @@ -340,20 +342,20 @@ static void createTransEdge(TransInfo *t) BMIter iter; float mtx[3][3], smtx[3][3]; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; int cd_edge_float_offset; BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } } if (countsel == 0) return; - if (propmode) { + if (is_prop_edit) { t->total = count; } else { @@ -379,7 +381,7 @@ static void createTransEdge(TransInfo *t) BLI_assert(cd_edge_float_offset != -1); BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || propmode)) { + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || is_prop_edit)) { float *fl_ptr; /* need to set center for center calculations */ mid_v3_v3v3(td->center, eed->v1->co, eed->v2->co); @@ -627,10 +629,10 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr mul_m3_m3m3(td->axismtx, omat, pmat); normalize_m3(td->axismtx); - if (t->mode == TFM_BONESIZE) { + if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { bArmature *arm = t->poseobj->data; - if (arm->drawtype == ARM_ENVELOPE) { + if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; td->val = &bone->dist; td->ival = bone->dist; @@ -717,7 +719,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) /* make sure no bone can be transformed when a parent is transformed */ /* since pchans are depsgraph sorted, the parents are in beginning of list */ - if (mode != TFM_BONESIZE) { + if (!ELEM(mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; if (bone->flag & BONE_TRANSFORM) @@ -1124,7 +1126,7 @@ static void createTransArmatureVerts(TransInfo *t) oldtot = t->total; if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { - if (t->mode == TFM_BONESIZE) { + if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { if (ebo->flag & BONE_SELECTED) t->total++; } @@ -1202,9 +1204,9 @@ static void createTransArmatureVerts(TransInfo *t) } } - else if (t->mode == TFM_BONESIZE) { + else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { if (ebo->flag & BONE_SELECTED) { - if (arm->drawtype == ARM_ENVELOPE) { + if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; td->val = &ebo->dist; td->ival = ebo->dist; @@ -1329,18 +1331,18 @@ static void createTransMBallVerts(TransInfo *t) TransDataExtension *tx; float mtx[3][3], smtx[3][3]; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; /* count totals */ for (ml = mb->editelems->first; ml; ml = ml->next) { if (ml->flag & SELECT) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(MBall EditMode)"); @@ -1350,7 +1352,7 @@ static void createTransMBallVerts(TransInfo *t) pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); for (ml = mb->editelems->first; ml; ml = ml->next) { - if (propmode || (ml->flag & SELECT)) { + if (is_prop_edit || (ml->flag & SELECT)) { td->loc = &ml->x; copy_v3_v3(td->iloc, td->loc); copy_v3_v3(td->center, td->loc); @@ -1463,7 +1465,7 @@ static void createTransCurveVerts(TransInfo *t) float mtx[3][3], smtx[3][3]; int a; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; short hide_handles = (cu->drawflag & CU_HIDE_HANDLES); ListBase *nurbs; @@ -1478,13 +1480,13 @@ static void createTransCurveVerts(TransInfo *t) if (bezt->hide == 0) { if (hide_handles) { if (bezt->f2 & SELECT) countsel += 3; - if (propmode) count += 3; + if (is_prop_edit) count += 3; } else { if (bezt->f1 & SELECT) countsel++; if (bezt->f2 & SELECT) countsel++; if (bezt->f3 & SELECT) countsel++; - if (propmode) count += 3; + if (is_prop_edit) count += 3; } } } @@ -1492,7 +1494,7 @@ static void createTransCurveVerts(TransInfo *t) else { for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { if (bp->hide == 0) { - if (propmode) count++; + if (is_prop_edit) count++; if (bp->f1 & SELECT) countsel++; } } @@ -1501,7 +1503,7 @@ static void createTransCurveVerts(TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Curve EditMode)"); @@ -1536,7 +1538,7 @@ static void createTransCurveVerts(TransInfo *t) } } - if (propmode || + if (is_prop_edit || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f1 & SELECT) && hide_handles == 0)) { @@ -1570,7 +1572,7 @@ static void createTransCurveVerts(TransInfo *t) } /* This is the Curve Point, the other two are handles */ - if (propmode || (bezt->f2 & SELECT)) { + if (is_prop_edit || (bezt->f2 & SELECT)) { copy_v3_v3(td->iloc, bezt->vec[1]); td->loc = bezt->vec[1]; copy_v3_v3(td->center, td->loc); @@ -1606,7 +1608,7 @@ static void createTransCurveVerts(TransInfo *t) count++; tail++; } - if (propmode || + if (is_prop_edit || ((bezt->f2 & SELECT) && hide_handles) || ((bezt->f3 & SELECT) && hide_handles == 0)) { @@ -1643,12 +1645,12 @@ static void createTransCurveVerts(TransInfo *t) (void)hdata; /* quiet warning */ } - else if (propmode && head != tail) { + else if (is_prop_edit && head != tail) { calc_distanceCurveVerts(head, tail - 1); head = tail; } } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandles @@ -1663,7 +1665,7 @@ static void createTransCurveVerts(TransInfo *t) head = tail = td; for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { if (bp->hide == 0) { - if (propmode || (bp->f1 & SELECT)) { + if (is_prop_edit || (bp->f1 & SELECT)) { copy_v3_v3(td->iloc, bp->vec); td->loc = bp->vec; copy_v3_v3(td->center, td->loc); @@ -1688,12 +1690,12 @@ static void createTransCurveVerts(TransInfo *t) tail++; } } - else if (propmode && head != tail) { + else if (is_prop_edit && head != tail) { calc_distanceCurveVerts(head, tail - 1); head = tail; } } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); } } @@ -1709,14 +1711,14 @@ static void createTransLatticeVerts(TransInfo *t) float mtx[3][3], smtx[3][3]; int a; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { if (bp->hide == 0) { if (bp->f1 & SELECT) countsel++; - if (propmode) count++; + if (is_prop_edit) count++; } bp++; } @@ -1724,7 +1726,7 @@ static void createTransLatticeVerts(TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (propmode) t->total = count; + if (is_prop_edit) t->total = count; else t->total = countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Lattice EditMode)"); @@ -1735,7 +1737,7 @@ static void createTransLatticeVerts(TransInfo *t) bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { - if (propmode || (bp->f1 & SELECT)) { + if (is_prop_edit || (bp->f1 & SELECT)) { if (bp->hide == 0) { copy_v3_v3(td->iloc, bp->vec); td->loc = bp->vec; @@ -1776,7 +1778,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) float mat[4][4]; int i, k, transformparticle; int count = 0, hasselected = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return; @@ -1798,7 +1800,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) hasselected = 1; transformparticle = 1; } - else if (propmode) + else if (is_prop_edit) transformparticle = 1; } } @@ -1848,7 +1850,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) if (key->flag & PEK_SELECT) td->flag |= TD_SELECTED; - else if (!propmode) + else if (!is_prop_edit) td->flag |= TD_SKIP; unit_m3(td->mtx); @@ -1877,7 +1879,7 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) tx++; tail++; } - if (propmode && head != tail) + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); } } @@ -1893,7 +1895,8 @@ void flushTransParticles(TransInfo *t) PTCacheEditKey *key; TransData *td; float mat[4][4], imat[4][4], co[3]; - int i, k, propmode = t->flag & T_PROP_EDIT; + int i, k; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; if (psys) psmd = psys_get_modifier(ob, psys); @@ -1914,7 +1917,7 @@ void flushTransParticles(TransInfo *t) /* optimization for proportional edit */ - if (!propmode || !compare_v3v3(key->co, co, 0.0001f)) { + if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) { copy_v3_v3(key->co, co); point->flag |= PEP_EDIT_RECALC; } @@ -1972,17 +1975,19 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float int i; BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) { + float dist; BM_elem_index_set(v, i); /* set_inline */ BM_elem_flag_disable(v, BM_ELEM_TAG); if (BM_elem_flag_test(v, BM_ELEM_SELECT) == 0 || BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { - dists[i] = FLT_MAX; + dist = FLT_MAX; } else { BLI_LINKSTACK_PUSH(queue, v); - - dists[i] = 0.0f; + dist = 0.0f; } + + dists[i] = dists_prev[i] = dist; } bm->elem_index_dirty &= ~BM_VERT; } @@ -1991,55 +1996,77 @@ static void editmesh_set_connectivity_distance(BMesh *bm, float mtx[3][3], float BMVert *v; LinkNode *lnk; + /* this is correct but slow to do each iteration, + * instead sync the dist's while clearing BM_ELEM_TAG (below) */ +#if 0 memcpy(dists_prev, dists, sizeof(float) * bm->totvert); +#endif while ((v = BLI_LINKSTACK_POP(queue))) { - /* quick checks */ - bool has_edges = (v->e != NULL); - bool has_faces = false; + BLI_assert(dists[BM_elem_index_get(v)] != FLT_MAX); /* connected edge-verts */ - if (has_edges) { - BMIter iter; - BMEdge *e; + if (v->e != NULL) { + BMEdge *e_iter, *e_first; + + e_iter = e_first = v->e; - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - has_faces |= (BM_edge_is_wire(e) == false); + /* would normally use BM_EDGES_OF_VERT, but this runs so often, + * its faster to iterate on the data directly */ + do { - if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == 0) { - BMVert *v_other = BM_edge_other_vert(e, v); + if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == 0) { + + /* edge distance */ + BMVert *v_other = BM_edge_other_vert(e_iter, 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); } } - } - } - } - - /* 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); - } + /* face distance */ + if (e_iter->l) { + BMLoop *l_iter_radial, *l_first_radial; + /** + * imaginary edge diagonally across quad, + * \note, this takes advantage of the rules of winding that we + * know 2 or more of a verts edges wont reference the same face twice. + * Also, if the edge is hidden, the face will be hidden too. + */ + l_iter_radial = l_first_radial = e_iter->l; + + do { + if ((l_iter_radial->v == v) && + (l_iter_radial->f->len == 4) && + (BM_elem_flag_test(l_iter_radial->f, BM_ELEM_HIDDEN) == 0)) + { + BMVert *v_other = l_iter_radial->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_iter_radial = l_iter_radial->radial_next) != l_first_radial); } } - } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); } } + /* clear for the next loop */ for (lnk = queue_next; lnk; lnk = lnk->next) { - BM_elem_flag_disable((BMVert *)lnk->link, BM_ELEM_TAG); + BMVert *v = lnk->link; + const int i = BM_elem_index_get(v); + + BM_elem_flag_disable(v, BM_ELEM_TAG); + + /* keep in sync, avoid having to do full memcpy each iteration */ + dists_prev[i] = dists[i]; } BLI_LINKSTACK_SWAP(queue, queue_next); @@ -2097,7 +2124,7 @@ static struct TransIslandData *editmesh_islands_info_calc(BMEditMesh *em, int *r vert_map = MEM_mallocN(sizeof(*vert_map) * bm->totvert, __func__); /* we shouldn't need this, but with incorrect selection flushing * its possible we have a selected vertex thats not in a face, for now best not crash in that case. */ - fill_vn_i(vert_map, bm->totvert, -1); + copy_vn_i(vert_map, bm->totvert, -1); BM_mesh_elem_table_ensure(bm, htype); ele_array = (htype == BM_FACE) ? (void **)bm->ftable : (void **)bm->etable; @@ -2257,7 +2284,7 @@ static void createTransEditVerts(TransInfo *t) float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; float *dists = NULL; int a; - int propmode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; int mirror = 0; int cd_vert_bweight_offset = -1; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; @@ -2297,7 +2324,7 @@ static void createTransEditVerts(TransInfo *t) cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } - if (propmode) { + if (prop_mode) { unsigned int count = 0; BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { @@ -2308,7 +2335,7 @@ static void createTransEditVerts(TransInfo *t) t->total = count; /* allocating scratch arrays */ - if (propmode & T_PROP_CONNECTED) + if (prop_mode & T_PROP_CONNECTED) dists = MEM_mallocN(em->bm->totvert * sizeof(float), "scratch nears"); } else { @@ -2330,7 +2357,7 @@ static void createTransEditVerts(TransInfo *t) * matrix inversion still works and we can still moving along the other */ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - if (propmode & T_PROP_CONNECTED) { + if (prop_mode & T_PROP_CONNECTED) { editmesh_set_connectivity_distance(em->bm, mtx, dists); } @@ -2359,7 +2386,7 @@ static void createTransEditVerts(TransInfo *t) { mappedcos = BKE_crazyspace_get_mapped_editverts(t->scene, t->obedit); quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); - BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !propmode); + BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); if (mappedcos) MEM_freeN(mappedcos); } @@ -2384,7 +2411,7 @@ static void createTransEditVerts(TransInfo *t) BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (propmode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { struct TransIslandData *v_island = (island_info && island_vert_map[a] != -1) ? &island_info[island_vert_map[a]] : NULL; float *bweight = (cd_vert_bweight_offset != -1) ? BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : NULL; @@ -2397,8 +2424,8 @@ static void createTransEditVerts(TransInfo *t) if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) tob->flag |= TD_SELECTED; - if (propmode) { - if (propmode & T_PROP_CONNECTED) { + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { tob->dist = dists[a]; } else { @@ -2650,24 +2677,23 @@ void flushTransSeq(TransInfo *t) /* ********************* UV ****************** */ -static void UVsToTransData(SpaceImage *sima, TransData *td, TransData2D *td2d, float *uv, int selected) +static void UVsToTransData( + const float aspect[2], TransData *td, TransData2D *td2d, + float *uv, const float *center, bool selected) { - float aspx, aspy; - - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - /* uv coords are scaled by aspects. this is needed for rotations and * proportional editing to be consistent with the stretched uv coords * that are displayed. this also means that for display and numinput, * and when the uv coords are flushed, these are converted each time */ - td2d->loc[0] = uv[0] * aspx; - td2d->loc[1] = uv[1] * aspy; + td2d->loc[0] = uv[0] * aspect[0]; + td2d->loc[1] = uv[1] * aspect[1]; td2d->loc[2] = 0.0f; td2d->loc2d = uv; td->flag = 0; td->loc = td2d->loc; - copy_v3_v3(td->center, td->loc); + copy_v2_v2(td->center, center ? center : td->loc); + td->center[2] = 0.0f; copy_v3_v3(td->iloc, td->loc); memset(td->axismtx, 0, sizeof(td->axismtx)); @@ -2694,36 +2720,43 @@ static void createTransUVs(bContext *C, TransInfo *t) ToolSettings *ts = CTX_data_tool_settings(C); TransData *td = NULL; TransData2D *td2d = NULL; - MTexPoly *tf; - MLoopUV *luv; BMEditMesh *em = BKE_editmesh_from_object(t->obedit); BMFace *efa; - BMLoop *l; BMIter iter, liter; UvElementMap *elementmap = NULL; BLI_bitmap *island_enabled = NULL; + struct { float co[2]; int co_num; } *island_center = NULL; int count = 0, countsel = 0, count_rejected = 0; - const bool propmode = (t->flag & T_PROP_EDIT) != 0; - const bool propconnected = (t->flag & T_PROP_CONNECTED) != 0; - + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; + const bool is_island_center = (t->around == V3D_LOCAL); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - if (!ED_space_image_show_uvedit(sima, t->obedit)) return; + if (!ED_space_image_show_uvedit(sima, t->obedit)) + return; /* count */ - if (propconnected) { + if (is_prop_connected || is_island_center) { /* create element map with island information */ - if (ts->uv_flag & UV_SYNC_SELECTION) { - elementmap = BM_uv_element_map_create(em->bm, false, true); + const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; + elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); + if (elementmap == NULL) { + return; } - else { - elementmap = BM_uv_element_map_create(em->bm, true, true); + + if (is_prop_connected) { + island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); + } + + if (is_island_center) { + island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); } - island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY); + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + BMLoop *l; if (!uvedit_face_visible_test(scene, ima, efa, tf)) { BM_elem_flag_disable(efa, BM_ELEM_TAG); @@ -2735,14 +2768,25 @@ static void createTransUVs(bContext *C, TransInfo *t) if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { countsel++; - if (propconnected) { + if (is_prop_connected || island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - BLI_BITMAP_ENABLE(island_enabled, element->island); - } + if (is_prop_connected) { + BLI_BITMAP_ENABLE(island_enabled, element->island); + } + + if (is_island_center) { + if (element->flag == false) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(island_center[element->island].co, luv->uv); + island_center[element->island].co_num++; + element->flag = true; + } + } + } } - if (propmode) { + if (is_prop_edit) { count++; } } @@ -2750,13 +2794,19 @@ static void createTransUVs(bContext *C, TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) { - if (propconnected) { - MEM_freeN(island_enabled); + goto finally; + } + + if (is_island_center) { + int i; + + for (i = 0; i < elementmap->totalIslands; i++) { + mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); + mul_v2_v2(island_center[i].co, t->aspect); } - return; } - t->total = (propmode) ? count : countsel; + t->total = (is_prop_edit) ? count : countsel; t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(UV Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ @@ -2769,56 +2819,88 @@ static void createTransUVs(bContext *C, TransInfo *t) td2d = t->data2d; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l; + if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (!propmode && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) + const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + MLoopUV *luv; + const float *center = NULL; + + if (!is_prop_edit && !selected) continue; - if (propconnected) { + if (is_prop_connected || is_island_center) { UvElement *element = BM_uv_element_get(elementmap, efa, l); - if (!BLI_BITMAP_TEST(island_enabled, element->island)) { - count_rejected++; - continue; + + if (is_prop_connected) { + if (!BLI_BITMAP_TEST(island_enabled, element->island)) { + count_rejected++; + continue; + } + } + + if (is_island_center) { + center = island_center[element->island].co; } } + BM_elem_flag_enable(l, BM_ELEM_TAG); luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - UVsToTransData(sima, td++, td2d++, luv->uv, uvedit_uv_select_test(scene, l, cd_loop_uv_offset)); + UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected); } } - if (propconnected) { + if (is_prop_connected) { t->total -= count_rejected; - BM_uv_element_map_free(elementmap); - MEM_freeN(island_enabled); } if (sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_begin(t->scene, t->obedit); + + +finally: + if (is_prop_connected || is_island_center) { + BM_uv_element_map_free(elementmap); + + if (is_prop_connected) { + MEM_freeN(island_enabled); + } + + if (island_center) { + MEM_freeN(island_center); + } + } } void flushTransUVs(TransInfo *t) { SpaceImage *sima = t->sa->spacedata.first; TransData2D *td; - int a, width, height; - float aspx, aspy, invx, invy; + int a; + float aspect_inv[2], size[2]; + const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)); - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - ED_space_image_get_size(sima, &width, &height); - invx = 1.0f / aspx; - invy = 1.0f / aspy; + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + if (use_pixel_snap) { + int size_i[2]; + ED_space_image_get_size(sima, &size_i[0], &size_i[1]); + size[0] = size_i[0]; + size[1] = size_i[1]; + } /* flush to 2d vector from internally used 3d vector */ for (a = 0, td = t->data2d; a < t->total; a++, td++) { - td->loc2d[0] = td->loc[0] * invx; - td->loc2d[1] = td->loc[1] * invy; + td->loc2d[0] = td->loc[0] * aspect_inv[0]; + td->loc2d[1] = td->loc[1] * aspect_inv[1]; - if ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)) { - td->loc2d[0] = roundf(width * td->loc2d[0]) / width; - td->loc2d[1] = roundf(height * td->loc2d[1]) / height; + if (use_pixel_snap) { + td->loc2d[0] = roundf(td->loc2d[0] * size[0]) / size[0]; + td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1]; } } } @@ -2826,44 +2908,45 @@ void flushTransUVs(TransInfo *t) bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { TransData *td; - int a, clipx = 1, clipy = 1; - float aspx, aspy, min[2], max[2]; + int a; + bool clipx = true, clipy = true; + float min[2], max[2]; - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); min[0] = min[1] = 0.0f; - max[0] = aspx; max[1] = aspy; + max[0] = t->aspect[0]; + max[1] = t->aspect[1]; for (a = 0, td = t->data; a < t->total; a++, td++) { minmax_v2v2_v2(min, max, td->loc); } if (resize) { - if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < aspx * 0.5f) + if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f) vec[0] *= t->center[0] / (t->center[0] - min[0]); - else if (max[0] > aspx && t->center[0] < aspx) - vec[0] *= (t->center[0] - aspx) / (t->center[0] - max[0]); + else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0]) + vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]); else clipx = 0; - if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < aspy * 0.5f) + if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f) vec[1] *= t->center[1] / (t->center[1] - min[1]); - else if (max[1] > aspy && t->center[1] < aspy) - vec[1] *= (t->center[1] - aspy) / (t->center[1] - max[1]); + else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1]) + vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]); else clipy = 0; } else { if (min[0] < 0.0f) vec[0] -= min[0]; - else if (max[0] > aspx) - vec[0] -= max[0] - aspx; + else if (max[0] > t->aspect[0]) + vec[0] -= max[0] - t->aspect[0]; else clipx = 0; if (min[1] < 0.0f) vec[1] -= min[1]; - else if (max[1] > aspy) - vec[1] -= max[1] - aspy; + else if (max[1] > t->aspect[1]) + vec[1] -= max[1] - t->aspect[1]; else clipy = 0; } @@ -2875,9 +2958,6 @@ void clipUVData(TransInfo *t) { TransData *td = NULL; int a; - float aspx, aspy; - - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); for (a = 0, td = t->data; a < t->total; a++, td++) { if (td->flag & TD_NOACTION) @@ -2886,8 +2966,8 @@ void clipUVData(TransInfo *t) if ((td->flag & TD_SKIP) || (!td->loc)) continue; - td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), aspx); - td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), aspy); + td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]); + td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]); } } @@ -3161,7 +3241,7 @@ static void posttrans_gpd_clean(bGPdata *gpd) bGPDframe *gpf, *gpfn; bool is_double = false; - BLI_listbase_sort_r(&gpl->frames, &is_double, gpf_cmp_frame); + BLI_listbase_sort_r(&gpl->frames, gpf_cmp_frame, &is_double); if (is_double) { for (gpf = gpl->frames.first; gpf; gpf = gpfn) { @@ -3188,7 +3268,7 @@ static void posttrans_mask_clean(Mask *mask) MaskLayerShape *masklay_shape, *masklay_shape_next; bool is_double = false; - BLI_listbase_sort_r(&masklay->splines_shapes, &is_double, masklay_shape_cmp_frame); + BLI_listbase_sort_r(&masklay->splines_shapes, masklay_shape_cmp_frame, &is_double); if (is_double) { for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape_next) { @@ -3304,76 +3384,90 @@ static void posttrans_action_clean(bAnimContext *ac, bAction *act) /* ----------------------------- */ /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_fcurve_keys(FCurve *fcu, char side, float cfra) +static int count_fcurve_keys(FCurve *fcu, char side, float cfra, bool is_prop_edit) { BezTriple *bezt; - int i, count = 0; + int i, count = 0, count_all = 0; if (ELEM(NULL, fcu, fcu->bezt)) return count; /* only include points that occur on the right side of cfra */ for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { - if (bezt->f2 & SELECT) { + if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { /* no need to adjust the handle selection since they are assumed * selected (like graph editor with SIPO_NOHANDLES) */ - if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - count += 1; - } + if (bezt->f2 & SELECT) + count++; + + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else return count; } /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra) +static int count_gplayer_frames(bGPDlayer *gpl, char side, float cfra, bool is_prop_edit) { bGPDframe *gpf; - int count = 0; + int count = 0, count_all = 0; if (gpl == NULL) return count; /* only include points that occur on the right side of cfra */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) { - if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) + if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { + if (gpf->flag & GP_FRAME_SELECT) count++; + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else + return count; } /* fully select selected beztriples, but only include if it's on the right side of cfra */ -static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra) +static int count_masklayer_frames(MaskLayer *masklay, char side, float cfra, bool is_prop_edit) { MaskLayerShape *masklayer_shape; - int count = 0; + int count = 0, count_all = 0; if (masklay == NULL) return count; /* only include points that occur on the right side of cfra */ for (masklayer_shape = masklay->splines_shapes.first; masklayer_shape; masklayer_shape = masklayer_shape->next) { - if (masklayer_shape->flag & MASK_SHAPE_SELECT) { - if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) + if (FrameOnMouseSide(side, (float)masklayer_shape->frame, cfra)) { + if (masklayer_shape->flag & MASK_SHAPE_SELECT) count++; + count_all++; } } - return count; + if (is_prop_edit && count > 0) + return count_all; + else + return count; } /* This function assigns the information to transdata */ -static void TimeToTransData(TransData *td, float *time, AnimData *adt) +static void TimeToTransData(TransData *td, float *time, AnimData *adt, float ypos) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = time; td->ival = *(time); + td->center[0] = td->ival; + td->center[1] = ypos; + /* store the AnimData where this keyframe exists as a keyframe of the * active action as td->extra. */ @@ -3387,7 +3481,7 @@ static void TimeToTransData(TransData *td, float *time, AnimData *adt) * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ -static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra) +static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FCurve *fcu, AnimData *adt, char side, float cfra, bool is_prop_edit, float ypos) { BezTriple *bezt; TransData2D *td2d = *td2dv; @@ -3398,11 +3492,14 @@ static TransData *ActionFCurveToTransData(TransData *td, TransData2D **td2dv, FC for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* only add selected keyframes (for now, proportional edit is not enabled) */ - if (bezt->f2 & SELECT) { /* note this MUST match count_fcurve_keys(), so can't use BEZSELECTED() macro */ + if (is_prop_edit || (bezt->f2 & SELECT)) { /* note this MUST match count_fcurve_keys(), so can't use BEZSELECTED() macro */ /* only add if on the right 'side' of the current frame */ if (FrameOnMouseSide(side, bezt->vec[1][0], cfra)) { - TimeToTransData(td, bezt->vec[1], adt); + TimeToTransData(td, bezt->vec[1], adt, ypos); + if (bezt->f2 & SELECT) + td->flag |= TD_SELECTED; + /*set flags to move handles as necessary*/ td->flag |= TD_MOVEHANDLE1 | TD_MOVEHANDLE2; td2d->h1 = bezt->vec[0]; @@ -3453,19 +3550,22 @@ void flushTransIntFrameActionData(TransInfo *t) * The 'side' argument is needed for the extend mode. 'B' = both sides, 'R'/'L' mean only data * on the named side are used. */ -static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra) +static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, char side, float cfra, bool is_prop_edit, float ypos) { bGPDframe *gpf; int count = 0; /* check for select frames on right side of current frame */ for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { - if (gpf->flag & GP_FRAME_SELECT) { + if (is_prop_edit || (gpf->flag & GP_FRAME_SELECT)) { if (FrameOnMouseSide(side, (float)gpf->framenum, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)gpf->framenum; + td->center[0] = td->ival; + td->center[1] = ypos; + tfd->val = (float)gpf->framenum; tfd->sdata = &gpf->framenum; @@ -3481,19 +3581,22 @@ static int GPLayerToTransData(TransData *td, tGPFtransdata *tfd, bGPDlayer *gpl, } /* refer to comment above #GPLayerToTransData, this is the same but for masks */ -static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra) +static int MaskLayerToTransData(TransData *td, tGPFtransdata *tfd, MaskLayer *masklay, char side, float cfra, bool is_prop_edit, float ypos) { MaskLayerShape *masklay_shape; int count = 0; /* check for select frames on right side of current frame */ for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { - if (masklay_shape->flag & MASK_SHAPE_SELECT) { + if (is_prop_edit || (masklay_shape->flag & MASK_SHAPE_SELECT)) { if (FrameOnMouseSide(side, (float)masklay_shape->frame, cfra)) { /* memory is calloc'ed, so that should zero everything nicely for us */ td->val = &tfd->val; td->ival = (float)masklay_shape->frame; + td->center[0] = td->ival; + td->center[1] = ypos; + tfd->val = (float)masklay_shape->frame; tfd->sdata = &masklay_shape->frame; @@ -3515,15 +3618,25 @@ static void createTransActionData(bContext *C, TransInfo *t) TransData *td = NULL; TransData2D *td2d = NULL; tGPFtransdata *tfd = NULL; - + + rcti *mask = &t->ar->v2d.mask; + rctf *datamask = &t->ar->v2d.cur; + + float xsize = BLI_rctf_size_x(datamask); + float ysize = BLI_rctf_size_y(datamask); + float xmask = BLI_rcti_size_x(mask); + float ymask = BLI_rcti_size_y(mask); + bAnimContext ac; ListBase anim_data = {NULL, NULL}; bAnimListElem *ale; int filter; - + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + int count = 0; float cfra; - + float ypos = 1.0f / ((ysize / xsize) * (xmask / ymask)) * BLI_rctf_cent_y(&t->ar->v2d.cur); + /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) return; @@ -3551,7 +3664,7 @@ static void createTransActionData(bContext *C, TransInfo *t) /* loop 1: fully select ipo-keys and count how many BezTriples are selected */ for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); - + int adt_count = 0; /* convert current-frame to action-time (slightly less accurate, especially under * higher scaling ratios, but is faster than converting all points) */ @@ -3560,14 +3673,19 @@ static void createTransActionData(bContext *C, TransInfo *t) else cfra = (float)CFRA; - if (ale->type == ANIMTYPE_FCURVE) - count += count_fcurve_keys(ale->key_data, t->frame_side, cfra); + if (ELEM(ale->type, ANIMTYPE_FCURVE, ANIMTYPE_NLACURVE)) + adt_count = count_fcurve_keys(ale->key_data, t->frame_side, cfra, is_prop_edit); else if (ale->type == ANIMTYPE_GPLAYER) - count += count_gplayer_frames(ale->data, t->frame_side, cfra); + adt_count = count_gplayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); else if (ale->type == ANIMTYPE_MASKLAYER) - count += count_masklayer_frames(ale->data, t->frame_side, cfra); + adt_count = count_masklayer_frames(ale->data, t->frame_side, cfra, is_prop_edit); else BLI_assert(0); + + if (adt_count > 0) { + count += adt_count; + ale->tag = true; + } } /* stop if trying to build list if nothing selected */ @@ -3604,11 +3722,22 @@ static void createTransActionData(bContext *C, TransInfo *t) /* loop 2: build transdata array */ for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt; + + if (is_prop_edit && !ale->tag) + continue; + + adt = ANIM_nla_mapping_get(&ac, ale); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + if (ale->type == ANIMTYPE_GPLAYER) { bGPDlayer *gpl = (bGPDlayer *)ale->data; int i; - i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra); + i = GPLayerToTransData(td, tfd, gpl, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } @@ -3616,7 +3745,7 @@ static void createTransActionData(bContext *C, TransInfo *t) MaskLayer *masklay = (MaskLayer *)ale->data; int i; - i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra); + i = MaskLayerToTransData(td, tfd, masklay, t->frame_side, cfra, is_prop_edit, ypos); td += i; tfd += i; } @@ -3624,15 +3753,7 @@ static void createTransActionData(bContext *C, TransInfo *t) AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; - /* convert current-frame to action-time (slightly less accurate, especially under - * higher scaling ratios, but is faster than converting all points) - */ - if (adt) - cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); - else - cfra = (float)CFRA; - - td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra); + td = ActionFCurveToTransData(td, &td2d, fcu, adt, t->frame_side, cfra, is_prop_edit, ypos); } } @@ -3661,6 +3782,109 @@ static void createTransActionData(bContext *C, TransInfo *t) *((float *)(t->customData) + 1) = max; } + /* calculate distances for proportional editing */ + if (is_prop_edit) { + td = t->data; + + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt; + + /* F-Curve may not have any keyframes */ + if (!ale->tag) + continue; + + adt = ANIM_nla_mapping_get(&ac, ale); + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + + if (ale->type == ANIMTYPE_GPLAYER) { + bGPDlayer *gpl = (bGPDlayer *)ale->data; + bGPDframe *gpf; + + for (gpf = gpl->frames.first; gpf; gpf = gpf->next) { + if (gpf->flag & GP_FRAME_SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + bGPDframe *gpf_iter; + int min = INT_MAX; + for (gpf_iter = gpl->frames.first; gpf_iter; gpf_iter = gpf->next) { + if (gpf_iter->flag & GP_FRAME_SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)gpf_iter->framenum, cfra)) { + int val = abs(gpf->framenum - gpf_iter->framenum); + if (val < min) { + min = val; + } + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + else if (ale->type == ANIMTYPE_MASKLAYER) { + MaskLayer *masklay = (MaskLayer *)ale->data; + MaskLayerShape *masklay_shape; + + for (masklay_shape = masklay->splines_shapes.first; masklay_shape; masklay_shape = masklay_shape->next) { + if (FrameOnMouseSide(t->frame_side, (float)masklay_shape->frame, cfra)) { + if (masklay_shape->flag & MASK_SHAPE_SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + MaskLayerShape *masklay_iter; + int min = INT_MAX; + for (masklay_iter = masklay->splines_shapes.first; masklay_iter; masklay_iter = masklay_iter->next) { + if (masklay_iter->flag & MASK_SHAPE_SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)masklay_iter->frame, cfra)) { + int val = abs(masklay_shape->frame - masklay_iter->frame); + if (val < min) { + min = val; + } + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + } + else { + FCurve *fcu = (FCurve *)ale->key_data; + BezTriple *bezt; + int i; + + for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { + if (bezt->f2 & SELECT) { + td->dist = td->rdist = 0.0f; + } + else { + BezTriple *bezt_iter; + int j; + float min = FLT_MAX; + for (j = 0, bezt_iter = fcu->bezt; j < fcu->totvert; j++, bezt_iter++) { + if (bezt_iter->f2 & SELECT) { + if (FrameOnMouseSide(t->frame_side, (float)bezt_iter->vec[1][0], cfra)) { + float val = fabs(bezt->vec[1][0] - bezt_iter->vec[1][0]); + if (val < min) + min = val; + } + } + } + td->dist = td->rdist = min; + } + td++; + } + } + } + } + } + /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); } @@ -3669,6 +3893,7 @@ static void createTransActionData(bContext *C, TransInfo *t) typedef struct TransDataGraph { float unit_scale; + float offset; } TransDataGraph; /* Helper function for createTransGraphEditData, which is responsible for associating @@ -3677,7 +3902,7 @@ typedef struct TransDataGraph { 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 mtx[3][3], float smtx[3][3], float unit_scale, float offset) { float *loc = bezt->vec[bi]; const float *cent = bezt->vec[1]; @@ -3691,26 +3916,26 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph * if (adt) { td2d->loc[0] = BKE_nla_tweakedit_remap(adt, loc[0], NLATIME_CONVERT_MAP); - td2d->loc[1] = loc[1] * unit_scale; + td2d->loc[1] = (loc[1] + offset) * 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] * unit_scale; + td->center[1] = (cent[1] + offset) * 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] * unit_scale; + td2d->loc[1] = (loc[1] + offset) * 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; + td->center[1] = (td->center[1] + offset) * unit_scale; copy_v3_v3(td->iloc, td->loc); } @@ -3750,6 +3975,7 @@ static void bezt_to_transdata(TransData *td, TransData2D *td2d, TransDataGraph * copy_m3_m3(td->smtx, smtx); tdg->unit_scale = unit_scale; + tdg->offset = offset; } static bool graph_edit_is_translation_mode(TransInfo *t) @@ -3762,6 +3988,29 @@ static bool graph_edit_use_local_center(TransInfo *t) return (t->around == V3D_LOCAL) && !graph_edit_is_translation_mode(t); } + +static void graph_key_shortest_dist(TransInfo *t, FCurve *fcu, TransData *td_start, TransData *td, int cfra, bool use_handle) +{ + int j = 0; + TransData *td_iter = td_start; + + td->dist = FLT_MAX; + for (; j < fcu->totvert; j++) { + BezTriple *bezt = fcu->bezt + j; + if (FrameOnMouseSide(t->frame_side, bezt->vec[1][0], cfra)) { + const bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + + if (sel1 || sel2 || sel3) { + td->dist = td->rdist = min_ff(td->dist, fabs(td_iter->center[0] - td->center[0])); + } + + td_iter += 3; + } + } +} + static void createTransGraphEditData(bContext *C, TransInfo *t) { SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; @@ -3784,6 +4033,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) const bool is_translation_mode = graph_edit_is_translation_mode(t); const bool use_handle = !(sipo->flag & SIPO_NOHANDLES); const bool use_local_center = graph_edit_use_local_center(t); + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; short anim_map_flag = ANIM_UNITCONV_ONLYSEL | ANIM_UNITCONV_SELVERTS; /* determine what type of data we are operating on */ @@ -3815,6 +4065,8 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; float cfra; + int curvecount = 0; + bool selected = false; /* F-Curve may not have any keyframes */ if (fcu->bezt == NULL) @@ -3835,20 +4087,34 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; - if (!is_translation_mode || !(sel2)) { - if (sel1) { - count++; + if (is_prop_edit) { + curvecount += 3; + if (sel2 || sel1 || sel3) + selected = true; + } + else { + if (!is_translation_mode || !(sel2)) { + if (sel1) { + count++; + } + + if (sel3) { + count++; + } } - if (sel3) { + /* only include main vert if selected */ + if (sel2 && !use_local_center) { count++; } } + } + } - /* only include main vert if selected */ - if (sel2 && !use_local_center) { - count++; - } + if (is_prop_edit) { + if (selected) { + count += curvecount; + ale->tag = true; } } } @@ -3897,11 +4163,11 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) AnimData *adt = ANIM_nla_mapping_get(&ac, ale); FCurve *fcu = (FCurve *)ale->key_data; bool intvals = (fcu->flag & FCURVE_INT_VALUES) != 0; - float unit_scale; + float unit_scale, offset; float cfra; /* F-Curve may not have any keyframes */ - if (fcu->bezt == NULL) + if (fcu->bezt == NULL || (is_prop_edit && ale->tag == 0)) continue; /* convert current-frame to action-time (slightly less accurate, especially under @@ -3912,7 +4178,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) else cfra = (float)CFRA; - unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag); + unit_scale = ANIM_unit_mapping_get_factor(ac.scene, ale->id, ale->key_data, anim_map_flag, &offset); /* 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++) { @@ -3924,66 +4190,135 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) TransDataCurveHandleFlags *hdata = NULL; /* short h1=1, h2=1; */ /* UNUSED */ - /* only include handles if selected, irrespective of the interpolation modes. - * also, only treat handles specially if the center point isn't selected. - */ - if (!is_translation_mode || !(sel2)) { - if (sel1) { - hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale); + if (is_prop_edit) { + bool is_sel = (sel2 || sel1 || sel3); + /* we always select all handles for proportional editing if central handle is selected */ + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, is_sel, true, intvals, mtx, smtx, unit_scale, offset); + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, is_sel, false, intvals, mtx, smtx, unit_scale, offset); + initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, is_sel, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* only include handles if selected, irrespective of the interpolation modes. + * also, only treat handles specially if the center point isn't selected. + */ + if (!is_translation_mode || !(sel2)) { + if (sel1) { + hdata = initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 0, sel1, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* h1 = 0; */ /* UNUSED */ + } + + if (sel3) { + if (hdata == NULL) + hdata = initTransDataCurveHandles(td, bezt); + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale, offset); + } + else { + /* h2 = 0; */ /* UNUSED */ + } } - else { - /* h1 = 0; */ /* UNUSED */ + + /* only include main vert if selected */ + if (sel2 && !use_local_center) { + /* move handles relative to center */ + if (is_translation_mode) { + if (sel1) td->flag |= TD_MOVEHANDLE1; + if (sel3) td->flag |= TD_MOVEHANDLE2; + } + + /* if handles were not selected, store their selection status */ + if (!(sel1) || !(sel3)) { + if (hdata == NULL) + hdata = initTransDataCurveHandles(td, bezt); + } + + bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 1, sel2, false, intvals, mtx, smtx, unit_scale, offset); + } - - if (sel3) { - if (hdata == NULL) - hdata = initTransDataCurveHandles(td, bezt); - bezt_to_transdata(td++, td2d++, tdg++, adt, bezt, 2, sel3, true, intvals, mtx, smtx, unit_scale); + /* special hack (must be done after initTransDataCurveHandles(), as that stores handle settings to restore...): + * - Check if we've got entire BezTriple selected and we're scaling/rotating that point, + * then check if we're using auto-handles. + * - If so, change them auto-handles to aligned handles so that handles get affected too + */ + if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && + ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) && + ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) + { + if (hdata && (sel1) && (sel3)) { + bezt->h1 = HD_ALIGN; + bezt->h2 = HD_ALIGN; + } + } + } + } + } + + /* Sets handles based on the selection */ + testhandles_fcurve(fcu, use_handle); + } + + if (is_prop_edit) { + /* loop 2: build transdata arrays */ + td = t->data; + + for (ale = anim_data.first; ale; ale = ale->next) { + AnimData *adt = ANIM_nla_mapping_get(&ac, ale); + FCurve *fcu = (FCurve *)ale->key_data; + TransData *td_start = td; + float cfra; + + /* F-Curve may not have any keyframes */ + if (fcu->bezt == NULL || (ale->tag == 0)) + continue; + + /* convert current-frame to action-time (slightly less accurate, especially under + * higher scaling ratios, but is faster than converting all points) + */ + if (adt) + cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP); + else + cfra = (float)CFRA; + + /* 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 bool sel2 = (bezt->f2 & SELECT) != 0; + const bool sel1 = use_handle ? (bezt->f1 & SELECT) != 0 : sel2; + const bool sel3 = use_handle ? (bezt->f3 & SELECT) != 0 : sel2; + + if (sel1 || sel2) { + td->dist = td->rdist = 0.0f; } else { - /* h2 = 0; */ /* UNUSED */ + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } - } - - /* only include main vert if selected */ - if (sel2 && !use_local_center) { - /* move handles relative to center */ - if (is_translation_mode) { - if (sel1) td->flag |= TD_MOVEHANDLE1; - if (sel3) td->flag |= TD_MOVEHANDLE2; + td++; + + if (sel2) { + td->dist = td->rdist = 0.0f; } - - /* if handles were not selected, store their selection status */ - if (!(sel1) || !(sel3)) { - if (hdata == NULL) - hdata = initTransDataCurveHandles(td, bezt); + else { + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } - - 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...): - * - Check if we've got entire BezTriple selected and we're scaling/rotating that point, - * then check if we're using auto-handles. - * - If so, change them auto-handles to aligned handles so that handles get affected too - */ - if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) && - ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM) && - ELEM(t->mode, TFM_ROTATION, TFM_RESIZE)) - { - if (hdata && (sel1) && (sel3)) { - bezt->h1 = HD_ALIGN; - bezt->h2 = HD_ALIGN; + td++; + + if (sel3 || sel2) { + td->dist = td->rdist = 0.0f; + } + else { + graph_key_shortest_dist(t, fcu, td_start, td, cfra, use_handle); } + td++; } } } - - /* Sets handles based on the selection */ - testhandles_fcurve(fcu, use_handle); } - + /* cleanup temp list */ ANIM_animdata_freelist(&anim_data); } @@ -4267,7 +4602,7 @@ 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] * inv_unit_scale; + td2d->loc2d[1] = td2d->loc[1] * inv_unit_scale - tdg->offset; if ((td->flag & TD_MOVEHANDLE1) && td2d->h1) { td2d->h1[0] = td2d->ih1[0] + td->loc[0] - td->iloc[0]; @@ -5080,7 +5415,9 @@ static void set_trans_object_base_flags(TransInfo *t) } /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ +#ifdef WITH_LEGACY_DEPSGRAPH DAG_scene_flush_update(G.main, t->scene, -1, 0); +#endif /* and we store them temporal in base (only used for transform code) */ /* this because after doing updates, the object->recalc is cleared */ @@ -5157,7 +5494,9 @@ static int count_proportional_objects(TransInfo *t) /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ DAG_scene_relations_update(G.main, t->scene); +#ifdef WITH_LEGACY_DEPSGRAPH DAG_scene_flush_update(G.main, t->scene, -1, 0); +#endif /* and we store them temporal in base (only used for transform code) */ /* this because after doing updates, the object->recalc is cleared */ @@ -6039,7 +6378,7 @@ static void createTransObject(bContext *C, TransInfo *t) TransData *td = NULL; TransDataExtension *tx; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; set_trans_object_base_flags(t); @@ -6052,7 +6391,7 @@ static void createTransObject(bContext *C, TransInfo *t) return; } - if (propmode) { + if (is_prop_edit) { t->total += count_proportional_objects(t); } @@ -6085,7 +6424,7 @@ static void createTransObject(bContext *C, TransInfo *t) } CTX_DATA_END; - if (propmode) { + if (is_prop_edit) { View3D *v3d = t->view; Base *base; @@ -6239,22 +6578,22 @@ typedef struct TransDataTracking { static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingTrack *track, MovieTrackingMarker *marker, - int area, float loc[2], float rel[2], const float off[2], float aspx, float aspy) + int area, float loc[2], float rel[2], const float off[2], const float aspect[2]) { int anchor = area == TRACK_AREA_POINT && off; tdt->mode = transDataTracking_ModeTracks; if (anchor) { - td2d->loc[0] = rel[0] * aspx; /* hold original location */ - td2d->loc[1] = rel[1] * aspy; + td2d->loc[0] = rel[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = rel[1] * aspect[1]; tdt->loc = loc; td2d->loc2d = loc; /* current location */ } else { - td2d->loc[0] = loc[0] * aspx; /* hold original location */ - td2d->loc[1] = loc[1] * aspy; + td2d->loc[0] = loc[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = loc[1] * aspect[1]; td2d->loc2d = loc; /* current location */ } @@ -6269,8 +6608,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra if (rel) { if (!anchor) { - td2d->loc[0] += rel[0] * aspx; - td2d->loc[1] += rel[1] * aspy; + td2d->loc[0] += rel[0] * aspect[0]; + td2d->loc[1] += rel[1] * aspect[1]; } copy_v2_v2(tdt->srelative, rel); @@ -6285,8 +6624,8 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra //copy_v3_v3(td->center, td->loc); td->flag |= TD_INDIVIDUAL_SCALE; - td->center[0] = marker->pos[0] * aspx; - td->center[1] = marker->pos[1] * aspy; + td->center[0] = marker->pos[0] * aspect[0]; + td->center[1] = marker->pos[1] * aspect[1]; memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; @@ -6301,8 +6640,9 @@ static void markerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTra unit_m3(td->smtx); } -static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d, - TransDataTracking *tdt, MovieTrackingTrack *track, float aspx, float aspy) +static void trackToTransData( + const int framenr, TransData *td, TransData2D *td2d, + TransDataTracking *tdt, MovieTrackingTrack *track, const float aspect[2]) { MovieTrackingMarker *marker = BKE_tracking_marker_ensure(track, framenr); @@ -6310,11 +6650,11 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d marker->flag &= ~(MARKER_DISABLED | MARKER_TRACKED); markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, - track->offset, marker->pos, track->offset, aspx, aspy); + track->offset, marker->pos, track->offset, aspect); if (track->flag & SELECT) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_POINT, - marker->pos, NULL, NULL, aspx, aspy); + marker->pos, NULL, NULL, aspect); } if (track->pat_flag & SELECT) { @@ -6322,28 +6662,28 @@ static void trackToTransData(const int framenr, TransData *td, TransData2D *td2d for (a = 0; a < 4; a++) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_PAT, - marker->pattern_corners[a], marker->pos, NULL, aspx, aspy); + marker->pattern_corners[a], marker->pos, NULL, aspect); } } if (track->search_flag & SELECT) { markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH, - marker->search_min, marker->pos, NULL, aspx, aspy); + marker->search_min, marker->pos, NULL, aspect); markerToTransDataInit(td++, td2d++, tdt++, track, marker, TRACK_AREA_SEARCH, - marker->search_max, marker->pos, NULL, aspx, aspy); + marker->search_max, marker->pos, NULL, aspect); } } static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingPlaneTrack *plane_track, float corner[2], - float aspx, float aspy) + const float aspect[2]) { tdt->mode = transDataTracking_ModePlaneTracks; tdt->plane_track = plane_track; - td2d->loc[0] = corner[0] * aspx; /* hold original location */ - td2d->loc[1] = corner[1] * aspy; + td2d->loc[0] = corner[0] * aspect[0]; /* hold original location */ + td2d->loc[1] = corner[1] * aspect[1]; td2d->loc2d = corner; /* current location */ td2d->loc[2] = 0.0f; @@ -6368,7 +6708,7 @@ static void planeMarkerToTransDataInit(TransData *td, TransData2D *td2d, TransDa static void planeTrackToTransData(const int framenr, TransData *td, TransData2D *td2d, TransDataTracking *tdt, MovieTrackingPlaneTrack *plane_track, - float aspx, float aspy) + const float aspect[2]) { MovieTrackingPlaneMarker *plane_marker = BKE_tracking_plane_marker_ensure(plane_track, framenr); int i; @@ -6377,7 +6717,7 @@ static void planeTrackToTransData(const int framenr, TransData *td, TransData2D plane_marker->flag &= ~PLANE_MARKER_TRACKED; for (i = 0; i < 4; i++) { - planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspx, aspy); + planeMarkerToTransDataInit(td++, td2d++, tdt++, plane_track, plane_marker->corners[i], aspect); } } @@ -6406,7 +6746,6 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) MovieTrackingPlaneTrack *plane_track; TransDataTracking *tdt; int framenr = ED_space_clip_get_clip_frame_number(sc); - float aspx, aspy; /* count */ t->total = 0; @@ -6441,8 +6780,6 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) if (t->total == 0) return; - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); tdt = t->customData = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); @@ -6453,7 +6790,7 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) track = tracksbase->first; while (track) { if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - trackToTransData(framenr, td, td2d, tdt, track, aspx, aspy); + trackToTransData(framenr, td, td2d, tdt, track, t->aspect); /* offset */ td++; @@ -6487,7 +6824,7 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) plane_track = plane_track->next) { if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - planeTrackToTransData(framenr, td, td2d, tdt, plane_track, aspx, aspy); + planeTrackToTransData(framenr, td, td2d, tdt, plane_track, t->aspect); td += 4; td2d += 4; tdt += 4; @@ -6699,14 +7036,10 @@ static void cancelTransTracking(TransInfo *t) void flushTransTracking(TransInfo *t) { - SpaceClip *sc = t->sa->spacedata.first; TransData *td; TransData2D *td2d; TransDataTracking *tdt; int a; - float aspx, aspy; - - ED_space_clip_get_aspect_dimension_aware(sc, &aspx, &aspy); if (t->state == TRANS_CANCEL) cancelTransTracking(t); @@ -6720,8 +7053,8 @@ void flushTransTracking(TransInfo *t) continue; } - loc2d[0] = td2d->loc[0] / aspx; - loc2d[1] = td2d->loc[1] / aspy; + loc2d[0] = td2d->loc[0] / t->aspect[0]; + loc2d[1] = td2d->loc[1] / t->aspect[1]; if (t->flag & T_ALT_TRANSFORM) { if (t->mode == TFM_RESIZE) { @@ -6763,8 +7096,8 @@ void flushTransTracking(TransInfo *t) td2d->loc2d[tdt->coord] = tdt->prev_pos[tdt->coord] + td2d->loc[1] * tdt->scale; } else if (tdt->mode == transDataTracking_ModePlaneTracks) { - td2d->loc2d[0] = td2d->loc[0] / aspx; - td2d->loc2d[1] = td2d->loc[1] / aspy; + td2d->loc2d[0] = td2d->loc[0] / t->aspect[0]; + td2d->loc2d[1] = td2d->loc[1] / t->aspect[1]; } } } @@ -6842,9 +7175,10 @@ static void MaskHandleToTransData(MaskSplinePoint *point, eMaskWhichHandle which } } -static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, - TransData *td, TransData2D *td2d, TransDataMasking *tdm, - const int propmode, const float asp[2]) +static void MaskPointToTransData( + Scene *scene, MaskSplinePoint *point, + TransData *td, TransData2D *td2d, TransDataMasking *tdm, + const bool is_prop_edit, const float asp[2]) { BezTriple *bezt = &point->bezt; const bool is_sel_point = MASKPOINT_ISSEL_KNOT(point); @@ -6854,7 +7188,7 @@ static void MaskPointToTransData(Scene *scene, MaskSplinePoint *point, BKE_mask_point_parent_matrix_get(point, CFRA, parent_matrix); invert_m3_m3(parent_inverse_matrix, parent_matrix); - if (propmode || is_sel_point) { + if (is_prop_edit || is_sel_point) { int i; tdm->point = point; @@ -6974,7 +7308,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) TransData2D *td2d = NULL; TransDataMasking *tdm = NULL; int count = 0, countsel = 0; - int propmode = t->flag & T_PROP_EDIT; + const bool is_prop_edit = (t->flag & T_PROP_EDIT); float asp[2]; t->total = 0; @@ -7024,7 +7358,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) } } - if (propmode) + if (is_prop_edit) count += 3; } } @@ -7037,7 +7371,7 @@ static void createTransMaskingData(bContext *C, TransInfo *t) ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); - t->total = (propmode) ? count : countsel; + t->total = (is_prop_edit) ? count : countsel; td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mask Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ @@ -7060,10 +7394,10 @@ static void createTransMaskingData(bContext *C, TransInfo *t) for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; - if (propmode || MASKPOINT_ISSEL_ANY(point)) { - MaskPointToTransData(scene, point, td, td2d, tdm, propmode, asp); + if (is_prop_edit || MASKPOINT_ISSEL_ANY(point)) { + MaskPointToTransData(scene, point, td, td2d, tdm, is_prop_edit, asp); - if (propmode || MASKPOINT_ISSEL_KNOT(point)) { + if (is_prop_edit || MASKPOINT_ISSEL_KNOT(point)) { td += 3; td2d += 3; tdm += 3; @@ -7306,8 +7640,8 @@ static void createTransGPencil(bContext *C, TransInfo *t) const Scene *scene = CTX_data_scene(C); const int cfra = CFRA; - const int propedit = (t->flag & T_PROP_EDIT); - const int propedit_connected = (t->flag & T_PROP_CONNECTED); + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; /* == Grease Pencil Strokes to Transform Data == @@ -7338,9 +7672,9 @@ static void createTransGPencil(bContext *C, TransInfo *t) continue; } - if (propedit) { + if (is_prop_edit) { /* Proportional Editing... */ - if (propedit_connected) { + if (is_prop_edit_connected) { /* connected only - so only if selected */ if (gps->flag & GP_STROKE_SELECT) t->total += gps->totpoints; @@ -7447,8 +7781,8 @@ static void createTransGPencil(bContext *C, TransInfo *t) } /* What we need to include depends on proportional editing settings... */ - if (propedit) { - if (propedit_connected) { + if (is_prop_edit) { + if (is_prop_edit_connected) { /* A) "Connected" - Only those in selected strokes */ stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; } @@ -7467,7 +7801,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) bGPDspoint *pt; int i; -#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or propedit breaks */ +#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or is_prop_edit breaks */ const float ninv = 1.0f / gps->totpoints; float center[3] = {0.0f}; @@ -7482,7 +7816,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) bool point_ok; /* include point? */ - if (propedit) { + if (is_prop_edit) { /* Always all points in strokes that get included */ point_ok = true; } @@ -7525,7 +7859,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) } /* March over these points, and calculate the proportional editing distances */ - if (propedit && (head != tail)) { + if (is_prop_edit && (head != tail)) { /* XXX: for now, we are similar enough that this works... */ calc_distanceCurveVerts(head, tail - 1); } @@ -7594,6 +7928,12 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_ACTION) { t->flag |= T_POINTS | T_2D_EDIT; createTransActionData(C, t); + + if (t->data && (t->flag & T_PROP_EDIT)) { + sort_trans_data(t); // makes selected become first in array + //set_prop_dist(t, false); /* don't do that, distance has been set in createTransActionData already */ + sort_trans_data_dist(t); + } } else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS | T_2D_EDIT; @@ -7607,13 +7947,12 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IPO) { t->flag |= T_POINTS | T_2D_EDIT; createTransGraphEditData(C, t); -#if 0 + if (t->data && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array - set_prop_dist(t, 1); + set_prop_dist(t, false); /* don't do that, distance has been set in createTransGraphEditData already */ sort_trans_data_dist(t); } -#endif } else if (t->spacetype == SPACE_NODE) { t->flag |= T_POINTS | T_2D_EDIT; diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 5f02fa1b05f..90c806a2d05 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -80,6 +80,7 @@ #include "BKE_editmesh.h" #include "BKE_tracking.h" #include "BKE_mask.h" +#include "BKE_utildefines.h" #include "ED_anim_api.h" #include "ED_armature.h" @@ -808,10 +809,14 @@ static void recalcData_objects(TransInfo *t) ebo->rad_head *= ebo->length / ebo->oldlength; ebo->rad_tail *= ebo->length / ebo->oldlength; ebo->oldlength = ebo->length; + + if (ebo_parent) { + ebo_parent->rad_tail = ebo->rad_head; + } } } - if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONESIZE)) { + if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) { /* fix roll */ for (i = 0; i < t->total; i++, td++) { if (td->extra) { @@ -1065,6 +1070,8 @@ static int initTransInfo_edit_pet_to_flag(const int proportional) * Setup internal data, mouse, vectors * * \note \a op and \a event can be NULL + * + * \see #saveTransform does the reverse. */ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event) { @@ -1121,6 +1128,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve zero_v3(t->vec); zero_v3(t->center); + zero_v3(t->center_global); unit_m3(t->mat); @@ -1156,6 +1164,19 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->spacetype = sa->spacetype; } + /* handle T_ALT_TRANSFORM initialization, we may use for different operators */ + if (op) { + const char *prop_id = NULL; + if (t->mode == TFM_SHRINKFATTEN) { + prop_id = "use_even_offset"; + } + + if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id)) && + RNA_property_is_set(op->ptr, prop)) + { + BKE_BIT_TEST_SET(t->flag, RNA_property_boolean_get(op->ptr, prop), T_ALT_TRANSFORM); + } + } if (t->spacetype == SPACE_VIEW3D) { View3D *v3d = sa->spacedata.first; @@ -1316,7 +1337,13 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve /* use settings from scene only if modal */ if (t->flag & T_MODAL) { if ((t->options & CTX_NO_PET) == 0) { - if (t->obedit) { + if (t->spacetype == SPACE_IPO) { + t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_fcurve); + } + else if (t->spacetype == SPACE_ACTION) { + t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_action); + } + else if (t->obedit) { t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional); } else if (t->options & CTX_GPENCIL_STROKES) { @@ -1375,6 +1402,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve #endif setTransformViewMatrices(t); + setTransformViewAspect(t, t->aspect); initNumInput(&t->num); } @@ -1541,6 +1569,19 @@ void calculateCenter2D(TransInfo *t) } } +void calculateCenterGlobal(TransInfo *t) +{ + /* setting constraint center */ + /* note, init functions may over-ride t->center */ + if (t->flag & (T_EDIT | T_POSE)) { + Object *ob = t->obedit ? t->obedit : t->poseobj; + mul_v3_m4v3(t->center_global, ob->obmat, t->center); + } + else { + copy_v3_v3(t->center_global, t->center); + } +} + void calculateCenterCursor(TransInfo *t, float r_center[3]) { const float *cursor; @@ -1569,30 +1610,17 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) void calculateCenterCursor2D(TransInfo *t, float r_center[2]) { - float aspx = 1.0, aspy = 1.0; const float *cursor = NULL; if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; - if (t->options & CTX_MASK) { - ED_space_image_get_aspect(sima, &aspx, &aspy); - } - else { - ED_space_image_get_uv_aspect(sima, &aspx, &aspy); - } cursor = sima->cursor; } else if (t->spacetype == SPACE_CLIP) { SpaceClip *space_clip = (SpaceClip *) t->sa->spacedata.first; - if (t->options & CTX_MOVIECLIP) { - ED_space_clip_get_aspect_dimension_aware(space_clip, &aspx, &aspy); - } - else { - ED_space_clip_get_aspect(space_clip, &aspx, &aspy); - } cursor = space_clip->cursor; } - + if (cursor) { if (t->options & CTX_MASK) { float co[2]; @@ -1609,8 +1637,8 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) BLI_assert(!"Shall not happen"); } - r_center[0] = co[0] * aspx; - r_center[1] = co[1] * aspy; + r_center[0] = co[0] * t->aspect[0]; + r_center[1] = co[1] * t->aspect[1]; } else if (t->options & CTX_PAINT_CURVE) { if (t->spacetype == SPACE_IMAGE) { @@ -1619,8 +1647,8 @@ void calculateCenterCursor2D(TransInfo *t, float r_center[2]) } } else { - r_center[0] = cursor[0] * aspx; - r_center[1] = cursor[1] * aspy; + r_center[0] = cursor[0] * t->aspect[0]; + r_center[1] = cursor[1] * t->aspect[1]; } } } @@ -1755,14 +1783,8 @@ void calculateCenter(TransInfo *t) } calculateCenter2D(t); + calculateCenterGlobal(t); - /* setting constraint center */ - copy_v3_v3(t->con.center, t->center); - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->con.center); - } - /* for panning from cameraview */ if (t->flag & T_OBJECT) { if (t->spacetype == SPACE_VIEW3D && t->ar && t->ar->regiontype == RGN_TYPE_WINDOW) { @@ -1783,7 +1805,7 @@ void calculateCenter(TransInfo *t) /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */ if (t->mode == TFM_TRANSLATION) { copy_v3_v3(t->center, axis); - copy_v3_v3(t->con.center, t->center); + copy_v3_v3(t->center_global, t->center); } } } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c index fc8e6cd0286..acc6108f264 100644 --- a/source/blender/editors/transform/transform_manipulator.c +++ b/source/blender/editors/transform/transform_manipulator.c @@ -512,7 +512,7 @@ static int calc_manipulator_stats(const bContext *C) for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) { if (ek->flag & PEK_SELECT) { - calc_tw_center(scene, ek->flag & PEK_USE_WCO ? ek->world_co : ek->co); + calc_tw_center(scene, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co); totsel++; } } @@ -895,6 +895,11 @@ static void postOrtho(const bool ortho) } } +BLI_INLINE bool manipulator_rotate_is_visible(const int drawflags) +{ + return (drawflags & (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)); +} + static void draw_manipulator_rotate( View3D *v3d, RegionView3D *rv3d, const int drawflags, const int combo, const bool is_moving, const bool is_picksel) @@ -908,8 +913,8 @@ static void draw_manipulator_rotate( const int colcode = (is_moving) ? MAN_MOVECOL : MAN_RGB; bool ortho; - /* when called while moving in mixed mode, do not draw when... */ - if ((drawflags & MAN_ROT_C) == 0) return; + /* skip drawing if all axes are locked */ + if (manipulator_rotate_is_visible(drawflags) == false) return; /* Init stuff */ glDisable(GL_DEPTH_TEST); @@ -1051,8 +1056,6 @@ static void draw_manipulator_rotate( glRotatef(90.0, 1.0, 0.0, 0.0); postOrtho(ortho); } - - if (arcs) glDisable(GL_CLIP_PLANE0); } // donut arcs if (arcs) { @@ -1448,8 +1451,8 @@ static void draw_manipulator_rotate_cyl( int axis_order[3] = {2, 0, 1}; int i; - /* when called while moving in mixed mode, do not draw when... */ - if ((drawflags & MAN_ROT_C) == 0) return; + /* skip drawing if all axes are locked */ + if (manipulator_rotate_is_visible(drawflags) == false) return; manipulator_axis_order(rv3d, axis_order); diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index e33bc19fc92..013e47886eb 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -38,6 +38,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_report.h" +#include "BKE_editmesh.h" #include "RNA_access.h" #include "RNA_define.h" @@ -63,23 +64,23 @@ typedef struct TransformModeItem { static const float VecOne[3] = {1, 1, 1}; -static char OP_TRANSLATION[] = "TRANSFORM_OT_translate"; -static char OP_ROTATION[] = "TRANSFORM_OT_rotate"; -static char OP_TOSPHERE[] = "TRANSFORM_OT_tosphere"; -static char OP_RESIZE[] = "TRANSFORM_OT_resize"; -static char OP_SKIN_RESIZE[] = "TRANSFORM_OT_skin_resize"; -static char OP_SHEAR[] = "TRANSFORM_OT_shear"; -static char OP_BEND[] = "TRANSFORM_OT_bend"; -static char OP_SHRINK_FATTEN[] = "TRANSFORM_OT_shrink_fatten"; -static char OP_PUSH_PULL[] = "TRANSFORM_OT_push_pull"; -static char OP_TILT[] = "TRANSFORM_OT_tilt"; -static char OP_TRACKBALL[] = "TRANSFORM_OT_trackball"; -static char OP_MIRROR[] = "TRANSFORM_OT_mirror"; -static char OP_EDGE_SLIDE[] = "TRANSFORM_OT_edge_slide"; -static char OP_VERT_SLIDE[] = "TRANSFORM_OT_vert_slide"; -static char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease"; -static char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight"; -static char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide"; +static const char OP_TRANSLATION[] = "TRANSFORM_OT_translate"; +static const char OP_ROTATION[] = "TRANSFORM_OT_rotate"; +static const char OP_TOSPHERE[] = "TRANSFORM_OT_tosphere"; +static const char OP_RESIZE[] = "TRANSFORM_OT_resize"; +static const char OP_SKIN_RESIZE[] = "TRANSFORM_OT_skin_resize"; +static const char OP_SHEAR[] = "TRANSFORM_OT_shear"; +static const char OP_BEND[] = "TRANSFORM_OT_bend"; +static const char OP_SHRINK_FATTEN[] = "TRANSFORM_OT_shrink_fatten"; +static const char OP_PUSH_PULL[] = "TRANSFORM_OT_push_pull"; +static const char OP_TILT[] = "TRANSFORM_OT_tilt"; +static const char OP_TRACKBALL[] = "TRANSFORM_OT_trackball"; +static const char OP_MIRROR[] = "TRANSFORM_OT_mirror"; +static const char OP_EDGE_SLIDE[] = "TRANSFORM_OT_edge_slide"; +static const char OP_VERT_SLIDE[] = "TRANSFORM_OT_vert_slide"; +static const char OP_EDGE_CREASE[] = "TRANSFORM_OT_edge_crease"; +static const char OP_EDGE_BWEIGHT[] = "TRANSFORM_OT_edge_bevelweight"; +static const char OP_SEQ_SLIDE[] = "TRANSFORM_OT_seq_slide"; static void TRANSFORM_OT_translate(struct wmOperatorType *ot); static void TRANSFORM_OT_rotate(struct wmOperatorType *ot); @@ -140,6 +141,7 @@ EnumPropertyItem transform_mode_types[] = {TFM_MIRROR, "MIRROR", 0, "Mirror", ""}, {TFM_BONESIZE, "BONE_SIZE", 0, "Bonesize", ""}, {TFM_BONE_ENVELOPE, "BONE_ENVELOPE", 0, "Bone_Envelope", ""}, + {TFM_BONE_ENVELOPE_DIST, "BONE_ENVELOPE_DIST", 0, "Bone_Envelope_Distance", ""}, {TFM_CURVE_SHRINKFATTEN, "CURVE_SHRINKFATTEN", 0, "Curve_Shrinkfatten", ""}, {TFM_MASK_SHRINKFATTEN, "MASK_SHRINKFATTEN", 0, "Mask_Shrinkfatten", ""}, {TFM_GPENCIL_SHRINKFATTEN, "GPENCIL_SHRINKFATTEN", 0, "GPencil_Shrinkfatten", ""}, @@ -488,7 +490,7 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* add temp handler */ WM_event_add_modal_handler(C, op); - op->flag |= OP_GRAB_POINTER; // XXX maybe we want this with the manipulator only? + op->flag |= OP_IS_MODAL_GRAB_CURSOR; // XXX maybe we want this with the manipulator only? return OPERATOR_RUNNING_MODAL; } } @@ -790,6 +792,8 @@ static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot) RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); + RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness"); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP); } @@ -834,6 +838,8 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot) static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Edge Slide"; ot->description = "Slide an edge loop along a mesh"; @@ -845,9 +851,12 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) ot->exec = transform_exec; ot->modal = transform_modal; ot->cancel = transform_cancel; - ot->poll = ED_operator_editmesh; + ot->poll = ED_operator_editmesh_region_view3d; - RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); + RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); + + prop = RNA_def_boolean(ot->srna, "single_side", false, "Single Side", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); Transform_Properties(ot, P_MIRROR | P_SNAP | P_CORRECT_UV); } @@ -865,7 +874,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) ot->exec = transform_exec; ot->modal = transform_modal; ot->cancel = transform_cancel; - ot->poll = ED_operator_editmesh; + ot->poll = ED_operator_editmesh_region_view3d; RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); @@ -1118,7 +1127,7 @@ void transform_keymap_for_space(wmKeyConfig *keyconf, wmKeyMap *keymap, int spac kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", TABKEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_snap"); kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", TABKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); - RNA_string_set(kmi->ptr, "data_path", "tool_settings.snap_element"); + RNA_string_set(kmi->ptr, "data_path", "tool_settings.snap_node_element"); break; case SPACE_SEQ: WM_keymap_add_item(keymap, OP_SEQ_SLIDE, GKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 4a2927ace00..1b0256bdeed 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -454,7 +454,7 @@ void applySnapping(TransInfo *t, float *vec) t->tsnap.applySnap(t, vec); } - else if ((t->tsnap.mode != SCE_SNAP_MODE_INCREMENT) && activeSnap(t)) { + else if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) && activeSnap(t)) { double current = PIL_check_seconds_timer(); // Time base quirky code to go around findnearest slowness @@ -577,6 +577,10 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; } } + else if (t->spacetype == SPACE_SEQ) { + /* We do our own snapping currently, so nothing here */ + t->tsnap.mode = SCE_SNAP_MODE_GRID; /* Dummy, should we rather add a NOP mode? */ + } else { /* Always grid outside of 3D view */ t->tsnap.mode = SCE_SNAP_MODE_INCREMENT; @@ -837,16 +841,10 @@ static float TranslationBetween(TransInfo *UNUSED(t), const float p1[3], const f static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) { - float angle, start[3], end[3], center[3]; - - copy_v3_v3(center, t->center); - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, center); - } + float angle, start[3], end[3]; - sub_v3_v3v3(start, p1, center); - sub_v3_v3v3(end, p2, center); + sub_v3_v3v3(start, p1, t->center_global); + sub_v3_v3v3(end, p2, t->center_global); // Angle around a constraint axis (error prone, will need debug) if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { @@ -893,16 +891,10 @@ static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) static float ResizeBetween(TransInfo *t, const float p1[3], const float p2[3]) { - float d1[3], d2[3], center[3], len_d1; - - copy_v3_v3(center, t->center); - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, center); - } + float d1[3], d2[3], len_d1; - sub_v3_v3v3(d1, p1, center); - sub_v3_v3v3(d2, p2, center); + sub_v3_v3v3(d1, p1, t->center_global); + sub_v3_v3v3(d2, p2, t->center_global); if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { mul_m3_v3(t->con.pmtx, d1); @@ -949,9 +941,9 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) // last_p = LAST_SNAP_POINT; // } // else -// { + { last_p = t->tsnap.snapPoint; -// } + } for (p1 = depth_peels.first; p1; p1 = p1->next) { @@ -1041,14 +1033,13 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type == OB_MESH) { /* same as above but for UV's */ Image *ima = ED_space_image(t->sa->spacedata.first); - float aspx, aspy, co[2]; + float co[2]; UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); if (ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint)) { - ED_space_image_get_uv_aspect(t->sa->spacedata.first, &aspx, &aspy); - t->tsnap.snapPoint[0] *= aspx; - t->tsnap.snapPoint[1] *= aspy; + t->tsnap.snapPoint[0] *= t->aspect[0]; + t->tsnap.snapPoint[1] *= t->aspect[1]; t->tsnap.status |= POINT_INIT; } @@ -1109,13 +1100,7 @@ static void TargetSnapCenter(TransInfo *t) { /* Only need to calculate once */ if ((t->tsnap.status & TARGET_INIT) == 0) { - copy_v3_v3(t->tsnap.snapTarget, t->center); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->tsnap.snapTarget); - } - + copy_v3_v3(t->tsnap.snapTarget, t->center_global); TargetSnapOffset(t, NULL); t->tsnap.status |= TARGET_INIT; @@ -1396,11 +1381,8 @@ static bool snapArmature(short snap_mode, ARegion *ar, Object *ob, bArmature *ar invert_m4_m4(imat, obmat); - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); if (arm->edbo) { EditBone *eBone; @@ -1758,11 +1740,8 @@ static bool snapEmpty(short snap_mode, ARegion *ar, Object *ob, float obmat[4][4 invert_m4_m4(imat, obmat); - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); switch (snap_mode) { case SCE_SNAP_MODE_VERTEX: @@ -2107,12 +2086,8 @@ static bool peelDerivedMesh(Object *ob, DerivedMesh *dm, float obmat[4][4], transpose_m3_m4(timat, imat); - copy_v3_v3(ray_start_local, ray_start); - copy_v3_v3(ray_normal_local, ray_normal); - - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); - + mul_v3_m4v3(ray_start_local, imat, ray_start); + mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal); /* If number of vert is more than an arbitrary limit, * test against boundbox first @@ -2418,8 +2393,8 @@ void snapGridIncrement(TransInfo *t, float *val) { GearsType action; - // Only do something if using Snap to Grid - if (t->tsnap.mode != SCE_SNAP_MODE_INCREMENT) + /* only do something if using absolute or incremental grid snapping */ + if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID)) return; action = activeSnap(t) ? BIG_GEARS : NO_GEARS; @@ -2431,7 +2406,7 @@ void snapGridIncrement(TransInfo *t, float *val) snapGridIncrementAction(t, val, action); } -int snapSequenceBounds(TransInfo *t, const int mval[2]) +void snapSequenceBounds(TransInfo *t, const int mval[2]) { float xmouse, ymouse; int frame; @@ -2439,7 +2414,7 @@ int snapSequenceBounds(TransInfo *t, const int mval[2]) TransSeq *ts = t->customData; /* reuse increment, strictly speaking could be another snap mode, but leave as is */ if (!(t->modifiers & MOD_SNAP_INVERT)) - return 0; + return; /* convert to frame range */ UI_view2d_region_to_view(&t->ar->v2d, mval[0], mval[1], &xmouse, &ymouse); @@ -2450,50 +2425,58 @@ int snapSequenceBounds(TransInfo *t, const int mval[2]) if (!ts->snap_left) frame = frame - (ts->max - ts->min); - return frame; + t->values[0] = frame - ts->min; } static void applyGridIncrement(TransInfo *t, float *val, int max_index, const float fac[3], GearsType action) { + float asp_local[3] = {1, 1, 1}; + const bool use_aspect = ELEM(t->mode, TFM_TRANSLATION); + const float *asp = use_aspect ? t->aspect : asp_local; int i; - float asp[3] = {1.0f, 1.0f, 1.0f}; // TODO: Remove hard coded limit here (3) - if (max_index > 2) { - printf("applyGridIncrement: invalid index %d, clamping\n", max_index); - max_index = 2; - } + BLI_assert(ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID)); + BLI_assert(max_index <= 2); - // Early bailing out if no need to snap - if (fac[action] == 0.0f) + /* Early bailing out if no need to snap */ + if (fac[action] == 0.0f) { return; - - /* evil hack - snapping needs to be adapted for image aspect ratio */ - if ((t->spacetype == SPACE_IMAGE) && (t->mode == TFM_TRANSLATION)) { - 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); - } } - else if ((t->spacetype == SPACE_IPO) && (t->mode == TFM_TRANSLATION)) { - View2D *v2d = &t->ar->v2d; - View2DGrid *grid; - SpaceIpo *sipo = t->sa->spacedata.first; - int unity = V2D_UNIT_VALUES; - int unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE; - /* grid */ - grid = UI_view2d_grid_calc(t->scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, t->ar->winx, t->ar->winy); + if (use_aspect) { + /* custom aspect for fcurve */ + if (t->spacetype == SPACE_IPO) { + View2D *v2d = &t->ar->v2d; + View2DGrid *grid; + SpaceIpo *sipo = t->sa->spacedata.first; + int unity = V2D_UNIT_VALUES; + int unitx = (sipo->flag & SIPO_DRAWTIME) ? V2D_UNIT_SECONDS : V2D_UNIT_FRAMESCALE; + + /* grid */ + grid = UI_view2d_grid_calc(t->scene, v2d, unitx, V2D_GRID_NOCLAMP, unity, V2D_GRID_NOCLAMP, t->ar->winx, t->ar->winy); + + UI_view2d_grid_size(grid, &asp_local[0], &asp_local[1]); + UI_view2d_grid_free(grid); - UI_view2d_grid_size(grid, &asp[0], &asp[1]); - UI_view2d_grid_free(grid); + asp = asp_local; + } } - for (i = 0; i <= max_index; i++) { - val[i] = fac[action] * asp[i] * floorf(val[i] / (fac[action] * asp[i]) + 0.5f); + /* absolute snapping on grid based on global center */ + if ((t->tsnap.mode == SCE_SNAP_MODE_GRID) && (t->mode == TFM_TRANSLATION)) { + for (i = 0; i <= max_index; i++) { + /* do not let unconstrained axis jump to absolute grid increments */ + if (!(t->con.mode & CON_APPLY) || t->con.mode & (CON_AXIS0 << i)) { + const float iter_fac = fac[action] * asp[i]; + val[i] = iter_fac * roundf((val[i] + t->center_global[i]) / iter_fac) - t->center_global[i]; + } + } + } + else { + /* relative snapping in fixed increments */ + for (i = 0; i <= max_index; i++) { + const float iter_fac = fac[action] * asp[i]; + val[i] = iter_fac * roundf(val[i] / iter_fac); + } } } diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index f4189a18da4..47894e1bc1f 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -24,6 +24,7 @@ set(INC ../../blenkernel ../../blenlib ../../bmesh + ../../imbuf ../../gpu ../../makesdna ../../makesrna @@ -69,6 +70,7 @@ set(SRC ../include/ED_node.h ../include/ED_numinput.h ../include/ED_object.h + ../include/ED_outliner.h ../include/ED_paint.h ../include/ED_particle.h ../include/ED_physics.h diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 438eb1f3864..433c7603f35 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -38,6 +38,7 @@ #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_packedFile_types.h" @@ -58,9 +59,12 @@ #include "BKE_paint.h" #include "ED_armature.h" +#include "ED_buttons.h" #include "ED_image.h" #include "ED_mesh.h" +#include "ED_node.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_paint.h" #include "ED_space_api.h" #include "ED_util.h" @@ -318,3 +322,25 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *ar, void *arg_info glEnd(); setlinestyle(0); } + +/** + * Use to free ID references within runtime data (stored outside of DNA) + * + * \note Typically notifiers take care of this, + * but there are times we have to free references immediately, see: T44376 + */ +void ED_spacedata_id_unref(struct SpaceLink *sl, const ID *id) +{ + + switch (sl->spacetype) { + case SPACE_OUTLINER: + ED_outliner_id_unref((SpaceOops *)sl, id); + break; + case SPACE_BUTS: + ED_buttons_id_unref((SpaceButs *)sl, id); + break; + case SPACE_NODE: + ED_node_id_unref((SpaceNode *)sl, id); + break; + } +} diff --git a/source/blender/editors/util/editmode_undo.c b/source/blender/editors/util/editmode_undo.c index 7f5edb5ea9e..bc7a8374c73 100644 --- a/source/blender/editors/util/editmode_undo.c +++ b/source/blender/editors/util/editmode_undo.c @@ -315,7 +315,7 @@ void undo_editmode_name(bContext *C, const char *undoname) } /* undoname optionally, if NULL it just checks for existing undo steps */ -int undo_editmode_valid(const char *undoname) +bool undo_editmode_is_valid(const char *undoname) { if (undoname) { UndoElem *uel; @@ -332,19 +332,20 @@ int undo_editmode_valid(const char *undoname) /* get name of undo item, return null if no item with this index */ /* if active pointer, set it to 1 if true */ -const char *undo_editmode_get_name(bContext *C, int nr, int *active) +const char *undo_editmode_get_name(bContext *C, int nr, bool *r_active) { UndoElem *uel; /* prevent wrong numbers to be returned */ undo_clean_stack(C); - if (active) *active = 0; + if (r_active) *r_active = false; uel = BLI_findlink(&undobase, nr); if (uel) { - if (active && uel == curundo) - *active = 1; + if (r_active && (uel == curundo)) { + *r_active = true; + } return uel->name; } return NULL; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index 050631362b4..effb45a61ab 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -73,14 +73,14 @@ void initNumInput(NumInput *n) { n->idx_max = 0; n->unit_sys = USER_UNIT_NONE; - fill_vn_i(n->unit_type, NUM_MAX_ELEMENTS, B_UNIT_NONE); + copy_vn_i(n->unit_type, NUM_MAX_ELEMENTS, B_UNIT_NONE); n->unit_use_radians = false; n->flag = 0; - fill_vn_short(n->val_flag, NUM_MAX_ELEMENTS, 0); + copy_vn_short(n->val_flag, NUM_MAX_ELEMENTS, 0); zero_v3(n->val); - fill_vn_fl(n->val_org, NUM_MAX_ELEMENTS, 0.0f); - fill_vn_fl(n->val_inc, NUM_MAX_ELEMENTS, 1.0f); + copy_vn_fl(n->val_org, NUM_MAX_ELEMENTS, 0.0f); + copy_vn_fl(n->val_inc, NUM_MAX_ELEMENTS, 1.0f); n->idx = 0; n->str[0] = '\0'; diff --git a/source/blender/editors/util/undo.c b/source/blender/editors/util/undo.c index ab882a388ad..ee6101d2952 100644 --- a/source/blender/editors/util/undo.c +++ b/source/blender/editors/util/undo.c @@ -108,7 +108,7 @@ void ED_undo_push(bContext *C, const char *str) /* do nothing for now */ } else { - BKE_write_undo(C, str); + BKE_undo_write(C, str); } if (wm->file_saved) { @@ -244,7 +244,7 @@ void ED_undo_pop_op(bContext *C, wmOperator *op) } /* name optionally, function used to check for operator redo panel */ -int ED_undo_valid(const bContext *C, const char *undoname) +bool ED_undo_is_valid(const bContext *C, const char *undoname) { Object *obedit = CTX_data_edit_object(C); Object *obact = CTX_data_active_object(C); @@ -263,7 +263,7 @@ int ED_undo_valid(const bContext *C, const char *undoname) } else if (obedit) { if (OB_TYPE_SUPPORT_EDITMODE(obedit->type)) { - return undo_editmode_valid(undoname); + return undo_editmode_is_valid(undoname); } } else { @@ -271,19 +271,19 @@ int ED_undo_valid(const bContext *C, const char *undoname) /* if below tests fail, global undo gets executed */ if (obact && obact->mode & OB_MODE_TEXTURE_PAINT) { - if (ED_undo_paint_valid(UNDO_PAINT_IMAGE, undoname)) + if (ED_undo_paint_is_valid(UNDO_PAINT_IMAGE, undoname)) return 1; } else if (obact && obact->mode & OB_MODE_SCULPT) { - if (ED_undo_paint_valid(UNDO_PAINT_MESH, undoname)) + if (ED_undo_paint_is_valid(UNDO_PAINT_MESH, undoname)) return 1; } else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) { - return PE_undo_valid(CTX_data_scene(C)); + return PE_undo_is_valid(CTX_data_scene(C)); } if (U.uiflag & USER_GLOBALUNDO) { - return BKE_undo_valid(undoname); + return BKE_undo_is_valid(undoname); } } return 0; @@ -487,7 +487,8 @@ static int get_undo_system(bContext *C) static EnumPropertyItem *rna_undo_itemf(bContext *C, int undosys, int *totitem) { EnumPropertyItem item_tmp = {0}, *item = NULL; - int active, i = 0; + int i = 0; + bool active; while (true) { const char *name = NULL; diff --git a/source/blender/editors/util/util_intern.h b/source/blender/editors/util/util_intern.h index d366ad7997f..0f650330951 100644 --- a/source/blender/editors/util/util_intern.h +++ b/source/blender/editors/util/util_intern.h @@ -35,12 +35,12 @@ /* internal exports only */ /* editmode_undo.c */ -void undo_editmode_name (struct bContext *C, const char *undoname); -int undo_editmode_valid (const char *undoname); -const char *undo_editmode_get_name (struct bContext *C, int nr, int *active); -void *undo_editmode_get_prev (struct Object *ob); -void undo_editmode_step (struct bContext *C, int step); -void undo_editmode_number (struct bContext *C, int nr); +void undo_editmode_name(struct bContext *C, const char *undoname); +bool undo_editmode_is_valid(const char *undoname); +const char *undo_editmode_get_name(struct bContext *C, int nr, bool *r_active); +void *undo_editmode_get_prev(struct Object *ob); +void undo_editmode_step(struct bContext *C, int step); +void undo_editmode_number(struct bContext *C, int nr); #endif /* __UTIL_INTERN_H__ */ diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index d1fd8d969a4..e028c08091c 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -34,18 +34,13 @@ struct MTexPoly; struct Image; -struct MTFace; struct Object; struct Scene; struct SpaceImage; -struct UvElementMap; struct wmOperatorType; struct BMEditMesh; -struct BMesh; struct BMFace; struct BMLoop; -struct BMEdge; -struct BMVert; /* visibility and selection */ bool uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index c70fcdbbd94..3b40dad3f3b 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -53,6 +53,8 @@ #include "BLI_blenlib.h" #include "BLI_array.h" +#include "BLF_translation.h" + #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_depsgraph.h" @@ -80,6 +82,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "uvedit_intern.h" @@ -1028,7 +1032,7 @@ static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestH /* setup */ BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, 0, limit); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); @@ -1114,7 +1118,7 @@ static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestH /*********************** linked select ***********************/ -static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend) +static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend, bool select_faces) { BMFace *efa; BMLoop *l; @@ -1131,7 +1135,9 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ - vmap = BM_uv_vert_map_create(em->bm, 1, limit); + + /* use winding so we don't consider overlapping islands as connected, see T44320 */ + vmap = BM_uv_vert_map_create(em->bm, limit, !select_faces, true); if (vmap == NULL) return; @@ -1144,15 +1150,24 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); if (uvedit_face_visible_test(scene, ima, efa, tf)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { + if (select_faces) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { stack[stacksize] = a; stacksize++; flag[a] = 1; + } + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - break; + if (luv->flag & MLOOPUV_VERTSEL) { + stack[stacksize] = a; + stacksize++; + flag[a] = 1; + + break; + } } } } @@ -1204,13 +1219,21 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo if (!extend) { BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - + if (select_faces) { if (flag[a]) - luv->flag |= MLOOPUV_VERTSEL; + BM_face_select_set(em->bm, efa, true); else - luv->flag &= ~MLOOPUV_VERTSEL; + BM_face_select_set(em->bm, efa, false); + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (flag[a]) + luv->flag |= MLOOPUV_VERTSEL; + else + luv->flag &= ~MLOOPUV_VERTSEL; + } } } } @@ -1219,17 +1242,23 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo if (!flag[a]) { continue; } - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { + + if (select_faces) { + if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) break; - } } - - if (l) { - break; + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if (luv->flag & MLOOPUV_VERTSEL) { + break; + } + } + + if (l) { + break; + } } } @@ -1239,10 +1268,15 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo continue; } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - luv->flag &= ~MLOOPUV_VERTSEL; + if (select_faces) { + BM_face_select_set(em->bm, efa, false); + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + luv->flag &= ~MLOOPUV_VERTSEL; + } } } } @@ -1252,10 +1286,15 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo continue; } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - luv->flag |= MLOOPUV_VERTSEL; + if (select_faces) { + BM_face_select_set(em->bm, efa, true); + } + else { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + luv->flag |= MLOOPUV_VERTSEL; + } } } } @@ -2087,7 +2126,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo /* mark 1 vertex as being hit */ hitv = BLI_array_alloca(hitv, hit.efa->len); hituv = BLI_array_alloca(hituv, hit.efa->len); - fill_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); + copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); hitv[hit.lindex] = BM_elem_index_get(hit.l->v); hituv[hit.lindex] = hit.luv->uv; @@ -2104,7 +2143,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo /* mark 2 edge vertices as being hit */ hitv = BLI_array_alloca(hitv, hit.efa->len); hituv = BLI_array_alloca(hituv, hit.efa->len); - fill_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); + copy_vn_i(hitv, hit.efa->len, 0xFFFFFFFF); hitv[hit.lindex] = BM_elem_index_get(hit.l->v); hitv[(hit.lindex + 1) % hit.efa->len] = BM_elem_index_get(hit.l->next->v); @@ -2154,7 +2193,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo flush = uv_select_edgeloop(scene, ima, em, &hit, limit, extend); } else if (selectmode == UV_SELECT_ISLAND) { - uv_select_linked(scene, ima, em, limit, &hit, extend); + uv_select_linked(scene, ima, em, limit, &hit, extend, false); } else if (extend) { if (selectmode == UV_SELECT_VERTEX) { @@ -2375,11 +2414,12 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent BMEditMesh *em = BKE_editmesh_from_object(obedit); float limit[2]; int extend; + bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE); NearestHit hit, *hit_p = NULL; - if (ts->uv_flag & UV_SYNC_SELECTION) { - BKE_report(op->reports, RPT_ERROR, "Cannot select linked when sync selection is enabled"); + if ((ts->uv_flag & UV_SYNC_SELECTION) && !(ts->selectmode & SCE_SELECT_FACE)) { + BKE_report(op->reports, RPT_ERROR, "Select linked only works in face select mode when sync selection is enabled"); return OPERATOR_CANCELLED; } @@ -2405,7 +2445,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent hit_p = &hit; } - uv_select_linked(scene, ima, em, limit, hit_p, extend); + uv_select_linked(scene, ima, em, limit, hit_p, extend, select_faces); DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); @@ -2680,7 +2720,7 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object uvedit_pixel_to_float(sima, limit, 0.05); BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, 0, limit); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); if (vmap == NULL) { return; } @@ -2771,7 +2811,7 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object uvedit_pixel_to_float(sima, limit, 0.05); BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, 0, limit); + vmap = BM_uv_vert_map_create(em->bm, limit, false, false); if (vmap == NULL) { return; } @@ -3064,9 +3104,10 @@ static bool do_lasso_select_mesh_uv(bContext *C, const int mcords[][2], short mo Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int use_face_center = (ts->uv_flag & UV_SYNC_SELECTION) ? - (ts->selectmode == SCE_SELECT_FACE) : - (ts->uv_selectmode == UV_SELECT_FACE); + const bool use_face_center = ( + (ts->uv_flag & UV_SYNC_SELECTION) ? + (ts->selectmode == SCE_SELECT_FACE) : + (ts->uv_selectmode == UV_SELECT_FACE)); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); @@ -4002,7 +4043,7 @@ static int uv_seams_from_islands_exec(bContext *C, wmOperator *op) /* This code sets editvert->tmp.l to the index. This will be useful later on. */ BM_mesh_elem_table_ensure(bm, BM_FACE); - vmap = BM_uv_vert_map_create(bm, 0, limit); + vmap = BM_uv_vert_map_create(bm, limit, false, false); BM_ITER_MESH (editedge, &iter, bm, BM_EDGES_OF_MESH) { /* flags to determine if we uv is separated from first editface match */ @@ -4107,7 +4148,7 @@ static void UV_OT_seams_from_islands(wmOperatorType *ot) RNA_def_boolean(ot->srna, "mark_sharp", 0, "Mark Sharp", "Mark boundary edges as sharp"); } -static int uv_mark_seam_exec(bContext *C, wmOperator *UNUSED(op)) +static int uv_mark_seam_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); @@ -4117,13 +4158,17 @@ static int uv_mark_seam_exec(bContext *C, wmOperator *UNUSED(op)) BMFace *efa; BMLoop *loop; BMIter iter, liter; + bool clear = RNA_boolean_get(op->ptr, "clear"); const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { BM_ITER_ELEM (loop, &liter, efa, BM_LOOPS_OF_FACE) { if (uvedit_edge_select_test(scene, loop, cd_loop_uv_offset)) { - BM_elem_flag_enable(loop->e, BM_ELEM_SEAM); + if (clear) + BM_elem_flag_disable(loop->e, BM_ELEM_SEAM); + else + BM_elem_flag_enable(loop->e, BM_ELEM_SEAM); } } } @@ -4139,10 +4184,27 @@ static int uv_mark_seam_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } +static int uv_mark_seam_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + uiPopupMenu *pup; + uiLayout *layout; + + pup = UI_popup_menu_begin(C, IFACE_("Edges"), ICON_NONE); + layout = UI_popup_menu_layout(pup); + + uiLayoutSetOperatorContext(layout, WM_OP_EXEC_DEFAULT); + uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Mark Seam"), ICON_NONE, op->type->idname, "clear", false); + uiItemBooleanO(layout, CTX_IFACE_(BLF_I18NCONTEXT_OPERATOR_DEFAULT, "Clear Seam"), ICON_NONE, op->type->idname, "clear", true); + + UI_popup_menu_end(C, pup); + + return OPERATOR_INTERFACE; +} + static void UV_OT_mark_seam(wmOperatorType *ot) { /* identifiers */ - ot->name = "Mark Seams"; + ot->name = "Mark Seam"; ot->description = "Mark selected UV edges as seams"; ot->idname = "UV_OT_mark_seam"; @@ -4151,7 +4213,10 @@ static void UV_OT_mark_seam(wmOperatorType *ot) /* api callbacks */ ot->exec = uv_mark_seam_exec; + ot->invoke = uv_mark_seam_invoke; ot->poll = ED_operator_uvedit; + + RNA_def_boolean(ot->srna, "clear", false, "Clear Seams", "Clear instead of marking seams"); } diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index f7a8735ccca..828537fd585 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -1649,10 +1649,10 @@ static int stitch_init(bContext *C, wmOperator *op) /* in uv synch selection, all uv's are visible */ if (ts->uv_flag & UV_SYNC_SELECTION) { - state->element_map = BM_uv_element_map_create(state->em->bm, false, true); + state->element_map = BM_uv_element_map_create(state->em->bm, false, true, true); } else { - state->element_map = BM_uv_element_map_create(state->em->bm, true, true); + state->element_map = BM_uv_element_map_create(state->em->bm, true, true, true); } if (!state->element_map) { state_delete(state); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 793f84b05ec..f915a4b2e51 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -681,7 +681,7 @@ void UV_OT_minimize_stretch(wmOperatorType *ot) /* identifiers */ ot->name = "Minimize Stretch"; ot->idname = "UV_OT_minimize_stretch"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_POINTER | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; ot->description = "Reduce UV stretching by relaxing angles"; /* api callbacks */ |