diff options
Diffstat (limited to 'source/blender/editors')
96 files changed, 3232 insertions, 950 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index d71cba86f8d..ebd05d8b16b 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -3576,8 +3576,24 @@ 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) { const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale); View2D *v2d = &ac->ar->v2d; @@ -3664,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! */ @@ -4175,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); } } diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 1782152560a..4828343fa0e 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2117,7 +2117,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); } } diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 140b7e0b117..68660740ac7 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); diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index 67ba82f1858..08e0daf8eb9 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -84,7 +84,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; diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 78509dd6079..64ced437142 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>"); diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 177fb6c9118..eedff896315 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -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 552faa4ecfb..3f00068c3d2 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -271,7 +271,7 @@ void ED_keymap_armature(wmKeyConfig *keyconf) 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); - WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "ARMATURE_OT_click_extrude", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "ARMATURE_OT_fill", FKEY, KM_PRESS, 0, 0); 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); @@ -412,5 +412,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 ad9715d2ab2..cee34257b2c 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -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 */ diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index b7d14e089cf..fa774b0744a 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) diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 5f9f24d23a4..09c8b4c370c 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -898,6 +898,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; @@ -1137,6 +1139,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 +1226,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/curve/curve_ops.c b/source/blender/editors/curve/curve_ops.c index 4bcb16d31ef..561c349edec 100644 --- a/source/blender/editors/curve/curve_ops.c +++ b/source/blender/editors/curve/curve_ops.c @@ -230,7 +230,7 @@ void ED_keymap_curve(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "CURVE_OT_handle_type_set", VKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "CURVE_OT_vertex_add", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); kmi = WM_keymap_add_item(keymap, "CURVE_OT_select_all", AKEY, KM_PRESS, 0, 0); RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE); diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index c2072d30310..d4d601b1e8d 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,8 +60,10 @@ #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" /* ************************************************** */ @@ -1042,6 +1047,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) { @@ -1193,6 +1243,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 @@ -1227,10 +1282,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_data.c b/source/blender/editors/gpencil/gpencil_data.c index fc4dd3f410a..fa76029bb2e 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -350,4 +350,98 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) 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_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index cd0c32a35bf..72cd9022d69 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -144,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 5970a9497a0..4752f3dbb36 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -181,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); @@ -260,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..3a564cc6ddb 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1885,8 +1885,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; @@ -1930,7 +1931,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) /* stroke could be smoothed, send notifier to refresh screen */ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } - else { + else if (event->val == KM_RELEASE) { /* printf("\t\tGP - end of stroke + op\n"); */ p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 7fd0d35d514..9dea66adc7c 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -462,7 +462,7 @@ const bAnimChannelType *ANIM_channel_get_typeinfo(bAnimListElem *ale); 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); @@ -676,6 +676,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_screen.h b/source/blender/editors/include/ED_screen.h index b4f7d806079..90b0b3510bc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -112,6 +112,7 @@ void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); 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_view3d.h b/source/blender/editors/include/ED_view3d.h index 235cb2b21eb..b14173131cf 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -329,12 +329,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); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 0681cacf87e..717d5db6b66 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -893,6 +893,9 @@ void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char * 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 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); diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index f5847a9d701..96f9b87611f 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -277,6 +277,10 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) eyedropper_cancel(C, op); return OPERATOR_CANCELLED; case LEFTMOUSE: + /* two release events are sent on KM_CLICK, so ignore second one */ + if (event->click_type == KM_CLICK) { + break; + } if (event->val == KM_RELEASE) { if (eye->accum_tot == 0) { eyedropper_color_sample(C, eye, event->x, event->y); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 754967d9de7..2c00447229c 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1180,7 +1180,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void switch (event->type) { case LEFTMOUSE: { - if (event->val != KM_PRESS) { + if (event->val == KM_RELEASE) { done = true; } break; @@ -2721,8 +2721,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle if (data->searchbox) inbox = ui_searchbox_inside(data->searchbox, event->x, event->y); - /* for double click: we do a press again for when you first click on button (selects all text, no cursor pos) */ - if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) { + if (event->val == KM_PRESS) { float mx, my; mx = event->x; @@ -2747,7 +2746,7 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle } /* only select a word in button if there was no selection before */ - if (event->val == KM_DBL_CLICK && had_selection == false) { + if ((event->click_type == KM_DBL_CLICK) && (had_selection == false)) { ui_textedit_move(but, data, STRCUR_DIR_PREV, false, STRCUR_JUMP_DELIM); ui_textedit_move(but, data, STRCUR_DIR_NEXT, true, STRCUR_JUMP_DELIM); retval = WM_UI_HANDLER_BREAK; @@ -2864,8 +2863,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)) { @@ -2944,7 +2941,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); } @@ -3175,7 +3172,7 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons } } else if (data->state == BUTTON_STATE_WAIT_RELEASE) { - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (!(but->flag & UI_SELECT)) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -3743,7 +3740,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragchange) { #ifdef USE_DRAG_MULTINUM /* if we started multibutton but didnt drag, then edit */ @@ -4040,7 +4037,7 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragchange) { #ifdef USE_DRAG_MULTINUM /* if we started multibutton but didnt drag, then edit */ @@ -4185,7 +4182,7 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } else if (event->type == MOUSEMOVE) { @@ -4233,7 +4230,7 @@ static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButto button_activate_state(C, but, BUTTON_STATE_EXIT); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } else if (event->type == MOUSEMOVE) { @@ -4257,7 +4254,7 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, * editing field for editing list item names */ if ((ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS && event->ctrl) || - (event->type == LEFTMOUSE && event->val == KM_DBL_CLICK)) + (event->type == LEFTMOUSE && event->click_type == KM_DBL_CLICK)) { uiBut *labelbut = ui_but_list_row_text_activate(C, but, data, event, BUTTON_ACTIVATE_TEXT_EDITING); if (labelbut) { @@ -4609,7 +4606,7 @@ static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleBu ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -4930,7 +4927,7 @@ static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleBu ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -5203,7 +5200,7 @@ static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandle } } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5288,7 +5285,7 @@ static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } @@ -5527,7 +5524,7 @@ static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButt ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (data->dragsel != -1) { CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; @@ -5623,7 +5620,7 @@ static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandle ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5696,7 +5693,7 @@ static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleB ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -5721,7 +5718,7 @@ static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, con } else if (data->state == BUTTON_STATE_WAIT_RELEASE) { - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { if (!(but->flag & UI_SELECT)) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); @@ -5804,7 +5801,7 @@ static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHan ui_numedit_apply(C, block, but, data); } } - else if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + else if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { button_activate_state(C, but, BUTTON_STATE_EXIT); } return WM_UI_HANDLER_BREAK; @@ -8345,7 +8342,7 @@ static int ui_handle_menu_event( #ifdef USE_DRAG_POPUP if (menu->is_grab) { - if (event->type == LEFTMOUSE) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { menu->is_grab = false; retval = WM_UI_HANDLER_BREAK; } @@ -8630,7 +8627,7 @@ static int ui_handle_menu_event( uiSafetyRct *saferct = block->saferct.first; if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE) && - ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) + (event->val == KM_PRESS || event->click_type == KM_DBL_CLICK)) { if ((is_parent_menu == false) && (U.uiflag & USER_MENUOPENAUTO) == 0) { /* for root menus, allow clicking to close */ @@ -9007,6 +9004,11 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle switch (event->type) { case MOUSEMOVE: + /* verify we have a real mousemove */ + if (event->x == event->prevx && event->y == event->prevy) { + break; + } + if (!is_click_style) { float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); @@ -9033,7 +9035,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case LEFTMOUSE: if (is_click_style) { - if (block->pie_data.flags & UI_PIE_INVALID_DIR) { + /* the click_type test sends a second event on KM_CLICK that + * may destroy nested pies even before they are drawn */ + if ((block->pie_data.flags & UI_PIE_INVALID_DIR) && (event->click_type != KM_CLICK)) { menu->menuretval = UI_RETURN_CANCEL; } else { @@ -9074,9 +9078,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case YKEY: case ZKEY: { - if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && - !IS_EVENT_MOD(event, shift, ctrl, oskey)) - { + if ((event->val == KM_PRESS) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) { for (but = block->buttons.first; but; but = but->next) { if (but->menu_key == event->type) { ui_but_pie_button_activate(C, but, menu); @@ -9183,8 +9185,9 @@ static int ui_handle_menus_recursive( if (block->flag & UI_BLOCK_RADIAL) retval = ui_pie_handler(C, event, menu); - else if (event->type == LEFTMOUSE || event->val != KM_DBL_CLICK) + else if ((event->type == LEFTMOUSE) || (event->click_type != KM_DBL_CLICK)) { retval = ui_handle_menu_event(C, event, menu, level, is_parent_inside, is_parent_menu, is_floating); + } } } @@ -9220,10 +9223,16 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use retval = ui_handle_list_event(C, event, ar); if (retval == WM_UI_HANDLER_CONTINUE) { - if (but) + if (but) { retval = ui_handle_button_event(C, event, but); - else + } + else { retval = ui_handle_button_over(C, event, ar); + + /* let's make sure we are really not hovering a button by adding a mousemove! + * XXX some WM_event_add_mousemove calls may become unnecessary with this and can be removed */ + WM_event_add_mousemove(C); + } } /* re-enable tooltips */ diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 4baaade33b9..705b55b7ea6 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -617,6 +617,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; diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index d165e2719c5..7d6d5c3a6d7 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1865,7 +1865,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) uiHandlePanelData *data = panel->activedata; /* verify if we can stop */ - if (event->type == LEFTMOUSE && event->val != KM_PRESS) { + if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); int align = panel_aligned(sa, ar); diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c index 7cb9dca78bf..0fbd0c40510 100644 --- a/source/blender/editors/interface/interface_regions.c +++ b/source/blender/editors/interface/interface_regions.c @@ -2749,8 +2749,8 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co pie->block_radial->puphash = ui_popup_menu_hash(title); pie->block_radial->flag |= UI_BLOCK_RADIAL; - /* if pie is spawned by a left click, it is always assumed to be click style */ - if (event->type == LEFTMOUSE) { + /* if pie is spawned by a left click or on release, it is always assumed to be click style */ + if ((event->type == LEFTMOUSE) || (event->val == KM_RELEASE)) { pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; pie->block_radial->pie_data.event = EVENT_NONE; win->lock_pie_event = EVENT_NONE; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 7d019474f9e..c185ac45181 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3279,17 +3279,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/resources.c b/source/blender/editors/interface/resources.c index 15e742c930a..b7545199f56 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -2602,7 +2602,12 @@ void init_userdef_do_versions(void) cp[3] = 255; } } - + + if (U.versionfile < 274 || (U.versionfile == 274 && U.subversionfile < 3)) { + if (U.click_timeout == 0) + U.click_timeout = 250; + } + if (U.pixelsize == 0.0f) U.pixelsize = 1.0f; diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 9b1b0b915c1..5cdd45061e1 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -510,7 +510,11 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const w float min[3], max[3]; bool done = false; bool use_proj; - + + if (ELEM(event->click_type, KM_CLICK, KM_HOLD)) { + return OPERATOR_CANCELLED; + } + em_setup_viewcontext(C, &vc); ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index 2855af063c0..79eec57712f 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -407,9 +407,9 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) /* Vertex Slide */ WM_keymap_add_item(keymap, "TRANSFORM_OT_vert_slide", VKEY, KM_PRESS, KM_SHIFT, 0); /* use KM_CLICK because same key is used for tweaks */ - kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_RELEASE, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "rotate_source", true); - kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_CLICK, KM_SHIFT | KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "MESH_OT_dupli_extrude_cursor", ACTIONMOUSE, KM_PRESS, KM_SHIFT | KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "rotate_source", false); WM_keymap_add_menu(keymap, "VIEW3D_MT_edit_mesh_delete", XKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 99b351561c7..37faa7ba664 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1051,7 +1051,7 @@ static int object_speaker_add_exec(bContext *C, wmOperator *op) */ { /* 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 +1358,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 +1976,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 +1989,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 +2004,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--; @@ -2132,9 +2132,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 fe9ee71aa8e..d492a4b9dda 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -929,7 +929,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, "_"); diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index ad02f78e7b6..ac5ec1976ab 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1870,7 +1870,7 @@ void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot) { /* identifiers */ ot->name = "Corrective Smooth Bind"; - ot->description = "Bind base pose in delta mush modifier"; + ot->description = "Bind base pose in Corrective Smooth modifier"; ot->idname = "OBJECT_OT_correctivesmooth_bind"; /* api callbacks */ @@ -2045,7 +2045,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, @@ -2103,7 +2103,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; @@ -2114,7 +2114,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; } @@ -2145,7 +2145,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); @@ -2179,11 +2179,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 1045d7245fe..5c12a84ce6f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -305,7 +305,7 @@ void ED_keymap_object(wmKeyConfig *keyconf) keymap = WM_keymap_find(keyconf, "Object Non-modal", 0, 0); /* Note: this keymap works disregarding mode */ - kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_CLICK, 0, 0); RNA_enum_set(kmi->ptr, "mode", OB_MODE_EDIT); RNA_boolean_set(kmi->ptr, "toggle", true); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index bfa501c3732..2d981594524 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -1574,13 +1574,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 +1871,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 +1883,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 +1909,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 +1933,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 +1956,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 +1967,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 +1994,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/render/render_intern.h b/source/blender/editors/render/render_intern.h index ee0046b49dc..5c6744b0248 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -55,6 +55,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..f76f7cad1ae 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; @@ -188,11 +189,11 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu */ /* TODO(sergey): Need to check has_combined here? */ if (iuser->pass == 0) { + size_t view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* find current float rect for display, first case is after composite... still weak */ - if (rr->rectf) - rectf = rr->rectf; - else { - if (rr->rect32) { + rectf = RE_RenderViewGetRectf(rr, view_id); + if (rectf == NULL) { + if (RE_RenderViewGetRect32(rr, view_id)) { /* 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 +202,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; @@ -531,6 +532,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 +565,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 */ @@ -1487,7 +1489,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 58ea0961307..c680522219f 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,16 +128,125 @@ 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; + 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); + + 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]; int sizex = oglrender->sizex; int sizey = oglrender->sizey; @@ -138,8 +254,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) 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,9 +267,11 @@ 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) { + float *rectf; ImBuf *linear_ibuf; BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); @@ -175,7 +292,8 @@ 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); + rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); + memcpy(rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); IMB_freeImBuf(linear_ibuf); } @@ -221,7 +339,7 @@ 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; + camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat); if (camera->type == OB_CAMERA) { Camera *cam = camera->data; @@ -249,7 +367,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 { @@ -265,7 +383,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++) @@ -281,7 +399,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++) @@ -301,7 +419,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) { @@ -326,6 +444,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) if (rect) { int profile_to; + float *rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); if (BKE_scene_check_color_management_enabled(scene)) profile_to = IB_PROFILE_LINEAR_RGB; @@ -334,47 +453,60 @@ 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 */ + /* 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, rr->rectf, rr->rectx, rr->recty, 4); + 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); } +} - RE_ReleaseResult(oglrender->re); +static void screen_opengl_render_write(OGLRender *oglrender) +{ + Scene *scene = oglrender->scene; + RenderResult *rr; + bool ok; + char name[FILE_MAX]; + Object *camera = RE_GetCamera(oglrender->re); + + rr = RE_AcquireResultRead(oglrender->re); - /* update byte from float buffer */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); + 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); - if (ibuf) { - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, camera, false, name); - /* write file for animation */ - if (oglrender->write_still) { - char name[FILE_MAX]; - int ok; + RE_ReleaseResultImage(oglrender->re); - if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) { - IMB_color_to_bw(ibuf); - } + if (ok) printf("OpenGL Render written to '%s'\n", name); + else printf("OpenGL Render failed to write '%s'\n", name); +} - 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); - } +static void screen_opengl_render_apply(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + int view_id; + + 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); } - - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); - if (rect) - MEM_freeN(rect); + RE_ReleaseResult(oglrender->re); + + if (oglrender->write_still) { + screen_opengl_render_write(oglrender); + } } static bool screen_opengl_render_init(bContext *C, wmOperator *op) @@ -386,7 +518,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; @@ -459,7 +590,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->sseq = CTX_wm_space_seq(C); } - oglrender->prevsa = prevsa; oglrender->prevar = prevar; @@ -493,15 +623,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; } @@ -509,10 +641,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 */ @@ -553,13 +694,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, PRVRANGEON != 0)) { - 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; + } } } @@ -569,18 +724,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) @@ -600,7 +754,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); @@ -620,89 +774,40 @@ 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); + 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 { + ok = RE_WriteRenderViewsImage(op->reports, rr, scene, scene->camera, 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..6aaad98f946 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -58,6 +58,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 9dbd2e88563..e6fbfda3067 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -548,7 +548,9 @@ 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); + + /* material preview only needs monoscopy (view 0) */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { @@ -561,9 +563,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); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index cf712a653e6..3de21536148 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -605,6 +605,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) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 90c09070bc3..455d280e9ef 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -493,7 +493,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 +772,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 +817,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 +862,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; diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 115f7829420..3a40c300338 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" @@ -2124,4 +2125,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_OVERDROP) { + return true; + } + + break; + } + } + } + + return false; +} diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b5d7eb98595..c723a30bb73 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -717,7 +717,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 +990,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); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 330bc7e3395..4644f0ae0b8 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -303,6 +303,7 @@ typedef struct ScreenshotJob { const short *stop; const short *do_update; ReportList reports; + void *movie_ctx; } ScreenshotJob; @@ -312,7 +313,10 @@ static void screenshot_freejob(void *sjv) if (sj->dumprect) MEM_freeN(sj->dumprect); - + + if (sj->movie_ctx) + MEM_freeN(sj->movie_ctx); + MEM_freeN(sj); } @@ -337,20 +341,21 @@ 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, false)) { + mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); + sj->movie_ctx = mh->context_create(); + + 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 +367,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 +384,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 +415,10 @@ 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); + } BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped"); } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 5cfbd164153..7992767072a 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -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 4f71bea7cd2..9d39eea7b96 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -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; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index f853fd91491..b01d315650f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1964,7 +1964,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 @@ -5233,7 +5233,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 */ @@ -5444,7 +5444,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; } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index bfd429d0924..0db2259b307 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1022,7 +1022,7 @@ static void paint_stroke_line_constrain (PaintStroke *stroke, float mouse[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 */ diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5c55ad1c63d..6ca368ea4f7 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -4912,7 +4912,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(); } @@ -4950,7 +4950,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) /* Create sculpt mode session data */ if (ob->sculpt) - BKE_free_sculptsession(ob); + BKE_sculptsession_free(ob); sculpt_init_session(scene, ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index a4adbc6bca8..7b4e6109f1a 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; } 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..a7358172ef5 --- /dev/null +++ b/source/blender/editors/space_action/action_data.c @@ -0,0 +1,938 @@ +/* + * ***** 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_ERROR, + "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 */ + +/* Get the NLA Track that the active action comes from, since this is not stored in AnimData */ +/* TODO: Move this to blenkernel/nla.c */ +static NlaTrack *nla_tweak_track_get(AnimData *adt) +{ + NlaTrack *nlt; + + /* sanity check */ + if (adt == NULL) + return NULL; + + /* Since the track itself gets disabled, we want the first disabled... */ + for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { + if (nlt->flag & (NLATRACK_ACTIVE | NLATRACK_DISABLED)) { + /* For good measure, make sure that strip actually exists there */ + if (BLI_findindex(&nlt->strips, adt->actstrip) != -1) { + return nlt; + } + else if (G.debug & G_DEBUG) { + printf("%s: Active strip (%p, %s) not in NLA track found (%p, %s)\n", + __func__, + adt->actstrip, (adt->actstrip) ? adt->actstrip->name : "<None>", + nlt, (nlt) ? nlt->name : "<None>"); + } + } + } + + /* Not found! */ + return NULL; +} + +/* 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 = nla_tweak_track_get(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 = nla_tweak_track_get(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 cc30639332a..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 */ diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index ce8723c572d..4b8ddacb418 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -80,432 +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... */ - 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 (!(scene->flag & SCE_NLA_EDIT_ON)) { - 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 */ - if (ob) - return true; - } - else if (saction->mode == SACTCONT_SHAPEKEY) { - Key *key = BKE_key_from_object(ob); - if (key) - return true; - } - } - 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 */ diff --git a/source/blender/editors/space_action/action_intern.h b/source/blender/editors/space_action/action_intern.h index 9a19e4a926b..9f9b15c3c26 100644 --- a/source/blender/editors/space_action/action_intern.h +++ b/source/blender/editors/space_action/action_intern.h @@ -98,10 +98,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..9ad0931a957 100644 --- a/source/blender/editors/space_action/action_ops.c +++ b/source/blender/editors/space_action/action_ops.c @@ -79,10 +79,15 @@ 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); diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 92731c2f135..b3149c1bbf4 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -1140,8 +1140,11 @@ static int console_modal_select(bContext *C, wmOperator *op, const wmEvent *even case LEFTMOUSE: case MIDDLEMOUSE: case RIGHTMOUSE: - console_cursor_set_exit(C, op); - return OPERATOR_FINISHED; + if (event->val == KM_RELEASE) { + console_cursor_set_exit(C, op); + return OPERATOR_FINISHED; + } + break; case MOUSEMOVE: console_modal_select_apply(C, op, event); break; diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 14b72eb8f6d..6858f536e92 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -642,8 +642,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 +694,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); } } } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 782b318b8a2..557a495cb8f 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -444,27 +444,27 @@ static void file_keymap(struct wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "need_active", true); /* left mouse selects and opens */ - WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, 0, 0); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "fill", true); /* right mouse selects without opening */ - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, 0, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "open", false); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "open", false); - kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_CLICK, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "FILE_OT_select", RIGHTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "fill", true); RNA_boolean_set(kmi->ptr, "open", false); /* 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); + WM_keymap_add_item(keymap, "FILE_OT_previous", BUTTON4MOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "FILE_OT_next", BUTTON5MOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "FILE_OT_select_all_toggle", AKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "FILE_OT_refresh", PADPERIOD, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c index a5c04ce2cb4..0313ce4b1d4 100644 --- a/source/blender/editors/space_graph/graph_draw.c +++ b/source/blender/editors/space_graph/graph_draw.c @@ -1137,6 +1137,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 +1150,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_ops.c b/source/blender/editors/space_graph/graph_ops.c index 33e8b522335..e266a5089c8 100644 --- a/source/blender/editors/space_graph/graph_ops.c +++ b/source/blender/editors/space_graph/graph_ops.c @@ -574,7 +574,7 @@ static void graphedit_keymap_keyframes(wmKeyConfig *keyconf, wmKeyMap *keymap) /* insertkey */ WM_keymap_add_item(keymap, "GRAPH_OT_keyframe_insert", IKEY, KM_PRESS, 0, 0); - WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_CLICK, KM_CTRL, 0); + WM_keymap_add_item(keymap, "GRAPH_OT_click_insert", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0); /* copy/paste */ WM_keymap_add_item(keymap, "GRAPH_OT_copy", CKEY, KM_PRESS, KM_CTRL, 0); diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index 78dbae7618b..28107337084 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1089,6 +1089,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,7 +1118,7 @@ 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, + ListBase *matches, View2D *v2d, FCurve *fcu, eAnim_ChannelType ctype, BezTriple *bezt, FPoint *fpt, short hpoint, const int mval[2], float unit_scale) { /* Keyframes or Samples? */ @@ -1149,6 +1151,8 @@ static void nearest_fcurve_vert_store( /* store values */ nvi->fcu = fcu; + nvi->ctype = ctype; + nvi->bezt = bezt; nvi->hpoint = hpoint; nvi->dist = dist; @@ -1190,29 +1194,29 @@ static void get_nearest_fcurve_verts_list(bAnimContext *ac, const int mval[2], L 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); - + /* 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); /* 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); } /* 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); } } } @@ -1405,7 +1409,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_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index fb3c140fddf..a02f64d68d8 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,10 @@ 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) { + if (RE_RenderViewGetRectf(rr, 0)) { return IFACE_("Composite"); } - else if (rr->rect32) { + else if (RE_RenderViewGetRect32(rr, 0)) { return IFACE_("Sequence"); } else { @@ -375,7 +376,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 +384,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 +399,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 +421,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, ""); + uiDefButS(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 +450,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) { @@ -483,7 +552,7 @@ static void image_multi_incpass_cb(bContext *C, void *rr_v, void *iuser_v) if (rl) { int tot = BLI_listbase_count(&rl->passes); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->pass < tot - 1) { @@ -504,6 +573,16 @@ static void image_multi_decpass_cb(bContext *C, void *rr_v, void *iuser_v) } } +/* 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 +602,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 +617,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 +639,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes if (rr) { RenderPass *rpass; + RenderView *rview; int rpass_index; /* layer */ @@ -566,20 +648,50 @@ 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); - display_name = rpass ? rpass->name : (fake_name ? fake_name : ""); + display_name = rpass ? rpass->internal_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")); 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); } } @@ -734,13 +846,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"); } @@ -792,6 +904,17 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); } + if ((scene->r.scemode & R_MULTIVIEW) != 0) { + uiItemS(layout); + + col = uiLayoutColumn(layout, false); + uiItemR(col, &imaptr, "use_multiview", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(&imaptr, "use_multiview")); + uiTemplateImageViews(col, &imaptr); + } + if (ima->source == IMA_SRC_MOVIE) { col = uiLayoutColumn(layout, false); uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE); @@ -962,6 +1085,101 @@ 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; + uiLayout *col; + + if (ptr) { + uiItemR(layout, ptr, "use_multiview", 0, NULL, ICON_NONE); + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_multiview")); + } + else { + col = uiLayoutColumn(layout, false); + } + + 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(col, imfptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(col, imfptr, NULL); + } +} + void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) { Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 517cf64214c..5a3c3e90618 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -783,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? */ @@ -813,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 @@ -823,6 +825,14 @@ 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 */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 976c7675371..aa625210464 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" @@ -936,6 +945,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 +956,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); @@ -1048,7 +1057,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]; @@ -1085,6 +1094,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 +1152,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 +1172,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 +1212,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 +1262,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 +1294,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; @@ -1433,6 +1494,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 +1533,131 @@ static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op) RNA_string_set(op->ptr, "filepath", simopts->filepath); } +/* returns the pass index for the view_id */ +static int get_multiview_pass_id(RenderResult *rr, ImageUser *iuser, const int view_id) +{ + RenderLayer *rl; + RenderPass *rpass; + int passtype; + short rl_index = 0, rp_index; + + if (rr == NULL || iuser == NULL) + return 0; + + if (BLI_listbase_count_ex(&rr->views, 2) < 2) + return iuser->pass; + + if (RE_HasFakeLayer(rr)) + rl_index ++; /* fake compo/sequencer layer */ + + rl = BLI_findlink(&rr->layers, rl_index); + if (!rl) return iuser->pass; + + rpass = BLI_findlink(&rl->passes, iuser->pass); + passtype = rpass->passtype; + + rp_index = 0; + for (rpass = rl->passes.first; rpass; rpass = rpass->next, rp_index++) { + if (rpass->passtype == passtype && + rpass->view_id == view_id) + { + return rp_index; + } + } + + return iuser->pass; +} + +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) { + 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 */ + } + + IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + + } + } + 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 +1678,197 @@ 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); + /* 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; } - if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; + } + } + + /* 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); + 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); + } + + 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) { + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, i); + 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); - /* only image path, never ibuf */ - if (relative) { - BLI_path_rel(ima->name, relbase); /* only after saving */ + 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); + + for (i = 0; i < 2; i ++) { + ImageUser iuser = sima->iuser; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, id); + 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; + } + + ibuf->planes = planes; - WM_cursor_wait(0); + /* 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); - 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; + BKE_imbuf_write_prepare(colormanaged_ibuf, imf); + IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); - IMB_freeImBuf(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 +1916,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 +1938,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 +1970,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, "use_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, NULL); } static int image_save_as_poll(bContext *C) @@ -1714,8 +2006,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 +2222,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 +2243,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 +2328,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 +2395,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 +2415,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 @@ -2200,7 +2541,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 +2570,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 +2642,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 +2670,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 +2681,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 +2950,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; diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index bd3d76bbaba..1023f9641b3 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -156,7 +156,8 @@ 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; + scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; 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..4ed4a3f9514 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -504,6 +504,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 +771,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..5d9a77c8576 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -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_intern.h b/source/blender/editors/space_nla/nla_intern.h index bd96b5a4de5..70fdb2d478d 100644 --- a/source/blender/editors/space_nla/nla_intern.h +++ b/source/blender/editors/space_nla/nla_intern.h @@ -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..43bc4b9df0f 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); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index eedf03ef0b9..3a5067b87f0 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); } @@ -1238,6 +1240,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; @@ -1251,6 +1271,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) @@ -1741,8 +1763,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 @@ -1751,15 +1773,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"); @@ -1821,6 +1850,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); } } } @@ -2114,6 +2146,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; @@ -2611,6 +2655,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; @@ -2983,6 +3030,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_edit.c b/source/blender/editors/space_node/node_edit.c index 3595b4520ea..e2bcf358a33 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -212,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; @@ -238,7 +238,11 @@ 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); + + 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; @@ -1660,6 +1664,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_intern.h b/source/blender/editors/space_node/node_intern.h index d49d37c651e..b15e9025a82 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -200,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_relationships.c b/source/blender/editors/space_node/node_relationships.c index c8951a1172e..f72def8377f 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -633,11 +633,14 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: case MIDDLEMOUSE: { - node_link_exit(C, op, true); - - ED_area_headerprint(CTX_wm_area(C), NULL); - ED_region_tag_redraw(ar); - return OPERATOR_FINISHED; + if (event->val != KM_NOTHING) { + node_link_exit(C, op, true); + + ED_area_headerprint(CTX_wm_area(C), NULL); + ED_region_tag_redraw(ar); + return OPERATOR_FINISHED; + } + break; } } diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c index 8ff16df9bf0..12e1603761b 100644 --- a/source/blender/editors/space_node/node_view.c +++ b/source/blender/editors/space_node/node_view.c @@ -613,8 +613,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_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index f5869575cc6..93116dbe22c 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -91,19 +91,19 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "OUTLINER_OT_item_rename", LEFTMOUSE, KM_DBL_CLICK, 0, 0); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, 0, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, 0, 0); RNA_boolean_set(kmi->ptr, "recursive", false); RNA_boolean_set(kmi->ptr, "extend", false); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "recursive", false); RNA_boolean_set(kmi->ptr, "extend", true); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); RNA_boolean_set(kmi->ptr, "recursive", true); RNA_boolean_set(kmi->ptr, "extend", false); - kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_CLICK, KM_CTRL | KM_SHIFT, 0); + kmi = WM_keymap_add_item(keymap, "OUTLINER_OT_item_activate", LEFTMOUSE, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "recursive", true); RNA_boolean_set(kmi->ptr, "extend", true); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index af21478150b..e5833103658 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -467,7 +467,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); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 9239d9cb27e..17a6e952ee1 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" @@ -114,7 +114,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 +133,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 +151,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 +171,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 +216,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); 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 329e6db4cd0..373095d68f9 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -60,6 +60,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" +#include "UI_interface.h" #include "BKE_sound.h" @@ -70,6 +71,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 +227,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; + } + } } /** @@ -573,6 +591,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 +602,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 +645,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 +688,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; @@ -735,6 +807,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 +817,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 +828,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 +850,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; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 4e4b108cd95..98937716cc0 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" @@ -879,7 +880,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; @@ -907,6 +908,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 @@ -1013,6 +1015,7 @@ 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 char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; 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 @@ -1042,8 +1045,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq return; } - ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs); - + /* for now we only support Left/Right */ + ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); + if (ibuf == NULL) return; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index e2602ca81ef..f5c55cd91f0 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 @@ -3452,12 +3448,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); } } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 63ae52c3207..7c7bdde9165 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -62,7 +62,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; diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 40191905c38..e34388db42d 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -368,5 +368,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_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 71eb64a74f5..5a564dd6f25 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -98,7 +98,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; @@ -205,8 +205,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_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index ad8050a50e8..9d517b20f01 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -2163,6 +2163,11 @@ static int text_scroll_modal(bContext *C, wmOperator *op, const wmEvent *event) case LEFTMOUSE: case RIGHTMOUSE: case MIDDLEMOUSE: + /* don't exit on dummy events */ + if (event->val == KM_NOTHING) { + return OPERATOR_RUNNING_MODAL; + } + if (ELEM(tsc->zone, SCROLLHANDLE_MIN_OUTSIDE, SCROLLHANDLE_MAX_OUTSIDE)) { txt_screen_skip(st, ar, st->viewlines * (tsc->zone == SCROLLHANDLE_MIN_OUTSIDE ? 1 : -1)); @@ -2667,8 +2672,11 @@ static int text_set_selection_modal(bContext *C, wmOperator *op, const wmEvent * case LEFTMOUSE: case MIDDLEMOUSE: case RIGHTMOUSE: - text_cursor_set_exit(C, op); - return OPERATOR_FINISHED; + if (event->val != KM_NOTHING) { + text_cursor_set_exit(C, op); + return OPERATOR_FINISHED; + } + break; case TIMER: case MOUSEMOVE: text_cursor_set_apply(C, op, event); diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index f0b73efe63c..aeb7d108edc 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -1741,6 +1741,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]) @@ -1755,6 +1987,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera); MovieClip *clip = BKE_object_movieclip_get(scene, base->object, false); + const bool is_stereo3d = drawcamera_is_stereo3d(scene, v3d, ob); + const bool is_stereo3d_cameras = (ob == scene->camera) && + (scene->r.scemode & R_MULTIVIEW) && + (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) && + (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS); + /* draw data for movie clip set as active for scene */ if (clip) { draw_viewport_reconstruction(scene, base, v3d, clip, dflag, ob_wire_col, false); @@ -1791,12 +2029,8 @@ 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) + drawcamera_frame(vec, GL_LINE_LOOP); if (is_view) return; @@ -1804,20 +2038,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 */ @@ -1867,6 +2093,11 @@ 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() */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 25b333f2b95..8558a90d5b7 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -354,7 +354,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"); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 285c0645c37..b1fb7b907b8 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -50,6 +50,7 @@ #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" @@ -100,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 @@ -1482,14 +1488,15 @@ 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); @@ -1602,6 +1609,24 @@ 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; + + /* show only left or right camera */ + else if (v3d->stereo3d_camera != STEREO_3D_ID) + 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) { @@ -1645,6 +1670,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; } @@ -2127,7 +2153,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); @@ -2672,7 +2698,7 @@ static void gpu_update_lamps_shadows_world(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; @@ -3250,7 +3276,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; @@ -3285,7 +3312,10 @@ 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)) + 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) { @@ -3349,7 +3379,7 @@ 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; @@ -3374,13 +3404,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); @@ -3389,13 +3420,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 */ @@ -3420,7 +3451,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}; @@ -3450,9 +3482,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); @@ -3466,7 +3500,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); } @@ -3671,6 +3705,103 @@ 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) || 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]) @@ -3705,8 +3836,11 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 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 diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 785d0e60c4c..3fc90c97678 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -4701,16 +4701,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])) @@ -4761,8 +4769,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..a3dfd62ba6b 100644 --- a/source/blender/editors/space_view3d/view3d_fly.c +++ b/source/blender/editors/space_view3d/view3d_fly.c @@ -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_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index ac397506438..d1b50b1579f 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -236,8 +236,9 @@ void view3d_keymap(wmKeyConfig *keyconf) WM_keymap_verify_item(keymap, "VIEW3D_OT_navigate", FKEY, KM_PRESS, KM_SHIFT, 0); - WM_keymap_verify_item(keymap, "VIEW3D_OT_smoothview", TIMER1, KM_ANY, KM_ANY, 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_NOTHING, KM_ANY, 0); + WM_keymap_add_item(keymap, "VIEW3D_OT_rotate", MOUSEPAN, 0, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_rotate", MOUSEROTATE, 0, 0, 0); WM_keymap_add_item(keymap, "VIEW3D_OT_move", MOUSEPAN, 0, KM_SHIFT, 0); @@ -386,7 +387,7 @@ void view3d_keymap(wmKeyConfig *keyconf) /* drawtype */ - kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle_enum", ZKEY, KM_CLICK, 0, 0); RNA_string_set(kmi->ptr, "data_path", "space_data.viewport_shade"); RNA_string_set(kmi->ptr, "value_1", "SOLID"); RNA_string_set(kmi->ptr, "value_2", "WIREFRAME"); diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c index 191eeb05c71..11756e4feca 100644 --- a/source/blender/editors/space_view3d/view3d_walk.c +++ b/source/blender/editors/space_view3d/view3d_walk.c @@ -220,8 +220,7 @@ void walk_modal_keymap(wmKeyConfig *keyconf) } -typedef struct WalkTeleport -{ +typedef struct WalkTeleport { eWalkTeleportState state; float duration; /* from user preferences */ float origin[3]; diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 72be301480e..801e360a288 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -866,10 +866,10 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) keymap = WM_modalkeymap_add(keyconf, "Transform Modal Map", modal_items); /* 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, ESCKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CANCEL); + WM_modalkeymap_add_item(keymap, LEFTMOUSE, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, RETKEY, KM_RELEASE, KM_ANY, 0, TFM_MODAL_CONFIRM); + WM_modalkeymap_add_item(keymap, PADENTER, KM_RELEASE, 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); @@ -5300,9 +5300,10 @@ 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)) { + 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); } |