diff options
Diffstat (limited to 'source/blender/editors')
146 files changed, 4089 insertions, 2445 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 6dd1fe75163..d4941950029 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -4674,7 +4674,7 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void DEG_id_tag_update(ale_setting->id, ID_RECALC_ANIMATION); } if (ale_setting->adt && ale_setting->adt->action) { - /* Action is it's own datablock, so has to be tagged specifically. */ + /* Action is its own datablock, so has to be tagged specifically. */ DEG_id_tag_update(&ale_setting->adt->action->id, ID_RECALC_ANIMATION); } diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 98c050950be..0030e78002b 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -17,6 +17,7 @@ set(INC ../include + ../../blenfont ../../blenkernel ../../blenlib ../../blentranslation diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index 68fff1091af..3902f6613a1 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -339,7 +339,7 @@ void postEditBoneDuplicate(struct ListBase *editbones, Object *ob) } BKE_pose_channels_hash_free(ob->pose); - BKE_pose_channels_hash_make(ob->pose); + BKE_pose_channels_hash_ensure(ob->pose); GHash *name_map = BLI_ghash_str_new(__func__); @@ -390,7 +390,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone, bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name))) { + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name))) { if ((conlist = &pchan->constraints)) { for (curcon = conlist->first; curcon; curcon = curcon->next) { /* does this constraint have a subtarget in @@ -825,7 +825,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig bConstraint *curcon; ListBase *conlist; - if ((pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name)) == NULL || + if ((pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name)) == NULL || (conlist = &pchan->constraints) == NULL) { return; } @@ -855,7 +855,7 @@ static void updateDuplicateCustomBoneShapes(bContext *C, EditBone *dup_bone, Obj return; } bPoseChannel *pchan; - pchan = BKE_pose_channel_verify(ob->pose, dup_bone->name); + pchan = BKE_pose_channel_ensure(ob->pose, dup_bone->name); if (pchan->custom != NULL) { Main *bmain = CTX_data_main(C); @@ -885,12 +885,12 @@ static void copy_pchan(EditBone *src_bone, EditBone *dst_bone, Object *src_ob, O if (src_ob->pose) { bPoseChannel *chanold, *channew; - chanold = BKE_pose_channel_verify(src_ob->pose, src_bone->name); + chanold = BKE_pose_channel_ensure(src_ob->pose, src_bone->name); if (chanold) { /* WARNING: this creates a new posechannel, but there will not be an attached bone * yet as the new bones created here are still 'EditBones' not 'Bones'. */ - channew = BKE_pose_channel_verify(dst_ob->pose, dst_bone->name); + channew = BKE_pose_channel_ensure(dst_ob->pose, dst_bone->name); if (channew) { BKE_pose_channel_copy_data(channew, chanold); @@ -1193,7 +1193,7 @@ static int armature_symmetrize_exec(bContext *C, wmOperator *op) * is synchronized. */ bPoseChannel *pchan; /* Make sure we clean up the old data before overwriting it */ - pchan = BKE_pose_channel_verify(obedit->pose, ebone_iter->temp.ebone->name); + pchan = BKE_pose_channel_ensure(obedit->pose, ebone_iter->temp.ebone->name); BKE_pose_channel_free(pchan); /* Sync pchan data */ copy_pchan(ebone_iter, ebone_iter->temp.ebone, obedit, obedit); diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index 93d36abe792..f7b54b79601 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -33,6 +33,7 @@ #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_vec_types.h" #include "BKE_fcurve.h" #include "BKE_nla.h" @@ -41,6 +42,7 @@ #include "BKE_layer.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_screen.h" #include "BKE_unit.h" #include "RNA_access.h" @@ -50,15 +52,28 @@ #include "WM_types.h" #include "UI_interface.h" +#include "UI_resources.h" #include "ED_armature.h" #include "ED_keyframes_draw.h" #include "ED_markers.h" #include "ED_numinput.h" #include "ED_screen.h" +#include "ED_space_api.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" +#include "GPU_state.h" #include "armature_intern.h" +#include "BLF_api.h" + +/* Pixel distance from 0% to 100%. */ +#define SLIDE_PIXEL_DISTANCE (300 * U.pixelsize) +#define OVERSHOOT_RANGE_DELTA 0.2f + /* **************************************************** */ /* == POSE 'SLIDING' TOOLS == * @@ -110,15 +125,36 @@ typedef struct tPoseSlideOp { /** unused for now, but can later get used for storing runtime settings.... */ short flag; + /* Store overlay settings when invoking the operator. Bones will be temporarily hidden. */ + int overlay_flag; + /** which transforms/channels are affected (ePoseSlide_Channels) */ short channels; /** axis-limits for transforms (ePoseSlide_AxisLock) */ short axislock; - /** 0-1 value for determining the influence of whatever is relevant */ + /* Allow overshoot or clamp between 0% and 100%. */ + bool overshoot; + + /* Reduces percentage delta from mouse movement. */ + bool precision; + + /* Move percentage in 10% steps. */ + bool increments; + + /* Draw callback handler. */ + void *draw_handle; + + /* Accumulative, unclamped and unrounded percentage. */ + float raw_percentage; + + /* 0-1 value for determining the influence of whatever is relevant. */ float percentage; - /** numeric input */ + /* Last cursor position in screen space used for mouse movement delta calculation. */ + int last_cursor_x; + + /* Numeric input. */ NumInput num; struct tPoseSlideObject *ob_data_array; @@ -187,6 +223,240 @@ static const EnumPropertyItem prop_axis_lock_types[] = { /* ------------------------------------ */ +static void draw_overshoot_triangle(const uint8_t color[4], + const bool facing_right, + const float x, + const float y) +{ + const uint shdr_pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(true); + immUniformColor3ubvAlpha(color, 225); + const float triangle_side_length = facing_right ? 6 * U.pixelsize : -6 * U.pixelsize; + const float triangle_offset = facing_right ? 2 * U.pixelsize : -2 * U.pixelsize; + + immBegin(GPU_PRIM_TRIS, 3); + immVertex2f(shdr_pos_2d, x + triangle_offset + triangle_side_length, y); + immVertex2f(shdr_pos_2d, x + triangle_offset, y + triangle_side_length / 2); + immVertex2f(shdr_pos_2d, x + triangle_offset, y - triangle_side_length / 2); + immEnd(); + + GPU_polygon_smooth(false); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); +} + +static void draw_ticks(const float start_percentage, + const float end_percentage, + const struct vec2f line_start, + const float base_tick_height, + const float line_width, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + /* Use percentage represented as 0-100 int to avoid floating point precision problems. */ + const int tick_increment = 10; + + /* Round initial_tick_percentage up to the next tick_increment. */ + int tick_percentage = ceil((start_percentage * 100) / tick_increment) * tick_increment; + float tick_height = base_tick_height; + + while (tick_percentage <= (int)(end_percentage * 100)) { + /* Different ticks have different heights. Multiples of 100% are the tallest, 50% is a bit + * smaller and the rest is the minimum size. */ + if (tick_percentage % 100 == 0) { + tick_height = base_tick_height; + } + else if (tick_percentage % 50 == 0) { + tick_height = base_tick_height * 0.8; + } + else { + tick_height = base_tick_height * 0.5; + } + + const float x = line_start.x + + (((float)tick_percentage / 100) - start_percentage) * SLIDE_PIXEL_DISTANCE; + const struct rctf tick_rect = {.xmin = x - (line_width / 2), + .xmax = x + (line_width / 2), + .ymin = line_start.y - (tick_height / 2), + .ymax = line_start.y + (tick_height / 2)}; + + if (tick_percentage < 0 || tick_percentage > 100) { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_overshoot, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&tick_rect, true, 1, color_line, 255); + } + tick_percentage += tick_increment; + } +} + +static void draw_main_line(const struct rctf main_line_rect, + const float percentage, + const bool overshoot, + const uint8_t color_overshoot[4], + const uint8_t color_line[4]) +{ + if (overshoot) { + /* In overshoot mode, draw the 0-100% range differently to provide a visual reference. */ + const float line_zero_percent = main_line_rect.xmin - + ((percentage - 0.5f - OVERSHOOT_RANGE_DELTA) * + SLIDE_PIXEL_DISTANCE); + + const float clamped_line_zero_percent = clamp_f( + line_zero_percent, main_line_rect.xmin, main_line_rect.xmax); + const float clamped_line_hundred_percent = clamp_f( + line_zero_percent + SLIDE_PIXEL_DISTANCE, main_line_rect.xmin, main_line_rect.xmax); + + const struct rctf left_overshoot_line_rect = {.xmin = main_line_rect.xmin, + .xmax = clamped_line_zero_percent, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + const struct rctf right_overshoot_line_rect = {.xmin = clamped_line_hundred_percent, + .xmax = main_line_rect.xmax, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + UI_draw_roundbox_3ub_alpha(&left_overshoot_line_rect, true, 0, color_overshoot, 255); + UI_draw_roundbox_3ub_alpha(&right_overshoot_line_rect, true, 0, color_overshoot, 255); + + const struct rctf non_overshoot_line_rect = {.xmin = clamped_line_zero_percent, + .xmax = clamped_line_hundred_percent, + .ymin = main_line_rect.ymin, + .ymax = main_line_rect.ymax}; + UI_draw_roundbox_3ub_alpha(&non_overshoot_line_rect, true, 0, color_line, 255); + } + else { + UI_draw_roundbox_3ub_alpha(&main_line_rect, true, 0, color_line, 255); + } +} + +static void draw_backdrop(const int fontid, + const struct rctf main_line_rect, + const float color_bg[4], + const short region_y_size, + const float base_tick_height) +{ + float string_pixel_size[2]; + const char *percentage_placeholder = "000%%"; + BLF_width_and_height(fontid, + percentage_placeholder, + sizeof(percentage_placeholder), + &string_pixel_size[0], + &string_pixel_size[1]); + const struct vec2f pad = {.x = (region_y_size - base_tick_height) / 2, .y = 2.0f * U.pixelsize}; + const struct rctf backdrop_rect = {.xmin = main_line_rect.xmin - string_pixel_size[0] - pad.x, + .xmax = main_line_rect.xmax + pad.x, + .ymin = pad.y, + .ymax = region_y_size - pad.y}; + UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); +} + +/* Draw an on screen Slider for a Pose Slide Operator. */ +static void pose_slide_draw_2d_slider(const struct bContext *UNUSED(C), ARegion *region, void *arg) +{ + tPoseSlideOp *pso = arg; + + /* Only draw in region from which the Operator was started. */ + if (region != pso->region) { + return; + } + + uint8_t color_text[4]; + uint8_t color_line[4]; + uint8_t color_handle[4]; + uint8_t color_overshoot[4]; + float color_bg[4]; + + /* Get theme colors. */ + UI_GetThemeColor4ubv(TH_TEXT, color_text); + UI_GetThemeColor4ubv(TH_TEXT, color_line); + UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); + UI_GetThemeColor3fv(TH_BACK, color_bg); + + color_bg[3] = 0.5f; + color_overshoot[0] = color_overshoot[0] * 0.7; + color_overshoot[1] = color_overshoot[1] * 0.7; + color_overshoot[2] = color_overshoot[2] * 0.7; + + /* Get the default font. */ + const uiStyle *style = UI_style_get(); + const uiFontStyle *fstyle = &style->widget; + const int fontid = fstyle->uifont_id; + BLF_color3ubv(fontid, color_text); + BLF_rotation(fontid, 0.0f); + + const float line_width = 1.5 * U.pixelsize; + const float base_tick_height = 12.0 * U.pixelsize; + const float line_y = region->winy / 2; + + struct rctf main_line_rect = {.xmin = (region->winx / 2) - (SLIDE_PIXEL_DISTANCE / 2), + .xmax = (region->winx / 2) + (SLIDE_PIXEL_DISTANCE / 2), + .ymin = line_y - line_width / 2, + .ymax = line_y + line_width / 2}; + float line_start_percentage = 0; + int handle_pos_x = main_line_rect.xmin + SLIDE_PIXEL_DISTANCE * pso->percentage; + + if (pso->overshoot) { + main_line_rect.xmin = main_line_rect.xmin - SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + main_line_rect.xmax = main_line_rect.xmax + SLIDE_PIXEL_DISTANCE * OVERSHOOT_RANGE_DELTA; + line_start_percentage = pso->percentage - 0.5f - OVERSHOOT_RANGE_DELTA; + handle_pos_x = region->winx / 2; + } + + draw_backdrop(fontid, main_line_rect, color_bg, pso->region->winy, base_tick_height); + + draw_main_line(main_line_rect, pso->percentage, pso->overshoot, color_overshoot, color_line); + + const float percentage_range = pso->overshoot ? 1 + OVERSHOOT_RANGE_DELTA * 2 : 1; + const struct vec2f line_start_position = {.x = main_line_rect.xmin, .y = line_y}; + draw_ticks(line_start_percentage, + line_start_percentage + percentage_range, + line_start_position, + base_tick_height, + line_width, + color_overshoot, + color_line); + + /* Draw triangles at the ends of the line in overshoot mode to indicate direction of 0-100% + * range.*/ + if (pso->overshoot) { + if (pso->percentage > 1 + OVERSHOOT_RANGE_DELTA + 0.5) { + draw_overshoot_triangle(color_line, false, main_line_rect.xmin, line_y); + } + if (pso->percentage < 0 - OVERSHOOT_RANGE_DELTA - 0.5) { + draw_overshoot_triangle(color_line, true, main_line_rect.xmax, line_y); + } + } + + char percentage_string[256]; + + /* Draw handle indicating current percentage. */ + const struct rctf handle_rect = {.xmin = handle_pos_x - (line_width), + .xmax = handle_pos_x + (line_width), + .ymin = line_y - (base_tick_height / 2), + .ymax = line_y + (base_tick_height / 2)}; + + UI_draw_roundbox_3ub_alpha(&handle_rect, true, 1, color_handle, 255); + BLI_snprintf(percentage_string, sizeof(percentage_string), "%.0f%%", pso->percentage * 100); + + /* Draw percentage string. */ + float percentage_pixel_size[2]; + BLF_width_and_height(fontid, + percentage_string, + sizeof(percentage_string), + &percentage_pixel_size[0], + &percentage_pixel_size[1]); + + BLF_position(fontid, + main_line_rect.xmin - 24.0 * U.pixelsize - percentage_pixel_size[0] / 2, + (region->winy / 2) - percentage_pixel_size[1] / 2, + 0.0f); + BLF_draw(fontid, percentage_string, sizeof(percentage_string)); +} + /* operator init */ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) { @@ -205,6 +475,7 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) /* set range info from property values - these may get overridden for the invoke() */ pso->percentage = RNA_float_get(op->ptr, "percentage"); + pso->raw_percentage = pso->percentage; pso->prevFrame = RNA_int_get(op->ptr, "prev_frame"); pso->nextFrame = RNA_int_get(op->ptr, "next_frame"); @@ -257,6 +528,14 @@ static int pose_slide_init(bContext *C, wmOperator *op, ePoseSlide_Modes mode) pso->num.val_flag[0] |= NUM_NO_NEGATIVE; pso->num.unit_type[0] = B_UNIT_NONE; /* percentages don't have any units... */ + /* Register UI drawing callback. */ + ARegion *region_header = BKE_area_find_region_type(pso->area, RGN_TYPE_HEADER); + if (region_header != NULL) { + pso->region = region_header; + pso->draw_handle = ED_region_draw_cb_activate( + region_header->type, pose_slide_draw_2d_slider, pso, REGION_DRAW_POST_PIXEL); + } + /* return status is whether we've got all the data we were requested to get */ return 1; } @@ -266,6 +545,13 @@ static void pose_slide_exit(wmOperator *op) { tPoseSlideOp *pso = op->customdata; + /* Hide Bone Overlay. */ + View3D *v3d = pso->area->spacedata.first; + v3d->overlay.flag = pso->overlay_flag; + + /* Remove UI drawing callback. */ + ED_region_draw_cb_exit(pso->region->type, pso->draw_handle); + /* if data exists, clear its data and exit */ if (pso) { /* free the temp pchan links and their data */ @@ -602,7 +888,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], float default_value) { - /* We only slide to the rest pose. So only use the default rest pose value */ + /* We only slide to the rest pose. So only use the default rest pose value. */ const int lock = pso->axislock; for (int idx = 0; idx < 3; idx++) { if ((lock == 0) || ((lock & PS_LOCK_X) && (idx == 0)) || ((lock & PS_LOCK_Y) && (idx == 1)) || @@ -621,7 +907,7 @@ static void pose_slide_rest_pose_apply_vec3(tPoseSlideOp *pso, float vec[3], flo static void pose_slide_rest_pose_apply_other_rot(tPoseSlideOp *pso, float vec[4], bool quat) { - /* We only slide to the rest pose. So only use the default rest pose value */ + /* We only slide to the rest pose. So only use the default rest pose value. */ float default_values[] = {1.0f, 0.0f, 0.0f, 0.0f}; if (!quat) { /* Axis Angle */ @@ -789,14 +1075,18 @@ static void pose_slide_reset(tPoseSlideOp *pso) /* ------------------------------------ */ -/* draw percentage indicator in header */ +/* Draw percentage indicator in workspace footer. */ /* TODO: Include hints about locks here... */ -static void pose_slide_draw_status(tPoseSlideOp *pso) +static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) { char status_str[UI_MAX_DRAW_STR]; char limits_str[UI_MAX_DRAW_STR]; char axis_str[50]; char mode_str[32]; + char overshoot_str[50]; + char precision_str[50]; + char increments_str[50]; + char bone_vis_str[50]; switch (pso->mode) { case POSESLIDE_PUSH: @@ -871,25 +1161,51 @@ static void pose_slide_draw_status(tPoseSlideOp *pso) break; } + if (pso->overshoot) { + BLI_strncpy(overshoot_str, TIP_("[E] - Disable overshoot"), sizeof(overshoot_str)); + } + else { + BLI_strncpy(overshoot_str, TIP_("E - Enable overshoot"), sizeof(overshoot_str)); + } + + if (pso->precision) { + BLI_strncpy(precision_str, TIP_("[Shift] - Precision active"), sizeof(precision_str)); + } + else { + BLI_strncpy(precision_str, TIP_("Shift - Hold for precision"), sizeof(precision_str)); + } + + if (pso->increments) { + BLI_strncpy(increments_str, TIP_("[Ctrl] - Increments active"), sizeof(increments_str)); + } + else { + BLI_strncpy(increments_str, TIP_("Ctrl - Hold for 10% increments"), sizeof(increments_str)); + } + + BLI_strncpy(bone_vis_str, TIP_("[H] - Toggle bone visibility"), sizeof(increments_str)); + if (hasNumInput(&pso->num)) { Scene *scene = pso->scene; - char str_ofs[NUM_STR_REP_LEN]; + char str_offs[NUM_STR_REP_LEN]; - outputNumInput(&pso->num, str_ofs, &scene->unit); + outputNumInput(&pso->num, str_offs, &scene->unit); - BLI_snprintf( - status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_ofs, limits_str); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s | %s", mode_str, str_offs, limits_str); } else { BLI_snprintf(status_str, sizeof(status_str), - "%s: %d %% | %s", + "%s: %s | %s | %s | %s | %s", mode_str, - (int)(pso->percentage * 100.0f), - limits_str); + limits_str, + overshoot_str, + precision_str, + increments_str, + bone_vis_str); } - ED_area_status_text(pso->area, status_str); + ED_workspace_status_text(C, status_str); + ED_area_status_text(pso->area, ""); } /* common code for invoke() methods */ @@ -975,21 +1291,40 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, tPoseSlideOp *p WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL); /* header print */ - pose_slide_draw_status(pso); + pose_slide_draw_status(C, pso); /* add a modal handler for this operator */ WM_event_add_modal_handler(C, op); + + /* Hide Bone Overlay. */ + View3D *v3d = pso->area->spacedata.first; + pso->overlay_flag = v3d->overlay.flag; + v3d->overlay.flag |= V3D_OVERLAY_HIDE_BONES; + return OPERATOR_RUNNING_MODAL; } -/* calculate percentage based on position of mouse (we only use x-axis for now. - * since this is more convenient for users to do), and store new percentage value +/* Calculate percentage based on mouse movement, clamp or round to increments if + * enabled by the user. Store the new percentage value. */ static void pose_slide_mouse_update_percentage(tPoseSlideOp *pso, wmOperator *op, const wmEvent *event) { - pso->percentage = (event->x - pso->region->winrct.xmin) / ((float)pso->region->winx); + const float percentage_delta = (event->x - pso->last_cursor_x) / ((float)(SLIDE_PIXEL_DISTANCE)); + /* Reduced percentage delta in precision mode (shift held). */ + pso->raw_percentage += pso->precision ? (percentage_delta / 8) : percentage_delta; + pso->percentage = pso->raw_percentage; + pso->last_cursor_x = event->x; + + if (!pso->overshoot) { + pso->percentage = clamp_f(pso->percentage, 0, 1); + } + + if (pso->increments) { + pso->percentage = round(pso->percentage * 10) / 10; + } + RNA_float_set(op->ptr, "percentage", pso->percentage); } @@ -1056,9 +1391,13 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case EVT_PADENTER: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); + /* Depsgraph updates + redraws. Redraw needed to remove UI. */ + pose_slide_refresh(C, pso); + /* insert keyframes as required... */ pose_slide_autoKeyframe(C, pso); pose_slide_exit(op); @@ -1073,13 +1412,14 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) case RIGHTMOUSE: { if (event->val == KM_PRESS) { /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); ED_area_status_text(pso->area, NULL); WM_cursor_modal_restore(win); /* reset transforms back to original state */ pose_slide_reset(pso); - /* depsgraph updates + redraws */ + /* Depsgraph updates + redraws.*/ pose_slide_refresh(C, pso); /* clean up temp data */ @@ -1178,10 +1518,58 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } + /* Overshoot. */ + case EVT_EKEY: { + pso->overshoot = !pso->overshoot; + do_pose_update = true; + break; + } + + /* Precision mode. */ + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: { + pso->precision = true; + do_pose_update = true; + break; + } + + /* Increments mode. */ + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: { + pso->increments = true; + do_pose_update = true; + break; + } + + /* Toggle Bone visibility. */ + case EVT_HKEY: { + View3D *v3d = pso->area->spacedata.first; + v3d->overlay.flag ^= V3D_OVERLAY_HIDE_BONES; + } + default: /* Some other unhandled key... */ break; } } + /* Precision and stepping only active while button is held. */ + else if (event->val == KM_RELEASE) { + switch (event->type) { + case EVT_LEFTSHIFTKEY: + case EVT_RIGHTSHIFTKEY: { + pso->precision = false; + do_pose_update = true; + break; + } + case EVT_LEFTCTRLKEY: + case EVT_RIGHTCTRLKEY: { + pso->increments = false; + do_pose_update = true; + break; + } + default: + break; + } + } else { /* unhandled event - maybe it was some view manipulation? */ /* allow to pass through */ @@ -1193,8 +1581,10 @@ static int pose_slide_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Perform pose updates - in response to some user action * (e.g. pressing a key or moving the mouse). */ if (do_pose_update) { + pose_slide_mouse_update_percentage(pso, op, event); + /* update percentage indicator in header */ - pose_slide_draw_status(pso); + pose_slide_draw_status(C, pso); /* reset transforms (to avoid accumulation errors) */ pose_slide_reset(pso); @@ -1247,11 +1637,11 @@ static void pose_slide_opdef_properties(wmOperatorType *ot) PropertyRNA *prop; prop = RNA_def_float_factor(ot->srna, - "factor", + "percentage", 0.5f, 0.0f, 1.0f, - "Factor", + "Percentage", "Weighting factor for which keyframe is favored more", 0.0, 1.0); @@ -1310,6 +1700,8 @@ static int pose_slide_push_invoke(bContext *C, wmOperator *op, const wmEvent *ev pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1349,7 +1741,7 @@ void POSE_OT_push(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1370,6 +1762,8 @@ static int pose_slide_relax_invoke(bContext *C, wmOperator *op, const wmEvent *e pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1409,7 +1803,7 @@ void POSE_OT_relax(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1429,6 +1823,8 @@ static int pose_slide_push_rest_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1468,7 +1864,7 @@ void POSE_OT_push_rest(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1489,6 +1885,8 @@ static int pose_slide_relax_rest_invoke(bContext *C, wmOperator *op, const wmEve pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1528,7 +1926,7 @@ void POSE_OT_relax_rest(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); @@ -1549,6 +1947,8 @@ static int pose_slide_breakdown_invoke(bContext *C, wmOperator *op, const wmEven pso = op->customdata; + pso->last_cursor_x = event->x; + /* Initialize percentage so that it won't pop on first mouse move. */ pose_slide_mouse_update_percentage(pso, op, event); @@ -1588,7 +1988,7 @@ void POSE_OT_breakdown(wmOperatorType *ot) ot->poll = ED_operator_posemode; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; /* Properties */ pose_slide_opdef_properties(ot); diff --git a/source/blender/editors/geometry/geometry_attributes.c b/source/blender/editors/geometry/geometry_attributes.c index 12f6bb90677..b2ecee90a57 100644 --- a/source/blender/editors/geometry/geometry_attributes.c +++ b/source/blender/editors/geometry/geometry_attributes.c @@ -52,12 +52,16 @@ static const EnumPropertyItem *geometry_attribute_domain_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); - if (ob != NULL) { - return rna_enum_attribute_domain_itemf(ob->data, r_free); + if (ob == NULL) { + return DummyRNA_NULL_items; } - return DummyRNA_NULL_items; + return rna_enum_attribute_domain_itemf(ob->data, r_free); } static int geometry_attribute_add_exec(bContext *C, wmOperator *op) diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index ee344561b0e..b2d3a2e1576 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -614,8 +614,8 @@ static void snap_gizmo_draw(const bContext *C, wmGizmo *gz) GPU_line_width(1.0f); - const float *prev_point = snap_gizmo_snap_elements(snap_gizmo) & - SCE_SNAP_MODE_EDGE_PERPENDICULAR ? + const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) & + SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? snap_gizmo->prevpoint : NULL; @@ -627,12 +627,23 @@ static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; wmWindowManager *wm = CTX_wm_manager(C); + ARegion *region = CTX_wm_region(C); + + /* FIXME: this hack is to ignore drag events, otherwise drag events + * cause momentary snap gizmo re-positioning at the drag-start location, see: T87511. */ + if (wm && wm->winactive) { + const wmEvent *event = wm->winactive->eventstate; + int mval_compare[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + if (!equals_v2v2_int(mval_compare, mval)) { + return snap_gizmo->snap_elem ? 0 : -1; + } + } + if (!eventstate_has_changed(snap_gizmo, wm)) { /* Performance, do not update. */ return snap_gizmo->snap_elem ? 0 : -1; } - ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); const float mval_fl[2] = {UNPACK2(mval)}; short snap_elem = ED_gizmotypes_snap_3d_update( diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 47ae90acb74..f7ab72a8a43 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -36,6 +36,7 @@ set(SRC annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_blank.c gpencil_add_lineart.c gpencil_add_monkey.c gpencil_add_stroke.c diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 5c40bc8e418..c155587e95a 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -1548,7 +1548,7 @@ static void annotation_paint_initstroke(tGPsdata *p, if (p->gpl == NULL) { /* tag for annotations */ p->gpd->flag |= GP_DATA_ANNOTATIONS; - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true, false); if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); diff --git a/source/blender/editors/gpencil/gpencil_add_blank.c b/source/blender/editors/gpencil/gpencil_add_blank.c new file mode 100644 index 00000000000..3aa16e54597 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_blank.c @@ -0,0 +1,101 @@ +/* + * 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) 2017 Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup edgpencil + */ + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_geom.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_stroke_material(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + int index; + Material *ma = BKE_gpencil_object_material_ensure_by_name(bmain, ob, pct->name, &index); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + srgb_to_linearrgb_v4(ma->gp_style->stroke_rgba, ma->gp_style->stroke_rgba); + + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + srgb_to_linearrgb_v4(ma->gp_style->fill_rgba, ma->gp_style->fill_rgba); + + return index; +} + +/* ***************************************************************** */ +/* Stroke Geometry */ + +/* ***************************************************************** */ +/* Color Data */ + +static const ColorTemplate gp_stroke_material_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +/* ***************************************************************** */ +/* Blank API */ + +/* Add a Simple empty object with one layer and one color. */ +void ED_gpencil_create_blank(bContext *C, Object *ob, float UNUSED(mat[4][4])) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + bGPdata *gpd = (bGPdata *)ob->data; + + /* create colors */ + int color_black = gpencil_stroke_material(bmain, ob, &gp_stroke_material_black); + + /* set first color as active and in brushes */ + ob->actcol = color_black + 1; + + /* layers */ + bGPDlayer *layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); + + /* frames */ + BKE_gpencil_frame_addnew(layer, CFRA); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} diff --git a/source/blender/editors/gpencil/gpencil_add_lineart.c b/source/blender/editors/gpencil/gpencil_add_lineart.c index 6b28c6ec13e..ac0da0ad1db 100644 --- a/source/blender/editors/gpencil/gpencil_add_lineart.c +++ b/source/blender/editors/gpencil/gpencil_add_lineart.c @@ -96,7 +96,7 @@ void ED_gpencil_create_lineart(bContext *C, Object *ob) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ BKE_gpencil_frame_addnew(lines, 0); diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c index 4497d963c6d..d8734c4ae6b 100644 --- a/source/blender/editors/gpencil/gpencil_add_monkey.c +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -837,8 +837,8 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4]) /* layers */ /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ - bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false); - bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *Fills = BKE_gpencil_layer_addnew(gpd, "Fills", false, false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ /* NOTE: No need to check for existing, as this will take care of it for us */ diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c index 26237636526..e95496b51ee 100644 --- a/source/blender/editors/gpencil/gpencil_add_stroke.c +++ b/source/blender/editors/gpencil/gpencil_add_stroke.c @@ -225,8 +225,8 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4]) ob->actcol = color_black + 1; /* layers */ - bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); - bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + bGPDlayer *colors = BKE_gpencil_layer_addnew(gpd, "Colors", false, false); + bGPDlayer *lines = BKE_gpencil_layer_addnew(gpd, "Lines", true, false); /* frames */ bGPDframe *frame_color = BKE_gpencil_frame_addnew(colors, CFRA); diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ac75ae44c8a..8ab413e907c 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -1857,7 +1857,7 @@ static int image_to_gpencil_exec(bContext *C, wmOperator *op) /* Add layer and frame. */ bGPdata *gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, "Image Layer", true, false); bGPDframe *gpf = BKE_gpencil_frame_addnew(gpl, CFRA); done = BKE_gpencil_from_image(sima, gpd, gpf, size, is_mask); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c93fcb9eb8c..d9a807d17ab 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -129,7 +129,7 @@ static int gpencil_data_add_exec(bContext *C, wmOperator *op) gpd->flag |= GP_DATA_ANNOTATIONS; /* add new layer (i.e. a "note") */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -231,7 +231,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) /* mark as annotation */ (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true, false); gpd = *gpd_ptr; } else { @@ -239,7 +239,7 @@ static int gpencil_layer_add_exec(bContext *C, wmOperator *op) Object *ob = CTX_data_active_object(C); if ((ob != NULL) && (ob->type == OB_GPENCIL)) { gpd = (bGPdata *)ob->data; - bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + bGPDlayer *gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); /* Add a new frame to make it visible in Dopesheet. */ if (gpl != NULL) { gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); @@ -524,7 +524,6 @@ enum { static bool gpencil_layer_duplicate_object_poll(bContext *C) { - ViewLayer *view_layer = CTX_data_view_layer(C); Object *ob = CTX_data_active_object(C); if ((ob == NULL) || (ob->type != OB_GPENCIL)) { return false; @@ -537,90 +536,75 @@ static bool gpencil_layer_duplicate_object_poll(bContext *C) return false; } - /* check there are more grease pencil objects */ - LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if ((base->object != ob) && (base->object->type == OB_GPENCIL)) { - return true; - } - } - - return false; + return true; } static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - char name[MAX_ID_NAME - 2]; - RNA_string_get(op->ptr, "object", name); - - if (name[0] == '\0') { - return OPERATOR_CANCELLED; - } - - Object *ob_dst = (Object *)BKE_scene_object_find_by_name(scene, name); - - int mode = RNA_enum_get(op->ptr, "mode"); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + const int mode = RNA_enum_get(op->ptr, "mode"); Object *ob_src = CTX_data_active_object(C); bGPdata *gpd_src = (bGPdata *)ob_src->data; - bGPDlayer *gpl_src = BKE_gpencil_layer_active_get(gpd_src); - - /* Sanity checks. */ - if (ELEM(NULL, gpd_src, gpl_src, ob_dst)) { - return OPERATOR_CANCELLED; - } - /* Cannot copy itself and check destination type. */ - if ((ob_src == ob_dst) || (ob_dst->type != OB_GPENCIL)) { - return OPERATOR_CANCELLED; - } - - bGPdata *gpd_dst = (bGPdata *)ob_dst->data; + bGPDlayer *gpl_active = BKE_gpencil_layer_active_get(gpd_src); - /* Create new layer. */ - bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true); - /* Need to copy some variables (not all). */ - gpl_dst->onion_flag = gpl_src->onion_flag; - gpl_dst->thickness = gpl_src->thickness; - gpl_dst->line_change = gpl_src->line_change; - copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); - gpl_dst->opacity = gpl_src->opacity; - - /* Create all frames. */ - LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { - - if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { continue; } + bGPdata *gpd_dst = (bGPdata *)ob->data; + LISTBASE_FOREACH_BACKWARD (bGPDlayer *, gpl_src, &gpd_src->layers) { + if ((only_active) && (gpl_src != gpl_active)) { + continue; + } + /* Create new layer (adding at head of the list). */ + bGPDlayer *gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl_src->info, true, true); + /* Need to copy some variables (not all). */ + gpl_dst->onion_flag = gpl_src->onion_flag; + gpl_dst->thickness = gpl_src->thickness; + gpl_dst->line_change = gpl_src->line_change; + copy_v4_v4(gpl_dst->tintcolor, gpl_src->tintcolor); + gpl_dst->opacity = gpl_src->opacity; + + /* Create all frames. */ + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { + + if ((mode == GP_LAYER_COPY_OBJECT_ACT_FRAME) && (gpf_src != gpl_src->actframe)) { + continue; + } - /* Create new frame. */ - bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); + /* Create new frame. */ + bGPDframe *gpf_dst = BKE_gpencil_frame_addnew(gpl_dst, gpf_src->framenum); - /* Copy strokes. */ - LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { + /* Copy strokes. */ + LISTBASE_FOREACH (bGPDstroke *, gps_src, &gpf_src->strokes) { - /* Make copy of source stroke. */ - bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); + /* Make copy of source stroke. */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps_src, true, true); - /* Check if material is in destination object, - * otherwise add the slot with the material. */ - Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); - if (ma_src != NULL) { - int idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma_src); + /* Check if material is in destination object, + * otherwise add the slot with the material. */ + Material *ma_src = BKE_object_material_get(ob_src, gps_src->mat_nr + 1); + if (ma_src != NULL) { + int idx = BKE_gpencil_object_material_ensure(bmain, ob, ma_src); - /* Reassign the stroke material to the right slot in destination object. */ - gps_dst->mat_nr = idx; - } + /* Reassign the stroke material to the right slot in destination object. */ + gps_dst->mat_nr = idx; + } - /* Add new stroke to frame. */ - BLI_addtail(&gpf_dst->strokes, gps_dst); + /* Add new stroke to frame. */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + } + } } + /* notifiers */ + DEG_id_tag_update(&gpd_dst->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } + CTX_DATA_END; - /* notifiers */ - DEG_id_tag_update(&gpd_dst->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update(&ob_dst->id, ID_RECALC_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -628,6 +612,8 @@ static int gpencil_layer_duplicate_object_exec(bContext *C, wmOperator *op) void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) { + PropertyRNA *prop; + static const EnumPropertyItem copy_mode[] = { {GP_LAYER_COPY_OBJECT_ALL_FRAME, "ALL", 0, "All Frames", ""}, {GP_LAYER_COPY_OBJECT_ACT_FRAME, "ACTIVE", 0, "Active Frame", ""}, @@ -637,7 +623,7 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* identifiers */ ot->name = "Duplicate Layer to New Object"; ot->idname = "GPENCIL_OT_layer_duplicate_object"; - ot->description = "Make a copy of the active Grease Pencil layer to new object"; + ot->description = "Make a copy of the active Grease Pencil layer to selected object"; /* callbacks */ ot->exec = gpencil_layer_duplicate_object_exec; @@ -646,11 +632,14 @@ void GPENCIL_OT_layer_duplicate_object(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - ot->prop = RNA_def_string( - ot->srna, "object", NULL, MAX_ID_NAME - 2, "Object", "Name of the destination object"); - RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + ot->prop = RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); - RNA_def_enum(ot->srna, "mode", copy_mode, GP_LAYER_COPY_OBJECT_ALL_FRAME, "Mode", ""); + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Copy only active Layer, uncheck to append all layers"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ********************* Duplicate Frame ************************** */ @@ -1443,7 +1432,7 @@ static int gpencil_layer_change_exec(bContext *C, wmOperator *op) /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else { /* Try to get layer */ @@ -3589,6 +3578,79 @@ void GPENCIL_OT_set_active_material(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************* Append Materials in a new object ************************** */ +static bool gpencil_materials_copy_to_object_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_GPENCIL)) { + return false; + } + short *totcolp = BKE_object_material_len_p(ob); + if (*totcolp == 0) { + return false; + } + + return true; +} + +static int gpencil_materials_copy_to_object_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + const bool only_active = RNA_boolean_get(op->ptr, "only_active"); + Object *ob_src = CTX_data_active_object(C); + Material *ma_active = BKE_gpencil_material(ob_src, ob_src->actcol); + + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { + if ((ob == ob_src) || (ob->type != OB_GPENCIL)) { + continue; + } + /* Duplicate materials. */ + for (int i = 0; i < ob_src->totcol; i++) { + Material *ma_src = BKE_object_material_get(ob_src, i + 1); + if (only_active && ma_src != ma_active) { + continue; + } + + if (ma_src != NULL) { + BKE_gpencil_object_material_ensure(bmain, ob, ma_src); + } + } + + /* notifiers */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + } + CTX_DATA_END; + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_materials_copy_to_object(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Copy Materials to Selected Object"; + ot->idname = "GPENCIL_OT_materials_copy_to_object"; + ot->description = "Append Materials of the active Grease Pencil to other object"; + + /* callbacks */ + ot->exec = gpencil_materials_copy_to_object_exec; + ot->poll = gpencil_materials_copy_to_object_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + prop = RNA_def_boolean(ot->srna, + "only_active", + true, + "Only Active", + "Append only active material, uncheck to append all materials"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + /* Parent GPencil object to Lattice */ bool ED_gpencil_add_lattice_modifier(const bContext *C, ReportList *reports, diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index a2a69d94ce9..e65afa1abff 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1677,7 +1677,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op) if (gpl == NULL) { /* no active layer - let's just create one */ - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true); + gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false); } else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) { BKE_report( @@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ + /* If autolock enabled, disabled now. */ if (use_autolock) { gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } @@ -1840,7 +1835,7 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) } else { /* Create a new layer. */ - target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true); + target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true, false); } if (target_layer == NULL) { @@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) { + /* Extract all strokes to move to this layer. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) { + /* Skip if this is the layer we're moving strokes to. */ + if (gpl_src == target_layer) { continue; } + bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe; + for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) { + if ((gpf_src == gpl_src->actframe) || + ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf_src == NULL) { + continue; + } - /* make copies of selected strokes, and deselect these once we're done */ - LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gpsn = NULL; + BLI_listbase_clear(&strokes); + for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) { + continue; + } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf_src->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + /* Paste them all in one go. */ + if (strokes.first) { + bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get( + target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW); - /* Check if the color is editable. */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { - continue; + BLI_movelisttolist(&gpf_dst->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } } - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - - /* if new layer and autolock, lock old layer */ + /* If new layer and autolock, lock old layer. */ if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; + gpl_src->flag |= GP_LAYER_LOCKED; } } CTX_DATA_END; - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - /* back autolock status */ if (use_autolock) { gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; @@ -3814,13 +3815,13 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) } changed = true; - /* If not multi-edit, exit loop. */ - if (!is_multiedit) { - break; - } } } } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } } } CTX_DATA_END; @@ -4543,6 +4544,9 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); + const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src); + /* sanity checks */ if (ELEM(NULL, gpd_src)) { return OPERATOR_CANCELLED; @@ -4553,8 +4557,22 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); - const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src); + /* Cancel if nothing selected. */ + if (ELEM(mode, GP_SEPARATE_POINT, GP_SEPARATE_STROKE)) { + bool has_selected = false; + CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { + if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) { + has_selected = true; + break; + } + } + CTX_DATA_END; + + if (!has_selected) { + BKE_report(op->reports, RPT_ERROR, "Nothing selected"); + return OPERATOR_CANCELLED; + } + } /* Create a new object. */ /* Take into account user preferences for duplicating actions. */ @@ -4599,7 +4617,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) if (gps->flag & GP_STROKE_SELECT) { /* add layer if not created before */ if (gpl_dst == NULL) { - gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false); } /* add frame if not created before */ diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 3f86e5474c5..f74e211dd65 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -1029,9 +1029,15 @@ static void gpencil_invert_image(tGPDfill *tgpf) /* Red->Green */ else if (color[0] == 1.0f) { set_pixel(ibuf, v, fill_col[1]); - /* Add thickness of 2 pixels to avoid too thin lines. */ - int offset = (v % ibuf->x < center) ? 1 : -1; - set_pixel(ibuf, v + offset, fill_col[1]); + /* Add thickness of 2 pixels to avoid too thin lines, but avoid extremes of the pixel line. + */ + int row = v / ibuf->x; + int lowpix = row * ibuf->x; + int highpix = lowpix + ibuf->x - 1; + if ((v > lowpix) && (v < highpix)) { + int offset = (v % ibuf->x < center) ? 1 : -1; + set_pixel(ibuf, v + offset, fill_col[1]); + } } else { /* Set to Transparent. */ @@ -1241,6 +1247,7 @@ static bool dilate_shape(ImBuf *ibuf) static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) { ImBuf *ibuf; + Brush *brush = tgpf->brush; float rgba[4]; void *lock; int v[2]; @@ -1273,7 +1280,9 @@ static void gpencil_get_outline_points(tGPDfill *tgpf, const bool dilate) /* Dilate. */ if (dilate) { - dilate_shape(ibuf); + for (int i = 0; i < brush->gpencil_settings->dilate_pixels; i++) { + dilate_shape(ibuf); + } } for (int idx = imagesize - 1; idx != 0; idx--) { @@ -1680,7 +1689,7 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op) tgpf->gpd = gpd; tgpf->gpl = BKE_gpencil_layer_active_get(gpd); if (tgpf->gpl == NULL) { - tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true); + tgpf->gpl = BKE_gpencil_layer_addnew(tgpf->gpd, DATA_("GP_Layer"), true, false); } tgpf->lock_axis = ts->gp_sculpt.lock_axis; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 09200125cb7..9f48c81c8f1 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -538,6 +538,7 @@ void GPENCIL_OT_material_lock_unused(struct wmOperatorType *ot); void GPENCIL_OT_material_select(struct wmOperatorType *ot); void GPENCIL_OT_material_set(struct wmOperatorType *ot); void GPENCIL_OT_set_active_material(struct wmOperatorType *ot); +void GPENCIL_OT_materials_copy_to_object(struct wmOperatorType *ot); /* convert old 2.7 files to 2.8 */ void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot); diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 7d454eb3be1..698d784c3b0 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -651,6 +651,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_material_to_vertex_color); WM_operatortype_append(GPENCIL_OT_extract_palette_vertex); + WM_operatortype_append(GPENCIL_OT_materials_copy_to_object); WM_operatortype_append(GPENCIL_OT_transform_fill); WM_operatortype_append(GPENCIL_OT_reset_transform_fill); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index d072d8a35df..993065a3a70 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -1332,7 +1332,7 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) /* only erase stroke points that are visible */ static bool gpencil_stroke_eraser_is_occluded( - tGPsdata *p, bGPDlayer *gpl, const bGPDspoint *pt, const int x, const int y) + tGPsdata *p, bGPDlayer *gpl, bGPDspoint *pt, const int x, const int y) { Object *obact = (Object *)p->ownerPtr.data; Brush *brush = p->brush; @@ -1364,7 +1364,11 @@ static bool gpencil_stroke_eraser_is_occluded( mul_v3_m4v3(fpt, diff_mat, &pt->x); const float depth_pt = view3d_point_depth(rv3d, fpt); + /* Checked occlusion flag. */ + pt->flag |= GP_SPOINT_TEMP_TAG; if (depth_pt > depth_mval) { + /* Is occluded. */ + pt->flag |= GP_SPOINT_TEMP_TAG2; return true; } } @@ -1540,6 +1544,10 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, for (i = 0; i < gps->totpoints; i++) { bGPDspoint *pt = &gps->points[i]; pt->flag &= ~GP_SPOINT_TAG; + /* Occlusion already checked. */ + pt->flag &= ~GP_SPOINT_TEMP_TAG; + /* Point is occluded. */ + pt->flag &= ~GP_SPOINT_TEMP_TAG2; } /* First Pass: Loop over the points in the stroke @@ -1585,9 +1593,23 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, * - this assumes that linewidth is irrelevant */ if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { - if ((gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]) == false) || - (gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]) == false) || - (gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]) == false)) { + + bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true; + is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ? + ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + if (is_occluded_pt0) { + is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt1, pc1[0], pc1[1]); + if (is_occluded_pt1) { + is_occluded_pt2 = ((pt2->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt2->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt2, pc2[0], pc2[1]); + } + } + + if (!is_occluded_pt0 || !is_occluded_pt1 || !is_occluded_pt2) { /* Point is affected: */ /* Adjust thickness * - Influence of eraser falls off with distance from the middle of the eraser @@ -2120,7 +2142,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_active_get(p->gpd); if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true, false); changed = true; if (p->custom_color[3]) { copy_v3_v3(p->gpl->color, p->custom_color); @@ -3677,14 +3699,6 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) * is essential for ensuring that they can quickly return to that view */ } - else if ((event->type == EVT_BKEY) && (event->val == KM_RELEASE)) { - /* Add Blank Frame - * - Since this operator is non-modal, we can just call it here, and keep going... - * - This operator is especially useful when animating - */ - WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); - estate = OPERATOR_RUNNING_MODAL; - } else if ((!ELEM(p->paintmode, GP_PAINTMODE_ERASER, GP_PAINTMODE_SET_CP))) { gpencil_guide_event_handling(C, op, event, p); estate = OPERATOR_RUNNING_MODAL; diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 3d20e32ed49..1b0a40b1be1 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -314,7 +314,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) /* if layer doesn't exist, create a new one */ if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true, false); } tgpi->gpl = gpl; diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index d2e5fa3db32..cd75f9313ad 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -207,7 +207,7 @@ static void trace_initialize_job_data(TraceJob *trace_job) trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data; trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd); if (trace_job->gpl == NULL) { - trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true); + trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true, false); } } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 8d42024a518..04764587ebe 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -543,6 +543,38 @@ bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, } /* ******************************************************** */ +/* Selection Validity Testing */ + +bool ED_gpencil_frame_has_selected_stroke(const bGPDframe *gpf) +{ + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + if (gps->flag & GP_STROKE_SELECT) { + return true; + } + } + + return false; +} + +bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit) +{ + bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (ED_gpencil_frame_has_selected_stroke(gpf)) { + return true; + } + } + /* If not multiedit, exit loop. */ + if (!is_multiedit) { + break; + } + } + + return false; +} + +/* ******************************************************** */ /* Stroke Validity Testing */ /* Check whether given stroke can be edited given the supplied context */ @@ -1244,7 +1276,6 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, } else { /* Geometry - Snap to surfaces of visible geometry */ - float ray_start[3]; float ray_normal[3]; /* magic value for initial depth copied from the default * value of Python's Scene.ray_cast function @@ -1253,13 +1284,14 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, float location[3] = {0.0f, 0.0f, 0.0f}; float normal[3] = {0.0f, 0.0f, 0.0f}; - ED_view3d_win_to_ray(region, xy, &ray_start[0], &ray_normal[0]); + ED_view3d_win_to_vector(region, xy, &ray_normal[0]); + BLI_assert(gps->flag & GP_STROKE_3DSPACE); if (ED_transform_snap_object_project_ray(sctx, depsgraph, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, - &ray_start[0], + &pt2.x, &ray_normal[0], &depth, &location[0], diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 983ae94b637..8118e3c6c69 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -110,7 +110,7 @@ struct FileAssetSelectParams *ED_fileselect_get_asset_params(const struct SpaceF void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile); void ED_fileselect_params_to_userdef(struct SpaceFile *sfile, - const int temp_win_size[], + const int temp_win_size[2], const bool is_maximized); void ED_fileselect_init_layout(struct SpaceFile *sfile, struct ARegion *region); diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index e9ac21f60cf..bad080e1609 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -144,6 +144,8 @@ bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr); bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra); /* ----------- Stroke Editing Utilities ---------------- */ +bool ED_gpencil_frame_has_selected_stroke(const struct bGPDframe *gpf); +bool ED_gpencil_layer_has_selected_stroke(const struct bGPDlayer *gpl, const bool is_multiedit); bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *area, const struct bGPDstroke *gps); bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps); @@ -249,6 +251,7 @@ void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); /* ----------- Add Primitive Utilities -------------- */ +void ED_gpencil_create_blank(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_monkey(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_stroke(struct bContext *C, struct Object *ob, float mat[4][4]); void ED_gpencil_create_lineart(struct bContext *C, struct Object *ob); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 8279268bfd0..6f88ab4253a 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -247,6 +247,7 @@ void ED_object_texture_paint_mode_enter(struct bContext *C); void ED_object_texture_paint_mode_exit_ex(struct Main *bmain, struct Scene *scene, Object *ob); void ED_object_texture_paint_mode_exit(struct bContext *C); +bool ED_object_particle_edit_mode_supported(const Object *ob); void ED_object_particle_edit_mode_enter_ex(struct Depsgraph *depsgraph, struct Scene *scene, Object *ob); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b3205acb8ee..bdd7ec571dc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -162,7 +162,7 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area); void ED_area_tag_redraw_regiontype(ScrArea *area, int type); void ED_area_tag_refresh(ScrArea *area); void ED_area_do_refresh(struct bContext *C, ScrArea *area); -struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[]); +struct AZone *ED_area_azones_update(ScrArea *area, const int mouse_xy[2]); void ED_area_status_text(ScrArea *area, const char *str); void ED_area_newspace(struct bContext *C, ScrArea *area, int type, const bool skip_region_exit); void ED_area_prevspace(struct bContext *C, ScrArea *area); @@ -200,8 +200,6 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /* screens */ void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); -void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); -void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win); void ED_screen_ensure_updated(struct wmWindowManager *wm, struct wmWindow *win, @@ -304,6 +302,7 @@ void ED_operatortypes_workspace(void); /* operators; context poll callbacks */ bool ED_operator_screenactive(struct bContext *C); +bool ED_operator_screenactive_nobackground(struct bContext *C); bool ED_operator_screen_mainwinactive(struct bContext *C); bool ED_operator_areaactive(struct bContext *C); bool ED_operator_regionactive(struct bContext *C); @@ -449,10 +448,10 @@ enum { }; /* SCREEN_OT_space_context_cycle direction */ -enum { +typedef enum eScreenCycle { SPACE_CONTEXT_CYCLE_PREV, SPACE_CONTEXT_CYCLE_NEXT, -}; +} eScreenCycle; #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 499f28beb60..66ec57c8a31 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -454,7 +454,7 @@ bool ED_view3d_calc_render_border(const struct Scene *scene, struct ARegion *region, struct rcti *rect); -void ED_view3d_clipping_calc_from_boundbox(float clip[6][4], +void ED_view3d_clipping_calc_from_boundbox(float clip[4][4], const struct BoundBox *clipbb, const bool is_flip); void ED_view3d_clipping_calc(struct BoundBox *bb, @@ -690,7 +690,7 @@ float ED_view3d_grid_scale(const struct Scene *scene, void ED_view3d_grid_steps(const struct Scene *scene, struct View3D *v3d, struct RegionView3D *rv3d, - float *r_grid_steps); + float r_grid_steps[8]); float ED_view3d_grid_view_scale(struct Scene *scene, struct View3D *v3d, struct ARegion *region, diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index e0d9971529f..338b12f7985 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -2137,7 +2137,7 @@ void uiTemplateComponentMenu(uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name); -void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float *color); +void uiTemplateNodeSocket(uiLayout *layout, struct bContext *C, float color[4]); void uiTemplateCacheFile(uiLayout *layout, const struct bContext *C, struct PointerRNA *ptr, @@ -2405,9 +2405,12 @@ void uiItemS_ex(uiLayout *layout, float factor); void uiItemSpacer(uiLayout *layout); void uiItemPopoverPanel_ptr( - uiLayout *layout, struct bContext *C, struct PanelType *pt, const char *name, int icon); -void uiItemPopoverPanel( - uiLayout *layout, struct bContext *C, const char *panel_type, const char *name, int icon); + uiLayout *layout, const struct bContext *C, struct PanelType *pt, const char *name, int icon); +void uiItemPopoverPanel(uiLayout *layout, + const struct bContext *C, + const char *panel_type, + const char *name, + int icon); void uiItemPopoverPanelFromGroup(uiLayout *layout, struct bContext *C, int space_id, diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 1820c2f133c..c99c7f681b3 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -451,7 +451,7 @@ int UI_ThemeMenuShadowWidth(void); /* only for buttons in theme editor! */ const unsigned char *UI_ThemeGetColorPtr(struct bTheme *btheme, int spacetype, int colorid); -void UI_make_axis_color(const unsigned char *src_col, unsigned char *dst_col, const char axis); +void UI_make_axis_color(const unsigned char src_col[3], unsigned char dst_col[3], const char axis); #ifdef __cplusplus } diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 90d604b3190..a31efefd99c 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -2327,6 +2327,14 @@ bool ui_but_is_float(const uiBut *but) return false; } +PropertyScaleType ui_but_scale_type(const uiBut *but) +{ + if (but->rnaprop) { + return RNA_property_ui_scale(but->rnaprop); + } + return PROP_SCALE_LINEAR; +} + bool ui_but_is_bool(const uiBut *but) { if (ELEM(but->type, diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 178f663ff58..b52bfc81b7a 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -130,14 +130,19 @@ void eyedropper_draw_cursor_text_region(const struct bContext *C, const char *name) { wmWindow *win = CTX_wm_window(C); - const int x = win->eventstate->x - region->winrct.xmin; - const int y = win->eventstate->y - region->winrct.ymin; + const int x = win->eventstate->x; + const int y = win->eventstate->y; if ((name[0] == '\0') || (BLI_rcti_isect_pt(®ion->winrct, x, y) == false)) { return; } - eyedropper_draw_cursor_text_ex(x, y, name); + const int mval[2] = { + x - region->winrct.xmin, + y - region->winrct.ymin, + }; + + eyedropper_draw_cursor_text_ex(mval[0], mval[1], name); } /** diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 4cbf5fca49a..282d470c7ea 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -126,6 +126,24 @@ #define UI_MAX_PASSWORD_STR 128 /** + * This is a lower limit on the soft minimum of the range. + * Usually the derived lower limit from the visible precision is higher, + * so this number is the backup minimum. + * + * Logarithmic scale does not work with a minimum value of zero, + * but we want to support it anyway. It is set to 0.5e... for + * correct rounding since when the tweaked value is lower than + * the log minimum (lower limit), it will snap to 0. + */ +#define UI_PROP_SCALE_LOG_MIN 0.5e-8f +/** + * This constant defines an offset for the precision change in + * snap rounding, when going to higher values. It is set to + * `0.5 - log10(3) = 0.03` to make the switch at `0.3` values. + */ +#define UI_PROP_SCALE_LOG_SNAP_OFFSET 0.03f + +/** * When #USER_CONTINUOUS_MOUSE is disabled or tablet input is used, * Use this as a maximum soft range for mapping cursor motion to the value. * Otherwise min/max of #FLT_MAX, #INT_MAX cause small adjustments to jump to large numbers. @@ -477,6 +495,7 @@ static bool ui_do_but_extra_operator_icon(bContext *C, static void ui_do_but_extra_operator_icons_mousemove(uiBut *but, uiHandleButtonData *data, const wmEvent *event); +static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data); #ifdef USE_DRAG_MULTINUM static void ui_multibut_restore(bContext *C, uiHandleButtonData *data, uiBlock *block); @@ -1114,16 +1133,17 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data) { if (data->str) { - if (ui_but_string_set(C, but, data->str)) { - data->value = ui_but_value_get(but); - } - else { + double value; + /* Check if the string value is a number and cancel if it's equal to the startvalue. */ + if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) { data->cancel = true; return; } - /* If the value entered is the exact same, do not trigger an update. */ - if (data->value == data->startvalue) { + if (ui_but_string_set(C, but, data->str)) { + data->value = ui_but_value_get(but); + } + else { data->cancel = true; return; } @@ -3360,6 +3380,8 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) if (is_num_but) { BLI_assert(data->is_str_dynamic == false); ui_but_convert_to_unit_alt_name(but, data->str, data->maxlen); + + ui_numedit_begin_set_values(but, data); } /* won't change from now on */ @@ -3896,6 +3918,14 @@ static void ui_do_but_textedit_select( /** \name Button Number Editing (various types) * \{ */ +static void ui_numedit_begin_set_values(uiBut *but, uiHandleButtonData *data) +{ + data->startvalue = ui_but_value_get(but); + data->origvalue = data->startvalue; + data->value = data->origvalue; + but->editval = &data->value; +} + static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) { if (but->type == UI_BTYPE_CURVE) { @@ -3921,19 +3951,21 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) but->editvec = data->vec; } else { - float softrange, softmin, softmax; + ui_numedit_begin_set_values(but, data); - data->startvalue = ui_but_value_get(but); - data->origvalue = data->startvalue; - data->value = data->origvalue; - but->editval = &data->value; + float softmin = but->softmin; + float softmax = but->softmax; + float softrange = softmax - softmin; + const PropertyScaleType scale_type = ui_but_scale_type(but); - softmin = but->softmin; - softmax = but->softmax; - softrange = softmax - softmin; + float log_min = (scale_type == PROP_SCALE_LOG) ? max_ff(softmin, UI_PROP_SCALE_LOG_MIN) : 0.0f; if ((but->type == UI_BTYPE_NUM) && (ui_but_is_cursor_warp(but) == false)) { uiButNumber *number_but = (uiButNumber *)but; + + if (scale_type == PROP_SCALE_LOG) { + log_min = max_ff(log_min, powf(10, -number_but->precision) * 0.5f); + } /* Use a minimum so we have a predictable range, * otherwise some float buttons get a large range. */ const float value_step_float_min = 0.1f; @@ -3982,7 +4014,31 @@ static void ui_numedit_begin(uiBut *but, uiHandleButtonData *data) } } - data->dragfstart = (softrange == 0.0f) ? 0.0f : ((float)data->value - softmin) / softrange; + if (softrange == 0.0f) { + data->dragfstart = 0.0f; + } + else { + switch (scale_type) { + case PROP_SCALE_LINEAR: { + data->dragfstart = ((float)data->value - softmin) / softrange; + break; + } + case PROP_SCALE_LOG: { + BLI_assert(log_min != 0.0f); + const float base = softmax / log_min; + data->dragfstart = logf((float)data->value / log_min) / logf(base); + break; + } + case PROP_SCALE_CUBIC: { + const float cubic_min = cube_f(softmin); + const float cubic_max = cube_f(softmax); + const float cubic_range = cubic_max - cubic_min; + const float f = ((float)data->value - softmin) * cubic_range / softrange + cubic_min; + data->dragfstart = (cbrtf(f) - softmin) / softrange; + break; + } + } + } data->dragf = data->dragfstart; data->drag_map_soft_min = softmin; @@ -4700,6 +4756,7 @@ static float ui_numedit_apply_snapf( /* pass */ } else { + const PropertyScaleType scale_type = ui_but_scale_type(but); float softrange = softmax - softmin; float fac = 1.0f; @@ -4737,31 +4794,30 @@ static float ui_numedit_apply_snapf( } } - if (snap == SNAP_ON) { - if (softrange < 2.10f) { - tempf = roundf(tempf * 10.0f) * 0.1f; - } - else if (softrange < 21.0f) { - tempf = roundf(tempf); - } - else { - tempf = roundf(tempf * 0.1f) * 10.0f; - } - } - else if (snap == SNAP_ON_SMALL) { - if (softrange < 2.10f) { - tempf = roundf(tempf * 100.0f) * 0.01f; - } - else if (softrange < 21.0f) { - tempf = roundf(tempf * 10.0f) * 0.1f; + BLI_assert(ELEM(snap, SNAP_ON, SNAP_ON_SMALL)); + switch (scale_type) { + case PROP_SCALE_LINEAR: + case PROP_SCALE_CUBIC: { + const float snap_fac = (snap == SNAP_ON_SMALL ? 0.1f : 1.0f); + if (softrange < 2.10f) { + tempf = roundf(tempf * 10.0f / snap_fac) * 0.1f * snap_fac; + } + else if (softrange < 21.0f) { + tempf = roundf(tempf / snap_fac) * snap_fac; + } + else { + tempf = roundf(tempf * 0.1f / snap_fac) * 10.0f * snap_fac; + } + break; } - else { - tempf = roundf(tempf); + case PROP_SCALE_LOG: { + const float snap_fac = powf(10.0f, + roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - + (snap == SNAP_ON_SMALL ? 2.0f : 1.0f)); + tempf = roundf(tempf / snap_fac) * snap_fac; + break; } } - else { - BLI_assert(0); - } if (fac != 1.0f) { tempf *= fac; @@ -4806,6 +4862,7 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, int lvalue, temp; bool changed = false; const bool is_float = ui_but_is_float(but); + const PropertyScaleType scale_type = ui_but_scale_type(but); /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */ if ((is_motion || data->draglock) && (ui_but_dragedit_update_mval(data, mx) == false)) { @@ -4817,21 +4874,74 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, const float softmax = but->softmax; const float softrange = softmax - softmin; + const float log_min = (scale_type == PROP_SCALE_LOG) ? + max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), + powf(10, -number_but->precision) * 0.5f) : + 0; + /* Mouse location isn't screen clamped to the screen so use a linear mapping * 2px == 1-int, or 1px == 1-ClickStep */ if (is_float) { fac *= 0.01f * number_but->step_size; - tempf = (float)data->startvalue + ((float)(mx - data->dragstartx) * fac); + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = (float)data->startvalue + (float)(mx - data->dragstartx) * fac; + break; + } + case PROP_SCALE_LOG: { + const float startvalue = max_ff((float)data->startvalue, log_min); + tempf = expf((float)(mx - data->dragstartx) * fac) * startvalue; + if (tempf <= log_min) { + tempf = 0.0f; + } + break; + } + case PROP_SCALE_CUBIC: { + tempf = cbrtf((float)data->startvalue) + (float)(mx - data->dragstartx) * fac; + tempf *= tempf * tempf; + break; + } + } + tempf = ui_numedit_apply_snapf(but, tempf, softmin, softmax, snap); #if 1 /* fake moving the click start, nicer for dragging back after passing the limit */ - if (tempf < softmin) { - data->dragstartx -= (softmin - tempf) / fac; - tempf = softmin; - } - else if (tempf > softmax) { - data->dragstartx += (tempf - softmax) / fac; - tempf = softmax; + switch (scale_type) { + case PROP_SCALE_LINEAR: { + if (tempf < softmin) { + data->dragstartx -= (softmin - tempf) / fac; + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx -= (softmax - tempf) / fac; + tempf = softmax; + } + break; + } + case PROP_SCALE_LOG: { + if (tempf < log_min) { + data->dragstartx -= logf(log_min / (float)data->startvalue) / fac - + (float)(mx - data->dragstartx); + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx -= logf(softmax / (float)data->startvalue) / fac - + (float)(mx - data->dragstartx); + tempf = softmax; + } + break; + } + case PROP_SCALE_CUBIC: { + if (tempf < softmin) { + data->dragstartx = mx - (int)((cbrtf(softmin) - cbrtf((float)data->startvalue)) / fac); + tempf = softmin; + } + else if (tempf > softmax) { + data->dragstartx = mx - (int)((cbrtf(softmax) - cbrtf((float)data->startvalue)) / fac); + tempf = softmax; + } + break; + } } #else CLAMP(tempf, softmin, softmax); @@ -4938,7 +5048,31 @@ static bool ui_numedit_but_NUM(uiButNumber *number_but, } data->draglastx = mx; - tempf = (softmin + data->dragf * softrange); + + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = (softmin + data->dragf * softrange); + break; + } + case PROP_SCALE_LOG: { + const float log_min = max_ff(max_ff(softmin, UI_PROP_SCALE_LOG_MIN), + powf(10.0f, -number_but->precision) * 0.5f); + const float base = softmax / log_min; + tempf = powf(base, data->dragf) * log_min; + if (tempf <= log_min) { + tempf = 0.0f; + } + break; + } + case PROP_SCALE_CUBIC: { + tempf = (softmin + data->dragf * softrange); + tempf *= tempf * tempf; + float cubic_min = softmin * softmin * softmin; + float cubic_max = softmax * softmax * softmax; + tempf = (tempf - cubic_min) / (cubic_max - cubic_min) * softrange + softmin; + break; + } + } if (!is_float) { temp = round_fl_to_int(tempf); @@ -5185,9 +5319,19 @@ static int ui_do_but_NUM( else { /* Float Value. */ if (but->drawflag & (UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT)) { + const PropertyScaleType scale_type = ui_but_scale_type(but); + button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); - const double value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE; + double value_step; + if (scale_type == PROP_SCALE_LOG) { + value_step = powf(10.0f, + (roundf(log10f(data->value) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f) + + log10f(number_but->step_size)); + } + else { + value_step = (double)number_but->step_size * UI_PRECISION_FLOAT_SCALE; + } BLI_assert(value_step > 0.0f); const double value_test = (but->drawflag & UI_BUT_ACTIVE_LEFT) ? (double)max_ff(but->softmin, @@ -5235,6 +5379,8 @@ static bool ui_numedit_but_SLI(uiBut *but, return changed; } + const PropertyScaleType scale_type = ui_but_scale_type(but); + softmin = but->softmin; softmax = but->softmax; softrange = softmax - softmin; @@ -5276,7 +5422,24 @@ static bool ui_numedit_but_SLI(uiBut *but, #endif /* done correcting mouse */ - tempf = softmin + f * softrange; + switch (scale_type) { + case PROP_SCALE_LINEAR: { + tempf = softmin + f * softrange; + break; + } + case PROP_SCALE_LOG: { + tempf = powf(softmax / softmin, f) * softmin; + break; + } + case PROP_SCALE_CUBIC: { + const float cubicmin = cube_f(softmin); + const float cubicmax = cube_f(softmax); + const float cubicrange = cubicmax - cubicmin; + tempf = cube_f(softmin + f * softrange); + tempf = (tempf - cubicmin) / cubicrange * softrange + softmin; + break; + } + } temp = round_fl_to_int(tempf); if (snap) { @@ -5470,6 +5633,8 @@ static int ui_do_but_SLI( if (click) { if (click == 2) { + const PropertyScaleType scale_type = ui_but_scale_type(but); + /* nudge slider to the left or right */ float f, tempf, softmin, softmax, softrange; int temp; @@ -5494,14 +5659,20 @@ static int ui_do_but_SLI( f = (float)(mx - but->rect.xmin) / (BLI_rctf_size_x(&but->rect)); } - f = softmin + f * softrange; + if (scale_type == PROP_SCALE_LOG) { + f = powf(softmax / softmin, f) * softmin; + } + else { + f = softmin + f * softrange; + } if (!ui_but_is_float(but)) { + int value_step = 1; if (f < temp) { - temp--; + temp -= value_step; } else { - temp++; + temp += value_step; } if (temp >= softmin && temp <= softmax) { @@ -5512,14 +5683,23 @@ static int ui_do_but_SLI( } } else { - if (f < tempf) { - tempf -= 0.01f; - } - else { - tempf += 0.01f; - } - if (tempf >= softmin && tempf <= softmax) { + float value_step; + if (scale_type == PROP_SCALE_LOG) { + value_step = powf(10.0f, roundf(log10f(tempf) + UI_PROP_SCALE_LOG_SNAP_OFFSET) - 1.0f); + } + else { + value_step = 0.01f; + } + + if (f < tempf) { + tempf -= value_step; + } + else { + tempf += value_step; + } + + CLAMP(tempf, softmin, softmax); data->value = tempf; } else { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 0e465be5bf6..23856c41ceb 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -62,7 +62,7 @@ struct wmTimer; #define UI_MENU_PADDING (int)(0.2f * UI_UNIT_Y) #define UI_MENU_WIDTH_MIN (UI_UNIT_Y * 9) -/* some extra padding added to menus containing submenu icons */ +/** Some extra padding added to menus containing sub-menu icons. */ #define UI_MENU_SUBMENU_PADDING (6 * UI_DPI_FAC) /* menu scrolling */ @@ -74,7 +74,7 @@ struct wmTimer; #define UI_PANEL_MINX 100 #define UI_PANEL_MINY 70 -/* popover width (multiplied by 'U.widget_unit') */ +/** Popover width (multiplied by #U.widget_unit) */ #define UI_POPOVER_WIDTH_UNITS 10 /** #uiBut.flag */ @@ -93,12 +93,12 @@ enum { /* WARNING: rest of #uiBut.flag in UI_interface.h */ }; -/* uiBut->dragflag */ +/** #uiBut.dragflag */ enum { UI_BUT_DRAGPOIN_FREE = (1 << 0), }; -/* but->pie_dir */ +/** #uiBut.pie_dir */ typedef enum RadialDirection { UI_RADIAL_NONE = -1, UI_RADIAL_N = 0, @@ -129,13 +129,13 @@ extern const short ui_radial_dir_to_angle[8]; #define UI_BITBUT_ROW(min, max) \ (((max) >= 31 ? 0xFFFFFFFF : (1 << ((max) + 1)) - 1) - ((min) ? ((1 << (min)) - 1) : 0)) -/* split numbuts by ':' and align l/r */ +/** Split number-buttons by ':' and align left/right. */ #define USE_NUMBUTS_LR_ALIGN -/* Use new 'align' computation code. */ +/** Use new 'align' computation code. */ #define USE_UIBUT_SPATIAL_ALIGN -/* PieMenuData->flags */ +/** #PieMenuData.flags */ enum { /** pie menu item collision is detected at 90 degrees */ UI_PIE_DEGREES_RANGE_LARGE = (1 << 0), @@ -155,13 +155,13 @@ enum { #define PIE_CLICK_THRESHOLD_SQ 50.0f -/* max amount of items a radial menu (pie menu) can contain */ +/** The maximum number of items a radial menu (pie menu) can contain. */ #define PIE_MAX_ITEMS 8 struct uiBut { struct uiBut *next, *prev; - /* Pointer back to the layout item holding this button. */ + /** Pointer back to the layout item holding this button. */ uiLayout *layout; int flag, drawflag; eButType type; @@ -238,10 +238,10 @@ struct uiBut { short modifier_key; short iconadd; - /* UI_BTYPE_BLOCK data */ + /** #UI_BTYPE_BLOCK data */ uiBlockCreateFunc block_create_func; - /* UI_BTYPE_PULLDOWN/UI_BTYPE_MENU data */ + /** #UI_BTYPE_PULLDOWN / #UI_BTYPE_MENU data */ uiMenuCreateFunc menu_create_func; uiMenuStepFunc menu_step_func; @@ -255,9 +255,11 @@ struct uiBut { struct wmOperatorType *optype; struct PointerRNA *opptr; short opcontext; - uchar menu_key; /* 'a'-'z', always lower case */ - ListBase extra_op_icons; /* uiButExtraOpIcon */ + /** When non-zero, this is the key used to activate a menu items (`a-z` always lower case). */ + uchar menu_key; + + ListBase extra_op_icons; /** #uiButExtraOpIcon */ /* Draggable data, type is WM_DRAG_... */ char dragtype; @@ -266,10 +268,10 @@ struct uiBut { struct ImBuf *imb; float imb_scale; - /* active button data */ + /** Active button data (set when the user is hovering or interacting with a button). */ struct uiHandleButtonData *active; - /* Custom button data. */ + /** Custom button data (borrowed, not owned). */ void *custom_data; char *editstr; @@ -432,7 +434,7 @@ struct PieMenuData { float alphafac; }; -/* uiBlock.content_hints */ +/** #uiBlock.content_hints */ enum eBlockContentHints { /** In a menu block, if there is a single sub-menu button, we add some * padding to the right to put nicely aligned triangle icons there. */ @@ -466,7 +468,8 @@ struct uiBlock { struct Panel *panel; uiBlock *oldblock; - ListBase butstore; /* UI_butstore_* runtime function */ + /** Used for `UI_butstore_*` runtime function. */ + ListBase butstore; ListBase button_groups; /* #uiButtonGroup. */ @@ -482,7 +485,8 @@ struct uiBlock { rctf rect; float aspect; - uint puphash; /* popup menu hash for memory */ + /** Unique hash used to implement popup menu memory. */ + uint puphash; uiButHandleFunc func; void *func_arg1; @@ -497,10 +501,10 @@ struct uiBlock { uiBlockHandleFunc handle_func; void *handle_func_arg; - /* custom extra handling */ + /** Custom extra event handling. */ int (*block_event_func)(const struct bContext *C, struct uiBlock *, const struct wmEvent *); - /* extra draw function for custom blocks */ + /** Custom extra draw function for custom blocks. */ void (*drawextra)(const struct bContext *C, void *idv, void *arg1, void *arg2, rcti *rect); void *drawextra_arg1; void *drawextra_arg2; @@ -510,7 +514,7 @@ struct uiBlock { /** Hints about the buttons of this block. Used to avoid iterating over * buttons to find out if some criteria is met by any. Instead, check this * criteria when adding the button and set a flag here if it's met. */ - short content_hints; /* eBlockContentHints */ + short content_hints; /* #eBlockContentHints */ char direction; /** UI_BLOCK_THEME_STYLE_* */ @@ -524,11 +528,11 @@ struct uiBlock { const char *lockstr; bool lock; - /** to keep blocks while drawing and free them afterwards */ + /** To keep blocks while drawing and free them afterwards. */ bool active; - /** to avoid tooltip after click */ + /** To avoid tool-tip after click. */ bool tooltipdisabled; - /** UI_block_end done? */ + /** True when #UI_block_end has been called. */ bool endblock; /** for doing delayed */ @@ -538,12 +542,12 @@ struct uiBlock { /** for doing delayed */ int bounds, minbounds; - /** pull-downs, to detect outside, can differ per case how it is created. */ + /** Pull-downs, to detect outside, can differ per case how it is created. */ rctf safety; - /** uiSafetyRct list */ + /** #uiSafetyRct list */ ListBase saferct; - uiPopupBlockHandle *handle; /* handle */ + uiPopupBlockHandle *handle; /** use so presets can find the operator, * across menus and from nested popups which fail for operator context. */ @@ -558,10 +562,12 @@ struct uiBlock { /** \note only accessed by color picker templates. */ ColorPickerData color_pickers; - bool is_color_gamma_picker; /* Block for color picker with gamma baked in. */ + /** Block for color picker with gamma baked in. */ + bool is_color_gamma_picker; - /** display device name used to display this block, - * used by color widgets to transform colors from/to scene linear + /** + * Display device name used to display this block, + * used by color widgets to transform colors from/to scene linear. */ char display_device[64]; @@ -654,6 +660,7 @@ bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot, extern void ui_but_update(uiBut *but); extern void ui_but_update_edited(uiBut *but); +extern PropertyScaleType ui_but_scale_type(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_float(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_bool(const uiBut *but) ATTR_WARN_UNUSED_RESULT; extern bool ui_but_is_unit(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -674,9 +681,9 @@ void ui_block_cm_to_display_space_v3(uiBlock *block, float pixel[3]); /* interface_regions.c */ struct uiKeyNavLock { - /* Set when we're using key-input. */ + /** Set when we're using keyboard-input. */ bool is_keynav; - /* only used to check if we've moved the cursor */ + /** Only used to check if we've moved the cursor. */ int event_xy[2]; }; @@ -692,7 +699,7 @@ struct uiPopupBlockCreate { int event_xy[2]; - /* when popup is initialized from a button */ + /** Set when popup is initialized from a button. */ struct ARegion *butregion; uiBut *but; }; @@ -701,7 +708,7 @@ struct uiPopupBlockHandle { /* internal */ struct ARegion *region; - /* use only for 'UI_BLOCK_MOVEMOUSE_QUIT' popups */ + /** Use only for #UI_BLOCK_MOVEMOUSE_QUIT popups. */ float towards_xy[2]; double towardstime; bool dotowards; @@ -711,9 +718,9 @@ struct uiPopupBlockHandle { void (*cancel_func)(struct bContext *C, void *arg); void *popup_arg; - /* store data for refreshing popups */ + /** Store data for refreshing popups. */ struct uiPopupBlockCreate popup_create_vars; - /* true if we can re-create the popup using 'popup_create_vars' */ + /** True if we can re-create the popup using #uiPopupBlockHandle.popup_create_vars. */ bool can_refresh; bool refresh; @@ -733,7 +740,7 @@ struct uiPopupBlockHandle { int retvalue; float retvec[4]; - /* menu direction */ + /** Menu direction. */ int direction; /* Previous values so we don't resize or reposition on refresh. */ @@ -927,9 +934,7 @@ extern void ui_but_execute_end(struct bContext *C, void *active_back); extern void ui_but_active_free(const struct bContext *C, uiBut *but); extern int ui_but_menu_direction(uiBut *but); -extern void ui_but_text_password_hide(char password_str[UI_MAX_DRAW_STR], - uiBut *but, - const bool restore); +extern void ui_but_text_password_hide(char password_str[128], uiBut *but, const bool restore); extern uiBut *ui_but_find_select_in_enum(uiBut *but, int direction); bool ui_but_is_editing(const uiBut *but); float ui_block_calc_pie_segment(struct uiBlock *block, const float event_xy[2]); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index cabd98902a6..8f2871ce18b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -3038,7 +3038,7 @@ void uiItemDecoratorR(uiLayout *layout, PointerRNA *ptr, const char *propname, i /* popover */ void uiItemPopoverPanel_ptr( - uiLayout *layout, bContext *C, PanelType *pt, const char *name, int icon) + uiLayout *layout, const bContext *C, PanelType *pt, const char *name, int icon) { if (!name) { name = CTX_IFACE_(pt->translation_context, pt->label); @@ -3067,7 +3067,7 @@ void uiItemPopoverPanel_ptr( } void uiItemPopoverPanel( - uiLayout *layout, bContext *C, const char *panel_type, const char *name, int icon) + uiLayout *layout, const bContext *C, const char *panel_type, const char *name, int icon) { PanelType *pt = WM_paneltype_find(panel_type, true); if (pt == NULL) { diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 62be13c11c9..0cf3ad59903 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1380,7 +1380,9 @@ static int editsource_exec(bContext *C, wmOperator *op) /* redraw and get active button python info */ ED_region_do_layout(C, region); + WM_draw_region_viewport_bind(region); ED_region_do_draw(C, region); + WM_draw_region_viewport_unbind(region); region->do_draw = false; for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); @@ -1493,117 +1495,115 @@ static void edittranslation_find_po_file(const char *root, static int edittranslation_exec(bContext *C, wmOperator *op) { uiBut *but = UI_context_active_but_get(C); - int ret = OPERATOR_CANCELLED; - - if (but) { - wmOperatorType *ot; - PointerRNA ptr; - char popath[FILE_MAX]; - const char *root = U.i18ndir; - const char *uilng = BLT_lang_get(); - - uiStringInfo but_label = {BUT_GET_LABEL, NULL}; - uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; - uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; - uiStringInfo but_tip = {BUT_GET_TIP, NULL}; - uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; - uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; - uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; - uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; - uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; - uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; - - if (!BLI_is_dir(root)) { - BKE_report(op->reports, - RPT_ERROR, - "Please set your Preferences' 'Translation Branches " - "Directory' path to a valid directory"); - return OPERATOR_CANCELLED; - } - ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); - if (ot == NULL) { - BKE_reportf(op->reports, - RPT_ERROR, - "Could not find operator '%s'! Please enable ui_translate add-on " - "in the User Preferences", - EDTSRC_I18N_OP_NAME); - return OPERATOR_CANCELLED; - } - /* Try to find a valid po file for current language... */ - edittranslation_find_po_file(root, uilng, popath, FILE_MAX); - /* printf("po path: %s\n", popath); */ - if (popath[0] == '\0') { - BKE_reportf( - op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); - return OPERATOR_CANCELLED; - } - - UI_but_string_info_get(C, - but, - &but_label, - &rna_label, - &enum_label, - &but_tip, - &rna_tip, - &enum_tip, - &rna_struct, - &rna_prop, - &rna_enum, - &rna_ctxt, - NULL); - - WM_operator_properties_create_ptr(&ptr, ot); - RNA_string_set(&ptr, "lang", uilng); - RNA_string_set(&ptr, "po_file", popath); - RNA_string_set(&ptr, "but_label", but_label.strinfo); - RNA_string_set(&ptr, "rna_label", rna_label.strinfo); - RNA_string_set(&ptr, "enum_label", enum_label.strinfo); - RNA_string_set(&ptr, "but_tip", but_tip.strinfo); - RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); - RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); - RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); - RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); - RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); - RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); - - /* Clean up */ - if (but_label.strinfo) { - MEM_freeN(but_label.strinfo); - } - if (rna_label.strinfo) { - MEM_freeN(rna_label.strinfo); - } - if (enum_label.strinfo) { - MEM_freeN(enum_label.strinfo); - } - if (but_tip.strinfo) { - MEM_freeN(but_tip.strinfo); - } - if (rna_tip.strinfo) { - MEM_freeN(rna_tip.strinfo); - } - if (enum_tip.strinfo) { - MEM_freeN(enum_tip.strinfo); - } - if (rna_struct.strinfo) { - MEM_freeN(rna_struct.strinfo); - } - if (rna_prop.strinfo) { - MEM_freeN(rna_prop.strinfo); - } - if (rna_enum.strinfo) { - MEM_freeN(rna_enum.strinfo); - } - if (rna_ctxt.strinfo) { - MEM_freeN(rna_ctxt.strinfo); - } + if (but == NULL) { + BKE_report(op->reports, RPT_ERROR, "Active button not found"); + return OPERATOR_CANCELLED; + } - return ret; + wmOperatorType *ot; + PointerRNA ptr; + char popath[FILE_MAX]; + const char *root = U.i18ndir; + const char *uilng = BLT_lang_get(); + + uiStringInfo but_label = {BUT_GET_LABEL, NULL}; + uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; + uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; + uiStringInfo but_tip = {BUT_GET_TIP, NULL}; + uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; + uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; + uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; + uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; + uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; + uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; + + if (!BLI_is_dir(root)) { + BKE_report(op->reports, + RPT_ERROR, + "Please set your Preferences' 'Translation Branches " + "Directory' path to a valid directory"); + return OPERATOR_CANCELLED; + } + ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); + if (ot == NULL) { + BKE_reportf(op->reports, + RPT_ERROR, + "Could not find operator '%s'! Please enable ui_translate add-on " + "in the User Preferences", + EDTSRC_I18N_OP_NAME); + return OPERATOR_CANCELLED; + } + /* Try to find a valid po file for current language... */ + edittranslation_find_po_file(root, uilng, popath, FILE_MAX); + /* printf("po path: %s\n", popath); */ + if (popath[0] == '\0') { + BKE_reportf( + op->reports, RPT_ERROR, "No valid po found for language '%s' under %s", uilng, root); + return OPERATOR_CANCELLED; } - BKE_report(op->reports, RPT_ERROR, "Active button not found"); - return OPERATOR_CANCELLED; + UI_but_string_info_get(C, + but, + &but_label, + &rna_label, + &enum_label, + &but_tip, + &rna_tip, + &enum_tip, + &rna_struct, + &rna_prop, + &rna_enum, + &rna_ctxt, + NULL); + + WM_operator_properties_create_ptr(&ptr, ot); + RNA_string_set(&ptr, "lang", uilng); + RNA_string_set(&ptr, "po_file", popath); + RNA_string_set(&ptr, "but_label", but_label.strinfo); + RNA_string_set(&ptr, "rna_label", rna_label.strinfo); + RNA_string_set(&ptr, "enum_label", enum_label.strinfo); + RNA_string_set(&ptr, "but_tip", but_tip.strinfo); + RNA_string_set(&ptr, "rna_tip", rna_tip.strinfo); + RNA_string_set(&ptr, "enum_tip", enum_tip.strinfo); + RNA_string_set(&ptr, "rna_struct", rna_struct.strinfo); + RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); + RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); + RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); + const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr); + + /* Clean up */ + if (but_label.strinfo) { + MEM_freeN(but_label.strinfo); + } + if (rna_label.strinfo) { + MEM_freeN(rna_label.strinfo); + } + if (enum_label.strinfo) { + MEM_freeN(enum_label.strinfo); + } + if (but_tip.strinfo) { + MEM_freeN(but_tip.strinfo); + } + if (rna_tip.strinfo) { + MEM_freeN(rna_tip.strinfo); + } + if (enum_tip.strinfo) { + MEM_freeN(enum_tip.strinfo); + } + if (rna_struct.strinfo) { + MEM_freeN(rna_struct.strinfo); + } + if (rna_prop.strinfo) { + MEM_freeN(rna_prop.strinfo); + } + if (rna_enum.strinfo) { + MEM_freeN(rna_enum.strinfo); + } + if (rna_ctxt.strinfo) { + MEM_freeN(rna_ctxt.strinfo); + } + + return ret; } static void UI_OT_edittranslation_init(wmOperatorType *ot) diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index f234f0fbbf5..58a74a3473e 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -86,6 +86,17 @@ int ui_but_menu_step(uiBut *but, int direction) return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Popup Menu Memory + * + * Support menu-memory, a feature that positions the cursor + * over the previously used menu item. + * + * \note This is stored for each unique menu title. + * \{ */ + static uint ui_popup_string_hash(const char *str, const bool use_sep) { /* sometimes button contains hotkey, sometimes not, strip for proper compare */ @@ -392,7 +403,7 @@ uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, pup->layout = UI_block_layout( pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, UI_MENU_PADDING, style); - /* note, this intentionally differs from the menu & submenu default because many operators + /* note, this intentionally differs from the menu & sub-menu default because many operators * use popups like this to select one of their options - * where having invoke doesn't make sense */ uiLayoutSetOperatorContext(pup->layout, WM_OP_EXEC_REGION_WIN); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 12044863b8c..987cde61f97 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -677,8 +677,7 @@ static void ui_searchbox_region_draw_cb(const bContext *C, ARegion *region) } /* The previous menu item draws the active selection. */ - ui_draw_menu_item( - &data->fstyle, &rect, name_sep, icon, state & ~UI_ACTIVE, separator_type, NULL); + ui_draw_menu_item(&data->fstyle, &rect, name_sep, icon, state, separator_type, NULL); } } /* indicate more */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 760fbe75688..e3df9704826 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1110,7 +1110,7 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } - if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_TXT, ID_OB, ID_WS)) && + if (id->lib == NULL && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) && (hide_buttons == false)) { uiDefIconButR(block, UI_BTYPE_ICON_TOGGLE, @@ -6830,6 +6830,7 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) uiLayout *ui_abs = uiLayoutAbsolute(layout, false); uiBlock *block = uiLayoutGetBlock(ui_abs); + eUIEmbossType previous_emboss = UI_block_emboss_get(block); UI_fontstyle_set(&style->widgetlabel); int width = BLF_width(style->widgetlabel.uifont_id, report->message, report->len); @@ -6904,6 +6905,8 @@ void uiTemplateReportsBanner(uiLayout *layout, bContext *C) width + UI_UNIT_X, UI_UNIT_Y, "Show in Info Log"); + + UI_block_emboss_set(block, previous_emboss); } void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) @@ -7192,7 +7195,7 @@ void uiTemplateComponentMenu(uiLayout *layout, /** \name Node Socket Icon Template * \{ */ -void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float *color) +void uiTemplateNodeSocket(uiLayout *layout, bContext *UNUSED(C), float color[4]) { uiBlock *block = uiLayoutGetBlock(layout); UI_block_align_begin(block); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e351cd30c14..d2e6165a20f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2416,11 +2416,12 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, but->block->aspect = aspect_orig; #endif - rect->xmin += icon_size + icon_padding; + rect->xmin += round_fl_to_int(icon_size + icon_padding); } if (!no_text_padding) { - const int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect; + const int text_padding = round_fl_to_int((UI_TEXT_MARGIN_X * U.widget_unit) / + but->block->aspect); if (but->editstr) { rect->xmin += text_padding; } @@ -3751,12 +3752,35 @@ static void widget_numslider( float factor, factor_ui; float factor_discard = 1.0f; /* No discard. */ const float value = (float)ui_but_value_get(but); - - if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { - factor = value / but->softmax; - } - else { - factor = (value - but->softmin) / (but->softmax - but->softmin); + const float softmin = but->softmin; + const float softmax = but->softmax; + const float softrange = softmax - softmin; + const PropertyScaleType scale_type = ui_but_scale_type(but); + + switch (scale_type) { + case PROP_SCALE_LINEAR: { + if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { + factor = value / softmax; + } + else { + factor = (value - softmin) / softrange; + } + break; + } + case PROP_SCALE_LOG: { + const float logmin = fmaxf(softmin, 0.5e-8f); + const float base = softmax / logmin; + factor = logf(value / logmin) / logf(base); + break; + } + case PROP_SCALE_CUBIC: { + const float cubicmin = cube_f(softmin); + const float cubicmax = cube_f(softmax); + const float cubicrange = cubicmax - cubicmin; + const float f = (value - softmin) * cubicrange / softrange + cubicmin; + factor = (cbrtf(f) - softmin) / softrange; + break; + } } const float width = (float)BLI_rcti_size_x(rect); diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 7453cd17868..40c510af7e5 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -757,7 +757,7 @@ typedef struct v2dViewZoomData { } v2dViewZoomData; /** - * Clamp by convention rather then locking flags, + * Clamp by convention rather than locking flags, * for ndof and +/- keys */ static void view_zoom_axis_lock_defaults(bContext *C, bool r_do_zoom_xy[2]) diff --git a/source/blender/editors/io/io_alembic.c b/source/blender/editors/io/io_alembic.c index 592467c2a85..28838d677f0 100644 --- a/source/blender/editors/io/io_alembic.c +++ b/source/blender/editors/io/io_alembic.c @@ -121,6 +121,7 @@ static int wm_alembic_export_exec(bContext *C, wmOperator *op) .uvs = RNA_boolean_get(op->ptr, "uvs"), .normals = RNA_boolean_get(op->ptr, "normals"), .vcolors = RNA_boolean_get(op->ptr, "vcolors"), + .orcos = RNA_boolean_get(op->ptr, "orcos"), .apply_subdiv = RNA_boolean_get(op->ptr, "apply_subdiv"), .curves_as_mesh = RNA_boolean_get(op->ptr, "curves_as_mesh"), .flatten_hierarchy = RNA_boolean_get(op->ptr, "flatten"), @@ -210,6 +211,7 @@ static void ui_alembic_export_settings(uiLayout *layout, PointerRNA *imfptr) uiItemR(col, imfptr, "normals", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "vcolors", 0, NULL, ICON_NONE); + uiItemR(col, imfptr, "orcos", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "face_sets", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "curves_as_mesh", 0, NULL, ICON_NONE); @@ -378,6 +380,12 @@ void WM_OT_alembic_export(wmOperatorType *ot) RNA_def_boolean(ot->srna, "vcolors", 0, "Vertex Colors", "Export vertex colors"); + RNA_def_boolean(ot->srna, + "orcos", + true, + "Generated Coordinates", + "Export undeformed mesh vertex coordinates"); + RNA_def_boolean( ot->srna, "face_sets", 0, "Face Sets", "Export per face shading group assignments"); diff --git a/source/blender/editors/mask/mask_shapekey.c b/source/blender/editors/mask/mask_shapekey.c index 1eb6613d8fe..81bf66da72c 100644 --- a/source/blender/editors/mask/mask_shapekey.c +++ b/source/blender/editors/mask/mask_shapekey.c @@ -220,8 +220,8 @@ void MASK_OT_shape_key_feather_reset(wmOperatorType *ot) } /* - * - loop over selected shapekeys. - * - find firstsel/lastsel pairs. + * - loop over selected shape-keys. + * - find first-selected/last-selected pairs. * - move these into a temp list. * - re-key all the original shapes. * - copy unselected values back from the original. diff --git a/source/blender/editors/mesh/editmesh_bisect.c b/source/blender/editors/mesh/editmesh_bisect.c index bc8c456889d..ea35d5a9e26 100644 --- a/source/blender/editors/mesh/editmesh_bisect.c +++ b/source/blender/editors/mesh/editmesh_bisect.c @@ -444,15 +444,17 @@ void MESH_OT_bisect(struct wmOperatorType *ot) RNA_def_boolean( ot->srna, "clear_outer", false, "Clear Outer", "Remove geometry in front of the plane"); - RNA_def_float(ot->srna, - "threshold", - 0.0001, - 0.0, - 10.0, - "Axis Threshold", - "Preserves the existing geometry along the cut plane", - 0.00001, - 0.1); + prop = RNA_def_float(ot->srna, + "threshold", + 0.0001, + 0.0, + 10.0, + "Axis Threshold", + "Preserves the existing geometry along the cut plane", + 0.00001, + 0.1); + /* Without higher precision, the default value displays as zero. */ + RNA_def_property_ui_range(prop, 0.0, 10.0, 0.01, 5); WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 37e0ef661b6..b5cd9c7f60d 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -205,6 +205,12 @@ typedef struct KnifeTool_OpData { BLI_mempool *refs; + /** + * Use this instead of #Object.imat since it's calculated using #invert_m4_m4_safe_ortho + * to support objects with zero scale on a single axis. + */ + float ob_imat[4][4]; + float projmat[4][4]; float projmat_inv[4][4]; /* vector along view z axis (object space, normalized) */ @@ -287,7 +293,7 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) copy_v3_v3(co_depth, kcd->prev.cage); mul_m4_v3(kcd->ob->obmat, co_depth); ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - mul_m4_v3(kcd->ob->imat, curr_cage_adjust); + mul_m4_v3(kcd->ob_imat, curr_cage_adjust); sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); } @@ -576,10 +582,8 @@ static void knife_input_ray_segment(KnifeTool_OpData *kcd, ED_view3d_unproject(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); /* transform into object space */ - invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); - - mul_m4_v3(kcd->ob->imat, r_origin); - mul_m4_v3(kcd->ob->imat, r_origin_ofs); + mul_m4_v3(kcd->ob_imat, r_origin); + mul_m4_v3(kcd->ob_imat, r_origin_ofs); } static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) @@ -625,11 +629,11 @@ static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) static void knife_recalc_projmat(KnifeTool_OpData *kcd) { - invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat); invert_m4_m4(kcd->projmat_inv, kcd->projmat); - mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob->imat, kcd->vc.rv3d->viewinv[2]); + invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); + mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob_imat, kcd->vc.rv3d->viewinv[2]); normalize_v3(kcd->proj_zaxis); kcd->is_ortho = ED_view3d_clip_range_get( @@ -941,8 +945,7 @@ static void knife_start_cut(KnifeTool_OpData *kcd) float ofs_local[3]; negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs); - invert_m4_m4(kcd->ob->imat, kcd->ob->obmat); - mul_m4_v3(kcd->ob->imat, ofs_local); + mul_m4_v3(kcd->ob_imat, ofs_local); knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); @@ -1744,7 +1747,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, /* TODO: I think there's a simpler way to get the required raycast ray */ ED_view3d_unproject(kcd->vc.region, s[0], s[1], 0.0f, view); - mul_m4_v3(kcd->ob->imat, view); + mul_m4_v3(kcd->ob_imat, view); /* make p_ofs a little towards view, so ray doesn't hit p's face. */ sub_v3_v3(view, p); @@ -1875,10 +1878,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true); ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true); - mul_m4_v3(kcd->ob->imat, v1); - mul_m4_v3(kcd->ob->imat, v2); - mul_m4_v3(kcd->ob->imat, v3); - mul_m4_v3(kcd->ob->imat, v4); + mul_m4_v3(kcd->ob_imat, v1); + mul_m4_v3(kcd->ob_imat, v2); + mul_m4_v3(kcd->ob_imat, v3); + mul_m4_v3(kcd->ob_imat, v4); /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4' * can end up being ~2000 units apart with an orthogonal perspective. @@ -2644,6 +2647,8 @@ static void knifetool_init(bContext *C, kcd->ob = obedit; kcd->region = CTX_wm_region(C); + invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); + em_setup_viewcontext(C, &kcd->vc); kcd->em = BKE_editmesh_from_object(kcd->ob); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 0aeaee57548..6cb103460f6 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -2547,6 +2547,7 @@ bool EDBM_selectmode_set_multi(bContext *C, const short selectmode) changed = true; } } + MEM_freeN(objects); if (changed) { WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); diff --git a/source/blender/editors/mesh/editmesh_select_similar.c b/source/blender/editors/mesh/editmesh_select_similar.c index f9651454dee..f3c0da67ecc 100644 --- a/source/blender/editors/mesh/editmesh_select_similar.c +++ b/source/blender/editors/mesh/editmesh_select_similar.c @@ -1325,7 +1325,9 @@ void MESH_OT_select_similar(wmOperatorType *ot) RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", ""); - RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); + prop = RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f); + /* Very small values are needed sometimes, similar area of small faces for e.g: see T87823 */ + RNA_def_property_ui_range(prop, 0.0, 1.0, 0.01, 5); } /** \} */ diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 77b5379ddd4..4338b043fc9 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -87,7 +87,7 @@ if(WITH_INTERNATIONAL) endif() if(WITH_EXPERIMENTAL_FEATURES) - add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) endif() diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index a0de1e0fa84..b0255e79858 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -117,6 +117,7 @@ #include "ED_physics.h" #include "ED_render.h" #include "ED_screen.h" +#include "ED_select_utils.h" #include "ED_transform.h" #include "ED_view3d.h" @@ -482,9 +483,7 @@ bool ED_object_add_generic_get_opts(bContext *C, if (local_view_bits) { View3D *v3d = CTX_wm_view3d(C); - if (v3d && v3d->localvd) { - *local_view_bits = v3d->local_view_uuid; - } + *local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; } /* Location! */ @@ -861,8 +860,9 @@ static int effector_add_exec(bContext *C, wmOperator *op) float mat[4][4]; ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + mul_mat3_m4_fl(mat, dia); BLI_addtail(&cu->editnurb->nurbs, - ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, dia)); + ED_curve_add_nurbs_primitive(C, ob, mat, CU_NURBS | CU_PRIM_PATH, 1)); if (!enter_editmode) { ED_object_editmode_exit_ex(bmain, scene, ob, EM_FREEDATA); } @@ -1311,6 +1311,9 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL; const int type = RNA_enum_get(op->ptr, "type"); + const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); + const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); + const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); ushort local_view_bits; float loc[3], rot[3]; @@ -1321,12 +1324,14 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, NULL, &local_view_bits, NULL)) { return OPERATOR_CANCELLED; } - /* add new object if not currently editing a GP object, - * or if "empty" was chosen (i.e. user wants a blank GP canvas) - */ - if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) { + /* Add new object if not currently editing a GP object. */ + if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false)) { const char *ob_name = NULL; switch (type) { + case GP_EMPTY: { + ob_name = "GPencil"; + break; + } case GP_MONKEY: { ob_name = "Suzanne"; break; @@ -1336,6 +1341,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) break; } case GP_LRT_OBJECT: + case GP_LRT_SCENE: case GP_LRT_COLLECTION: { ob_name = "Line Art"; break; @@ -1356,6 +1362,13 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) /* create relevant geometry */ switch (type) { + case GP_EMPTY: { + float mat[4][4]; + + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + ED_gpencil_create_blank(C, ob, mat); + break; + } case GP_STROKE: { float radius = RNA_float_get(op->ptr, "radius"); float mat[4][4]; @@ -1420,13 +1433,25 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) id_us_plus(&md->target_material->id); } + if (use_lights) { + ob->dtx |= OB_USE_GPENCIL_LIGHTS; + } + else { + ob->dtx &= ~OB_USE_GPENCIL_LIGHTS; + } + /* Stroke object is drawn in front of meshes by default. */ - ob->dtx |= OB_DRAW_IN_FRONT; - } - case GP_EMPTY: - /* do nothing */ - break; + if (use_in_front) { + ob->dtx |= OB_DRAW_IN_FRONT; + } + else { + if (stroke_depth_order == GP_DRAWMODE_3D) { + gpd->draw_mode = GP_DRAWMODE_3D; + } + } + break; + } default: BKE_report(op->reports, RPT_WARNING, "Not implemented"); break; @@ -1443,6 +1468,39 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void object_add_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, op->ptr, "radius", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "align", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "location", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "rotation", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE); + + int type = RNA_enum_get(op->ptr, "type"); + if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) { + uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); + uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); + bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); + uiLayout *row = uiLayoutRow(layout, false); + uiLayoutSetActive(row, !in_front); + uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + } +} + +static EnumPropertyItem rna_enum_gpencil_add_stroke_depth_order_items[] = { + {GP_DRAWMODE_2D, + "2D", + 0, + "2D Layers", + "Display strokes using grease pencil layers to define order"}, + {GP_DRAWMODE_3D, "3D", 0, "3D Location", "Display strokes using real 3D position in 3D space"}, + {0, NULL, 0, NULL, NULL}, +}; + void OBJECT_OT_gpencil_add(wmOperatorType *ot) { /* identifiers */ @@ -1458,11 +1516,28 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* ui */ + ot->ui = object_add_ui; + /* properties */ ED_object_add_unit_props_radius(ot); ED_object_add_generic_props(ot, false); ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); + RNA_def_boolean(ot->srna, + "use_in_front", + false, + "In Front", + "Show line art grease pencil in front of everything"); + RNA_def_boolean( + ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); + RNA_def_enum( + ot->srna, + "stroke_depth_order", + rna_enum_gpencil_add_stroke_depth_order_items, + GP_DRAWMODE_3D, + "Stroke Depth Order", + "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'"); } /** \} */ @@ -2622,6 +2697,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) int a, mballConverted = 0; bool gpencilConverted = false; + bool gpencilCurveConverted = false; /* don't forget multiple users! */ @@ -2834,6 +2910,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) me_eval = BKE_mesh_copy_for_eval(me_eval, false); /* Full (edge-angle based) draw calculation should ideally be performed. */ BKE_mesh_edges_set_draw_render(me_eval); + BKE_object_material_from_eval_data(bmain, newob, &me_eval->id); BKE_mesh_nomain_to_mesh(me_eval, newob->data, newob, &CD_MASK_MESH, true); BKE_object_free_modifiers(newob, 0); /* after derivedmesh calls! */ } @@ -2906,6 +2983,16 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* meshes doesn't use displist */ BKE_object_free_curve_cache(newob); } + else if (target == OB_GPENCIL) { + ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; + Object *ob_gpencil = ED_gpencil_add_object(C, newob->loc, local_view_bits); + copy_v3_v3(ob_gpencil->rot, newob->rot); + copy_v3_v3(ob_gpencil->scale, newob->scale); + BKE_gpencil_convert_curve(bmain, scene, ob_gpencil, newob, false, 1.0f, 0.0f); + gpencilConverted = true; + gpencilCurveConverted = true; + basen = NULL; + } } else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { ob->flag |= OB_DONE; @@ -3087,6 +3174,17 @@ static int object_convert_exec(bContext *C, wmOperator *op) FOREACH_SCENE_OBJECT_END; } } + else { + /* Remove Text curves converted to Grease Pencil object to avoid duplicated curves. */ + if (gpencilCurveConverted) { + FOREACH_SCENE_OBJECT_BEGIN (scene, ob_delete) { + if (ELEM(ob_delete->type, OB_CURVE) && (ob_delete->flag & OB_DONE)) { + ED_object_base_free_and_unlink(bmain, scene, ob_delete); + } + } + FOREACH_SCENE_OBJECT_END; + } + } // XXX ED_object_editmode_enter(C, 0); // XXX exit_editmode(C, EM_FREEDATA|); /* freedata, but no undo */ @@ -3404,7 +3502,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ - ED_object_base_deselect_all(view_layer, NULL, BA_DESELECT); + ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); ED_object_base_select(basen, BA_SELECT); ED_object_base_activate(C, basen); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index f651f5bc3fd..3370476d466 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -669,9 +669,11 @@ static void bake_targets_clear(Main *bmain, const bool is_tangent) } /* create new mesh with edit mode changes and modifiers applied */ -static Mesh *bake_mesh_new_from_object(Object *object) +static Mesh *bake_mesh_new_from_object(Depsgraph *depsgraph, + Object *object, + const bool preserve_origindex) { - Mesh *me = BKE_mesh_new_from_object(NULL, object, false); + Mesh *me = BKE_mesh_new_from_object(depsgraph, object, false, preserve_origindex); if (me->flag & ME_AUTOSMOOTH) { BKE_mesh_split_faces(me, true); @@ -961,10 +963,39 @@ static bool bake_targets_init_vertex_colors(BakeTargets *targets, Object *ob, Re return true; } +static int find_original_loop(const Mesh *me_orig, + const int *vert_origindex, + const int *poly_origindex, + const int poly_eval, + const int vert_eval) +{ + /* Get original vertex and polygon index. There is currently no loop mapping + * in modifier stack evaluation. */ + const int vert_orig = vert_origindex[vert_eval]; + const int poly_orig = poly_origindex[poly_eval]; + + if (vert_orig == ORIGINDEX_NONE || poly_orig == ORIGINDEX_NONE) { + return ORIGINDEX_NONE; + } + + /* Find matching loop with original vertex in original polygon. */ + MPoly *mpoly_orig = me_orig->mpoly + poly_orig; + MLoop *mloop_orig = me_orig->mloop + mpoly_orig->loopstart; + for (int j = 0; j < mpoly_orig->totloop; ++j, ++mloop_orig) { + if (mloop_orig->v == vert_orig) { + return mpoly_orig->loopstart + j; + } + } + + return ORIGINDEX_NONE; +} + static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, - Mesh *me, + Object *ob, + Mesh *me_eval, BakePixel *pixel_array) { + Mesh *me = ob->data; const int num_pixels = targets->num_pixels; /* Initialize blank pixels. */ @@ -983,16 +1014,31 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, } /* Populate through adjacent triangles, first triangle wins. */ - const int tottri = poly_to_tri_count(me->totpoly, me->totloop); + const int tottri = poly_to_tri_count(me_eval->totpoly, me_eval->totloop); MLoopTri *looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__); - BKE_mesh_recalc_looptri(me->mloop, me->mpoly, me->mvert, me->totloop, me->totpoly, looptri); + BKE_mesh_recalc_looptri( + me_eval->mloop, me_eval->mpoly, me_eval->mvert, me_eval->totloop, me_eval->totpoly, looptri); + + /* For mapping back to original mesh in case there are modifiers. */ + const int *vert_origindex = CustomData_get_layer(&me_eval->vdata, CD_ORIGINDEX); + const int *poly_origindex = CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX); for (int i = 0; i < tottri; i++) { const MLoopTri *lt = &looptri[i]; for (int j = 0; j < 3; j++) { - const unsigned int l = lt->tri[j]; + unsigned int l = lt->tri[j]; + unsigned int v = me_eval->mloop[l].v; + + /* Map back to original loop if there are modifiers. */ + if (vert_origindex != NULL && poly_origindex != NULL) { + l = find_original_loop(me, vert_origindex, poly_origindex, lt->poly, v); + if (l == ORIGINDEX_NONE || l >= me->totloop) { + continue; + } + } + BakePixel *pixel = &pixel_array[l]; if (pixel->primitive_id != -1) { @@ -1004,7 +1050,7 @@ static void bake_targets_populate_pixels_vertex_colors(BakeTargets *targets, /* Seed is the vertex, so that sampling noise is coherent for the same * vertex, but different corners can still have different normals, * materials and UVs. */ - pixel->seed = me->mloop[l].v; + pixel->seed = v; /* Barycentric coordinates, nudged a bit to avoid precision issues that * may happen when exactly at the vertex coordinate. */ @@ -1043,7 +1089,7 @@ static void bake_result_add_to_rgba(float rgba[4], const float *result, const in } } -static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, Mesh *me_split) +static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob) { Mesh *me = ob->data; MPropCol *mcol = CustomData_get_layer(&me->vdata, CD_PROP_COLOR); @@ -1052,11 +1098,6 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, const int num_channels = targets->num_channels; const float *result = targets->result; - /* We bake using a mesh with additional vertices for split normals, but the - * number of loops must match to be able to transfer the vertex colors. */ - BLI_assert(me->totloop == me_split->totloop); - UNUSED_VARS_NDEBUG(me_split); - if (mcol_valid) { const int totvert = me->totvert; const int totloop = me->totloop; @@ -1111,16 +1152,17 @@ static bool bake_targets_output_vertex_colors(BakeTargets *targets, Object *ob, static bool bake_targets_init(const BakeAPIRender *bkr, BakeTargets *targets, Object *ob, + Object *ob_eval, ReportList *reports) { if (bkr->target == R_BAKE_TARGET_IMAGE_TEXTURES) { if (bkr->save_mode == R_BAKE_SAVE_INTERNAL) { - if (!bake_targets_init_internal(bkr, targets, ob, reports)) { + if (!bake_targets_init_internal(bkr, targets, ob_eval, reports)) { return false; } } else if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) { - if (!bake_targets_init_external(bkr, targets, ob, reports)) { + if (!bake_targets_init_external(bkr, targets, ob_eval, reports)) { return false; } } @@ -1145,14 +1187,15 @@ static bool bake_targets_init(const BakeAPIRender *bkr, static void bake_targets_populate_pixels(const BakeAPIRender *bkr, BakeTargets *targets, - Mesh *me, + Object *ob, + Mesh *me_eval, BakePixel *pixel_array) { if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) { - bake_targets_populate_pixels_vertex_colors(targets, me, pixel_array); + bake_targets_populate_pixels_vertex_colors(targets, ob, me_eval, pixel_array); } else { - RE_bake_pixels_populate(me, pixel_array, targets->num_pixels, targets, bkr->uv_layer); + RE_bake_pixels_populate(me_eval, pixel_array, targets->num_pixels, targets, bkr->uv_layer); } } @@ -1160,7 +1203,7 @@ static bool bake_targets_output(const BakeAPIRender *bkr, BakeTargets *targets, Object *ob, Object *ob_eval, - Mesh *me, + Mesh *me_eval, BakePixel *pixel_array, ReportList *reports) { @@ -1169,11 +1212,12 @@ static bool bake_targets_output(const BakeAPIRender *bkr, return bake_targets_output_internal(bkr, targets, ob, pixel_array, reports); } if (bkr->save_mode == R_BAKE_SAVE_EXTERNAL) { - return bake_targets_output_external(bkr, targets, ob, ob_eval, me, pixel_array, reports); + return bake_targets_output_external( + bkr, targets, ob, ob_eval, me_eval, pixel_array, reports); } } else if (bkr->target == R_BAKE_TARGET_VERTEX_COLORS) { - return bake_targets_output_vertex_colors(targets, ob, me); + return bake_targets_output_vertex_colors(targets, ob); } return false; @@ -1213,8 +1257,8 @@ static int bake(const BakeAPIRender *bkr, BakeHighPolyData *highpoly = NULL; int tot_highpoly = 0; - Mesh *me_low = NULL; - Mesh *me_cage = NULL; + Mesh *me_low_eval = NULL; + Mesh *me_cage_eval = NULL; MultiresModifierData *mmd_low = NULL; int mmd_flags_low = 0; @@ -1224,6 +1268,8 @@ static int bake(const BakeAPIRender *bkr, BakeTargets targets = {NULL}; + const bool preserve_origindex = (bkr->target == R_BAKE_TARGET_VERTEX_COLORS); + RE_bake_engine_set_engine_parameters(re, bmain, scene); if (!RE_bake_has_engine(re)) { @@ -1287,10 +1333,10 @@ static int bake(const BakeAPIRender *bkr, ob_low_eval = DEG_get_evaluated_object(depsgraph, ob_low); /* get the mesh as it arrives in the renderer */ - me_low = bake_mesh_new_from_object(ob_low_eval); + me_low_eval = bake_mesh_new_from_object(depsgraph, ob_low_eval, preserve_origindex); /* Initialize bake targets. */ - if (!bake_targets_init(bkr, &targets, ob_low_eval, reports)) { + if (!bake_targets_init(bkr, &targets, ob_low, ob_low_eval, reports)) { goto cleanup; } @@ -1298,7 +1344,7 @@ static int bake(const BakeAPIRender *bkr, * it is populated later with the cage mesh (smoothed version of the mesh). */ pixel_array_low = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels, "bake pixels low poly"); if ((bkr->is_selected_to_active && (ob_cage == NULL) && bkr->is_cage) == false) { - bake_targets_populate_pixels(bkr, &targets, me_low, pixel_array_low); + bake_targets_populate_pixels(bkr, &targets, ob_low, me_low_eval, pixel_array_low); } if (bkr->is_selected_to_active) { @@ -1307,8 +1353,9 @@ static int bake(const BakeAPIRender *bkr, /* prepare cage mesh */ if (ob_cage) { - me_cage = bake_mesh_new_from_object(ob_cage_eval); - if ((me_low->totpoly != me_cage->totpoly) || (me_low->totloop != me_cage->totloop)) { + me_cage_eval = bake_mesh_new_from_object(depsgraph, ob_cage_eval, preserve_origindex); + if ((me_low_eval->totpoly != me_cage_eval->totpoly) || + (me_low_eval->totloop != me_cage_eval->totloop)) { BKE_report(reports, RPT_ERROR, "Invalid cage object, the cage mesh must have the same number " @@ -1348,8 +1395,8 @@ static int bake(const BakeAPIRender *bkr, BKE_object_handle_data_update(depsgraph, scene, ob_low_eval); } - me_cage = BKE_mesh_new_from_object(NULL, ob_low_eval, false); - bake_targets_populate_pixels(bkr, &targets, me_cage, pixel_array_low); + me_cage_eval = BKE_mesh_new_from_object(NULL, ob_low_eval, false, preserve_origindex); + bake_targets_populate_pixels(bkr, &targets, ob_low, me_cage_eval, pixel_array_low); } highpoly = MEM_callocN(sizeof(BakeHighPolyData) * tot_highpoly, "bake high poly objects"); @@ -1367,7 +1414,7 @@ static int bake(const BakeAPIRender *bkr, highpoly[i].ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); highpoly[i].ob_eval->restrictflag &= ~OB_RESTRICT_RENDER; highpoly[i].ob_eval->base_flag |= (BASE_VISIBLE_DEPSGRAPH | BASE_ENABLED_RENDER); - highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false); + highpoly[i].me = BKE_mesh_new_from_object(NULL, highpoly[i].ob_eval, false, false); /* Low-poly to high-poly transformation matrix. */ copy_m4_m4(highpoly[i].obmat, highpoly[i].ob->obmat); @@ -1391,7 +1438,7 @@ static int bake(const BakeAPIRender *bkr, pixel_array_high = MEM_mallocN(sizeof(BakePixel) * targets.num_pixels, "bake pixels high poly"); - if (!RE_bake_pixels_populate_from_objects(me_low, + if (!RE_bake_pixels_populate_from_objects(me_low_eval, pixel_array_low, pixel_array_high, highpoly, @@ -1402,7 +1449,7 @@ static int bake(const BakeAPIRender *bkr, bkr->max_ray_distance, ob_low_eval->obmat, (ob_cage ? ob_cage->obmat : ob_low_eval->obmat), - me_cage)) { + me_cage_eval)) { BKE_report(reports, RPT_ERROR, "Error handling selected objects"); goto cleanup; } @@ -1478,7 +1525,7 @@ static int bake(const BakeAPIRender *bkr, targets.num_pixels, targets.num_channels, targets.result, - me_low, + me_low_eval, bkr->normal_swizzle, ob_low_eval->obmat); } @@ -1497,8 +1544,8 @@ static int bake(const BakeAPIRender *bkr, } /* Evaluate modifiers again. */ - me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false); - bake_targets_populate_pixels(bkr, &targets, me_nores, pixel_array_low); + me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false); + bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low); RE_bake_normal_world_to_tangent(pixel_array_low, targets.num_pixels, @@ -1527,7 +1574,7 @@ static int bake(const BakeAPIRender *bkr, else { /* save the results */ if (bake_targets_output( - bkr, &targets, ob_low, ob_low_eval, me_low, pixel_array_low, reports)) { + bkr, &targets, ob_low, ob_low_eval, me_low_eval, pixel_array_low, reports)) { op_result = OPERATOR_FINISHED; } else { @@ -1562,12 +1609,12 @@ cleanup: bake_targets_free(&targets); - if (me_low != NULL) { - BKE_id_free(NULL, &me_low->id); + if (me_low_eval != NULL) { + BKE_id_free(NULL, &me_low_eval->id); } - if (me_cage != NULL) { - BKE_id_free(NULL, &me_cage->id); + if (me_cage_eval != NULL) { + BKE_id_free(NULL, &me_cage_eval->id); } DEG_graph_free(depsgraph); diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 0894314328e..5be572baec5 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -55,6 +55,7 @@ #include "IMB_imbuf_types.h" #include "BKE_anim_visualization.h" +#include "BKE_armature.h" #include "BKE_collection.h" #include "BKE_constraint.h" #include "BKE_context.h" @@ -578,6 +579,14 @@ static bool ED_object_editmode_load_free_ex(Main *bmain, if (free_data) { ED_armature_edit_free(obedit->data); + + if (load_data == false) { + /* Don't keep unused pose channels created by duplicating bones + * which may have been deleted/undone, see: T87631. */ + if (obedit->pose != NULL) { + BKE_pose_channels_clear_with_null_bone(obedit->pose, true); + } + } } /* TODO(sergey): Pose channels might have been changed, so need * to inform dependency graph about this. But is it really the @@ -1581,30 +1590,10 @@ static const EnumPropertyItem *object_mode_set_itemsf(bContext *C, return rna_enum_object_mode_items; } - Object *ob = CTX_data_active_object(C); + const Object *ob = CTX_data_active_object(C); if (ob) { - const bool use_mode_particle_edit = (BLI_listbase_is_empty(&ob->particlesystem) == false) || - (ob->soft != NULL) || - (BKE_modifiers_findby_type(ob, eModifierType_Cloth) != - NULL); while (input->identifier) { - if ((input->value == OB_MODE_EDIT && OB_TYPE_SUPPORT_EDITMODE(ob->type)) || - (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) || - (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || - (ELEM(input->value, - OB_MODE_SCULPT, - OB_MODE_VERTEX_PAINT, - OB_MODE_WEIGHT_PAINT, - OB_MODE_TEXTURE_PAINT) && - (ob->type == OB_MESH)) || - (ELEM(input->value, - OB_MODE_EDIT_GPENCIL, - OB_MODE_PAINT_GPENCIL, - OB_MODE_SCULPT_GPENCIL, - OB_MODE_WEIGHT_GPENCIL, - OB_MODE_VERTEX_GPENCIL) && - (ob->type == OB_GPENCIL)) || - (input->value == OB_MODE_OBJECT)) { + if (ED_object_mode_compat_test(ob, input->value)) { RNA_enum_item_add(&item, &totitem, input); } input++; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 5b2e0e36ba9..5bf04e195fe 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -157,10 +157,6 @@ bool edit_modifier_poll_generic(struct bContext *C, const bool is_liboverride_allowed); void edit_modifier_properties(struct wmOperatorType *ot); bool edit_modifier_invoke_properties(struct bContext *C, struct wmOperator *op); -bool edit_modifier_invoke_properties_with_hover_no_active(struct bContext *C, - struct wmOperator *op, - const struct wmEvent *event, - int *r_retval); struct ModifierData *edit_modifier_property_get(struct wmOperator *op, struct Object *ob, diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index ff1855cafc5..dcb294bcb12 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -115,43 +115,46 @@ static const char *object_mode_op_string(eObjectMode mode) */ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) { - if (ob) { - if (mode == OB_MODE_OBJECT) { - return true; - } + if (mode == OB_MODE_OBJECT) { + return true; + } - switch (ob->type) { - case OB_MESH: - if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | - OB_MODE_TEXTURE_PAINT | OB_MODE_PARTICLE_EDIT)) { - return true; - } - break; - case OB_CURVE: - case OB_SURF: - case OB_FONT: - case OB_MBALL: - if (mode & OB_MODE_EDIT) { - return true; - } - break; - case OB_LATTICE: - if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) { - return true; - } - break; - case OB_ARMATURE: - if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) { - return true; - } - break; - case OB_GPENCIL: - if (mode & (OB_MODE_EDIT | OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | - OB_MODE_SCULPT_GPENCIL | OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) { + switch (ob->type) { + case OB_MESH: + if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | + OB_MODE_TEXTURE_PAINT)) { + return true; + } + if (mode & OB_MODE_PARTICLE_EDIT) { + if (ED_object_particle_edit_mode_supported(ob)) { return true; } - break; - } + } + break; + case OB_CURVE: + case OB_SURF: + case OB_FONT: + case OB_MBALL: + if (mode & OB_MODE_EDIT) { + return true; + } + break; + case OB_LATTICE: + if (mode & (OB_MODE_EDIT | OB_MODE_WEIGHT_PAINT)) { + return true; + } + break; + case OB_ARMATURE: + if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) { + return true; + } + break; + case OB_GPENCIL: + if (mode & (OB_MODE_EDIT_GPENCIL | OB_MODE_PAINT_GPENCIL | OB_MODE_SCULPT_GPENCIL | + OB_MODE_WEIGHT_GPENCIL | OB_MODE_VERTEX_GPENCIL)) { + return true; + } + break; } return false; diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 2c279eb64ee..e14e5cbd44b 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -63,6 +63,7 @@ #include "BKE_lattice.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_runtime.h" @@ -210,7 +211,7 @@ ModifierData *ED_object_modifier_add( /* special cases */ if (type == eModifierType_Softbody) { if (!ob->soft) { - ob->soft = sbNew(scene); + ob->soft = sbNew(); ob->softflag |= OB_SB_GOAL | OB_SB_EDGES; } } @@ -772,6 +773,8 @@ static bool modifier_apply_obdata( return false; } + Main *bmain = DEG_get_bmain(depsgraph); + BKE_object_material_from_eval_data(bmain, ob, &mesh_applied->id); BKE_mesh_nomain_to_mesh(mesh_applied, me, ob, &CD_MASK_MESH, true); if (md_eval->type == eModifierType_Multires) { @@ -1116,20 +1119,27 @@ bool edit_modifier_invoke_properties(bContext *C, wmOperator *op) } /** - * If the "modifier" property is not set,fill the modifier property with the name of the modifier - * with a UI panel below the mouse cursor, without checking the context pointer. Used in order to - * apply modifier operators on hover over their panels. If this checked the context pointer then it - * would always use the active modifier, which isn't desired. + * If the "modifier" property is not set, fill the modifier property with the name of the modifier + * with a UI panel below the mouse cursor, unless a specific modifier is set with a context + * pointer. Used in order to apply modifier operators on hover over their panels. */ -bool edit_modifier_invoke_properties_with_hover_no_active(bContext *C, - wmOperator *op, - const wmEvent *event, - int *r_retval) +static bool edit_modifier_invoke_properties_with_hover(bContext *C, + wmOperator *op, + const wmEvent *event, + int *r_retval) { if (RNA_struct_property_is_set(op->ptr, "modifier")) { return true; } + /* Note that the context pointer is *not* the active modifier, it is set in UI layouts. */ + PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_Modifier); + if (ctx_ptr.data != NULL) { + ModifierData *md = ctx_ptr.data; + RNA_string_set(op->ptr, "modifier", md->name); + return true; + } + PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event); if (panel_ptr == NULL || RNA_pointer_is_null(panel_ptr)) { *r_retval = OPERATOR_CANCELLED; @@ -1211,7 +1221,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op) static int modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_remove_exec(C, op); } return retval; @@ -1257,7 +1267,7 @@ static int modifier_move_up_exec(bContext *C, wmOperator *op) static int modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_move_up_exec(C, op); } return retval; @@ -1302,7 +1312,7 @@ static int modifier_move_down_exec(bContext *C, wmOperator *op) static int modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_move_down_exec(C, op); } return retval; @@ -1345,7 +1355,7 @@ static int modifier_move_to_index_exec(bContext *C, wmOperator *op) static int modifier_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_move_to_index_exec(C, op); } return retval; @@ -1458,7 +1468,7 @@ static int modifier_apply_exec(bContext *C, wmOperator *op) static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_apply_exec(C, op); } return retval; @@ -1502,7 +1512,7 @@ static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op) static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_apply_as_shapekey_exec(C, op); } return retval; @@ -1614,7 +1624,7 @@ static int modifier_copy_exec(bContext *C, wmOperator *op) static int modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_copy_exec(C, op); } return retval; @@ -1657,7 +1667,7 @@ static int modifier_set_active_exec(bContext *C, wmOperator *op) static int modifier_set_active_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_set_active_exec(C, op); } return retval; @@ -1749,7 +1759,7 @@ static int modifier_copy_to_selected_exec(bContext *C, wmOperator *op) static int modifier_copy_to_selected_invoke(bContext *C, wmOperator *op, const wmEvent *event) { int retval; - if (edit_modifier_invoke_properties_with_hover_no_active(C, op, event, &retval)) { + if (edit_modifier_invoke_properties_with_hover(C, op, event, &retval)) { return modifier_copy_to_selected_exec(C, op); } return retval; diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index a7308002e76..b685a93f27b 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -801,7 +801,7 @@ bool ED_object_parent_set(ReportList *reports, * so we check this by assuming that the parent is selected too. */ /* XXX currently this should only happen for meshes, curves, surfaces, - * and lattices - this stuff isn't available for metas yet */ + * and lattices - this stuff isn't available for meta-balls yet. */ if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_LATTICE)) { ModifierData *md; diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c index 5f81f9afe4f..3f40d637188 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.c @@ -3985,6 +3985,10 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { + if (C == NULL) { + return DummyRNA_NULL_items; + } + Object *ob = ED_object_context(C); EnumPropertyItem tmp = {0, "", 0, "", ""}; EnumPropertyItem *item = NULL; diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index de4ad913d6d..5b545784e5b 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -5375,8 +5375,7 @@ static bool particle_edit_toggle_poll(bContext *C) return 0; } - return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) || - BKE_modifiers_findby_type(ob, eModifierType_Softbody)); + return ED_object_particle_edit_mode_supported(ob); } static void free_all_psys_edit(Object *object) @@ -5391,6 +5390,12 @@ static void free_all_psys_edit(Object *object) } } +bool ED_object_particle_edit_mode_supported(const Object *ob) +{ + return (ob->particlesystem.first || BKE_modifiers_findby_type(ob, eModifierType_Cloth) || + BKE_modifiers_findby_type(ob, eModifierType_Softbody)); +} + void ED_object_particle_edit_mode_enter_ex(Depsgraph *depsgraph, Scene *scene, Object *ob) { /* Needed so #ParticleSystemModifierData.mesh_final is set. */ diff --git a/source/blender/editors/physics/rigidbody_object.c b/source/blender/editors/physics/rigidbody_object.c index 81a8b57776b..fca3b5817b0 100644 --- a/source/blender/editors/physics/rigidbody_object.c +++ b/source/blender/editors/physics/rigidbody_object.c @@ -520,6 +520,26 @@ static int rigidbody_objects_calc_mass_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } +static bool mass_calculate_poll_property(const bContext *UNUSED(C), + wmOperator *op, + const PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + /* Disable density input when not using the 'Custom' preset. */ + if (STREQ(prop_id, "density")) { + int material = RNA_enum_get(op->ptr, "material"); + if (material >= 0) { + RNA_def_property_clear_flag((PropertyRNA *)prop, PROP_EDITABLE); + } + else { + RNA_def_property_flag((PropertyRNA *)prop, PROP_EDITABLE); + } + } + + return true; +} + void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot) { PropertyRNA *prop; @@ -533,6 +553,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot) ot->invoke = WM_menu_invoke; /* XXX */ ot->exec = rigidbody_objects_calc_mass_exec; ot->poll = ED_operator_rigidbody_active_poll; + ot->poll_property = mass_calculate_poll_property; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -554,7 +575,7 @@ void RIGIDBODY_OT_mass_calculate(wmOperatorType *ot) FLT_MIN, FLT_MAX, "Density", - "Custom density value (kg/m^3) to use instead of material preset", + "Density value (kg/m^3), allows custom value if the 'Custom' preset is used", 1.0f, 2500.0f); } diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 0b5a8db0115..0bec509cd7e 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -574,9 +574,12 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec return; } if (rj->image_outdated) { - /* update entire render */ + /* Free all render buffer caches when switching slots, with lock to ensure main + * thread is not drawing the buffer at the same time. */ rj->image_outdated = false; - BKE_image_signal(rj->main, ima, NULL, IMA_SIGNAL_COLORMANAGE); + ibuf = BKE_image_acquire_ibuf(ima, &rj->iuser, &lock); + BKE_image_free_buffers(ima); + BKE_image_release_ibuf(ima, ibuf, lock); *(rj->do_update) = true; return; } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 10e0a143d9b..e28fe8a5d04 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -70,6 +70,7 @@ #include "BKE_node.h" #include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BKE_texture.h" #include "BKE_world.h" @@ -692,8 +693,9 @@ struct ObjectPreviewData { int sizey; }; -static Object *object_preview_camera_create( - Main *preview_main, ViewLayer *view_layer, Object *preview_object, int sizex, int sizey) +static Object *object_preview_camera_create(Main *preview_main, + ViewLayer *view_layer, + Object *preview_object) { Object *camera = BKE_object_add(preview_main, view_layer, OB_CAMERA, "Preview Camera"); @@ -701,18 +703,17 @@ static Object *object_preview_camera_create( float dummyscale[3]; mat4_to_loc_rot_size(camera->loc, rotmat, dummyscale, preview_object->obmat); - /* Camera is Y up, so needs additional 90deg rotation around X to match object's Z up. */ + /* Camera is Y up, so needs additional rotations to obliquely face the front. */ float drotmat[3][3]; - axis_angle_to_mat3_single(drotmat, 'X', M_PI_2); + const float eul[3] = {M_PI * 0.4f, 0.0f, M_PI * 0.1f}; + eul_to_mat3(drotmat, eul); mul_m3_m3_post(rotmat, drotmat); camera->rotmode = ROT_MODE_QUAT; mat3_to_quat(camera->quat, rotmat); - /* shader_preview_render() does this too. */ - if (sizex > sizey) { - ((Camera *)camera->data)->lens *= (float)sizey / (float)sizex; - } + /* Nice focal length for close portraiture. */ + ((Camera *)camera->data)->lens = 85; return camera; } @@ -730,11 +731,8 @@ static Scene *object_preview_scene_create(const struct ObjectPreviewData *previe BKE_collection_object_add(preview_data->pr_main, scene->master_collection, preview_data->object); - Object *camera_object = object_preview_camera_create(preview_data->pr_main, - view_layer, - preview_data->object, - preview_data->sizex, - preview_data->sizey); + Object *camera_object = object_preview_camera_create( + preview_data->pr_main, view_layer, preview_data->object); scene->camera = camera_object; scene->r.xsch = preview_data->sizex; @@ -779,16 +777,21 @@ static void object_preview_render(IconPreview *preview, IconPreviewSize *preview U.pixelsize = 2.0f; + View3DShading shading; + BKE_screen_view3d_shading_init(&shading); + /* Enable shadows, makes it a bit easier to see the shape. */ + shading.flag |= V3D_SHADING_SHADOW; + ImBuf *ibuf = ED_view3d_draw_offscreen_imbuf_simple( depsgraph, DEG_get_evaluated_scene(depsgraph), - NULL, - OB_SOLID, + &shading, + OB_TEXTURE, DEG_get_evaluated_object(depsgraph, scene->camera), preview_sized->sizex, preview_sized->sizey, IB_rect, - V3D_OFSDRAW_NONE, + V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS, R_ALPHAPREMUL, NULL, NULL, diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index bd2b1c4c553..2814a4c9351 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1172,12 +1172,12 @@ static void region_azones_add(const bScreen *screen, ScrArea *area, ARegion *reg } /* dir is direction to check, not the splitting edge direction! */ -static int rct_fits(const rcti *rect, char dir, int size) +static int rct_fits(const rcti *rect, const eScreenAxis dir_axis, int size) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { return BLI_rcti_size_x(rect) + 1 - size; } - /* 'v' */ + /* Vertical. */ return BLI_rcti_size_y(rect) + 1 - size; } @@ -1398,7 +1398,8 @@ static void region_rect_recursive( region->flag |= RGN_FLAG_TOO_SMALL; } } - else if (rct_fits(remainder, 'v', 1) < 0 || rct_fits(remainder, 'h', 1) < 0) { + else if (rct_fits(remainder, SCREEN_AXIS_V, 1) < 0 || + rct_fits(remainder, SCREEN_AXIS_H, 1) < 0) { /* remainder is too small for any usage */ region->flag |= RGN_FLAG_TOO_SMALL; } @@ -1410,11 +1411,11 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_TOP, RGN_ALIGN_BOTTOM)) { rcti *winrct = (region->overlap) ? overlap_remainder : remainder; - if ((prefsizey == 0) || (rct_fits(winrct, 'v', prefsizey) < 0)) { + if ((prefsizey == 0) || (rct_fits(winrct, SCREEN_AXIS_V, prefsizey) < 0)) { region->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(winrct, 'v', prefsizey); + int fac = rct_fits(winrct, SCREEN_AXIS_V, prefsizey); if (fac < 0) { prefsizey += fac; @@ -1436,11 +1437,11 @@ static void region_rect_recursive( else if (ELEM(alignment, RGN_ALIGN_LEFT, RGN_ALIGN_RIGHT)) { rcti *winrct = (region->overlap) ? overlap_remainder : remainder; - if ((prefsizex == 0) || (rct_fits(winrct, 'h', prefsizex) < 0)) { + if ((prefsizex == 0) || (rct_fits(winrct, SCREEN_AXIS_H, prefsizex) < 0)) { region->flag |= RGN_FLAG_TOO_SMALL; } else { - int fac = rct_fits(winrct, 'h', prefsizex); + int fac = rct_fits(winrct, SCREEN_AXIS_H, prefsizex); if (fac < 0) { prefsizex += fac; @@ -1464,7 +1465,7 @@ static void region_rect_recursive( region->winrct = *remainder; if (alignment == RGN_ALIGN_HSPLIT) { - if (rct_fits(remainder, 'h', prefsizex) > 4) { + if (rct_fits(remainder, SCREEN_AXIS_H, prefsizex) > 4) { region->winrct.xmax = BLI_rcti_cent_x(remainder); remainder->xmin = region->winrct.xmax + 1; } @@ -1473,7 +1474,7 @@ static void region_rect_recursive( } } else { - if (rct_fits(remainder, 'v', prefsizey) > 4) { + if (rct_fits(remainder, SCREEN_AXIS_V, prefsizey) > 4) { region->winrct.ymax = BLI_rcti_cent_y(remainder); remainder->ymin = region->winrct.ymax + 1; } @@ -1526,8 +1527,8 @@ static void region_rect_recursive( BLI_rcti_init(remainder, 0, 0, 0, 0); } - /* Fix any negative dimensions. This can happen when a quad split 3d view gets to small. (see - * T72200). */ + /* Fix any negative dimensions. This can happen when a quad split 3d view gets too small. + * (see T72200). */ BLI_rcti_sanitize(®ion->winrct); quad++; diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 2ba7ef8f972..2c45524ef94 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -33,184 +33,11 @@ #include "WM_api.h" +#include "UI_interface.h" #include "UI_resources.h" #include "screen_intern.h" -/** - * Draw horizontal shape visualizing future joining - * (left as well right direction of future joining). - */ -static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 8; - w = height / 4; - } - else { - h = width / 8; - w = width / 4; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x; - points[0].y = area->v1->vec.y + height / 2; - - points[1].x = area->v1->vec.x; - points[1].y = area->v1->vec.y; - - points[2].x = area->v4->vec.x - w; - points[2].y = area->v4->vec.y; - - points[3].x = area->v4->vec.x - w; - points[3].y = area->v4->vec.y + height / 2 - 2 * h; - - points[4].x = area->v4->vec.x - 2 * w; - points[4].y = area->v4->vec.y + height / 2; - - points[5].x = area->v4->vec.x - w; - points[5].y = area->v4->vec.y + height / 2 + 2 * h; - - points[6].x = area->v3->vec.x - w; - points[6].y = area->v3->vec.y; - - points[7].x = area->v2->vec.x; - points[7].y = area->v2->vec.y; - - points[8].x = area->v4->vec.x; - points[8].y = area->v4->vec.y + height / 2 - h; - - points[9].x = area->v4->vec.x; - points[9].y = area->v4->vec.y + height / 2 + h; - - if (dir == 'l') { - /* when direction is left, then we flip direction of arrow */ - float cx = area->v1->vec.x + width; - for (int i = 0; i < 10; i++) { - points[i].x -= cx; - points[i].x = -points[i].x; - points[i].x += area->v1->vec.x; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw vertical shape visualizing future joining (up/down direction). - */ -static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 4; - w = height / 8; - } - else { - h = width / 4; - w = width / 8; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x + width / 2; - points[0].y = area->v3->vec.y; - - points[1].x = area->v2->vec.x; - points[1].y = area->v2->vec.y; - - points[2].x = area->v1->vec.x; - points[2].y = area->v1->vec.y + h; - - points[3].x = area->v1->vec.x + width / 2 - 2 * w; - points[3].y = area->v1->vec.y + h; - - points[4].x = area->v1->vec.x + width / 2; - points[4].y = area->v1->vec.y + 2 * h; - - points[5].x = area->v1->vec.x + width / 2 + 2 * w; - points[5].y = area->v1->vec.y + h; - - points[6].x = area->v4->vec.x; - points[6].y = area->v4->vec.y + h; - - points[7].x = area->v3->vec.x; - points[7].y = area->v3->vec.y; - - points[8].x = area->v1->vec.x + width / 2 - w; - points[8].y = area->v1->vec.y; - - points[9].x = area->v1->vec.x + width / 2 + w; - points[9].y = area->v1->vec.y; - - if (dir == 'u') { - /* when direction is up, then we flip direction of arrow */ - float cy = area->v1->vec.y + height; - for (int i = 0; i < 10; i++) { - points[i].y -= cy; - points[i].y = -points[i].y; - points[i].y += area->v1->vec.y; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw join shape due to direction of joining. - */ -static void draw_join_shape(ScrArea *area, char dir, uint pos) -{ - if (ELEM(dir, 'u', 'd')) { - draw_vertical_join_shape(area, dir, pos); - } - else { - draw_horizontal_join_shape(area, dir, pos); - } -} - #define CORNER_RESOLUTION 3 static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i) @@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len) #undef CORNER_RESOLUTION -/** - * Draw screen area darker with arrow (visualization of future joining). - */ -static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(0, 0, 0, 50); - - draw_join_shape(area, dir, pos); -} - -/** - * Draw screen area lighter with arrow shape ("eraser" of previous dark shape). - */ -static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(255, 255, 255, 25); - - immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y); -} - static void drawscredge_area_draw( int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness) { @@ -427,53 +232,97 @@ void ED_screen_draw_edges(wmWindow *win) } /** - * The blended join arrows. + * Visual indication of the two areas involved in a proposed join. * * \param sa1: Area from which the resultant originates. * \param sa2: Target area that will be replaced. */ -void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) +void screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return; + } + + /* Rect of the combined areas.*/ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + const rctf combined = { + .xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) : + MIN2(sa1->totrct.xmin, sa2->totrct.xmin), + .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) : + MAX2(sa1->totrct.xmax, sa2->totrct.xmax), + .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) : + MAX2(sa1->totrct.ymin, sa2->totrct.ymin), + .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) : + MIN2(sa1->totrct.ymax, sa2->totrct.ymax), + }; + + uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - GPU_line_width(1); - - /* blended join arrow */ - int dir = area_getorientation(sa1, sa2); - int dira = -1; - if (dir != -1) { - switch (dir) { - case 0: /* W */ - dir = 'r'; - dira = 'l'; - break; - case 1: /* N */ - dir = 'd'; - dira = 'u'; - break; - case 2: /* E */ - dir = 'l'; - dira = 'r'; - break; - case 3: /* S */ - dir = 'u'; - dira = 'd'; - break; + /* Highlight source (sa1) within combined area. */ + immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f}); + immRectf(pos_id, + MAX2(sa1->totrct.xmin, combined.xmin), + MAX2(sa1->totrct.ymin, combined.ymin), + MIN2(sa1->totrct.xmax, combined.xmax), + MIN2(sa1->totrct.ymax, combined.ymax)); + + /* Highlight destination (sa2) within combined area. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f}); + immRectf(pos_id, + MAX2(sa2->totrct.xmin, combined.xmin), + MAX2(sa2->totrct.ymin, combined.ymin), + MIN2(sa2->totrct.xmax, combined.xmax), + MIN2(sa2->totrct.ymax, combined.ymax)); + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + if (offset1 < 0 || offset2 > 0) { + /* Show partial areas that will be closed. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f}); + if (vertical) { + if (sa1->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax); + } + if (sa2->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax); + } + if (sa1->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax); + } + if (sa2->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax); + } + } + else { + if (sa1->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin); + } + if (sa2->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin); + } + if (sa1->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax); + } + if (sa2->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax); + } } - - GPU_blend(GPU_BLEND_ALPHA); - - scrarea_draw_shape_dark(sa2, dir, pos); - scrarea_draw_shape_light(sa1, dira, pos); - - GPU_blend(GPU_BLEND_NONE); } immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); + + /* Outline the combined area. */ + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f}); } -void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) +void screen_draw_split_preview(ScrArea *area, const eScreenAxis dir_axis, const float fac) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -485,7 +334,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immBegin(GPU_PRIM_LINES, 2); - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { const float y = (1 - fac) * area->totrct.ymin + fac * area->totrct.ymax; immVertex2f(pos, area->totrct.xmin, y); @@ -503,7 +352,7 @@ void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) immEnd(); } else { - BLI_assert(dir == 'v'); + BLI_assert(dir_axis == SCREEN_AXIS_V); const float x = (1 - fac) * area->totrct.xmin + fac * area->totrct.xmax; immVertex2f(pos, x, area->totrct.ymin); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 7ad8eada3b9..6fb5f33d836 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -104,8 +104,12 @@ static void screen_delarea(bContext *C, bScreen *screen, ScrArea *area) MEM_freeN(area); } -ScrArea *area_split( - const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge) +ScrArea *area_split(const wmWindow *win, + bScreen *screen, + ScrArea *area, + const eScreenAxis dir_axis, + const float fac, + const bool merge) { ScrArea *newa = NULL; @@ -116,7 +120,7 @@ ScrArea *area_split( rcti window_rect; WM_window_rect_calc(win, &window_rect); - short split = screen_geom_find_area_split_point(area, &window_rect, dir, fac); + short split = screen_geom_find_area_split_point(area, &window_rect, dir_axis, fac); if (split == 0) { return NULL; } @@ -125,7 +129,7 @@ ScrArea *area_split( * normally it shouldn't matter which is used since the copy should match the original * however with viewport rendering and python console this isn't the case. - campbell */ - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { /* new vertices */ ScrVert *sv1 = screen_geom_vertex_add(screen, area->v1->vec.x, split); ScrVert *sv2 = screen_geom_vertex_add(screen, area->v4->vec.x, split); @@ -279,47 +283,44 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) screen_new->do_draw = true; } -/* with area as center, sb is located at: 0=W, 1=N, 2=E, 3=S */ -/* -1 = not valid check */ -/* used with join operator */ -int area_getorientation(ScrArea *area, ScrArea *sb) +/** + * with `sa_a` as center, `sa_b` is located at: 0=W, 1=N, 2=E, 3=S + * -1 = not valid check. + * used with join operator. + */ +eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b) { - if (area == NULL || sb == NULL) { - return -1; + if (sa_a == NULL || sa_b == NULL || sa_a == sa_b) { + return SCREEN_DIR_NONE; } - ScrVert *saBL = area->v1; - ScrVert *saTL = area->v2; - ScrVert *saTR = area->v3; - ScrVert *saBR = area->v4; + const vec2s *sa_bl = &sa_a->v1->vec; + const vec2s *sa_tl = &sa_a->v2->vec; + const vec2s *sa_tr = &sa_a->v3->vec; + const vec2s *sa_br = &sa_a->v4->vec; - ScrVert *sbBL = sb->v1; - ScrVert *sbTL = sb->v2; - ScrVert *sbTR = sb->v3; - ScrVert *sbBR = sb->v4; + const vec2s *sb_bl = &sa_b->v1->vec; + const vec2s *sb_tl = &sa_b->v2->vec; + const vec2s *sb_tr = &sa_b->v3->vec; + const vec2s *sb_br = &sa_b->v4->vec; - if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */ - if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) && - (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) { + if (sa_bl->x == sb_br->x && sa_tl->x == sb_tr->x) { /* sa_a to right of sa_b = W */ + if ((MIN2(sa_tl->y, sb_tr->y) - MAX2(sa_bl->y, sb_br->y)) > AREAJOINTOLERANCEY) { return 0; } } - else if (saTL->vec.y == sbBL->vec.y && - saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */ - if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_tl->y == sb_bl->y && sa_tr->y == sb_br->y) { /* sa_a to bottom of sa_b = N */ + if ((MIN2(sa_tr->x, sb_br->x) - MAX2(sa_tl->x, sb_bl->x)) > AREAJOINTOLERANCEX) { return 1; } } - else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */ - if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) { + else if (sa_tr->x == sb_tl->x && sa_br->x == sb_bl->x) { /* sa_a to left of sa_b = E */ + if ((MIN2(sa_tr->y, sb_tl->y) - MAX2(sa_br->y, sb_bl->y)) > AREAJOINTOLERANCEY) { return 2; } } - else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/ - if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) { + else if (sa_bl->y == sb_tl->y && sa_br->y == sb_tr->y) { /* sa_a on top of sa_b = S */ + if ((MIN2(sa_br->x, sb_tr->x) - MAX2(sa_bl->x, sb_tl->x)) > AREAJOINTOLERANCEX) { return 3; } } @@ -327,6 +328,39 @@ int area_getorientation(ScrArea *area, ScrArea *sb) return -1; } +/** + * Get alignment offset of adjacent areas. 'dir' value is like #area_getorientation(). + */ +void area_getoffsets( + ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2) +{ + if (sa_a == NULL || sa_b == NULL) { + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } + else if (dir == SCREEN_DIR_W) { /* West: sa on right and sa_b to the left. */ + *r_offset1 = sa_b->v3->vec.y - sa_a->v2->vec.y; + *r_offset2 = sa_b->v4->vec.y - sa_a->v1->vec.y; + } + else if (dir == SCREEN_DIR_N) { /* North: sa below and sa_b above. */ + *r_offset1 = sa_a->v2->vec.x - sa_b->v1->vec.x; + *r_offset2 = sa_a->v3->vec.x - sa_b->v4->vec.x; + } + else if (dir == SCREEN_DIR_E) { /* East: sa on left and sa_b to the right. */ + *r_offset1 = sa_b->v2->vec.y - sa_a->v3->vec.y; + *r_offset2 = sa_b->v1->vec.y - sa_a->v4->vec.y; + } + else if (dir == SCREEN_DIR_S) { /* South: sa above and sa_b below. */ + *r_offset1 = sa_a->v1->vec.x - sa_b->v2->vec.x; + *r_offset2 = sa_a->v4->vec.x - sa_b->v3->vec.x; + } + else { + BLI_assert(dir == SCREEN_DIR_NONE); + *r_offset1 = INT_MAX; + *r_offset2 = INT_MAX; + } +} + /* Screen verts with horizontal position equal to from_x are moved to to_x. */ static void screen_verts_halign(const wmWindow *win, const bScreen *screen, @@ -358,11 +392,11 @@ static void screen_verts_valign(const wmWindow *win, /* Adjust all screen edges to allow joining two areas. 'dir' value is like area_getorientation(). */ static void screen_areas_align( - bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const int dir) + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, const eScreenDir dir) { wmWindow *win = CTX_wm_window(C); - if (ELEM(dir, 0, 2)) { + if (SCREEN_DIR_IS_HORIZONTAL(dir)) { /* horizontal join, use average for new top and bottom. */ int top = (sa1->v2->vec.y + sa2->v2->vec.y) / 2; int bottom = (sa1->v4->vec.y + sa2->v4->vec.y) / 2; @@ -390,41 +424,47 @@ static void screen_areas_align( } } -/* Helper function to join 2 areas, it has a return value, 0=failed 1=success - * used by the split, join operators - */ -int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +/* Simple join of two areas without any splitting. Will return false if not possible. */ +static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) { - int dir = area_getorientation(sa1, sa2); + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return false; + } - if (dir == -1) { - return 0; + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + int tolerance = SCREEN_DIR_IS_HORIZONTAL(dir) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX; + if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) { + return false; } - /* Align areas if they are not. Do sanity checking before getting here. */ + /* Align areas if they are not. */ screen_areas_align(C, screen, sa1, sa2, dir); - if (dir == 0) { /* sa1 to right of sa2 = W */ - sa1->v1 = sa2->v1; /* BL */ - sa1->v2 = sa2->v2; /* TL */ + if (dir == SCREEN_DIR_W) { /* sa1 to right of sa2 = West. */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v2 = sa2->v2; /* TL */ screen_geom_edge_add(screen, sa1->v2, sa1->v3); screen_geom_edge_add(screen, sa1->v1, sa1->v4); } - else if (dir == 1) { /* sa1 to bottom of sa2 = N */ - sa1->v2 = sa2->v2; /* TL */ - sa1->v3 = sa2->v3; /* TR */ + else if (dir == SCREEN_DIR_N) { /* sa1 to bottom of sa2 = North. */ + sa1->v2 = sa2->v2; /* TL */ + sa1->v3 = sa2->v3; /* TR */ screen_geom_edge_add(screen, sa1->v1, sa1->v2); screen_geom_edge_add(screen, sa1->v3, sa1->v4); } - else if (dir == 2) { /* sa1 to left of sa2 = E */ - sa1->v3 = sa2->v3; /* TR */ - sa1->v4 = sa2->v4; /* BR */ + else if (dir == SCREEN_DIR_E) { /* sa1 to left of sa2 = East. */ + sa1->v3 = sa2->v3; /* TR */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(screen, sa1->v2, sa1->v3); screen_geom_edge_add(screen, sa1->v1, sa1->v4); } - else if (dir == 3) { /* sa1 on top of sa2 = S */ - sa1->v1 = sa2->v1; /* BL */ - sa1->v4 = sa2->v4; /* BR */ + else if (dir == SCREEN_DIR_S) { /* sa1 on top of sa2 = South. */ + sa1->v1 = sa2->v1; /* BL */ + sa1->v4 = sa2->v4; /* BR */ screen_geom_edge_add(screen, sa1->v1, sa1->v2); screen_geom_edge_add(screen, sa1->v3, sa1->v4); } @@ -434,7 +474,104 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) /* Update preview thumbnail */ BKE_icon_changed(screen->id.icon_id); - return 1; + return true; +} + +/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */ +static ScrArea *screen_area_trim( + bContext *C, bScreen *screen, ScrArea **area, int size, eScreenDir dir, bool reverse) +{ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) { + return NULL; + } + + /* Measurement with ScrVerts because winx and winy might not be correct at this time. */ + float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) : + ((*area)->v3->vec.y - (*area)->v1->vec.y)); + fac = (reverse == vertical) ? 1.0f - fac : fac; + ScrArea *newsa = area_split( + CTX_wm_window(C), screen, *area, vertical ? SCREEN_AXIS_V : SCREEN_AXIS_H, fac, true); + + /* area_split always returns smallest of the two areas, so might have to swap. */ + if (((fac > 0.5f) == vertical) != reverse) { + ScrArea *temp = *area; + *area = newsa; + newsa = temp; + } + + return newsa; +} + +/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */ +static bool screen_area_join_ex( + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders) +{ + const eScreenDir dir = area_getorientation(sa1, sa2); + if (dir == SCREEN_DIR_NONE) { + return false; + } + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + /* Split Left/Top into new area if overhanging. */ + ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false); + + /* Split Right/Bottom into new area if overhanging. */ + ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true); + + /* The two areas now line up, so join them. */ + screen_area_join_aligned(C, screen, sa1, sa2); + + if (close_all_remainders || offset1 < 0 || offset2 > 0) { + /* Close both if trimming `sa1`. */ + screen_area_close(C, screen, side1); + screen_area_close(C, screen, side2); + } + + BKE_icon_changed(screen->id.icon_id); + return true; +} + +/* Join any two neighboring areas. Might involve complex changes. */ +int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +{ + return screen_area_join_ex(C, screen, sa1, sa2, false); +} + +/* Close a screen area, allowing most-aligned neighbor to take its place. */ +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) +{ + if (area == NULL) { + return false; + } + + ScrArea *sa2 = NULL; + float best_alignment = 0.0f; + + LISTBASE_FOREACH (ScrArea *, neighbor, &screen->areabase) { + const eScreenDir dir = area_getorientation(area, neighbor); + /* Must at least partially share an edge and not be a global area. */ + if ((dir != SCREEN_DIR_NONE) && (neighbor->global == NULL)) { + /* Winx/Winy might not be updated yet, so get lengths from verts. */ + const bool vertical = SCREEN_DIR_IS_VERTICAL(dir); + const int area_length = vertical ? (area->v3->vec.x - area->v1->vec.x) : + (area->v3->vec.y - area->v1->vec.y); + const int ar_length = vertical ? (neighbor->v3->vec.x - neighbor->v1->vec.x) : + (neighbor->v3->vec.y - neighbor->v1->vec.y); + /* Calculate the ratio of the lengths of the shared edges. */ + float alignment = MIN2(area_length, ar_length) / (float)MAX2(area_length, ar_length); + if (alignment > best_alignment) { + best_alignment = alignment; + sa2 = neighbor; + } + } + } + + /* Join from neighbor into this area to close it. */ + return screen_area_join_ex(C, screen, sa2, area, true); } /* ****************** EXPORTED API TO OTHER MODULES *************************** */ diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index ac159f4d633..51edad0332b 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -304,7 +304,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) */ short screen_geom_find_area_split_point(const ScrArea *area, const rcti *window_rect, - char dir, + const eScreenAxis dir_axis, float fac) { const int cur_area_width = screen_geom_area_width(area); @@ -313,17 +313,21 @@ short screen_geom_find_area_split_point(const ScrArea *area, const short area_min_y = ED_area_headersize(); /* area big enough? */ - if ((dir == 'v') && (cur_area_width <= 2 * area_min_x)) { - return 0; + if (dir_axis == SCREEN_AXIS_V) { + if (cur_area_width <= 2 * area_min_x) { + return 0; + } } - if ((dir == 'h') && (cur_area_height <= 2 * area_min_y)) { - return 0; + else if (dir_axis == SCREEN_AXIS_H) { + if (cur_area_height <= 2 * area_min_y) { + return 0; + } } /* to be sure */ CLAMP(fac, 0.0f, 1.0f); - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { short y = area->v1->vec.y + round_fl_to_short(fac * cur_area_height); int area_min = area_min_y; @@ -373,13 +377,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge) { bScreen *screen = WM_window_get_active_screen(win); - /* 'dir' is the direction of EDGE */ - char dir; + /* 'dir_axis' is the direction of EDGE */ + eScreenAxis dir_axis; if (edge->v1->vec.x == edge->v2->vec.x) { - dir = 'v'; + dir_axis = SCREEN_AXIS_V; } else { - dir = 'h'; + dir_axis = SCREEN_AXIS_H; } ED_screen_verts_iter(win, screen, sv) @@ -396,13 +400,13 @@ void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge) oneselected = false; LISTBASE_FOREACH (ScrEdge *, se, &screen->edgebase) { if (se->v1->flag + se->v2->flag == 1) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { if (se->v1->vec.y == se->v2->vec.y) { se->v1->flag = se->v2->flag = 1; oneselected = true; } } - if (dir == 'v') { + else if (dir_axis == SCREEN_AXIS_V) { if (se->v1->vec.x == se->v2->vec.x) { se->v1->flag = se->v2->flag = 1; oneselected = true; diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index c51ff559786..683f2844371 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -29,12 +29,37 @@ struct bContextDataResult; /* internal exports only */ +typedef enum eScreenDir { + /** This can mean unset, unknown or invalid. */ + SCREEN_DIR_NONE = -1, + /** West/Left. */ + SCREEN_DIR_W = 0, + /** North/Up. */ + SCREEN_DIR_N = 1, + /** East/Right. */ + SCREEN_DIR_E = 2, + /** South/Down. */ + SCREEN_DIR_S = 3, +} eScreenDir; + +#define SCREEN_DIR_IS_VERTICAL(dir) (ELEM(dir, SCREEN_DIR_N, SCREEN_DIR_S)) +#define SCREEN_DIR_IS_HORIZONTAL(dir) (ELEM(dir, SCREEN_DIR_W, SCREEN_DIR_E)) + +typedef enum eScreenAxis { + /** Horizontal. */ + SCREEN_AXIS_H = 'h', + /** Vertical. */ + SCREEN_AXIS_V = 'v', +} eScreenAxis; + #define AZONESPOTW UI_HEADER_OFFSET /* width of corner #AZone - max */ #define AZONESPOTH (0.6f * U.widget_unit) /* height of corner #AZone */ #define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */ #define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */ -#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */ +/* Edges must be within these to allow joining. */ +#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac) +#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ #define BORDERPADDING (U.dpi_fac + U.pixelsize) @@ -44,6 +69,10 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) void ED_area_data_swap(ScrArea *area_dst, ScrArea *area_src); void region_toggle_hidden(struct bContext *C, ARegion *region, const bool do_fade); +/* screen_draw.c */ +void screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2); +void screen_draw_split_preview(struct ScrArea *area, const eScreenAxis dir_axis, const float fac); + /* screen_edit.c */ bScreen *screen_add(struct Main *bmain, const char *name, const rcti *rect); void screen_data_copy(bScreen *to, bScreen *from); @@ -54,11 +83,17 @@ void screen_change_prepare(bScreen *screen_old, struct Main *bmain, struct bContext *C, wmWindow *win); -ScrArea *area_split( - const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); +ScrArea *area_split(const wmWindow *win, + bScreen *screen, + ScrArea *area, + const eScreenAxis dir_axis, + const float fac, + const bool merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); -int area_getorientation(ScrArea *area, ScrArea *sb); - +eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b); +void area_getoffsets( + ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2); +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ @@ -80,7 +115,7 @@ ScrEdge *screen_geom_find_active_scredge(const wmWindow *win, void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen); short screen_geom_find_area_split_point(const ScrArea *area, const rcti *window_rect, - char dir, + const eScreenAxis dir_axis, float fac); void screen_geom_select_connected_edge(const wmWindow *win, ScrEdge *edge); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 5cd4e8c353b..6b8d4e73f12 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -141,6 +141,14 @@ bool ED_operator_screenactive(bContext *C) return true; } +bool ED_operator_screenactive_nobackground(bContext *C) +{ + if (G.background) { + return false; + } + return ED_operator_screenactive(C); +} + /* XXX added this to prevent anim state to change during renders */ static bool ED_operator_screenactive_norender(bContext *C) { @@ -686,7 +694,9 @@ static bool screen_active_editable(bContext *C) typedef struct sActionzoneData { ScrArea *sa1, *sa2; AZone *az; - int x, y, gesture_dir, modifier; + int x, y; + eScreenDir gesture_dir; + int modifier; } sActionzoneData; /* quick poll to save operators to be created and handled */ @@ -1037,16 +1047,16 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Calculate gesture cardinal direction. */ if (delta_y > abs(delta_x)) { - sad->gesture_dir = 'n'; + sad->gesture_dir = SCREEN_DIR_N; } else if (delta_x >= abs(delta_y)) { - sad->gesture_dir = 'e'; + sad->gesture_dir = SCREEN_DIR_E; } else if (delta_y < -abs(delta_x)) { - sad->gesture_dir = 's'; + sad->gesture_dir = SCREEN_DIR_S; } else { - sad->gesture_dir = 'w'; + sad->gesture_dir = SCREEN_DIR_W; } bool is_gesture; @@ -1063,22 +1073,24 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Are we still in same area? */ if (BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y) == sad->sa1) { /* Same area, so possible split. */ - WM_cursor_set( - win, (ELEM(sad->gesture_dir, 'n', 's')) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); + WM_cursor_set(win, + SCREEN_DIR_IS_VERTICAL(sad->gesture_dir) ? WM_CURSOR_H_SPLIT : + WM_CURSOR_V_SPLIT); is_gesture = (delta_max > split_threshold); } else { /* Different area, so possible join. */ - if (sad->gesture_dir == 'n') { + if (sad->gesture_dir == SCREEN_DIR_N) { WM_cursor_set(win, WM_CURSOR_N_ARROW); } - else if (sad->gesture_dir == 's') { + else if (sad->gesture_dir == SCREEN_DIR_S) { WM_cursor_set(win, WM_CURSOR_S_ARROW); } - else if (sad->gesture_dir == 'e') { + else if (sad->gesture_dir == SCREEN_DIR_E) { WM_cursor_set(win, WM_CURSOR_E_ARROW); } else { + BLI_assert(sad->gesture_dir == SCREEN_DIR_W); WM_cursor_set(win, WM_CURSOR_W_ARROW); } is_gesture = (delta_max > join_threshold); @@ -1387,6 +1399,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Area Close Operator + * + * Close selected area, replace by expanding a neighbor + * \{ */ + +/* operator callback */ +static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + ScrArea *area = CTX_wm_area(C); + if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) { + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool area_close_poll(bContext *C) +{ + if (!ED_operator_areaactive(C)) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + + if (ED_area_is_global(area)) { + return false; + } + + bScreen *screen = CTX_wm_screen(C); + + /* Can this area join with ANY other area? */ + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + if (area_getorientation(ar, area) != -1) { + return true; + } + } + + return false; +} + +static void SCREEN_OT_area_close(wmOperatorType *ot) +{ + ot->name = "Close Area"; + ot->description = "Close selected area"; + ot->idname = "SCREEN_OT_area_close"; + ot->invoke = area_close_invoke; + ot->poll = area_close_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Move Area Edge Operator * \{ */ @@ -1420,7 +1484,7 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) typedef struct sAreaMoveData { int bigger, smaller, origval, step; - char dir; + eScreenAxis dir_axis; enum AreaMoveSnapType { /* Snapping disabled */ SNAP_NONE = 0, @@ -1439,7 +1503,7 @@ typedef struct sAreaMoveData { * need window bounds in order to get correct limits */ static void area_move_set_limits(wmWindow *win, bScreen *screen, - int dir, + const eScreenAxis dir_axis, int *bigger, int *smaller, bool *use_bigger_smaller_snap) @@ -1492,7 +1556,7 @@ static void area_move_set_limits(wmWindow *win, WM_window_rect_calc(win, &window_rect); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { int areamin = ED_area_headersize(); if (area->v1->vec.y > window_rect.ymin) { @@ -1555,8 +1619,8 @@ static bool area_move_init(bContext *C, wmOperator *op) sAreaMoveData *md = MEM_callocN(sizeof(sAreaMoveData), "sAreaMoveData"); op->customdata = md; - md->dir = screen_geom_edge_is_horizontal(actedge) ? 'h' : 'v'; - if (md->dir == 'h') { + md->dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_H : SCREEN_AXIS_V; + if (md->dir_axis == SCREEN_AXIS_H) { md->origval = actedge->v1->vec.y; } else { @@ -1571,7 +1635,8 @@ static bool area_move_init(bContext *C, wmOperator *op) } bool use_bigger_smaller_snap = false; - area_move_set_limits(win, screen, md->dir, &md->bigger, &md->smaller, &use_bigger_smaller_snap); + area_move_set_limits( + win, screen, md->dir_axis, &md->bigger, &md->smaller, &use_bigger_smaller_snap); md->snap_type = use_bigger_smaller_snap ? SNAP_BIGGER_SMALLER_ONLY : SNAP_AREAGRID; @@ -1582,7 +1647,7 @@ static int area_snap_calc_location(const bScreen *screen, const enum AreaMoveSnapType snap_type, const int delta, const int origval, - const int dir, + const eScreenAxis dir_axis, const int bigger, const int smaller) { @@ -1607,7 +1672,7 @@ static int area_snap_calc_location(const bScreen *screen, break; case SNAP_FRACTION_AND_ADJACENT: { - const int axis = (dir == 'v') ? 0 : 1; + const int axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1; int snap_dist_best = INT_MAX; { const float div_array[] = { @@ -1675,7 +1740,7 @@ static int area_snap_calc_location(const bScreen *screen, static void area_move_apply_do(const bContext *C, int delta, const int origval, - const int dir, + const eScreenAxis dir_axis, const int bigger, const int smaller, const enum AreaMoveSnapType snap_type) @@ -1693,11 +1758,12 @@ static void area_move_apply_do(const bContext *C, final_loc = origval + delta; } else { - final_loc = area_snap_calc_location(screen, snap_type, delta, origval, dir, bigger, smaller); + final_loc = area_snap_calc_location( + screen, snap_type, delta, origval, dir_axis, bigger, smaller); } BLI_assert(final_loc != -1); - short axis = (dir == 'v') ? 0 : 1; + short axis = (dir_axis == SCREEN_AXIS_V) ? 0 : 1; ED_screen_verts_iter(win, screen, v1) { @@ -1753,7 +1819,7 @@ static void area_move_apply(bContext *C, wmOperator *op) sAreaMoveData *md = op->customdata; int delta = RNA_int_get(op->ptr, "delta"); - area_move_apply_do(C, delta, md->origval, md->dir, md->bigger, md->smaller, md->snap_type); + area_move_apply_do(C, delta, md->origval, md->dir_axis, md->bigger, md->smaller, md->snap_type); } static void area_move_exit(bContext *C, wmOperator *op) @@ -1818,7 +1884,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event) int x = RNA_int_get(op->ptr, "x"); int y = RNA_int_get(op->ptr, "y"); - int delta = (md->dir == 'v') ? event->x - x : event->y - y; + const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y; RNA_int_set(op->ptr, "delta", delta); area_move_apply(C, op); @@ -1884,7 +1950,7 @@ static void SCREEN_OT_area_move(wmOperatorType *ot) /* * operator state vars: * fac spit point - * dir direction 'v' or 'h' + * dir direction #SCREEN_AXIS_V or #SCREEN_AXIS_H * * operator customdata: * area pointer to (active) area @@ -1921,7 +1987,7 @@ typedef struct sAreaSplitData { int delta; /* delta move edge */ int origmin, origsize; /* to calculate fac, for property storage */ int previewmode; /* draw previewline, then split */ - void *draw_callback; /* call `ED_screen_draw_split_preview` */ + void *draw_callback; /* call `screen_draw_split_preview` */ bool do_snap; ScrEdge *nedge; /* new edge */ @@ -1936,10 +2002,10 @@ static void area_split_draw_cb(const struct wmWindow *UNUSED(win), void *userdat sAreaSplitData *sd = op->customdata; if (sd->sarea) { - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); float fac = RNA_float_get(op->ptr, "factor"); - ED_screen_draw_split_preview(sd->sarea, dir, fac); + screen_draw_split_preview(sd->sarea, dir_axis, fac); } } @@ -1966,14 +2032,18 @@ static bool area_split_init(bContext *C, wmOperator *op) } /* required properties */ - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); /* minimal size */ - if (dir == 'v' && area->winx < 2 * AREAMINX) { - return false; + if (dir_axis == SCREEN_AXIS_V) { + if (area->winx < 2 * AREAMINX) { + return false; + } } - if (dir == 'h' && area->winy < 2 * ED_area_headersize()) { - return false; + else { + if (area->winy < 2 * ED_area_headersize()) { + return false; + } } /* custom data */ @@ -1981,7 +2051,7 @@ static bool area_split_init(bContext *C, wmOperator *op) op->customdata = sd; sd->sarea = area; - if (dir == 'v') { + if (dir_axis == SCREEN_AXIS_V) { sd->origmin = area->v1->vec.x; sd->origsize = area->v4->vec.x - sd->origmin; } @@ -2030,9 +2100,9 @@ static bool area_split_apply(bContext *C, wmOperator *op) sAreaSplitData *sd = (sAreaSplitData *)op->customdata; float fac = RNA_float_get(op->ptr, "factor"); - int dir = RNA_enum_get(op->ptr, "direction"); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); - sd->narea = area_split(win, screen, sd->sarea, dir, fac, 0); /* 0 = no merge */ + sd->narea = area_split(win, screen, sd->sarea, dir_axis, fac, false); /* false = no merge */ if (sd->narea == NULL) { return false; @@ -2049,7 +2119,7 @@ static bool area_split_apply(bContext *C, wmOperator *op) sd->nedge->v1->editflag = 1; sd->nedge->v2->editflag = 1; - if (dir == 'h') { + if (dir_axis == SCREEN_AXIS_H) { sd->origval = sd->nedge->v1->vec.y; } else { @@ -2098,8 +2168,8 @@ static void area_split_exit(bContext *C, wmOperator *op) static void area_split_preview_update_cursor(bContext *C, wmOperator *op) { wmWindow *win = CTX_wm_window(C); - int dir = RNA_enum_get(op->ptr, "direction"); - WM_cursor_set(win, dir == 'h' ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); + const eScreenAxis dir_axis = RNA_enum_get(op->ptr, "direction"); + WM_cursor_set(win, (dir_axis == SCREEN_AXIS_H) ? WM_CURSOR_H_SPLIT : WM_CURSOR_V_SPLIT); } /* UI callback, adds new handler */ @@ -2115,7 +2185,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) PropertyRNA *prop_factor = RNA_struct_find_property(op->ptr, "factor"); PropertyRNA *prop_cursor = RNA_struct_find_property(op->ptr, "cursor"); - int dir; + eScreenAxis dir_axis; if (event->type == EVT_ACTIONZONE_AREA) { sActionzoneData *sad = event->customdata; @@ -2143,12 +2213,12 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) float factor; /* Prepare operator state vars. */ - if (ELEM(sad->gesture_dir, 'n', 's')) { - dir = 'h'; + if (SCREEN_DIR_IS_VERTICAL(sad->gesture_dir)) { + dir_axis = SCREEN_AXIS_H; factor = factor_h; } else { - dir = 'v'; + dir_axis = SCREEN_AXIS_V; factor = factor_v; } @@ -2158,7 +2228,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_property_float_set(op->ptr, prop_factor, factor); - RNA_property_enum_set(op->ptr, prop_dir, dir); + RNA_property_enum_set(op->ptr, prop_dir, dir_axis); /* general init, also non-UI case, adds customdata, sets area and defaults */ if (!area_split_init(C, op)) { @@ -2170,8 +2240,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (area == NULL) { return OPERATOR_CANCELLED; } - dir = RNA_property_enum_get(op->ptr, prop_dir); - if (dir == 'h') { + dir_axis = RNA_property_enum_get(op->ptr, prop_dir); + if (dir_axis == SCREEN_AXIS_H) { RNA_property_float_set( op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx); } @@ -2204,9 +2274,9 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - dir = screen_geom_edge_is_horizontal(actedge) ? 'v' : 'h'; + dir_axis = screen_geom_edge_is_horizontal(actedge) ? SCREEN_AXIS_V : SCREEN_AXIS_H; - RNA_property_enum_set(op->ptr, prop_dir, dir); + RNA_property_enum_set(op->ptr, prop_dir, dir_axis); /* special case, adds customdata, sets defaults */ if (!area_split_menu_init(C, op)) { @@ -2219,7 +2289,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == EVT_ACTIONZONE_AREA) { /* do the split */ if (area_split_apply(C, op)) { - area_move_set_limits(win, screen, dir, &sd->bigger, &sd->smaller, NULL); + area_move_set_limits(win, screen, dir_axis, &sd->bigger, &sd->smaller, NULL); /* add temp handler for edge move or cancel */ G.moving |= G_TRANSFORM_WM; @@ -2307,8 +2377,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) else { if (event->val == KM_PRESS) { if (sd->sarea) { - int dir = RNA_property_enum_get(op->ptr, prop_dir); - RNA_property_enum_set(op->ptr, prop_dir, (dir == 'v') ? 'h' : 'v'); + const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); + RNA_property_enum_set( + op->ptr, prop_dir, (dir_axis == SCREEN_AXIS_V) ? SCREEN_AXIS_H : SCREEN_AXIS_V); area_split_preview_update_cursor(C, op); update_factor = true; } @@ -2329,9 +2400,9 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (update_factor) { - const int dir = RNA_property_enum_get(op->ptr, prop_dir); + const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); - sd->delta = (dir == 'v') ? event->x - sd->origval : event->y - sd->origval; + sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval; if (sd->previewmode == 0) { if (sd->do_snap) { @@ -2339,12 +2410,12 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, - dir, + dir_axis, sd->bigger, sd->smaller); sd->delta = snap_loc - sd->origval; } - area_move_apply_do(C, sd->delta, sd->origval, dir, sd->bigger, sd->smaller, SNAP_NONE); + area_move_apply_do(C, sd->delta, sd->origval, dir_axis, sd->bigger, sd->smaller, SNAP_NONE); } else { if (sd->sarea) { @@ -2355,7 +2426,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (sd->sarea) { ScrArea *area = sd->sarea; - if (dir == 'v') { + if (dir_axis == SCREEN_AXIS_V) { sd->origmin = area->v1->vec.x; sd->origsize = area->v4->vec.x - sd->origmin; } @@ -2371,7 +2442,7 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) SNAP_FRACTION_AND_ADJACENT, sd->delta, sd->origval, - dir, + dir_axis, sd->origmin + sd->origsize, -sd->origmin); @@ -2393,8 +2464,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) } static const EnumPropertyItem prop_direction_items[] = { - {'h', "HORIZONTAL", 0, "Horizontal", ""}, - {'v', "VERTICAL", 0, "Vertical", ""}, + {SCREEN_AXIS_H, "HORIZONTAL", 0, "Horizontal", ""}, + {SCREEN_AXIS_V, "VERTICAL", 0, "Vertical", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -2415,7 +2486,7 @@ static void SCREEN_OT_area_split(wmOperatorType *ot) ot->flag = OPTYPE_BLOCKING | OPTYPE_INTERNAL; /* rna */ - RNA_def_enum(ot->srna, "direction", prop_direction_items, 'h', "Direction", ""); + RNA_def_enum(ot->srna, "direction", prop_direction_items, SCREEN_AXIS_H, "Direction", ""); RNA_def_float(ot->srna, "factor", 0.5f, 0.0, 1.0, "Factor", "", 0.0, 1.0); RNA_def_int_vector( ot->srna, "cursor", 2, NULL, INT_MIN, INT_MAX, "Cursor", "", INT_MIN, INT_MAX); @@ -3210,9 +3281,10 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot) */ typedef struct sAreaJoinData { - ScrArea *sa1; /* first area to be considered */ - ScrArea *sa2; /* second area to be considered */ - void *draw_callback; /* call `ED_screen_draw_join_shape` */ + ScrArea *sa1; /* Potential source area (kept). */ + ScrArea *sa2; /* Potential target area (removed or reduced). */ + eScreenDir dir; /* Direction of potential join. */ + void *draw_callback; /* call #screen_draw_join_highlight */ } sAreaJoinData; @@ -3221,8 +3293,8 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata const wmOperator *op = userdata; sAreaJoinData *sd = op->customdata; - if (sd->sa1 && sd->sa2) { - ED_screen_draw_join_shape(sd->sa1, sd->sa2); + if (sd->sa1 && sd->sa2 && (sd->dir != SCREEN_DIR_NONE)) { + screen_draw_join_highlight(sd->sa1, sd->sa2); } } @@ -3244,6 +3316,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s jd->sa1 = sa1; jd->sa2 = sa2; + jd->dir = SCREEN_DIR_NONE; op->customdata = jd; @@ -3256,7 +3329,7 @@ static bool area_join_init(bContext *C, wmOperator *op, ScrArea *sa1, ScrArea *s static bool area_join_apply(bContext *C, wmOperator *op) { sAreaJoinData *jd = (sAreaJoinData *)op->customdata; - if (!jd) { + if (!jd || (jd->dir == SCREEN_DIR_NONE)) { return false; } @@ -3358,61 +3431,30 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) case MOUSEMOVE: { ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); - int dir = -1; + jd->dir = area_getorientation(jd->sa1, jd->sa2); - if (area) { - if (jd->sa1 != area) { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - else { - /* we are not bordering on the previously selected area - * we check if area has common border with the one marked for removal - * in this case we can swap areas. - */ - dir = area_getorientation(area, jd->sa2); - if (dir != -1) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - } - else { - jd->sa2 = NULL; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } - else { - /* we are back in the area previously selected for keeping - * we swap the areas if possible to allow user to choose */ - if (jd->sa2 != NULL) { - jd->sa1 = jd->sa2; - jd->sa2 = area; - dir = area_getorientation(jd->sa1, jd->sa2); - if (dir == -1) { - printf("oops, didn't expect that!\n"); - } - } - else { - dir = area_getorientation(jd->sa1, area); - if (dir != -1) { - jd->sa2 = area; - } - } - WM_event_add_notifier(C, NC_WINDOW, NULL); - } + if (area == jd->sa1) { + /* Hovering current source, so change direction. */ + jd->sa1 = jd->sa2; + jd->sa2 = area; + jd->dir = area_getorientation(jd->sa1, jd->sa2); } + else if (area != jd->sa2) { + jd->dir = SCREEN_DIR_NONE; + } + + WM_event_add_notifier(C, NC_WINDOW, NULL); - if (dir == 1) { + if (jd->dir == SCREEN_DIR_N) { WM_cursor_set(win, WM_CURSOR_N_ARROW); } - else if (dir == 3) { + else if (jd->dir == SCREEN_DIR_S) { WM_cursor_set(win, WM_CURSOR_S_ARROW); } - else if (dir == 2) { + else if (jd->dir == SCREEN_DIR_E) { WM_cursor_set(win, WM_CURSOR_E_ARROW); } - else if (dir == 0) { + else if (jd->dir == SCREEN_DIR_W) { WM_cursor_set(win, WM_CURSOR_W_ARROW); } else { @@ -3423,6 +3465,10 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { + if (jd->dir == SCREEN_DIR_NONE) { + area_join_cancel(C, op); + return OPERATOR_CANCELLED; + } ED_area_tag_redraw(jd->sa1); ED_area_tag_redraw(jd->sa2); @@ -3493,7 +3539,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent &ptr); /* store initial mouse cursor position. */ RNA_int_set_array(&ptr, "cursor", &event->x); - RNA_enum_set(&ptr, "direction", 'v'); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); /* Horizontal Split */ uiItemFullO(layout, @@ -3506,7 +3552,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent &ptr); /* store initial mouse cursor position. */ RNA_int_set_array(&ptr, "cursor", &event->x); - RNA_enum_set(&ptr, "direction", 'h'); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); if (sa1 && sa2) { uiItemS(layout); @@ -4069,6 +4115,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \name Region Context Menu Operator (Header/Footer/Navbar) * \{ */ +static void screen_area_menu_items(ScrArea *area, uiLayout *layout) +{ + if (ED_area_is_global(area)) { + return; + } + + PointerRNA ptr; + + /* Mouse position as if in middle of area. */ + const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)}; + + /* Vertical Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Vertical Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", loc); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); + + /* Horizontal Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Horizontal Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", &loc[0]); + RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); + + uiItemS(layout); + + if (area->spacetype != SPACE_FILE) { + uiItemO(layout, + area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"), + ICON_NONE, + "SCREEN_OT_screen_full_area"); + + if (!area->full) { + uiItemFullO(layout, + "SCREEN_OT_screen_full_area", + IFACE_("Full Screen Area"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + RNA_boolean_set(&ptr, "use_hide_panels", true); + } + } + + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli"); + uiItemS(layout); + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close"); +} + void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { ScrArea *area = CTX_wm_area(C); @@ -4102,17 +4211,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN if (!ELEM(area->spacetype, SPACE_TOPBAR)) { uiItemS(layout); - uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - } - - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored. */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); + screen_area_menu_items(area, layout); } } @@ -4134,14 +4235,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored... */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { - uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); - } + uiItemS(layout); + screen_area_menu_items(area, layout); } void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) @@ -4883,7 +4978,7 @@ static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) /* api callbacks */ ot->exec = userpref_show_exec; - ot->poll = ED_operator_screenactive; + ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */ } /** \} */ @@ -4960,7 +5055,7 @@ static void SCREEN_OT_drivers_editor_show(struct wmOperatorType *ot) /* api callbacks */ ot->exec = drivers_editor_show_exec; - ot->poll = ED_operator_screenactive; + ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */ } /** \} */ @@ -5004,7 +5099,7 @@ static void SCREEN_OT_info_log_show(struct wmOperatorType *ot) /* api callbacks */ ot->exec = info_log_show_exec; - ot->poll = ED_operator_screenactive; + ot->poll = ED_operator_screenactive_nobackground; } /** \} */ @@ -5336,7 +5431,7 @@ static void context_cycle_prop_get(bScreen *screen, static int space_context_cycle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - const int direction = RNA_enum_get(op->ptr, "direction"); + const eScreenCycle direction = RNA_enum_get(op->ptr, "direction"); PointerRNA ptr; PropertyRNA *prop; @@ -5385,7 +5480,7 @@ static int space_workspace_cycle_invoke(bContext *C, wmOperator *op, const wmEve } Main *bmain = CTX_data_main(C); - const int direction = RNA_enum_get(op->ptr, "direction"); + const eScreenCycle direction = RNA_enum_get(op->ptr, "direction"); WorkSpace *workspace_src = WM_window_get_active_workspace(win); WorkSpace *workspace_dst = NULL; @@ -5461,6 +5556,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_area_move); WM_operatortype_append(SCREEN_OT_area_split); WM_operatortype_append(SCREEN_OT_area_join); + WM_operatortype_append(SCREEN_OT_area_close); WM_operatortype_append(SCREEN_OT_area_options); WM_operatortype_append(SCREEN_OT_area_dupli); WM_operatortype_append(SCREEN_OT_area_swap); diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 7740fb42c37..3829aeebbeb 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1324,6 +1324,13 @@ static bool paint_cursor_context_init(bContext *C, copy_v3_fl(pcontext->outline_col, 0.8f); } + const bool is_brush_tool = PAINT_brush_tool_poll(C); + if (!is_brush_tool) { + /* Use a default color for tools that are not brushes. */ + pcontext->outline_alpha = 0.8f; + copy_v3_fl(pcontext->outline_col, 0.8f); + } + pcontext->is_stroke_active = pcontext->ups->stroke_active; return true; @@ -1610,9 +1617,11 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * pcontext->radius); } + const bool is_brush_tool = PAINT_brush_tool_poll(pcontext->C); + /* Pose brush updates and rotation origins. */ - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) { /* Just after switching to the Pose Brush, the active vertex can be the same and the * cursor won't be tagged to update, so always initialize the preview chain if it is * null before drawing it. */ @@ -1645,7 +1654,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * 2); } - if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { paint_cursor_preview_boundary_data_update(pcontext, update_previews); paint_cursor_preview_boundary_data_pivot_draw(pcontext); } @@ -1666,17 +1675,18 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * GPU_matrix_mul(pcontext->vc.obact->obmat); /* Drawing Cursor overlays in 3D object space. */ - if (brush->sculpt_tool == SCULPT_TOOL_GRAB && (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_GRAB && + (brush->flag & BRUSH_GRAB_ACTIVE_VERTEX)) { SCULPT_geometry_preview_lines_update(pcontext->C, pcontext->ss, pcontext->radius); sculpt_geometry_preview_lines_draw( pcontext->pos, pcontext->brush, pcontext->is_multires, pcontext->ss); } - if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_POSE) { paint_cursor_pose_brush_segments_draw(pcontext); } - if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { SCULPT_boundary_edges_preview_draw( pcontext->pos, pcontext->ss, pcontext->outline_col, pcontext->outline_alpha); SCULPT_boundary_pivot_line_preview_draw(pcontext->pos, pcontext->ss); @@ -1692,7 +1702,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_draw_main_inactive_cursor(pcontext); /* Cloth brush local simulation areas. */ - if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_CLOTH && brush->cloth_simulation_area_type != BRUSH_CLOTH_SIMULATION_AREA_GLOBAL) { const float white[3] = {1.0f, 1.0f, 1.0f}; const float zero_v[3] = {0.0f}; @@ -1704,7 +1714,7 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * } /* Layer brush height. */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + if (is_brush_tool && brush->sculpt_tool == SCULPT_TOOL_LAYER) { SCULPT_layer_brush_height_preview_draw(pcontext->pos, brush, pcontext->radius, diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 3ca0d853d6a..7341d984c91 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -87,7 +87,7 @@ struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke); void *paint_stroke_mode_data(struct PaintStroke *stroke); float paint_stroke_distance_get(struct PaintStroke *stroke); void paint_stroke_set_mode_data(struct PaintStroke *stroke, void *mode_data); -bool paint_poll(struct bContext *C); +bool PAINT_brush_tool_poll(struct bContext *C); void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); void paint_cursor_delete_textures(void); @@ -304,7 +304,7 @@ bool paint_curve_poll(struct bContext *C); bool facemask_paint_poll(struct bContext *C); void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm); -void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm); +void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm); /* stroke operator */ typedef enum BrushStrokeMode { diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 7671f69ee05..b6ae6f8bee7 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -1650,7 +1650,7 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) ot->modal = WM_gesture_lasso_modal; ot->exec = paint_mask_gesture_lasso_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1671,7 +1671,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot) ot->modal = WM_gesture_box_modal; ot->exec = paint_mask_gesture_box_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1692,7 +1692,7 @@ void PAINT_OT_mask_line_gesture(wmOperatorType *ot) ot->modal = WM_gesture_straightline_oneshot_modal; ot->exec = paint_mask_gesture_line_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1713,6 +1713,8 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot) ot->modal = WM_gesture_lasso_modal; ot->exec = face_set_gesture_lasso_exec; + ot->poll = SCULPT_mode_poll_view3d; + /* Properties. */ WM_operator_properties_gesture_lasso(ot); sculpt_gesture_operator_properties(ot); @@ -1728,7 +1730,7 @@ void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot) ot->modal = WM_gesture_box_modal; ot->exec = face_set_gesture_box_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1747,7 +1749,7 @@ void SCULPT_OT_trim_lasso_gesture(wmOperatorType *ot) ot->modal = WM_gesture_lasso_modal; ot->exec = sculpt_trim_gesture_lasso_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1768,7 +1770,7 @@ void SCULPT_OT_trim_box_gesture(wmOperatorType *ot) ot->modal = WM_gesture_box_modal; ot->exec = sculpt_trim_gesture_box_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; @@ -1789,7 +1791,7 @@ void SCULPT_OT_project_line_gesture(wmOperatorType *ot) ot->modal = WM_gesture_straightline_oneshot_modal; ot->exec = project_gesture_line_exec; - ot->poll = SCULPT_mode_poll; + ot->poll = SCULPT_mode_poll_view3d; ot->flag = OPTYPE_REGISTER; diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index e1dc8fa30b9..fed89e02e8f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -139,13 +139,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + const bool is_gpencil = (brush && brush->gpencil_settings != NULL); // Object *ob = CTX_data_active_object(C); float scalar = RNA_float_get(op->ptr, "scalar"); if (brush) { /* pixel radius */ { - const int old_size = BKE_brush_size_get(scene, brush); + const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size; int size = (int)(scalar * old_size); if (abs(old_size - size) < U.pixelsize) { @@ -156,6 +157,12 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) size -= U.pixelsize; } } + /* Grease Pencil does not use unified size. */ + if (is_gpencil) { + brush->size = max_ii(size, 1); + WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush); + return OPERATOR_FINISHED; + } BKE_brush_size_set(scene, brush, size); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 57b1102219e..b093f07226e 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -1184,14 +1184,43 @@ static void paint_line_strokes_spacing(bContext *C, const float new_pos[2]) { UnifiedPaintSettings *ups = stroke->ups; + Paint *paint = BKE_paint_get_active_from_context(C); + Brush *brush = BKE_paint_brush(paint); + ePaintMode mode = BKE_paintmode_get_active_from_context(C); + ARegion *region = CTX_wm_region(C); + + const bool use_scene_spacing = paint_stroke_use_scene_spacing(brush, mode); float mouse[2], dmouse[2]; float length; + float d_world_space_position[3] = {0.0f}; + float world_space_position_old[3], world_space_position_new[3]; - sub_v2_v2v2(dmouse, new_pos, old_pos); copy_v2_v2(stroke->last_mouse_position, old_pos); - length = normalize_v2(dmouse); + if (use_scene_spacing) { + bool hit_old = SCULPT_stroke_get_location(C, world_space_position_old, old_pos); + bool hit_new = SCULPT_stroke_get_location(C, world_space_position_new, new_pos); + mul_m4_v3(stroke->vc.obact->obmat, world_space_position_old); + mul_m4_v3(stroke->vc.obact->obmat, world_space_position_new); + if (hit_old && hit_new && stroke->stroke_over_mesh) { + sub_v3_v3v3(d_world_space_position, world_space_position_new, world_space_position_old); + length = len_v3(d_world_space_position); + stroke->stroke_over_mesh = true; + } + else { + length = 0.0f; + zero_v3(d_world_space_position); + stroke->stroke_over_mesh = hit_new; + if (stroke->stroke_over_mesh) { + copy_v3_v3(stroke->last_world_space_position, world_space_position_old); + } + } + } + else { + sub_v2_v2v2(dmouse, new_pos, old_pos); + length = normalize_v2(dmouse); + } BLI_assert(length >= 0.0f); @@ -1205,8 +1234,18 @@ static void paint_line_strokes_spacing(bContext *C, *length_residue = 0.0; if (length >= spacing) { - mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; - mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; + if (use_scene_spacing) { + float final_world_space_position[3]; + normalize_v3(d_world_space_position); + mul_v3_v3fl(final_world_space_position, d_world_space_position, spacing_final); + add_v3_v3v3( + final_world_space_position, world_space_position_old, final_world_space_position); + ED_view3d_project(region, final_world_space_position, mouse); + } + else { + mouse[0] = stroke->last_mouse_position[0] + dmouse[0] * spacing_final; + mouse[1] = stroke->last_mouse_position[1] + dmouse[1] * spacing_final; + } ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0); @@ -1302,6 +1341,13 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str if (!stroke->stroke_started) { stroke->last_pressure = 1.0; copy_v2_v2(stroke->last_mouse_position, data + 2 * j); + + if (paint_stroke_use_scene_spacing(br, BKE_paintmode_get_active_from_context(C))) { + stroke->stroke_over_mesh = SCULPT_stroke_get_location( + C, stroke->last_world_space_position, data + 2 * j); + mul_m4_v3(stroke->vc.obact->obmat, stroke->last_world_space_position); + } + stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position); if (stroke->stroke_started) { @@ -1416,7 +1462,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (paint_supports_smooth_stroke(br, mode)) { stroke->stroke_cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_smooth_cursor, stroke); + SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_smooth_cursor, stroke); } stroke->stroke_init = true; @@ -1443,7 +1489,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event) if (br->flag & BRUSH_LINE) { stroke->stroke_cursor = WM_paint_cursor_activate( - SPACE_TYPE_ANY, RGN_TYPE_ANY, paint_poll, paint_draw_line_cursor, stroke); + SPACE_TYPE_ANY, RGN_TYPE_ANY, PAINT_brush_tool_poll, paint_draw_line_cursor, stroke); } first_dab = true; @@ -1613,7 +1659,7 @@ void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data) stroke->mode_data = mode_data; } -bool paint_poll(bContext *C) +bool PAINT_brush_tool_poll(bContext *C) { Paint *p = BKE_paint_get_active_from_context(C); Object *ob = CTX_data_active_object(C); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 964e5bdaa90..2e1dd928f96 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -780,6 +780,10 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, iter->neighbors = iter->neighbors_fixed; for (int i = 0; i < ss->pmap[index].count; i++) { + if (ss->face_sets[vert_map->indices[i]] < 0) { + /* Skip connectivity from hidden faces. */ + continue; + } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; uint f_adj_v[2]; if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { @@ -6602,7 +6606,7 @@ bool SCULPT_poll_view3d(bContext *C) bool SCULPT_poll(bContext *C) { - return SCULPT_mode_poll(C) && paint_poll(C); + return SCULPT_mode_poll(C) && PAINT_brush_tool_poll(C); } static const char *sculpt_tool_name(Sculpt *sd) diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 8b8ed42a694..40874375772 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -273,7 +273,7 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache */ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f) { - if (ss->face_sets[f] <= 0) { + if (expand_cache->original_face_sets[f] <= 0) { return false; } @@ -1398,6 +1398,13 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa { const int totfaces = ss->totfaces; for (int i = 0; i < totfaces; i++) { + if (expand_cache->original_face_sets[i] <= 0) { + /* Do not modify hidden Face Sets, even when restoring the IDs state. */ + continue; + } + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + continue; + } ss->face_sets[i] = expand_cache->initial_face_sets[i]; } } @@ -1892,13 +1899,22 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event * The faces that were using the `delete_id` Face Set are filled * using the content from their neighbors. */ -static void sculpt_expand_delete_face_set_id( - int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id) +static void sculpt_expand_delete_face_set_id(int *r_face_sets, + SculptSession *ss, + ExpandCache *expand_cache, + Mesh *mesh, + const int delete_id) { + const int totface = ss->totfaces; + MeshElemMap *pmap = ss->pmap; + /* Check that all the face sets IDs in the mesh are not equal to `delete_id` * before attempting to delete it. */ bool all_same_id = true; for (int i = 0; i < totface; i++) { + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + continue; + } if (r_face_sets[i] != delete_id) { all_same_id = false; break; @@ -1921,6 +1937,7 @@ static void sculpt_expand_delete_face_set_id( } while (BLI_LINKSTACK_SIZE(queue)) { + bool any_updated = false; while (BLI_LINKSTACK_SIZE(queue)) { const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); int other_id = delete_id; @@ -1931,6 +1948,10 @@ static void sculpt_expand_delete_face_set_id( for (int i = 0; i < vert_map->count; i++) { const int neighbor_face_index = vert_map->indices[i]; + if (expand_cache->original_face_sets[neighbor_face_index] <= 0) { + /* Skip picking IDs from hidden Face Sets. */ + continue; + } if (r_face_sets[neighbor_face_index] != delete_id) { other_id = r_face_sets[neighbor_face_index]; } @@ -1938,18 +1959,36 @@ static void sculpt_expand_delete_face_set_id( } if (other_id != delete_id) { + any_updated = true; r_face_sets[f_index] = other_id; } else { BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index)); } } + if (!any_updated) { + /* No Face Sets where updated in this iteration, which means that no more content to keep + * filling the polys of the deleted Face Set was found. Break to avoid entering an infinite + * loop trying to search for those polys again. */ + break; + } BLI_LINKSTACK_SWAP(queue, queue_next); } BLI_LINKSTACK_FREE(queue); BLI_LINKSTACK_FREE(queue_next); + + /* Ensure that the visibility state of the modified Face Sets is the same as the original ones. + */ + for (int i = 0; i < totface; i++) { + if (expand_cache->original_face_sets[i] >= 0) { + r_face_sets[i] = abs(r_face_sets[i]); + } + else { + r_face_sets[i] = -abs(r_face_sets[i]); + } + } } static void sculpt_expand_cache_initial_config_set(bContext *C, @@ -2070,9 +2109,9 @@ static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *even if (ss->expand_cache->modify_active_face_set) { sculpt_expand_delete_face_set_id(ss->expand_cache->initial_face_sets, + ss, + ss->expand_cache, ob->data, - ss->pmap, - ss->totfaces, ss->expand_cache->next_face_set); } diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 17c4beab086..bdbdb75732a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -949,10 +949,6 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); - if (BKE_pbvh_type(pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(ob->data); - } - SCULPT_tag_update_overlays(C); return OPERATOR_FINISHED; diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 4d2a1bf13dc..587ce346428 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -197,8 +197,9 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(disp, segments[ik].weights[vd.index]); /* Apply the vertex mask to the displacement. */ - float mask = vd.mask ? *vd.mask : 0.0f; - mul_v3_fl(disp, 1.0f - mask); + const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ add_v3_v3(total_disp, disp); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 85616f6356d..fe0a53ae964 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -52,6 +52,7 @@ #include "RNA_enum_types.h" #include "SEQ_iterator.h" +#include "SEQ_utils.h" #include "UI_interface.h" @@ -258,7 +259,7 @@ static void sound_update_animation_flags(Scene *scene) scene->id.tag |= LIB_TAG_DOIT; SEQ_ALL_BEGIN (scene->ed, seq) { - SEQ_iterator_recursive_apply(seq, sound_update_animation_flags_fn, scene); + SEQ_recursive_apply(seq, sound_update_animation_flags_fn, scene); } SEQ_ALL_END; diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index c71e5e49d8d..b5f6874fcfc 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -50,7 +50,7 @@ if(WITH_FREESTYLE) endif() if(WITH_EXPERIMENTAL_FEATURES) - add_definitions(-DWITH_GEOMETRY_NODES) + add_definitions(-DWITH_SIMULATION_DATABLOCK) add_definitions(-DWITH_POINT_CLOUD) add_definitions(-DWITH_HAIR_NODES) endif() diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index c42e2531f25..aeb2c04656e 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -975,23 +975,13 @@ int /*eContextResult*/ buttons_context(const bContext *C, if (matnr < 0) { matnr = 0; } - CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, &ob->mat[matnr]); + /* Keep aligned with rna_Object_material_slots_get. */ + CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1)); } } return CTX_RESULT_OK; } - if (CTX_data_equals(member, "modifier")) { - PointerRNA *ptr = get_pointer_type(path, &RNA_Modifier); - - if (ptr != NULL && !RNA_pointer_is_null(ptr)) { - Object *ob = (Object *)ptr->owner_id; - ModifierData *md = ptr->data; - CTX_data_pointer_set(result, &ob->id, &RNA_Modifier, md); - return CTX_RESULT_OK; - } - return CTX_RESULT_NO_DATA; - } if (CTX_data_equals(member, "texture_user")) { ButsContextTexture *ct = sbuts->texuser; diff --git a/source/blender/editors/space_buttons/buttons_intern.h b/source/blender/editors/space_buttons/buttons_intern.h index 74e7bc11c26..7564fa4b930 100644 --- a/source/blender/editors/space_buttons/buttons_intern.h +++ b/source/blender/editors/space_buttons/buttons_intern.h @@ -35,6 +35,7 @@ struct bContext; struct bContextDataResult; struct bNode; struct bNodeTree; +struct bNodeSocket; struct wmOperatorType; struct SpaceProperties_Runtime { @@ -66,6 +67,7 @@ typedef struct ButsTextureUser { struct bNodeTree *ntree; struct bNode *node; + struct bNodeSocket *socket; const char *category; int icon; diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 43128ed00fa..97e3cb750c1 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -75,15 +75,16 @@ static SpaceProperties *find_space_properties(const bContext *C); /************************* Texture User **************************/ -static void buttons_texture_user_node_property_add(ListBase *users, - ID *id, - PointerRNA ptr, - PropertyRNA *prop, - bNodeTree *ntree, - bNode *node, - const char *category, - int icon, - const char *name) +static void buttons_texture_user_socket_property_add(ListBase *users, + ID *id, + PointerRNA ptr, + PropertyRNA *prop, + bNodeTree *ntree, + bNode *node, + bNodeSocket *socket, + const char *category, + int icon, + const char *name) { ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser"); @@ -92,6 +93,7 @@ static void buttons_texture_user_node_property_add(ListBase *users, user->prop = prop; user->ntree = ntree; user->node = node; + user->socket = socket; user->category = category; user->icon = icon; user->name = name; @@ -181,25 +183,29 @@ static void buttons_texture_modifier_geonodes_users_add(Object *ob, /* Recurse into the node group */ buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users); } - else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { - RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr); - prop = RNA_struct_find_property(&ptr, "texture"); - if (prop == NULL) { + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->flag & SOCK_UNAVAIL) { + continue; + } + if (socket->type != SOCK_TEXTURE) { continue; } + RNA_pointer_create(&node_tree->id, &RNA_NodeSocket, socket, &ptr); + prop = RNA_struct_find_property(&ptr, "default_value"); PointerRNA texptr = RNA_property_pointer_get(&ptr, prop); Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL; if (tex != NULL) { - buttons_texture_user_node_property_add(users, - &ob->id, - ptr, - prop, - node_tree, - node, - N_("Geometry Nodes"), - RNA_struct_ui_icon(ptr.type), - nmd->modifier.name); + buttons_texture_user_socket_property_add(users, + &ob->id, + ptr, + prop, + node_tree, + node, + socket, + N_("Geometry Nodes"), + RNA_struct_ui_icon(ptr.type), + nmd->modifier.name); } } } diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index 9882304d97d..0a99d1020f6 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -24,8 +24,11 @@ #include "MEM_guardedalloc.h" #include "BLI_math.h" +#include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLT_translation.h" + #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" @@ -398,6 +401,28 @@ static int track_markers_modal(bContext *C, wmOperator *UNUSED(op), const wmEven return OPERATOR_PASS_THROUGH; } +static char *track_markers_desc(bContext *UNUSED(C), wmOperatorType *UNUSED(op), PointerRNA *ptr) +{ + const bool backwards = RNA_boolean_get(ptr, "backwards"); + const bool sequence = RNA_boolean_get(ptr, "sequence"); + + if (backwards && sequence) { + return BLI_strdup(TIP_("Track the selected markers backward for the entire clip")); + } + if (backwards && !sequence) { + return BLI_strdup(TIP_("Track the selected markers backward by one frame")); + } + if (!backwards && sequence) { + return BLI_strdup(TIP_("Track the selected markers forward for the entire clip")); + } + if (!backwards && !sequence) { + return BLI_strdup(TIP_("Track the selected markers forward by one frame")); + } + + /* Use default description. */ + return NULL; +} + void CLIP_OT_track_markers(wmOperatorType *ot) { /* identifiers */ @@ -410,6 +435,7 @@ void CLIP_OT_track_markers(wmOperatorType *ot) ot->invoke = track_markers_invoke; ot->modal = track_markers_modal; ot->poll = ED_space_clip_tracking_poll; + ot->get_description = track_markers_desc; /* flags */ ot->flag = OPTYPE_UNDO; diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index c1dcf2e56d3..189b9b4c874 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -323,6 +323,7 @@ static void file_draw_preview(uiBlock *block, int ex, ey; bool show_outline = !is_icon && (file->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_BLENDER)); + const bool is_offline = (file->attributes & FILE_ATTR_OFFLINE); BLI_assert(imb != NULL); @@ -419,14 +420,14 @@ static void file_draw_preview(uiBlock *block, icon_x, icon_y, icon, icon_aspect / U.dpi_fac, icon_opacity, 0.0f, icon_color, false); } - if (is_link) { - /* Arrow icon to indicate it is a shortcut, link, or alias. */ + if (is_link || is_offline) { + /* Icon at bottom to indicate it is a shortcut, link, alias, or offline. */ float icon_x, icon_y; icon_x = xco + (2.0f * UI_DPI_FAC); icon_y = yco + (2.0f * UI_DPI_FAC); - const int arrow = ICON_LOOP_FORWARDS; + const int arrow = is_link ? ICON_LOOP_FORWARDS : ICON_URL; if (!is_icon) { - /* Arrow at very bottom-left if preview style. */ + /* At very bottom-left if preview style. */ const uchar dark[4] = {0, 0, 0, 255}; const uchar light[4] = {255, 255, 255, 255}; UI_icon_draw_ex(icon_x + 1, icon_y - 1, arrow, 1.0f / U.dpi_fac, 0.2f, 0.0f, dark, false); diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 309b280177c..f1d0197b9ae 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -63,6 +63,7 @@ void FILE_OT_bookmark_move(struct wmOperatorType *ot); void FILE_OT_reset_recent(wmOperatorType *ot); void FILE_OT_hidedot(struct wmOperatorType *ot); void FILE_OT_execute(struct wmOperatorType *ot); +void FILE_OT_mouse_execute(struct wmOperatorType *ot); void FILE_OT_cancel(struct wmOperatorType *ot); void FILE_OT_parent(struct wmOperatorType *ot); void FILE_OT_directory_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 856bd5b1bc3..61f3c046550 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -536,6 +536,14 @@ void FILE_OT_select_box(wmOperatorType *ot) /** \name Select Pick Operator * \{ */ +static rcti file_select_mval_to_select_rect(const int mval[2]) +{ + rcti rect; + rect.xmin = rect.xmax = mval[0]; + rect.ymin = rect.ymax = mval[1]; + return rect; +} + static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); @@ -551,8 +559,7 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - rect.xmin = rect.xmax = event->mval[0]; - rect.ymin = rect.ymax = event->mval[1]; + rect = file_select_mval_to_select_rect(event->mval); if (!ED_fileselect_layout_is_inside_pt(sfile->layout, ®ion->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; @@ -1711,14 +1718,14 @@ bool file_draw_check_exists(SpaceFile *sfile) /** \name Execute File Window Operator * \{ */ -static int file_exec(bContext *C, wmOperator *exec_op) +/** + * Execute the active file, as set in the file select params. + */ +static bool file_execute(bContext *C, SpaceFile *sfile) { Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - SpaceFile *sfile = CTX_wm_space_file(C); FileSelectParams *params = ED_fileselect_get_active_params(sfile); - struct FileDirEntry *file = filelist_file(sfile->files, params->active_file); - char filepath[FILE_MAX]; + FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file && file->redirection_path) { /* redirection_path is an absolute path that takes precedence @@ -1753,22 +1760,7 @@ static int file_exec(bContext *C, wmOperator *exec_op) /* opening file - sends events now, so things get handled on windowqueue level */ else if (sfile->op) { wmOperator *op = sfile->op; - - /* When used as a macro, for double-click, to prevent closing when double-clicking on item. */ - if (RNA_boolean_get(exec_op->ptr, "need_active")) { - const int numfiles = filelist_files_ensure(sfile->files); - int i, active = 0; - - for (i = 0; i < numfiles; i++) { - if (filelist_entry_select_index_get(sfile->files, i, CHECK_ALL)) { - active = 1; - break; - } - } - if (active == 0) { - return OPERATOR_CANCELLED; - } - } + char filepath[FILE_MAX]; sfile->op = NULL; @@ -1788,50 +1780,94 @@ static int file_exec(bContext *C, wmOperator *exec_op) BKE_appdir_folder_id_create(BLENDER_USER_CONFIG, NULL), BLENDER_BOOKMARK_FILE); fsmenu_write_file(ED_fsmenu_get(), filepath); - WM_event_fileselect_event(wm, op, EVT_FILESELECT_EXEC); + WM_event_fileselect_event(CTX_wm_manager(C), op, EVT_FILESELECT_EXEC); } return OPERATOR_FINISHED; } -static int file_exec_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int file_exec(bContext *C, wmOperator *UNUSED(op)) { - ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - if (!ED_fileselect_layout_is_inside_pt( - sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { - return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; } - return file_exec(C, op); + return OPERATOR_FINISHED; } void FILE_OT_execute(struct wmOperatorType *ot) { - PropertyRNA *prop; - /* identifiers */ ot->name = "Execute File Window"; ot->description = "Execute selected file"; ot->idname = "FILE_OT_execute"; /* api callbacks */ - ot->invoke = file_exec_invoke; ot->exec = file_exec; /* Important since handler is on window level. * * Avoid using #file_operator_poll since this is also used for entering directories * which is used even when the file manager doesn't have an operator. */ ot->poll = ED_operator_file_active; +} - /* properties */ - prop = RNA_def_boolean(ot->srna, - "need_active", - 0, - "Need Active", - "Only execute if there's an active selected file in the file list"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); +/** + * \returns false if the mouse doesn't hover a selectable item. + */ +static bool file_ensure_hovered_is_active(bContext *C, const wmEvent *event) +{ + rcti rect = file_select_mval_to_select_rect(event->mval); + if (file_select(C, &rect, FILE_SEL_ADD, false, false) == FILE_SELECT_NOTHING) { + return false; + } + + return true; +} + +static int file_execute_mouse_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + if (!ED_fileselect_layout_is_inside_pt( + sfile->layout, ®ion->v2d, event->mval[0], event->mval[1])) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + /* Note that this isn't needed practically, because the keymap already activates the hovered item + * on mouse-press. This execute operator is called afterwards on the double-click event then. + * However relying on this would be fragile and could break with keymap changes, so better to + * have this mouse-execute operator that makes sure once more that the hovered file is active. */ + if (!file_ensure_hovered_is_active(C, event)) { + return OPERATOR_CANCELLED; + } + + if (!file_execute(C, sfile)) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +/** + * Variation of #FILE_OT_execute that accounts for some mouse specific handling. Otherwise calls + * the same logic. + */ +void FILE_OT_mouse_execute(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Execute File"; + ot->description = + "Perform the current execute action for the file under the cursor (e.g. open the file)"; + ot->idname = "FILE_OT_mouse_execute"; + + /* api callbacks */ + ot->invoke = file_execute_mouse_invoke; + ot->poll = ED_operator_file_active; + + ot->flag = OPTYPE_INTERNAL; } /** \} */ diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index c2b3a7ac0ee..37a32164cfc 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1601,37 +1601,51 @@ static void filelist_cache_previews_push(FileList *filelist, FileDirEntry *entry BLI_assert(cache->flags & FLC_PREVIEWS_ACTIVE); - if (!entry->preview_icon_id && !(entry->flags & FILE_ENTRY_INVALID_PREVIEW) && - (entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | - FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) { - FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); - FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; + if (!entry->preview_icon_id && (entry->attributes & FILE_ATTR_OFFLINE)) { + entry->flags |= FILE_ENTRY_INVALID_PREVIEW; + return; + } - if (entry->redirection_path) { - BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); - } - else { - BLI_join_dirfile( - preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); - } + if (entry->preview_icon_id) { + return; + } - preview->index = index; - preview->flags = entry->typeflag; - preview->in_memory_preview = intern_entry->local_data.preview_image; - preview->icon_id = 0; - // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + if (entry->flags & FILE_ENTRY_INVALID_PREVIEW) { + return; + } + + if (!(entry->typeflag & (FILE_TYPE_IMAGE | FILE_TYPE_MOVIE | FILE_TYPE_FTFONT | + FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP | FILE_TYPE_BLENDERLIB))) { + return; + } - filelist_cache_preview_ensure_running(cache); + FileListEntryPreview *preview = MEM_mallocN(sizeof(*preview), __func__); + FileListInternEntry *intern_entry = filelist->filelist_intern.filtered[index]; - FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), - __func__); - preview_taskdata->preview = preview; - BLI_task_pool_push(cache->previews_pool, - filelist_cache_preview_runf, - preview_taskdata, - true, - filelist_cache_preview_freef); + if (entry->redirection_path) { + BLI_strncpy(preview->path, entry->redirection_path, FILE_MAXDIR); + } + else { + BLI_join_dirfile( + preview->path, sizeof(preview->path), filelist->filelist.root, entry->relpath); } + + preview->index = index; + preview->flags = entry->typeflag; + preview->in_memory_preview = intern_entry->local_data.preview_image; + preview->icon_id = 0; + // printf("%s: %d - %s - %p\n", __func__, preview->index, preview->path, preview->img); + + filelist_cache_preview_ensure_running(cache); + + FileListEntryPreviewTaskData *preview_taskdata = MEM_mallocN(sizeof(*preview_taskdata), + __func__); + preview_taskdata->preview = preview; + BLI_task_pool_push(cache->previews_pool, + filelist_cache_preview_runf, + preview_taskdata, + true, + filelist_cache_preview_freef); } static void filelist_cache_init(FileListEntryCache *cache, size_t cache_size) @@ -2360,17 +2374,19 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index) // printf("Re-queueing previews...\n"); - /* Note we try to preview first images around given index - i.e. assumed visible ones. */ if (cache->flags & FLC_PREVIEWS_ACTIVE) { - for (i = 0; ((index + i) < end_index) || ((index - i) >= start_index); i++) { - if ((index - i) >= start_index) { - const int idx = (cache->block_cursor + (index - start_index) - i) % cache_size; - filelist_cache_previews_push(filelist, cache->block_entries[idx], index - i); - } - if ((index + i) < end_index) { - const int idx = (cache->block_cursor + (index - start_index) + i) % cache_size; - filelist_cache_previews_push(filelist, cache->block_entries[idx], index + i); - } + /* Note we try to preview first images around given index - i.e. assumed visible ones. */ + int block_index = cache->block_cursor + (index - start_index); + int offs_max = max_ii(end_index - index, index - start_index); + for (i = 0; i <= offs_max; i++) { + int offs = i; + do { + int offs_idx = index + offs; + if (start_index <= offs_idx && offs_idx < end_index) { + int offs_block_idx = (block_index + offs) % (int)cache_size; + filelist_cache_previews_push(filelist, cache->block_entries[offs_block_idx], offs_idx); + } + } while ((offs = -offs) < 0); /* Switch between negative and positive offset. */ } } diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index 993b1d9b69c..12bc0a68ca6 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -467,6 +467,15 @@ static void file_listener(const wmSpaceTypeListenerParams *params) break; } break; + case NC_ID: { + switch (wmn->action) { + case NA_RENAME: + /* Force list to update sorting (with a full reset for now). */ + file_reset_filelist_showing_main_data(area, sfile); + break; + } + break; + } case NC_ASSET: { switch (wmn->action) { case NA_SELECTED: @@ -654,6 +663,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_highlight); WM_operatortype_append(FILE_OT_sort_column_ui_context); WM_operatortype_append(FILE_OT_execute); + WM_operatortype_append(FILE_OT_mouse_execute); WM_operatortype_append(FILE_OT_cancel); WM_operatortype_append(FILE_OT_parent); WM_operatortype_append(FILE_OT_previous); diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 0583628be43..aaf9852e212 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -56,7 +56,9 @@ #include "info_intern.h" -/********************* pack blend file libraries operator *********************/ +/* -------------------------------------------------------------------- */ +/** \name Pack Blend File Libraries Operator + * \{ */ static int pack_libraries_exec(bContext *C, wmOperator *op) { @@ -70,9 +72,11 @@ static int pack_libraries_exec(bContext *C, wmOperator *op) void FILE_OT_pack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack Blender Libraries"; + ot->name = "Pack Linked Libraries"; ot->idname = "FILE_OT_pack_libraries"; - ot->description = "Pack all used Blender library files into the current .blend"; + ot->description = + "Store all data-blocks linked from other .blend files in the current .blend file. " + "Library references are preserved so the linked data-blocks can be unpacked again"; /* api callbacks */ ot->exec = pack_libraries_exec; @@ -90,18 +94,24 @@ static int unpack_libraries_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Blend File Libraries Operator + * \{ */ + static int unpack_libraries_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { return WM_operator_confirm_message( - C, op, "Unpack Blender Libraries - creates directories, all new paths should work"); + C, op, "Unpack Linked Libraries - creates directories, all new paths should work"); } void FILE_OT_unpack_libraries(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack Blender Libraries"; + ot->name = "Unpack Linked Libraries"; ot->idname = "FILE_OT_unpack_libraries"; - ot->description = "Unpack all used Blender library files from this .blend file"; + ot->description = "Restore all packed linked data-blocks to their original locations"; /* api callbacks */ ot->invoke = unpack_libraries_invoke; @@ -111,7 +121,11 @@ void FILE_OT_unpack_libraries(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* toggle auto-pack operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Toggle Auto-Pack Operator + * \{ */ static int autopack_toggle_exec(bContext *C, wmOperator *op) { @@ -131,7 +145,7 @@ static int autopack_toggle_exec(bContext *C, wmOperator *op) void FILE_OT_autopack_toggle(wmOperatorType *ot) { /* identifiers */ - ot->name = "Automatically Pack Into .blend"; + ot->name = "Automatically Pack Resources"; ot->idname = "FILE_OT_autopack_toggle"; ot->description = "Automatically pack all external files into the .blend file"; @@ -142,7 +156,11 @@ void FILE_OT_autopack_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* pack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Pack All Operator + * \{ */ static int pack_all_exec(bContext *C, wmOperator *op) { @@ -176,9 +194,9 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev void FILE_OT_pack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Pack All Into .blend"; + ot->name = "Pack Resources"; ot->idname = "FILE_OT_pack_all"; - ot->description = "Pack all used external files into the .blend"; + ot->description = "Pack all used external files into this .blend"; /* api callbacks */ ot->exec = pack_all_exec; @@ -188,7 +206,11 @@ void FILE_OT_pack_all(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* unpack all operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack All Operator + * \{ */ static const EnumPropertyItem unpack_all_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use files in current directory (create when necessary)", ""}, @@ -263,7 +285,7 @@ static int unpack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( void FILE_OT_unpack_all(wmOperatorType *ot) { /* identifiers */ - ot->name = "Unpack All Into Files"; + ot->name = "Unpack Resources"; ot->idname = "FILE_OT_unpack_all"; ot->description = "Unpack all files packed into this .blend to external ones"; @@ -279,7 +301,11 @@ void FILE_OT_unpack_all(wmOperatorType *ot) ot->srna, "method", unpack_all_method_items, PF_USE_LOCAL, "Method", "How to unpack"); } -/********************* unpack single item operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Unpack Single Item Operator + * \{ */ static const EnumPropertyItem unpack_item_method_items[] = { {PF_USE_LOCAL, "USE_LOCAL", 0, "Use file from current directory (create when necessary)", ""}, @@ -373,7 +399,11 @@ void FILE_OT_unpack_item(wmOperatorType *ot) INT_MAX); } -/********************* make paths relative operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Relative Operator + * \{ */ static int make_paths_relative_exec(bContext *C, wmOperator *op) { @@ -395,7 +425,7 @@ static int make_paths_relative_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_relative(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Relative"; + ot->name = "Make Paths Relative"; ot->idname = "FILE_OT_make_paths_relative"; ot->description = "Make all paths to external files relative to current .blend"; @@ -406,7 +436,11 @@ void FILE_OT_make_paths_relative(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* make paths absolute operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Make Paths Absolute Operator + * \{ */ static int make_paths_absolute_exec(bContext *C, wmOperator *op) { @@ -428,7 +462,7 @@ static int make_paths_absolute_exec(bContext *C, wmOperator *op) void FILE_OT_make_paths_absolute(wmOperatorType *ot) { /* identifiers */ - ot->name = "Make All Paths Absolute"; + ot->name = "Make Paths Absolute"; ot->idname = "FILE_OT_make_paths_absolute"; ot->description = "Make all paths to external files absolute"; @@ -439,7 +473,11 @@ void FILE_OT_make_paths_absolute(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************* report missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Missing Files Operator + * \{ */ static int report_missing_files_exec(bContext *C, wmOperator *op) { @@ -465,7 +503,11 @@ void FILE_OT_report_missing_files(wmOperatorType *ot) ot->flag = 0; /* only reports so no need to undo/register */ } -/********************* find missing files operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Find Missing Files Operator + * \{ */ static int find_missing_files_exec(bContext *C, wmOperator *op) { @@ -516,7 +558,11 @@ void FILE_OT_find_missing_files(wmOperatorType *ot) FILE_SORT_DEFAULT); } -/********************* report box operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Report Box Operator + * \{ */ /* Hard to decide whether to keep this as an operator, * or turn it into a hardcoded ui control feature, @@ -621,3 +667,5 @@ void INFO_OT_reports_display_update(wmOperatorType *ot) } /* report operators */ + +/** \} */ diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index ffac5c982d6..0bdfceb36b6 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -697,7 +697,8 @@ void ED_info_draw_stats( UI_FontThemeColor(font_id, TH_TEXT_HI); BLF_enable(font_id, BLF_SHADOW); - BLF_shadow(font_id, 5, (const float[4]){0.0f, 0.0f, 0.0f, 1.0f}); + const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + BLF_shadow(font_id, 5, shadow_color); BLF_shadow_offset(font_id, 1, -1); /* Translated labels for each stat row. */ diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index dfc0abee704..e56bb44b1e6 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -255,11 +255,10 @@ static void info_header_region_message_subscribe(const wmRegionMessageSubscribeP struct wmMsgBus *mbus = params->message_bus; ARegion *region = params->region; - wmMsgSubscribeValue msg_sub_value_region_tag_redraw = { - .owner = region, - .user_data = region, - .notify = ED_region_do_msg_notify_tag_redraw, - }; + wmMsgSubscribeValue msg_sub_value_region_tag_redraw = {NULL}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_region_tag_redraw); WM_msg_subscribe_rna_anon_prop(mbus, ViewLayer, name, &msg_sub_value_region_tag_redraw); diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index 8bf6f2698e0..7d4011e0812 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -266,7 +266,7 @@ static void nla_strip_get_color_inside(AnimData *adt, NlaStrip *strip, float col } else if (strip->type == NLASTRIP_TYPE_META) { /* Meta Clip */ - /* TODO: should temporary metas get different colors too? */ + /* TODO: should temporary meta-strips get different colors too? */ if (strip->flag & NLASTRIP_FLAG_SELECT) { /* selected - use a bold purple color */ UI_GetThemeColor3fv(TH_NLA_META_SEL, color); diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 5110c14ef4d..6b4366b2966 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -456,7 +456,9 @@ static void node_draw_frame(const bContext *C, } /* label */ - node_draw_frame_label(ntree, node, snode->runtime->aspect); + if (node->label[0] != '\0') { + node_draw_frame_label(ntree, node, snode->runtime->aspect); + } UI_block_end(C, node->block); UI_block_draw(C, node->block); @@ -3332,12 +3334,14 @@ static const float std_node_socket_colors[][4] = { {0.39, 0.78, 0.39, 1.0}, /* SOCK_SHADER */ {0.80, 0.65, 0.84, 1.0}, /* SOCK_BOOLEAN */ {0.0, 0.0, 0.0, 1.0}, /*__SOCK_MESH (deprecated) */ - {0.25, 0.75, 0.26, 1.0}, /* SOCK_INT */ + {0.35, 0.55, 0.36, 1.0}, /* SOCK_INT */ {0.44, 0.70, 1.00, 1.0}, /* SOCK_STRING */ {0.93, 0.62, 0.36, 1.0}, /* SOCK_OBJECT */ - {0.89, 0.76, 0.43, 1.0}, /* SOCK_IMAGE */ + {0.39, 0.22, 0.39, 1.0}, /* SOCK_IMAGE */ {0.00, 0.84, 0.64, 1.0}, /* SOCK_GEOMETRY */ {0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */ + {0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */ + {0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */ }; /* common color callbacks for standard types */ @@ -3478,6 +3482,14 @@ static void std_node_socket_draw( uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); break; } + case SOCK_TEXTURE: { + uiTemplateID(layout, C, ptr, "default_value", "texture.new", NULL, NULL, 0, ICON_NONE, NULL); + break; + } + case SOCK_MATERIAL: { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + break; + } default: node_socket_button_label(C, layout, ptr, node_ptr, text); break; diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 7282ed4b667..50fa8b28468 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -1136,8 +1136,8 @@ static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSo * But keep it smaller because for multi-input socket you * sometimes want to drag the link to the other side, if you may * accidentally pick the wrong link otherwise. */ - .ymin = socket->locy - node_socket_height * 0.5 - NODE_SOCKSIZE, - .ymax = socket->locy + node_socket_height * 0.5 + NODE_SOCKSIZE, + .ymin = socket->locy - node_socket_height, + .ymax = socket->locy + node_socket_height, }; if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) { return true; @@ -1713,8 +1713,6 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op)) } } - do_tag_update |= ED_node_is_geometry(snode); - snode_notify(C, snode); if (do_tag_update) { snode_dag_update(C, snode); @@ -1755,8 +1753,6 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op)) } } - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 856a90be5ae..94080a7b616 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -33,6 +33,8 @@ #include "RNA_access.h" #include "RNA_enum_types.h" +#include "ED_undo.h" + #include "BLT_translation.h" #include "UI_interface.h" @@ -132,7 +134,7 @@ static void attribute_search_update_fn(const bContext *UNUSED(C), BLI_string_search_free(search); } -static void attribute_search_exec_fn(bContext *UNUSED(C), void *data_v, void *item_v) +static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) { AttributeSearchData *data = static_cast<AttributeSearchData *>(data_v); AvailableAttributeInfo *item = static_cast<AvailableAttributeInfo *>(item_v); @@ -140,6 +142,8 @@ static void attribute_search_exec_fn(bContext *UNUSED(C), void *data_v, void *it bNodeSocket &socket = data->socket; bNodeSocketValueString *value = static_cast<bNodeSocketValueString *>(socket.default_value); BLI_strncpy(value->value, item->name.c_str(), MAX_NAME); + + ED_undo_push(C, "Assign Attribute Name"); } void node_geometry_add_attribute_search_button(const bContext *C, diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 91fe8f5ec89..28c660b0632 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -852,8 +852,6 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) } ntree->is_updating = false; - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(bmain, ntree); snode_notify(C, snode); if (do_tag_update) { @@ -1291,8 +1289,6 @@ static int cut_links_exec(bContext *C, wmOperator *op) } } - do_tag_update |= ED_node_is_geometry(snode); - if (found) { ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); @@ -1399,8 +1395,6 @@ static int mute_links_exec(bContext *C, wmOperator *op) link->flag &= ~NODE_LINK_TEST; } - do_tag_update |= ED_node_is_geometry(snode); - ntreeUpdateTree(CTX_data_main(C), snode->edittree); snode_notify(C, snode); if (do_tag_update) { @@ -1882,28 +1876,63 @@ void ED_node_link_intersect_test(ScrArea *area, int test) } } -/* assumes sockets in list */ -static bNodeSocket *socket_best_match(ListBase *sockets) -{ - /* find type range */ - int maxtype = 0; +static int get_main_socket_priority(const bNodeSocket *socket) +{ + switch ((eNodeSocketDatatype)socket->type) { + case __SOCK_MESH: + case SOCK_CUSTOM: + return -1; + case SOCK_BOOLEAN: + return 0; + case SOCK_INT: + return 1; + case SOCK_FLOAT: + return 2; + case SOCK_VECTOR: + return 3; + case SOCK_RGBA: + return 4; + case SOCK_STRING: + case SOCK_SHADER: + case SOCK_OBJECT: + case SOCK_IMAGE: + case SOCK_GEOMETRY: + case SOCK_COLLECTION: + case SOCK_TEXTURE: + case SOCK_MATERIAL: + return 5; + } + return -1; +} + +/** Get the "main" socket of a socket list using a heuristic based on socket types. */ +static bNodeSocket *get_main_socket(ListBase *sockets) +{ + /* find priority range */ + int maxpriority = -1; LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - maxtype = max_ii(sock->type, maxtype); + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + maxpriority = max_ii(get_main_socket_priority(sock), maxpriority); } - /* try all types, starting from 'highest' (i.e. colors, vectors, values) */ - for (int type = maxtype; type >= 0; type--) { + /* try all priorities, starting from 'highest' */ + for (int priority = maxpriority; priority >= 0; priority--) { LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (!nodeSocketIsHidden(sock) && type == sock->type) { + if (!nodeSocketIsHidden(sock) && priority == get_main_socket_priority(sock)) { return sock; } } } - /* no visible sockets, unhide first of highest type */ - for (int type = maxtype; type >= 0; type--) { + /* no visible sockets, unhide first of highest priority */ + for (int priority = maxpriority; priority >= 0; priority--) { LISTBASE_FOREACH (bNodeSocket *, sock, sockets) { - if (type == sock->type) { + if (sock->flag & SOCK_UNAVAIL) { + continue; + } + if (priority == get_main_socket_priority(sock)) { sock->flag &= ~SOCK_HIDDEN; return sock; } @@ -2248,8 +2277,8 @@ void ED_node_link_insert(Main *bmain, ScrArea *area) } if (link) { - bNodeSocket *best_input = socket_best_match(&select->inputs); - bNodeSocket *best_output = socket_best_match(&select->outputs); + bNodeSocket *best_input = get_main_socket(&select->inputs); + bNodeSocket *best_output = get_main_socket(&select->outputs); if (best_input && best_output) { bNode *node = link->tonode; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 92cd50560e4..328a787c768 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -2106,7 +2106,7 @@ static void outliner_draw_mode_column_toggle(uiBlock *block, tip); UI_but_func_set(but, outliner_mode_toggle_fn, tselem, NULL); UI_but_flag_enable(but, UI_BUT_DRAG_LOCK); - /* Mode toggling handles it's own undo state because undo steps need to be grouped. */ + /* Mode toggling handles its own undo state because undo steps need to be grouped. */ UI_but_flag_disable(but, UI_BUT_UNDO); if (ID_IS_LINKED(&ob->id)) { diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 456c2079f27..c532dd8accd 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -902,7 +902,7 @@ static void id_override_library_reset_fn(bContext *C, } static void id_override_library_resync_fn(bContext *C, - ReportList *UNUSED(reports), + ReportList *reports, Scene *scene, TreeElement *te, TreeStoreElem *UNUSED(tsep), @@ -931,7 +931,7 @@ static void id_override_library_resync_fn(bContext *C, } BKE_lib_override_library_resync( - bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true); + bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true, reports); WM_event_add_notifier(C, NC_WINDOW, NULL); } diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index 5d774049e3e..728be1ccaaf 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -155,6 +155,8 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) case NC_OBJECT: switch (wmn->data) { case ND_TRANSFORM: + ED_region_tag_redraw_no_rebuild(region); + break; case ND_BONE_ACTIVE: case ND_BONE_SELECT: case ND_DRAW: diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index c4fd0dd5d73..ac31e0e7c37 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -95,6 +95,7 @@ typedef struct SequencerAddData { #define SEQPROP_NOPATHS (1 << 2) #define SEQPROP_NOCHAN (1 << 3) #define SEQPROP_FIT_METHOD (1 << 4) +#define SEQPROP_VIEW_TRANSFORM (1 << 5) static const EnumPropertyItem scale_fit_methods[] = { {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, @@ -152,6 +153,14 @@ static void sequencer_generic_props__internal(wmOperatorType *ot, int flag) "Fit Method", "Scale fit method"); } + + if (flag & SEQPROP_VIEW_TRANSFORM) { + ot->prop = RNA_def_boolean(ot->srna, + "set_view_transform", + true, + "Set View Transform", + "Set appropriate view transform based on media colorspace"); + } } static void sequencer_generic_invoke_path__internal(bContext *C, @@ -284,6 +293,11 @@ static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wm load_data->flags |= SEQ_LOAD_MOVIE_SYNC_FPS; } + if ((prop = RNA_struct_find_property(op->ptr, "set_view_transform")) && + RNA_property_boolean_get(op->ptr, prop)) { + load_data->flags |= SEQ_LOAD_SET_VIEW_TRANSFORM; + } + if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) && RNA_property_boolean_get(op->ptr, prop)) { if (op->customdata) { @@ -368,8 +382,23 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void sequencer_disable_one_time_properties(bContext *C, wmOperator *op) +{ + Editing *ed = SEQ_editing_get(CTX_data_scene(C), false); + /* Disable following properties if there are any existing strips, unless overridden by user. */ + if (ed && ed->seqbasep && ed->seqbasep->first) { + if (RNA_struct_find_property(op->ptr, "use_framerate")) { + RNA_boolean_set(op->ptr, "use_framerate", false); + } + if (RNA_struct_find_property(op->ptr, "set_view_transform")) { + RNA_boolean_set(op->ptr, "set_view_transform", false); + } + } +} + static int sequencer_add_scene_strip_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + sequencer_disable_one_time_properties(C, op); if (!RNA_struct_property_is_set(op->ptr, "scene")) { return WM_enum_search_invoke(C, op, event); } @@ -702,13 +731,9 @@ static int sequencer_add_movie_strip_invoke(bContext *C, { PropertyRNA *prop; Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, false); - /* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user. - */ - if (ed && ed->seqbasep && ed->seqbasep->first) { - RNA_boolean_set(op->ptr, "use_framerate", false); - } + sequencer_disable_one_time_properties(C, op); + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* This is for drag and drop. */ @@ -775,7 +800,8 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD); + sequencer_generic_props__internal( + ot, SEQPROP_STARTFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM); RNA_def_boolean(ot->srna, "sound", true, "Sound", "Load sound with the movie"); RNA_def_boolean(ot->srna, "use_framerate", @@ -1053,8 +1079,9 @@ static int sequencer_add_image_strip_invoke(bContext *C, PropertyRNA *prop; Scene *scene = CTX_data_scene(C); - const SequencerToolSettings *tool_settings = scene->toolsettings->sequencer_tool_settings; - RNA_enum_set(op->ptr, "fit_method", tool_settings->fit_method); + sequencer_disable_one_time_properties(C, op); + + RNA_enum_set(op->ptr, "fit_method", SEQ_tool_settings_fit_method_get(scene)); /* Name set already by drag and drop. */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { @@ -1100,8 +1127,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) WM_FILESEL_SHOW_PROPS | WM_FILESEL_DIRECTORY, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); - sequencer_generic_props__internal(ot, - SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD); + sequencer_generic_props__internal( + ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME | SEQPROP_FIT_METHOD | SEQPROP_VIEW_TRANSFORM); RNA_def_boolean(ot->srna, "use_placeholders", diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 7beb61e48d2..b55c8035fa3 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -885,10 +885,12 @@ static void draw_seq_background(Scene *scene, immUniformColor4ubv(col); if (seq->startstill) { - immRectf(pos, seq->startdisp, y1, (float)(seq->start), y2); + const float content_start = min_ff(seq->enddisp, seq->start); + immRectf(pos, seq->startdisp, y1, content_start, y2); } if (seq->endstill) { - immRectf(pos, (float)(seq->start + seq->len), y1, seq->enddisp, y2); + const float content_end = max_ff(seq->startdisp, seq->start + seq->len); + immRectf(pos, content_end, y1, seq->enddisp, y2); } } @@ -1105,6 +1107,10 @@ static void draw_seq_strip(const bContext *C, x2 = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; y2 = seq->machine + SEQ_STRIP_OFSTOP; + /* Limit body to strip bounds. Meta strip can end up with content outside of strip range. */ + x1 = min_ff(x1, seq->enddisp); + x2 = max_ff(x2, seq->startdisp); + float text_margin_y; bool y_threshold; if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) || @@ -1522,10 +1528,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C, ImBuf *ibuf, bool *r_glsl_used, eGPUTextureFormat *r_format, - eGPUDataFormat *r_data) + eGPUDataFormat *r_data, + void **r_buffer_cache_handle) { void *display_buffer; - void *cache_handle = NULL; bool force_fallback = false; *r_glsl_used = false; force_fallback |= (ED_draw_imbuf_method(ibuf) != IMAGE_DRAW_METHOD_GLSL); @@ -1578,13 +1584,10 @@ static void *sequencer_OCIO_transform_ibuf(const bContext *C, /* There is data to be displayed, but GLSL is not initialized * properly, in this case we fallback to CPU-based display transform. */ if ((ibuf->rect || ibuf->rect_float) && !*r_glsl_used) { - display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, &cache_handle); + display_buffer = IMB_display_buffer_acquire_ctx(C, ibuf, r_buffer_cache_handle); *r_format = GPU_RGBA8; *r_data = GPU_DATA_UBYTE; } - if (cache_handle) { - IMB_display_buffer_release(cache_handle); - } return display_buffer; } @@ -1658,6 +1661,7 @@ static void sequencer_draw_display_buffer(const bContext *C, bool draw_backdrop) { void *display_buffer; + void *buffer_cache_handle = NULL; if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { GPU_blend(GPU_BLEND_ALPHA); @@ -1685,7 +1689,8 @@ static void sequencer_draw_display_buffer(const bContext *C, data = GPU_DATA_UBYTE; } else { - display_buffer = sequencer_OCIO_transform_ibuf(C, ibuf, &glsl_used, &format, &data); + display_buffer = sequencer_OCIO_transform_ibuf( + C, ibuf, &glsl_used, &format, &data, &buffer_cache_handle); } if (draw_backdrop) { @@ -1745,6 +1750,10 @@ static void sequencer_draw_display_buffer(const bContext *C, IMB_colormanagement_finish_glsl_draw(); } + if (buffer_cache_handle) { + IMB_display_buffer_release(buffer_cache_handle); + } + if (sseq->mainb == SEQ_DRAW_IMG_IMBUF && sseq->flag & SEQ_USE_ALPHA) { GPU_blend(GPU_BLEND_NONE); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 52cfeeecb5c..ebd4c0090b4 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -290,7 +290,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) snap_frame = RNA_int_get(op->ptr, "frame"); - /* Check metas. */ + /* Check meta-strips. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK) && SEQ_transform_sequence_can_be_translated(seq)) { @@ -348,7 +348,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) } } - SEQ_sort(scene); + SEQ_sort(SEQ_active_seqbase_get(ed)); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1443,7 +1443,7 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) } } - SEQ_sort(scene); + SEQ_sort(SEQ_active_seqbase_get(ed)); } if (changed) { WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1577,17 +1577,6 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot) /** \name Duplicate Strips Operator * \{ */ -static int apply_unique_name_fn(Sequence *seq, void *arg_pt) -{ - Scene *scene = (Scene *)arg_pt; - char name[sizeof(seq->name) - 2]; - - BLI_strncpy_utf8(name, seq->name + 2, sizeof(name)); - SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); - SEQ_dupe_animdata(scene, name, seq->name + 2); - return 1; -} - static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1608,7 +1597,7 @@ static int sequencer_add_duplicate_exec(bContext *C, wmOperator *UNUSED(op)) BLI_movelisttolist(ed->seqbasep, &nseqbase); for (; seq; seq = seq->next) { - SEQ_iterator_recursive_apply(seq, apply_unique_name_fn, scene); + SEQ_ensure_unique_name(seq, scene); } WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1829,7 +1818,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) } } - SEQ_sort(scene); + SEQ_sort(SEQ_active_seqbase_get(ed)); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1867,13 +1856,13 @@ static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) Sequence *active_seq = SEQ_select_active_get(scene); if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) { - /* Enter metastrip. */ + /* Enter meta-strip. */ SEQ_meta_stack_alloc(ed, active_seq); SEQ_seqbase_active_set(ed, &active_seq->seqbase); SEQ_select_active_set(scene, NULL); } else { - /* Exit metastrip if possible. */ + /* Exit meta-strip if possible. */ if (BLI_listbase_is_empty(&ed->metastack)) { return OPERATOR_CANCELLED; } @@ -1895,7 +1884,7 @@ void SEQUENCER_OT_meta_toggle(wmOperatorType *ot) /* Identifiers. */ ot->name = "Toggle Meta Strip"; ot->idname = "SEQUENCER_OT_meta_toggle"; - ot->description = "Toggle a metastrip (to edit enclosed strips)"; + ot->description = "Toggle a meta-strip (to edit enclosed strips)"; /* Api callbacks. */ ot->exec = sequencer_meta_toggle_exec; @@ -1963,7 +1952,7 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot) /* Identifiers. */ ot->name = "Make Meta Strip"; ot->idname = "SEQUENCER_OT_meta_make"; - ot->description = "Group selected strips into a metastrip"; + ot->description = "Group selected strips into a meta-strip"; /* Api callbacks. */ ot->exec = sequencer_meta_make_exec; @@ -2014,7 +2003,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) } } - SEQ_sort(scene); + SEQ_sort(active_seqbase); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -2026,7 +2015,7 @@ void SEQUENCER_OT_meta_separate(wmOperatorType *ot) /* Identifiers. */ ot->name = "UnMeta Strip"; ot->idname = "SEQUENCER_OT_meta_separate"; - ot->description = "Put the contents of a metastrip back in the sequencer"; + ot->description = "Put the contents of a meta-strip back in the sequencer"; /* Api callbacks. */ ot->exec = sequencer_meta_separate_exec; @@ -2237,7 +2226,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) } } - SEQ_sort(scene); + SEQ_sort(SEQ_active_seqbase_get(ed)); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -2274,43 +2263,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot) static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op)) { - int retval = OPERATOR_CANCELLED; Scene *scene = CTX_data_scene(C); Sequence *active_seq = SEQ_select_active_get(scene); StripElem *se = NULL; - if (active_seq == NULL) { + if (active_seq == NULL || active_seq->strip == NULL) { return OPERATOR_CANCELLED; } - if (active_seq->strip) { - switch (active_seq->type) { - case SEQ_TYPE_IMAGE: - se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); - break; - case SEQ_TYPE_MOVIE: - se = active_seq->strip->stripdata; - break; - case SEQ_TYPE_SCENE: - case SEQ_TYPE_META: - case SEQ_TYPE_SOUND_RAM: - case SEQ_TYPE_SOUND_HD: - default: - break; - } + switch (active_seq->type) { + case SEQ_TYPE_IMAGE: + se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); + break; + case SEQ_TYPE_MOVIE: + se = active_seq->strip->stripdata; + break; + default: + return OPERATOR_CANCELLED; } - if (se) { - /* Prevent setting the render size if sequence values aren't initialized. */ - if ((se->orig_width > 0) && (se->orig_height > 0)) { - scene->r.xsch = se->orig_width; - scene->r.ysch = se->orig_height; - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); - retval = OPERATOR_FINISHED; - } + if (se == NULL) { + return OPERATOR_CANCELLED; } - return retval; + /* Prevent setting the render size if sequence values aren't initialized. */ + if (se->orig_width <= 0 || se->orig_height <= 0) { + return OPERATOR_CANCELLED; + } + + scene->r.xsch = se->orig_width; + scene->r.ysch = se->orig_height; + + active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f; + active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f; + + SEQ_relations_invalidate_cache_preprocessed(scene, active_seq); + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; } void SEQUENCER_OT_rendersize(wmOperatorType *ot) @@ -2463,7 +2454,7 @@ static int sequencer_paste_exec(bContext *C, wmOperator *op) for (iseq = iseq_first; iseq; iseq = iseq->next) { /* Make sure, that pasted strips have unique names. */ - SEQ_iterator_recursive_apply(iseq, apply_unique_name_fn, scene); + SEQ_ensure_unique_name(iseq, scene); /* Translate after name has been changed, otherwise this will affect animdata of original * strip. */ SEQ_transform_translate_sequence(scene, iseq, ofs); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index a9f8a70d61e..cfc11afce13 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -26,7 +26,6 @@ #include <string.h> #include "BLI_blenlib.h" -#include "BLI_ghash.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -1187,7 +1186,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) case 1: test = (timeline_frame <= seq->startdisp); break; - case 0: + case 2: test = (timeline_frame <= seq->enddisp) && (timeline_frame >= seq->startdisp); break; } @@ -1210,6 +1209,7 @@ void SEQUENCER_OT_select_side_of_frame(wmOperatorType *ot) static const EnumPropertyItem sequencer_select_left_right_types[] = { {-1, "LEFT", 0, "Left", "Select to the left of the current frame"}, {1, "RIGHT", 0, "Right", "Select to the right of the current frame"}, + {2, "CURRENT", 0, "Current Frame", "Select intersecting with the current frame"}, {0, NULL, 0, NULL, NULL}, }; @@ -1622,64 +1622,47 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) return changed; } -static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int channel) +/* Query strips that are in lower channel and intersect in time with seq_reference. */ +static void query_lower_channel_strips(Sequence *seq_reference, + ListBase *seqbase, + SeqCollection *collection) { - bool changed = false; - const bool is_audio = ((actseq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(actseq)); - int startdisp = actseq->startdisp; - int enddisp = actseq->enddisp; - int machine = actseq->machine; - SeqIterator iter; - - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { - seq->tmp = NULL; - } - - actseq->tmp = POINTER_FROM_INT(true); - - Sequence *seq = NULL; - for (SEQ_iterator_begin(ed, &iter, true); iter.valid; SEQ_iterator_next(&iter)) { - seq = iter.seq; - - /* Ignore all seqs already selected. */ - /* Ignore all seqs not sharing some time with active one. */ - /* Ignore all seqs of incompatible types (audio vs video). */ - if (!SEQ_CHANNEL_CHECK(seq, channel) || (seq->flag & SELECT) || (seq->startdisp >= enddisp) || - (seq->enddisp < startdisp) || (!is_audio && SEQ_IS_SOUND(seq)) || - (is_audio && !((seq->type == SEQ_TYPE_META) || SEQ_IS_SOUND(seq)))) { - continue; + LISTBASE_FOREACH (Sequence *, seq_test, seqbase) { + if (seq_test->machine > seq_reference->machine) { + continue; /* Not lower channel. */ } + if (seq_test->enddisp <= seq_reference->startdisp || + seq_test->startdisp >= seq_reference->enddisp) { + continue; /* Not intersecting in time. */ + } + SEQ_collection_append_strip(seq_test, collection); + } +} - /* If the seq is an effect one, we need extra checking. */ - if (SEQ_IS_EFFECT(seq) && ((seq->seq1 && seq->seq1->tmp) || (seq->seq2 && seq->seq2->tmp) || - (seq->seq3 && seq->seq3->tmp))) { - if (startdisp > seq->startdisp) { - startdisp = seq->startdisp; - } - if (enddisp < seq->enddisp) { - enddisp = seq->enddisp; - } - if (machine < seq->machine) { - machine = seq->machine; - } - - seq->tmp = POINTER_FROM_INT(true); +/* Select all strips within time range and with lower channel of initial selection. Then select + * effect chains of these strips. */ +static bool select_grouped_effect_link(Editing *ed, + Sequence *UNUSED(actseq), + const int UNUSED(channel)) +{ + ListBase *seqbase = SEQ_active_seqbase_get(ed); - seq->flag |= SELECT; - changed = true; + /* Get collection of strips. */ + SeqCollection *collection = SEQ_query_selected_strips(seqbase); + const int selected_strip_count = BLI_gset_len(collection->set); + SEQ_collection_expand(seqbase, collection, query_lower_channel_strips); + SEQ_collection_expand(seqbase, collection, SEQ_query_strip_effect_chain); - /* Unfortunately, we must restart checks from the beginning. */ - SEQ_iterator_end(&iter); - SEQ_iterator_begin(ed, &iter, true); - } + /* Check if other strips will be affected. */ + const bool changed = BLI_gset_len(collection->set) > selected_strip_count; - /* Video strips below active one, or any strip for audio (order doesn't matter here). */ - else if (seq->machine < machine || is_audio) { - seq->flag |= SELECT; - changed = true; - } + /* Actual logic. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq->flag |= SELECT; } - SEQ_iterator_end(&iter); + + SEQ_collection_free(collection); return changed; } diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index 26f89a0700e..1f0b5d5d13e 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -304,7 +304,12 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source) continue; } - used_ids.add(*column->id); + if (!used_ids.add(*column->id)) { + /* Remove duplicate columns for now. */ + BLI_remlink(&columns, column); + spreadsheet_column_free(column); + continue; + } } data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) { @@ -399,6 +404,7 @@ static void spreadsheet_main_region_listener(const wmRegionListenerParams *param } break; } + case NC_TEXTURE: case NC_GEOM: { ED_region_tag_redraw(region); break; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 0627cff7abc..c9b73aabf96 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -18,6 +18,10 @@ #include <optional> +#include "BLI_color.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" + struct Object; struct Collection; @@ -44,6 +48,9 @@ class CellValue { std::optional<int> value_int; std::optional<float> value_float; std::optional<bool> value_bool; + std::optional<float2> value_float2; + std::optional<float3> value_float3; + std::optional<ColorGeometry4f> value_color; std::optional<ObjectCellValue> value_object; std::optional<CollectionCellValue> value_collection; }; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc index 6c573ce7238..de40545fdae 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.cc @@ -37,7 +37,6 @@ SpreadsheetColumnID *spreadsheet_column_id_copy(const SpreadsheetColumnID *src_c { SpreadsheetColumnID *new_column_id = spreadsheet_column_id_new(); new_column_id->name = BLI_strdup(src_column_id->name); - new_column_id->index = src_column_id->index; return new_column_id; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh index 0f74ee0141a..bb245851d55 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column.hh @@ -24,7 +24,7 @@ namespace blender { template<> struct DefaultHash<SpreadsheetColumnID> { uint64_t operator()(const SpreadsheetColumnID &column_id) const { - return get_default_hash_2(StringRef(column_id.name), column_id.index); + return get_default_hash(StringRef(column_id.name)); } }; } // namespace blender @@ -32,7 +32,7 @@ template<> struct DefaultHash<SpreadsheetColumnID> { inline bool operator==(const SpreadsheetColumnID &a, const SpreadsheetColumnID &b) { using blender::StringRef; - return StringRef(a.name) == StringRef(b.name) && a.index == b.index; + return StringRef(a.name) == StringRef(b.name); } namespace blender::ed::spreadsheet { diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 58a2776e0fc..373c988a41c 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -74,11 +74,19 @@ template<typename GetValueF> class LambdaColumnValues : public ColumnValues { /* Utility function that simplifies creating a spreadsheet column from a lambda function. */ template<typename GetValueF> std::unique_ptr<ColumnValues> column_values_from_function(std::string name, - int size, - GetValueF get_value) + const int size, + GetValueF get_value, + const float default_width = 0.0f) { - return std::make_unique<LambdaColumnValues<GetValueF>>( + std::unique_ptr<ColumnValues> column_values = std::make_unique<LambdaColumnValues<GetValueF>>( std::move(name), size, std::move(get_value)); + column_values->default_width = default_width; + return column_values; } +static constexpr float default_float_column_width = 3; +static constexpr float default_float2_column_width = 2 * default_float_column_width; +static constexpr float default_float3_column_width = 3 * default_float_column_width; +static constexpr float default_color_column_width = 4 * default_float_column_width; + } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 405f0cd9455..452885959f6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -47,28 +47,7 @@ void GeometryDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; column_id.name = (char *)name.c_str(); - if (meta_data.data_type == CD_PROP_FLOAT3) { - for (const int i : {0, 1, 2}) { - column_id.index = i; - fn(column_id); - } - } - else if (meta_data.data_type == CD_PROP_FLOAT2) { - for (const int i : {0, 1}) { - column_id.index = i; - fn(column_id); - } - } - else if (meta_data.data_type == CD_PROP_COLOR) { - for (const int i : {0, 1, 2, 3}) { - column_id.index = i; - fn(column_id); - } - } - else { - column_id.index = -1; - fn(column_id); - } + fn(column_id); return true; }); } @@ -90,9 +69,6 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( const CustomDataType type = bke::cpp_type_to_custom_data_type(varray->type()); switch (type) { case CD_PROP_FLOAT: - if (column_id.index != -1) { - return {}; - } return column_values_from_function( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { float value; @@ -100,9 +76,6 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( r_cell_value.value_float = value; }); case CD_PROP_INT32: - if (column_id.index != -1) { - return {}; - } return column_values_from_function( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { int value; @@ -110,9 +83,6 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( r_cell_value.value_int = value; }); case CD_PROP_BOOL: - if (column_id.index != -1) { - return {}; - } return column_values_from_function( column_id.name, domain_size, [varray](int index, CellValue &r_cell_value) { bool value; @@ -120,43 +90,37 @@ std::unique_ptr<ColumnValues> GeometryDataSource::get_column_values( r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { - if (column_id.index < 0 || column_id.index > 1) { - return {}; - } - const std::array<const char *, 2> suffixes = {" X", " Y"}; - const std::string name = StringRef(column_id.name) + suffixes[column_id.index]; return column_values_from_function( - name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) { + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { float2 value; varray->get(index, &value); - r_cell_value.value_float = value[axis]; - }); + r_cell_value.value_float2 = value; + }, + default_float2_column_width); } case CD_PROP_FLOAT3: { - if (column_id.index < 0 || column_id.index > 2) { - return {}; - } - const std::array<const char *, 3> suffixes = {" X", " Y", " Z"}; - const std::string name = StringRef(column_id.name) + suffixes[column_id.index]; return column_values_from_function( - name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) { + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { float3 value; varray->get(index, &value); - r_cell_value.value_float = value[axis]; - }); + r_cell_value.value_float3 = value; + }, + default_float3_column_width); } case CD_PROP_COLOR: { - if (column_id.index < 0 || column_id.index > 3) { - return {}; - } - const std::array<const char *, 4> suffixes = {" R", " G", " B", " A"}; - const std::string name = StringRef(column_id.name) + suffixes[column_id.index]; return column_values_from_function( - name, domain_size, [varray, axis = column_id.index](int index, CellValue &r_cell_value) { - Color4f value; + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + ColorGeometry4f value; varray->get(index, &value); - r_cell_value.value_float = value[axis]; - }); + r_cell_value.value_color = value; + }, + default_color_column_width); } default: break; @@ -295,15 +259,11 @@ void InstancesDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; - column_id.index = -1; column_id.name = (char *)"Name"; fn(column_id); - for (const char *name : {"Position", "Rotation", "Scale"}) { - for (const int i : {0, 1, 2}) { - column_id.name = (char *)name; - column_id.index = i; - fn(column_id); - } + for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { + column_id.name = (char *)name; + fn(column_id); } } @@ -314,51 +274,68 @@ std::unique_ptr<ColumnValues> InstancesDataSource::get_column_values( return {}; } - const std::array<const char *, 3> suffixes = {" X", " Y", " Z"}; const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { - Span<InstancedData> instance_data = component_->instanced_data(); + Span<int> reference_handles = component_->instance_reference_handles(); + Span<InstanceReference> references = component_->references(); std::unique_ptr<ColumnValues> values = column_values_from_function( - "Name", size, [instance_data](int index, CellValue &r_cell_value) { - const InstancedData &data = instance_data[index]; - if (data.type == INSTANCE_DATA_TYPE_OBJECT) { - if (data.data.object != nullptr) { - r_cell_value.value_object = ObjectCellValue{data.data.object}; + "Name", size, [reference_handles, references](int index, CellValue &r_cell_value) { + const InstanceReference &reference = references[reference_handles[index]]; + switch (reference.type()) { + case InstanceReference::Type::Object: { + Object &object = reference.object(); + r_cell_value.value_object = ObjectCellValue{&object}; + break; } - } - else if (data.type == INSTANCE_DATA_TYPE_COLLECTION) { - if (data.data.collection != nullptr) { - r_cell_value.value_collection = CollectionCellValue{data.data.collection}; + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + r_cell_value.value_collection = CollectionCellValue{&collection}; + break; + } + case InstanceReference::Type::None: { + break; } } }); values->default_width = 8.0f; return values; } - if (column_id.index < 0 || column_id.index > 2) { - return {}; - } - Span<float4x4> transforms = component_->transforms(); + Span<float4x4> transforms = component_->instance_transforms(); if (STREQ(column_id.name, "Position")) { - std::string name = StringRef("Position") + suffixes[column_id.index]; return column_values_from_function( - name, size, [transforms, axis = column_id.index](int index, CellValue &r_cell_value) { - r_cell_value.value_float = transforms[index].translation()[axis]; - }); + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].translation(); + }, + default_float3_column_width); } if (STREQ(column_id.name, "Rotation")) { - std::string name = StringRef("Rotation") + suffixes[column_id.index]; return column_values_from_function( - name, size, [transforms, axis = column_id.index](int index, CellValue &r_cell_value) { - r_cell_value.value_float = transforms[index].to_euler()[axis]; - }); + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].to_euler(); + }, + default_float3_column_width); } if (STREQ(column_id.name, "Scale")) { - std::string name = StringRef("Scale") + suffixes[column_id.index]; return column_values_from_function( - name, size, [transforms, axis = column_id.index](int index, CellValue &r_cell_value) { - r_cell_value.value_float = transforms[index].scale()[axis]; - }); + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].scale(); + }, + default_float3_column_width); + } + Span<int> ids = component_->instance_ids(); + if (STREQ(column_id.name, "ID")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); } return {}; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc index d6379c740e8..b911c80fa63 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_draw.cc @@ -142,7 +142,9 @@ static void draw_left_column_content(const int scroll_offset_y, ARegion *region, const SpreadsheetDrawer &drawer) { - GPU_scissor_test(true); + int old_scissor[4]; + GPU_scissor_get(old_scissor); + GPU_scissor(0, 0, drawer.left_column_width, region->winy - drawer.top_row_height); uiBlock *left_column_block = UI_block_begin(C, region, __func__, UI_EMBOSS_NONE); @@ -165,7 +167,7 @@ static void draw_left_column_content(const int scroll_offset_y, UI_block_end(C, left_column_block); UI_block_draw(C, left_column_block); - GPU_scissor_test(false); + GPU_scissor(UNPACK4(old_scissor)); } static void draw_top_row_content(const bContext *C, @@ -173,7 +175,9 @@ static void draw_top_row_content(const bContext *C, const SpreadsheetDrawer &drawer, const int scroll_offset_x) { - GPU_scissor_test(true); + int old_scissor[4]; + GPU_scissor_get(old_scissor); + GPU_scissor(drawer.left_column_width + 1, region->winy - drawer.top_row_height, region->winx - drawer.left_column_width, @@ -200,7 +204,7 @@ static void draw_top_row_content(const bContext *C, UI_block_end(C, first_row_block); UI_block_draw(C, first_row_block); - GPU_scissor_test(false); + GPU_scissor(UNPACK4(old_scissor)); } static void draw_cell_contents(const bContext *C, @@ -209,7 +213,9 @@ static void draw_cell_contents(const bContext *C, const int scroll_offset_x, const int scroll_offset_y) { - GPU_scissor_test(true); + int old_scissor[4]; + GPU_scissor_get(old_scissor); + GPU_scissor(drawer.left_column_width + 1, 0, region->winx - drawer.left_column_width, @@ -248,7 +254,7 @@ static void draw_cell_contents(const bContext *C, UI_block_end(C, cells_block); UI_block_draw(C, cells_block); - GPU_scissor_test(false); + GPU_scissor(UNPACK4(old_scissor)); } static void update_view2d_tot_rect(const SpreadsheetDrawer &drawer, diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index bca80cff773..8079763a339 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -161,6 +161,18 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { nullptr); UI_but_drawflag_disable(but, UI_BUT_ICON_LEFT); } + else if (cell_value.value_float2.has_value()) { + const float2 value = *cell_value.value_float2; + this->draw_float_vector(params, Span(&value.x, 2)); + } + else if (cell_value.value_float3.has_value()) { + const float3 value = *cell_value.value_float3; + this->draw_float_vector(params, Span(&value.x, 3)); + } + else if (cell_value.value_color.has_value()) { + const ColorGeometry4f value = *cell_value.value_color; + this->draw_float_vector(params, Span(&value.r, 4)); + } else if (cell_value.value_object.has_value()) { const ObjectCellValue value = *cell_value.value_object; uiDefIconTextBut(params.block, @@ -199,6 +211,36 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { } } + void draw_float_vector(const CellDrawParams ¶ms, const Span<float> values) const + { + BLI_assert(!values.is_empty()); + const float segment_width = (float)params.width / values.size(); + for (const int i : values.index_range()) { + std::stringstream ss; + const float value = values[i]; + ss << std::fixed << std::setprecision(3) << value; + const std::string value_str = ss.str(); + uiBut *but = uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + value_str.c_str(), + params.xmin + i * segment_width, + params.ymin, + segment_width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + /* Right-align Floats. */ + UI_but_drawflag_disable(but, UI_BUT_TEXT_LEFT); + UI_but_drawflag_enable(but, UI_BUT_TEXT_RIGHT); + } + } + int column_width(int column_index) const final { return spreadsheet_layout_.columns[column_index].width; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index e6803d12a42..9ec759ce4ae 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -266,8 +266,6 @@ static int text_new_exec(bContext *C, wmOperator *UNUSED(op)) PropertyRNA *prop; text = BKE_text_add(bmain, "Text"); - /* Texts have no user by default... Only the 'real' user flag. */ - id_us_min(&text->id); /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index e6916c34a88..b5274c2357e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1595,7 +1595,6 @@ static void space_view3d_refresh(const bContext *C, ScrArea *UNUSED(area)) } const char *view3d_context_dir[] = { - "active_base", "active_object", NULL, }; @@ -1608,20 +1607,6 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_dir(member)) { CTX_data_dir_set(result, view3d_context_dir); } - else if (CTX_data_equals(member, "active_base")) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - if (view_layer->basact) { - Object *ob = view_layer->basact->object; - /* if hidden but in edit mode, we still display, can happen with animation */ - if ((view_layer->basact->flag & BASE_VISIBLE_DEPSGRAPH) != 0 || - (ob->mode != OB_MODE_OBJECT)) { - CTX_data_pointer_set(result, &scene->id, &RNA_ObjectBase, view_layer->basact); - } - } - - return 1; - } else if (CTX_data_equals(member, "active_object")) { /* In most cases the active object is the `view_layer->basact->object`. * For the 3D view however it can be NULL when hidden. diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 0b30ca2771d..4a595c716b6 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -778,7 +778,7 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, } /* draw */ - immUniformThemeColorShade(TH_VIEW_OVERLAY, 100); + immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255); /* TODO Was using: * UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color); @@ -1288,6 +1288,11 @@ static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *y name_array[name_array_len++] = IFACE_(" (Local)"); } + /* Indicate that clipping region is enabled. */ + if (rv3d->rflag & RV3D_CLIPPING) { + name_array[name_array_len++] = IFACE_(" (Clipped)"); + } + if (name_array_len > 1) { BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len); name = tmpstr; @@ -2017,7 +2022,6 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, source_shading_settings = shading_override; } memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading)); - v3d.shading.type = drawtype; if (drawtype == OB_MATERIAL) { v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS; @@ -2027,8 +2031,17 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, v3d.shading.flag = V3D_SHADING_SCENE_WORLD_RENDER | V3D_SHADING_SCENE_LIGHTS_RENDER; v3d.shading.render_pass = SCE_PASS_COMBINED; } + else if (drawtype == OB_TEXTURE) { + drawtype = OB_SOLID; + v3d.shading.light = V3D_LIGHTING_STUDIO; + v3d.shading.color_type = V3D_SHADING_TEXTURE_COLOR; + } + v3d.shading.type = drawtype; v3d.flag2 = V3D_HIDE_OVERLAYS; + /* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in + * render mode. */ + v3d.overlay.gpencil_vertex_paint_opacity = 1.0f; if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { v3d.flag2 |= V3D_SHOW_ANNOTATION; @@ -2067,7 +2080,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph, return ED_view3d_draw_offscreen_imbuf(depsgraph, scene, - drawtype, + v3d.shading.type, &v3d, ®ion, width, diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 358373ea4d1..967ad966320 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -2507,7 +2507,7 @@ static void view_dolly_to_vector_3d(ARegion *region, madd_v3_v3v3fl(rv3d->ofs, orig_ofs, dvec, -(1.0f - dfac)); } -static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const short zoom_invert) +static void viewdolly_apply(ViewOpsData *vod, const int xy[2], const bool zoom_invert) { float zfac = 1.0; diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index 3134553c159..0d568363b00 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -446,7 +446,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) gpl = view3d_ruler_layer_get(gpd); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false, false); copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE | GP_LAYER_IS_RULER; diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 1afdcdd2993..e602521f6a2 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -252,7 +252,7 @@ static int dot_v3_array_find_max_index(const float dirs[][3], } /** - * Re-order \a mat so \a axis_align uses it's own axis which is closest to \a v. + * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. */ static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3]) { @@ -1762,7 +1762,7 @@ static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP; } - /* Sets the gizmos custom-data which has it's own free callback. */ + /* Sets the gizmos custom-data which has its own free callback. */ preview_plane_cursor_setup(gzgroup); } @@ -2061,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac; + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; float final_scale = (scale_mod * pixel_size); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 2fbcbe22349..17bfc23aea7 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -74,7 +74,7 @@ static void drawTransformApply(const struct bContext *C, ARegion *region, void *arg); -static void initSnapSpatial(TransInfo *t, float r_snap[3]); +static void initSnapSpatial(TransInfo *t, float r_snap[2]); bool transdata_check_local_islands(TransInfo *t, short around) { @@ -724,81 +724,92 @@ wmKeyMap *transform_modal_keymap(wmKeyConfig *keyconf) static bool transform_event_modal_constraint(TransInfo *t, short modal_type) { - if (!(t->flag & T_NO_CONSTRAINT)) { - if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + if (t->flag & T_NO_CONSTRAINT) { + return false; + } + + if (t->flag & T_2D_EDIT && ELEM(modal_type, TFM_MODAL_AXIS_Z, TFM_MODAL_PLANE_Z)) { + return false; + } + + int constraint_curr = -1; + + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); + + /* Avoid changing orientation in this case. */ + constraint_curr = -2; + } + else if (t->con.mode & CON_APPLY) { + constraint_curr = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); + } + + int constraint_new; + const char *msg_2d = "", *msg_3d = ""; + + /* Initialize */ + switch (modal_type) { + case TFM_MODAL_AXIS_X: + msg_2d = TIP_("along X"); + msg_3d = TIP_("along %s X"); + constraint_new = CON_AXIS0; + break; + case TFM_MODAL_AXIS_Y: + msg_2d = TIP_("along Y"); + msg_3d = TIP_("along %s Y"); + constraint_new = CON_AXIS1; + break; + case TFM_MODAL_AXIS_Z: + msg_2d = TIP_("along Z"); + msg_3d = TIP_("along %s Z"); + constraint_new = CON_AXIS2; + break; + case TFM_MODAL_PLANE_X: + msg_3d = TIP_("locking %s X"); + constraint_new = CON_AXIS1 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Y: + msg_3d = TIP_("locking %s Y"); + constraint_new = CON_AXIS0 | CON_AXIS2; + break; + case TFM_MODAL_PLANE_Z: + msg_3d = TIP_("locking %s Z"); + constraint_new = CON_AXIS0 | CON_AXIS1; + break; + default: + /* Invalid key */ return false; - } - int constraint_curr = (t->con.mode & CON_APPLY) ? - t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2) : - -1; - int constraint_new; - const char *msg_2d = "", *msg_3d = ""; + } - /* Initialize */ - switch (modal_type) { - case TFM_MODAL_AXIS_X: - msg_2d = TIP_("along X"); - msg_3d = TIP_("along %s X"); - constraint_new = CON_AXIS0; - break; - case TFM_MODAL_AXIS_Y: - msg_2d = TIP_("along Y"); - msg_3d = TIP_("along %s Y"); - constraint_new = CON_AXIS1; - break; - case TFM_MODAL_AXIS_Z: - msg_2d = TIP_("along Z"); - msg_3d = TIP_("along %s Z"); - constraint_new = CON_AXIS2; - break; - case TFM_MODAL_PLANE_X: - msg_3d = TIP_("locking %s X"); - constraint_new = CON_AXIS1 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Y: - msg_3d = TIP_("locking %s Y"); - constraint_new = CON_AXIS0 | CON_AXIS2; - break; - case TFM_MODAL_PLANE_Z: - msg_3d = TIP_("locking %s Z"); - constraint_new = CON_AXIS0 | CON_AXIS1; - break; - default: - /* Invalid key */ - return false; + if (t->flag & T_2D_EDIT) { + BLI_assert(modal_type < TFM_MODAL_PLANE_X); + if (constraint_new == CON_AXIS2) { + return false; } - - if (t->flag & T_2D_EDIT) { - BLI_assert(modal_type < TFM_MODAL_PLANE_X); - if (constraint_new == CON_AXIS2) { - return false; - } - if (constraint_curr == constraint_new) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_2d); - } + if (constraint_curr == constraint_new) { + stopConstraint(t); } else { - short orient_index = 1; - if (t->orient_curr == 0 || ELEM(constraint_curr, -1, constraint_new)) { - /* Successive presses on existing axis, cycle orientation modes. */ - orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); - } + setUserConstraint(t, constraint_new, msg_2d); + } + } + else { + short orient_index = 1; + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { + /* Successive presses on existing axis, cycle orientation modes. */ + orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); + } - transform_orientations_current_set(t, orient_index); - if (orient_index == 0) { - stopConstraint(t); - } - else { - setUserConstraint(t, constraint_new, msg_3d); - } + transform_orientations_current_set(t, orient_index); + if (orient_index == 0) { + stopConstraint(t); + } + else { + setUserConstraint(t, constraint_new, msg_3d); } - t->redraw |= TREDRAW_HARD; - return true; } - return false; + t->redraw |= TREDRAW_HARD; + return true; } int transformEvent(TransInfo *t, const wmEvent *event) @@ -814,7 +825,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } else if (event->type == MOUSEMOVE) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { t->con.mode |= CON_SELECT; } @@ -1062,10 +1073,10 @@ int transformEvent(TransInfo *t, const wmEvent *event) t->state = TRANS_CONFIRM; } else if ((t->flag & T_NO_CONSTRAINT) == 0) { - if (t->modifiers & (MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE)) { + if (t->modifiers & (MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE)) { /* Confirm. */ postSelectConstraint(t); - t->modifiers &= ~(MOD_CONSTRAINT_SELECT | MOD_CONSTRAINT_PLANE); + t->modifiers &= ~(MOD_CONSTRAINT_SELECT_AXIS | MOD_CONSTRAINT_SELECT_PLANE); } else { if (t->options & CTX_CAMERA) { @@ -1079,8 +1090,9 @@ int transformEvent(TransInfo *t, const wmEvent *event) } } else { - t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? MOD_CONSTRAINT_SELECT : - MOD_CONSTRAINT_PLANE; + t->modifiers |= (event->val == TFM_MODAL_AUTOCONSTRAINT) ? + MOD_CONSTRAINT_SELECT_AXIS : + MOD_CONSTRAINT_SELECT_PLANE; if (t->con.mode & CON_APPLY) { stopConstraint(t); } @@ -1412,25 +1424,6 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ToolSettings *ts = CTX_data_tool_settings(C); PropertyRNA *prop; - if (!(t->con.mode & CON_APPLY) && (t->flag & T_MODAL) && - ELEM(t->mode, TFM_TRANSLATION, TFM_RESIZE)) { - /* When redoing these modes the first time, it's more convenient to save - * in the Global orientation. */ - if (t->mode == TFM_TRANSLATION) { - mul_m3_v3(t->spacemtx, t->values_final); - } - else { - float tmat[3][3], sizemat[3][3]; - size_to_mat3(sizemat, t->values_final); - mul_m3_m3m3(tmat, t->spacemtx, sizemat); - mat3_to_size(t->values_final, tmat); - } - - BLI_assert(t->orient_curr == 0); - unit_m3(t->spacemtx); - t->orient[0].type = V3D_ORIENT_GLOBAL; - } - /* Save back mode in case we're in the generic operator */ if ((prop = RNA_struct_find_property(op->ptr, "mode"))) { RNA_property_enum_set(op->ptr, prop, t->mode); @@ -1638,7 +1631,7 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) /** * \note caller needs to free 't' on a 0 return - * \warning \a event might be NULL (when tweaking from redo panel) + * \warning \a event might be NULL (when tweaking from redo panel) * \see #saveTransform which writes these values back. */ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event, int mode) diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index f2d5800abb7..f03defe26e2 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -88,12 +88,11 @@ typedef enum { CTX_TEXTURE_SPACE = (1 << 9), CTX_NO_PET = (1 << 10), - CTX_NO_MIRROR = (1 << 11), - CTX_AUTOCONFIRM = (1 << 12), + CTX_AUTOCONFIRM = (1 << 11), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 13), + CTX_OBMODE_XFORM_OBDATA = (1 << 12), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), } eTContext; /** #TransInfo.flag */ @@ -105,60 +104,59 @@ typedef enum { /** restrictions flags */ T_NO_CONSTRAINT = 1 << 2, T_NULL_ONE = 1 << 3, - T_NO_ZERO = 1 << 4, - T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE | T_NO_ZERO, + T_ALL_RESTRICTIONS = T_NO_CONSTRAINT | T_NULL_ONE, - T_PROP_EDIT = 1 << 5, - T_PROP_CONNECTED = 1 << 6, - T_PROP_PROJECTED = 1 << 7, + T_PROP_EDIT = 1 << 4, + T_PROP_CONNECTED = 1 << 5, + T_PROP_PROJECTED = 1 << 6, T_PROP_EDIT_ALL = T_PROP_EDIT | T_PROP_CONNECTED | T_PROP_PROJECTED, - T_V3D_ALIGN = 1 << 8, + T_V3D_ALIGN = 1 << 7, /** For 2D views such as UV or f-curve. */ - T_2D_EDIT = 1 << 9, - T_CLIP_UV = 1 << 10, + T_2D_EDIT = 1 << 8, + T_CLIP_UV = 1 << 9, /** Auto-IK is on. */ - T_AUTOIK = 1 << 11, + T_AUTOIK = 1 << 10, /** Don't use mirror even if the data-block option is set. */ - T_NO_MIRROR = 1 << 12, + T_NO_MIRROR = 1 << 11, /** To indicate that the value set in the `value` parameter is the final * value of the transformation, modified only by the constrain. */ - T_INPUT_IS_VALUES_FINAL = 1 << 13, + T_INPUT_IS_VALUES_FINAL = 1 << 12, /** To specify if we save back settings at the end. */ - T_MODAL = 1 << 14, + T_MODAL = 1 << 13, /** No re-topology (projection). */ - T_NO_PROJECT = 1 << 15, + T_NO_PROJECT = 1 << 14, - T_RELEASE_CONFIRM = 1 << 16, + T_RELEASE_CONFIRM = 1 << 15, /** Alternative transformation. used to add offset to tracking markers. */ - T_ALT_TRANSFORM = 1 << 17, + T_ALT_TRANSFORM = 1 << 16, /** #TransInfo.center has been set, don't change it. */ - T_OVERRIDE_CENTER = 1 << 18, + T_OVERRIDE_CENTER = 1 << 17, - T_MODAL_CURSOR_SET = 1 << 19, + T_MODAL_CURSOR_SET = 1 << 18, - T_CLNOR_REBUILD = 1 << 20, + T_CLNOR_REBUILD = 1 << 19, /** Merges unselected into selected after transforming (runs after transforming). */ - T_AUTOMERGE = 1 << 21, + T_AUTOMERGE = 1 << 20, /** Runs auto-merge & splits. */ - T_AUTOSPLIT = 1 << 22, + T_AUTOSPLIT = 1 << 21, } eTFlag; /** #TransInfo.modifiers */ typedef enum { - MOD_CONSTRAINT_SELECT = 1 << 0, + MOD_CONSTRAINT_SELECT_AXIS = 1 << 0, MOD_PRECISION = 1 << 1, MOD_SNAP = 1 << 2, MOD_SNAP_INVERT = 1 << 3, - MOD_CONSTRAINT_PLANE = 1 << 4, + MOD_CONSTRAINT_SELECT_PLANE = 1 << 4, } eTModifier; /** #TransSnap.status */ @@ -432,14 +430,14 @@ typedef struct TransCustomDataContainer { /** * Container for Transform Data * - * Used to implement multi-object modes, so each object can have it's + * Used to implement multi-object modes, so each object can have its * own data array as well as object matrix, local center etc. * * Anything that can't be shared between all objects * and doesn't make sense to store for every vertex (in the #TransDataContainer.data). * * \note at some point this could be used to store non object containers - * although this only makes sense if each container has it's own matrices, + * although this only makes sense if each container has its own matrices, * otherwise all elements may as well be stored in one array (#TransDataContainer.data), * as is already done for curve-objects, f-curves. etc. */ @@ -598,11 +596,18 @@ typedef struct TransInfo { * mouse button then.) */ bool is_launch_event_tweak; + bool is_orient_set; + struct { short type; float matrix[3][3]; } orient[3]; - short orient_curr; + + enum { + O_DEFAULT = 0, + O_SCENE, + O_SET, + } orient_curr; /** backup from view3d, to restore on end. */ short gizmo_flag; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 2037981e655..8c74d5349ba 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -953,9 +953,8 @@ void startConstraint(TransInfo *t) void stopConstraint(TransInfo *t) { - if (t->orient_curr != 0) { - t->orient_curr = 0; - transform_orientations_current_set(t, t->orient_curr); + if (t->orient_curr != O_DEFAULT) { + transform_orientations_current_set(t, O_DEFAULT); } t->con.mode &= ~(CON_APPLY | CON_SELECT); @@ -971,12 +970,11 @@ void stopConstraint(TransInfo *t) void initSelectConstraint(TransInfo *t) { - if (t->orient_curr == 0) { - transform_orientations_current_set(t, 1); + if (t->orient_curr == O_DEFAULT) { + transform_orientations_current_set(t, O_SCENE); } setUserConstraint(t, CON_APPLY | CON_SELECT, "%s"); - setNearestAxis(t); } void selectConstraint(TransInfo *t) @@ -1063,7 +1061,7 @@ static void setNearestAxis3d(TransInfo *t) } if (len[0] <= len[1] && len[0] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS1 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s X axis"), t->spacename); } @@ -1073,7 +1071,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[1] <= len[0] && len[1] <= len[2]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS2); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Y axis"), t->spacename); } @@ -1083,7 +1081,7 @@ static void setNearestAxis3d(TransInfo *t) } } else if (len[2] <= len[1] && len[2] <= len[0]) { - if (t->modifiers & MOD_CONSTRAINT_PLANE) { + if (t->modifiers & MOD_CONSTRAINT_SELECT_PLANE) { t->con.mode |= (CON_AXIS0 | CON_AXIS1); BLI_snprintf(t->con.text, sizeof(t->con.text), TIP_(" locking %s Z axis"), t->spacename); } diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index c021c084a23..b4175faacf4 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -37,6 +37,7 @@ #include "BKE_context.h" #include "BKE_fcurve.h" #include "BKE_global.h" +#include "BKE_image.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -505,9 +506,27 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) bool clipx = true, clipy = true; float min[2], max[2]; - min[0] = min[1] = 0.0f; - max[0] = t->aspect[0]; - max[1] = t->aspect[1]; + /* Check if the current image in UV editor is a tiled image or not. */ + const SpaceImage *sima = t->area->spacedata.first; + const Image *image = sima->image; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + /* Stores the coordinates of the closest UDIM tile. + * Also acts as an offset to the tile from the origin of UV space. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ + if (is_tiled_image) { + int nearest_tile_index = BKE_image_find_nearest_tile(image, t->center_global); + if (nearest_tile_index != -1) { + nearest_tile_index -= 1001; + /* Getting coordinates of nearest tile from the tile index. */ + base_offset[0] = nearest_tile_index % 10; + base_offset[1] = nearest_tile_index / 10; + } + } + + min[0] = min[1] = FLT_MAX; + max[0] = max[1] = FLT_MIN; FOREACH_TRANS_DATA_CONTAINER (t, tc) { @@ -520,42 +539,48 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) } if (resize) { - if (min[0] < 0.0f && t->center_global[0] > 0.0f && t->center_global[0] < t->aspect[0] * 0.5f) { - vec[0] *= t->center_global[0] / (t->center_global[0] - min[0]); + if (min[0] < base_offset[0] && t->center_global[0] > base_offset[0] && + t->center_global[0] < base_offset[0] + (t->aspect[0] * 0.5f)) { + vec[0] *= (t->center_global[0] - base_offset[0]) / (t->center_global[0] - min[0]); } - else if (max[0] > t->aspect[0] && t->center_global[0] < t->aspect[0]) { - vec[0] *= (t->center_global[0] - t->aspect[0]) / (t->center_global[0] - max[0]); + else if (max[0] > (base_offset[0] + t->aspect[0]) && + t->center_global[0] < (base_offset[0] + t->aspect[0])) { + vec[0] *= (t->center_global[0] - (base_offset[0] + t->aspect[0])) / + (t->center_global[0] - max[0]); } else { clipx = 0; } - if (min[1] < 0.0f && t->center_global[1] > 0.0f && t->center_global[1] < t->aspect[1] * 0.5f) { - vec[1] *= t->center_global[1] / (t->center_global[1] - min[1]); + if (min[1] < base_offset[1] && t->center_global[1] > base_offset[1] && + t->center_global[1] < base_offset[1] + (t->aspect[1] * 0.5f)) { + vec[1] *= (t->center_global[1] - base_offset[1]) / (t->center_global[1] - min[1]); } - else if (max[1] > t->aspect[1] && t->center_global[1] < t->aspect[1]) { - vec[1] *= (t->center_global[1] - t->aspect[1]) / (t->center_global[1] - max[1]); + else if (max[1] > (base_offset[1] + t->aspect[1]) && + t->center_global[1] < (base_offset[1] + t->aspect[1])) { + vec[1] *= (t->center_global[1] - (base_offset[1] + t->aspect[1])) / + (t->center_global[1] - max[1]); } else { clipy = 0; } } else { - if (min[0] < 0.0f) { - vec[0] -= min[0]; + if (min[0] < base_offset[0]) { + vec[0] += base_offset[0] - min[0]; } - else if (max[0] > t->aspect[0]) { - vec[0] -= max[0] - t->aspect[0]; + else if (max[0] > base_offset[0] + t->aspect[0]) { + vec[0] -= max[0] - base_offset[0] - t->aspect[0]; } else { clipx = 0; } - if (min[1] < 0.0f) { - vec[1] -= min[1]; + if (min[1] < base_offset[1]) { + vec[1] += base_offset[1] - min[1]; } - else if (max[1] > t->aspect[1]) { - vec[1] -= max[1] - t->aspect[1]; + else if (max[1] > base_offset[1] + t->aspect[1]) { + vec[1] -= max[1] - base_offset[1] - t->aspect[1]; } else { clipy = 0; @@ -1122,8 +1147,7 @@ static void init_TransDataContainers(TransInfo *t, for (int i = 0; i < objects_len; i++) { TransDataContainer *tc = &t->data_container[i]; - if (((t->flag & T_NO_MIRROR) == 0) && ((t->options & CTX_NO_MIRROR) == 0) && - (objects[i]->type == OB_MESH)) { + if (!(t->flag & T_NO_MIRROR) && (objects[i]->type == OB_MESH)) { tc->use_mirror_axis_x = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_X) != 0; tc->use_mirror_axis_y = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Y) != 0; tc->use_mirror_axis_z = (((Mesh *)objects[i]->data)->symmetry & ME_SYMMETRY_Z) != 0; @@ -1472,91 +1496,89 @@ void createTransData(bContext *C, TransInfo *t) /** \name Transform Data Recalc/Flush * \{ */ -void clipMirrorModifier(TransInfo *t) +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc) { - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - Object *ob = tc->obedit; - ModifierData *md = ob->modifiers.first; - float tolerance[3] = {0.0f, 0.0f, 0.0f}; - int axis = 0; - - for (; md; md = md->next) { - if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { - MirrorModifierData *mmd = (MirrorModifierData *)md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - axis = 0; - if (mmd->flag & MOD_MIR_AXIS_X) { - axis |= 1; - tolerance[0] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Y) { - axis |= 2; - tolerance[1] = mmd->tolerance; - } - if (mmd->flag & MOD_MIR_AXIS_Z) { - axis |= 4; - tolerance[2] = mmd->tolerance; - } - if (axis) { - float mtx[4][4], imtx[4][4]; - int i; + Object *ob = tc->obedit; + ModifierData *md = ob->modifiers.first; + float tolerance[3] = {0.0f, 0.0f, 0.0f}; + int axis = 0; + + for (; md; md = md->next) { + if ((md->type == eModifierType_Mirror) && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + axis = 0; + if (mmd->flag & MOD_MIR_AXIS_X) { + axis |= 1; + tolerance[0] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Y) { + axis |= 2; + tolerance[1] = mmd->tolerance; + } + if (mmd->flag & MOD_MIR_AXIS_Z) { + axis |= 4; + tolerance[2] = mmd->tolerance; + } + if (axis) { + float mtx[4][4], imtx[4][4]; + int i; - if (mmd->mirror_ob) { - float obinv[4][4]; + if (mmd->mirror_ob) { + float obinv[4][4]; - invert_m4_m4(obinv, mmd->mirror_ob->obmat); - mul_m4_m4m4(mtx, obinv, ob->obmat); - invert_m4_m4(imtx, mtx); - } + invert_m4_m4(obinv, mmd->mirror_ob->obmat); + mul_m4_m4m4(mtx, obinv, ob->obmat); + invert_m4_m4(imtx, mtx); + } - TransData *td = tc->data; - for (i = 0; i < tc->data_len; i++, td++) { - int clip; - float loc[3], iloc[3]; + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + int clip; + float loc[3], iloc[3]; - if (td->loc == NULL) { - break; - } + if (td->loc == NULL) { + break; + } - if (td->flag & TD_SKIP) { - continue; - } + if (td->flag & TD_SKIP) { + continue; + } - copy_v3_v3(loc, td->loc); - copy_v3_v3(iloc, td->iloc); + copy_v3_v3(loc, td->loc); + copy_v3_v3(iloc, td->iloc); - if (mmd->mirror_ob) { - mul_m4_v3(mtx, loc); - mul_m4_v3(mtx, iloc); - } + if (mmd->mirror_ob) { + mul_m4_v3(mtx, loc); + mul_m4_v3(mtx, iloc); + } - clip = 0; - if (axis & 1) { - if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { - loc[0] = 0.0f; - clip = 1; - } + clip = 0; + if (axis & 1) { + if (fabsf(iloc[0]) <= tolerance[0] || loc[0] * iloc[0] < 0.0f) { + loc[0] = 0.0f; + clip = 1; } + } - if (axis & 2) { - if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { - loc[1] = 0.0f; - clip = 1; - } + if (axis & 2) { + if (fabsf(iloc[1]) <= tolerance[1] || loc[1] * iloc[1] < 0.0f) { + loc[1] = 0.0f; + clip = 1; } - if (axis & 4) { - if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { - loc[2] = 0.0f; - clip = 1; - } + } + if (axis & 4) { + if (fabsf(iloc[2]) <= tolerance[2] || loc[2] * iloc[2] < 0.0f) { + loc[2] = 0.0f; + clip = 1; } - if (clip) { - if (mmd->mirror_ob) { - mul_m4_v3(imtx, loc); - } - copy_v3_v3(td->loc, loc); + } + if (clip) { + if (mmd->mirror_ob) { + mul_m4_v3(imtx, loc); } + copy_v3_v3(td->loc, loc); } } } @@ -1644,38 +1666,6 @@ void animrecord_check_state(TransInfo *t, struct Object *ob) } } -static void recalcData_cursor_image(TransInfo *t) -{ - TransDataContainer *tc = t->data_container; - TransData *td = tc->data; - float aspect_inv[2]; - - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; - - td->loc[0] = td->loc[0] * aspect_inv[0]; - td->loc[1] = td->loc[1] * aspect_inv[1]; - - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_cursor(TransInfo *t) -{ - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -static void recalcData_obedit(TransInfo *t) -{ - if (t->state != TRANS_CANCEL) { - applyProject(t); - } - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->data_len) { - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ - } - } -} - /* called for updating while transform acts, once per redraw */ void recalcData(TransInfo *t) { @@ -1742,7 +1732,7 @@ void recalcData(TransInfo *t) recalcData_tracking(t); break; case TC_MBALL_VERTS: - recalcData_obedit(t); + recalcData_mball(t); break; case TC_LATTICE_VERTS: recalcData_lattice(t); diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 36a51d57f64..11550ec8803 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -45,7 +45,7 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize); void clipUVData(TransInfo *t); /* transform_convert_mesh.c */ -void mesh_customdatacorrect_init(TransInfo *t); +void transform_convert_mesh_customdatacorrect_init(TransInfo *t); /* transform_convert_sequencer.c */ int transform_convert_sequencer_get_snap_bound(TransInfo *t); @@ -62,7 +62,7 @@ void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic); struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt); char transform_convert_frame_side_dir_get(TransInfo *t, float cframe); bool FrameOnMouseSide(char side, float frame, float cframe); -void clipMirrorModifier(TransInfo *t); +void transform_convert_clip_mirror_modifier_apply(TransDataContainer *tc); void animrecord_check_state(TransInfo *t, struct Object *ob); /* transform_convert_action.c */ @@ -84,6 +84,8 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); void createTransCursor_view3d(TransInfo *t); +void recalcData_cursor_image(TransInfo *t); +void recalcData_cursor(TransInfo *t); /* transform_convert_curve.c */ void createTransCurveVerts(TransInfo *t); @@ -109,6 +111,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t); /* transform_convert_mball.c */ void createTransMBallVerts(TransInfo *t); +void recalcData_mball(TransInfo *t); /* transform_convert_mesh.c */ struct TransIslandData { diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 7f24a0fa5f8..aaea9d05f84 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1268,9 +1268,6 @@ void recalcData_edit_armature(TransInfo *t) restoreBones(tc); } } - - /* Tag for redraw/invalidate overlay cache. */ - DEG_id_tag_update(&arm->id, ID_RECALC_SELECT); } } diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 67d85f9610b..1f3eff31205 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -134,3 +134,29 @@ void createTransCursor_view3d(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Cursor + * \{ */ + +void recalcData_cursor_image(TransInfo *t) +{ + TransDataContainer *tc = t->data_container; + TransData *td = tc->data; + float aspect_inv[2]; + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + td->loc[0] = td->loc[0] * aspect_inv[0]; + td->loc[1] = td->loc[1] * aspect_inv[1]; + + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +void recalcData_cursor(TransInfo *t) +{ + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c index 3ff83e00f43..e57fd85470f 100644 --- a/source/blender/editors/transform/transform_convert_curve.c +++ b/source/blender/editors/transform/transform_convert_curve.c @@ -441,7 +441,6 @@ void createTransCurveVerts(TransInfo *t) void recalcData_curve(TransInfo *t) { if (t->state != TRANS_CANCEL) { - clipMirrorModifier(t); applyProject(t); } @@ -450,7 +449,7 @@ void recalcData_curve(TransInfo *t) ListBase *nurbs = BKE_curve_editNurbs_get(cu); Nurb *nu = nurbs->first; - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); if (t->state == TRANS_CANCEL) { while (nu) { @@ -460,7 +459,10 @@ void recalcData_curve(TransInfo *t) } } else { - /* Normal updating */ + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + /* Normal updating. */ BKE_curve_dimension_update(cu); } } diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 45df0e66691..4932a5f8d23 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -78,14 +78,12 @@ static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visibl flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) | ((bezt->f3 & SELECT) ? SEL_F3 : 0); } - else { - if (bezt->f2 & SELECT) { - flag = SEL_ALL; - } + else if (bezt->f2 & SELECT) { + flag = SEL_ALL; } /* Special case for auto & aligned handles */ - if (flag != SEL_ALL && flag & SEL_F2) { + if ((flag != SEL_ALL) && (flag & SEL_F2)) { if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) { flag = SEL_ALL; } @@ -316,7 +314,7 @@ static void createTransGPencil_curves(bContext *C, } } else if (handles_visible) { - if (BEZT_ISSEL_IDX(bezt, j)) { + if (sel) { td->flag = TD_SELECTED; } else { diff --git a/source/blender/editors/transform/transform_convert_lattice.c b/source/blender/editors/transform/transform_convert_lattice.c index 20ac7dcb998..fbfce41d555 100644 --- a/source/blender/editors/transform/transform_convert_lattice.c +++ b/source/blender/editors/transform/transform_convert_lattice.c @@ -122,7 +122,7 @@ void recalcData_lattice(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { Lattice *la = tc->obedit->data; - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); if (la->editlatt->latt->flag & LT_OUTSIDE) { outside_lattice(la->editlatt->latt); } diff --git a/source/blender/editors/transform/transform_convert_mball.c b/source/blender/editors/transform/transform_convert_mball.c index 6f5c0318054..f38f3ccf421 100644 --- a/source/blender/editors/transform/transform_convert_mball.c +++ b/source/blender/editors/transform/transform_convert_mball.c @@ -30,6 +30,8 @@ #include "BKE_context.h" #include "transform.h" +#include "transform_snap.h" + #include "transform_convert.h" /* -------------------------------------------------------------------- */ @@ -128,3 +130,21 @@ void createTransMBallVerts(TransInfo *t) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Recalc Meta Ball + * \{ */ + +void recalcData_mball(TransInfo *t) +{ + if (t->state != TRANS_CANCEL) { + applyProject(t); + } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); + } + } +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index 93c36645873..f715228e25e 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -48,7 +48,575 @@ #include "transform_convert.h" +/* -------------------------------------------------------------------- */ +/** \name Container TransCustomData Creation + * \{ */ + +struct TransCustomDataMergeGroup { + /** map {BMVert: TransCustomDataLayerVert} */ + struct LinkNode **cd_loop_groups; +}; + +struct TransCustomDataLayer { + BMesh *bm; + struct MemArena *arena; + + struct GHash *origfaces; + struct BMesh *bm_origfaces; + + /* Special handle for multi-resolution. */ + int cd_loop_mdisp_offset; + + /* Optionally merge custom-data groups (this keeps UVs connected for example). */ + struct { + /** map {BMVert: TransDataBasic} */ + struct GHash *origverts; + struct TransCustomDataMergeGroup *data; + int data_len; + /** Array size of 'layer_math_map_len' + * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ + int *customdatalayer_map; + /** Number of math BMLoop layers. */ + int customdatalayer_map_len; + } merge_group; + + bool use_merge_group; +}; + +static void tc_mesh_customdatacorrect_free_fn(struct TransInfo *UNUSED(t), + struct TransDataContainer *UNUSED(tc), + struct TransCustomData *custom_data) +{ + struct TransCustomDataLayer *tcld = custom_data->data; + bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + + if (tcld->bm_origfaces) { + BM_mesh_free(tcld->bm_origfaces); + } + if (tcld->origfaces) { + BLI_ghash_free(tcld->origfaces, NULL, NULL); + } + if (tcld->merge_group.origverts) { + BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); + } + if (tcld->arena) { + BLI_memarena_free(tcld->arena); + } + if (tcld->merge_group.customdatalayer_map) { + MEM_freeN(tcld->merge_group.customdatalayer_map); + } + + MEM_freeN(tcld); + custom_data->data = NULL; +} + #define USE_FACE_SUBSTITUTE +#ifdef USE_FACE_SUBSTITUTE +# define FACE_SUBSTITUTE_INDEX INT_MIN + +/** + * Search for a neighboring face with area and preferably without selected vertex. + * Used to replace area-less faces in custom-data correction. + */ +static BMFace *tc_mesh_customdatacorrect_find_best_face_substitute(BMFace *f) +{ + BMFace *best_face = NULL; + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BMLoop *l_radial_next = l->radial_next; + BMFace *f_test = l_radial_next->f; + if (f_test == f) { + continue; + } + if (is_zero_v3(f_test->no)) { + continue; + } + + /* Check the loops edge isn't selected. */ + if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && + !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { + /* Prefer edges with unselected vertices. + * Useful for extrude. */ + best_face = f_test; + break; + } + if (best_face == NULL) { + best_face = f_test; + } + } + return best_face; +} + +static void tc_mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, + BMFace *f, + BMFace *f_copy) +{ + BLI_assert(is_zero_v3(f->no)); + BMesh *bm = tcld->bm; + /* It is impossible to calculate the loops weights of a face without area. + * Find a substitute. */ + BMFace *f_substitute = tc_mesh_customdatacorrect_find_best_face_substitute(f); + if (f_substitute) { + /* Copy the custom-data from the substitute face. */ + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); + } while ((l_iter = l_iter->next) != l_first); + + /* Use the substitute face as the reference during the transformation. */ + BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); + + /* Hack: reference substitute face in `f_copy->no`. + * `tcld->origfaces` is already used to restore the initial value. */ + BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); + *((BMFace **)&f_copy->no[0]) = f_substitute_copy; + } +} + +static BMFace *tc_mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) +{ + BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); + return *((BMFace **)&f_copy->no[0]); +} + +#endif /* USE_FACE_SUBSTITUTE */ + +static void tc_mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + const int index) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + BMIter liter; + int j, l_num; + float *loop_weights; + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMLoop *l = BM_iter_step(&liter); + BMLoop *l_prev, *l_next; + + /* Generic custom-data correction. Copy face data. */ + void **val_p; + if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { + BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); + *val_p = f_copy; +#ifdef USE_FACE_SUBSTITUTE + if (is_zero_v3(l->f->no)) { + tc_mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); + } +#endif + } + + if (tcld->use_merge_group) { + if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && + (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { + loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + /* Store cd_loop_groups. */ + struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; + if (l_num != 0) { + merge_data->cd_loop_groups = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + const int layer_nr = tcld->merge_group.customdatalayer_map[j]; + merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( + bm, v, layer_nr, loop_weights, tcld->arena); + } + } + else { + merge_data->cd_loop_groups = NULL; + } + + BLI_ghash_insert(tcld->merge_group.origverts, v, td); + } +} + +static void tc_mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + + struct GHash *origfaces = BLI_ghash_ptr_new(__func__); + struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); + + /* We need to have matching loop custom-data. */ + BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); + + tcld->origfaces = origfaces; + tcld->bm_origfaces = bm_origfaces; + + bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); + tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); +} + +static void tc_mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, + struct TransCustomDataLayer *tcld) +{ + BMesh *bm = tcld->bm; + BLI_assert(CustomData_has_math(&bm->ldata)); + + /* TODO: We don't need `layer_math_map` when there are no loops linked + * to one of the sliding vertices. */ + + /* Over allocate, only 'math' layers are indexed. */ + int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); + int layer_math_map_len = 0; + for (int i = 0; i < bm->ldata.totlayer; i++) { + if (CustomData_layer_has_math(&bm->ldata, i)) { + customdatalayer_map[layer_math_map_len++] = i; + } + } + BLI_assert(layer_math_map_len != 0); + + tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; + tcld->merge_group.customdatalayer_map = customdatalayer_map; + tcld->merge_group.customdatalayer_map_len = layer_math_map_len; + tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); + tcld->merge_group.data = BLI_memarena_alloc( + tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); +} + +static struct TransCustomDataLayer *tc_mesh_customdatacorrect_create(TransDataContainer *tc, + const bool use_merge_group) +{ + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + + if (bm->shapenr > 1) { + /* Don't do this at all for non-basis shape keys, too easy to + * accidentally break uv maps or vertex colors then */ + /* create copies of faces for custom-data projection. */ + return NULL; + } + if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + /* There is no custom-data to correct. */ + return NULL; + } + + struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); + tcld->bm = bm; + tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); + + /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ + tcld->cd_loop_mdisp_offset = -1; + tcld->use_merge_group = use_merge_group; + + tc_mesh_customdatacorrect_init_container_generic(tc, tcld); + + if (tcld->use_merge_group) { + tc_mesh_customdatacorrect_init_container_merge_group(tc, tcld); + } + + { + /* Setup Verts. */ + int i = 0; + + TransData *tob = tc->data; + for (int j = tc->data_len; j--; tob++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { + tc_mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); + } + } + + return tcld; +} + +static void tc_mesh_customdata_create(TransDataContainer *tc, const bool use_merge_group) +{ + struct TransCustomDataLayer *customdatacorrect; + customdatacorrect = tc_mesh_customdatacorrect_create(tc, use_merge_group); + + if (!customdatacorrect) { + return; + } + + BLI_assert(tc->custom.type.data == NULL); + tc->custom.type.data = customdatacorrect; + tc->custom.type.free_cb = tc_mesh_customdatacorrect_free_fn; +} + +void transform_convert_mesh_customdatacorrect_init(TransInfo *t) +{ + bool use_merge_group = false; + if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { + /* No custom-data correction. */ + return; + } + use_merge_group = true; + } + else if (ELEM(t->mode, + TFM_TRANSLATION, + TFM_ROTATION, + TFM_RESIZE, + TFM_TOSPHERE, + TFM_SHEAR, + TFM_BEND, + TFM_SHRINKFATTEN, + TFM_TRACKBALL, + TFM_PUSHPULL, + TFM_ALIGN)) { + { + if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { + /* No custom-data correction. */ + return; + } + use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; + } + } + else { + return; + } + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->custom.type.data != NULL) { + tc_mesh_customdatacorrect_free_fn(t, tc, &tc->custom.type); + } + + tc_mesh_customdata_create(tc, use_merge_group); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Apply + * \{ */ + +/** + * If we're sliding the vert, return its original location, if not, the current location is good. + */ +static const float *tc_mesh_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) +{ + TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); + return td ? td->iloc : v->co; +} + +static void tc_mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, + struct TransDataBasic *td, + struct TransCustomDataMergeGroup *merge_data, + bool do_loop_mdisps) +{ + BMesh *bm = tcld->bm; + BMVert *v = td->extra; + const float *co_orig_3d = td->iloc; + + BMIter liter; + int j, l_num; + float *loop_weights; + const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); + const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; + const float *v_proj_axis = v->no; + /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ + float v_proj[3][3]; + + if (do_loop_weight) { + project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); + } + + // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) + BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); + l_num = liter.count; + loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; + for (j = 0; j < l_num; j++) { + BMFace *f_copy; /* the copy of 'f' */ + BMLoop *l = BM_iter_step(&liter); + + f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + +#ifdef USE_FACE_SUBSTITUTE + /* In some faces it is not possible to calculate interpolation, + * so we use a substitute. */ + if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { + f_copy = tc_mesh_customdatacorrect_face_substitute_get(f_copy); + } +#endif + + /* only loop data, no vertex data since that contains shape keys, + * and we do not want to mess up other shape keys */ + BM_loop_interp_from_face(bm, l, f_copy, false, false); + + /* weight the loop */ + if (do_loop_weight) { + const float eps = 1.0e-8f; + const BMLoop *l_prev = l->prev; + const BMLoop *l_next = l->next; + const float *co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + const float *co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + bool co_prev_ok; + bool co_next_ok; + + /* In the unlikely case that we're next to a zero length edge - + * walk around the to the next. + * + * Since we only need to check if the vertex is in this corner, + * its not important _which_ loop - as long as its not overlapping + * 'sv->co_orig_3d', see: T45096. */ + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && + ((l_prev = l_prev->prev) != l->next))) { + co_prev = tc_mesh_vert_orig_co_get(tcld, l_prev->v); + project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); + } + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && + ((l_next = l_next->next) != l->prev))) { + co_next = tc_mesh_vert_orig_co_get(tcld, l_next->v); + project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); + } + + if (co_prev_ok && co_next_ok) { + const float dist = dist_signed_squared_to_corner_v3v3v3( + v->co, UNPACK3(v_proj), v_proj_axis); + + loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); + if (UNLIKELY(!isfinite(loop_weights[j]))) { + loop_weights[j] = 0.0f; + } + } + else { + loop_weights[j] = 0.0f; + } + } + } + + if (tcld->use_merge_group) { + struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; + if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { + if (do_loop_weight) { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge_weights( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + } + } + else { + for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { + BM_vert_loop_groups_data_layer_merge( + bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + } + } + } + } + + /* Special handling for multires + * + * Interpolate from every other loop (not ideal) + * However values will only be taken from loops which overlap other mdisps. + */ + const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); + if (update_loop_mdisps) { + float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); + BMLoop *l; + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BM_face_calc_center_median(l->f, faces_center[j]); + } + + BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { + BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); + float f_copy_center[3]; + BMIter liter_other; + BMLoop *l_other; + int j_other; + + BM_face_calc_center_median(f_copy, f_copy_center); + + BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { + BM_face_interp_multires_ex(bm, + l_other->f, + f_copy, + faces_center[j_other], + f_copy_center, + tcld->cd_loop_mdisp_offset); + } + } + } +} + +static void tc_mesh_customdatacorrect_apply(TransDataContainer *tc, bool is_final) +{ + if (!tc->custom.type.data) { + return; + } + struct TransCustomDataLayer *tcld = tc->custom.type.data; + const bool use_merge_group = tcld->use_merge_group; + + struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; + TransData *tob = tc->data; + for (int i = tc->data_len; i--; tob++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } + + TransDataMirror *td_mirror = tc->data_mirror; + for (int i = tc->data_mirror_len; i--; td_mirror++) { + tc_mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); + + if (use_merge_group) { + merge_data++; + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name CustomData Layer Correction Restore + * \{ */ + +static void tc_mesh_customdatacorrect_restore(struct TransInfo *t) +{ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + struct TransCustomDataLayer *tcld = tc->custom.type.data; + if (!tcld) { + continue; + } + + BMesh *bm = tcld->bm; + BMesh *bm_copy = tcld->bm_origfaces; + + GHashIterator gh_iter; + GHASH_ITER (gh_iter, tcld->origfaces) { + BMFace *f = BLI_ghashIterator_getKey(&gh_iter); + BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); + BLI_assert(f->len == f_copy->len); + + BMLoop *l_iter, *l_first, *l_copy; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + l_copy = BM_FACE_FIRST_LOOP(f_copy); + do { + /* TODO: Restore only the elements that transform. */ + BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); + l_copy = l_copy->next; + } while ((l_iter = l_iter->next) != l_first); + } + } +} + +/** \} */ /* -------------------------------------------------------------------- */ /** \name Island Creation @@ -384,6 +952,14 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, BMEdge *e; BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) { + + /* Always clear to satisfy the assert, also predictable to leave in cleared state. */ + BM_elem_flag_disable(e, tag_queued); + + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + continue; + } + BMVert *v1 = e->v1; BMVert *v2 = e->v2; int i1 = BM_elem_index_get(v1); @@ -392,7 +968,6 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, if (dists[i1] != FLT_MAX || dists[i2] != FLT_MAX) { BLI_LINKSTACK_PUSH(queue, e); } - BM_elem_flag_disable(e, tag_queued); BM_elem_flag_set(e, tag_loose, bmesh_test_loose_edge(e)); } } @@ -420,6 +995,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, BMIter eiter; BM_ITER_ELEM (e_other, &eiter, v2, BM_EDGES_OF_VERT) { if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 && + !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) && (BM_elem_flag_test(e, tag_loose) || BM_elem_flag_test(e_other, tag_loose))) { BM_elem_flag_enable(e_other, tag_queued); BLI_LINKSTACK_PUSH(queue_next, e_other); @@ -433,6 +1009,11 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, BMLoop *l; BMIter liter; BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { + if (BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { + continue; + } + /* Don't check hidden edges or vertices in this loop + * since any hidden edge causes the face to be hidden too. */ for (BMLoop *l_other = l->next->next; l_other != l; l_other = l_other->next) { BMVert *v_other = l_other->v; BLI_assert(!ELEM(v_other, v1, v2)); @@ -445,6 +1026,7 @@ void transform_convert_mesh_connectivity_distance(struct BMesh *bm, BMIter eiter; BM_ITER_ELEM (e_other, &eiter, v_other, BM_EDGES_OF_VERT) { if (e_other != e && BM_elem_flag_test(e_other, tag_queued) == 0 && + !BM_elem_flag_test(e_other, BM_ELEM_HIDDEN) && (BM_elem_flag_test(e_other, tag_loose) || dists[BM_elem_index_get(BM_edge_other_vert(e_other, v_other))] != FLT_MAX)) { BM_elem_flag_enable(e_other, tag_queued); @@ -727,10 +1309,10 @@ void transform_convert_mesh_crazyspace_free(struct TransMeshDataCrazySpace *r_cr /** \name Edit Mesh Verts Transform Creation * \{ */ -static void transdata_center_get(const struct TransIslandData *island_data, - const int island_index, - const float iloc[3], - float r_center[3]) +static void tc_mesh_transdata_center_copy(const struct TransIslandData *island_data, + const int island_index, + const float iloc[3], + float r_center[3]) { if (island_data->center && island_index != -1) { copy_v3_v3(r_center, island_data->center[island_index]); @@ -769,7 +1351,7 @@ static void VertsToTransData(TransInfo *t, no = eve->no; } - transdata_center_get(island_data, island_index, td->iloc, td->center); + tc_mesh_transdata_center_copy(island_data, island_index, td->iloc, td->center); if ((island_index != -1) && island_data->axismtx) { copy_m3_m3(td->axismtx, island_data->axismtx[island_index]); @@ -959,7 +1541,8 @@ void createTransEditVerts(TransInfo *t) copy_v3_v3(td_mirror->iloc, eve->co); td_mirror->flag = mirror_data.vert_map[a].flag; td_mirror->loc_src = v_src->co; - transdata_center_get(&island_data, island_index, td_mirror->iloc, td_mirror->center); + tc_mesh_transdata_center_copy( + &island_data, island_index, td_mirror->iloc, td_mirror->center); td_mirror++; } @@ -1031,590 +1614,39 @@ void createTransEditVerts(TransInfo *t) /** \} */ /* -------------------------------------------------------------------- */ -/** \name CustomData Layer Correction +/** \name Recalc Mesh Data * \{ */ -struct TransCustomDataMergeGroup { - /** map {BMVert: TransCustomDataLayerVert} */ - struct LinkNode **cd_loop_groups; -}; - -struct TransCustomDataLayer { - BMesh *bm; - struct MemArena *arena; - - struct GHash *origfaces; - struct BMesh *bm_origfaces; - - /* Special handle for multi-resolution. */ - int cd_loop_mdisp_offset; - - /* Optionally merge custom-data groups (this keeps UVs connected for example). */ - struct { - /** map {BMVert: TransDataBasic} */ - struct GHash *origverts; - struct TransCustomDataMergeGroup *data; - int data_len; - /** Array size of 'layer_math_map_len' - * maps #TransCustomDataLayerVert.cd_group index to absolute #CustomData layer index */ - int *customdatalayer_map; - /** Number of math BMLoop layers. */ - int customdatalayer_map_len; - } merge_group; - - bool use_merge_group; -}; - -static void mesh_customdatacorrect_free_cb(struct TransInfo *UNUSED(t), - struct TransDataContainer *UNUSED(tc), - struct TransCustomData *custom_data) -{ - struct TransCustomDataLayer *tcld = custom_data->data; - bmesh_edit_end(tcld->bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - - if (tcld->bm_origfaces) { - BM_mesh_free(tcld->bm_origfaces); - } - if (tcld->origfaces) { - BLI_ghash_free(tcld->origfaces, NULL, NULL); - } - if (tcld->merge_group.origverts) { - BLI_ghash_free(tcld->merge_group.origverts, NULL, NULL); - } - if (tcld->arena) { - BLI_memarena_free(tcld->arena); - } - if (tcld->merge_group.customdatalayer_map) { - MEM_freeN(tcld->merge_group.customdatalayer_map); - } - - MEM_freeN(tcld); - custom_data->data = NULL; -} - -#ifdef USE_FACE_SUBSTITUTE - -# define FACE_SUBSTITUTE_INDEX INT_MIN - -/** - * Search for a neighboring face with area and preferably without selected vertex. - * Used to replace area-less faces in custom-data correction. - */ -static BMFace *mesh_customdatacorrect_find_best_face_substitute(BMFace *f) -{ - BMFace *best_face = NULL; - BMLoop *l; - BMIter liter; - BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { - BMLoop *l_radial_next = l->radial_next; - BMFace *f_test = l_radial_next->f; - if (f_test == f) { - continue; - } - if (is_zero_v3(f_test->no)) { - continue; - } - - /* Check the loops edge isn't selected. */ - if (!BM_elem_flag_test(l_radial_next->v, BM_ELEM_SELECT) && - !BM_elem_flag_test(l_radial_next->next->v, BM_ELEM_SELECT)) { - /* Prefer edges with unselected vertices. - * Useful for extrude. */ - best_face = f_test; - break; - } - if (best_face == NULL) { - best_face = f_test; - } - } - return best_face; -} - -static void mesh_customdatacorrect_face_substitute_set(struct TransCustomDataLayer *tcld, - BMFace *f, - BMFace *f_copy) -{ - BLI_assert(is_zero_v3(f->no)); - BMesh *bm = tcld->bm; - /* It is impossible to calculate the loops weights of a face without area. - * Find a substitute. */ - BMFace *f_substitute = mesh_customdatacorrect_find_best_face_substitute(f); - if (f_substitute) { - /* Copy the custom-data from the substitute face. */ - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - do { - BM_loop_interp_from_face(bm, l_iter, f_substitute, false, false); - } while ((l_iter = l_iter->next) != l_first); - - /* Use the substitute face as the reference during the transformation. */ - BMFace *f_substitute_copy = BM_face_copy(tcld->bm_origfaces, bm, f_substitute, true, true); - - /* Hack: reference substitute face in `f_copy->no`. - * `tcld->origfaces` is already used to restore the initial value. */ - BM_elem_index_set(f_copy, FACE_SUBSTITUTE_INDEX); - *((BMFace **)&f_copy->no[0]) = f_substitute_copy; - } -} - -static BMFace *mesh_customdatacorrect_face_substitute_get(BMFace *f_copy) -{ - BLI_assert(BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX); - return *((BMFace **)&f_copy->no[0]); -} - -#endif /* USE_FACE_SUBSTITUTE */ - -static void mesh_customdatacorrect_init_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - const int index) -{ - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - BMIter liter; - int j, l_num; - float *loop_weights; - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) { - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = tcld->use_merge_group ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMLoop *l = BM_iter_step(&liter); - BMLoop *l_prev, *l_next; - - /* Generic custom-data correction. Copy face data. */ - void **val_p; - if (!BLI_ghash_ensure_p(tcld->origfaces, l->f, &val_p)) { - BMFace *f_copy = BM_face_copy(tcld->bm_origfaces, bm, l->f, true, true); - *val_p = f_copy; -#ifdef USE_FACE_SUBSTITUTE - if (is_zero_v3(l->f->no)) { - mesh_customdatacorrect_face_substitute_set(tcld, l->f, f_copy); - } -#endif - } - - if (tcld->use_merge_group) { - if ((l_prev = BM_loop_find_prev_nodouble(l, l->next, FLT_EPSILON)) && - (l_next = BM_loop_find_next_nodouble(l, l_prev, FLT_EPSILON))) { - loop_weights[j] = angle_v3v3v3(l_prev->v->co, l->v->co, l_next->v->co); - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - /* Store cd_loop_groups. */ - struct TransCustomDataMergeGroup *merge_data = &tcld->merge_group.data[index]; - if (l_num != 0) { - merge_data->cd_loop_groups = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.customdatalayer_map_len * sizeof(void *)); - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - const int layer_nr = tcld->merge_group.customdatalayer_map[j]; - merge_data->cd_loop_groups[j] = BM_vert_loop_groups_data_layer_create( - bm, v, layer_nr, loop_weights, tcld->arena); - } - } - else { - merge_data->cd_loop_groups = NULL; - } - - BLI_ghash_insert(tcld->merge_group.origverts, v, td); - } -} - -static void mesh_customdatacorrect_init_container_generic(TransDataContainer *UNUSED(tc), - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - - struct GHash *origfaces = BLI_ghash_ptr_new(__func__); - struct BMesh *bm_origfaces = BM_mesh_create(&bm_mesh_allocsize_default, - &((struct BMeshCreateParams){ - .use_toolflags = false, - })); - - /* We need to have matching loop custom-data. */ - BM_mesh_copy_init_customdata_all_layers(bm_origfaces, bm, BM_LOOP, NULL); - - tcld->origfaces = origfaces; - tcld->bm_origfaces = bm_origfaces; - - bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - tcld->cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); -} - -static void mesh_customdatacorrect_init_container_merge_group(TransDataContainer *tc, - struct TransCustomDataLayer *tcld) -{ - BMesh *bm = tcld->bm; - BLI_assert(CustomData_has_math(&bm->ldata)); - - /* TODO: We don't need `layer_math_map` when there are no loops linked - * to one of the sliding vertices. */ - - /* Over allocate, only 'math' layers are indexed. */ - int *customdatalayer_map = MEM_mallocN(sizeof(int) * bm->ldata.totlayer, __func__); - int layer_math_map_len = 0; - for (int i = 0; i < bm->ldata.totlayer; i++) { - if (CustomData_layer_has_math(&bm->ldata, i)) { - customdatalayer_map[layer_math_map_len++] = i; - } - } - BLI_assert(layer_math_map_len != 0); - - tcld->merge_group.data_len = tc->data_len + tc->data_mirror_len; - tcld->merge_group.customdatalayer_map = customdatalayer_map; - tcld->merge_group.customdatalayer_map_len = layer_math_map_len; - tcld->merge_group.origverts = BLI_ghash_ptr_new_ex(__func__, tcld->merge_group.data_len); - tcld->merge_group.data = BLI_memarena_alloc( - tcld->arena, tcld->merge_group.data_len * sizeof(*tcld->merge_group.data)); -} - -static void mesh_customdatacorrect_init_container(TransDataContainer *tc, - const bool use_merge_group) -{ - if (tc->custom.type.data) { - /* The custom-data correction has been initiated before. - * Free since some modes have different settings. */ - mesh_customdatacorrect_free_cb(NULL, tc, &tc->custom.type); - } - - BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); - BMesh *bm = em->bm; - - if (bm->shapenr > 1) { - /* Don't do this at all for non-basis shape keys, too easy to - * accidentally break uv maps or vertex colors then */ - /* create copies of faces for custom-data projection. */ - return; - } - if (!CustomData_has_math(&bm->ldata) && !CustomData_has_layer(&bm->ldata, CD_MDISPS)) { - /* There is no custom-data to correct. */ - return; - } - - struct TransCustomDataLayer *tcld = MEM_callocN(sizeof(*tcld), __func__); - tcld->bm = bm; - tcld->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); - - /* Init `cd_loop_mdisp_offset` to -1 to avoid problems with a valid index. */ - tcld->cd_loop_mdisp_offset = -1; - tcld->use_merge_group = use_merge_group; - - mesh_customdatacorrect_init_container_generic(tc, tcld); - - if (tcld->use_merge_group) { - mesh_customdatacorrect_init_container_merge_group(tc, tcld); - } - - { - /* Setup Verts. */ - int i = 0; - - TransData *tob = tc->data; - for (int j = tc->data_len; j--; tob++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)tob, i); - } - - TransDataMirror *td_mirror = tc->data_mirror; - for (int j = tc->data_mirror_len; j--; td_mirror++, i++) { - mesh_customdatacorrect_init_vert(tcld, (TransDataBasic *)td_mirror, i); - } - } - - tc->custom.type.data = tcld; - tc->custom.type.free_cb = mesh_customdatacorrect_free_cb; -} - -void mesh_customdatacorrect_init(TransInfo *t) -{ - bool use_merge_group = false; - if (ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_SLIDE)) { - /* No custom-data correction. */ - return; - } - use_merge_group = true; - } - else if (ELEM(t->mode, - TFM_TRANSLATION, - TFM_ROTATION, - TFM_RESIZE, - TFM_TOSPHERE, - TFM_SHEAR, - TFM_BEND, - TFM_SHRINKFATTEN, - TFM_TRACKBALL, - TFM_PUSHPULL, - TFM_ALIGN)) { - { - if (!(t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT)) { - /* No custom-data correction. */ - return; - } - use_merge_group = (t->settings->uvcalc_flag & UVCALC_TRANSFORM_CORRECT_KEEP_CONNECTED) != 0; - } - } - else { - return; - } - - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - mesh_customdatacorrect_init_container(tc, use_merge_group); - } -} - -/** - * If we're sliding the vert, return its original location, if not, the current location is good. - */ -static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BMVert *v) -{ - TransDataBasic *td = BLI_ghash_lookup(tcld->merge_group.origverts, v); - return td ? td->iloc : v->co; -} - -static void mesh_customdatacorrect_apply_vert(struct TransCustomDataLayer *tcld, - struct TransDataBasic *td, - struct TransCustomDataMergeGroup *merge_data, - bool do_loop_mdisps) +static void tc_mesh_transdata_mirror_apply(TransDataContainer *tc) { - BMesh *bm = tcld->bm; - BMVert *v = td->extra; - const float *co_orig_3d = td->iloc; - - BMIter liter; - int j, l_num; - float *loop_weights; - const bool is_moved = (len_squared_v3v3(v->co, co_orig_3d) > FLT_EPSILON); - const bool do_loop_weight = is_moved && tcld->merge_group.customdatalayer_map_len; - const float *v_proj_axis = v->no; - /* original (l->prev, l, l->next) projections for each loop ('l' remains unchanged) */ - float v_proj[3][3]; - - if (do_loop_weight) { - project_plane_normalized_v3_v3v3(v_proj[1], co_orig_3d, v_proj_axis); - } - - // BM_ITER_ELEM (l, &liter, sv->v, BM_LOOPS_OF_VERT) - BM_iter_init(&liter, bm, BM_LOOPS_OF_VERT, v); - l_num = liter.count; - loop_weights = do_loop_weight ? BLI_array_alloca(loop_weights, l_num) : NULL; - for (j = 0; j < l_num; j++) { - BMFace *f_copy; /* the copy of 'f' */ - BMLoop *l = BM_iter_step(&liter); - - f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - -#ifdef USE_FACE_SUBSTITUTE - /* In some faces it is not possible to calculate interpolation, - * so we use a substitute. */ - if (BM_elem_index_get(f_copy) == FACE_SUBSTITUTE_INDEX) { - f_copy = mesh_customdatacorrect_face_substitute_get(f_copy); - } -#endif - - /* only loop data, no vertex data since that contains shape keys, - * and we do not want to mess up other shape keys */ - BM_loop_interp_from_face(bm, l, f_copy, false, false); - - /* weight the loop */ - if (do_loop_weight) { - const float eps = 1.0e-8f; - const BMLoop *l_prev = l->prev; - const BMLoop *l_next = l->next; - const float *co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - const float *co_next = trans_vert_orig_co_get(tcld, l_next->v); - bool co_prev_ok; - bool co_next_ok; - - /* In the unlikely case that we're next to a zero length edge - - * walk around the to the next. - * - * Since we only need to check if the vertex is in this corner, - * its not important _which_ loop - as long as its not overlapping - * 'sv->co_orig_3d', see: T45096. */ - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - while (UNLIKELY(((co_prev_ok = (len_squared_v3v3(v_proj[1], v_proj[0]) > eps)) == false) && - ((l_prev = l_prev->prev) != l->next))) { - co_prev = trans_vert_orig_co_get(tcld, l_prev->v); - project_plane_normalized_v3_v3v3(v_proj[0], co_prev, v_proj_axis); - } - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - while (UNLIKELY(((co_next_ok = (len_squared_v3v3(v_proj[1], v_proj[2]) > eps)) == false) && - ((l_next = l_next->next) != l->prev))) { - co_next = trans_vert_orig_co_get(tcld, l_next->v); - project_plane_normalized_v3_v3v3(v_proj[2], co_next, v_proj_axis); - } - - if (co_prev_ok && co_next_ok) { - const float dist = dist_signed_squared_to_corner_v3v3v3( - v->co, UNPACK3(v_proj), v_proj_axis); - - loop_weights[j] = (dist >= 0.0f) ? 1.0f : ((dist <= -eps) ? 0.0f : (1.0f + (dist / eps))); - if (UNLIKELY(!isfinite(loop_weights[j]))) { - loop_weights[j] = 0.0f; + if (tc->use_mirror_axis_any) { + int i; + TransData *td; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { + if (td->flag & TD_MIRROR_EDGE_X) { + td->loc[0] = 0.0f; } - } - else { - loop_weights[j] = 0.0f; - } - } - } - - if (tcld->use_merge_group) { - struct LinkNode **cd_loop_groups = merge_data->cd_loop_groups; - if (tcld->merge_group.customdatalayer_map_len && cd_loop_groups) { - if (do_loop_weight) { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge_weights( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j], loop_weights); + if (td->flag & TD_MIRROR_EDGE_Y) { + td->loc[1] = 0.0f; } - } - else { - for (j = 0; j < tcld->merge_group.customdatalayer_map_len; j++) { - BM_vert_loop_groups_data_layer_merge( - bm, cd_loop_groups[j], tcld->merge_group.customdatalayer_map[j]); + if (td->flag & TD_MIRROR_EDGE_Z) { + td->loc[2] = 0.0f; } } } - } - - /* Special handling for multires - * - * Interpolate from every other loop (not ideal) - * However values will only be taken from loops which overlap other mdisps. - */ - const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1); - if (update_loop_mdisps) { - float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num); - BMLoop *l; - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BM_face_calc_center_median(l->f, faces_center[j]); - } - - BM_ITER_ELEM_INDEX (l, &liter, v, BM_LOOPS_OF_VERT, j) { - BMFace *f_copy = BLI_ghash_lookup(tcld->origfaces, l->f); - float f_copy_center[3]; - BMIter liter_other; - BMLoop *l_other; - int j_other; - - BM_face_calc_center_median(f_copy, f_copy_center); - - BM_ITER_ELEM_INDEX (l_other, &liter_other, v, BM_LOOPS_OF_VERT, j_other) { - BM_face_interp_multires_ex(bm, - l_other->f, - f_copy, - faces_center[j_other], - f_copy_center, - tcld->cd_loop_mdisp_offset); - } - } - } -} - -static void mesh_customdatacorrect_apply(TransInfo *t, bool is_final) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (!tc->custom.type.data) { - continue; - } - struct TransCustomDataLayer *tcld = tc->custom.type.data; - const bool use_merge_group = tcld->use_merge_group; - - struct TransCustomDataMergeGroup *merge_data = tcld->merge_group.data; - TransData *tob = tc->data; - for (int i = tc->data_len; i--; tob++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)tob, merge_data, is_final); - - if (use_merge_group) { - merge_data++; - } - } TransDataMirror *td_mirror = tc->data_mirror; - for (int i = tc->data_mirror_len; i--; td_mirror++) { - mesh_customdatacorrect_apply_vert(tcld, (TransDataBasic *)td_mirror, merge_data, is_final); - - if (use_merge_group) { - merge_data++; + for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { + copy_v3_v3(td_mirror->loc, td_mirror->loc_src); + if (td_mirror->flag & TD_MIRROR_X) { + td_mirror->loc[0] *= -1; } - } - } -} - -static void mesh_customdatacorrect_restore(struct TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransCustomDataLayer *tcld = tc->custom.type.data; - if (!tcld) { - continue; - } - - BMesh *bm = tcld->bm; - BMesh *bm_copy = tcld->bm_origfaces; - - GHashIterator gh_iter; - GHASH_ITER (gh_iter, tcld->origfaces) { - BMFace *f = BLI_ghashIterator_getKey(&gh_iter); - BMFace *f_copy = BLI_ghashIterator_getValue(&gh_iter); - BLI_assert(f->len == f_copy->len); - - BMLoop *l_iter, *l_first, *l_copy; - l_iter = l_first = BM_FACE_FIRST_LOOP(f); - l_copy = BM_FACE_FIRST_LOOP(f_copy); - do { - /* TODO: Restore only the elements that transform. */ - BM_elem_attrs_copy(bm_copy, bm, l_copy, l_iter); - l_copy = l_copy->next; - } while ((l_iter = l_iter->next) != l_first); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Recalc Mesh Data - * \{ */ - -static void mesh_apply_to_mirror(TransInfo *t) -{ - FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (tc->use_mirror_axis_any) { - int i; - TransData *td; - for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { - if (td->flag & (TD_MIRROR_EDGE_X | TD_MIRROR_EDGE_Y | TD_MIRROR_EDGE_Z)) { - if (td->flag & TD_MIRROR_EDGE_X) { - td->loc[0] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Y) { - td->loc[1] = 0.0f; - } - if (td->flag & TD_MIRROR_EDGE_Z) { - td->loc[2] = 0.0f; - } - } + if (td_mirror->flag & TD_MIRROR_Y) { + td_mirror->loc[1] *= -1; } - - TransDataMirror *td_mirror = tc->data_mirror; - for (i = 0; i < tc->data_mirror_len; i++, td_mirror++) { - copy_v3_v3(td_mirror->loc, td_mirror->loc_src); - if (td_mirror->flag & TD_MIRROR_X) { - td_mirror->loc[0] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Y) { - td_mirror->loc[1] *= -1; - } - if (td_mirror->flag & TD_MIRROR_Z) { - td_mirror->loc[2] *= -1; - } + if (td_mirror->flag & TD_MIRROR_Z) { + td_mirror->loc[2] *= -1; } } } @@ -1623,24 +1655,28 @@ static void mesh_apply_to_mirror(TransInfo *t) void recalcData_mesh(TransInfo *t) { bool is_canceling = t->state == TRANS_CANCEL; - /* mirror modifier clipping? */ + /* Apply corrections. */ if (!is_canceling) { - /* apply clipping after so we never project past the clip plane T25423. */ applyProject(t); - clipMirrorModifier(t); - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_apply_to_mirror(t); - } + bool do_mirror = !(t->flag & T_NO_MIRROR); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* Apply clipping after so we never project past the clip plane T25423. */ + transform_convert_clip_mirror_modifier_apply(tc); + + if (do_mirror) { + tc_mesh_transdata_mirror_apply(tc); + } - mesh_customdatacorrect_apply(t, false); + tc_mesh_customdatacorrect_apply(tc, false); + } } else { - mesh_customdatacorrect_restore(t); + tc_mesh_customdatacorrect_restore(t); } FOREACH_TRANS_DATA_CONTAINER (t, tc) { - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); EDBM_mesh_normals_update(em); BKE_editmesh_looptri_calc(em); @@ -1660,7 +1696,9 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) if (!is_canceling && ELEM(t->mode, TFM_EDGE_SLIDE, TFM_VERT_SLIDE)) { /* NOTE(joeedh): Handle multi-res re-projection, * done on transform completion since it's really slow. */ - mesh_customdatacorrect_apply(t, true); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc_mesh_customdatacorrect_apply(tc, true); + } } if (use_automerge) { diff --git a/source/blender/editors/transform/transform_convert_mesh_skin.c b/source/blender/editors/transform/transform_convert_mesh_skin.c index 5dbb1947773..7c61da31f72 100644 --- a/source/blender/editors/transform/transform_convert_mesh_skin.c +++ b/source/blender/editors/transform/transform_convert_mesh_skin.c @@ -47,9 +47,9 @@ /** \name Edit Mesh #CD_MVERT_SKIN Transform Creation * \{ */ -static float *mesh_skin_transdata_center(const struct TransIslandData *island_data, - const int island_index, - BMVert *eve) +static float *tc_mesh_skin_transdata_center(const struct TransIslandData *island_data, + const int island_index, + BMVert *eve) { if (island_data->center && island_index != -1) { return island_data->center[island_index]; @@ -57,11 +57,11 @@ static float *mesh_skin_transdata_center(const struct TransIslandData *island_da return eve->co; } -static void mesh_skin_transdata_create(TransDataBasic *td, - BMEditMesh *em, - BMVert *eve, - const struct TransIslandData *island_data, - const int island_index) +static void tc_mesh_skin_transdata_create(TransDataBasic *td, + BMEditMesh *em, + BMVert *eve, + const struct TransIslandData *island_data, + const int island_index) { BLI_assert(BM_elem_flag_test(eve, BM_ELEM_HIDDEN) == 0); MVertSkin *vs = CustomData_bmesh_get(&em->bm->vdata, eve->head.data, CD_MVERT_SKIN); @@ -78,7 +78,7 @@ static void mesh_skin_transdata_create(TransDataBasic *td, td->flag |= TD_SELECTED; } - copy_v3_v3(td->center, mesh_skin_transdata_center(island_data, island_index, eve)); + copy_v3_v3(td->center, tc_mesh_skin_transdata_center(island_data, island_index, eve)); td->extra = eve; } @@ -209,7 +209,7 @@ void createTransMeshSkin(TransInfo *t) } if (mirror_data.vert_map && mirror_data.vert_map[a].index != -1) { - mesh_skin_transdata_create( + tc_mesh_skin_transdata_create( (TransDataBasic *)td_mirror, em, eve, &island_data, island_index); int elem_index = mirror_data.vert_map[a].index; @@ -221,7 +221,7 @@ void createTransMeshSkin(TransInfo *t) td_mirror++; } else if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); + tc_mesh_skin_transdata_create((TransDataBasic *)td, em, eve, &island_data, island_index); if (t->around == V3D_AROUND_LOCAL_ORIGINS) { createSpaceNormal(td->axismtx, eve->no); @@ -275,7 +275,7 @@ void createTransMeshSkin(TransInfo *t) /** \name Recalc Mesh Data * \{ */ -static void mesh_skin_apply_to_mirror(TransInfo *t) +static void tc_mesh_skin_apply_to_mirror(TransInfo *t) { FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->use_mirror_axis_any) { @@ -292,13 +292,13 @@ void recalcData_mesh_skin(TransInfo *t) bool is_canceling = t->state == TRANS_CANCEL; /* mirror modifier clipping? */ if (!is_canceling) { - if ((t->flag & T_NO_MIRROR) == 0 && (t->options & CTX_NO_MIRROR) == 0) { - mesh_skin_apply_to_mirror(t); + if (!(t->flag & T_NO_MIRROR)) { + tc_mesh_skin_apply_to_mirror(t); } } FOREACH_TRANS_DATA_CONTAINER (t, tc) { - DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); EDBM_mesh_normals_update(em); BKE_editmesh_looptri_calc(em); diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index a5f90e9ac5f..d91a2a8be4b 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -475,7 +475,7 @@ void recalcData_uv(TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (tc->data_len) { - DEG_id_tag_update(tc->obedit->data, 0); + DEG_id_tag_update(tc->obedit->data, ID_RECALC_GEOMETRY); } } } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 30418471d6d..34be89e5ed9 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -491,7 +491,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c } } - SEQ_sort(t->scene); + SEQ_sort(seqbasep); } else { /* Canceled, need to update the strips display */ @@ -707,7 +707,7 @@ static void flushTransSeq(TransInfo *t) /* originally TFM_TIME_EXTEND, transform changes */ if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { - /* Special annoying case here, need to calc metas with TFM_TIME_EXTEND only */ + /* Special annoying case here, need to calc meta-strips with TFM_TIME_EXTEND only */ /* calc all meta's then effects T27953. */ for (seq = seqbasep->first; seq; seq = seq->next) { diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c0c4b22da98..71c91221fbb 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -414,8 +414,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve int orient_type_set = -1; int orient_type_matrix_set = -1; - bool use_orient_axis = false; - if ((t->spacetype == SPACE_VIEW3D) && (t->region->regiontype == RGN_TYPE_WINDOW)) { TransformOrientationSlot *orient_slot = &t->scene->orientation_slots[SCE_ORIENT_DEFAULT]; orient_type_scene = orient_slot->type; @@ -435,7 +433,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis"))) { t->orient_axis = RNA_property_enum_get(op->ptr, prop); - use_orient_axis = true; } if (op && (prop = RNA_struct_find_property(op->ptr, "orient_axis_ortho"))) { @@ -457,28 +454,23 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (orient_type_set != -1) { orient_type_default = orient_type_set; + t->is_orient_set = true; } else if (orient_type_matrix_set != -1) { orient_type_default = orient_type_set = orient_type_matrix_set; + t->is_orient_set = true; } else if (t->con.mode & CON_APPLY) { orient_type_default = orient_type_set = orient_type_scene; } else { + orient_type_default = orient_type_scene; if (orient_type_scene == V3D_ORIENT_GLOBAL) { orient_type_set = V3D_ORIENT_LOCAL; } else { orient_type_set = V3D_ORIENT_GLOBAL; } - - if ((t->flag & T_MODAL) && (use_orient_axis || transform_mode_is_changeable(t->mode)) && - (t->mode != TFM_ALIGN)) { - orient_type_default = V3D_ORIENT_VIEW; - } - else { - orient_type_default = orient_type_scene; - } } BLI_assert(!ELEM(-1, orient_type_default, orient_type_set)); @@ -487,9 +479,9 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve orient_type_set = V3D_ORIENT_CUSTOM_MATRIX; } - orient_types[0] = (short)orient_type_default; - orient_types[1] = (short)orient_type_scene; - orient_types[2] = (short)orient_type_set; + orient_types[O_DEFAULT] = (short)orient_type_default; + orient_types[O_SCENE] = (short)orient_type_scene; + orient_types[O_SET] = (short)orient_type_set; for (int i = 0; i < 3; i++) { /* For efficiency, avoid calculating the same orientation twice. */ @@ -506,9 +498,6 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } - /* Set orient_curr to -1 in order to force the update in - * `transform_orientations_current_set`. */ - t->orient_curr = -1; transform_orientations_current_set(t, (t->con.mode & CON_APPLY) ? 2 : 0); } @@ -538,7 +527,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } else { /* Avoid mirroring for unsupported contexts. */ - t->options |= CTX_NO_MIRROR; + t->flag |= T_NO_MIRROR; } /* setting PET flag only if property exist in operator. Otherwise, assume it's not supported */ diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index d14f693da9c..d61e7bb867c 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -45,6 +45,7 @@ #include "transform.h" #include "transform_convert.h" +#include "transform_orientations.h" #include "transform_snap.h" /* Own include. */ @@ -1263,7 +1264,7 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) if (t->data_type == TC_MESH_VERTS) { /* Init Custom Data correction. * Ideally this should be called when creating the TransData. */ - mesh_customdatacorrect_init(t); + transform_convert_mesh_customdatacorrect_init(t); } /* TODO(germano): Some of these operations change the `t->mode`. @@ -1271,4 +1272,38 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) * BLI_assert(t->mode == mode); */ } +/** + * When in modal and not set, initializes a default orientation for the mode. + */ +void transform_mode_default_modal_orientation_set(TransInfo *t, int type) +{ + /* Currently only these types are supported. */ + BLI_assert(ELEM(type, V3D_ORIENT_GLOBAL, V3D_ORIENT_VIEW)); + + if (t->is_orient_set) { + return; + } + + if (!(t->flag & T_MODAL)) { + return; + } + + if (t->orient[O_DEFAULT].type == type) { + return; + } + + RegionView3D *rv3d = NULL; + if ((type == V3D_ORIENT_VIEW) && (t->spacetype == SPACE_VIEW3D) && t->region && + (t->region->regiontype == RGN_TYPE_WINDOW)) { + rv3d = t->region->regiondata; + } + + t->orient[O_DEFAULT].type = ED_transform_calc_orientation_from_type_ex( + NULL, t->orient[O_DEFAULT].matrix, NULL, rv3d, NULL, NULL, type, 0); + + if (t->orient_curr == O_DEFAULT) { + /* Update Orientation. */ + transform_orientations_current_set(t, O_DEFAULT); + } +} /** \} */ diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index 6d7a0b528ae..106dc68c9ee 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -61,6 +61,7 @@ short getAnimEdit_SnapMode(TransInfo *t); void doAnimEdit_SnapFrame( TransInfo *t, TransData *td, TransData2D *td2d, struct AnimData *adt, short autosnap); void transform_mode_init(TransInfo *t, struct wmOperator *op, const int mode); +void transform_mode_default_modal_orientation_set(TransInfo *t, int type); /* transform_mode_align.c */ void initAlign(TransInfo *t); diff --git a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c index ee63bf4be6f..68416c780ef 100644 --- a/source/blender/editors/transform/transform_mode_curveshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_curveshrinkfatten.c @@ -107,7 +107,6 @@ void initCurveShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c index c78115561b2..b7b3de69731 100644 --- a/source/blender/editors/transform/transform_mode_edge_rotate_normal.c +++ b/source/blender/editors/transform/transform_mode_edge_rotate_normal.c @@ -148,5 +148,7 @@ void initNormalRotation(TransInfo *t) storeCustomLNorValue(tc, bm); } + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c index e67c6c03481..7c496d271ef 100644 --- a/source/blender/editors/transform/transform_mode_gpopacity.c +++ b/source/blender/editors/transform/transform_mode_gpopacity.c @@ -117,7 +117,6 @@ void initGPOpacity(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c index 95e3d89d2b7..608a49f38b1 100644 --- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c @@ -119,7 +119,6 @@ void initGPShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c index 3019984d70b..857ee37f0ad 100644 --- a/source/blender/editors/transform/transform_mode_maskshrinkfatten.c +++ b/source/blender/editors/transform/transform_mode_maskshrinkfatten.c @@ -133,7 +133,6 @@ void initMaskShrinkFatten(TransInfo *t) t->num.unit_sys = t->scene->unit.system; t->num.unit_type[0] = B_UNIT_NONE; - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; #endif diff --git a/source/blender/editors/transform/transform_mode_mirror.c b/source/blender/editors/transform/transform_mode_mirror.c index 9891af8b9a4..f225f1a7c06 100644 --- a/source/blender/editors/transform/transform_mode_mirror.c +++ b/source/blender/editors/transform/transform_mode_mirror.c @@ -235,8 +235,5 @@ void initMirror(TransInfo *t) initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; - if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; - } } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 4d0bb7fbe9c..0d7d0be3c0e 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -176,7 +176,6 @@ void initResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; @@ -194,5 +193,7 @@ void initResize(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; t->num.unit_type[1] = B_UNIT_NONE; t->num.unit_type[2] = B_UNIT_NONE; + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_GLOBAL); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_rotate.c b/source/blender/editors/transform/transform_mode_rotate.c index 8d8594d5775..0fdbfb25989 100644 --- a/source/blender/editors/transform/transform_mode_rotate.c +++ b/source/blender/editors/transform/transform_mode_rotate.c @@ -249,5 +249,7 @@ void initRotation(TransInfo *t) if (t->flag & T_2D_EDIT) { t->flag |= T_NO_CONSTRAINT; } + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_shear.c b/source/blender/editors/transform/transform_mode_shear.c index a41c49710b9..23ee55bf6c5 100644 --- a/source/blender/editors/transform/transform_mode_shear.c +++ b/source/blender/editors/transform/transform_mode_shear.c @@ -232,5 +232,7 @@ void initShear(TransInfo *t) t->num.unit_type[0] = B_UNIT_NONE; /* Don't think we have any unit here? */ t->flag |= T_NO_CONSTRAINT; + + transform_mode_default_modal_orientation_set(t, V3D_ORIENT_VIEW); } /** \} */ diff --git a/source/blender/editors/transform/transform_mode_skin_resize.c b/source/blender/editors/transform/transform_mode_skin_resize.c index 8beacb844b9..77e57484bef 100644 --- a/source/blender/editors/transform/transform_mode_skin_resize.c +++ b/source/blender/editors/transform/transform_mode_skin_resize.c @@ -111,7 +111,6 @@ void initSkinResize(TransInfo *t) t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; if ((t->flag & T_EDIT) == 0) { - t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; t->num.val_flag[1] |= NUM_NO_ZERO; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 41fc6ee0aaf..175b7b52a1a 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -470,5 +470,8 @@ void initTranslation(TransInfo *t) t->num.unit_type[1] = B_UNIT_NONE; t->num.unit_type[2] = B_UNIT_NONE; } + + transform_mode_default_modal_orientation_set( + t, (t->options & CTX_CAMERA) ? V3D_ORIENT_VIEW : V3D_ORIENT_GLOBAL); } /** \} */ diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 5a5f478fc2b..d97bcba161f 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -445,7 +445,7 @@ int BIF_countTransformOrientation(const bContext *C) return BLI_listbase_count(transform_orientations); } -void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name) +void applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char r_name[64]) { if (r_name) { BLI_strncpy(r_name, ts->name, MAX_NAME); @@ -671,10 +671,6 @@ const char *transform_orientations_spacename_get(TransInfo *t, const short orien void transform_orientations_current_set(TransInfo *t, const short orient_index) { - if (t->orient_curr == orient_index) { - return; - } - const short orientation = t->orient[orient_index].type; const char *spacename = transform_orientations_spacename_get(t, orientation); diff --git a/source/blender/editors/util/ed_transverts.c b/source/blender/editors/util/ed_transverts.c index ad45686dc75..c1d0dcdb095 100644 --- a/source/blender/editors/util/ed_transverts.c +++ b/source/blender/editors/util/ed_transverts.c @@ -54,7 +54,7 @@ void ED_transverts_update_obedit(TransVertStore *tvs, Object *obedit) const int mode = tvs->mode; BLI_assert(ED_transverts_check_obedit(obedit) == true); - DEG_id_tag_update(obedit->data, 0); + DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY); if (obedit->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(obedit); diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 14a6d751bb1..4e8cf1e92e6 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -88,11 +88,11 @@ int ED_select_similar_compare_float(const float delta, const float thresh, const { switch (compare) { case SIM_CMP_EQ: - return (fabsf(delta) < thresh + FLT_EPSILON); + return (fabsf(delta) <= thresh); case SIM_CMP_GT: - return ((delta + thresh) > -FLT_EPSILON); + return ((delta + thresh) >= 0.0); case SIM_CMP_LT: - return ((delta - thresh) < FLT_EPSILON); + return ((delta - thresh) <= 0.0); default: BLI_assert_unreachable(); return 0; |