diff options
Diffstat (limited to 'source/blender/editors/transform')
-rw-r--r-- | source/blender/editors/transform/CMakeLists.txt | 8 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.c | 2101 | ||||
-rw-r--r-- | source/blender/editors/transform/transform.h | 206 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_constraints.c | 191 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_conversions.c | 3458 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_generics.c | 1134 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_input.c | 39 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_manipulator.c | 1981 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_manipulator_2d.c | 382 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_manipulator_3d.c | 1732 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_ops.c | 96 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_orientations.c | 121 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_snap.c | 493 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_snap_object.c | 2360 |
14 files changed, 7756 insertions, 6546 deletions
diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index b7de49d8158..3e132192875 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../../ikplugin ../../makesdna ../../makesrna + ../../render/extern/include ../../windowmanager ../../depsgraph ../../../../intern/guardedalloc @@ -45,7 +46,8 @@ set(SRC transform_conversions.c transform_generics.c transform_input.c - transform_manipulator.c + transform_manipulator_2d.c + transform_manipulator_3d.c transform_ops.c transform_orientations.c transform_snap.c @@ -58,10 +60,6 @@ if(WITH_INTERNATIONAL) add_definitions(-DWITH_INTERNATIONAL) endif() -if(WITH_LEGACY_DEPSGRAPH) - add_definitions(-DWITH_LEGACY_DEPSGRAPH) -endif() - add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_transform "${SRC}" "${INC}" "${INC_SYS}") diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index ac9b2f2900c..b4f7e9256a3 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -43,6 +43,7 @@ #include "DNA_mask_types.h" #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" /* PET modes */ +#include "DNA_workspace_types.h" #include "BLI_alloca.h" #include "BLI_utildefines.h" @@ -61,12 +62,19 @@ #include "BKE_constraint.h" #include "BKE_particle.h" #include "BKE_unit.h" +#include "BKE_scene.h" #include "BKE_mask.h" #include "BKE_report.h" +#include "BKE_workspace.h" + +#include "DEG_depsgraph.h" -#include "BIF_gl.h" #include "BIF_glutil.h" +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_matrix.h" + #include "ED_image.h" #include "ED_keyframing.h" #include "ED_screen.h" @@ -103,7 +111,7 @@ static void drawEdgeSlide(TransInfo *t); static void drawVertSlide(TransInfo *t); static void postInputRotation(TransInfo *t, float values[3]); -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around); +static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around); static void initSnapSpatial(TransInfo *t, float r_snap[3]); @@ -204,7 +212,8 @@ static bool transdata_check_local_center(TransInfo *t, short around) { return ((around == V3D_AROUND_LOCAL_ORIGINS) && ( (t->flag & (T_OBJECT | T_POSE)) || - (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || + /* implicit: (t->flag & T_EDIT) */ + (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || (t->spacetype == SPACE_IPO) || (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE))) ); @@ -213,7 +222,7 @@ static bool transdata_check_local_center(TransInfo *t, short around) bool transdata_check_local_islands(TransInfo *t, short around) { return ((around == V3D_AROUND_LOCAL_ORIGINS) && ( - (t->obedit && ELEM(t->obedit->type, OB_MESH)))); + (ELEM(t->obedit_type, OB_MESH)))); } /* ************************** SPACE DEPENDANT CODE **************************** */ @@ -238,6 +247,7 @@ void setTransformViewMatrices(TransInfo *t) } calculateCenter2D(t); + calculateCenterLocal(t, t->center_global); } void setTransformViewAspect(TransInfo *t, float r_aspect[3]) @@ -612,8 +622,12 @@ static void viewRedrawForce(const bContext *C, TransInfo *t) else { // XXX how to deal with lock? SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; - if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data); - else ED_area_tag_redraw(t->sa); + if (sima->lock) { + WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data); + } + else { + ED_area_tag_redraw(t->sa); + } } } else if (t->spacetype == SPACE_CLIP) { @@ -1017,7 +1031,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } else { - if (t->obedit && t->obedit->type == OB_MESH) { + if (t->obedit_type == OB_MESH) { if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) { restoreTransObjects(t); resetTransModal(t); @@ -1027,11 +1041,13 @@ int transformEvent(TransInfo *t, const wmEvent *event) initEdgeSlide(t); /* if that fails, do vertex slide */ if (t->state == TRANS_CANCEL) { + resetTransModal(t); t->state = TRANS_STARTING; initVertSlide(t); } /* vert slide can fail on unconnected vertices (rare but possible) */ if (t->state == TRANS_CANCEL) { + resetTransModal(t); t->mode = TFM_TRANSLATION; t->state = TRANS_STARTING; restoreTransObjects(t); @@ -1547,6 +1563,8 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa TransInfo *t = MEM_callocN(sizeof(TransInfo), "TransInfo data"); bool success; + t->context = C; + t->state = TRANS_RUNNING; /* avoid calculating PET */ @@ -1563,7 +1581,7 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa t->around = centerMode; // override userdefined mode - if (t->total == 0) { + if (t->data_len_all == 0) { success = false; } else { @@ -1598,8 +1616,16 @@ typedef enum { LEFT, RIGHT } ArrowDirection; + +#define POS_INDEX 0 +/* NOTE: this --^ is a bit hackish, but simplifies Gwn_VertFormat usage among functions + * private to this file - merwin + */ + static void drawArrow(ArrowDirection d, short offset, short length, short size) { + immBegin(GWN_PRIM_LINES, 6); + switch (d) { case LEFT: offset = -offset; @@ -1607,14 +1633,12 @@ static void drawArrow(ArrowDirection d, short offset, short length, short size) size = -size; ATTR_FALLTHROUGH; case RIGHT: - glBegin(GL_LINES); - glVertex2s(offset, 0); - glVertex2s(offset + length, 0); - glVertex2s(offset + length, 0); - glVertex2s(offset + length - size, -size); - glVertex2s(offset + length, 0); - glVertex2s(offset + length - size, size); - glEnd(); + immVertex2f(POS_INDEX, offset, 0); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length - size, -size); + immVertex2f(POS_INDEX, offset + length, 0); + immVertex2f(POS_INDEX, offset + length - size, size); break; case DOWN: @@ -1623,45 +1647,45 @@ static void drawArrow(ArrowDirection d, short offset, short length, short size) size = -size; ATTR_FALLTHROUGH; case UP: - glBegin(GL_LINES); - glVertex2s(0, offset); - glVertex2s(0, offset + length); - glVertex2s(0, offset + length); - glVertex2s(-size, offset + length - size); - glVertex2s(0, offset + length); - glVertex2s(size, offset + length - size); - glEnd(); + immVertex2f(POS_INDEX, 0, offset); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, -size, offset + length - size); + immVertex2f(POS_INDEX, 0, offset + length); + immVertex2f(POS_INDEX, size, offset + length - size); break; } + + immEnd(); } static void drawArrowHead(ArrowDirection d, short size) { + immBegin(GWN_PRIM_LINES, 4); + switch (d) { case LEFT: size = -size; ATTR_FALLTHROUGH; case RIGHT: - glBegin(GL_LINES); - glVertex2s(0, 0); - glVertex2s(-size, -size); - glVertex2s(0, 0); - glVertex2s(-size, size); - glEnd(); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, -size); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, size); break; case DOWN: size = -size; ATTR_FALLTHROUGH; case UP: - glBegin(GL_LINES); - glVertex2s(0, 0); - glVertex2s(-size, -size); - glVertex2s(0, 0); - glVertex2s(size, -size); - glEnd(); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, -size, -size); + immVertex2f(POS_INDEX, 0, 0); + immVertex2f(POS_INDEX, size, -size); break; } + + immEnd(); } static void drawArc(float size, float angle_start, float angle_end, int segments) @@ -1670,14 +1694,14 @@ static void drawArc(float size, float angle_start, float angle_end, int segments float angle; int a; - glBegin(GL_LINE_STRIP); + immBegin(GWN_PRIM_LINE_STRIP, segments + 1); for (angle = angle_start, a = 0; a < segments; angle += delta, a++) { - glVertex2f(cosf(angle) * size, sinf(angle) * size); + immVertex2f(POS_INDEX, cosf(angle) * size, sinf(angle) * size); } - glVertex2f(cosf(angle_end) * size, sinf(angle_end) * size); + immVertex2f(POS_INDEX, cosf(angle_end) * size, sinf(angle_end) * size); - glEnd(); + immEnd(); } static int helpline_poll(bContext *C) @@ -1693,88 +1717,122 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) { TransInfo *t = (TransInfo *)customdata; - if (t->helpline != HLP_NONE && !(t->flag & T_USES_MANIPULATOR)) { + if (t->helpline != HLP_NONE) { float cent[2]; - int mval[2]; - - mval[0] = x; - mval[1] = y; + float mval[3] = { + x, + y, + 0.0f, + }; + float tmval[2] = { + (float)t->mval[0], + (float)t->mval[1], + }; projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); - glPushMatrix(); + /* Offset the values for the area region. */ + const float offset[2] = { + t->ar->winrct.xmin, + t->ar->winrct.ymin, + }; + + for (int i = 0; i < 2; i++) { + cent[i] += offset[i]; + tmval[i] += offset[i]; + } + + gpuPushMatrix(); + + /* Dashed lines first. */ + if (ELEM(t->helpline, HLP_SPRING, HLP_ANGLE)) { + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + UNUSED_VARS_NDEBUG(shdr_pos); /* silence warning */ + BLI_assert(shdr_pos == POS_INDEX); + + glLineWidth(1.0f); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 0); /* "simple" mode */ + immUniformThemeColor(TH_VIEW_OVERLAY); + immUniform1f("dash_width", 6.0f); + immUniform1f("dash_factor", 0.5f); + + immBegin(GWN_PRIM_LINES, 2); + immVertex2fv(POS_INDEX, cent); + immVertex2f(POS_INDEX, tmval[0], tmval[1]); + immEnd(); + + immUnbindProgram(); + } + + /* And now, solid lines. */ + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + UNUSED_VARS_NDEBUG(pos); /* silence warning */ + BLI_assert(pos == POS_INDEX); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); switch (t->helpline) { case HLP_SPRING: - UI_ThemeColor(TH_VIEW_OVERLAY); + immUniformThemeColor(TH_VIEW_OVERLAY); - setlinestyle(3); - glLineWidth(1); - glBegin(GL_LINES); - glVertex2iv(t->mval); - glVertex2fv(cent); - glEnd(); + gpuTranslate3fv(mval); + gpuRotateAxis(-RAD2DEGF(atan2f(cent[0] - tmval[0], cent[1] - tmval[1])), 'Z'); - glTranslate2iv(mval); - glRotatef(-RAD2DEGF(atan2f(cent[0] - t->mval[0], cent[1] - t->mval[1])), 0, 0, 1); - - setlinestyle(0); - glLineWidth(3.0); + glLineWidth(3.0f); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); break; case HLP_HARROW: - UI_ThemeColor(TH_VIEW_OVERLAY); - - glTranslate2iv(mval); + immUniformThemeColor(TH_VIEW_OVERLAY); + gpuTranslate3fv(mval); - glLineWidth(3.0); + glLineWidth(3.0f); drawArrow(RIGHT, 5, 10, 5); drawArrow(LEFT, 5, 10, 5); break; case HLP_VARROW: - UI_ThemeColor(TH_VIEW_OVERLAY); + immUniformThemeColor(TH_VIEW_OVERLAY); - glTranslate2iv(mval); + gpuTranslate3fv(mval); - glLineWidth(3.0); + glLineWidth(3.0f); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); break; case HLP_ANGLE: { - float dx = t->mval[0] - cent[0], dy = t->mval[1] - cent[1]; + float dx = tmval[0] - cent[0], dy = tmval[1] - cent[1]; float angle = atan2f(dy, dx); float dist = hypotf(dx, dy); float delta_angle = min_ff(15.0f / dist, (float)M_PI / 4.0f); float spacing_angle = min_ff(5.0f / dist, (float)M_PI / 12.0f); - UI_ThemeColor(TH_VIEW_OVERLAY); - setlinestyle(3); - glLineWidth(1); - glBegin(GL_LINES); - glVertex2iv(t->mval); - glVertex2fv(cent); - glEnd(); + immUniformThemeColor(TH_VIEW_OVERLAY); - glTranslatef(cent[0] - t->mval[0] + mval[0], cent[1] - t->mval[1] + mval[1], 0); + gpuTranslate3f(cent[0] - tmval[0] + mval[0], cent[1] - tmval[1] + mval[1], 0); - setlinestyle(0); - glLineWidth(3.0); + glLineWidth(3.0f); drawArc(dist, angle - delta_angle, angle - spacing_angle, 10); drawArc(dist, angle + spacing_angle, angle + delta_angle, 10); - glPushMatrix(); + gpuPushMatrix(); - glTranslatef(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); - glRotatef(RAD2DEGF(angle - delta_angle), 0, 0, 1); + gpuTranslate3f(cosf(angle - delta_angle) * dist, sinf(angle - delta_angle) * dist, 0); + gpuRotateAxis(RAD2DEGF(angle - delta_angle), 'Z'); drawArrowHead(DOWN, 5); - glPopMatrix(); + gpuPopMatrix(); - glTranslatef(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); - glRotatef(RAD2DEGF(angle + delta_angle), 0, 0, 1); + gpuTranslate3f(cosf(angle + delta_angle) * dist, sinf(angle + delta_angle) * dist, 0); + gpuRotateAxis(RAD2DEGF(angle + delta_angle), 'Z'); drawArrowHead(UP, 5); break; @@ -1784,18 +1842,18 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) unsigned char col[3], col2[3]; UI_GetThemeColor3ubv(TH_GRID, col); - glTranslate2iv(mval); + gpuTranslate3fv(mval); - glLineWidth(3.0); + glLineWidth(3.0f); UI_make_axis_color(col, col2, 'X'); - glColor3ubv((GLubyte *)col2); + immUniformColor3ubv((GLubyte *)col2); drawArrow(RIGHT, 5, 10, 5); drawArrow(LEFT, 5, 10, 5); UI_make_axis_color(col, col2, 'Y'); - glColor3ubv((GLubyte *)col2); + immUniformColor3ubv((GLubyte *)col2); drawArrow(UP, 5, 10, 5); drawArrow(DOWN, 5, 10, 5); @@ -1803,7 +1861,8 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) } } - glPopMatrix(); + immUnbindProgram(); + gpuPopMatrix(); } } @@ -1811,7 +1870,7 @@ static void drawTransformView(const struct bContext *C, ARegion *UNUSED(ar), voi { TransInfo *t = arg; - glLineWidth(1.0); + glLineWidth(1.0f); drawConstraint(t); drawPropCircle(C, t); @@ -1832,7 +1891,8 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) ED_region_visible_rect(ar, &rect); - BLF_width_and_height_default(printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + const int font_id = BLF_default(); + BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; yco = (rect.ymax - U.widget_unit); @@ -1840,7 +1900,9 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) /* warning text (to clarify meaning of overlays) * - original color was red to match the icon, but that clashes badly with a less nasty border */ - UI_ThemeColorShade(TH_TEXT_HI, -50); + unsigned char color[3]; + UI_GetThemeColorShade3ubv(TH_TEXT_HI, -50, color); + BLF_color3ubv(font_id, color); #ifdef WITH_INTERNATIONAL BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); #else @@ -1848,7 +1910,7 @@ static void drawAutoKeyWarning(TransInfo *UNUSED(t), ARegion *ar) #endif /* autokey recording icon... */ - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); xco -= U.widget_unit; @@ -1863,7 +1925,8 @@ static void drawTransformPixel(const struct bContext *UNUSED(C), ARegion *ar, vo { TransInfo *t = arg; Scene *scene = t->scene; - Object *ob = OBACT; + ViewLayer *view_layer = t->view_layer; + Object *ob = OBACT(view_layer); /* draw autokeyframing hint in the corner * - only draw if enabled (advanced users may be distracted/annoyed), @@ -1938,7 +2001,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ts->proportional_fcurve = proportional; else if (t->spacetype == SPACE_ACTION) ts->proportional_action = proportional; - else if (t->obedit) + else if (t->obedit_type != -1) ts->proportional = proportional; else if (t->options & CTX_MASK) ts->proportional_mask = (proportional != PROP_EDIT_OFF); @@ -1970,9 +2033,10 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) if ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) && !RNA_property_is_set(op->ptr, prop)) { - View3D *v3d = t->view; - - v3d->twmode = t->current_orientation; + t->scene->orientation_type = t->current_orientation; + BLI_assert(((t->scene->orientation_index_custom == -1) && (t->custom_orientation == NULL)) || + (BKE_scene_transform_orientation_get_index( + t->scene, t->custom_orientation) == t->scene->orientation_index_custom)); } } } @@ -1992,15 +2056,19 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis"))) { - /* constraint orientation can be global, event if user selects something else - * so use the orientation in the constraint if set - * */ - if (t->con.mode & CON_APPLY) { - RNA_enum_set(op->ptr, "constraint_orientation", t->con.orientation); - } - else { - RNA_enum_set(op->ptr, "constraint_orientation", t->current_orientation); + /* constraint orientation can be global, even if user selects something else + * so use the orientation in the constraint if set */ + short orientation = (t->con.mode & CON_APPLY) ? t->con.orientation : t->current_orientation; + + if (orientation == V3D_MANIP_CUSTOM) { + const int orientation_index_custom = BKE_scene_transform_orientation_get_index( + t->scene, t->custom_orientation); + + /* Maybe we need a t->con.custom_orientation? Seems like it would always match t->custom_orientation. */ + orientation = V3D_MANIP_CUSTOM + orientation_index_custom; + BLI_assert(orientation >= V3D_MANIP_CUSTOM); } + RNA_enum_set(op->ptr, "constraint_orientation", orientation); if (t->con.mode & CON_APPLY) { if (t->con.mode & CON_AXIS0) { @@ -2050,6 +2118,12 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->state = TRANS_STARTING; + if ((prop = RNA_struct_find_property(op->ptr, "cursor_transform")) && RNA_property_is_set(op->ptr, prop)) { + if (RNA_property_boolean_get(op->ptr, prop)) { + options |= CTX_CURSOR; + } + } + if ((prop = RNA_struct_find_property(op->ptr, "texture_space")) && RNA_property_is_set(op->ptr, prop)) { if (RNA_property_boolean_get(op->ptr, prop)) { options |= CTX_TEXTURE; @@ -2113,7 +2187,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve createTransData(C, t); // make TransData structs from selection - if (t->total == 0) { + if (t->data_len_all == 0) { postTrans(C, t); return 0; } @@ -2158,6 +2232,63 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve calculatePropRatio(t); calculateCenter(t); + /* Overwrite initial values if operator supplied a non-null vector. + * + * Run before init functions so 'values_modal_offset' can be applied on mouse input. + */ + BLI_assert(is_zero_v4(t->values_modal_offset)); + if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) { + float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */ + + if (RNA_property_array_check(prop)) { + RNA_float_get_array(op->ptr, "value", values); + } + else { + values[0] = RNA_float_get(op->ptr, "value"); + } + + copy_v4_v4(t->values, values); + + if (t->flag & T_MODAL) { + copy_v4_v4(t->values_modal_offset, values); + t->redraw = TREDRAW_HARD; + } + else { + copy_v4_v4(t->auto_values, values); + t->flag |= T_AUTOVALUES; + } + } + + /* Transformation axis from operator */ + if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, t->axis); + normalize_v3(t->axis); + copy_v3_v3(t->axis_orig, t->axis); + } + + /* Constraint init from operator */ + if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) { + int constraint_axis[3]; + + RNA_property_boolean_get_array(op->ptr, prop, constraint_axis); + + if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) { + t->con.mode |= CON_APPLY; + + if (constraint_axis[0]) { + t->con.mode |= CON_AXIS0; + } + if (constraint_axis[1]) { + t->con.mode |= CON_AXIS1; + } + if (constraint_axis[2]) { + t->con.mode |= CON_AXIS2; + } + + setUserConstraint(t, t->current_orientation, t->con.mode, "%s"); + } + } + if (event) { /* Initialize accurate transform to settings requested by keymap. */ bool use_accurate = false; @@ -2217,7 +2348,9 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve break; case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */ - bArmature *arm = t->poseobj->data; + /* Note: we have to pick one, use the active object. */ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t); + bArmature *arm = tc->poseobj->data; if (arm->drawtype == ARM_ENVELOPE) { initBoneEnvelope(t); t->mode = TFM_BONE_ENVELOPE_DIST; @@ -2304,55 +2437,6 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve return 0; } - /* Transformation axis from operator */ - if ((prop = RNA_struct_find_property(op->ptr, "axis")) && RNA_property_is_set(op->ptr, prop)) { - RNA_property_float_get_array(op->ptr, prop, t->axis); - normalize_v3(t->axis); - copy_v3_v3(t->axis_orig, t->axis); - } - - /* Constraint init from operator */ - if ((prop = RNA_struct_find_property(op->ptr, "constraint_axis")) && RNA_property_is_set(op->ptr, prop)) { - int constraint_axis[3]; - - RNA_property_boolean_get_array(op->ptr, prop, constraint_axis); - - if (constraint_axis[0] || constraint_axis[1] || constraint_axis[2]) { - t->con.mode |= CON_APPLY; - - if (constraint_axis[0]) { - t->con.mode |= CON_AXIS0; - } - if (constraint_axis[1]) { - t->con.mode |= CON_AXIS1; - } - if (constraint_axis[2]) { - t->con.mode |= CON_AXIS2; - } - - setUserConstraint(t, t->current_orientation, t->con.mode, "%s"); - } - } - - /* overwrite initial values if operator supplied a non-null vector - * - * keep last so we can apply the constraints space. - */ - if ((prop = RNA_struct_find_property(op->ptr, "value")) && RNA_property_is_set(op->ptr, prop)) { - float values[4] = {0}; /* in case value isn't length 4, avoid uninitialized memory */ - - if (RNA_property_array_check(prop)) { - RNA_float_get_array(op->ptr, "value", values); - } - else { - values[0] = RNA_float_get(op->ptr, "value"); - } - - copy_v4_v4(t->values, values); - copy_v4_v4(t->auto_values, values); - t->flag |= T_AUTOVALUES; - } - t->context = NULL; return 1; @@ -2615,7 +2699,7 @@ static void constraintTransLim(TransInfo *t, TransData *td) } /* get constraint targets if needed */ - BKE_constraint_targets_for_solving_get(con, &cob, &targets, ctime); + BKE_constraint_targets_for_solving_get(t->depsgraph, con, &cob, &targets, ctime); /* do constraint */ cti->evaluate_constraint(con, &cob, &targets); @@ -2822,6 +2906,7 @@ static void constraintSizeLim(TransInfo *t, TransData *td) * \{ */ struct BendCustomData { + /* All values are in global space. */ float warp_sta[3]; float warp_end[3]; @@ -2862,23 +2947,19 @@ static void initBend(TransInfo *t) //copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view)); if ((t->flag & T_OVERRIDE_CENTER) == 0) { - calculateCenterCursor(t, t->center); + calculateCenterCursor(t, t->center_global); } - calculateCenterGlobal(t, t->center, t->center_global); + calculateCenterLocal(t, t->center_global); t->val = 0.0f; data = MEM_callocN(sizeof(*data), __func__); - curs = ED_view3d_cursor3d_get(t->scene, t->view); + curs = ED_view3d_cursor3d_get(t->scene, t->view)->location; copy_v3_v3(data->warp_sta, curs); ED_view3d_win_to_3d(t->sa->spacedata.first, t->ar, curs, mval_fl, data->warp_end); copy_v3_v3(data->warp_nor, t->viewinv[2]); - if (t->flag & T_EDIT) { - sub_v3_v3(data->warp_sta, t->obedit->obmat[3]); - sub_v3_v3(data->warp_end, t->obedit->obmat[3]); - } normalize_v3(data->warp_nor); /* tangent */ @@ -2905,10 +2986,9 @@ static eRedrawFlag handleEventBend(TransInfo *UNUSED(t), const wmEvent *event) static void Bend(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float vec[3]; - float pivot[3]; - float warp_end_radius[3]; + float pivot_global[3]; + float warp_end_radius_global[3]; int i; char str[UI_MAX_DRAW_STR]; const struct BendCustomData *data = t->custom.mode.data; @@ -2927,7 +3007,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) #else /* hrmf, snapping radius is using 'angle' steps, need to convert to something else * this isnt essential but nicer to give reasonable snapping values for radius */ - if (t->tsnap.mode == SCE_SNAP_MODE_INCREMENT) { + if (t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) { const float radius_snap = 0.1f; const float snap_hack = (t->snap[1] * data->warp_init_dist) / radius_snap; values.scale *= snap_hack; @@ -2963,64 +3043,87 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) values.scale *= data->warp_init_dist; /* calc 'data->warp_end' from 'data->warp_end_init' */ - copy_v3_v3(warp_end_radius, data->warp_end); - dist_ensure_v3_v3fl(warp_end_radius, data->warp_sta, values.scale); + copy_v3_v3(warp_end_radius_global, data->warp_end); + dist_ensure_v3_v3fl(warp_end_radius_global, data->warp_sta, values.scale); /* done */ /* calculate pivot */ - copy_v3_v3(pivot, data->warp_sta); + copy_v3_v3(pivot_global, data->warp_sta); if (values.angle > 0.0f) { - madd_v3_v3fl(pivot, data->warp_tan, -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle)); + madd_v3_v3fl(pivot_global, data->warp_tan, -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle)); } else { - madd_v3_v3fl(pivot, data->warp_tan, +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); + madd_v3_v3fl(pivot_global, data->warp_tan, +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); } - for (i = 0; i < t->total; i++, td++) { - float mat[3][3]; - float delta[3]; - float fac, fac_scaled; + /* TODO(campbell): xform, compensate object center. */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; - if (td->flag & TD_NOACTION) - break; - - if (td->flag & TD_SKIP) - continue; + float warp_sta_local[3]; + float warp_end_local[3]; + float warp_end_radius_local[3]; + float pivot_local[3]; - if (UNLIKELY(values.angle == 0.0f)) { - copy_v3_v3(td->loc, td->iloc); - continue; + if (tc->use_local_mat) { + sub_v3_v3v3(warp_sta_local, data->warp_sta, tc->mat[3]); + sub_v3_v3v3(warp_end_local, data->warp_end, tc->mat[3]); + sub_v3_v3v3(warp_end_radius_local, warp_end_radius_global, tc->mat[3]); + sub_v3_v3v3(pivot_local, pivot_global, tc->mat[3]); + } + else { + copy_v3_v3(warp_sta_local, data->warp_sta); + copy_v3_v3(warp_end_local, data->warp_end); + copy_v3_v3(warp_end_radius_local, warp_end_radius_global); + copy_v3_v3(pivot_local, pivot_global); } - copy_v3_v3(vec, td->iloc); - mul_m3_v3(td->mtx, vec); + for (i = 0; i < tc->data_len; i++, td++) { + float mat[3][3]; + float delta[3]; + float fac, fac_scaled; - fac = line_point_factor_v3(vec, data->warp_sta, warp_end_radius); - if (is_clamp) { - CLAMP(fac, 0.0f, 1.0f); - } + if (td->flag & TD_NOACTION) + break; - fac_scaled = fac * td->factor; - axis_angle_normalized_to_mat3(mat, data->warp_nor, values.angle * fac_scaled); - interp_v3_v3v3(delta, data->warp_sta, warp_end_radius, fac_scaled); - sub_v3_v3(delta, data->warp_sta); + if (td->flag & TD_SKIP) + continue; - /* delta is subtracted, rotation adds back this offset */ - sub_v3_v3(vec, delta); + if (UNLIKELY(values.angle == 0.0f)) { + copy_v3_v3(td->loc, td->iloc); + continue; + } - sub_v3_v3(vec, pivot); - mul_m3_v3(mat, vec); - add_v3_v3(vec, pivot); + copy_v3_v3(vec, td->iloc); + mul_m3_v3(td->mtx, vec); - mul_m3_v3(td->smtx, vec); + fac = line_point_factor_v3(vec, warp_sta_local, warp_end_radius_local); + if (is_clamp) { + CLAMP(fac, 0.0f, 1.0f); + } - /* rotation */ - if ((t->flag & T_POINTS) == 0) { - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); - } + fac_scaled = fac * td->factor; + axis_angle_normalized_to_mat3(mat, data->warp_nor, values.angle * fac_scaled); + interp_v3_v3v3(delta, warp_sta_local, warp_end_radius_local, fac_scaled); + sub_v3_v3(delta, warp_sta_local); + + /* delta is subtracted, rotation adds back this offset */ + sub_v3_v3(vec, delta); - /* location */ - copy_v3_v3(td->loc, vec); + sub_v3_v3(vec, pivot_local); + mul_m3_v3(mat, vec); + add_v3_v3(vec, pivot_local); + + mul_m3_v3(td->smtx, vec); + + /* rotation */ + if ((t->flag & T_POINTS) == 0) { + ElementRotation(t, tc, td, mat, V3D_AROUND_LOCAL_ORIGINS); + } + + /* location */ + copy_v3_v3(td->loc, vec); + } } recalcData(t); @@ -3093,7 +3196,6 @@ static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event) static void applyShear(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float vec[3]; float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3]; float value; @@ -3136,43 +3238,46 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) mul_m3_m3m3(tmat, smat, persmat); mul_m3_m3m3(totmat, persinv, tmat); - for (i = 0; i < t->total; i++, td++) { - const float *center, *co; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + const float *center, *co; - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (t->obedit) { - float mat3[3][3]; - mul_m3_m3m3(mat3, totmat, td->mtx); - mul_m3_m3m3(tmat, td->smtx, mat3); - } - else { - copy_m3_m3(tmat, totmat); - } + if (t->flag & T_EDIT) { + float mat3[3][3]; + mul_m3_m3m3(mat3, totmat, td->mtx); + mul_m3_m3m3(tmat, td->smtx, mat3); + } + else { + copy_m3_m3(tmat, totmat); + } - if (is_local_center) { - center = td->center; - co = td->loc; - } - else { - center = t->center; - co = td->center; - } + if (is_local_center) { + center = td->center; + co = td->loc; + } + else { + center = tc->center_local; + co = td->center; + } - sub_v3_v3v3(vec, co, center); + sub_v3_v3v3(vec, co, center); - mul_m3_v3(tmat, vec); + mul_m3_v3(tmat, vec); - add_v3_v3(vec, center); - sub_v3_v3(vec, co); + add_v3_v3(vec, center); + sub_v3_v3(vec, co); - mul_v3_fl(vec, td->factor); + mul_v3_fl(vec, td->factor); - add_v3_v3v3(td->loc, td->iloc, vec); + add_v3_v3v3(td->loc, td->iloc, vec); + } } recalcData(t); @@ -3200,7 +3305,7 @@ static void initResize(TransInfo *t) t->num.val_flag[1] |= NUM_NULL_ONE; t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; @@ -3284,7 +3389,7 @@ static void TransMat3ToSize(float mat[3][3], float smat[3][3], float size[3]) if (dot_v3v3(rmat[2], smat[2]) < 0.0f) size[2] = -size[2]; } -static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) +static void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], center[3]; float vec[3]; @@ -3298,7 +3403,7 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) } if (t->con.applySize) { - t->con.applySize(t, td, tmat); + t->con.applySize(t, tc, td, tmat); } /* local constraint shouldn't alter center */ @@ -3310,11 +3415,11 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) copy_v3_v3(center, td->center); } else { - copy_v3_v3(center, t->center); + copy_v3_v3(center, tc->center_local); } } else { - copy_v3_v3(center, t->center); + copy_v3_v3(center, tc->center_local); } if (td->ext) { @@ -3385,9 +3490,8 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) constraintTransLim(t, td); } -static void applyResize(TransInfo *t, const int mval[2]) +static void applyResize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -3396,15 +3500,7 @@ static void applyResize(TransInfo *t, const int mval[2]) copy_v3_v3(t->values, t->auto_values); } else { - float ratio; - - /* for manipulator, center handle, the scaling can't be done relative to center */ - if ((t->flag & T_USES_MANIPULATOR) && t->con.mode == 0) { - ratio = 1.0f - ((t->mouse.imval[0] - mval[0]) + (t->mouse.imval[1] - mval[1])) / 100.0f; - } - else { - ratio = t->values[0]; - } + float ratio = t->values[0]; copy_v3_fl(t->values, ratio); @@ -3420,21 +3516,24 @@ static void applyResize(TransInfo *t, const int mval[2]) size_to_mat3(mat, t->values); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } copy_m3_m3(t->mat, mat); // used in manipulator headerResize(t, t->values, str); - for (i = 0, td = t->data; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - ElementResize(t, td, mat); + ElementResize(t, tc, td, mat); + } } /* evil hack - redo resize if cliping needed */ @@ -3442,17 +3541,21 @@ static void applyResize(TransInfo *t, const int mval[2]) size_to_mat3(mat, t->values); if (t->con.applySize) - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); - for (i = 0, td = t->data; i < t->total; i++, td++) - ElementResize(t, td, mat); - /* In proportional edit it can happen that */ - /* vertices in the radius of the brush end */ - /* outside the clipping area */ - /* XXX HACK - dg */ - if (t->flag & T_PROP_EDIT_ALL) { - clipUVData(t); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) + ElementResize(t, tc, td, mat); + + /* In proportional edit it can happen that */ + /* vertices in the radius of the brush end */ + /* outside the clipping area */ + /* XXX HACK - dg */ + if (t->flag & T_PROP_EDIT_ALL) { + clipUVData(t); + } } } @@ -3481,7 +3584,7 @@ static void initSkinResize(TransInfo *t) t->num.val_flag[1] |= NUM_NULL_ONE; t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; @@ -3505,7 +3608,6 @@ static void initSkinResize(TransInfo *t) static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float size[3], mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -3530,31 +3632,34 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) headerResize(t, size, str); - for (i = 0, td = t->data; i < t->total; i++, td++) { - float tmat[3][3], smat[3][3]; - float fsize[3]; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float tmat[3][3], smat[3][3]; + float fsize[3]; - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (t->flag & T_EDIT) { - mul_m3_m3m3(smat, mat, td->mtx); - mul_m3_m3m3(tmat, td->smtx, smat); - } - else { - copy_m3_m3(tmat, mat); - } + if (t->flag & T_EDIT) { + mul_m3_m3m3(smat, mat, td->mtx); + mul_m3_m3m3(tmat, td->smtx, smat); + } + else { + copy_m3_m3(tmat, mat); + } - if (t->con.applySize) { - t->con.applySize(t, NULL, tmat); - } + if (t->con.applySize) { + t->con.applySize(t, NULL, NULL, tmat); + } - mat3_to_size(fsize, tmat); - td->val[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor); - td->val[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor); + mat3_to_size(fsize, tmat); + td->val[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor); + td->val[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor); + } } recalcData(t); @@ -3572,7 +3677,6 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) static void initToSphere(TransInfo *t) { - TransData *td = t->data; int i; t->mode = TFM_TOSPHERE; @@ -3594,11 +3698,14 @@ static void initToSphere(TransInfo *t) t->flag |= T_NO_CONSTRAINT; // Calculate average radius - for (i = 0; i < t->total; i++, td++) { - t->val += len_v3v3(t->center, td->iloc); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + t->val += len_v3v3(tc->center_local, td->iloc); + } } - t->val /= (float)t->total; + t->val /= (float)t->data_len_all; } static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) @@ -3607,7 +3714,6 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) float ratio, radius; int i; char str[UI_MAX_DRAW_STR]; - TransData *td = t->data; ratio = t->values[0]; @@ -3632,27 +3738,28 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %.4f %s"), ratio, t->proptext); } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float tratio; + if (td->flag & TD_NOACTION) + break; - for (i = 0; i < t->total; i++, td++) { - float tratio; - if (td->flag & TD_NOACTION) - break; - - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - sub_v3_v3v3(vec, td->iloc, t->center); + sub_v3_v3v3(vec, td->iloc, tc->center_local); - radius = normalize_v3(vec); + radius = normalize_v3(vec); - tratio = ratio * td->factor; + tratio = ratio * td->factor; - mul_v3_fl(vec, radius * (1.0f - tratio) + t->val * tratio); + mul_v3_fl(vec, radius * (1.0f - tratio) + t->val * tratio); - add_v3_v3v3(td->loc, t->center, vec); + add_v3_v3v3(td->loc, tc->center_local, vec); + } } - recalcData(t); ED_area_headerprint(t->sa, str); @@ -3669,7 +3776,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) static void postInputRotation(TransInfo *t, float values[3]) { if ((t->con.mode & CON_APPLY) && t->con.applyRot) { - t->con.applyRot(t, NULL, t->axis, values); + t->con.applyRot(t, NULL, NULL, t->axis, values); } } @@ -3714,7 +3821,7 @@ static void initRotation(TransInfo *t) * * Protected axis and other transform settings are taken into account. */ -static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center) +static void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const float *center) { float vec[3], totmat[3][3], smat[3][3]; float eul[3], fmat[3][3], quat[4]; @@ -3758,18 +3865,14 @@ static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], con * has been computed, it has to be converted back into the bone's space. */ else if (t->flag & T_POSE) { - float pmtx[3][3], imtx[3][3]; - // Extract and invert armature object matrix - copy_m3_m4(pmtx, t->poseobj->obmat); - invert_m3_m3(imtx, pmtx); if ((td->flag & TD_NO_LOC) == 0) { sub_v3_v3v3(vec, td->center, center); - mul_m3_v3(pmtx, vec); // To Global space + mul_m3_v3(tc->mat3, vec); // To Global space mul_m3_v3(mat, vec); // Applying rotation - mul_m3_v3(imtx, vec); // To Local space + mul_m3_v3(tc->imat3, vec); // To Local space add_v3_v3(vec, center); /* vec now is the location where the object has to be */ @@ -3781,11 +3884,11 @@ static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], con /* do nothing */ } else if (td->flag & TD_PBONE_LOCAL_MTX_C) { - mul_m3_v3(pmtx, vec); // To Global space + mul_m3_v3(tc->mat3, vec); // To Global space mul_m3_v3(td->ext->l_smtx, vec); // To Pose space (Local Location) } else { - mul_m3_v3(pmtx, vec); // To Global space + mul_m3_v3(tc->mat3, vec); // To Global space mul_m3_v3(td->smtx, vec); // To Pose space } @@ -3927,7 +4030,7 @@ static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], con } } -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around) +static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around) { const float *center; @@ -3936,37 +4039,39 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const center = td->center; } else { - center = t->center; + center = tc->center_local; } - ElementRotation_ex(t, td, mat, center); + ElementRotation_ex(t, tc, td, mat, center); } static void applyRotationValue(TransInfo *t, float angle, float axis[3]) { - TransData *td = t->data; float mat[3][3]; int i; axis_angle_normalized_to_mat3(mat, axis, angle); - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (t->con.applyRot) { - t->con.applyRot(t, td, axis, NULL); - axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); - } - else if (t->flag & T_PROP_EDIT) { - axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); - } + if (t->con.applyRot) { + t->con.applyRot(t, tc, td, axis, NULL); + axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); + } + else if (t->flag & T_PROP_EDIT) { + axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); + } - ElementRotation(t, td, mat, t->around); + ElementRotation(t, tc, td, mat, t->around); + } } } @@ -3982,7 +4087,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) snapGridIncrement(t, &final); if ((t->con.mode & CON_APPLY) && t->con.applyRot) { - t->con.applyRot(t, NULL, t->axis, NULL); + t->con.applyRot(t, NULL, NULL, t->axis, NULL); } else { /* reset axis if constraint is not set */ @@ -4051,7 +4156,6 @@ static void initTrackball(TransInfo *t) static void applyTrackballValue(TransInfo *t, const float axis1[3], const float axis2[3], float angles[2]) { - TransData *td = t->data; float mat[3][3]; float axis[3]; float angle; @@ -4062,18 +4166,21 @@ static void applyTrackballValue(TransInfo *t, const float axis1[3], const float angle = normalize_v3(axis); axis_angle_normalized_to_mat3(mat, axis, angle); - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (t->flag & T_PROP_EDIT) { - axis_angle_normalized_to_mat3(mat, axis, td->factor * angle); - } + if (t->flag & T_PROP_EDIT) { + axis_angle_normalized_to_mat3(mat, axis, td->factor * angle); + } - ElementRotation(t, td, mat, t->around); + ElementRotation(t, tc, td, mat, t->around); + } } } @@ -4328,81 +4435,83 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ static void applyTranslationValue(TransInfo *t, const float vec[3]) { - TransData *td = t->data; + const bool apply_snap_align_rotation = usingSnappingNormal(t);// && (t->tsnap.status & POINT_INIT); float tvec[3]; /* The ideal would be "apply_snap_align_rotation" only when a snap point is found * so, maybe inside this function is not the best place to apply this rotation. * but you need "handle snapping rotation before doing the translation" (really?) */ - const bool apply_snap_align_rotation = usingSnappingNormal(t);// && (t->tsnap.status & POINT_INIT); - float pivot[3]; - if (apply_snap_align_rotation) { - copy_v3_v3(pivot, t->tsnap.snapTarget); - /* The pivot has to be in local-space (see T49494) */ - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->imat, pivot); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + float pivot[3]; + if (apply_snap_align_rotation) { + copy_v3_v3(pivot, t->tsnap.snapTarget); + /* The pivot has to be in local-space (see T49494) */ + if (tc->use_local_mat) { + mul_m4_v3(tc->imat, pivot); + } } - } - for (int i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - float rotate_offset[3] = {0}; - bool use_rotate_offset = false; + float rotate_offset[3] = {0}; + bool use_rotate_offset = false; - /* handle snapping rotation before doing the translation */ - if (apply_snap_align_rotation) { - float mat[3][3]; + /* handle snapping rotation before doing the translation */ + if (apply_snap_align_rotation) { + float mat[3][3]; - if (validSnappingNormal(t)) { - const float *original_normal; + if (validSnappingNormal(t)) { + const float *original_normal; - /* In pose mode, we want to align normals with Y axis of bones... */ - if (t->flag & T_POSE) - original_normal = td->axismtx[1]; - else - original_normal = td->axismtx[2]; + /* In pose mode, we want to align normals with Y axis of bones... */ + if (t->flag & T_POSE) + original_normal = td->axismtx[1]; + else + original_normal = td->axismtx[2]; - rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal); - } - else { - unit_m3(mat); - } + rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal); + } + else { + unit_m3(mat); + } - ElementRotation_ex(t, td, mat, pivot); + ElementRotation_ex(t, tc, td, mat, pivot); - if (td->loc) { - use_rotate_offset = true; - sub_v3_v3v3(rotate_offset, td->loc, td->iloc); + if (td->loc) { + use_rotate_offset = true; + sub_v3_v3v3(rotate_offset, td->loc, td->iloc); + } } - } - if (t->con.applyVec) { - float pvec[3]; - t->con.applyVec(t, td, vec, tvec, pvec); - } - else { - copy_v3_v3(tvec, vec); - } + if (t->con.applyVec) { + float pvec[3]; + t->con.applyVec(t, tc, td, vec, tvec, pvec); + } + else { + copy_v3_v3(tvec, vec); + } - if (use_rotate_offset) { - add_v3_v3(tvec, rotate_offset); - } + if (use_rotate_offset) { + add_v3_v3(tvec, rotate_offset); + } - mul_m3_v3(td->smtx, tvec); - mul_v3_fl(tvec, td->factor); + mul_m3_v3(td->smtx, tvec); + mul_v3_fl(tvec, td->factor); - protectedTransBits(td->protectflag, tvec); + protectedTransBits(td->protectflag, tvec); - if (td->loc) - add_v3_v3v3(td->loc, td->iloc, tvec); + if (td->loc) + add_v3_v3v3(td->loc, td->iloc, tvec); - constraintTransLim(t, td); + constraintTransLim(t, td); + } } } @@ -4428,7 +4537,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; - t->con.applyVec(t, NULL, t->values, value_final, pvec); + t->con.applyVec(t, NULL, NULL, t->values, value_final, pvec); headerTranslation(t, pvec, str); /* only so we have re-usable value with redo, see T46741. */ @@ -4472,7 +4581,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) static void initShrinkFatten(TransInfo *t) { // If not in mesh edit mode, fallback to Resize - if (t->obedit == NULL || t->obedit->type != OB_MESH) { + if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) { initResize(t); } else { @@ -4502,7 +4611,6 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) int i; char str[UI_MAX_DRAW_STR]; size_t ofs = 0; - TransData *td = t->data; distance = -t->values[0]; @@ -4539,21 +4647,24 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); /* done with header string */ - for (i = 0; i < t->total; i++, td++) { - float tdistance; /* temp dist */ - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float tdistance; /* temp dist */ + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - /* get the final offset */ - tdistance = distance * td->factor; - if (td->ext && (t->flag & T_ALT_TRANSFORM)) { - tdistance *= td->ext->isize[0]; /* shell factor */ - } + /* get the final offset */ + tdistance = distance * td->factor; + if (td->ext && (t->flag & T_ALT_TRANSFORM)) { + tdistance *= td->ext->isize[0]; /* shell factor */ + } - madd_v3_v3v3fl(td->loc, td->iloc, td->axismtx[2], tdistance); + madd_v3_v3v3fl(td->loc, td->iloc, td->axismtx[2], tdistance); + } } recalcData(t); @@ -4593,7 +4704,6 @@ static void initTilt(TransInfo *t) static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; int i; char str[UI_MAX_DRAW_STR]; @@ -4621,15 +4731,18 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - *td->val = td->ival + final * td->factor; + if (td->val) { + *td->val = td->ival + final * td->factor; + } } } @@ -4673,7 +4786,6 @@ static void initCurveShrinkFatten(TransInfo *t) static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -4697,18 +4809,21 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - *td->val = td->ival * ratio; - /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) *td->val = 0.001f; + if (td->val) { + *td->val = td->ival * ratio; + /* apply PET */ + *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + if (*td->val <= 0.0f) *td->val = 0.001f; + } } } @@ -4752,7 +4867,6 @@ static void initMaskShrinkFatten(TransInfo *t) static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float ratio; int i; bool initial_feather = false; @@ -4781,35 +4895,41 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (ratio > 1.0f) { initial_feather = true; - for (td = t->data, i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->ival >= 0.001f) - initial_feather = false; + if (td->ival >= 0.001f) + initial_feather = false; + } } } /* apply shrink/fatten */ - for (td = t->data, i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (td = tc->data, i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - if (initial_feather) - *td->val = td->ival + (ratio - 1.0f) * 0.01f; - else - *td->val = td->ival * ratio; + if (td->val) { + if (initial_feather) + *td->val = td->ival + (ratio - 1.0f) * 0.01f; + else + *td->val = td->ival * ratio; - /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) *td->val = 0.001f; + /* apply PET */ + *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + if (*td->val <= 0.0f) *td->val = 0.001f; + } } } @@ -4853,7 +4973,6 @@ static void initGPShrinkFatten(TransInfo *t) static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -4877,18 +4996,21 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - *td->val = td->ival * ratio; - /* apply PET */ - *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); - if (*td->val <= 0.0f) *td->val = 0.001f; + if (td->val) { + *td->val = td->ival * ratio; + /* apply PET */ + *td->val = (*td->val * td->factor) + ((1.0f - td->factor) * td->ival); + if (*td->val <= 0.0f) *td->val = 0.001f; + } } } @@ -4930,7 +5052,6 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) float distance; int i; char str[UI_MAX_DRAW_STR]; - TransData *td = t->data; distance = t->values[0]; @@ -4954,35 +5075,38 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) } if (t->con.applyRot && t->con.mode & CON_APPLY) { - t->con.applyRot(t, NULL, axis_global, NULL); + t->con.applyRot(t, NULL, NULL, axis_global, NULL); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; - - if (td->flag & TD_SKIP) - continue; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - sub_v3_v3v3(vec, t->center, td->center); - if (t->con.applyRot && t->con.mode & CON_APPLY) { - float axis[3]; - copy_v3_v3(axis, axis_global); - t->con.applyRot(t, td, axis, NULL); + if (td->flag & TD_SKIP) + continue; - mul_m3_v3(td->smtx, axis); - if (isLockConstraint(t)) { - float dvec[3]; - project_v3_v3v3(dvec, vec, axis); - sub_v3_v3(vec, dvec); - } - else { - project_v3_v3v3(vec, vec, axis); + sub_v3_v3v3(vec, tc->center_local, td->center); + if (t->con.applyRot && t->con.mode & CON_APPLY) { + float axis[3]; + copy_v3_v3(axis, axis_global); + t->con.applyRot(t, tc, td, axis, NULL); + + mul_m3_v3(td->smtx, axis); + if (isLockConstraint(t)) { + float dvec[3]; + project_v3_v3v3(dvec, vec, axis); + sub_v3_v3(vec, dvec); + } + else { + project_v3_v3v3(vec, vec, axis); + } } - } - normalize_v3_length(vec, distance * td->factor); + normalize_v3_length(vec, distance * td->factor); - add_v3_v3v3(td->loc, td->iloc, vec); + add_v3_v3v3(td->loc, td->iloc, vec); + } } recalcData(t); @@ -5020,7 +5144,6 @@ static void initBevelWeight(TransInfo *t) static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float weight; int i; char str[UI_MAX_DRAW_STR]; @@ -5054,14 +5177,17 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->val) { - *td->val = td->ival + weight * td->factor; - if (*td->val < 0.0f) *td->val = 0.0f; - if (*td->val > 1.0f) *td->val = 1.0f; + if (td->val) { + *td->val = td->ival + weight * td->factor; + if (*td->val < 0.0f) *td->val = 0.0f; + if (*td->val > 1.0f) *td->val = 1.0f; + } } } @@ -5100,7 +5226,6 @@ static void initCrease(TransInfo *t) static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float crease; int i; char str[UI_MAX_DRAW_STR]; @@ -5134,17 +5259,20 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Crease: %.3f %s"), crease, t->proptext); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - *td->val = td->ival + crease * td->factor; - if (*td->val < 0.0f) *td->val = 0.0f; - if (*td->val > 1.0f) *td->val = 1.0f; + if (td->val) { + *td->val = td->ival + crease * td->factor; + if (*td->val < 0.0f) *td->val = 0.0f; + if (*td->val > 1.0f) *td->val = 1.0f; + } } } @@ -5211,7 +5339,7 @@ static void headerBoneSize(TransInfo *t, const float vec[3], char str[UI_MAX_DRA } } -static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) +static void ElementBoneSize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], oldy; float sizemat[3][3]; @@ -5220,7 +5348,7 @@ static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) mul_m3_m3m3(tmat, td->smtx, smat); if (t->con.applySize) { - t->con.applySize(t, td, tmat); + t->con.applySize(t, tc, td, tmat); } /* we've tucked the scale in loc */ @@ -5231,23 +5359,13 @@ static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) td->loc[1] = oldy; } -static void applyBoneSize(TransInfo *t, const int mval[2]) +static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float size[3], mat[3][3]; - float ratio; + float ratio = t->values[0]; int i; char str[UI_MAX_DRAW_STR]; - // TRANSFORM_FIX_ME MOVE TO MOUSE INPUT - /* for manipulator, center handle, the scaling can't be done relative to center */ - if ((t->flag & T_USES_MANIPULATOR) && t->con.mode == 0) { - ratio = 1.0f - ((t->mouse.imval[0] - mval[0]) + (t->mouse.imval[1] - mval[1])) / 100.0f; - } - else { - ratio = t->values[0]; - } - copy_v3_fl(size, ratio); snapGridIncrement(t, size); @@ -5261,21 +5379,24 @@ static void applyBoneSize(TransInfo *t, const int mval[2]) size_to_mat3(mat, size); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } copy_m3_m3(t->mat, mat); // used in manipulator headerBoneSize(t, size, str); - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - ElementBoneSize(t, td, mat); + ElementBoneSize(t, tc, td, mat); + } } recalcData(t); @@ -5313,7 +5434,6 @@ static void initBoneEnvelope(TransInfo *t) static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -5337,19 +5457,22 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %3f"), ratio); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - /* if the old/original value was 0.0f, then just use ratio */ - if (td->ival) - *td->val = td->ival * ratio; - else - *td->val = ratio; + if (td->val) { + /* if the old/original value was 0.0f, then just use ratio */ + if (td->ival) + *td->val = td->ival * ratio; + else + *td->val = ratio; + } } } @@ -5366,9 +5489,9 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) * \{ */ static void slide_origdata_init_flag( - TransInfo *t, SlideOrigData *sod) + TransInfo *t, TransDataContainer *tc, SlideOrigData *sod) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; const bool has_layer_math = CustomData_has_math(&bm->ldata); const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); @@ -5389,10 +5512,10 @@ static void slide_origdata_init_flag( } static void slide_origdata_init_data( - TransInfo *t, SlideOrigData *sod) + TransDataContainer *tc, SlideOrigData *sod) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; sod->origfaces = BLI_ghash_ptr_new(__func__); @@ -5453,11 +5576,11 @@ static void slide_origdata_create_data_vert( } static void slide_origdata_create_data( - TransInfo *t, SlideOrigData *sod, + TransInfo *t, TransDataContainer *tc, SlideOrigData *sod, TransDataGenericSlideVert *sv_array, unsigned int v_stride, unsigned int v_num) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; unsigned int i; TransDataGenericSlideVert *sv; @@ -5489,15 +5612,15 @@ static void slide_origdata_create_data( } if (t->flag & T_MIRROR) { - TransData *td = t->data; + TransData *td = tc->data; TransDataGenericSlideVert *sv_mirror; - sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * t->total, __func__); - sod->totsv_mirror = t->total; + sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * tc->data_len, __func__); + sod->totsv_mirror = tc->data_len; sv_mirror = sod->sv_mirror; - for (i = 0; i < t->total; i++, td++) { + for (i = 0; i < tc->data_len; i++, td++) { BMVert *eve = td->extra; if (eve) { sv_mirror->v = eve; @@ -5651,12 +5774,12 @@ static void slide_origdata_interp_data_vert( } static void slide_origdata_interp_data( - TransInfo *t, SlideOrigData *sod, + Object *obedit, SlideOrigData *sod, TransDataGenericSlideVert *sv, unsigned int v_stride, unsigned int v_num, bool is_final) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; unsigned int i; const bool has_mdisps = (sod->cd_loop_mdisp_offset != -1); @@ -5719,7 +5842,7 @@ static void slide_origdata_free_date( static void calcEdgeSlideCustomPoints(struct TransInfo *t) { - EdgeSlideData *sld = t->custom.mode.data; + EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start); @@ -5787,6 +5910,7 @@ static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, BMLoop *l_last = l_tmp->prev; BMLoop *l_iter; float dist = FLT_MAX; + bool found = false; l_iter = l_first; do { @@ -5805,12 +5929,13 @@ static bool bm_loop_calc_opposite_co(BMLoop *l_tmp, if (tdist < dist) { copy_v3_v3(r_co, tvec); dist = tdist; + found = true; } } } } while ((l_iter = l_iter->next) != l_last); - return (dist != FLT_MAX); + return found; } /** @@ -5916,11 +6041,11 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, * Calculate screenspace `mval_start` / `mval_end`, optionally slide direction. */ static void calcEdgeSlide_mval_range( - TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr, + TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const int *sv_table, const int loop_nr, const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction) { TransDataEdgeSlideVert *sv_array = sld->sv; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; ARegion *ar = t->ar; View3D *v3d = NULL; @@ -5947,7 +6072,7 @@ static void calcEdgeSlide_mval_range( unit_m4(projectMat); } else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat); } if (use_occlude_geometry) { @@ -5989,7 +6114,9 @@ static void calcEdgeSlide_mval_range( continue; /* This test is only relevant if object is not wire-drawn! See [#32068]. */ - if (use_occlude_geometry && !BMBVH_EdgeVisible(bmbvh, e_other, ar, v3d, t->obedit)) { + if (use_occlude_geometry && + !BMBVH_EdgeVisible(bmbvh, e_other, t->depsgraph, ar, v3d, tc->obedit)) + { continue; } @@ -6076,7 +6203,7 @@ static void calcEdgeSlide_mval_range( } static void calcEdgeSlide_even( - TransInfo *t, EdgeSlideData *sld, const float mval[2]) + TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const float mval[2]) { TransDataEdgeSlideVert *sv = sld->sv; @@ -6101,7 +6228,7 @@ static void calcEdgeSlide_even( unit_m4(projectMat); } else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat); } for (i = 0; i < sld->totsv; i++, sv++) { @@ -6121,9 +6248,9 @@ static void calcEdgeSlide_even( } } -static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMEdge *e; @@ -6138,13 +6265,9 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f View3D *v3d = NULL; RegionView3D *rv3d = NULL; - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - sld->flipped = flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; /*ensure valid selection*/ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -6460,25 +6583,23 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); + calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); if (rv3d) { - calcEdgeSlide_even(t, sld, mval); + calcEdgeSlide_even(t, tc, sld, mval); } sld->em = em; - sld->perc = 0.0f; - - t->custom.mode.data = sld; + tc->custom.mode.data = sld; MEM_freeN(sv_table); @@ -6489,9 +6610,9 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f * A simple version of #createEdgeSlideVerts_double_side * Which assumes the longest unselected. */ -static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMEdge *e; @@ -6511,15 +6632,9 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f rv3d = t->ar ? t->ar->regiondata : NULL; } - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - /* happens to be best for single-sided */ - sld->flipped = !flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; - /* ensure valid selection */ { int i = 0, j = 0; @@ -6663,25 +6778,23 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); + calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); if (rv3d) { - calcEdgeSlide_even(t, sld, mval); + calcEdgeSlide_even(t, tc, sld, mval); } sld->em = em; - sld->perc = 0.0f; - - t->custom.mode.data = sld; + tc->custom.mode.data = sld; MEM_freeN(sv_table); @@ -6690,14 +6803,16 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f void projectEdgeSlideData(TransInfo *t, bool is_final) { - EdgeSlideData *sld = t->custom.mode.data; - SlideOrigData *sod = &sld->orig_data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + SlideOrigData *sod = &sld->orig_data; - if (sod->use_origfaces == false) { - return; - } + if (sod->use_origfaces == false) { + return; + } - slide_origdata_interp_data(t, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + slide_origdata_interp_data(tc->obedit, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + } } void freeEdgeSlideTempFaces(EdgeSlideData *sld) @@ -6705,7 +6820,7 @@ void freeEdgeSlideTempFaces(EdgeSlideData *sld) slide_origdata_free_date(&sld->orig_data); } -void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) +void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { EdgeSlideData *sld = custom_data->data; @@ -6725,17 +6840,39 @@ void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp) { EdgeSlideData *sld; - bool ok; + bool ok = false; t->mode = TFM_EDGE_SLIDE; t->transform = applyEdgeSlide; t->handleEvent = handleEventEdgeSlide; + { + EdgeSlideParams *slp = MEM_callocN(sizeof(*slp), __func__); + slp->use_even = use_even; + slp->flipped = flipped; + /* happens to be best for single-sided */ + if (use_double_side == false) { + slp->flipped = !flipped; + } + slp->perc = 0.0f; + + if (!use_clamp) { + t->flag |= T_ALT_TRANSFORM; + } + + t->custom.mode.data = slp; + t->custom.mode.use_free = true; + } + if (use_double_side) { - ok = createEdgeSlideVerts_double_side(t, use_even, flipped, use_clamp); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createEdgeSlideVerts_double_side(t, tc); + } } else { - ok = createEdgeSlideVerts_single_side(t, use_even, flipped, use_clamp); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createEdgeSlideVerts_single_side(t, tc); + } } if (!ok) { @@ -6743,12 +6880,13 @@ static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, return; } - sld = t->custom.mode.data; - - if (!sld) - return; - - t->custom.mode.free_cb = freeEdgeSlideVerts; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + sld = tc->custom.mode.data; + if (!sld) { + continue; + } + tc->custom.mode.free_cb = freeEdgeSlideVerts; + } /* set custom point first if you want value to be initialized by init */ calcEdgeSlideCustomPoints(t); @@ -6775,20 +6913,20 @@ static void initEdgeSlide(TransInfo *t) static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; + EdgeSlideParams *slp = t->custom.mode.data; - if (sld) { + if (slp) { switch (event->type) { case EKEY: if (event->val == KM_PRESS) { - sld->use_even = !sld->use_even; + slp->use_even = !slp->use_even; calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } break; case FKEY: if (event->val == KM_PRESS) { - sld->flipped = !sld->flipped; + slp->flipped = !slp->flipped; calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } @@ -6802,6 +6940,7 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven } break; case EVT_MODAL_MAP: +#if 0 switch (event->val) { case TFM_MODAL_EDGESLIDE_DOWN: sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv; @@ -6810,6 +6949,7 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv; return TREDRAW_HARD; } +#endif break; case MOUSEMOVE: calcEdgeSlideCustomPoints(t); @@ -6824,12 +6964,13 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven static void drawEdgeSlide(TransInfo *t) { - if ((t->mode == TFM_EDGE_SLIDE) && t->custom.mode.data) { - EdgeSlideData *sld = t->custom.mode.data; + if ((t->mode == TFM_EDGE_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) { + const EdgeSlideParams *slp = t->custom.mode.data; + EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); /* Even mode */ - if ((sld->use_even == true) || (is_clamp == false)) { + if ((slp->use_even == true) || (is_clamp == false)) { View3D *v3d = t->view; const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; @@ -6837,17 +6978,19 @@ static void drawEdgeSlide(TransInfo *t) glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + gpuPushMatrix(); + gpuMultMatrix(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); - glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT); - glPushMatrix(); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); - glMultMatrixf(t->obedit->obmat); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - if (sld->use_even == true) { + if (slp->use_even == true) { float co_a[3], co_b[3], co_mark[3]; TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - const float fac = (sld->perc + 1.0f) / 2.0f; + const float fac = (slp->perc + 1.0f) / 2.0f; const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; const float guide_size = ctrl_size - 0.5f; const int alpha_shade = -30; @@ -6856,39 +6999,35 @@ static void drawEdgeSlide(TransInfo *t) add_v3_v3v3(co_b, curr_sv->v_co_orig, curr_sv->dir_side[1]); glLineWidth(line_size); - UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); - glBegin(GL_LINES); + immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); + immBeginAtMost(GWN_PRIM_LINES, 4); if (curr_sv->v_side[0]) { - glVertex3fv(curr_sv->v_side[0]->co); - glVertex3fv(curr_sv->v_co_orig); + immVertex3fv(pos, curr_sv->v_side[0]->co); + immVertex3fv(pos, curr_sv->v_co_orig); } if (curr_sv->v_side[1]) { - glVertex3fv(curr_sv->v_side[1]->co); - glVertex3fv(curr_sv->v_co_orig); + immVertex3fv(pos, curr_sv->v_side[1]->co); + immVertex3fv(pos, curr_sv->v_co_orig); } - glEnd(); + immEnd(); - UI_ThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade); + immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade); glPointSize(ctrl_size); - glBegin(GL_POINTS); - if (sld->flipped) { - if (curr_sv->v_side[1]) glVertex3fv(curr_sv->v_side[1]->co); + immBegin(GWN_PRIM_POINTS, 1); + if (slp->flipped) { + if (curr_sv->v_side[1]) immVertex3fv(pos, curr_sv->v_side[1]->co); } else { - if (curr_sv->v_side[0]) glVertex3fv(curr_sv->v_side[0]->co); + if (curr_sv->v_side[0]) immVertex3fv(pos, curr_sv->v_side[0]->co); } - glEnd(); + immEnd(); - UI_ThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade); + immUniformThemeColorShadeAlpha(TH_SELECT, 255, alpha_shade); glPointSize(guide_size); - glBegin(GL_POINTS); -#if 0 - interp_v3_v3v3(co_mark, co_b, co_a, fac); - glVertex3fv(co_mark); -#endif + immBegin(GWN_PRIM_POINTS, 1); interp_line_v3_v3v3v3(co_mark, co_b, curr_sv->v_co_orig, co_a, fac); - glVertex3fv(co_mark); - glEnd(); + immVertex3fv(pos, co_mark); + immEnd(); } else { if (is_clamp == false) { @@ -6898,9 +7037,10 @@ static void drawEdgeSlide(TransInfo *t) const int alpha_shade = -160; glLineWidth(line_size); - UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); - glBegin(GL_LINES); + immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); + immBegin(GWN_PRIM_LINES, sld->totsv * 2); + /* TODO(campbell): Loop over all verts */ sv = sld->sv; for (i = 0; i < sld->totsv; i++, sv++) { float a[3], b[3]; @@ -6917,18 +7057,19 @@ static void drawEdgeSlide(TransInfo *t) add_v3_v3(a, sv->v_co_orig); add_v3_v3(b, sv->v_co_orig); - glVertex3fv(a); - glVertex3fv(b); + immVertex3fv(pos, a); + immVertex3fv(pos, b); } - glEnd(); + immEnd(); } else { BLI_assert(0); } } - glPopMatrix(); - glPopAttrib(); + immUnbindProgram(); + + gpuPopMatrix(); glDisable(GL_BLEND); @@ -6940,38 +7081,43 @@ static void drawEdgeSlide(TransInfo *t) static void doEdgeSlide(TransInfo *t, float perc) { - EdgeSlideData *sld = t->custom.mode.data; - TransDataEdgeSlideVert *svlist = sld->sv, *sv; - int i; + EdgeSlideParams *slp = t->custom.mode.data; + EdgeSlideData *sld_active = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; - sld->perc = perc; - sv = svlist; + slp->perc = perc; - if (sld->use_even == false) { + if (slp->use_even == false) { const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); if (is_clamp) { const int side_index = (perc < 0.0f); const float perc_final = fabsf(perc); - for (i = 0; i < sld->totsv; i++, sv++) { - madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { + madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final); + } + sld->curr_side_unclamp = side_index; } - - sld->curr_side_unclamp = side_index; } else { - const int side_index = sld->curr_side_unclamp; - const float perc_init = fabsf(perc) * ((sld->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1); - for (i = 0; i < sld->totsv; i++, sv++) { - float dir_flip[3]; - float perc_final = perc_init; - if (!is_zero_v3(sv->dir_side[side_index])) { - copy_v3_v3(dir_flip, sv->dir_side[side_index]); - } - else { - copy_v3_v3(dir_flip, sv->dir_side[!side_index]); - perc_final *= -1; + const float perc_init = fabsf(perc) * ((sld_active->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1); + const int side_index = sld_active->curr_side_unclamp; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { + float dir_flip[3]; + float perc_final = perc_init; + if (!is_zero_v3(sv->dir_side[side_index])) { + copy_v3_v3(dir_flip, sv->dir_side[side_index]); + } + else { + copy_v3_v3(dir_flip, sv->dir_side[!side_index]); + perc_final *= -1; + } + madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final); } - madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final); } } } @@ -6984,24 +7130,28 @@ static void doEdgeSlide(TransInfo *t, float perc) * \note len_v3v3(curr_sv->dir_side[0], curr_sv->dir_side[1]) * is the same as the distance between the original vert locations, same goes for the lines below. */ - TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - const float curr_length_perc = curr_sv->edge_len * (((sld->flipped ? perc : -perc) + 1.0f) / 2.0f); + TransDataEdgeSlideVert *curr_sv = &sld_active->sv[sld_active->curr_sv_index]; + const float curr_length_perc = curr_sv->edge_len * (((slp->flipped ? perc : -perc) + 1.0f) / 2.0f); float co_a[3]; float co_b[3]; - for (i = 0; i < sld->totsv; i++, sv++) { - if (sv->edge_len > FLT_EPSILON) { - const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { + if (sv->edge_len > FLT_EPSILON) { + const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len; - add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]); - add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]); + add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]); + add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]); - if (sld->flipped) { - interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac); - } - else { - interp_line_v3_v3v3v3(sv->v->co, co_a, sv->v_co_orig, co_b, fac); + if (slp->flipped) { + interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac); + } + else { + interp_line_v3_v3v3v3(sv->v->co, co_a, sv->v_co_orig, co_b, fac); + } } } } @@ -7013,9 +7163,9 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; - EdgeSlideData *sld = t->custom.mode.data; - bool flipped = sld->flipped; - bool use_even = sld->use_even; + EdgeSlideParams *slp = t->custom.mode.data; + bool flipped = slp->flipped; + bool use_even = slp->use_even; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num)); @@ -7067,7 +7217,8 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) static void calcVertSlideCustomPoints(struct TransInfo *t) { - VertSlideData *sld = t->custom.mode.data; + VertSlideParams *slp = t->custom.mode.data; + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index]; const float *co_orig_3d = sv->co_orig_3d; @@ -7084,7 +7235,7 @@ static void calcVertSlideCustomPoints(struct TransInfo *t) ARRAY_SET_ITEMS(mval_start, co_orig_2d[0] + mval_ofs[0], co_orig_2d[1] + mval_ofs[1]); ARRAY_SET_ITEMS(mval_end, co_curr_2d[0] + mval_ofs[0], co_curr_2d[1] + mval_ofs[1]); - if (sld->flipped && sld->use_even) { + if (slp->flipped && slp->use_even) { setCustomPoints(t, &t->mouse, mval_start, mval_end); } else { @@ -7102,7 +7253,8 @@ static void calcVertSlideCustomPoints(struct TransInfo *t) */ static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2]) { - VertSlideData *sld = t->custom.mode.data; + /* Active object may have no selected vertices. */ + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; float mval_fl[2] = {UNPACK2(mval)}; TransDataVertSlideVert *sv; @@ -7129,7 +7281,7 @@ static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2]) */ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2]) { - VertSlideData *sld = t->custom.mode.data; + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; float imval_fl[2] = {UNPACK2(t->mouse.imval)}; float mval_fl[2] = {UNPACK2(mval)}; @@ -7157,7 +7309,7 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] float dir_dot; sub_v3_v3v3(tdir, sv->co_orig_3d, sv->co_link_orig_3d[j]); - mul_mat3_m4_v3(t->obedit->obmat, tdir); + mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, tdir); project_plane_v3_v3v3(tdir, tdir, t->viewinv[2]); normalize_v3(tdir); @@ -7175,9 +7327,9 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] } } -static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMIter eiter; @@ -7187,13 +7339,9 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); int j; - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - sld->flipped = flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; j = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -7256,14 +7404,12 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool sld->totsv = j; bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); sld->em = em; - sld->perc = 0.0f; - - t->custom.mode.data = sld; + tc->custom.mode.data = sld; /* most likely will be set below */ unit_m4(sld->proj_mat); @@ -7275,9 +7421,12 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool rv3d = ar ? ar->regiondata : NULL; if (rv3d) { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, sld->proj_mat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, sld->proj_mat); } + } + /* XXX, calc vert slide across all objects */ + if (tc == t->data_container) { calcVertSlideMouseActiveVert(t, t->mval); calcVertSlideMouseActiveEdges(t, t->mval); } @@ -7287,14 +7436,13 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool void projectVertSlideData(TransInfo *t, bool is_final) { - VertSlideData *sld = t->custom.mode.data; - SlideOrigData *sod = &sld->orig_data; - - if (sod->use_origfaces == false) { - return; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; + SlideOrigData *sod = &sld->orig_data; + if (sod->use_origfaces == true) { + slide_origdata_interp_data(tc->obedit, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + } } - - slide_origdata_interp_data(t, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); } void freeVertSlideTempFaces(VertSlideData *sld) @@ -7302,7 +7450,7 @@ void freeVertSlideTempFaces(VertSlideData *sld) slide_origdata_free_date(&sld->orig_data); } -void freeVertSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) +void freeVertSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { VertSlideData *sld = custom_data->data; @@ -7329,23 +7477,38 @@ void freeVertSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp) { - VertSlideData *sld; t->mode = TFM_VERT_SLIDE; t->transform = applyVertSlide; t->handleEvent = handleEventVertSlide; - if (!createVertSlideVerts(t, use_even, flipped, use_clamp)) { - t->state = TRANS_CANCEL; - return; + { + VertSlideParams *slp = MEM_callocN(sizeof(*slp), __func__); + slp->use_even = use_even; + slp->flipped = flipped; + slp->perc = 0.0f; + + if (!use_clamp) { + t->flag |= T_ALT_TRANSFORM; + } + + t->custom.mode.data = slp; + t->custom.mode.use_free = true; } - sld = t->custom.mode.data; + bool ok = false; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createVertSlideVerts(t, tc); + VertSlideData *sld = tc->custom.mode.data; + if (sld) { + tc->custom.mode.free_cb = freeVertSlideVerts; + } + } - if (!sld) + if (ok == false) { + t->state = TRANS_CANCEL; return; - - t->custom.mode.free_cb = freeVertSlideVerts; + } /* set custom point first if you want value to be initialized by init */ calcVertSlideCustomPoints(t); @@ -7372,14 +7535,14 @@ static void initVertSlide(TransInfo *t) static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_VERT_SLIDE) { - VertSlideData *sld = t->custom.mode.data; + VertSlideParams *slp = t->custom.mode.data; - if (sld) { + if (slp) { switch (event->type) { case EKEY: if (event->val == KM_PRESS) { - sld->use_even = !sld->use_even; - if (sld->flipped) { + slp->use_even = !slp->use_even; + if (slp->flipped) { calcVertSlideCustomPoints(t); } return TREDRAW_HARD; @@ -7387,7 +7550,7 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven break; case FKEY: if (event->val == KM_PRESS) { - sld->flipped = !sld->flipped; + slp->flipped = !slp->flipped; calcVertSlideCustomPoints(t); return TREDRAW_HARD; } @@ -7432,8 +7595,9 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven static void drawVertSlide(TransInfo *t) { - if ((t->mode == TFM_VERT_SLIDE) && t->custom.mode.data) { - VertSlideData *sld = t->custom.mode.data; + if ((t->mode == TFM_VERT_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) { + const VertSlideParams *slp = t->custom.mode.data; + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); /* Non-Prop mode */ @@ -7450,21 +7614,24 @@ static void drawVertSlide(TransInfo *t) glDisable(GL_DEPTH_TEST); glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glPushAttrib(GL_CURRENT_BIT | GL_LINE_BIT | GL_POINT_BIT); - glPushMatrix(); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - glMultMatrixf(t->obedit->obmat); + gpuPushMatrix(); + gpuMultMatrix(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); glLineWidth(line_size); - UI_ThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); - glBegin(GL_LINES); + + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); + + immBegin(GWN_PRIM_LINES, sld->totsv * 2); if (is_clamp) { sv = sld->sv; for (i = 0; i < sld->totsv; i++, sv++) { - glVertex3fv(sv->co_orig_3d); - glVertex3fv(sv->co_link_orig_3d[sv->co_link_curr]); + immVertex3fv(shdr_pos, sv->co_orig_3d); + immVertex3fv(shdr_pos, sv->co_link_orig_3d[sv->co_link_curr]); } } else { @@ -7477,21 +7644,21 @@ static void drawVertSlide(TransInfo *t) add_v3_v3(a, sv->co_orig_3d); add_v3_v3(b, sv->co_orig_3d); - glVertex3fv(a); - glVertex3fv(b); + immVertex3fv(shdr_pos, a); + immVertex3fv(shdr_pos, b); } } - glEnd(); + immEnd(); glPointSize(ctrl_size); - glBegin(GL_POINTS); - glVertex3fv((sld->flipped && sld->use_even) ? + immBegin(GWN_PRIM_POINTS, 1); + immVertex3fv(shdr_pos, (slp->flipped && slp->use_even) ? curr_sv->co_link_orig_3d[curr_sv->co_link_curr] : curr_sv->co_orig_3d); - glEnd(); + immEnd(); - glDisable(GL_BLEND); + immUnbindProgram(); /* direction from active vertex! */ if ((t->mval[0] != t->mouse.imval[0]) || @@ -7505,29 +7672,38 @@ static void drawVertSlide(TransInfo *t) mval_ofs[0] = t->mval[0] - t->mouse.imval[0]; mval_ofs[1] = t->mval[1] - t->mouse.imval[1]; - mul_v3_m4v3(co_orig_3d, t->obedit->obmat, curr_sv->co_orig_3d); + mul_v3_m4v3(co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d); zfac = ED_view3d_calc_zfac(t->ar->regiondata, co_orig_3d, NULL); ED_view3d_win_to_delta(t->ar, mval_ofs, co_dest_3d, zfac); - invert_m4_m4(t->obedit->imat, t->obedit->obmat); - mul_mat3_m4_v3(t->obedit->imat, co_dest_3d); + invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); + mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, co_dest_3d); add_v3_v3(co_dest_3d, curr_sv->co_orig_3d); - glLineWidth(1); - setlinestyle(1); + glLineWidth(1.0f); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 0); /* "simple" mode */ + immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f); + immUniform1f("dash_width", 6.0f); + immUniform1f("dash_factor", 0.5f); - cpack(0xffffff); - glBegin(GL_LINES); - glVertex3fv(curr_sv->co_orig_3d); - glVertex3fv(co_dest_3d); + immBegin(GWN_PRIM_LINES, 2); + immVertex3fv(shdr_pos, curr_sv->co_orig_3d); + immVertex3fv(shdr_pos, co_dest_3d); + immEnd(); - glEnd(); + immUnbindProgram(); } - glPopMatrix(); - glPopAttrib(); + gpuPopMatrix(); if (v3d && v3d->zbuf) glEnable(GL_DEPTH_TEST); @@ -7537,41 +7713,46 @@ static void drawVertSlide(TransInfo *t) static void doVertSlide(TransInfo *t, float perc) { - VertSlideData *sld = t->custom.mode.data; - TransDataVertSlideVert *svlist = sld->sv, *sv; - int i; + VertSlideParams *slp = t->custom.mode.data; - sld->perc = perc; - sv = svlist; + slp->perc = perc; - if (sld->use_even == false) { - for (i = 0; i < sld->totsv; i++, sv++) { - interp_v3_v3v3(sv->v->co, sv->co_orig_3d, sv->co_link_orig_3d[sv->co_link_curr], perc); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; + TransDataVertSlideVert *svlist = sld->sv, *sv; + int i; + + sv = svlist; + + if (slp->use_even == false) { + for (i = 0; i < sld->totsv; i++, sv++) { + interp_v3_v3v3(sv->v->co, sv->co_orig_3d, sv->co_link_orig_3d[sv->co_link_curr], perc); + } } - } - else { - TransDataVertSlideVert *sv_curr = &sld->sv[sld->curr_sv_index]; - const float edge_len_curr = len_v3v3(sv_curr->co_orig_3d, sv_curr->co_link_orig_3d[sv_curr->co_link_curr]); - const float tperc = perc * edge_len_curr; + else { + TransDataVertSlideVert *sv_curr = &sld->sv[sld->curr_sv_index]; + const float edge_len_curr = len_v3v3(sv_curr->co_orig_3d, sv_curr->co_link_orig_3d[sv_curr->co_link_curr]); + const float tperc = perc * edge_len_curr; - for (i = 0; i < sld->totsv; i++, sv++) { - float edge_len; - float dir[3]; + for (i = 0; i < sld->totsv; i++, sv++) { + float edge_len; + float dir[3]; - sub_v3_v3v3(dir, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d); - edge_len = normalize_v3(dir); + sub_v3_v3v3(dir, sv->co_link_orig_3d[sv->co_link_curr], sv->co_orig_3d); + edge_len = normalize_v3(dir); - if (edge_len > FLT_EPSILON) { - if (sld->flipped) { - madd_v3_v3v3fl(sv->v->co, sv->co_link_orig_3d[sv->co_link_curr], dir, -tperc); + if (edge_len > FLT_EPSILON) { + if (slp->flipped) { + madd_v3_v3v3fl(sv->v->co, sv->co_link_orig_3d[sv->co_link_curr], dir, -tperc); + } + else { + madd_v3_v3v3fl(sv->v->co, sv->co_orig_3d, dir, tperc); + } } else { - madd_v3_v3v3fl(sv->v->co, sv->co_orig_3d, dir, tperc); + copy_v3_v3(sv->v->co, sv->co_orig_3d); } } - else { - copy_v3_v3(sv->v->co, sv->co_orig_3d); - } } } } @@ -7581,9 +7762,9 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; - VertSlideData *sld = t->custom.mode.data; - const bool flipped = sld->flipped; - const bool use_even = sld->use_even; + VertSlideParams *slp = t->custom.mode.data; + const bool flipped = slp->flipped; + const bool use_even = slp->use_even; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num)); @@ -7656,7 +7837,6 @@ static void initBoneRoll(TransInfo *t) static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; int i; char str[UI_MAX_DRAW_STR]; @@ -7682,14 +7862,17 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) } /* set roll values */ - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - *(td->val) = td->ival - final; + *(td->val) = td->ival - final; + } } recalcData(t); @@ -7723,7 +7906,6 @@ static void initBakeTime(TransInfo *t) static void applyBakeTime(TransInfo *t, const int mval[2]) { - TransData *td = t->data; float time; int i; char str[UI_MAX_DRAW_STR]; @@ -7767,17 +7949,20 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) BLI_snprintf(str, sizeof(str), IFACE_("Time: %.3f %s"), time, t->proptext); } - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if (td->val) { - *td->val = td->ival + time * td->factor; - if (td->ext->size && *td->val < *td->ext->size) *td->val = *td->ext->size; - if (td->ext->quat && *td->val > *td->ext->quat) *td->val = *td->ext->quat; + if (td->val) { + *td->val = td->ival + time * td->factor; + if (td->ext->size && *td->val < *td->ext->size) *td->val = *td->ext->size; + if (td->ext->quat && *td->val > *td->ext->quat) *td->val = *td->ext->quat; + } } } @@ -7800,14 +7985,13 @@ static void initMirror(TransInfo *t) initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; } } static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float size[3], mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -7825,19 +8009,22 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } BLI_snprintf(str, sizeof(str), IFACE_("Mirror%s"), t->con.text); - for (i = 0, td = t->data; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - ElementResize(t, td, mat); + ElementResize(t, tc, td, mat); + } } recalcData(t); @@ -7849,14 +8036,17 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); - for (i = 0, td = t->data; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - ElementResize(t, td, mat); + ElementResize(t, tc, td, mat); + } } recalcData(t); @@ -7887,42 +8077,43 @@ static void initAlign(TransInfo *t) static void applyAlign(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float center[3]; int i; - /* saving original center */ - copy_v3_v3(center, t->center); - for (i = 0; i < t->total; i++, td++) { - float mat[3][3], invmat[3][3]; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* saving original center */ + copy_v3_v3(center, tc->center_local); + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float mat[3][3], invmat[3][3]; - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - /* around local centers */ - if (t->flag & (T_OBJECT | T_POSE)) { - copy_v3_v3(t->center, td->center); - } - else { - if (t->settings->selectmode & SCE_SELECT_FACE) { - copy_v3_v3(t->center, td->center); + /* around local centers */ + if (t->flag & (T_OBJECT | T_POSE)) { + copy_v3_v3(tc->center_local, td->center); + } + else { + if (t->settings->selectmode & SCE_SELECT_FACE) { + copy_v3_v3(tc->center_local, td->center); + } } - } - invert_m3_m3(invmat, td->axismtx); + invert_m3_m3(invmat, td->axismtx); - mul_m3_m3m3(mat, t->spacemtx, invmat); + mul_m3_m3m3(mat, t->spacemtx, invmat); - ElementRotation(t, td, mat, t->around); + ElementRotation(t, tc, td, mat, t->around); + } + /* restoring original center */ + copy_v3_v3(tc->center_local, center); } - /* restoring original center */ - copy_v3_v3(t->center, center); - recalcData(t); ED_area_headerprint(t->sa, IFACE_("Align")); @@ -7983,17 +8174,19 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA static void applySeqSlideValue(TransInfo *t, const float val[2]) { - TransData *td = t->data; int i; - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); + madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); + } } } @@ -8006,7 +8199,7 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; float tvec[3]; - t->con.applyVec(t, NULL, t->values, tvec, pvec); + t->con.applyVec(t, NULL, NULL, t->values, tvec, pvec); copy_v3_v3(t->values, tvec); } else { @@ -8226,8 +8419,6 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeTranslateValue(TransInfo *t) { - TransData *td = t->data; - TransData2D *td2d = t->data2d; Scene *scene = t->scene; int i; @@ -8236,46 +8427,50 @@ static void applyTimeTranslateValue(TransInfo *t) float deltax, val /* , valprev */; - /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ - for (i = 0; i < t->total; i++, td++, td2d++) { - /* it is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from - * (this is only valid when not in NLA) - */ - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + TransData2D *td2d = tc->data_2d; + /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ + for (i = 0; i < tc->data_len; i++, td++, td2d++) { + /* it is assumed that td->extra is a pointer to the AnimData, + * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) + */ + AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - /* valprev = *td->val; */ /* UNUSED */ + /* valprev = *td->val; */ /* UNUSED */ - /* check if any need to apply nla-mapping */ - if (adt && (t->spacetype != SPACE_SEQ)) { - deltax = t->values[0]; + /* check if any need to apply nla-mapping */ + if (adt && (t->spacetype != SPACE_SEQ)) { + deltax = t->values[0]; - if (autosnap == SACTSNAP_TSTEP) { - deltax = (float)(floor(((double)deltax / secf) + 0.5) * secf); - } - else if (autosnap == SACTSNAP_STEP) { - deltax = floorf(deltax + 0.5f); + if (autosnap == SACTSNAP_TSTEP) { + deltax = (float)(floor(((double)deltax / secf) + 0.5) * secf); + } + else if (autosnap == SACTSNAP_STEP) { + deltax = floorf(deltax + 0.5f); + } + + val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP); + val += deltax * td->factor; + *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); } + else { + deltax = val = t->values[0]; - val = BKE_nla_tweakedit_remap(adt, td->ival, NLATIME_CONVERT_MAP); - val += deltax * td->factor; - *(td->val) = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_UNMAP); - } - else { - deltax = val = t->values[0]; + if (autosnap == SACTSNAP_TSTEP) { + val = (float)(floor(((double)deltax / secf) + 0.5) * secf); + } + else if (autosnap == SACTSNAP_STEP) { + val = floorf(val + 0.5f); + } - if (autosnap == SACTSNAP_TSTEP) { - val = (float)(floor(((double)deltax / secf) + 0.5) * secf); - } - else if (autosnap == SACTSNAP_STEP) { - val = floorf(val + 0.5f); + *(td->val) = td->ival + val; } - *(td->val) = td->ival + val; + /* apply nearest snapping */ + doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } - - /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } } @@ -8342,18 +8537,19 @@ static void initTimeSlide(TransInfo *t) float min = 999999999.0f, max = -999999999.0f; int i; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; + float val = *(td->val); - TransData *td = t->data; - for (i = 0; i < t->total; i++, td++) { - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - float val = *(td->val); - - /* strip/action time to global (mapped) time */ - if (adt) - val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP); + /* strip/action time to global (mapped) time */ + if (adt) + val = BKE_nla_tweakedit_remap(adt, val, NLATIME_CONVERT_MAP); - if (min > val) min = val; - if (max < val) max = val; + if (min > val) min = val; + if (max < val) max = val; + } } if (min == max) { @@ -8406,7 +8602,6 @@ static void headerTimeSlide(TransInfo *t, const float sval, char str[UI_MAX_DRAW static void applyTimeSlideValue(TransInfo *t, float sval) { - TransData *td = t->data; int i; const float *range = t->custom.mode.data; float minx = range[0]; @@ -8421,44 +8616,47 @@ static void applyTimeSlideValue(TransInfo *t, float sval) } /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ - for (i = 0; i < t->total; i++, td++) { - /* it is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from - * (this is only valid when not in NLA) - */ - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - float cval = t->values[0]; - - /* only apply to data if in range */ - if ((sval > minx) && (sval < maxx)) { - float cvalc = CLAMPIS(cval, minx, maxx); - float ival = td->ival; - float timefac; - - /* NLA mapping magic here works as follows: - * - "ival" goes from strip time to global time - * - calculation is performed into td->val in global time - * (since sval and min/max are all in global time) - * - "td->val" then gets put back into strip time + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + /* it is assumed that td->extra is a pointer to the AnimData, + * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) */ - if (adt) { - /* strip to global */ - ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP); - } + AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; + float cval = t->values[0]; + + /* only apply to data if in range */ + if ((sval > minx) && (sval < maxx)) { + float cvalc = CLAMPIS(cval, minx, maxx); + float ival = td->ival; + float timefac; + + /* NLA mapping magic here works as follows: + * - "ival" goes from strip time to global time + * - calculation is performed into td->val in global time + * (since sval and min/max are all in global time) + * - "td->val" then gets put back into strip time + */ + if (adt) { + /* strip to global */ + ival = BKE_nla_tweakedit_remap(adt, ival, NLATIME_CONVERT_MAP); + } - /* left half? */ - if (ival < sval) { - timefac = (sval - ival) / (sval - minx); - *(td->val) = cvalc - timefac * (cvalc - minx); - } - else { - timefac = (ival - sval) / (maxx - sval); - *(td->val) = cvalc + timefac * (maxx - cvalc); - } + /* left half? */ + if (ival < sval) { + timefac = (sval - ival) / (sval - minx); + *(td->val) = cvalc - timefac * (cvalc - minx); + } + else { + timefac = (ival - sval) / (maxx - sval); + *(td->val) = cvalc + timefac * (maxx - cvalc); + } - if (adt) { - /* global to strip */ - *(td->val) = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_UNMAP); + if (adt) { + /* global to strip */ + *(td->val) = BKE_nla_tweakedit_remap(adt, *(td->val), NLATIME_CONVERT_UNMAP); + } } } } @@ -8519,8 +8717,8 @@ static void initTimeScale(TransInfo *t) /* recalculate center2d to use CFRA and mouse Y, since that's * what is used in time scale */ if ((t->flag & T_OVERRIDE_CENTER) == 0) { - t->center[0] = t->scene->r.cfra; - projectFloatView(t, t->center, center); + t->center_global[0] = t->scene->r.cfra; + projectFloatView(t, t->center_global, center); center[1] = t->mouse.imval[1]; } @@ -8561,39 +8759,40 @@ static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeScaleValue(TransInfo *t) { Scene *scene = t->scene; - TransData *td = t->data; - TransData2D *td2d = t->data2d; int i; const short autosnap = getAnimEdit_SnapMode(t); const double secf = FPS; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + TransData2D *td2d = tc->data_2d; + for (i = 0; i < tc->data_len; i++, td++, td2d++) { + /* it is assumed that td->extra is a pointer to the AnimData, + * whose active action is where this keyframe comes from + * (this is only valid when not in NLA) + */ + AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; + float startx = CFRA; + float fac = t->values[0]; - for (i = 0; i < t->total; i++, td++, td2d++) { - /* it is assumed that td->extra is a pointer to the AnimData, - * whose active action is where this keyframe comes from - * (this is only valid when not in NLA) - */ - AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; - float startx = CFRA; - float fac = t->values[0]; - - if (autosnap == SACTSNAP_TSTEP) { - fac = (float)(floor((double)fac / secf + 0.5) * secf); - } - else if (autosnap == SACTSNAP_STEP) { - fac = floorf(fac + 0.5f); - } + if (autosnap == SACTSNAP_TSTEP) { + fac = (float)(floor((double)fac / secf + 0.5) * secf); + } + else if (autosnap == SACTSNAP_STEP) { + fac = floorf(fac + 0.5f); + } - /* check if any need to apply nla-mapping */ - if (adt) - startx = BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP); + /* check if any need to apply nla-mapping */ + if (adt) + startx = BKE_nla_tweakedit_remap(adt, startx, NLATIME_CONVERT_UNMAP); - /* now, calculate the new value */ - *(td->val) = ((td->ival - startx) * fac) + startx; + /* now, calculate the new value */ + *(td->val) = ((td->ival - startx) * fac) + startx; - /* apply nearest snapping */ - doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); + /* apply nearest snapping */ + doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); + } } } @@ -8622,7 +8821,7 @@ bool checkUseAxisMatrix(TransInfo *t) /* currently only checks for editmode */ if (t->flag & T_EDIT) { if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) + (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { /* not all editmode supports axis-matrix */ return true; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index b1b1dd36bf0..419f400df53 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -37,11 +37,17 @@ #include "ED_numinput.h" #include "ED_view3d.h" +#include "RE_engine.h" + #include "DNA_listBase.h" +#include "DEG_depsgraph.h" + /* ************************** Types ***************************** */ +struct Depsgraph; struct TransInfo; +struct TransDataContainer; struct TransData; struct TransformOrientation; struct TransSnap; @@ -50,6 +56,7 @@ struct Object; struct View3D; struct ScrArea; struct Scene; +struct ViewLayer; struct bConstraint; struct wmKeyMap; struct wmKeyConfig; @@ -59,8 +66,11 @@ struct wmTimer; struct ARegion; struct ReportList; struct EditBone; +struct RenderEngineType; struct SnapObjectContext; +#include "DNA_object_enums.h" + /* transinfo->redraw */ typedef enum { TREDRAW_NOTHING = 0, @@ -100,7 +110,7 @@ typedef struct TransSnap { * \note Return value can be anything, * where the smallest absolute value defines whats closest. */ - float (*distance)(struct TransInfo *, const float p1[3], const float p2[3]); + float (*distance)(struct TransInfo *t, const float p1[3], const float p2[3]); /** * Re-usable snap context data. @@ -118,14 +128,16 @@ typedef struct TransCon { /* the one in TransInfo is not garanty to stay the same (Rotates change it) */ int mode; /* Mode flags of the Constraint */ void (*drawExtra)(struct TransInfo *t); + + /* Note: if 'tc' is NULL, 'td' must also be NULL. */ /* For constraints that needs to draw differently from the other * uses this instead of the generic draw function */ - void (*applyVec)(struct TransInfo *t, struct TransData *td, const float in[3], float out[3], float pvec[3]); + void (*applyVec)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, const float in[3], float out[3], float pvec[3]); /* Apply function pointer for linear vectorial transformation */ /* The last three parameters are pointers to the in/out/printable vectors */ - void (*applySize)(struct TransInfo *t, struct TransData *td, float smat[3][3]); + void (*applySize)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float smat[3][3]); /* Apply function pointer for size transformation */ - void (*applyRot)(struct TransInfo *t, struct TransData *td, float vec[3], float *angle); + void (*applyRot)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float vec[3], float *angle); /* Apply function pointer for rotation transformation */ } TransCon; @@ -261,10 +273,6 @@ typedef struct EdgeSlideData { SlideOrigData orig_data; - float perc; - - bool use_even; - bool flipped; int curr_sv_index; @@ -272,6 +280,12 @@ typedef struct EdgeSlideData { int curr_side_unclamp; } EdgeSlideData; +typedef struct EdgeSlideParams { + float perc; + + bool use_even; + bool flipped; +} EdgeSlideParams; typedef struct TransDataVertSlideVert { /* TransDataGenericSlideVert */ @@ -293,17 +307,19 @@ typedef struct VertSlideData { SlideOrigData orig_data; - float perc; - - bool use_even; - bool flipped; - int curr_sv_index; /* result of ED_view3d_ob_project_mat_get */ float proj_mat[4][4]; } VertSlideData; +typedef struct VertSlideParams { + float perc; + + bool use_even; + bool flipped; +} VertSlideParams; + typedef struct BoneInitData { struct EditBone *bone; float tail[3]; @@ -364,16 +380,80 @@ typedef struct MouseInput { typedef struct TransCustomData { void *data; - void (*free_cb)(struct TransInfo *, struct TransCustomData *); + void (*free_cb)(struct TransInfo *, struct TransDataContainer *tc, struct TransCustomData *custom_data); unsigned int use_free : 1; } TransCustomData; typedef struct TransCenterData { - float local[3], global[3]; + float global[3]; unsigned int is_set : 1; } TransCenterData; +/** + * Rule of thumb for choosing between mode/type: + * - If transform mode uses the data, assign to `mode` + * (typically in transform.c). + * - If conversion uses the data as an extension to the #TransData, assign to `type` + * (typically in transform_conversion.c). + */ +typedef struct TransCustomDataContainer { + /** Owned by the mode (grab, scale, bend... ).*/ + union { + TransCustomData mode, first_elem; + }; + TransCustomData type; +} TransCustomDataContainer; +#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(TransCustomDataContainer) / sizeof(TransCustomData)) + +typedef struct TransDataContainer { + /** + * Use for cases we care about the active, eg: active vert of active mesh. + * if set this will _always_ be the first item in the array. + */ + bool is_active; + + /** Transformed data (array). */ + TransData *data; + /** Total number of transformed data. */ + int data_len; + + /** Transformed data extension (array). */ + TransDataExtension *data_ext; + /** Transformed data for 2d (array). */ + TransData2D *data_2d; + + struct Object *obedit; + + /** + * Use when #T_LOCAL_MATRIX is set. + * Typically: 'obedit->obmat' or 'poseobj->obmat', but may be used elsewhere too. + */ + bool use_local_mat; + float mat[4][4]; + float imat[4][4]; + /** 3x3 copies of matrices above. */ + float mat3[3][3]; + float imat3[3][3]; + + /** Normalized 'mat3' */ + float mat3_unit[3][3]; + + /** if 't->flag & T_POSE', this denotes pose object */ + struct Object *poseobj; + + /** Center of transformation (in local-space), Calculated from #TransInfo.center_global. */ + float center_local[3]; + + TransCustomDataContainer custom; +} TransDataContainer; + typedef struct TransInfo { + TransDataContainer *data_container; + int data_container_len; + /** Combine length of all #TransDataContainer.data_len + * Use to check if nothing is selected or if we have a single selection. */ + int data_len_all; + int mode; /* current mode */ int flag; /* generic flags for special behaviors */ int modifiers; /* special modifiers, by function, not key */ @@ -384,10 +464,6 @@ typedef struct TransInfo { /* transform function pointer */ eRedrawFlag (*handleEvent)(struct TransInfo *, const struct wmEvent *); /* event handler function pointer RETURN 1 if redraw is needed */ - int total; /* total number of transformed data */ - TransData *data; /* transformed data (array) */ - TransDataExtension *ext; /* transformed data extension (array) */ - TransData2D *data2d; /* transformed data for 2d (array) */ TransCon con; /* transformed constraint */ TransSnap tsnap; NumInput num; /* numerical input */ @@ -397,7 +473,6 @@ typedef struct TransInfo { char proptext[20]; /* proportional falloff text */ float aspect[3]; /* spaces using non 1:1 aspect, (uv's, f-curve, movie-clip... etc) * use for conversion and snapping. */ - float center[3]; /* center of transformation (in local-space) */ float center_global[3]; /* center of transformation (in global-space) */ float center2d[2]; /* center in screen coordinates */ /* Lazy initialize center data for when we need other center values. @@ -415,7 +490,8 @@ typedef struct TransInfo { short persp; short around; char spacetype; /* spacetype where transforming is */ - char helpline; /* helpline modes (not to be confused with hotline) */ + char helpline; /* Choice of custom cursor with or without a help line from the manipulator to the mouse position. */ + short obedit_type; /* Avoid looking inside TransDataContainer obedit. */ float vec[3]; /* translation, to show for widget */ float mat[3][3]; /* rot/rescale, to show for widget */ @@ -423,36 +499,19 @@ typedef struct TransInfo { float spacemtx[3][3]; /* orientation matrix of the current space */ char spacename[64]; /* name of the current space, MAX_NAME */ - struct Object *poseobj; /* if t->flag & T_POSE, this denotes pose object */ - - /** - * Rule of thumb for choosing between mode/type: - * - If transform mode uses the data, assign to `mode` - * (typically in transform.c). - * - If conversion uses the data as an extension to the #TransData, assign to `type` - * (typically in transform_conversion.c). - */ - struct { - /* owned by the mode (grab, scale, bend... )*/ - union { - TransCustomData mode, first_elem; - }; - /* owned by the type (mesh, armature, nla...) */ - TransCustomData type; - } custom; -#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(((TransInfo *)NULL)->custom) / sizeof(TransCustomData)) - /*************** NEW STUFF *********************/ short launch_event; /* event type used to launch transform */ short current_orientation; - short twtype; /* backup from view3d, to restore on end */ + TransformOrientation *custom_orientation; /* this gets used when current_orientation is V3D_MANIP_CUSTOM */ + short twflag; /* backup from view3d, to restore on end */ short prop_mode; short mirror; float values[4]; + float values_modal_offset[4]; /* Offset applied ontop of modal input. */ float auto_values[4]; float axis[3]; float axis_orig[3]; /* TransCon can change 'axis', store the original value here */ @@ -463,19 +522,22 @@ typedef struct TransInfo { struct bContext *context; /* Only valid (non null) during an operator called function. */ struct ScrArea *sa; struct ARegion *ar; + struct Depsgraph *depsgraph; struct Scene *scene; + struct ViewLayer *view_layer; struct ToolSettings *settings; struct wmTimer *animtimer; struct wmKeyMap *keymap; /* so we can do lookups for header text */ struct ReportList *reports; /* assign from the operator, or can be NULL */ int mval[2]; /* current mouse position */ float zfac; /* use for 3d view */ - struct Object *obedit; - float obedit_mat[3][3]; /* normalized editmode matrix (T_EDIT only) */ void *draw_handle_apply; void *draw_handle_view; void *draw_handle_pixel; void *draw_handle_cursor; + + /** Typically for mode settings. */ + TransCustomDataContainer custom; } TransInfo; @@ -489,15 +551,20 @@ typedef struct TransInfo { /* transinfo->flag */ #define T_OBJECT (1 << 0) +/** \note We could remove 'T_EDIT' and use 'obedit_type', for now ensure they're in sync. */ #define T_EDIT (1 << 1) #define T_POSE (1 << 2) #define T_TEXTURE (1 << 3) /* transforming the camera while in camera view */ #define T_CAMERA (1 << 4) + /* transforming the 3D cursor. */ +#define T_CURSOR (1 << 5) // trans on points, having no rotation/scale #define T_POINTS (1 << 6) - // for manipulator exceptions, like scaling using center point, drawing help lines -#define T_USES_MANIPULATOR (1 << 7) +/** + * Apply matrix #TransDataContainer.matrix, this avoids having to have duplicate check all over + * that happen to apply to spesiifc modes (edit & pose for eg). */ +#define T_LOCAL_MATRIX (1 << 7) /* restrictions flags */ #define T_ALL_RESTRICTIONS ((1 << 8)|(1 << 9)|(1 << 10)) @@ -536,6 +603,8 @@ typedef struct TransInfo { /** #TransInfo.center has been set, don't change it. */ #define T_OVERRIDE_CENTER (1 << 25) +#define T_MODAL_CURSOR_SET (1 << 26) + /* TransInfo->modifiers */ #define MOD_CONSTRAINT_SELECT 0x01 #define MOD_PRECISION 0x02 @@ -635,10 +704,14 @@ void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); void flushTransPaintCurve(TransInfo *t); -void restoreBones(TransInfo *t); +void restoreBones(TransDataContainer *tc); -/*********************** exported from transform_manipulator.c ********** */ -bool gimbal_axis(struct Object *ob, float gmat[3][3]); /* return 0 when no gimbal for selection */ +/*********************** transform_manipulator.c ********** */ + +#define MANIPULATOR_AXIS_LINE_WIDTH 2.0f + +/* return 0 when no gimbal for selection */ +bool gimbal_axis(struct Object *ob, float gmat[3][3]); /*********************** TransData Creation and General Handling *********** */ void createTransData(struct bContext *C, TransInfo *t); @@ -649,13 +722,13 @@ int special_transform_moving(TransInfo *t); void transform_autoik_update(TransInfo *t, short mode); bool transdata_check_local_islands(TransInfo *t, short around); -int count_set_pose_transflags(int *out_mode, short around, struct Object *ob); +int count_set_pose_transflags(struct Object *ob, const int mode, const short around, bool has_translate_rotate[2]); /* auto-keying stuff used by special_aftertrans_update */ void autokeyframe_ob_cb_func( - struct bContext *C, struct Scene *scene, struct View3D *v3d, struct Object *ob, int tmode); + struct bContext *C, struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob, int tmode); void autokeyframe_pose_cb_func( - struct bContext *C, struct Scene *scene, struct View3D *v3d, struct Object *ob, int tmode, short targetless_ik); + struct bContext *C, struct Scene *scene, struct Object *ob, int tmode, short targetless_ik); /*********************** Constraints *****************************/ @@ -743,7 +816,9 @@ void setInputPostFct(MouseInput *mi, void (*post)(struct TransInfo *t, float val /*********************** Generics ********************************/ +void initTransDataContainers_FromObjectData(TransInfo *t); void initTransInfo(struct bContext *C, TransInfo *t, struct wmOperator *op, const struct wmEvent *event); +void freeTransCustomDataForMode(TransInfo *t); void postTrans(struct bContext *C, TransInfo *t); void resetTransModal(TransInfo *t); void resetTransRestrictions(TransInfo *t); @@ -758,9 +833,7 @@ void restoreTransObjects(TransInfo *t); void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); -void calculateCenterGlobal( - TransInfo *t, const float center_local[3], - float r_center_global[3]); +void calculateCenterLocal(TransInfo *t, const float center_global[3]); const TransCenterData *transformCenter_from_type(TransInfo *t, int around); void calculateCenter(TransInfo *t); @@ -789,7 +862,7 @@ bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const floa struct TransformOrientation *addMatrixSpace(struct bContext *C, float mat[3][3], const char *name, const bool overwrite); -bool applyTransformOrientation(const struct bContext *C, float mat[3][3], char r_name[64], int index); +bool applyTransformOrientation(const struct TransformOrientation *ts, float r_mat[3][3], char r_name[64]); #define ORIENTATION_NONE 0 #define ORIENTATION_NORMAL 1 @@ -801,19 +874,36 @@ int getTransformOrientation_ex(const struct bContext *C, float normal[3], float int getTransformOrientation(const struct bContext *C, float normal[3], float plane[3]); void freeEdgeSlideTempFaces(EdgeSlideData *sld); -void freeEdgeSlideVerts(TransInfo *t, TransCustomData *custom_data); +void freeEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectEdgeSlideData(TransInfo *t, bool is_final); void freeVertSlideTempFaces(VertSlideData *sld); -void freeVertSlideVerts(TransInfo *t, TransCustomData *custom_data); +void freeVertSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectVertSlideData(TransInfo *t, bool is_final); /* TODO. transform_queries.c */ bool checkUseAxisMatrix(TransInfo *t); -#define TRANSFORM_DIST_MAX_PX 1000.0f #define TRANSFORM_SNAP_MAX_PX 100.0f #define TRANSFORM_DIST_INVALID -FLT_MAX +/* Temp macros. */ + +/* This is to be replaced, just to get things compiling early on. */ +#define TRANS_DATA_CONTAINER_FIRST_EVIL(t) (&(t)->data_container[0]) +#define TRANS_DATA_CONTAINER_FIRST_OK(t) (&(t)->data_container[0]) +/* For cases we _know_ there is only one handle. */ +#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t) (BLI_assert((t)->data_container_len == 1), (&(t)->data_container[0])) + +#define FOREACH_TRANS_DATA_CONTAINER(t, th) \ + for (TransDataContainer *tc = t->data_container, *tc_end = t->data_container + t->data_container_len; \ + th != tc_end; \ + th++) + +#define FOREACH_TRANS_DATA_CONTAINER_INDEX(t, th, i) \ + for (TransDataContainer *tc = ((i = 0), t->data_container), *tc_end = t->data_container + t->data_container_len; \ + th != tc_end; \ + th++, i++) + #endif diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 719a6e11ce7..3c70eaae2d3 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -40,9 +40,11 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" -#include "BIF_gl.h" #include "BIF_glutil.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" + #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_string.h" @@ -333,15 +335,15 @@ static void planeProjection(const TransInfo *t, const float in[3], float out[3]) * */ -static void applyAxisConstraintVec(TransInfo *t, TransData *td, const float in[3], float out[3], float pvec[3]) +static void applyAxisConstraintVec( + TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, const float in[3], float out[3], float pvec[3]) { copy_v3_v3(out, in); if (!td && t->con.mode & CON_APPLY) { mul_m3_v3(t->con.pmtx, out); // With snap, a projection is alright, no need to correct for view alignment - if (!(!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) && activeSnap(t))) { - + if (!validSnap(t)) { const int dims = getConstraintSpaceDimension(t); if (dims == 2) { if (!is_zero_v3(out)) { @@ -380,7 +382,8 @@ static void applyAxisConstraintVec(TransInfo *t, TransData *td, const float in[3 * Further down, that vector is mapped to each data's space. */ -static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in[3], float out[3], float pvec[3]) +static void applyObjectConstraintVec( + TransInfo *t, TransDataContainer *tc, TransData *td, const float in[3], float out[3], float pvec[3]) { copy_v3_v3(out, in); if (t->con.mode & CON_APPLY) { @@ -428,7 +431,7 @@ static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in mul_m3_v3(td->axismtx, out); if (t->flag & T_EDIT) { - mul_m3_v3(t->obedit_mat, out); + mul_m3_v3(tc->mat3_unit, out); } } } @@ -438,7 +441,8 @@ static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in * Generic callback for constant spatial constraints applied to resize motion */ -static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +static void applyAxisConstraintSize( + TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float smat[3][3]) { if (!td && t->con.mode & CON_APPLY) { float tmat[3][3]; @@ -462,7 +466,8 @@ static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3 * Callback for object based spatial constraints applied to resize motion */ -static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +static void applyObjectConstraintSize( + TransInfo *t, TransDataContainer *tc, TransData *td, float smat[3][3]) { if (td && t->con.mode & CON_APPLY) { float tmat[3][3]; @@ -482,7 +487,7 @@ static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3] mul_m3_m3m3(tmat, smat, imat); if (t->flag & T_EDIT) { - mul_m3_m3m3(smat, t->obedit_mat, smat); + mul_m3_m3m3(smat, tc->mat3_unit, smat); } mul_m3_m3m3(smat, td->axismtx, tmat); } @@ -502,7 +507,7 @@ static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3] * (ie: not doing counterclockwise rotations when the mouse moves clockwise). */ -static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +static void applyAxisConstraintRot(TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle) { if (!td && t->con.mode & CON_APPLY) { int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); @@ -544,7 +549,8 @@ static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], fl * (ie: not doing counterclockwise rotations when the mouse moves clockwise). */ -static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +static void applyObjectConstraintRot( + TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle) { if (t->con.mode & CON_APPLY) { int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); @@ -553,11 +559,11 @@ static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], /* on setup call, use first object */ if (td == NULL) { - td = t->data; + td = tc->data; } if (t->flag & T_EDIT) { - mul_m3_m3m3(tmp_axismtx, t->obedit_mat, td->axismtx); + mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx); axismtx = tmp_axismtx; } else { @@ -607,20 +613,21 @@ void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) /* applies individual td->axismtx constraints */ void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[]) { - if (t->total == 1) { + TransDataContainer *tc = t->data_container; + if (t->data_len_all == 1) { float axismtx[3][3]; if (t->flag & T_EDIT) { - mul_m3_m3m3(axismtx, t->obedit_mat, t->data->axismtx); + mul_m3_m3m3(axismtx, tc->mat3_unit, tc->data->axismtx); } else { - copy_m3_m3(axismtx, t->data->axismtx); + copy_m3_m3(axismtx, tc->data->axismtx); } setConstraint(t, axismtx, mode, text); } else { BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1); - copy_m3_m3(t->con.mtx, t->data->axismtx); + copy_m3_m3(t->con.mtx, tc->data->axismtx); t->con.mode = mode; getConstraintMatrix(t); @@ -638,7 +645,9 @@ void setLocalConstraint(TransInfo *t, int mode, const char text[]) { /* edit-mode now allows local transforms too */ if (t->flag & T_EDIT) { - setConstraint(t, t->obedit_mat, mode, text); + /* Use the active (first) edit object. */ + TransDataContainer *tc = t->data_container; + setConstraint(t, tc->mat3_unit, mode, text); } else { setAxisMatrixConstraint(t, mode, text); @@ -653,7 +662,7 @@ void setLocalConstraint(TransInfo *t, int mode, const char text[]) */ void setUserConstraint(TransInfo *t, short orientation, int mode, const char ftext[]) { - char text[40]; + char text[256]; switch (orientation) { case V3D_MANIP_GLOBAL: @@ -681,14 +690,23 @@ void setUserConstraint(TransInfo *t, short orientation, int mode, const char fte BLI_snprintf(text, sizeof(text), ftext, IFACE_("view")); setConstraint(t, t->spacemtx, mode, text); break; + case V3D_MANIP_CURSOR: + BLI_snprintf(text, sizeof(text), ftext, IFACE_("cursor")); + setConstraint(t, t->spacemtx, mode, text); + break; case V3D_MANIP_GIMBAL: BLI_snprintf(text, sizeof(text), ftext, IFACE_("gimbal")); setConstraint(t, t->spacemtx, mode, text); break; - default: /* V3D_MANIP_CUSTOM */ - BLI_snprintf(text, sizeof(text), ftext, t->spacename); + case V3D_MANIP_CUSTOM: + { + char orientation_str[128]; + BLI_snprintf(orientation_str, sizeof(orientation_str), "%s \"%s\"", + IFACE_("custom orientation"), t->custom_orientation->name); + BLI_snprintf(text, sizeof(text), ftext, orientation_str); setConstraint(t, t->spacemtx, mode, text); break; + } } t->con.orientation = orientation; @@ -706,8 +724,6 @@ void drawConstraint(TransInfo *t) return; if (!(tc->mode & CON_APPLY)) return; - if (t->flag & T_USES_MANIPULATOR) - return; if (t->flag & T_NO_CONSTRAINT) return; @@ -717,7 +733,6 @@ void drawConstraint(TransInfo *t) else { if (tc->mode & CON_SELECT) { float vec[3]; - char col2[3] = {255, 255, 255}; int depth_test_enabled; convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1])); @@ -727,18 +742,29 @@ void drawConstraint(TransInfo *t) drawLine(t, t->center_global, tc->mtx[1], 'Y', 0); drawLine(t, t->center_global, tc->mtx[2], 'Z', 0); - glColor3ubv((GLubyte *)col2); - depth_test_enabled = glIsEnabled(GL_DEPTH_TEST); if (depth_test_enabled) glDisable(GL_DEPTH_TEST); - setlinestyle(1); - glBegin(GL_LINES); - glVertex3fv(t->center_global); - glVertex3fv(vec); - glEnd(); - setlinestyle(0); + const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniform1i("num_colors", 0); /* "simple" mode */ + immUniformColor4f(1.0f, 1.0f, 1.0f, 1.0f); + immUniform1f("dash_width", 2.0f); + immUniform1f("dash_factor", 0.5f); + + immBegin(GWN_PRIM_LINES, 2); + immVertex3fv(shdr_pos, t->center_global); + immVertex3fv(shdr_pos, vec); + immEnd(); + + immUnbindProgram(); if (depth_test_enabled) glEnable(GL_DEPTH_TEST); @@ -764,8 +790,6 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) float tmat[4][4], imat[4][4]; int depth_test_enabled; - UI_ThemeColor(TH_GRID); - if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) { copy_m4_m4(tmat, rv3d->viewmat); invert_m4_m4(imat, tmat); @@ -775,13 +799,13 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) unit_m4(imat); } - glPushMatrix(); + gpuPushMatrix(); if (t->spacetype == SPACE_VIEW3D) { /* pass */ } else if (t->spacetype == SPACE_IMAGE) { - glScalef(1.0f / t->aspect[0], 1.0f / t->aspect[1], 1.0f); + gpuScale2f(1.0f / t->aspect[0], 1.0f / t->aspect[1]); } else if (ELEM(t->spacetype, SPACE_IPO, SPACE_ACTION)) { /* only scale y */ @@ -791,21 +815,28 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) float ysize = BLI_rctf_size_y(datamask); float xmask = BLI_rcti_size_x(mask); float ymask = BLI_rcti_size_y(mask); - glScalef(1.0f, (ysize / xsize) * (xmask / ymask), 1.0f); + gpuScale2f(1.0f, (ysize / xsize) * (xmask / ymask)); } depth_test_enabled = glIsEnabled(GL_DEPTH_TEST); if (depth_test_enabled) glDisable(GL_DEPTH_TEST); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformThemeColor(TH_GRID); + set_inverted_drawing(1); - drawcircball(GL_LINE_LOOP, t->center_global, t->prop_size, imat); + imm_drawcircball(t->center_global, t->prop_size, imat, pos); set_inverted_drawing(0); + immUnbindProgram(); + if (depth_test_enabled) glEnable(GL_DEPTH_TEST); - glPopMatrix(); + gpuPopMatrix(); } } @@ -818,57 +849,59 @@ static void drawObjectConstraint(TransInfo *t) * Without drawing the first light, users have little clue what they are doing. */ short options = DRAWLIGHT; - TransData *td = t->data; int i; float tmp_axismtx[3][3]; - for (i = 0; i < t->total; i++, td++) { - float co[3]; - float (*axismtx)[3]; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float co[3]; + float (*axismtx)[3]; - if (t->flag & T_PROP_EDIT) { - /* we're sorted, so skip the rest */ - if (td->factor == 0.0f) { - break; + if (t->flag & T_PROP_EDIT) { + /* we're sorted, so skip the rest */ + if (td->factor == 0.0f) { + break; + } } - } - if (t->options & CTX_GPENCIL_STROKES) { - /* only draw a constraint line for one point, otherwise we can't see anything */ - if ((options & DRAWLIGHT) == 0) { - break; + if (t->options & CTX_GPENCIL_STROKES) { + /* only draw a constraint line for one point, otherwise we can't see anything */ + if ((options & DRAWLIGHT) == 0) { + break; + } } - } - if (t->flag & T_OBJECT) { - copy_v3_v3(co, td->ob->obmat[3]); - axismtx = td->axismtx; - } - else if (t->flag & T_EDIT) { - mul_v3_m4v3(co, t->obedit->obmat, td->center); + if (t->flag & T_OBJECT) { + copy_v3_v3(co, td->ob->obmat[3]); + axismtx = td->axismtx; + } + else if (t->flag & T_EDIT) { + mul_v3_m4v3(co, tc->mat, td->center); - mul_m3_m3m3(tmp_axismtx, t->obedit_mat, td->axismtx); - axismtx = tmp_axismtx; - } - else if (t->flag & T_POSE) { - mul_v3_m4v3(co, t->poseobj->obmat, td->center); - axismtx = td->axismtx; - } - else { - copy_v3_v3(co, td->center); - axismtx = td->axismtx; - } + mul_m3_m3m3(tmp_axismtx, tc->mat3_unit, td->axismtx); + axismtx = tmp_axismtx; + } + else if (t->flag & T_POSE) { + mul_v3_m4v3(co, tc->mat, td->center); + axismtx = td->axismtx; + } + else { + copy_v3_v3(co, td->center); + axismtx = td->axismtx; + } - if (t->con.mode & CON_AXIS0) { - drawLine(t, co, axismtx[0], 'X', options); - } - if (t->con.mode & CON_AXIS1) { - drawLine(t, co, axismtx[1], 'Y', options); - } - if (t->con.mode & CON_AXIS2) { - drawLine(t, co, axismtx[2], 'Z', options); + if (t->con.mode & CON_AXIS0) { + drawLine(t, co, axismtx[0], 'X', options); + } + if (t->con.mode & CON_AXIS1) { + drawLine(t, co, axismtx[1], 'Y', options); + } + if (t->con.mode & CON_AXIS2) { + drawLine(t, co, axismtx[2], 'Z', options); + } + options &= ~DRAWLIGHT; } - options &= ~DRAWLIGHT; } } diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index 9f2effde46a..6c608a0b5e4 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -68,10 +68,10 @@ #include "BKE_context.h" #include "BKE_crazyspace.h" #include "BKE_curve.h" -#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_layer.h" #include "BKE_key.h" #include "BKE_main.h" #include "BKE_mesh.h" @@ -118,6 +118,8 @@ #include "RNA_access.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "transform.h" #include "bmesh.h" @@ -128,7 +130,7 @@ */ static void transform_around_single_fallback(TransInfo *t) { - if ((t->total == 1) && + if ((t->data_len_all == 1) && (ELEM(t->around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEAN, V3D_AROUND_ACTIVE)) && (ELEM(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL))) { @@ -169,45 +171,50 @@ static int trans_data_compare_rdist(const void *a, const void *b) void sort_trans_data_dist(TransInfo *t) { - TransData *start = t->data; - int i; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *start = tc->data; + int i; - for (i = 0; i < t->total && start->flag & TD_SELECTED; i++) - start++; + for (i = 0; i < tc->data_len && start->flag & TD_SELECTED; i++) { + start++; + } - if (i < t->total) { - if (t->flag & T_PROP_CONNECTED) - qsort(start, t->total - i, sizeof(TransData), trans_data_compare_dist); - else - qsort(start, t->total - i, sizeof(TransData), trans_data_compare_rdist); + if (i < tc->data_len) { + if (t->flag & T_PROP_CONNECTED) + qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_dist); + else + qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_rdist); + } } } static void sort_trans_data(TransInfo *t) { - TransData *sel, *unsel; - TransData temp; - unsel = t->data; - sel = t->data; - sel += t->total - 1; - while (sel > unsel) { - while (unsel->flag & TD_SELECTED) { - unsel++; - if (unsel == sel) { - return; - } - } - while (!(sel->flag & TD_SELECTED)) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *sel, *unsel; + TransData temp; + unsel = tc->data; + sel = tc->data; + sel += tc->data_len - 1; + while (sel > unsel) { + while (unsel->flag & TD_SELECTED) { + unsel++; + if (unsel == sel) { + return; + } + } + while (!(sel->flag & TD_SELECTED)) { + sel--; + if (unsel == sel) { + return; + } + } + temp = *unsel; + *unsel = *sel; + *sel = temp; sel--; - if (unsel == sel) { - return; - } + unsel++; } - temp = *unsel; - *unsel = *sel; - *sel = temp; - sel--; - unsel++; } } @@ -215,7 +222,6 @@ static void sort_trans_data(TransInfo *t) * warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */ static void set_prop_dist(TransInfo *t, const bool with_dist) { - TransData *tob; int a; float _proj_vec[3]; @@ -232,49 +238,52 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } } - for (a = 0, tob = t->data; a < t->total; a++, tob++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *tob = tc->data; + for (a = 0; a < tc->data_len; a++, tob++) { - tob->rdist = 0.0f; // init, it was mallocced + tob->rdist = 0.0f; // init, it was mallocced - if ((tob->flag & TD_SELECTED) == 0) { - TransData *td; - int i; - float dist_sq, vec[3]; + if ((tob->flag & TD_SELECTED) == 0) { + TransData *td; + int i; + float dist_sq, vec[3]; - tob->rdist = -1.0f; // signal for next loop + tob->rdist = -1.0f; // signal for next loop - for (i = 0, td = t->data; i < t->total; i++, td++) { - if (td->flag & TD_SELECTED) { - if (use_island) { - sub_v3_v3v3(vec, tob->iloc, td->iloc); - } - else { - sub_v3_v3v3(vec, tob->center, td->center); - } - mul_m3_v3(tob->mtx, vec); + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->flag & TD_SELECTED) { + if (use_island) { + sub_v3_v3v3(vec, tob->iloc, td->iloc); + } + else { + sub_v3_v3v3(vec, tob->center, td->center); + } + mul_m3_v3(tob->mtx, vec); - if (proj_vec) { - float vec_p[3]; - project_v3_v3v3(vec_p, vec, proj_vec); - sub_v3_v3(vec, vec_p); - } + if (proj_vec) { + float vec_p[3]; + project_v3_v3v3(vec_p, vec, proj_vec); + sub_v3_v3(vec, vec_p); + } - dist_sq = len_squared_v3(vec); - if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { - tob->rdist = sqrtf(dist_sq); - if (use_island) { - copy_v3_v3(tob->center, td->center); - copy_m3_m3(tob->axismtx, td->axismtx); + dist_sq = len_squared_v3(vec); + if ((tob->rdist == -1.0f) || (dist_sq < SQUARE(tob->rdist))) { + tob->rdist = sqrtf(dist_sq); + if (use_island) { + copy_v3_v3(tob->center, td->center); + copy_m3_m3(tob->axismtx, td->axismtx); + } } } + else { + break; /* by definition transdata has selected items in beginning */ + } } - else { - break; /* by definition transdata has selected items in beginning */ + if (with_dist) { + tob->dist = tob->rdist; } } - if (with_dist) { - tob->dist = tob->rdist; - } } } } @@ -285,35 +294,37 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) static void createTransTexspace(TransInfo *t) { - Scene *scene = t->scene; + ViewLayer *view_layer = t->view_layer; TransData *td; Object *ob; ID *id; short *texflag; - ob = OBACT; + ob = OBACT(view_layer); if (ob == NULL) { // Shouldn't logically happen, but still... - t->total = 0; return; } id = ob->data; if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) { BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform"); - t->total = 0; return; } if (BKE_object_obdata_is_libdata(ob)) { BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform"); - t->total = 0; return; } - t->total = 1; - td = t->data = MEM_callocN(sizeof(TransData), "TransTexspace"); - td->ext = t->ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + + { + BLI_assert(t->data_container_len == 1); + TransDataContainer *tc = t->data_container; + tc->data_len = 1; + td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace"); + td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + } td->flag = TD_SELECTED; copy_v3_v3(td->center, ob->obmat[3]); @@ -334,76 +345,118 @@ static void createTransTexspace(TransInfo *t) copy_v3_v3(td->ext->isize, td->ext->size); } +static void createTransCursor3D(TransInfo *t) +{ + TransData *td; + + Scene *scene = t->scene; + View3D *v3d = ((t->spacetype == SPACE_VIEW3D) && (t->ar->regiontype == RGN_TYPE_WINDOW)) ? t->view : NULL; + View3DCursor *cursor = ED_view3d_cursor3d_get(scene, v3d); + + if ((cursor == &scene->cursor) && ID_IS_LINKED(scene)) { + BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform"); + return; + } + + { + BLI_assert(t->data_container_len == 1); + TransDataContainer *tc = t->data_container; + tc->data_len = 1; + td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace"); + td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + } + + td->flag = TD_SELECTED; + copy_v3_v3(td->center, cursor->location); + td->ob = NULL; + + unit_m3(td->mtx); + quat_to_mat3(td->axismtx, cursor->rotation); + normalize_m3(td->axismtx); + pseudoinverse_m3_m3(td->smtx, td->mtx, PSEUDOINVERSE_EPSILON); + + td->loc = cursor->location; + copy_v3_v3(td->iloc, cursor->location); + + td->ext->quat = cursor->rotation; + copy_qt_qt(td->ext->iquat, cursor->rotation); +} + /* ********************* edge (for crease) ***** */ static void createTransEdge(TransInfo *t) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - TransData *td = NULL; - BMEdge *eed; - BMIter iter; - float mtx[3][3], smtx[3][3]; - int count = 0, countsel = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - int cd_edge_float_offset; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) countsel++; - if (is_prop_edit) count++; + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + TransData *td = NULL; + BMEdge *eed; + BMIter iter; + float mtx[3][3], smtx[3][3]; + int count = 0, countsel = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + int cd_edge_float_offset; + + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) countsel++; + if (is_prop_edit) count++; + } } - } - if (countsel == 0) - return; + if (countsel == 0) { + tc->data_len = 0; + continue; + } - if (is_prop_edit) { - t->total = count; - } - else { - t->total = countsel; - } + if (is_prop_edit) { + tc->data_len = count; + } + else { + tc->data_len = countsel; + } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransCrease"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransCrease"); - copy_m3_m4(mtx, t->obedit->obmat); - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + copy_m3_m4(mtx, tc->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - /* create data we need */ - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_EDGE_BWEIGHT); - cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_BWEIGHT); - } - else { //if (t->mode == TFM_CREASE) { - BLI_assert(t->mode == TFM_CREASE); - BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_EDGE_CREASE); - cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_CREASE); - } + /* create data we need */ + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_BWEIGHT); + cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_BWEIGHT); + } + else { //if (t->mode == TFM_CREASE) { + BLI_assert(t->mode == TFM_CREASE); + BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_CREASE); + cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_CREASE); + } - BLI_assert(cd_edge_float_offset != -1); + BLI_assert(cd_edge_float_offset != -1); - BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { - if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || is_prop_edit)) { - float *fl_ptr; - /* need to set center for center calculations */ - mid_v3_v3v3(td->center, eed->v1->co, eed->v2->co); + BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) { + if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN) && (BM_elem_flag_test(eed, BM_ELEM_SELECT) || is_prop_edit)) { + float *fl_ptr; + /* need to set center for center calculations */ + mid_v3_v3v3(td->center, eed->v1->co, eed->v2->co); - td->loc = NULL; - if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) - td->flag = TD_SELECTED; - else - td->flag = 0; + td->loc = NULL; + if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) + td->flag = TD_SELECTED; + else + td->flag = 0; - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - td->ext = NULL; + td->ext = NULL; - fl_ptr = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_float_offset); - td->val = fl_ptr; - td->ival = *fl_ptr; + fl_ptr = BM_ELEM_CD_GET_VOID_P(eed, cd_edge_float_offset); + td->val = fl_ptr; + td->ival = *fl_ptr; - td++; + td++; + } } } } @@ -521,7 +574,7 @@ static short apply_targetless_ik(Object *ob) return apply; } -static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td) +static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td) { Bone *bone = pchan->bone; float pmat[3][3], omat[3][3]; @@ -632,7 +685,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr normalize_m3(td->axismtx); if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { - bArmature *arm = t->poseobj->data; + bArmature *arm = tc->poseobj->data; if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; @@ -695,12 +748,11 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) /* sets transform flags in the bones * returns total number of bones with BONE_TRANSFORM */ -int count_set_pose_transflags(int *out_mode, short around, Object *ob) +int count_set_pose_transflags(Object *ob, const int mode, short around, bool has_translate_rotate[2]) { bArmature *arm = ob->data; bPoseChannel *pchan; Bone *bone; - int mode = *out_mode; int total = 0; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { @@ -728,42 +780,34 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob) } } /* now count, and check if we have autoIK or have to switch from translate to rotate */ - bool has_translation = false, has_rotation = false; - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { bone = pchan->bone; if (bone->flag & BONE_TRANSFORM) { total++; - if (mode == TFM_TRANSLATION) { + if (has_translate_rotate != NULL) { if (has_targetless_ik(pchan) == NULL) { if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) { - if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) - has_translation = true; + if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM) { + has_translate_rotate[0] = true; + } } else { - if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) - has_translation = true; + if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC) { + has_translate_rotate[0] = true; + } + } + if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) { + has_translate_rotate[1] = true; } - if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT) - has_rotation = true; } - else - has_translation = true; + else { + has_translate_rotate[1] = true; + } } } } - /* if there are no translatable bones, do rotation */ - if (mode == TFM_TRANSLATION && !has_translation) { - if (has_rotation) { - *out_mode = TFM_ROTATION; - } - else { - *out_mode = TFM_RESIZE; - } - } - return total; } @@ -828,25 +872,25 @@ void transform_autoik_update(TransInfo *t, short mode) } } - /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ - if (ELEM(NULL, t->poseobj, t->poseobj->pose)) - return; - /* apply to all pose-channels */ bool changed = false; - for (pchan = t->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) { - changed |= pchan_autoik_adjust(pchan, *chainlen); - } -#ifdef WITH_LEGACY_DEPSGRAPH - if (!DEG_depsgraph_use_legacy()) -#endif - { - if (changed) { - /* TODO(sergey): Consider doing partial update only. */ - DAG_relations_tag_update(bmain); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ + if (ELEM(NULL, tc->poseobj, tc->poseobj->pose)) { + continue; + } + + for (pchan = tc->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) { + changed |= pchan_autoik_adjust(pchan, *chainlen); } } + + if (changed) { + /* TODO(sergey): Consider doing partial update only. */ + DEG_relations_tag_update(bmain); + } } /* frees temporal IKs */ @@ -855,9 +899,7 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob) bKinematicConstraint *data; bPoseChannel *pchan; bConstraint *con, *next; -#ifdef WITH_LEGACY_DEPSGRAPH - bool need_dependency_update = false; -#endif + bool relations_changed = false; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { /* clear all temporary lock flags */ @@ -871,10 +913,9 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob) if (con->type == CONSTRAINT_TYPE_KINEMATIC) { data = con->data; if (data->flag & CONSTRAINT_IK_TEMP) { + relations_changed = true; + /* iTaSC needs clear for removed constraints */ -#ifdef WITH_LEGACY_DEPSGRAPH - need_dependency_update = true; -#endif BIK_clear_data(ob->pose); BLI_remlink(&pchan->constraints, con); @@ -889,12 +930,9 @@ static void pose_grab_with_ik_clear(Main *bmain, Object *ob) } } -#ifdef WITH_LEGACY_DEPSGRAPH - if (!DEG_depsgraph_use_legacy() && need_dependency_update) -#endif - { + if (relations_changed) { /* TODO(sergey): Consider doing partial update only. */ - DAG_relations_tag_update(bmain); + DEG_relations_tag_update(bmain); } } @@ -1048,89 +1086,126 @@ static short pose_grab_with_ik(Main *bmain, Object *ob) /* iTaSC needs clear for new IK constraints */ if (tot_ik) { BIK_clear_data(ob->pose); -#ifdef WITH_LEGACY_DEPSGRAPH - if (!DEG_depsgraph_use_legacy()) -#endif - { - /* TODO(sergey): Consider doing partial update only. */ - DAG_relations_tag_update(bmain); - } + /* TODO(sergey): Consider doing partial update only. */ + DEG_relations_tag_update(bmain); } return (tot_ik) ? 1 : 0; } -/* only called with pose mode active object now */ -static void createTransPose(TransInfo *t, Object *ob) +/** + * When objects array is NULL, use 't->data_container' as is. + */ +static void createTransPose(TransInfo *t, Object **objects, uint objects_len) { + if (objects != NULL) { + if (t->data_container) { + MEM_freeN(t->data_container); + } + t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__); + t->data_container_len = objects_len; + int th_index; + FOREACH_TRANS_DATA_CONTAINER_INDEX (t, tc, th_index) { + tc->poseobj = objects[th_index]; + } + } Main *bmain = CTX_data_main(t->context); - bArmature *arm; - bPoseChannel *pchan; - TransData *td; - TransDataExtension *tdx; - short ik_on = 0; - int i; - t->total = 0; + t->data_len_all = 0; - /* check validity of state */ - arm = BKE_armature_from_object(ob); - if ((arm == NULL) || (ob->pose == NULL)) return; + bool has_translate_rotate_buf[2] = {false, false}; + bool *has_translate_rotate = (t->mode == TFM_TRANSLATION) ? has_translate_rotate_buf : NULL; - if (arm->flag & ARM_RESTPOS) { - if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) { - BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled"); - return; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->poseobj; + + bArmature *arm; + short ik_on = 0; + + /* check validity of state */ + arm = BKE_armature_from_object(tc->poseobj); + if ((arm == NULL) || (ob->pose == NULL)) { + continue; + } + + if (arm->flag & ARM_RESTPOS) { + if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) { + BKE_report(t->reports, RPT_ERROR, "Cannot change Pose when 'Rest Position' is enabled"); + return; + } + } + + /* do we need to add temporal IK chains? */ + if ((arm->flag & ARM_AUTO_IK) && t->mode == TFM_TRANSLATION) { + ik_on = pose_grab_with_ik(bmain, ob); + if (ik_on) t->flag |= T_AUTOIK; } + + /* set flags and count total (warning, can change transform to rotate) */ + tc->data_len = count_set_pose_transflags(ob, t->mode, t->around, has_translate_rotate); + /* len may be zero, skip next iteration. */ } - /* do we need to add temporal IK chains? */ - if ((arm->flag & ARM_AUTO_IK) && t->mode == TFM_TRANSLATION) { - ik_on = pose_grab_with_ik(bmain, ob); - if (ik_on) t->flag |= T_AUTOIK; + /* if there are no translatable bones, do rotation */ + if ((t->mode == TFM_TRANSLATION) && !has_translate_rotate[0]) { + if (has_translate_rotate[1]) { + t->mode = TFM_ROTATION; + } + else { + t->mode = TFM_RESIZE; + } } - /* set flags and count total (warning, can change transform to rotate) */ - t->total = count_set_pose_transflags(&t->mode, t->around, ob); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len == 0) { + continue; + } + Object *ob = tc->poseobj; + TransData *td; + TransDataExtension *tdx; + short ik_on = 0; + int i; - if (t->total == 0) return; + tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */ - t->flag |= T_POSE; - t->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */ + /* init trans data */ + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransPoseBone"); + tdx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransPoseBoneExt"); + for (i = 0; i < tc->data_len; i++, td++, tdx++) { + td->ext = tdx; + td->val = NULL; + } - /* disable PET, its not usable in pose mode yet [#32444] */ - t->flag &= ~T_PROP_EDIT_ALL; + /* use pose channels to fill trans data */ + td = tc->data; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->bone->flag & BONE_TRANSFORM) { + add_pose_transdata(t, pchan, ob, tc, td); + td++; + } + } - /* init trans data */ - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransPoseBone"); - tdx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "TransPoseBoneExt"); - for (i = 0; i < t->total; i++, td++, tdx++) { - td->ext = tdx; - td->val = NULL; - } + if (td != (tc->data + tc->data_len)) { + BKE_report(t->reports, RPT_DEBUG, "Bone selection count error"); + } - /* use pose channels to fill trans data */ - td = t->data; - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - if (pchan->bone->flag & BONE_TRANSFORM) { - add_pose_transdata(t, pchan, ob, td); - td++; + /* initialize initial auto=ik chainlen's? */ + if (ik_on) { + transform_autoik_update(t, 0); } } - if (td != (t->data + t->total)) { - BKE_report(t->reports, RPT_DEBUG, "Bone selection count error"); - } + t->flag |= T_POSE; + /* disable PET, its not usable in pose mode yet [#32444] */ + t->flag &= ~T_PROP_EDIT_ALL; - /* initialize initial auto=ik chainlen's? */ - if (ik_on) transform_autoik_update(t, 0); } -void restoreBones(TransInfo *t) +void restoreBones(TransDataContainer *tc) { - bArmature *arm = t->obedit->data; - BoneInitData *bid = t->custom.type.data; + bArmature *arm = tc->obedit->data; + BoneInitData *bid = tc->custom.type.data; EditBone *ebo; while (bid->bone) { @@ -1171,226 +1246,230 @@ void restoreBones(TransInfo *t) /* ********************* armature ************** */ static void createTransArmatureVerts(TransInfo *t) { - EditBone *ebo, *eboflip; - bArmature *arm = t->obedit->data; - ListBase *edbo = arm->edbo; - TransData *td, *td_old; - float mtx[3][3], smtx[3][3], bonemat[3][3]; - bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0); - int total_mirrored = 0, i; - int oldtot; - BoneInitData *bid; - - t->total = 0; - for (ebo = edbo->first; ebo; ebo = ebo->next) { - oldtot = t->total; - - if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { - if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { - if (ebo->flag & BONE_SELECTED) - t->total++; - } - else if (t->mode == TFM_BONE_ROLL) { - if (ebo->flag & BONE_SELECTED) - t->total++; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EditBone *ebo, *eboflip; + bArmature *arm = tc->obedit->data; + ListBase *edbo = arm->edbo; + TransData *td, *td_old; + float mtx[3][3], smtx[3][3], bonemat[3][3]; + bool mirror = ((arm->flag & ARM_MIRROR_EDIT) != 0); + int total_mirrored = 0, i; + int oldtot; + BoneInitData *bid; + + tc->data_len = 0; + for (ebo = edbo->first; ebo; ebo = ebo->next) { + oldtot = tc->data_len; + + if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { + if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { + if (ebo->flag & BONE_SELECTED) + tc->data_len++; + } + else if (t->mode == TFM_BONE_ROLL) { + if (ebo->flag & BONE_SELECTED) + tc->data_len++; + } + else { + if (ebo->flag & BONE_TIPSEL) + tc->data_len++; + if (ebo->flag & BONE_ROOTSEL) + tc->data_len++; + } } - else { - if (ebo->flag & BONE_TIPSEL) - t->total++; - if (ebo->flag & BONE_ROOTSEL) - t->total++; + + if (mirror && (oldtot < tc->data_len)) { + eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); + if (eboflip) + total_mirrored++; } } - if (mirror && (oldtot < t->total)) { - eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); - if (eboflip) - total_mirrored++; + if (!tc->data_len) { + continue; } - } - if (!t->total) return; + transform_around_single_fallback(t); - transform_around_single_fallback(t); + copy_m3_m4(mtx, tc->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - copy_m3_m4(mtx, t->obedit->obmat); - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransEditBone"); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransEditBone"); + if (mirror) { + tc->custom.type.data = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData"); + tc->custom.type.use_free = true; + } - if (mirror) { - t->custom.type.data = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData"); - t->custom.type.use_free = true; - } + i = 0; - i = 0; + for (ebo = edbo->first; ebo; ebo = ebo->next) { + td_old = td; + ebo->oldlength = ebo->length; // length==0.0 on extrude, used for scaling radius of bone points - for (ebo = edbo->first; ebo; ebo = ebo->next) { - td_old = td; - ebo->oldlength = ebo->length; // length==0.0 on extrude, used for scaling radius of bone points + if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { + if (t->mode == TFM_BONE_ENVELOPE) { + if (ebo->flag & BONE_ROOTSEL) { + td->val = &ebo->rad_head; + td->ival = *td->val; - if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { - if (t->mode == TFM_BONE_ENVELOPE) { - if (ebo->flag & BONE_ROOTSEL) { - td->val = &ebo->rad_head; - td->ival = *td->val; + copy_v3_v3(td->center, ebo->head); + td->flag = TD_SELECTED; - copy_v3_v3(td->center, ebo->head); - td->flag = TD_SELECTED; + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + td->loc = NULL; + td->ext = NULL; + td->ob = tc->obedit; - td->loc = NULL; - td->ext = NULL; - td->ob = t->obedit; + td++; + } + if (ebo->flag & BONE_TIPSEL) { + td->val = &ebo->rad_tail; + td->ival = *td->val; + copy_v3_v3(td->center, ebo->tail); + td->flag = TD_SELECTED; - td++; - } - if (ebo->flag & BONE_TIPSEL) { - td->val = &ebo->rad_tail; - td->ival = *td->val; - copy_v3_v3(td->center, ebo->tail); - td->flag = TD_SELECTED; + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + td->loc = NULL; + td->ext = NULL; + td->ob = tc->obedit; - td->loc = NULL; - td->ext = NULL; - td->ob = t->obedit; + td++; + } - td++; } + else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { + if (ebo->flag & BONE_SELECTED) { + if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { + td->loc = NULL; + td->val = &ebo->dist; + td->ival = ebo->dist; + } + else { + // abusive storage of scale in the loc pointer :) + td->loc = &ebo->xwidth; + copy_v3_v3(td->iloc, td->loc); + td->val = NULL; + } + copy_v3_v3(td->center, ebo->head); + td->flag = TD_SELECTED; - } - else if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { - if (ebo->flag & BONE_SELECTED) { - if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { - td->loc = NULL; - td->val = &ebo->dist; - td->ival = ebo->dist; - } - else { - // abusive storage of scale in the loc pointer :) - td->loc = &ebo->xwidth; - copy_v3_v3(td->iloc, td->loc); - td->val = NULL; - } - copy_v3_v3(td->center, ebo->head); - td->flag = TD_SELECTED; - - /* use local bone matrix */ - ED_armature_ebone_to_mat3(ebo, bonemat); - mul_m3_m3m3(td->mtx, mtx, bonemat); - invert_m3_m3(td->smtx, td->mtx); + /* use local bone matrix */ + ED_armature_ebone_to_mat3(ebo, bonemat); + mul_m3_m3m3(td->mtx, mtx, bonemat); + invert_m3_m3(td->smtx, td->mtx); - copy_m3_m3(td->axismtx, td->mtx); - normalize_m3(td->axismtx); + copy_m3_m3(td->axismtx, td->mtx); + normalize_m3(td->axismtx); - td->ext = NULL; - td->ob = t->obedit; + td->ext = NULL; + td->ob = tc->obedit; - td++; + td++; + } } - } - else if (t->mode == TFM_BONE_ROLL) { - if (ebo->flag & BONE_SELECTED) { - td->loc = NULL; - td->val = &(ebo->roll); - td->ival = ebo->roll; + else if (t->mode == TFM_BONE_ROLL) { + if (ebo->flag & BONE_SELECTED) { + td->loc = NULL; + td->val = &(ebo->roll); + td->ival = ebo->roll; - copy_v3_v3(td->center, ebo->head); - td->flag = TD_SELECTED; + copy_v3_v3(td->center, ebo->head); + td->flag = TD_SELECTED; - td->ext = NULL; - td->ob = t->obedit; + td->ext = NULL; + td->ob = tc->obedit; - td++; - } - } - else { - if (ebo->flag & BONE_TIPSEL) { - copy_v3_v3(td->iloc, ebo->tail); - - /* Don't allow single selected tips to have a modified center, - * causes problem with snapping (see T45974). - * However, in rotation mode, we want to keep that 'rotate bone around root with - * only its tip selected' behavior (see T46325). */ - if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - ((t->mode == TFM_ROTATION) || (ebo->flag & BONE_ROOTSEL))) - { - copy_v3_v3(td->center, ebo->head); - } - else { - copy_v3_v3(td->center, td->iloc); + td++; } + } + else { + if (ebo->flag & BONE_TIPSEL) { + copy_v3_v3(td->iloc, ebo->tail); + + /* Don't allow single selected tips to have a modified center, + * causes problem with snapping (see T45974). + * However, in rotation mode, we want to keep that 'rotate bone around root with + * only its tip selected' behavior (see T46325). */ + if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && + ((t->mode == TFM_ROTATION) || (ebo->flag & BONE_ROOTSEL))) + { + copy_v3_v3(td->center, ebo->head); + } + else { + copy_v3_v3(td->center, td->iloc); + } - td->loc = ebo->tail; - td->flag = TD_SELECTED; - if (ebo->flag & BONE_EDITMODE_LOCKED) - td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE; + td->loc = ebo->tail; + td->flag = TD_SELECTED; + if (ebo->flag & BONE_EDITMODE_LOCKED) + td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE; - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - ED_armature_ebone_to_mat3(ebo, td->axismtx); + ED_armature_ebone_to_mat3(ebo, td->axismtx); - if ((ebo->flag & BONE_ROOTSEL) == 0) { - td->extra = ebo; - td->ival = ebo->roll; - } + if ((ebo->flag & BONE_ROOTSEL) == 0) { + td->extra = ebo; + td->ival = ebo->roll; + } - td->ext = NULL; - td->val = NULL; - td->ob = t->obedit; + td->ext = NULL; + td->val = NULL; + td->ob = tc->obedit; - td++; - } - if (ebo->flag & BONE_ROOTSEL) { - copy_v3_v3(td->iloc, ebo->head); - copy_v3_v3(td->center, td->iloc); - td->loc = ebo->head; - td->flag = TD_SELECTED; - if (ebo->flag & BONE_EDITMODE_LOCKED) - td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE; + td++; + } + if (ebo->flag & BONE_ROOTSEL) { + copy_v3_v3(td->iloc, ebo->head); + copy_v3_v3(td->center, td->iloc); + td->loc = ebo->head; + td->flag = TD_SELECTED; + if (ebo->flag & BONE_EDITMODE_LOCKED) + td->protectflag = OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE; - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - ED_armature_ebone_to_mat3(ebo, td->axismtx); + ED_armature_ebone_to_mat3(ebo, td->axismtx); - td->extra = ebo; /* to fix roll */ - td->ival = ebo->roll; + td->extra = ebo; /* to fix roll */ + td->ival = ebo->roll; - td->ext = NULL; - td->val = NULL; - td->ob = t->obedit; + td->ext = NULL; + td->val = NULL; + td->ob = tc->obedit; - td++; + td++; + } } } - } - if (mirror && (td_old != td)) { - eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); - if (eboflip) { - bid[i].bone = eboflip; - bid[i].dist = eboflip->dist; - bid[i].rad_tail = eboflip->rad_tail; - bid[i].roll = eboflip->roll; - bid[i].xwidth = eboflip->xwidth; - bid[i].zwidth = eboflip->zwidth; - copy_v3_v3(bid[i].head, eboflip->head); - copy_v3_v3(bid[i].tail, eboflip->tail); - i++; + if (mirror && (td_old != td)) { + eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); + if (eboflip) { + bid[i].bone = eboflip; + bid[i].dist = eboflip->dist; + bid[i].rad_tail = eboflip->rad_tail; + bid[i].roll = eboflip->roll; + bid[i].xwidth = eboflip->xwidth; + bid[i].zwidth = eboflip->zwidth; + copy_v3_v3(bid[i].head, eboflip->head); + copy_v3_v3(bid[i].tail, eboflip->tail); + i++; + } } } - } - if (mirror) { - /* trick to terminate iteration */ - bid[total_mirrored].bone = NULL; + if (mirror) { + /* trick to terminate iteration */ + bid[total_mirrored].bone = NULL; + } } } @@ -1398,72 +1477,76 @@ static void createTransArmatureVerts(TransInfo *t) static void createTransMBallVerts(TransInfo *t) { - MetaBall *mb = (MetaBall *)t->obedit->data; - MetaElem *ml; - TransData *td; - TransDataExtension *tx; - float mtx[3][3], smtx[3][3]; - int count = 0, countsel = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - - /* count totals */ - for (ml = mb->editelems->first; ml; ml = ml->next) { - if (ml->flag & SELECT) countsel++; - if (is_prop_edit) count++; - } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + MetaBall *mb = (MetaBall *)tc->obedit->data; + MetaElem *ml; + TransData *td; + TransDataExtension *tx; + float mtx[3][3], smtx[3][3]; + int count = 0, countsel = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + + /* count totals */ + for (ml = mb->editelems->first; ml; ml = ml->next) { + if (ml->flag & SELECT) countsel++; + if (is_prop_edit) count++; + } - /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) return; + /* note: in prop mode we need at least 1 selected */ + if (countsel == 0) { + continue; + } - if (is_prop_edit) t->total = count; - else t->total = countsel; + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(MBall EditMode)"); - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "MetaElement_TransExtension"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(MBall EditMode)"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "MetaElement_TransExtension"); - copy_m3_m4(mtx, t->obedit->obmat); - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + copy_m3_m4(mtx, tc->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - for (ml = mb->editelems->first; ml; ml = ml->next) { - if (is_prop_edit || (ml->flag & SELECT)) { - td->loc = &ml->x; - copy_v3_v3(td->iloc, td->loc); - copy_v3_v3(td->center, td->loc); + for (ml = mb->editelems->first; ml; ml = ml->next) { + if (is_prop_edit || (ml->flag & SELECT)) { + td->loc = &ml->x; + copy_v3_v3(td->iloc, td->loc); + copy_v3_v3(td->center, td->loc); - quat_to_mat3(td->axismtx, ml->quat); + quat_to_mat3(td->axismtx, ml->quat); - if (ml->flag & SELECT) td->flag = TD_SELECTED | TD_USEQUAT | TD_SINGLESIZE; - else td->flag = TD_USEQUAT; + if (ml->flag & SELECT) td->flag = TD_SELECTED | TD_USEQUAT | TD_SINGLESIZE; + else td->flag = TD_USEQUAT; - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - td->ext = tx; + td->ext = tx; - /* Radius of MetaElem (mass of MetaElem influence) */ - if (ml->flag & MB_SCALE_RAD) { - td->val = &ml->rad; - td->ival = ml->rad; - } - else { - td->val = &ml->s; - td->ival = ml->s; - } + /* Radius of MetaElem (mass of MetaElem influence) */ + if (ml->flag & MB_SCALE_RAD) { + td->val = &ml->rad; + td->ival = ml->rad; + } + else { + td->val = &ml->s; + td->ival = ml->s; + } - /* expx/expy/expz determine "shape" of some MetaElem types */ - tx->size = &ml->expx; - tx->isize[0] = ml->expx; - tx->isize[1] = ml->expy; - tx->isize[2] = ml->expz; + /* expx/expy/expz determine "shape" of some MetaElem types */ + tx->size = &ml->expx; + tx->isize[0] = ml->expx; + tx->isize[1] = ml->expy; + tx->isize[2] = ml->expz; - /* quat is used for rotation of MetaElem */ - tx->quat = ml->quat; - copy_qt_qt(tx->iquat, ml->quat); + /* quat is used for rotation of MetaElem */ + tx->quat = ml->quat; + copy_qt_qt(tx->iquat, ml->quat); - tx->rot = NULL; + tx->rot = NULL; - td++; - tx++; + td++; + tx++; + } } } } @@ -1572,271 +1655,277 @@ static int bezt_select_to_transform_triple_flag( static void createTransCurveVerts(TransInfo *t) { - Curve *cu = t->obedit->data; - TransData *td = NULL; - Nurb *nu; - BezTriple *bezt; - BPoint *bp; - float mtx[3][3], smtx[3][3]; - int a; - int count = 0, countsel = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - short hide_handles = (cu->drawflag & CU_HIDE_HANDLES); - ListBase *nurbs; - - /* to be sure */ - if (cu->editnurb == NULL) return; #define SEL_F1 (1 << 0) #define SEL_F2 (1 << 1) #define SEL_F3 (1 << 2) - /* count total of vertices, check identical as in 2nd loop for making transdata! */ - nurbs = BKE_curve_editNurbs_get(cu); - for (nu = nurbs->first; nu; nu = nu->next) { - if (nu->type == CU_BEZIER) { - for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) { - if (bezt->hide == 0) { - const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); - if (bezt_tx & SEL_F1) { countsel++; } - if (bezt_tx & SEL_F2) { countsel++; } - if (bezt_tx & SEL_F3) { countsel++; } - if (is_prop_edit) count += 3; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Curve *cu = tc->obedit->data; + TransData *td = NULL; + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + float mtx[3][3], smtx[3][3]; + int a; + int count = 0, countsel = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + short hide_handles = (cu->drawflag & CU_HIDE_HANDLES); + ListBase *nurbs; + + /* to be sure */ + if (cu->editnurb == NULL) return; + + /* count total of vertices, check identical as in 2nd loop for making transdata! */ + nurbs = BKE_curve_editNurbs_get(cu); + for (nu = nurbs->first; nu; nu = nu->next) { + if (nu->type == CU_BEZIER) { + for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) { + if (bezt->hide == 0) { + const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); + if (bezt_tx & SEL_F1) { countsel++; } + if (bezt_tx & SEL_F2) { countsel++; } + if (bezt_tx & SEL_F3) { countsel++; } + if (is_prop_edit) count += 3; + + } } } - } - else { - for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { - if (bp->hide == 0) { - if (is_prop_edit) count++; - if (bp->f1 & SELECT) countsel++; + else { + for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { + if (bp->hide == 0) { + if (is_prop_edit) count++; + if (bp->f1 & SELECT) countsel++; + } } } } - } - /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) return; + /* note: in prop mode we need at least 1 selected */ + if (countsel == 0) { + tc->data_len = 0; + continue; + } - if (is_prop_edit) t->total = count; - else t->total = countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Curve EditMode)"); + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)"); - transform_around_single_fallback(t); + transform_around_single_fallback(t); - copy_m3_m4(mtx, t->obedit->obmat); - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + copy_m3_m4(mtx, tc->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - td = t->data; - for (nu = nurbs->first; nu; nu = nu->next) { - if (nu->type == CU_BEZIER) { - TransData *head, *tail; - head = tail = td; - for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) { - if (bezt->hide == 0) { - TransDataCurveHandleFlags *hdata = NULL; - float axismtx[3][3]; + td = tc->data; + for (nu = nurbs->first; nu; nu = nu->next) { + if (nu->type == CU_BEZIER) { + TransData *head, *tail; + head = tail = td; + for (a = 0, bezt = nu->bezt; a < nu->pntsu; a++, bezt++) { + if (bezt->hide == 0) { + TransDataCurveHandleFlags *hdata = NULL; + float axismtx[3][3]; - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - float normal[3], plane[3]; + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + float normal[3], plane[3]; - BKE_nurb_bezt_calc_normal(nu, bezt, normal); - BKE_nurb_bezt_calc_plane(nu, bezt, plane); + BKE_nurb_bezt_calc_normal(nu, bezt, normal); + BKE_nurb_bezt_calc_plane(nu, bezt, plane); - if (createSpaceNormalTangent(axismtx, normal, plane)) { - /* pass */ - } - else { - normalize_v3(normal); - axis_dominant_v3_to_m3(axismtx, normal); - invert_m3(axismtx); + if (createSpaceNormalTangent(axismtx, normal, plane)) { + /* pass */ + } + else { + normalize_v3(normal); + axis_dominant_v3_to_m3(axismtx, normal); + invert_m3(axismtx); + } } - } - /* Elements that will be transform (not always a match to selection). */ - const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); + /* Elements that will be transform (not always a match to selection). */ + const int bezt_tx = bezt_select_to_transform_triple_flag(bezt, hide_handles); + + if (is_prop_edit || bezt_tx & SEL_F1) { + copy_v3_v3(td->iloc, bezt->vec[0]); + td->loc = bezt->vec[0]; + copy_v3_v3(td->center, bezt->vec[(hide_handles || + (t->around == V3D_AROUND_LOCAL_ORIGINS) || + (bezt->f2 & SELECT)) ? 1 : 0]); + if (hide_handles) { + if (bezt->f2 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + } + else { + if (bezt->f1 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + } + td->ext = NULL; + td->val = NULL; - if (is_prop_edit || bezt_tx & SEL_F1) { - copy_v3_v3(td->iloc, bezt->vec[0]); - td->loc = bezt->vec[0]; - copy_v3_v3(td->center, bezt->vec[(hide_handles || - (t->around == V3D_AROUND_LOCAL_ORIGINS) || - (bezt->f2 & SELECT)) ? 1 : 0]); - if (hide_handles) { - if (bezt->f2 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - } - else { - if (bezt->f1 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - } - td->ext = NULL; - td->val = NULL; + hdata = initTransDataCurveHandles(td, bezt); - hdata = initTransDataCurveHandles(td, bezt); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + copy_m3_m3(td->axismtx, axismtx); + } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - copy_m3_m3(td->axismtx, axismtx); + td++; + count++; + tail++; } - td++; - count++; - tail++; - } + /* This is the Curve Point, the other two are handles */ + if (is_prop_edit || bezt_tx & SEL_F2) { + copy_v3_v3(td->iloc, bezt->vec[1]); + td->loc = bezt->vec[1]; + copy_v3_v3(td->center, td->loc); + if (bezt->f2 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + td->ext = NULL; - /* This is the Curve Point, the other two are handles */ - if (is_prop_edit || bezt_tx & SEL_F2) { - copy_v3_v3(td->iloc, bezt->vec[1]); - td->loc = bezt->vec[1]; - copy_v3_v3(td->center, td->loc); - if (bezt->f2 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - td->ext = NULL; + if (t->mode == TFM_CURVE_SHRINKFATTEN) { /* || t->mode==TFM_RESIZE) {*/ /* TODO - make points scale */ + td->val = &(bezt->radius); + td->ival = bezt->radius; + } + else if (t->mode == TFM_TILT) { + td->val = &(bezt->alfa); + td->ival = bezt->alfa; + } + else { + td->val = NULL; + } - if (t->mode == TFM_CURVE_SHRINKFATTEN) { /* || t->mode==TFM_RESIZE) {*/ /* TODO - make points scale */ - td->val = &(bezt->radius); - td->ival = bezt->radius; - } - else if (t->mode == TFM_TILT) { - td->val = &(bezt->alfa); - td->ival = bezt->alfa; - } - else { - td->val = NULL; - } + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + copy_m3_m3(td->axismtx, axismtx); + } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - copy_m3_m3(td->axismtx, axismtx); + if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) + /* If the middle is selected but the sides arnt, this is needed */ + if (hdata == NULL) { /* if the handle was not saved by the previous handle */ + hdata = initTransDataCurveHandles(td, bezt); + } + + td++; + count++; + tail++; } + if (is_prop_edit || bezt_tx & SEL_F3) { + copy_v3_v3(td->iloc, bezt->vec[2]); + td->loc = bezt->vec[2]; + copy_v3_v3(td->center, bezt->vec[(hide_handles || + (t->around == V3D_AROUND_LOCAL_ORIGINS) || + (bezt->f2 & SELECT)) ? 1 : 2]); + if (hide_handles) { + if (bezt->f2 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + } + else { + if (bezt->f3 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + } + td->ext = NULL; + td->val = NULL; - if ((bezt_tx & SEL_F1) == 0 && (bezt_tx & SEL_F3) == 0) - /* If the middle is selected but the sides arnt, this is needed */ if (hdata == NULL) { /* if the handle was not saved by the previous handle */ hdata = initTransDataCurveHandles(td, bezt); } - td++; - count++; - tail++; - } - if (is_prop_edit || bezt_tx & SEL_F3) { - copy_v3_v3(td->iloc, bezt->vec[2]); - td->loc = bezt->vec[2]; - copy_v3_v3(td->center, bezt->vec[(hide_handles || - (t->around == V3D_AROUND_LOCAL_ORIGINS) || - (bezt->f2 & SELECT)) ? 1 : 2]); - if (hide_handles) { - if (bezt->f2 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - } - else { - if (bezt->f3 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - } - td->ext = NULL; - td->val = NULL; - - if (hdata == NULL) { /* if the handle was not saved by the previous handle */ - hdata = initTransDataCurveHandles(td, bezt); - } + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + copy_m3_m3(td->axismtx, axismtx); + } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - copy_m3_m3(td->axismtx, axismtx); + td++; + count++; + tail++; } - td++; - count++; - tail++; + (void)hdata; /* quiet warning */ + } + else if (is_prop_edit && head != tail) { + calc_distanceCurveVerts(head, tail - 1); + head = tail; } - - (void)hdata; /* quiet warning */ } - else if (is_prop_edit && head != tail) { + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); - head = tail; - } - } - if (is_prop_edit && head != tail) - calc_distanceCurveVerts(head, tail - 1); - /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandles - * but for now just don't change handle types */ - if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT, TFM_DUMMY) == 0) { - /* sets the handles based on their selection, do this after the data is copied to the TransData */ - BKE_nurb_handles_test(nu, !hide_handles); + /* TODO - in the case of tilt and radius we can also avoid allocating the initTransDataCurveHandles + * but for now just don't change handle types */ + if (ELEM(t->mode, TFM_CURVE_SHRINKFATTEN, TFM_TILT, TFM_DUMMY) == 0) { + /* sets the handles based on their selection, do this after the data is copied to the TransData */ + BKE_nurb_handles_test(nu, !hide_handles); + } } - } - else { - TransData *head, *tail; - head = tail = td; - for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { - if (bp->hide == 0) { - if (is_prop_edit || (bp->f1 & SELECT)) { - float axismtx[3][3]; - - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - if (nu->pntsv == 1) { - float normal[3], plane[3]; - - BKE_nurb_bpoint_calc_normal(nu, bp, normal); - BKE_nurb_bpoint_calc_plane(nu, bp, plane); - - if (createSpaceNormalTangent(axismtx, normal, plane)) { - /* pass */ - } - else { - normalize_v3(normal); - axis_dominant_v3_to_m3(axismtx, normal); - invert_m3(axismtx); + else { + TransData *head, *tail; + head = tail = td; + for (a = nu->pntsu * nu->pntsv, bp = nu->bp; a > 0; a--, bp++) { + if (bp->hide == 0) { + if (is_prop_edit || (bp->f1 & SELECT)) { + float axismtx[3][3]; + + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + if (nu->pntsv == 1) { + float normal[3], plane[3]; + + BKE_nurb_bpoint_calc_normal(nu, bp, normal); + BKE_nurb_bpoint_calc_plane(nu, bp, plane); + + if (createSpaceNormalTangent(axismtx, normal, plane)) { + /* pass */ + } + else { + normalize_v3(normal); + axis_dominant_v3_to_m3(axismtx, normal); + invert_m3(axismtx); + } } } - } - copy_v3_v3(td->iloc, bp->vec); - td->loc = bp->vec; - copy_v3_v3(td->center, td->loc); - if (bp->f1 & SELECT) td->flag = TD_SELECTED; - else td->flag = 0; - td->ext = NULL; + copy_v3_v3(td->iloc, bp->vec); + td->loc = bp->vec; + copy_v3_v3(td->center, td->loc); + if (bp->f1 & SELECT) td->flag = TD_SELECTED; + else td->flag = 0; + td->ext = NULL; - if (t->mode == TFM_CURVE_SHRINKFATTEN || t->mode == TFM_RESIZE) { - td->val = &(bp->radius); - td->ival = bp->radius; - } - else { - td->val = &(bp->alfa); - td->ival = bp->alfa; - } + if (t->mode == TFM_CURVE_SHRINKFATTEN || t->mode == TFM_RESIZE) { + td->val = &(bp->radius); + td->ival = bp->radius; + } + else { + td->val = &(bp->alfa); + td->ival = bp->alfa; + } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); - if (t->around == V3D_AROUND_LOCAL_ORIGINS) { - if (nu->pntsv == 1) { - copy_m3_m3(td->axismtx, axismtx); + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); + if (t->around == V3D_AROUND_LOCAL_ORIGINS) { + if (nu->pntsv == 1) { + copy_m3_m3(td->axismtx, axismtx); + } } - } - td++; - count++; - tail++; + td++; + count++; + tail++; + } + } + else if (is_prop_edit && head != tail) { + calc_distanceCurveVerts(head, tail - 1); + head = tail; } } - else if (is_prop_edit && head != tail) { + if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); - head = tail; - } } - if (is_prop_edit && head != tail) - calc_distanceCurveVerts(head, tail - 1); } } - #undef SEL_F1 #undef SEL_F2 #undef SEL_F3 @@ -1846,226 +1935,236 @@ static void createTransCurveVerts(TransInfo *t) static void createTransLatticeVerts(TransInfo *t) { - Lattice *latt = ((Lattice *)t->obedit->data)->editlatt->latt; - TransData *td = NULL; - BPoint *bp; - float mtx[3][3], smtx[3][3]; - int a; - int count = 0, countsel = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - bp = latt->def; - a = latt->pntsu * latt->pntsv * latt->pntsw; - while (a--) { - if (bp->hide == 0) { - if (bp->f1 & SELECT) countsel++; - if (is_prop_edit) count++; + Lattice *latt = ((Lattice *)tc->obedit->data)->editlatt->latt; + TransData *td = NULL; + BPoint *bp; + float mtx[3][3], smtx[3][3]; + int a; + int count = 0, countsel = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + + bp = latt->def; + a = latt->pntsu * latt->pntsv * latt->pntsw; + while (a--) { + if (bp->hide == 0) { + if (bp->f1 & SELECT) countsel++; + if (is_prop_edit) count++; + } + bp++; } - bp++; - } - /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) return; + /* note: in prop mode we need at least 1 selected */ + if (countsel == 0) return; - if (is_prop_edit) t->total = count; - else t->total = countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Lattice EditMode)"); + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Lattice EditMode)"); - copy_m3_m4(mtx, t->obedit->obmat); - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + copy_m3_m4(mtx, tc->obedit->obmat); + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - td = t->data; - bp = latt->def; - a = latt->pntsu * latt->pntsv * latt->pntsw; - while (a--) { - if (is_prop_edit || (bp->f1 & SELECT)) { - if (bp->hide == 0) { - copy_v3_v3(td->iloc, bp->vec); - td->loc = bp->vec; - copy_v3_v3(td->center, td->loc); - if (bp->f1 & SELECT) { - td->flag = TD_SELECTED; - } - else { - td->flag = 0; - } - copy_m3_m3(td->smtx, smtx); - copy_m3_m3(td->mtx, mtx); + td = tc->data; + bp = latt->def; + a = latt->pntsu * latt->pntsv * latt->pntsw; + while (a--) { + if (is_prop_edit || (bp->f1 & SELECT)) { + if (bp->hide == 0) { + copy_v3_v3(td->iloc, bp->vec); + td->loc = bp->vec; + copy_v3_v3(td->center, td->loc); + if (bp->f1 & SELECT) { + td->flag = TD_SELECTED; + } + else { + td->flag = 0; + } + copy_m3_m3(td->smtx, smtx); + copy_m3_m3(td->mtx, mtx); - td->ext = NULL; - td->val = NULL; + td->ext = NULL; + td->val = NULL; - td++; - count++; + td++; + count++; + } } + bp++; } - bp++; } } /* ******************* particle edit **************** */ static void createTransParticleVerts(bContext *C, TransInfo *t) { - TransData *td = NULL; - TransDataExtension *tx; - Object *ob = CTX_data_active_object(C); - ParticleEditSettings *pset = PE_settings(t->scene); - PTCacheEdit *edit = PE_get_current(t->scene, ob); - ParticleSystem *psys = NULL; - ParticleSystemModifierData *psmd = NULL; - PTCacheEditPoint *point; - PTCacheEditKey *key; - float mat[4][4]; - int i, k, transformparticle; - int count = 0, hasselected = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - - if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return; - - psys = edit->psys; - - if (psys) - psmd = psys_get_modifier(ob, psys); - - for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) { - point->flag &= ~PEP_TRANSFORM; - transformparticle = 0; - - if ((point->flag & PEP_HIDE) == 0) { - for (k = 0, key = point->keys; k < point->totkey; k++, key++) { - if ((key->flag & PEK_HIDE) == 0) { - if (key->flag & PEK_SELECT) { - hasselected = 1; - transformparticle = 1; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + TransData *td = NULL; + TransDataExtension *tx; + Object *ob = CTX_data_active_object(C); + ParticleEditSettings *pset = PE_settings(t->scene); + PTCacheEdit *edit = PE_get_current(t->scene, ob); + ParticleSystem *psys = NULL; + ParticleSystemModifierData *psmd = NULL; + PTCacheEditPoint *point; + PTCacheEditKey *key; + float mat[4][4]; + int i, k, transformparticle; + int count = 0, hasselected = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + + if (edit == NULL || t->settings->particle.selectmode == SCE_SELECT_PATH) return; + + psys = edit->psys; + + if (psys) + psmd = psys_get_modifier(ob, psys); + + for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) { + point->flag &= ~PEP_TRANSFORM; + transformparticle = 0; + + if ((point->flag & PEP_HIDE) == 0) { + for (k = 0, key = point->keys; k < point->totkey; k++, key++) { + if ((key->flag & PEK_HIDE) == 0) { + if (key->flag & PEK_SELECT) { + hasselected = 1; + transformparticle = 1; + } + else if (is_prop_edit) + transformparticle = 1; } - else if (is_prop_edit) - transformparticle = 1; } } - } - if (transformparticle) { - count += point->totkey; - point->flag |= PEP_TRANSFORM; + if (transformparticle) { + count += point->totkey; + point->flag |= PEP_TRANSFORM; + } } - } - /* note: in prop mode we need at least 1 selected */ - if (hasselected == 0) return; + /* note: in prop mode we need at least 1 selected */ + if (hasselected == 0) return; - t->total = count; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Particle Mode)"); + tc->data_len = count; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Particle Mode)"); - if (t->mode == TFM_BAKE_TIME) - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "Particle_TransExtension"); - else - tx = t->ext = NULL; + if (t->mode == TFM_BAKE_TIME) + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "Particle_TransExtension"); + else + tx = tc->data_ext = NULL; - unit_m4(mat); + unit_m4(mat); - invert_m4_m4(ob->imat, ob->obmat); + invert_m4_m4(ob->imat, ob->obmat); - for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) { - TransData *head, *tail; - head = tail = td; + for (i = 0, point = edit->points; i < edit->totpoint; i++, point++) { + TransData *head, *tail; + head = tail = td; - if (!(point->flag & PEP_TRANSFORM)) continue; + if (!(point->flag & PEP_TRANSFORM)) continue; - if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) - psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat); + if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) + psys_mat_hair_to_global(ob, psmd->mesh_final, psys->part->from, psys->particles + i, mat); - for (k = 0, key = point->keys; k < point->totkey; k++, key++) { - if (key->flag & PEK_USE_WCO) { - copy_v3_v3(key->world_co, key->co); - mul_m4_v3(mat, key->world_co); - td->loc = key->world_co; - } - else - td->loc = key->co; + for (k = 0, key = point->keys; k < point->totkey; k++, key++) { + if (key->flag & PEK_USE_WCO) { + copy_v3_v3(key->world_co, key->co); + mul_m4_v3(mat, key->world_co); + td->loc = key->world_co; + } + else + td->loc = key->co; - copy_v3_v3(td->iloc, td->loc); - copy_v3_v3(td->center, td->loc); + copy_v3_v3(td->iloc, td->loc); + copy_v3_v3(td->center, td->loc); - if (key->flag & PEK_SELECT) - td->flag |= TD_SELECTED; - else if (!is_prop_edit) - td->flag |= TD_SKIP; + if (key->flag & PEK_SELECT) + td->flag |= TD_SELECTED; + else if (!is_prop_edit) + td->flag |= TD_SKIP; - unit_m3(td->mtx); - unit_m3(td->smtx); + unit_m3(td->mtx); + unit_m3(td->smtx); - /* don't allow moving roots */ - if (k == 0 && pset->flag & PE_LOCK_FIRST && (!psys || !(psys->flag & PSYS_GLOBAL_HAIR))) - td->protectflag |= OB_LOCK_LOC; + /* don't allow moving roots */ + if (k == 0 && pset->flag & PE_LOCK_FIRST && (!psys || !(psys->flag & PSYS_GLOBAL_HAIR))) + td->protectflag |= OB_LOCK_LOC; - td->ob = ob; - td->ext = tx; - if (t->mode == TFM_BAKE_TIME) { - td->val = key->time; - td->ival = *(key->time); - /* abuse size and quat for min/max values */ - td->flag |= TD_NO_EXT; - if (k == 0) tx->size = NULL; - else tx->size = (key - 1)->time; + td->ob = ob; + td->ext = tx; + if (t->mode == TFM_BAKE_TIME) { + td->val = key->time; + td->ival = *(key->time); + /* abuse size and quat for min/max values */ + td->flag |= TD_NO_EXT; + if (k == 0) tx->size = NULL; + else tx->size = (key - 1)->time; - if (k == point->totkey - 1) tx->quat = NULL; - else tx->quat = (key + 1)->time; - } + if (k == point->totkey - 1) tx->quat = NULL; + else tx->quat = (key + 1)->time; + } - td++; - if (tx) - tx++; - tail++; + td++; + if (tx) + tx++; + tail++; + } + if (is_prop_edit && head != tail) + calc_distanceCurveVerts(head, tail - 1); } - if (is_prop_edit && head != tail) - calc_distanceCurveVerts(head, tail - 1); } } void flushTransParticles(TransInfo *t) { - Scene *scene = t->scene; - Object *ob = OBACT; - PTCacheEdit *edit = PE_get_current(scene, ob); - ParticleSystem *psys = edit->psys; - ParticleSystemModifierData *psmd = NULL; - PTCacheEditPoint *point; - PTCacheEditKey *key; - TransData *td; - float mat[4][4], imat[4][4], co[3]; - int i, k; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - - if (psys) - psmd = psys_get_modifier(ob, psys); - - /* we do transform in world space, so flush world space position - * back to particle local space (only for hair particles) */ - td = t->data; - for (i = 0, point = edit->points; i < edit->totpoint; i++, point++, td++) { - if (!(point->flag & PEP_TRANSFORM)) continue; - - if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) { - psys_mat_hair_to_global(ob, psmd->dm_final, psys->part->from, psys->particles + i, mat); - invert_m4_m4(imat, mat); - - for (k = 0, key = point->keys; k < point->totkey; k++, key++) { - copy_v3_v3(co, key->world_co); - mul_m4_v3(imat, co); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - - /* optimization for proportional edit */ - if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) { - copy_v3_v3(key->co, co); - point->flag |= PEP_EDIT_RECALC; + Scene *scene = t->scene; + ViewLayer *view_layer = t->view_layer; + Object *ob = OBACT(view_layer); + PTCacheEdit *edit = PE_get_current(scene, ob); + ParticleSystem *psys = edit->psys; + ParticleSystemModifierData *psmd = NULL; + PTCacheEditPoint *point; + PTCacheEditKey *key; + TransData *td; + float mat[4][4], imat[4][4], co[3]; + int i, k; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + + if (psys) + psmd = psys_get_modifier(ob, psys); + + /* we do transform in world space, so flush world space position + * back to particle local space (only for hair particles) */ + td = tc->data; + for (i = 0, point = edit->points; i < edit->totpoint; i++, point++, td++) { + if (!(point->flag & PEP_TRANSFORM)) continue; + + if (psys && !(psys->flag & PSYS_GLOBAL_HAIR)) { + psys_mat_hair_to_global(ob, psmd->mesh_final, psys->part->from, psys->particles + i, mat); + invert_m4_m4(imat, mat); + + for (k = 0, key = point->keys; k < point->totkey; k++, key++) { + copy_v3_v3(co, key->world_co); + mul_m4_v3(imat, co); + + + /* optimization for proportional edit */ + if (!is_prop_edit || !compare_v3v3(key->co, co, 0.0001f)) { + copy_v3_v3(key->co, co); + point->flag |= PEP_EDIT_RECALC; + } } } + else + point->flag |= PEP_EDIT_RECALC; } - else - point->flag |= PEP_EDIT_RECALC; - } - PE_update_object(scene, OBACT, 1); + PE_update_object(t->depsgraph, scene, OBACT(view_layer), 1); + } } /* ********************* mesh ****************** */ @@ -2475,243 +2574,248 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx static void createTransEditVerts(TransInfo *t) { - TransData *tob = NULL; - TransDataExtension *tx = NULL; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - Mesh *me = t->obedit->data; - BMesh *bm = em->bm; - BMVert *eve; - BMIter iter; - float (*mappedcos)[3] = NULL, (*quats)[4] = NULL; - float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; - float *dists = NULL; - int a; - const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; - int mirror = 0; - int cd_vert_bweight_offset = -1; - bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - struct TransIslandData *island_info = NULL; - int island_info_tot; - int *island_vert_map = NULL; + TransData *tob = NULL; + TransDataExtension *tx = NULL; + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + Mesh *me = tc->obedit->data; + BMesh *bm = em->bm; + BMVert *eve; + BMIter iter; + float (*mappedcos)[3] = NULL, (*quats)[4] = NULL; + float mtx[3][3], smtx[3][3], (*defmats)[3][3] = NULL, (*defcos)[3] = NULL; + float *dists = NULL; + int a; + const int prop_mode = (t->flag & T_PROP_EDIT) ? (t->flag & T_PROP_EDIT_ALL) : 0; + int mirror = 0; + int cd_vert_bweight_offset = -1; + bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; - /* Even for translation this is needed because of island-orientation, see: T51651. */ - const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); - /* Original index of our connected vertex when connected distances are calculated. - * Optional, allocate if needed. */ - int *dists_index = NULL; + struct TransIslandData *island_info = NULL; + int island_info_tot; + int *island_vert_map = NULL; - if (t->flag & T_MIRROR) { - EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology); - mirror = 1; - } + /* Even for translation this is needed because of island-orientation, see: T51651. */ + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); + /* Original index of our connected vertex when connected distances are calculated. + * Optional, allocate if needed. */ + int *dists_index = NULL; - /** - * Quick check if we can transform. - * - * \note ignore modes here, even in edge/face modes, transform data is created by selected vertices. - * \note in prop mode we need at least 1 selected. - */ - if (bm->totvertsel == 0) { - goto cleanup; - } + if (t->flag & T_MIRROR) { + /* TODO(campbell): xform: We need support for many mirror objects at once! */ + if (tc->is_active) { + EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology); + mirror = 1; + } + } - if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_VERT_BWEIGHT); - cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); - } + /** + * Quick check if we can transform. + * + * \note ignore modes here, even in edge/face modes, transform data is created by selected vertices. + * \note in prop mode we need at least 1 selected. + */ + if (bm->totvertsel == 0) { + goto cleanup; + } - if (prop_mode) { - unsigned int count = 0; - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - count++; - } + if (t->mode == TFM_BWEIGHT) { + BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); + cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } - t->total = count; + if (prop_mode) { + unsigned int count = 0; + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + count++; + } + } + + tc->data_len = count; - /* allocating scratch arrays */ - if (prop_mode & T_PROP_CONNECTED) { - dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__); - if (is_island_center) { - dists_index = MEM_mallocN(em->bm->totvert * sizeof(int), __func__); + /* allocating scratch arrays */ + if (prop_mode & T_PROP_CONNECTED) { + dists = MEM_mallocN(em->bm->totvert * sizeof(float), __func__); + if (is_island_center) { + dists_index = MEM_mallocN(em->bm->totvert * sizeof(int), __func__); + } } } - } - else { - t->total = bm->totvertsel; - } - - tob = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mesh EditMode)"); - if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) { - /* warning, this is overkill, we only need 2 extra floats, - * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill - * since we may not use the 'alt' transform mode to maintain shell thickness, - * but with generic transform code its hard to lazy init vars */ - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), - "TransObData ext"); - } + else { + tc->data_len = bm->totvertsel; + } - copy_m3_m4(mtx, t->obedit->obmat); - /* we use a pseudoinverse so that when one of the axes is scaled to 0, - * matrix inversion still works and we can still moving along the other */ - pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); + tob = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); + if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) { + /* warning, this is overkill, we only need 2 extra floats, + * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill + * since we may not use the 'alt' transform mode to maintain shell thickness, + * but with generic transform code its hard to lazy init vars */ + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObData ext"); + } - if (prop_mode & T_PROP_CONNECTED) { - editmesh_set_connectivity_distance(em->bm, mtx, dists, dists_index); - } + copy_m3_m4(mtx, tc->obedit->obmat); + /* we use a pseudoinverse so that when one of the axes is scaled to 0, + * matrix inversion still works and we can still moving along the other */ + pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - if (is_island_center) { - /* In this specific case, near-by vertices will need to know the island of the nearest connected vertex. */ - const bool calc_single_islands = ( - (prop_mode & T_PROP_CONNECTED) && - (t->around == V3D_AROUND_LOCAL_ORIGINS) && - (em->selectmode & SCE_SELECT_VERTEX)); + if (prop_mode & T_PROP_CONNECTED) { + editmesh_set_connectivity_distance(em->bm, mtx, dists, dists_index); + } - island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map, calc_single_islands); - } + if (is_island_center) { + /* In this specific case, near-by vertices will need to know the island of the nearest connected vertex. */ + const bool calc_single_islands = ( + (prop_mode & T_PROP_CONNECTED) && + (t->around == V3D_AROUND_LOCAL_ORIGINS) && + (em->selectmode & SCE_SELECT_VERTEX)); - /* detect CrazySpace [tm] */ - if (modifiers_getCageIndex(t->scene, t->obedit, NULL, 1) != -1) { - int totleft = -1; - if (modifiers_isCorrectableDeformed(t->scene, t->obedit)) { - /* check if we can use deform matrices for modifier from the - * start up to stack, they are more accurate than quats */ - totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(t->scene, t->obedit, em, &defmats, &defcos); + island_info = editmesh_islands_info_calc(em, &island_info_tot, &island_vert_map, calc_single_islands); } - /* if we still have more modifiers, also do crazyspace - * correction with quats, relative to the coordinates after - * the modifiers that support deform matrices (defcos) */ + /* detect CrazySpace [tm] */ + if (modifiers_getCageIndex(t->scene, tc->obedit, NULL, 1) != -1) { + int totleft = -1; + if (modifiers_isCorrectableDeformed(t->scene, tc->obedit)) { + /* check if we can use deform matrices for modifier from the + * start up to stack, they are more accurate than quats */ + totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(t->depsgraph, t->scene, tc->obedit, em, &defmats, &defcos); + } + + /* if we still have more modifiers, also do crazyspace + * correction with quats, relative to the coordinates after + * the modifiers that support deform matrices (defcos) */ #if 0 /* TODO, fix crazyspace+extrude so it can be enabled for general use - campbell */ - if ((totleft > 0) || (totleft == -1)) + if ((totleft > 0) || (totleft == -1)) #else - if (totleft > 0) + if (totleft > 0) #endif - { - mappedcos = BKE_crazyspace_get_mapped_editverts(t->scene, t->obedit); - quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); - BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); - if (mappedcos) - MEM_freeN(mappedcos); - } + { + mappedcos = BKE_crazyspace_get_mapped_editverts(t->depsgraph, t->scene, tc->obedit); + quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); + BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); + if (mappedcos) + MEM_freeN(mappedcos); + } - if (defcos) { - MEM_freeN(defcos); + if (defcos) { + MEM_freeN(defcos); + } } - } - /* find out which half we do */ - if (mirror) { - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve->co[0] != 0.0f) { - if (eve->co[0] < 0.0f) { - t->mirror = -1; - mirror = -1; + /* find out which half we do */ + if (mirror) { + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve->co[0] != 0.0f) { + if (eve->co[0] < 0.0f) { + t->mirror = -1; + mirror = -1; + } + break; } - break; } } - } - - BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - struct TransIslandData *v_island = NULL; - float *bweight = (cd_vert_bweight_offset != -1) ? BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : NULL; - if (island_info) { - const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; - v_island = (island_vert_map[connected_index] != -1) ? - &island_info[island_vert_map[connected_index]] : NULL; - } + BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, a) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + if (prop_mode || BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + struct TransIslandData *v_island = NULL; + float *bweight = (cd_vert_bweight_offset != -1) ? BM_ELEM_CD_GET_VOID_P(eve, cd_vert_bweight_offset) : NULL; + + if (island_info) { + const int connected_index = (dists_index && dists_index[a] != -1) ? dists_index[a] : a; + v_island = (island_vert_map[connected_index] != -1) ? + &island_info[island_vert_map[connected_index]] : NULL; + } - VertsToTransData(t, tob, tx, em, eve, bweight, v_island); - if (tx) - tx++; + VertsToTransData(t, tob, tx, em, eve, bweight, v_island); + if (tx) + tx++; - /* selected */ - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) - tob->flag |= TD_SELECTED; + /* selected */ + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) + tob->flag |= TD_SELECTED; - if (prop_mode) { - if (prop_mode & T_PROP_CONNECTED) { - tob->dist = dists[a]; - } - else { - tob->flag |= TD_NOTCONNECTED; - tob->dist = FLT_MAX; + if (prop_mode) { + if (prop_mode & T_PROP_CONNECTED) { + tob->dist = dists[a]; + } + else { + tob->flag |= TD_NOTCONNECTED; + tob->dist = FLT_MAX; + } } - } - /* CrazySpace */ - if (defmats || (quats && BM_elem_flag_test(eve, BM_ELEM_TAG))) { - float mat[3][3], qmat[3][3], imat[3][3]; + /* CrazySpace */ + if (defmats || (quats && BM_elem_flag_test(eve, BM_ELEM_TAG))) { + float mat[3][3], qmat[3][3], imat[3][3]; - /* use both or either quat and defmat correction */ - if (quats && BM_elem_flag_test(eve, BM_ELEM_TAG)) { - quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]); + /* use both or either quat and defmat correction */ + if (quats && BM_elem_flag_test(eve, BM_ELEM_TAG)) { + quat_to_mat3(qmat, quats[BM_elem_index_get(eve)]); - if (defmats) - mul_m3_series(mat, defmats[a], qmat, mtx); + if (defmats) + mul_m3_series(mat, defmats[a], qmat, mtx); + else + mul_m3_m3m3(mat, mtx, qmat); + } else - mul_m3_m3m3(mat, mtx, qmat); - } - else - mul_m3_m3m3(mat, mtx, defmats[a]); + mul_m3_m3m3(mat, mtx, defmats[a]); - invert_m3_m3(imat, mat); + invert_m3_m3(imat, mat); - copy_m3_m3(tob->smtx, imat); - copy_m3_m3(tob->mtx, mat); - } - else { - copy_m3_m3(tob->smtx, smtx); - copy_m3_m3(tob->mtx, mtx); - } + copy_m3_m3(tob->smtx, imat); + copy_m3_m3(tob->mtx, mat); + } + else { + copy_m3_m3(tob->smtx, smtx); + copy_m3_m3(tob->mtx, mtx); + } - /* Mirror? */ - if ((mirror > 0 && tob->iloc[0] > 0.0f) || (mirror < 0 && tob->iloc[0] < 0.0f)) { - BMVert *vmir = EDBM_verts_mirror_get(em, eve); //t->obedit, em, eve, tob->iloc, a); - if (vmir && vmir != eve) { - tob->extra = vmir; + /* Mirror? */ + if ((mirror > 0 && tob->iloc[0] > 0.0f) || (mirror < 0 && tob->iloc[0] < 0.0f)) { + BMVert *vmir = EDBM_verts_mirror_get(em, eve); //t->obedit, em, eve, tob->iloc, a); + if (vmir && vmir != eve) { + tob->extra = vmir; + } } + tob++; } - tob++; } } - } - if (island_info) { - MEM_freeN(island_info); - MEM_freeN(island_vert_map); - } + if (island_info) { + MEM_freeN(island_info); + MEM_freeN(island_vert_map); + } - if (mirror != 0) { - tob = t->data; - for (a = 0; a < t->total; a++, tob++) { - if (ABS(tob->loc[0]) <= 0.00001f) { - tob->flag |= TD_MIRROR_EDGE; + if (mirror != 0) { + tob = tc->data; + for (a = 0; a < tc->data_len; a++, tob++) { + if (ABS(tob->loc[0]) <= 0.00001f) { + tob->flag |= TD_MIRROR_EDGE; + } } } - } cleanup: - /* crazy space free */ - if (quats) - MEM_freeN(quats); - if (defmats) - MEM_freeN(defmats); - if (dists) - MEM_freeN(dists); - if (dists_index) - MEM_freeN(dists_index); - - if (t->flag & T_MIRROR) { - EDBM_verts_mirror_cache_end(em); + /* crazy space free */ + if (quats) + MEM_freeN(quats); + if (defmats) + MEM_freeN(defmats); + if (dists) + MEM_freeN(dists); + if (dists_index) + MEM_freeN(dists_index); + + if (t->flag & T_MIRROR) { + EDBM_verts_mirror_cache_end(em); + } } } @@ -2719,39 +2823,42 @@ cleanup: void flushTransNodes(TransInfo *t) { const float dpi_fac = UI_DPI_FAC; - int a; - TransData *td; - TransData2D *td2d; - applyGridAbsolute(t); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + int a; + TransData *td; + TransData2D *td2d; - /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { - bNode *node = td->extra; - float locx, locy; + applyGridAbsolute(t); - /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ + /* flush to 2d vector from internally used 3d vector */ + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { + bNode *node = td->extra; + float locx, locy; + + /* weirdo - but the node system is a mix of free 2d elements and dpi sensitive UI */ #ifdef USE_NODE_CENTER - locx = (td2d->loc[0] - (BLI_rctf_size_x(&node->totr)) * +0.5f) / dpi_fac; - locy = (td2d->loc[1] - (BLI_rctf_size_y(&node->totr)) * -0.5f) / dpi_fac; + locx = (td2d->loc[0] - (BLI_rctf_size_x(&node->totr)) * +0.5f) / dpi_fac; + locy = (td2d->loc[1] - (BLI_rctf_size_y(&node->totr)) * -0.5f) / dpi_fac; #else - locx = td2d->loc[0] / dpi_fac; - locy = td2d->loc[1] / dpi_fac; + locx = td2d->loc[0] / dpi_fac; + locy = td2d->loc[1] / dpi_fac; #endif - /* account for parents (nested nodes) */ - if (node->parent) { - nodeFromView(node->parent, locx, locy, &node->locx, &node->locy); - } - else { - node->locx = locx; - node->locy = locy; + /* account for parents (nested nodes) */ + if (node->parent) { + nodeFromView(node->parent, locx, locy, &node->locx, &node->locy); + } + else { + node->locx = locx; + node->locy = locy; + } } - } - /* handle intersection with noodles */ - if (t->total == 1) { - ED_node_link_intersect_test(t->sa, 1); + /* handle intersection with noodles */ + if (tc->data_len == 1) { + ED_node_link_intersect_test(t->sa, 1); + } } } @@ -2781,13 +2888,14 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s void flushTransSeq(TransInfo *t) { ListBase *seqbasep = BKE_sequencer_editing_get(t->scene, false)->seqbasep; /* Editing null check already done */ + int a, new_frame; TransData *td = NULL; TransData2D *td2d = NULL; TransDataSeq *tdsq = NULL; Sequence *seq; - + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* prevent updating the same seq twice * if the transdata order is changed this will mess up @@ -2796,7 +2904,7 @@ void flushTransSeq(TransInfo *t) int old_start_prev = 0, sel_flag_prev = 0; /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { int old_start; tdsq = (TransDataSeq *)td->extra; seq = tdsq->seq; @@ -2869,8 +2977,8 @@ void flushTransSeq(TransInfo *t) } /* update effects inside meta's */ - for (a = 0, seq_prev = NULL, td = t->data, td2d = t->data2d; - a < t->total; + for (a = 0, seq_prev = NULL, td = tc->data, td2d = tc->data_2d; + a < tc->data_len; a++, td++, td2d++, seq_prev = seq) { tdsq = (TransDataSeq *)td->extra; @@ -2886,7 +2994,7 @@ void flushTransSeq(TransInfo *t) /* need to do the overlap check in a new loop otherwise adjacent strips * will not be updated and we'll get false positives */ seq_prev = NULL; - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { tdsq = (TransDataSeq *)td->extra; seq = tdsq->seq; @@ -2947,159 +3055,167 @@ static void createTransUVs(bContext *C, TransInfo *t) Image *ima = CTX_data_edit_image(C); Scene *scene = t->scene; ToolSettings *ts = CTX_data_tool_settings(C); - TransData *td = NULL; - TransData2D *td2d = NULL; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - BMFace *efa; - BMIter iter, liter; - UvElementMap *elementmap = NULL; - BLI_bitmap *island_enabled = NULL; - struct { float co[2]; int co_num; } *island_center = NULL; - int count = 0, countsel = 0, count_rejected = 0; + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - if (!ED_space_image_show_uvedit(sima, t->obedit)) - return; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - /* count */ - if (is_prop_connected || is_island_center) { - /* create element map with island information */ - const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; - elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); - if (elementmap == NULL) { - return; - } + TransData *td = NULL; + TransData2D *td2d = NULL; + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMFace *efa; + BMIter iter, liter; + UvElementMap *elementmap = NULL; + BLI_bitmap *island_enabled = NULL; + struct { float co[2]; int co_num; } *island_center = NULL; + int count = 0, countsel = 0, count_rejected = 0; + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - if (is_prop_connected) { - island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); + if (!ED_space_image_show_uvedit(sima, tc->obedit)) { + continue; } - if (is_island_center) { - island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); - } - } + /* count */ + if (is_prop_connected || is_island_center) { + /* create element map with island information */ + const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0; + elementmap = BM_uv_element_map_create(em->bm, use_facesel, false, true); + if (elementmap == NULL) { + return; + } - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); - BMLoop *l; + if (is_prop_connected) { + island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)"); + } - if (!uvedit_face_visible_test(scene, ima, efa, tf)) { - BM_elem_flag_disable(efa, BM_ELEM_TAG); - continue; + if (is_island_center) { + island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); + } } - BM_elem_flag_enable(efa, BM_ELEM_TAG); - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - countsel++; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l; - if (is_prop_connected || island_center) { - UvElement *element = BM_uv_element_get(elementmap, efa, l); + if (!uvedit_face_visible_test(scene, tc->obedit, ima, efa)) { + BM_elem_flag_disable(efa, BM_ELEM_TAG); + continue; + } - if (is_prop_connected) { - BLI_BITMAP_ENABLE(island_enabled, element->island); - } + BM_elem_flag_enable(efa, BM_ELEM_TAG); + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + countsel++; - if (is_island_center) { - if (element->flag == false) { - MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - add_v2_v2(island_center[element->island].co, luv->uv); - island_center[element->island].co_num++; - element->flag = true; + if (is_prop_connected || island_center) { + UvElement *element = BM_uv_element_get(elementmap, efa, l); + + if (is_prop_connected) { + BLI_BITMAP_ENABLE(island_enabled, element->island); + } + + if (is_island_center) { + if (element->flag == false) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + add_v2_v2(island_center[element->island].co, luv->uv); + island_center[element->island].co_num++; + element->flag = true; + } } } } - } - if (is_prop_edit) { - count++; + if (is_prop_edit) { + count++; + } } } - } - /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) { - goto finally; - } + /* note: in prop mode we need at least 1 selected */ + if (countsel == 0) { + goto finally; + } - if (is_island_center) { - int i; + if (is_island_center) { + int i; - for (i = 0; i < elementmap->totalIslands; i++) { - mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); - mul_v2_v2(island_center[i].co, t->aspect); + for (i = 0; i < elementmap->totalIslands; i++) { + mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); + mul_v2_v2(island_center[i].co, t->aspect); + } } - } - t->total = (is_prop_edit) ? count : countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(UV Editing)"); - /* for each 2d uv coord a 3d vector is allocated, so that they can be - * treated just as if they were 3d verts */ - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransObData2D(UV Editing)"); + tc->data_len = (is_prop_edit) ? count : countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(UV Editing)"); + /* for each 2d uv coord a 3d vector is allocated, so that they can be + * treated just as if they were 3d verts */ + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(UV Editing)"); - if (sima->flag & SI_CLIP_UV) - t->flag |= T_CLIP_UV; + if (sima->flag & SI_CLIP_UV) + t->flag |= T_CLIP_UV; - td = t->data; - td2d = t->data2d; + td = tc->data; + td2d = tc->data_2d; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BMLoop *l; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BMLoop *l; - if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) - continue; + if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) + continue; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); - MLoopUV *luv; - const float *center = NULL; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + MLoopUV *luv; + const float *center = NULL; - if (!is_prop_edit && !selected) - continue; + if (!is_prop_edit && !selected) + continue; - if (is_prop_connected || is_island_center) { - UvElement *element = BM_uv_element_get(elementmap, efa, l); + if (is_prop_connected || is_island_center) { + UvElement *element = BM_uv_element_get(elementmap, efa, l); - if (is_prop_connected) { - if (!BLI_BITMAP_TEST(island_enabled, element->island)) { - count_rejected++; - continue; + if (is_prop_connected) { + if (!BLI_BITMAP_TEST(island_enabled, element->island)) { + count_rejected++; + continue; + } } - } - if (is_island_center) { - center = island_center[element->island].co; + if (is_island_center) { + center = island_center[element->island].co; + } } - } - BM_elem_flag_enable(l, BM_ELEM_TAG); - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected); + BM_elem_flag_enable(l, BM_ELEM_TAG); + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected); + } } - } - if (is_prop_connected) { - t->total -= count_rejected; - } - - if (sima->flag & SI_LIVE_UNWRAP) - ED_uvedit_live_unwrap_begin(t->scene, t->obedit); + if (is_prop_connected) { + tc->data_len -= count_rejected; + } + if (sima->flag & SI_LIVE_UNWRAP) { + /* TODO(campbell): xform: Only active object currently! + * it uses a static variable. */ + if (tc->is_active) { + ED_uvedit_live_unwrap_begin(t->scene, tc->obedit); + } + } finally: - if (is_prop_connected || is_island_center) { - BM_uv_element_map_free(elementmap); + if (is_prop_connected || is_island_center) { + BM_uv_element_map_free(elementmap); - if (is_prop_connected) { - MEM_freeN(island_enabled); - } + if (is_prop_connected) { + MEM_freeN(island_enabled); + } - if (island_center) { - MEM_freeN(island_center); + if (island_center) { + MEM_freeN(island_center); + } } } } @@ -3107,37 +3223,38 @@ finally: void flushTransUVs(TransInfo *t) { SpaceImage *sima = t->sa->spacedata.first; - TransData2D *td; - int a; - float aspect_inv[2], size[2]; const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)); - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData2D *td; + int a; + float aspect_inv[2], size[2]; - if (use_pixel_snap) { - int size_i[2]; - ED_space_image_get_size(sima, &size_i[0], &size_i[1]); - size[0] = size_i[0]; - size[1] = size_i[1]; - } - - /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data2d; a < t->total; a++, td++) { - td->loc2d[0] = td->loc[0] * aspect_inv[0]; - td->loc2d[1] = td->loc[1] * aspect_inv[1]; + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; if (use_pixel_snap) { - td->loc2d[0] = roundf(td->loc2d[0] * size[0]) / size[0]; - td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1]; + int size_i[2]; + ED_space_image_get_size(sima, &size_i[0], &size_i[1]); + size[0] = size_i[0]; + size[1] = size_i[1]; + } + + /* flush to 2d vector from internally used 3d vector */ + for (a = 0, td = tc->data_2d; a < tc->data_len; a++, td++) { + td->loc2d[0] = td->loc[0] * aspect_inv[0]; + td->loc2d[1] = td->loc[1] * aspect_inv[1]; + + if (use_pixel_snap) { + td->loc2d[0] = roundf(td->loc2d[0] * size[0]) / size[0]; + td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1]; + } } } } bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { - TransData *td; - int a; bool clipx = true, clipy = true; float min[2], max[2]; @@ -3145,39 +3262,45 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) max[0] = t->aspect[0]; max[1] = t->aspect[1]; - for (a = 0, td = t->data; a < t->total; a++, td++) { - minmax_v2v2_v2(min, max, td->loc); - } + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (resize) { - if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f) - vec[0] *= t->center[0] / (t->center[0] - min[0]); - else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0]) - vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]); - else - clipx = 0; + TransData *td; + int a; - if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f) - vec[1] *= t->center[1] / (t->center[1] - min[1]); - else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1]) - vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]); - else - clipy = 0; - } - else { - if (min[0] < 0.0f) - vec[0] -= min[0]; - else if (max[0] > t->aspect[0]) - vec[0] -= max[0] - t->aspect[0]; - else - clipx = 0; + for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { + minmax_v2v2_v2(min, max, td->loc); + } - if (min[1] < 0.0f) - vec[1] -= min[1]; - else if (max[1] > t->aspect[1]) - vec[1] -= max[1] - t->aspect[1]; - else - clipy = 0; + 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]); + 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 + 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]); + 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 + clipy = 0; + } + else { + if (min[0] < 0.0f) + vec[0] -= min[0]; + else if (max[0] > t->aspect[0]) + vec[0] -= max[0] - t->aspect[0]; + else + clipx = 0; + + if (min[1] < 0.0f) + vec[1] -= min[1]; + else if (max[1] > t->aspect[1]) + vec[1] -= max[1] - t->aspect[1]; + else + clipy = 0; + } } return (clipx || clipy); @@ -3185,18 +3308,18 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) void clipUVData(TransInfo *t) { - TransData *td = NULL; - int a; - - for (a = 0, td = t->data; a < t->total; a++, td++) { - if (td->flag & TD_NOACTION) - break; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (int a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_NOACTION) + break; - if ((td->flag & TD_SKIP) || (!td->loc)) - continue; + if ((td->flag & TD_SKIP) || (!td->loc)) + continue; - td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]); - td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]); + td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]); + td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]); + } } } @@ -3231,6 +3354,8 @@ static void createTransNlaData(bContext *C, TransInfo *t) int count = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) return; @@ -3290,12 +3415,12 @@ static void createTransNlaData(bContext *C, TransInfo *t) } /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(NLA Editor)"); - td = t->data; - t->custom.type.data = tdn = MEM_callocN(t->total * sizeof(TransDataNla), "TransDataNla (NLA Editor)"); - t->custom.type.use_free = true; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(NLA Editor)"); + td = tc->data; + tc->custom.type.data = tdn = MEM_callocN(tc->data_len * sizeof(TransDataNla), "TransDataNla (NLA Editor)"); + tc->custom.type.use_free = true; /* loop 2: build transdata array */ for (ale = anim_data.first; ale; ale = ale->next) { @@ -3825,11 +3950,11 @@ typedef struct tGPFtransdata { /* This function helps flush transdata written to tempdata into the gp-frames */ void flushTransIntFrameActionData(TransInfo *t) { - tGPFtransdata *tfd = t->custom.type.data; - int i; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + tGPFtransdata *tfd = tc->custom.type.data; /* flush data! */ - for (i = 0; i < t->total; i++, tfd++) { + for (int i = 0; i < tc->data_len; i++, tfd++) { *(tfd->sdata) = round_fl_to_int(tfd->val); } } @@ -3986,17 +4111,19 @@ static void createTransActionData(bContext *C, TransInfo *t) return; } + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(Action Editor)"); - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "transdata2d"); - td = t->data; - td2d = t->data2d; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Action Editor)"); + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "transdata2d"); + td = tc->data; + td2d = tc->data_2d; if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - t->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); - t->custom.type.use_free = true; + tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); + tc->custom.type.use_free = true; } /* loop 2: build transdata array */ @@ -4041,7 +4168,7 @@ static void createTransActionData(bContext *C, TransInfo *t) /* calculate distances for proportional editing */ if (is_prop_edit) { - td = t->data; + td = tc->data; for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt; @@ -4384,18 +4511,20 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) return; } + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData (Graph Editor)"); + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData (Graph Editor)"); /* for each 2d vert a 3d vector is allocated, so that they can be treated just as if they were 3d verts */ - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D (Graph Editor)"); - t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataGraph), "TransDataGraph"); - t->custom.type.use_free = true; + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D (Graph Editor)"); + tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph"); + tc->custom.type.use_free = true; - td = t->data; - td2d = t->data2d; - tdg = t->custom.type.data; + td = tc->data; + td2d = tc->data_2d; + tdg = tc->custom.type.data; /* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */ unit_m3(mtx); @@ -4522,7 +4651,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (is_prop_edit) { /* loop 2: build transdata arrays */ - td = t->data; + td = tc->data; for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); @@ -4679,11 +4808,13 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve int i, j; char *adjusted; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* dynamically allocate an array of chars to mark whether an TransData's * pointers have been fixed already, so that we don't override ones that are * already done */ - adjusted = MEM_callocN(t->total, "beztmap_adjusted_map"); + adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map"); /* for each beztmap item, find if it is used anywhere */ bezm = bezms; @@ -4691,9 +4822,9 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve /* loop through transdata, testing if we have a hit * for the handles (vec[0]/vec[2]), we must also check if they need to be swapped... */ - td2d = t->data2d; - td = t->data; - for (j = 0; j < t->total; j++, td2d++, td++) { + td2d = tc->data_2d; + td = tc->data; + for (j = 0; j < tc->data_len; j++, td2d++, td++) { /* skip item if already marked */ if (adjusted[j] != 0) continue; @@ -4796,9 +4927,11 @@ void flushTransGraphData(TransInfo *t) double secf = FPS; int a; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d, tdg = t->custom.type.data; - a < t->total; + for (a = 0, td = tc->data, td2d = tc->data_2d, tdg = tc->custom.type.data; + a < tc->data_len; a++, td++, td2d++, tdg++) { AnimData *adt = (AnimData *)td->extra; /* pointers to relevant AnimData blocks are stored in the td->extra pointers */ @@ -5160,13 +5293,15 @@ static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts) } -static void freeSeqData(TransInfo *t, TransCustomData *custom_data) +static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) { Editing *ed = BKE_sequencer_editing_get(t->scene, false); if (ed != NULL) { + + ListBase *seqbasep = ed->seqbasep; - TransData *td = t->data; + TransData *td = tc->data; int a; /* prevent updating the same seq twice @@ -5194,7 +5329,7 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) { int overlap = 0; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) { overlap = 1; @@ -5207,8 +5342,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) for (seq = seqbasep->first; seq; seq = seq->next) seq->tmp = NULL; - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev)) { /* check effects strips, we cant change their time */ @@ -5231,8 +5366,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (t->flag & T_ALT_TRANSFORM) { int minframe = MAXFRAME; - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { minframe = min_ii(minframe, seq->startdisp); @@ -5268,8 +5403,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (has_effect_any) { /* update effects strips based on strips just moved in time */ - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev)) { if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { @@ -5281,8 +5416,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (has_effect_root) { /* now if any effects _still_ overlap, we need to move them up */ - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { @@ -5311,7 +5446,7 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) } else { /* Canceled, need to update the strips display */ - for (a = 0; a < t->total; a++, td++) { + for (a = 0; a < tc->data_len; a++, td++) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { if (seq->flag & SEQ_OVERLAP) @@ -5330,9 +5465,9 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) MEM_freeN(custom_data->data); custom_data->data = NULL; } - if (t->data) { - MEM_freeN(t->data); // XXX postTrans usually does this - t->data = NULL; + if (tc->data) { + MEM_freeN(tc->data); // XXX postTrans usually does this + tc->data = NULL; } } @@ -5351,12 +5486,14 @@ static void createTransSeqData(bContext *C, TransInfo *t) int count = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + if (ed == NULL) { - t->total = 0; + tc->data_len = 0; return; } - t->custom.type.free_cb = freeSeqData; + tc->custom.type.free_cb = freeSeqData; xmouse = (int)UI_view2d_region_to_view_x(v2d, t->mouse.imval[0]); @@ -5395,18 +5532,18 @@ static void createTransSeqData(bContext *C, TransInfo *t) count = SeqTransCount(t, NULL, ed->seqbasep, 0); /* allocate memory for data */ - t->total = count; + tc->data_len = count; /* stop if trying to build list if nothing selected */ if (count == 0) { return; } - t->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq"); - t->custom.type.use_free = true; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransSeq TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransSeq TransData2D"); - ts->tdseq = tdsq = MEM_callocN(t->total * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + tc->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq"); + tc->custom.type.use_free = true; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransSeq TransData2D"); + ts->tdseq = tdsq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); /* loop 2: build transdata array */ SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq); @@ -5535,11 +5672,11 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) if (skip_invert == false && constinv == false) { ob->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc_time checks this */ - BKE_object_where_is_calc(t->scene, ob); + BKE_object_where_is_calc(t->depsgraph, t->scene, ob); ob->transflag &= ~OB_NO_CONSTRAINTS; } else - BKE_object_where_is_calc(t->scene, ob); + BKE_object_where_is_calc(t->depsgraph, t->scene, ob); td->ob = ob; @@ -5606,85 +5743,96 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob) } } +static void trans_object_base_deps_flag_prepare(ViewLayer *view_layer) +{ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + base->object->id.tag &= ~LIB_TAG_DOIT; + } +} + +static void set_trans_object_base_deps_flag_cb(ID *id, void *UNUSED(user_data)) +{ + /* Here we only handle object IDs. */ + if (GS(id->name) != ID_OB) { + return; + } + id->tag |= LIB_TAG_DOIT; +} + +static void flush_trans_object_base_deps_flag(Depsgraph *depsgraph, Object *object) +{ + object->id.tag |= LIB_TAG_DOIT; + DEG_foreach_dependent_ID(depsgraph, &object->id, + set_trans_object_base_deps_flag_cb, NULL); +} + +static void trans_object_base_deps_flag_finish(ViewLayer *view_layer) +{ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (base->object->id.tag & LIB_TAG_DOIT) { + base->flag_legacy |= BA_SNAP_FIX_DEPS_FIASCO; + } + } +} /* sets flags in Bases to define whether they take part in transform */ /* it deselects Bases, so we have to call the clear function always after */ static void set_trans_object_base_flags(TransInfo *t) { Main *bmain = CTX_data_main(t->context); + ViewLayer *view_layer = t->view_layer; Scene *scene = t->scene; - View3D *v3d = t->view; - - /* - * if Base selected and has parent selected: - * base->flag = BA_WAS_SEL + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + /* NOTE: if Base selected and has parent selected: + * base->flag_legacy = BA_WAS_SEL */ - Base *base; - - /* don't do it if we're not actually going to recalculate anything */ - if (t->mode == TFM_DUMMY) + /* Don't do it if we're not actually going to recalculate anything. */ + if (t->mode == TFM_DUMMY) { return; - - /* makes sure base flags and object flags are identical */ - BKE_scene_base_flag_to_objects(t->scene); - - /* Make sure depsgraph is here. */ - DAG_scene_relations_update(bmain, t->scene); - - /* handle pending update events, otherwise they got copied below */ - for (base = scene->base.first; base; base = base->next) { - if (base->object->recalc & OB_RECALC_ALL) { - /* TODO(sergey): Ideally, it's not needed. */ - BKE_object_handle_update(bmain->eval_ctx, t->scene, base->object); - } } - - for (base = scene->base.first; base; base = base->next) { - base->flag &= ~BA_WAS_SEL; - - if (TESTBASELIB_BGMODE(v3d, scene, base)) { + /* Makes sure base flags and object flags are identical. */ + BKE_scene_base_flag_to_objects(t->view_layer); + /* Make sure depsgraph is here. */ + DEG_graph_relations_update(depsgraph, bmain, scene, view_layer); + /* Clear all flags we need. It will be used to detect dependencies. */ + trans_object_base_deps_flag_prepare(view_layer); + /* Traverse all bases and set all possible flags. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + base->flag_legacy &= ~BA_WAS_SEL; + if (TESTBASELIB_BGMODE(base)) { Object *ob = base->object; Object *parsel = ob->parent; - - /* if parent selected, deselect */ - while (parsel) { - if (parsel->flag & SELECT) { - Base *parbase = BKE_scene_base_find(scene, parsel); - if (parbase) { /* in rare cases this can fail */ - if (TESTBASELIB_BGMODE(v3d, scene, parbase)) { + /* If parent selected, deselect. */ + while (parsel != NULL) { + if (parsel->base_flag & BASE_SELECTED) { + Base *parbase = BKE_view_layer_base_find(view_layer, parsel); + if (parbase != NULL) { /* in rare cases this can fail */ + if (TESTBASELIB_BGMODE(parbase)) { break; } } } parsel = parsel->parent; } - - if (parsel) { - /* rotation around local centers are allowed to propagate */ + if (parsel != NULL) { + /* Rotation around local centers are allowed to propagate. */ if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)) { - base->flag |= BA_TRANSFORM_CHILD; + base->flag_legacy |= BA_TRANSFORM_CHILD; } else { - base->flag &= ~SELECT; - base->flag |= BA_WAS_SEL; + base->flag &= ~BASE_SELECTED; + base->flag_legacy |= BA_WAS_SEL; } } - DAG_id_tag_update(&ob->id, OB_RECALC_OB); - } - } - - /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ - DAG_scene_flush_update(bmain, t->scene, -1, 0); - - /* and we store them temporal in base (only used for transform code) */ - /* this because after doing updates, the object->recalc is cleared */ - for (base = scene->base.first; base; base = base->next) { - if (base->object->recalc & (OB_RECALC_OB | OB_RECALC_DATA)) { - base->flag |= BA_SNAP_FIX_DEPS_FIASCO; + flush_trans_object_base_deps_flag(depsgraph, ob); } } + /* Store temporary bits in base indicating that base is being modified + * (directly or indirectly) by transforming objects. + */ + trans_object_base_deps_flag_finish(view_layer); } static bool mark_children(Object *ob) @@ -5705,79 +5853,69 @@ static bool mark_children(Object *ob) static int count_proportional_objects(TransInfo *t) { int total = 0; - Main *bmain = CTX_data_main(t->context); + ViewLayer *view_layer = t->view_layer; Scene *scene = t->scene; - View3D *v3d = t->view; - Base *base; - - /* rotations around local centers are allowed to propagate, so we take all objects */ + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer, true); + /* Clear all flags we need. It will be used to detect dependencies. */ + trans_object_base_deps_flag_prepare(view_layer); + /* Rotations around local centers are allowed to propagate, so we take all objects. */ if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL))) { - /* mark all parents */ - for (base = scene->base.first; base; base = base->next) { - if (TESTBASELIB_BGMODE(v3d, scene, base)) { + /* Mark all parents. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + if (TESTBASELIB_BGMODE(base)) { Object *parent = base->object->parent; - /* flag all parents */ - while (parent) { + while (parent != NULL) { parent->flag |= BA_TRANSFORM_PARENT; parent = parent->parent; } } } - - /* mark all children */ - for (base = scene->base.first; base; base = base->next) { + /* Mark all children. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { /* all base not already selected or marked that is editable */ - if ((base->object->flag & (SELECT | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && - (BASE_EDITABLE_BGMODE(v3d, scene, base))) + if ((base->object->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && + (base->flag & BASE_SELECTED) == 0 && + (BASE_EDITABLE_BGMODE(base))) { mark_children(base->object); } } } - - for (base = scene->base.first; base; base = base->next) { + /* Flush changed flags to all dependencies. */ + for (Base *base = view_layer->object_bases.first; base; base = base->next) { Object *ob = base->object; - - /* if base is not selected, not a parent of selection or not a child of selection and it is editable */ - if ((ob->flag & (SELECT | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && - (BASE_EDITABLE_BGMODE(v3d, scene, base))) + /* If base is not selected, not a parent of selection or not a child of + * selection and it is editable. + */ + if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && + (base->flag & BASE_SELECTED) == 0 && + (BASE_EDITABLE_BGMODE(base))) { - - DAG_id_tag_update(&ob->id, OB_RECALC_OB); - + flush_trans_object_base_deps_flag(depsgraph, ob); total += 1; } } - - - /* all recalc flags get flushed to all layers, so a layer flip later on works fine */ - DAG_scene_relations_update(bmain, t->scene); - DAG_scene_flush_update(bmain, t->scene, -1, 0); - - /* and we store them temporal in base (only used for transform code) */ - /* this because after doing updates, the object->recalc is cleared */ - for (base = scene->base.first; base; base = base->next) { - if (base->object->recalc & (OB_RECALC_OB | OB_RECALC_DATA)) { - base->flag |= BA_SNAP_FIX_DEPS_FIASCO; - } - } - + /* Store temporary bits in base indicating that base is being modified + * (directly or indirectly) by transforming objects. + */ + trans_object_base_deps_flag_finish(view_layer); return total; } static void clear_trans_object_base_flags(TransInfo *t) { - Scene *sce = t->scene; + ViewLayer *view_layer = t->view_layer; Base *base; - for (base = sce->base.first; base; base = base->next) { - if (base->flag & BA_WAS_SEL) - base->flag |= SELECT; + for (base = view_layer->object_bases.first; base; base = base->next) { + if (base->flag_legacy & BA_WAS_SEL) { + base->flag |= BASE_SELECTED; + } - base->flag &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT); + base->flag_legacy &= ~(BA_WAS_SEL | BA_SNAP_FIX_DEPS_FIASCO | BA_TEMP_TAG | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT); } } @@ -5785,7 +5923,7 @@ static void clear_trans_object_base_flags(TransInfo *t) * tmode: should be a transform mode */ // NOTE: context may not always be available, so must check before using it as it's a luxury for a few cases -void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, int tmode) +void autokeyframe_ob_cb_func(bContext *C, Scene *scene, ViewLayer *view_layer, Object *ob, int tmode) { Main *bmain = CTX_data_main(C); ID *id = &ob->id; @@ -5793,6 +5931,7 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, // TODO: this should probably be done per channel instead... if (autokeyframe_cfra_can_key(scene, id)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); @@ -5819,7 +5958,7 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, if (adt && adt->action) { for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) { fcu->flag &= ~FCURVE_SELECTED; - insert_keyframe(bmain, reports, id, adt->action, + insert_keyframe(bmain, depsgraph, reports, id, adt->action, (fcu->grp ? fcu->grp->name : NULL), fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag); @@ -5834,26 +5973,30 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, do_loc = true; } else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { - if (v3d->around == V3D_AROUND_ACTIVE) { - if (ob != OBACT) + if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { + if (ob != OBACT(view_layer)) do_loc = true; } - else if (v3d->around == V3D_AROUND_CURSOR) + else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { do_loc = true; + } - if ((v3d->flag & V3D_ALIGN) == 0) + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { do_rot = true; + } } else if (tmode == TFM_RESIZE) { - if (v3d->around == V3D_AROUND_ACTIVE) { - if (ob != OBACT) + if (scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) { + if (ob != OBACT(view_layer)) do_loc = true; } - else if (v3d->around == V3D_AROUND_CURSOR) + else if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) { do_loc = true; + } - if ((v3d->flag & V3D_ALIGN) == 0) + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { do_scale = true; + } } /* insert keyframes for the affected sets of channels using the builtin KeyingSets found */ @@ -5902,7 +6045,7 @@ void autokeyframe_ob_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, * targetless_ik: has targetless ik been done on any channels? */ // NOTE: context may not always be available, so must check before using it as it's a luxury for a few cases -void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *ob, int tmode, short targetless_ik) +void autokeyframe_pose_cb_func(bContext *C, Scene *scene, Object *ob, int tmode, short targetless_ik) { Main *bmain = CTX_data_main(C); ID *id = &ob->id; @@ -5914,6 +6057,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o // TODO: this should probably be done per channel instead... if (autokeyframe_cfra_can_key(scene, id)) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); ReportList *reports = CTX_wm_reports(C); ToolSettings *ts = scene->toolsettings; KeyingSet *active_ks = ANIM_scene_get_active_keyingset(scene); @@ -5957,7 +6101,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o * NOTE: this will do constraints too, but those are ok to do here too? */ if (pchanName && STREQ(pchanName, pchan->name)) { - insert_keyframe(bmain, reports, id, act, + insert_keyframe(bmain, depsgraph, reports, id, act, ((fcu->grp) ? (fcu->grp->name) : (NULL)), fcu->rna_path, fcu->array_index, cfra, ts->keyframe_type, flag); @@ -5980,18 +6124,22 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o do_loc = true; } else if (ELEM(tmode, TFM_ROTATION, TFM_TRACKBALL)) { - if (ELEM(v3d->around, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) + if (ELEM(scene->toolsettings->transform_pivot_point, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) { do_loc = true; + } - if ((v3d->flag & V3D_ALIGN) == 0) + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { do_rot = true; + } } else if (tmode == TFM_RESIZE) { - if (ELEM(v3d->around, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) + if (ELEM(scene->toolsettings->transform_pivot_point, V3D_AROUND_CURSOR, V3D_AROUND_ACTIVE)) { do_loc = true; + } - if ((v3d->flag & V3D_ALIGN) == 0) + if ((scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) == 0) { do_scale = true; + } } if (do_loc) { @@ -6025,7 +6173,7 @@ void autokeyframe_pose_cb_func(bContext *C, Scene *scene, View3D *v3d, Object *o */ if (C && (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS)) { //ED_pose_clear_paths(C, ob); // XXX for now, don't need to clear - ED_pose_recalculate_paths(scene, ob); + ED_pose_recalculate_paths(C, scene, ob); } } else { @@ -6136,39 +6284,42 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) { /* so automerge supports mirror */ if ((t->scene->toolsettings->automerge) && - (t->obedit && t->obedit->type == OB_MESH)) + ((t->flag & T_EDIT) && t->obedit_type == OB_MESH)) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - BMesh *bm = em->bm; - char hflag; - bool has_face_sel = (bm->totfacesel != 0); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if (t->flag & T_MIRROR) { - TransData *td; - int i; + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + BMesh *bm = em->bm; + char hflag; + bool has_face_sel = (bm->totfacesel != 0); - /* rather then adjusting the selection (which the user would notice) - * tag all mirrored verts, then automerge those */ - BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + if (t->flag & T_MIRROR) { + TransData *td; + int i; - for (i = 0, td = t->data; i < t->total; i++, td++) { - if (td->extra) { - BM_elem_flag_enable((BMVert *)td->extra, BM_ELEM_TAG); + /* rather then adjusting the selection (which the user would notice) + * tag all mirrored verts, then automerge those */ + BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); + + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + if (td->extra) { + BM_elem_flag_enable((BMVert *)td->extra, BM_ELEM_TAG); + } } - } - hflag = BM_ELEM_SELECT | BM_ELEM_TAG; - } - else { - hflag = BM_ELEM_SELECT; - } + hflag = BM_ELEM_SELECT | BM_ELEM_TAG; + } + else { + hflag = BM_ELEM_SELECT; + } - EDBM_automerge(t->scene, t->obedit, true, hflag); + EDBM_automerge(t->scene, tc->obedit, true, hflag); - /* Special case, this is needed or faces won't re-select. - * Flush selected edges to faces. */ - if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) { - EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE); + /* Special case, this is needed or faces won't re-select. + * Flush selected edges to faces. */ + if (has_face_sel && (em->selectmode == SCE_SELECT_FACE)) { + EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE); + } } } } @@ -6189,50 +6340,53 @@ void special_aftertrans_update(bContext *C, TransInfo *t) const bool duplicate = (t->mode == TFM_TIME_DUPLICATE); /* early out when nothing happened */ - if (t->total == 0 || t->mode == TFM_DUMMY) + if (t->data_len_all == 0 || t->mode == TFM_DUMMY) { return; + } if (t->spacetype == SPACE_VIEW3D) { - if (t->obedit) { + if (t->flag & T_EDIT) { /* Special Exception: * We don't normally access 't->custom.mode' here, but its needed in this case. */ if (canceled == 0) { /* we need to delete the temporary faces before automerging */ if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; - /* handle multires re-projection, done * on transform completion since it's * really slow -joeedh */ projectEdgeSlideData(t, true); - /* free temporary faces to avoid automerging and deleting - * during cleanup - psy-fi */ - freeEdgeSlideTempFaces(sld); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + + /* free temporary faces to avoid automerging and deleting + * during cleanup - psy-fi */ + freeEdgeSlideTempFaces(sld); + } } else if (t->mode == TFM_VERT_SLIDE) { /* as above */ - VertSlideData *sld = t->custom.mode.data; projectVertSlideData(t, true); - freeVertSlideTempFaces(sld); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; + freeVertSlideTempFaces(sld); + } } - if (t->obedit->type == OB_MESH) { + if (t->obedit_type == OB_MESH) { special_aftertrans_update__mesh(C, t); } } else { if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; - - sld->perc = 0.0; + EdgeSlideParams *slp = t->custom.mode.data; + slp->perc = 0.0; projectEdgeSlideData(t, false); } else if (t->mode == TFM_VERT_SLIDE) { - VertSlideData *sld = t->custom.mode.data; - - sld->perc = 0.0; + EdgeSlideParams *slp = t->custom.mode.data; + slp->perc = 0.0; projectVertSlideData(t, false); } } @@ -6338,9 +6492,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) // fixme... some of this stuff is not good if (ob) { if (ob->pose || BKE_key_from_object(ob)) - DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); + DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); else - DAG_id_tag_update(&ob->id, OB_RECALC_OB); + DEG_id_tag_update(&ob->id, OB_RECALC_OB); } /* 3 cases here for curve cleanups: @@ -6506,78 +6660,95 @@ void special_aftertrans_update(bContext *C, TransInfo *t) ED_nla_postop_refresh(&ac); } } - else if (t->obedit) { - if (t->obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - /* table needs to be created for each edit command, since vertices can move etc */ - ED_mesh_mirror_spatial_table(t->obedit, em, NULL, NULL, 'e'); + else if (t->flag & T_EDIT) { + if (t->obedit_type == OB_MESH) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + /* table needs to be created for each edit command, since vertices can move etc */ + ED_mesh_mirror_spatial_table(tc->obedit, em, NULL, NULL, 'e'); + /* TODO(campbell): xform: We need support for many mirror objects at once! */ + break; + } } } - else if ((t->flag & T_POSE) && (t->poseobj)) { - bArmature *arm; - bPoseChannel *pchan; - short targetless_ik = 0; + else if (t->flag & T_POSE) { - ob = t->poseobj; - arm = ob->data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) { - /* when running transform non-interactively (operator exec), - * we need to update the pose otherwise no updates get called during - * transform and the auto-ik is not applied. see [#26164] */ - struct Object *pose_ob = t->poseobj; - BKE_pose_where_is(t->scene, pose_ob); - } + bArmature *arm; + bPoseChannel *pchan; + short targetless_ik = 0; - /* set BONE_TRANSFORM flags for autokey, manipulator draw might have changed them */ - if (!canceled && (t->mode != TFM_DUMMY)) - count_set_pose_transflags(&t->mode, t->around, ob); + ob = tc->poseobj; + arm = ob->data; - /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */ - if (!canceled && t->mode == TFM_TRANSLATION) - targetless_ik = apply_targetless_ik(ob); - else { - /* not forget to clear the auto flag */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - bKinematicConstraint *data = has_targetless_ik(pchan); - if (data) data->flag &= ~CONSTRAINT_IK_AUTO; + if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) { + /* when running transform non-interactively (operator exec), + * we need to update the pose otherwise no updates get called during + * transform and the auto-ik is not applied. see [#26164] */ + struct Object *pose_ob = tc->poseobj; + BKE_pose_where_is(t->depsgraph, t->scene, pose_ob); } - } - if (t->mode == TFM_TRANSLATION) - pose_grab_with_ik_clear(bmain, ob); + /* set BONE_TRANSFORM flags for autokey, manipulator draw might have changed them */ + if (!canceled && (t->mode != TFM_DUMMY)) { + count_set_pose_transflags(ob, t->mode, t->around, NULL); + } - /* automatic inserting of keys and unkeyed tagging - only if transform wasn't canceled (or TFM_DUMMY) */ - if (!canceled && (t->mode != TFM_DUMMY)) { - autokeyframe_pose_cb_func(C, t->scene, (View3D *)t->view, ob, t->mode, targetless_ik); - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - else if (arm->flag & ARM_DELAYDEFORM) { - /* old optimize trick... this enforces to bypass the depgraph */ - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); - ob->recalc = 0; // is set on OK position already by recalcData() - } - else - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + /* if target-less IK grabbing, we calculate the pchan transforms and clear flag */ + if (!canceled && t->mode == TFM_TRANSLATION) + targetless_ik = apply_targetless_ik(ob); + else { + /* not forget to clear the auto flag */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + bKinematicConstraint *data = has_targetless_ik(pchan); + if (data) data->flag &= ~CONSTRAINT_IK_AUTO; + } + } + + if (t->mode == TFM_TRANSLATION) + pose_grab_with_ik_clear(bmain, ob); + /* automatic inserting of keys and unkeyed tagging - only if transform wasn't canceled (or TFM_DUMMY) */ + if (!canceled && (t->mode != TFM_DUMMY)) { + autokeyframe_pose_cb_func(C, t->scene, ob, t->mode, targetless_ik); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + else if (arm->flag & ARM_DELAYDEFORM) { + /* TODO(sergey): Armature is already updated by recalcData(), so we + * might save some time by skipping re-evaluating it. But this isn't + * possible yet within new dependency graph, and also other contexts + * might need to update their CoW copies. + */ + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + else { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + } } else if (t->options & CTX_PAINT_CURVE) { /* pass */ } - else if ((t->scene->basact) && - (ob = t->scene->basact->object) && + else if ((t->view_layer->basact) && + (ob = t->view_layer->basact->object) && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, ob)) { /* do nothing */ } + else if (t->flag & T_CURSOR) { + /* do nothing */ + } else { /* Objects */ int i; BLI_assert(t->flag & (T_OBJECT | T_TEXTURE)); - for (i = 0; i < t->total; i++) { - TransData *td = t->data + i; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + for (i = 0; i < tc->data_len; i++) { + TransData *td = tc->data + i; ListBase pidlist; PTCacheID *pid; ob = td->ob; @@ -6598,17 +6769,17 @@ void special_aftertrans_update(bContext *C, TransInfo *t) /* pointcache refresh */ if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); /* Needed for proper updating of "quick cached" dynamics. */ /* Creates troubles for moving animated objects without */ /* autokey though, probably needed is an anim sys override? */ /* Please remove if some other solution is found. -jahka */ - DAG_id_tag_update(&ob->id, OB_RECALC_OB); + DEG_id_tag_update(&ob->id, OB_RECALC_OB); /* Set autokey if necessary */ if (!canceled) { - autokeyframe_ob_cb_func(C, t->scene, (View3D *)t->view, ob, t->mode); + autokeyframe_ob_cb_func(C, t->scene, t->view_layer, ob, t->mode); } /* restore rigid body transform */ @@ -6631,7 +6802,7 @@ int special_transform_moving(TransInfo *t) else if (t->spacetype == SPACE_IPO) { return G_TRANSFORM_FCURVES; } - else if (t->obedit || ((t->flag & T_POSE) && (t->poseobj))) { + else if ((t->flag & T_EDIT) || (t->flag & T_POSE)) { return G_TRANSFORM_EDIT; } else if (t->flag & (T_OBJECT | T_TEXTURE)) { @@ -6643,29 +6814,29 @@ int special_transform_moving(TransInfo *t) static void createTransObject(bContext *C, TransInfo *t) { - Scene *scene = t->scene; - TransData *td = NULL; TransDataExtension *tx; const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; set_trans_object_base_flags(t); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = CTX_DATA_COUNT(C, selected_objects); + tc->data_len = CTX_DATA_COUNT(C, selected_objects); - if (!t->total) { + if (!tc->data_len) { /* clear here, main transform function escapes too */ clear_trans_object_base_flags(t); return; } if (is_prop_edit) { - t->total += count_proportional_objects(t); + tc->data_len += count_proportional_objects(t); } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransOb"); - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "TransObExtension"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension"); CTX_DATA_BEGIN(C, Base *, base, selected_bases) { @@ -6694,15 +6865,16 @@ static void createTransObject(bContext *C, TransInfo *t) CTX_DATA_END; if (is_prop_edit) { - View3D *v3d = t->view; + ViewLayer *view_layer = t->view_layer; Base *base; - for (base = scene->base.first; base; base = base->next) { + for (base = view_layer->object_bases.first; base; base = base->next) { Object *ob = base->object; /* if base is not selected, not a parent of selection or not a child of selection and it is editable */ - if ((ob->flag & (SELECT | BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && - BASE_EDITABLE_BGMODE(v3d, scene, base)) + if ((ob->flag & (BA_TRANSFORM_CHILD | BA_TRANSFORM_PARENT)) == 0 && + (base->flag & BASE_SELECTED) == 0 && + BASE_EDITABLE_BGMODE(base)) { td->protectflag = ob->protectflag; td->ext = tx; @@ -6784,7 +6956,9 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) SpaceNode *snode = t->sa->spacedata.first; bNode *node; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!snode->edittree) { return; @@ -6797,15 +6971,15 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) for (node = snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_SELECT && is_node_parent_select(node) == false) { node->flag |= NODE_TRANSFORM; - t->total++; + tc->data_len++; } else { node->flag &= ~NODE_TRANSFORM; } } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransNode TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransNode TransData2D"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransNode TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransNode TransData2D"); for (node = snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_TRANSFORM) { @@ -6990,7 +7164,7 @@ static void planeTrackToTransData(const int framenr, TransData *td, TransData2D } } -static void transDataTrackingFree(TransInfo *UNUSED(t), TransCustomData *custom_data) +static void transDataTrackingFree(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { if (custom_data->data) { TransDataTracking *tdt = custom_data->data; @@ -7015,22 +7189,24 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) TransDataTracking *tdt; int framenr = ED_space_clip_get_clip_frame_number(sc); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = 0; + tc->data_len = 0; track = tracksbase->first; while (track) { if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - t->total++; /* offset */ + tc->data_len++; /* offset */ if (track->flag & SELECT) - t->total++; + tc->data_len++; if (track->pat_flag & SELECT) - t->total += 4; + tc->data_len += 4; if (track->search_flag & SELECT) - t->total += 2; + tc->data_len += 2; } track = track->next; @@ -7041,18 +7217,18 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) plane_track = plane_track->next) { if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - t->total += 4; + tc->data_len += 4; } } - if (t->total == 0) + if (tc->data_len == 0) return; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); - tdt = t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransTracking TransData2D"); + tdt = tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataTracking), "TransTracking TransDataTracking"); - t->custom.type.free_cb = transDataTrackingFree; + tc->custom.type.free_cb = transDataTrackingFree; /* create actual data */ track = tracksbase->first; @@ -7154,8 +7330,10 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t) BKE_movieclip_get_size(clip, &sc->user, &width, &height); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = 0; + tc->data_len = 0; if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) { return; @@ -7172,23 +7350,23 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t) continue; if (marker->flag & MARKER_GRAPH_SEL_X) - t->total += 1; + tc->data_len += 1; if (marker->flag & MARKER_GRAPH_SEL_Y) - t->total += 1; + tc->data_len += 1; } } track = track->next; } - if (t->total == 0) + if (tc->data_len == 0) return; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); - t->custom.type.data = tdt = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); - t->custom.type.free_cb = transDataTrackingFree; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransTracking TransData2D"); + tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTracking), "TransTracking TransDataTracking"); + tc->custom.type.free_cb = transDataTrackingFree; /* create actual data */ track = tracksbase->first; @@ -7229,7 +7407,9 @@ static void createTransTrackingData(bContext *C, TransInfo *t) MovieClip *clip = ED_space_clip_get_clip(sc); int width, height; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!clip) return; @@ -7250,12 +7430,14 @@ static void createTransTrackingData(bContext *C, TransInfo *t) static void cancelTransTracking(TransInfo *t) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); SpaceClip *sc = t->sa->spacedata.first; int i, framenr = ED_space_clip_get_clip_frame_number(sc); - TransDataTracking *tdt_array = t->custom.type.data; + TransDataTracking *tdt_array = tc->custom.type.data; + i = 0; - while (i < t->total) { + while (i < tc->data_len) { TransDataTracking *tdt = &tdt_array[i]; if (tdt->mode == transDataTracking_ModeTracks) { @@ -7312,8 +7494,10 @@ void flushTransTracking(TransInfo *t) if (t->state == TRANS_CANCEL) cancelTransTracking(t); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d, tdt = t->custom.type.data; a < t->total; a++, td2d++, td++, tdt++) { + for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len; a++, td2d++, td++, tdt++) { if (tdt->mode == transDataTracking_ModeTracks) { float loc2d[2]; @@ -7579,7 +7763,9 @@ static void createTransMaskingData(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT); float asp[2]; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!mask) return; @@ -7639,13 +7825,13 @@ static void createTransMaskingData(bContext *C, TransInfo *t) ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); - t->total = (is_prop_edit) ? count : countsel; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mask Editing)"); + tc->data_len = (is_prop_edit) ? count : countsel; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mask Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransObData2D(Mask Editing)"); - t->custom.type.data = tdm = MEM_callocN(t->total * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)"); - t->custom.type.use_free = true; + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(Mask Editing)"); + tc->custom.type.data = tdm = MEM_callocN(tc->data_len * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)"); + tc->custom.type.use_free = true; /* create data */ for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { @@ -7702,12 +7888,14 @@ void flushTransMasking(TransInfo *t) int a; float asp[2], inv[2]; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); inv[0] = 1.0f / asp[0]; inv[1] = 1.0f / asp[1]; /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data2d, tdm = t->custom.type.data; a < t->total; a++, td++, tdm++) { + for (a = 0, td = tc->data_2d, tdm = tc->custom.type.data; a < tc->data_len; a++, td++, tdm++) { td->loc2d[0] = td->loc[0] * inv[0]; td->loc2d[1] = td->loc[1] * inv[1]; mul_m3_v2(tdm->parent_inverse_matrix, td->loc2d); @@ -7826,7 +8014,9 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) int i; int total = 0; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!paint || !paint->brush || !paint->brush->paint_curve) return; @@ -7852,11 +8042,11 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) if (!total) return; - t->total = total; - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D"); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransData"); - t->custom.type.data = tdpc = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); - t->custom.type.use_free = true; + tc->data_len = total; + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData"); + tc->custom.type.data = tdpc = MEM_callocN(tc->data_len * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); + tc->custom.type.use_free = true; for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { if (PC_IS_ANY_SEL(pcp)) { @@ -7887,10 +8077,13 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) void flushTransPaintCurve(TransInfo *t) { int i; - TransData2D *td2d = t->data2d; - TransDataPaintCurve *tdpc = t->custom.type.data; - for (i = 0; i < t->total; i++, tdpc++, td2d++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + TransData2D *td2d = tc->data_2d; + TransDataPaintCurve *tdpc = tc->custom.type.data; + + for (i = 0; i < tc->data_len; i++, tdpc++, td2d++) { PaintCurvePoint *pcp = tdpc->pcp; copy_v2_v2(pcp->bez.vec[tdpc->id], td2d->loc); } @@ -7910,6 +8103,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* == Grease Pencil Strokes to Transform Data == * Grease Pencil stroke points can be a mixture of 2D (screen-space), @@ -7917,7 +8111,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) * For now, we just do these without creating TransData2D for the 2D * strokes. This may cause issues in future though. */ - t->total = 0; + tc->data_len = 0; if (gpd == NULL) return; @@ -7946,11 +8140,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (is_prop_edit_connected) { /* connected only - so only if selected */ if (gps->flag & GP_STROKE_SELECT) - t->total += gps->totpoints; + tc->data_len += gps->totpoints; } else { /* everything goes - connection status doesn't matter */ - t->total += gps->totpoints; + tc->data_len += gps->totpoints; } } else { @@ -7962,7 +8156,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) // TODO: 2D vs 3D? for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) - t->total++; + tc->data_len++; } } } @@ -7971,13 +8165,13 @@ static void createTransGPencil(bContext *C, TransInfo *t) } /* Stop trying if nothing selected */ - if (t->total == 0) { + if (tc->data_len == 0) { return; } /* Allocate memory for data */ - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(GPencil)"); - td = t->data; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)"); + td = tc->data; unit_m3(smtx); unit_m3(mtx); @@ -8145,22 +8339,65 @@ static void createTransGPencil(bContext *C, TransInfo *t) } } +static int countAndCleanTransDataContainer(TransInfo *t) +{ + BLI_assert(ELEM(t->data_len_all, 0, -1)); + t->data_len_all = 0; + uint data_container_len_orig = t->data_container_len; + for (TransDataContainer *th_end = t->data_container - 1, *tc = t->data_container + (t->data_container_len - 1); tc != th_end; tc--) { + if (tc->data_len == 0) { + uint index = tc - t->data_container; + if (index + 1 != t->data_container_len) { + SWAP(TransDataContainer, t->data_container[index], t->data_container[t->data_container_len - 1]); + } + t->data_container_len -= 1; + } + else { + t->data_len_all += tc->data_len; + } + } + if (data_container_len_orig != t->data_container_len) { + t->data_container = MEM_reallocN(t->data_container, sizeof(*t->data_container) * t->data_container_len); + } + return t->data_len_all; +} + void createTransData(bContext *C, TransInfo *t) { Scene *scene = t->scene; - Object *ob = OBACT; + ViewLayer *view_layer = t->view_layer; + Object *ob = OBACT(view_layer); + + t->data_len_all = -1; /* if tests must match recalcData for correct updates */ - if (t->options & CTX_TEXTURE) { + if (t->options & CTX_CURSOR) { + t->flag |= T_CURSOR; + t->obedit_type = -1; + + createTransCursor3D(t); + countAndCleanTransDataContainer(t); + } + else if (t->options & CTX_TEXTURE) { t->flag |= T_TEXTURE; + t->obedit_type = -1; + createTransTexspace(t); + countAndCleanTransDataContainer(t); } else if (t->options & CTX_EDGE) { - t->ext = NULL; + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->data_ext = NULL; + } t->flag |= T_EDIT; + createTransEdge(t); - if (t->data && t->flag & T_PROP_EDIT) { + countAndCleanTransDataContainer(t); + + if (t->data_len_all && t->flag & T_PROP_EDIT) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8169,9 +8406,11 @@ void createTransData(bContext *C, TransInfo *t) else if (t->options & CTX_GPENCIL_STROKES) { t->options |= CTX_GPENCIL_STROKES; t->flag |= T_POINTS; + createTransGPencil(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8180,22 +8419,32 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IMAGE) { t->flag |= T_POINTS | T_2D_EDIT; if (t->options & CTX_MASK) { + /* copied from below */ createTransMaskingData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, true); sort_trans_data_dist(t); } } else if (t->options & CTX_PAINT_CURVE) { - if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) + if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { createTransPaintCurveVerts(C, t); + countAndCleanTransDataContainer(t); + } } - else if (t->obedit) { + else if (t->obedit_type == OB_MESH) { + + initTransDataContainers_FromObjectData(t); createTransUVs(C, t); - if (t->data && (t->flag & T_PROP_EDIT)) { + countAndCleanTransDataContainer(t); + + t->flag |= T_EDIT; + + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8204,9 +8453,12 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_ACTION) { t->flag |= T_POINTS | T_2D_EDIT; + t->obedit_type = -1; + createTransActionData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array //set_prop_dist(t, false); /* don't do that, distance has been set in createTransActionData already */ sort_trans_data_dist(t); @@ -8214,18 +8466,27 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS | T_2D_EDIT; + t->obedit_type = -1; + createTransNlaData(C, t); + countAndCleanTransDataContainer(t); } else if (t->spacetype == SPACE_SEQ) { t->flag |= T_POINTS | T_2D_EDIT; + t->obedit_type = -1; + t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transformations */ createTransSeqData(C, t); + countAndCleanTransDataContainer(t); } else if (t->spacetype == SPACE_IPO) { t->flag |= T_POINTS | T_2D_EDIT; + t->obedit_type = -1; + createTransGraphEditData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, false); /* don't do that, distance has been set in createTransGraphEditData already */ sort_trans_data_dist(t); @@ -8233,8 +8494,12 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_NODE) { t->flag |= T_POINTS | T_2D_EDIT; + t->obedit_type = -1; + createTransNodeData(C, t); - if (t->data && (t->flag & T_PROP_EDIT)) { + countAndCleanTransDataContainer(t); + + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8242,34 +8507,44 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_CLIP) { t->flag |= T_POINTS | T_2D_EDIT; - if (t->options & CTX_MOVIECLIP) + t->obedit_type = -1; + + if (t->options & CTX_MOVIECLIP) { createTransTrackingData(C, t); + countAndCleanTransDataContainer(t); + } else if (t->options & CTX_MASK) { /* copied from above */ createTransMaskingData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, true); sort_trans_data_dist(t); } } } - else if (t->obedit) { - t->ext = NULL; - if (t->obedit->type == OB_MESH) { + else if (t->obedit_type != -1) { + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->data_ext = NULL; + } + if (t->obedit_type == OB_MESH) { createTransEditVerts(t); } - else if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { createTransCurveVerts(t); } - else if (t->obedit->type == OB_LATTICE) { + else if (t->obedit_type == OB_LATTICE) { createTransLatticeVerts(t); } - else if (t->obedit->type == OB_MBALL) { + else if (t->obedit_type == OB_MBALL) { createTransMBallVerts(t); } - else if (t->obedit->type == OB_ARMATURE) { + else if (t->obedit_type == OB_ARMATURE) { t->flag &= ~T_PROP_EDIT; createTransArmatureVerts(t); } @@ -8277,12 +8552,14 @@ void createTransData(bContext *C, TransInfo *t) printf("edit type not implemented!\n"); } + countAndCleanTransDataContainer(t); + t->flag |= T_EDIT | T_POINTS; - if (t->data && t->flag & T_PROP_EDIT) { - if (ELEM(t->obedit->type, OB_CURVE, OB_MESH)) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { + if (ELEM(t->obedit_type, OB_CURVE, OB_MESH)) { sort_trans_data(t); // makes selected become first in array - if ((t->obedit->type == OB_MESH) && (t->flag & T_PROP_CONNECTED)) { + if ((t->obedit_type == OB_MESH) && (t->flag & T_PROP_CONNECTED)) { /* already calculated by editmesh_set_connectivity_distance */ } else { @@ -8301,34 +8578,50 @@ void createTransData(bContext *C, TransInfo *t) if (t->mode == TFM_BONESIZE) { t->flag &= ~(T_EDIT | T_POINTS); t->flag |= T_POSE; - t->poseobj = ob; /* <- tsk tsk, this is going to give issues one day */ + t->obedit_type = -1; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->poseobj = tc->obedit; + tc->obedit = NULL; + } } } else if (ob && (ob->mode & OB_MODE_POSE)) { // XXX this is currently limited to active armature only... // XXX active-layer checking isn't done as that should probably be checked through context instead - createTransPose(t, ob); + + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + createTransPose(t, NULL, 0); + countAndCleanTransDataContainer(t); } else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { /* important that ob_armature can be set even when its not selected [#23412] * lines below just check is also visible */ Object *ob_armature = modifiers_isDeformedByArmature(ob); if (ob_armature && ob_armature->mode & OB_MODE_POSE) { - Base *base_arm = BKE_scene_base_find(t->scene, ob_armature); + Base *base_arm = BKE_view_layer_base_find(t->view_layer, ob_armature); if (base_arm) { - View3D *v3d = t->view; - if (BASE_VISIBLE(v3d, base_arm)) { - createTransPose(t, ob_armature); + if (BASE_VISIBLE(base_arm)) { + Object *objects[1]; + objects[0] = ob_armature; + uint objects_len = 1; + createTransPose(t, objects, objects_len); + countAndCleanTransDataContainer(t); } } - + } + /* Mark as initialized if above checks fail. */ + if (t->data_len_all == -1) { + t->data_len_all = 0; } } else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) { createTransParticleVerts(C, t); + countAndCleanTransDataContainer(t); t->flag |= T_POINTS; - if (t->data && t->flag & T_PROP_EDIT) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8338,13 +8631,15 @@ void createTransData(bContext *C, TransInfo *t) if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { t->flag |= T_POINTS | T_2D_EDIT; createTransPaintCurveVerts(C, t); + countAndCleanTransDataContainer(t); } } else { createTransObject(C, t); + countAndCleanTransDataContainer(t); t->flag |= T_OBJECT; - if (t->data && t->flag & T_PROP_EDIT) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { // selected objects are already first, no need to presort set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8356,10 +8651,15 @@ void createTransData(bContext *C, TransInfo *t) RegionView3D *rv3d = t->ar->regiondata; if ((rv3d->persp == RV3D_CAMOB) && v3d->camera) { /* we could have a flag to easily check an object is being transformed */ - if (v3d->camera->recalc) { + if (v3d->camera->id.tag & LIB_TAG_DOIT) { t->flag |= T_CAMERA; } } } } + + /* Check that 'countAndCleanTransDataContainer' ran. */ + BLI_assert(t->data_len_all != -1); + + BLI_assert((!(t->flag & T_EDIT)) == (!(t->obedit_type != -1))); } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index fec98c77c19..d3fcd5e5911 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -62,8 +62,8 @@ #include "RNA_access.h" -#include "BIF_gl.h" -#include "BIF_glutil.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" #include "BIK_api.h" @@ -71,7 +71,6 @@ #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_curve.h" -#include "BKE_depsgraph.h" #include "BKE_fcurve.h" #include "BKE_lattice.h" #include "BKE_library.h" @@ -82,6 +81,11 @@ #include "BKE_editmesh.h" #include "BKE_tracking.h" #include "BKE_mask.h" +#include "BKE_workspace.h" +#include "BKE_layer.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" #include "ED_anim_api.h" #include "ED_armature.h" @@ -102,6 +106,8 @@ #include "WM_types.h" #include "WM_api.h" +#include "RE_engine.h" + #include "UI_resources.h" #include "UI_view2d.h" @@ -123,98 +129,101 @@ void getViewVector(const TransInfo *t, const float coord[3], float vec[3]) /* ************************** GENERICS **************************** */ -static void clipMirrorModifier(TransInfo *t, Object *ob) +static void clipMirrorModifier(TransInfo *t) { - 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; - TransData *td = t->data; + 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; - 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); + } - for (i = 0; i < t->total; 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->flag & TD_NOACTION) - break; - if (td->loc == NULL) - break; + if (td->flag & TD_NOACTION) + 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); + if (clip) { + if (mmd->mirror_ob) { + mul_m4_v3(imtx, loc); + } + copy_v3_v3(td->loc, loc); } - copy_v3_v3(td->loc, loc); } } - } + } } } } @@ -223,27 +232,29 @@ static void clipMirrorModifier(TransInfo *t, Object *ob) /* assumes obedit set to mesh object */ static void editbmesh_apply_to_mirror(TransInfo *t) { - TransData *td = t->data; - BMVert *eve; - int i; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + BMVert *eve; + int i; - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_NOACTION) - break; - if (td->loc == NULL) - break; - if (td->flag & TD_SKIP) - continue; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) + break; + if (td->loc == NULL) + break; + if (td->flag & TD_SKIP) + continue; - eve = td->extra; - if (eve) { - eve->co[0] = -td->loc[0]; - eve->co[1] = td->loc[1]; - eve->co[2] = td->loc[2]; - } + eve = td->extra; + if (eve) { + eve->co[0] = -td->loc[0]; + eve->co[1] = td->loc[1]; + eve->co[2] = td->loc[2]; + } - if (td->flag & TD_MIRROR_EDGE) { - td->loc[0] = 0; + if (td->flag & TD_MIRROR_EDGE) { + td->loc[0] = 0; + } } } } @@ -338,7 +349,7 @@ static bool fcu_test_selected(FCurve *fcu) /* helper for recalcData() - for Action Editor transforms */ static void recalcData_actedit(TransInfo *t) { - Scene *scene = t->scene; + ViewLayer *view_layer = t->view_layer; SpaceAction *saction = (SpaceAction *)t->sa->spacedata.first; bAnimContext ac = {NULL}; @@ -350,7 +361,8 @@ static void recalcData_actedit(TransInfo *t) /* NOTE: sync this with the code in ANIM_animdata_get_context() */ ac.bmain = CTX_data_main(t->context); ac.scene = t->scene; - ac.obact = OBACT; + ac.view_layer = t->view_layer; + ac.obact = OBACT(view_layer); ac.sa = t->sa; ac.ar = t->ar; ac.sl = (t->sa) ? t->sa->spacedata.first : NULL; @@ -375,7 +387,7 @@ static void recalcData_actedit(TransInfo *t) if ((saction->flag & SACTION_NOREALTIMEUPDATES) == 0) { for (ale = anim_data.first; ale; ale = ale->next) { /* set refresh tags for objects using this animation */ - ANIM_list_elem_update(CTX_data_main(t->context), scene, ale); + ANIM_list_elem_update(CTX_data_main(t->context), t->scene, ale); } } @@ -387,7 +399,7 @@ static void recalcData_actedit(TransInfo *t) static void recalcData_graphedit(TransInfo *t) { SpaceIpo *sipo = (SpaceIpo *)t->sa->spacedata.first; - Scene *scene; + ViewLayer *view_layer = t->view_layer; ListBase anim_data = {NULL, NULL}; bAnimContext ac = {NULL}; @@ -399,8 +411,9 @@ static void recalcData_graphedit(TransInfo *t) /* initialize relevant anim-context 'context' data from TransInfo data */ /* NOTE: sync this with the code in ANIM_animdata_get_context() */ ac.bmain = CTX_data_main(t->context); - scene = ac.scene = t->scene; - ac.obact = OBACT; + ac.scene = t->scene; + ac.view_layer = t->view_layer; + ac.obact = OBACT(view_layer); ac.sa = t->sa; ac.ar = t->ar; ac.sl = (t->sa) ? t->sa->spacedata.first : NULL; @@ -453,11 +466,13 @@ static void recalcData_nla(TransInfo *t) double secf = FPS; int i; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* for each strip we've got, perform some additional validation of the values that got set before * using RNA to set the value (which does some special operations when setting these values to make * sure that everything works ok) */ - for (i = 0; i < t->total; i++, tdn++) { + for (i = 0; i < tc->data_len; i++, tdn++) { NlaStrip *strip = tdn->strip; PointerRNA strip_ptr; short pExceeded, nExceeded, iter; @@ -662,7 +677,7 @@ static void recalcData_mask_common(TransInfo *t) flushTransMasking(t); - DAG_id_tag_update(&mask->id, 0); + DEG_id_tag_update(&mask->id, 0); } /* helper for recalcData() - for Image Editor transforms */ @@ -674,14 +689,18 @@ static void recalcData_image(TransInfo *t) else if (t->options & CTX_PAINT_CURVE) { flushTransPaintCurve(t); } - else if (t->obedit && t->obedit->type == OB_MESH) { + else if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) { SpaceImage *sima = t->sa->spacedata.first; flushTransUVs(t); if (sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_re_solve(); - DAG_id_tag_update(t->obedit->data, 0); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); + } + } } } @@ -724,7 +743,7 @@ static void recalcData_spaceclip(TransInfo *t) track = track->next; } - DAG_id_tag_update(&clip->id, 0); + DEG_id_tag_update(&clip->id, 0); } else if (t->options & CTX_MASK) { recalcData_mask_common(t); @@ -734,54 +753,59 @@ static void recalcData_spaceclip(TransInfo *t) /* helper for recalcData() - for object transforms, typically in the 3D view */ static void recalcData_objects(TransInfo *t) { - Base *base = t->scene->basact; + Base *base = t->view_layer->basact; - if (t->obedit) { - if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) { - Curve *cu = t->obedit->data; - ListBase *nurbs = BKE_curve_editNurbs_get(cu); - Nurb *nu = nurbs->first; + if (t->obedit_type != -1) { + if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { if (t->state != TRANS_CANCEL) { - clipMirrorModifier(t, t->obedit); + clipMirrorModifier(t); applyProject(t); } - DAG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Curve *cu = tc->obedit->data; + ListBase *nurbs = BKE_curve_editNurbs_get(cu); + Nurb *nu = nurbs->first; + + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ - if (t->state == TRANS_CANCEL) { - while (nu) { - BKE_nurb_handles_calc(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */ - nu = nu->next; + if (t->state == TRANS_CANCEL) { + while (nu) { + BKE_nurb_handles_calc(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */ + nu = nu->next; + } } - } - else { - /* Normal updating */ - while (nu) { - BKE_nurb_test2D(nu); - BKE_nurb_handles_calc(nu); - nu = nu->next; + else { + /* Normal updating */ + while (nu) { + BKE_nurb_test2D(nu); + BKE_nurb_handles_calc(nu); + nu = nu->next; + } } } } - else if (t->obedit->type == OB_LATTICE) { - Lattice *la = t->obedit->data; + else if (t->obedit_type == OB_LATTICE) { if (t->state != TRANS_CANCEL) { applyProject(t); } - DAG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ - - if (la->editlatt->latt->flag & LT_OUTSIDE) outside_lattice(la->editlatt->latt); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Lattice *la = tc->obedit->data; + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + if (la->editlatt->latt->flag & LT_OUTSIDE) { + outside_lattice(la->editlatt->latt); + } + } } - else if (t->obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + else if (t->obedit_type == OB_MESH) { /* mirror modifier clipping? */ if (t->state != TRANS_CANCEL) { /* apply clipping after so we never project past the clip plane [#25423] */ applyProject(t); - clipMirrorModifier(t, t->obedit); + clipMirrorModifier(t); } if ((t->options & CTX_NO_MIRROR) == 0 && (t->flag & T_MIRROR)) editbmesh_apply_to_mirror(t); @@ -793,135 +817,152 @@ static void recalcData_objects(TransInfo *t) projectVertSlideData(t, false); } - DAG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ - - EDBM_mesh_normals_update(em); - BKE_editmesh_tessface_calc(em); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + EDBM_mesh_normals_update(em); + BKE_editmesh_tessface_calc(em); + } } - else if (t->obedit->type == OB_ARMATURE) { /* no recalc flag, does pose */ - bArmature *arm = t->obedit->data; - ListBase *edbo = arm->edbo; - EditBone *ebo, *ebo_parent; - TransData *td = t->data; - int i; + else if (t->obedit_type == OB_ARMATURE) { /* no recalc flag, does pose */ if (t->state != TRANS_CANCEL) { applyProject(t); } - /* Ensure all bones are correctly adjusted */ - for (ebo = edbo->first; ebo; ebo = ebo->next) { - ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + bArmature *arm = tc->obedit->data; + ListBase *edbo = arm->edbo; + EditBone *ebo, *ebo_parent; + TransData *td = tc->data; + int i; - if (ebo_parent) { - /* If this bone has a parent tip that has been moved */ - if (ebo_parent->flag & BONE_TIPSEL) { - copy_v3_v3(ebo->head, ebo_parent->tail); - if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo_parent->rad_tail; - } - /* If this bone has a parent tip that has NOT been moved */ - else { - copy_v3_v3(ebo_parent->tail, ebo->head); - if (t->mode == TFM_BONE_ENVELOPE) ebo_parent->rad_tail = ebo->rad_head; - } - } + /* Ensure all bones are correctly adjusted */ + for (ebo = edbo->first; ebo; ebo = ebo->next) { + ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL; - /* on extrude bones, oldlength==0.0f, so we scale radius of points */ - ebo->length = len_v3v3(ebo->head, ebo->tail); - if (ebo->oldlength == 0.0f) { - ebo->rad_head = 0.25f * ebo->length; - ebo->rad_tail = 0.10f * ebo->length; - ebo->dist = 0.25f * ebo->length; - if (ebo->parent) { - if (ebo->rad_head > ebo->parent->rad_tail) - ebo->rad_head = ebo->parent->rad_tail; + if (ebo_parent) { + /* If this bone has a parent tip that has been moved */ + if (ebo_parent->flag & BONE_TIPSEL) { + copy_v3_v3(ebo->head, ebo_parent->tail); + if (t->mode == TFM_BONE_ENVELOPE) ebo->rad_head = ebo_parent->rad_tail; + } + /* If this bone has a parent tip that has NOT been moved */ + else { + copy_v3_v3(ebo_parent->tail, ebo->head); + if (t->mode == TFM_BONE_ENVELOPE) ebo_parent->rad_tail = ebo->rad_head; + } } - } - else if (t->mode != TFM_BONE_ENVELOPE) { - /* if bones change length, lets do that for the deform distance as well */ - ebo->dist *= ebo->length / ebo->oldlength; - ebo->rad_head *= ebo->length / ebo->oldlength; - ebo->rad_tail *= ebo->length / ebo->oldlength; - ebo->oldlength = ebo->length; - if (ebo_parent) { - ebo_parent->rad_tail = ebo->rad_head; + /* on extrude bones, oldlength==0.0f, so we scale radius of points */ + ebo->length = len_v3v3(ebo->head, ebo->tail); + if (ebo->oldlength == 0.0f) { + ebo->rad_head = 0.25f * ebo->length; + ebo->rad_tail = 0.10f * ebo->length; + ebo->dist = 0.25f * ebo->length; + if (ebo->parent) { + if (ebo->rad_head > ebo->parent->rad_tail) + ebo->rad_head = ebo->parent->rad_tail; + } + } + else if (t->mode != TFM_BONE_ENVELOPE) { + /* if bones change length, lets do that for the deform distance as well */ + ebo->dist *= ebo->length / ebo->oldlength; + ebo->rad_head *= ebo->length / ebo->oldlength; + ebo->rad_tail *= ebo->length / ebo->oldlength; + ebo->oldlength = ebo->length; + + if (ebo_parent) { + ebo_parent->rad_tail = ebo->rad_head; + } } } - } - if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) { - /* fix roll */ - for (i = 0; i < t->total; i++, td++) { - if (td->extra) { - float vec[3], up_axis[3]; - float qrot[4]; - float roll; + if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) { + /* fix roll */ + for (i = 0; i < tc->data_len; i++, td++) { + if (td->extra) { + float vec[3], up_axis[3]; + float qrot[4]; + float roll; - ebo = td->extra; + ebo = td->extra; - if (t->state == TRANS_CANCEL) { - /* restore roll */ - ebo->roll = td->ival; - } - else { - copy_v3_v3(up_axis, td->axismtx[2]); + if (t->state == TRANS_CANCEL) { + /* restore roll */ + ebo->roll = td->ival; + } + else { + copy_v3_v3(up_axis, td->axismtx[2]); - sub_v3_v3v3(vec, ebo->tail, ebo->head); - normalize_v3(vec); - rotation_between_vecs_to_quat(qrot, td->axismtx[1], vec); - mul_qt_v3(qrot, up_axis); + sub_v3_v3v3(vec, ebo->tail, ebo->head); + normalize_v3(vec); + rotation_between_vecs_to_quat(qrot, td->axismtx[1], vec); + mul_qt_v3(qrot, up_axis); - /* roll has a tendency to flip in certain orientations - [#34283], [#33974] */ - roll = ED_armature_ebone_roll_to_vector(ebo, up_axis, false); - ebo->roll = angle_compat_rad(roll, td->ival); + /* roll has a tendency to flip in certain orientations - [#34283], [#33974] */ + roll = ED_armature_ebone_roll_to_vector(ebo, up_axis, false); + ebo->roll = angle_compat_rad(roll, td->ival); + } } } } - } - if (arm->flag & ARM_MIRROR_EDIT) { - if (t->state != TRANS_CANCEL) - ED_armature_edit_transform_mirror_update(t->obedit); - else - restoreBones(t); + if (arm->flag & ARM_MIRROR_EDIT) { + if (t->state != TRANS_CANCEL) { + ED_armature_edit_transform_mirror_update(tc->obedit); + } + else { + restoreBones(tc); + } + } } } else { if (t->state != TRANS_CANCEL) { applyProject(t); } - DAG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + } + } } + } - else if ((t->flag & T_POSE) && t->poseobj) { - Object *ob = t->poseobj; - bArmature *arm = ob->data; + else if (t->flag & T_POSE) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->poseobj; + bArmature *arm = ob->data; - /* if animtimer is running, and the object already has animation data, - * check if the auto-record feature means that we should record 'samples' - * (i.e. uneditable animation values) - * - * context is needed for keying set poll() functions. - */ - // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes? - if ((t->animtimer) && (t->context) && IS_AUTOKEY_ON(t->scene)) { - int targetless_ik = (t->flag & T_AUTOIK); // XXX this currently doesn't work, since flags aren't set yet! + /* if animtimer is running, and the object already has animation data, + * check if the auto-record feature means that we should record 'samples' + * (i.e. uneditable animation values) + * + * context is needed for keying set poll() functions. + */ + // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes? + if ((t->animtimer) && (t->context) && IS_AUTOKEY_ON(t->scene)) { + int targetless_ik = (t->flag & T_AUTOIK); // XXX this currently doesn't work, since flags aren't set yet! - animrecord_check_state(t->scene, &ob->id, t->animtimer); - autokeyframe_pose_cb_func(t->context, t->scene, (View3D *)t->view, ob, t->mode, targetless_ik); - } + animrecord_check_state(t->scene, &ob->id, t->animtimer); + autokeyframe_pose_cb_func(t->context, t->scene, ob, t->mode, targetless_ik); + } - /* old optimize trick... this enforces to bypass the depgraph */ - if (!(arm->flag & ARM_DELAYDEFORM)) { - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); /* sets recalc flags */ - /* transformation of pose may affect IK tree, make sure it is rebuilt */ - BIK_clear_data(ob->pose); + /* old optimize trick... this enforces to bypass the depgraph */ + if (!(arm->flag & ARM_DELAYDEFORM)) { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); /* sets recalc flags */ + /* transformation of pose may affect IK tree, make sure it is rebuilt */ + BIK_clear_data(ob->pose); + } + else { + BKE_pose_where_is(t->depsgraph, t->scene, ob); + } } - else - BKE_pose_where_is(t->scene, ob); } - else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, base->object)) { + else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && + PE_get_current(t->scene, base->object)) + { if (t->state != TRANS_CANCEL) { applyProject(t); } @@ -934,37 +975,45 @@ static void recalcData_objects(TransInfo *t) applyProject(t); } - for (i = 0; i < t->total; i++) { - TransData *td = t->data + i; - Object *ob = td->ob; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; - if (td->flag & TD_NOACTION) - break; + for (i = 0; i < tc->data_len; i++, td++) { + Object *ob = td->ob; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_NOACTION) + break; - /* if animtimer is running, and the object already has animation data, - * check if the auto-record feature means that we should record 'samples' - * (i.e. uneditable animation values) - */ - // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes? - if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { - animrecord_check_state(t->scene, &ob->id, t->animtimer); - autokeyframe_ob_cb_func(t->context, t->scene, (View3D *)t->view, ob, t->mode); - } + if (td->flag & TD_SKIP) + continue; - /* sets recalc flags fully, instead of flushing existing ones - * otherwise proxies don't function correctly - */ - DAG_id_tag_update(&ob->id, OB_RECALC_OB); + /* if animtimer is running, and the object already has animation data, + * check if the auto-record feature means that we should record 'samples' + * (i.e. uneditable animation values) + */ + // TODO: autokeyframe calls need some setting to specify to add samples (FPoints) instead of keyframes? + if ((t->animtimer) && IS_AUTOKEY_ON(t->scene)) { + animrecord_check_state(t->scene, &ob->id, t->animtimer); + autokeyframe_ob_cb_func(t->context, t->scene, t->view_layer, ob, t->mode); + } + + /* sets recalc flags fully, instead of flushing existing ones + * otherwise proxies don't function correctly + */ + DEG_id_tag_update(&ob->id, OB_RECALC_OB); - if (t->flag & T_TEXTURE) - DAG_id_tag_update(&ob->id, OB_RECALC_DATA); + if (t->flag & T_TEXTURE) + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } } } } +static void recalcData_cursor(TransInfo *t) +{ + DEG_id_tag_update(&t->scene->id, DEG_TAG_COPY_ON_WRITE); +} + /* helper for recalcData() - for sequencer transforms */ static void recalcData_sequencer(TransInfo *t) { @@ -972,7 +1021,9 @@ static void recalcData_sequencer(TransInfo *t) int a; Sequence *seq_prev = NULL; - for (a = 0, td = t->data; a < t->total; a++, td++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { TransDataSeq *tdsq = (TransDataSeq *) td->extra; Sequence *seq = tdsq->seq; @@ -997,8 +1048,10 @@ static void recalcData_sequencer(TransInfo *t) /* force recalculation of triangles during transformation */ static void recalcData_gpencil_strokes(TransInfo *t) { - TransData *td = t->data; - for (int i = 0; i < t->total; i++, td++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { bGPDstroke *gps = td->extra; if (gps != NULL) { gps->flag |= GP_STROKE_RECALC_CACHES; @@ -1010,7 +1063,10 @@ static void recalcData_gpencil_strokes(TransInfo *t) void recalcData(TransInfo *t) { /* if tests must match createTransData for correct updates */ - if (t->options & CTX_TEXTURE) { + if (t->options & CTX_CURSOR) { + recalcData_cursor(t); + } + else if (t->options & CTX_TEXTURE) { recalcData_objects(t); } else if (t->options & CTX_EDGE) { @@ -1057,10 +1113,7 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis if (t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->view; - glPushMatrix(); - - //if (t->obedit) glLoadMatrixf(t->obedit->obmat); // sets opengl viewing - + gpuPushMatrix(); copy_v3_v3(v3, dir); mul_v3_fl(v3, v3d->far); @@ -1075,15 +1128,20 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis UI_GetThemeColor3ubv(TH_GRID, col); } UI_make_axis_color(col, col2, axis); - glColor3ubv(col2); - setlinestyle(0); - glBegin(GL_LINES); - glVertex3fv(v1); - glVertex3fv(v2); - glEnd(); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3ubv(col2); + + immBegin(GWN_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); + + immUnbindProgram(); - glPopMatrix(); + gpuPopMatrix(); } } @@ -1092,12 +1150,7 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis */ void resetTransModal(TransInfo *t) { - if (t->mode == TFM_EDGE_SLIDE) { - freeEdgeSlideVerts(t, &t->custom.mode); - } - else if (t->mode == TFM_VERT_SLIDE) { - freeVertSlideVerts(t, &t->custom.mode); - } + freeTransCustomDataForMode(t); } void resetTransRestrictions(TransInfo *t) @@ -1119,6 +1172,53 @@ static int initTransInfo_edit_pet_to_flag(const int proportional) } } +void initTransDataContainers_FromObjectData(TransInfo *t) +{ + const eObjectMode object_mode = OBACT(t->view_layer) ? OBACT(t->view_layer)->mode : OB_MODE_OBJECT; + const short object_type = OBACT(t->view_layer) ? OBACT(t->view_layer)->type : -1; + + if ((object_mode & OB_MODE_EDIT) || + ((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE))) + { + if (t->data_container) { + MEM_freeN(t->data_container); + } + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_mode( + t->view_layer, &objects_len, { + .object_mode = object_mode, + .no_dup_data = true}); + t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__); + t->data_container_len = objects_len; + + for (int i = 0; i < objects_len; i++) { + TransDataContainer *tc = &t->data_container[i]; + if (object_mode & OB_MODE_EDIT) { + tc->obedit = objects[i]; + /* Check needed for UV's */ + if ((t->flag & T_2D_EDIT) == 0) { + tc->use_local_mat = true; + } + } + else if (object_mode & OB_MODE_POSE) { + tc->poseobj = objects[i]; + tc->use_local_mat = true; + } + + if (tc->use_local_mat) { + BLI_assert((t->flag & T_2D_EDIT) == 0); + copy_m4_m4(tc->mat, objects[i]->obmat); + copy_m3_m4(tc->mat3, tc->mat); + invert_m4_m4(tc->imat, tc->mat); + invert_m3_m3(tc->imat3, tc->mat3); + normalize_m3_m3(tc->mat3_unit, tc->mat3); + } + /* Otherwise leave as zero. */ + } + MEM_freeN(objects); + } +} + /** * Setup internal data, mouse, vectors * @@ -1128,34 +1228,38 @@ static int initTransInfo_edit_pet_to_flag(const int proportional) */ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *event) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); Scene *sce = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + const eObjectMode object_mode = OBACT(view_layer) ? OBACT(view_layer)->mode : OB_MODE_OBJECT; + const short object_type = OBACT(view_layer) ? OBACT(view_layer)->type : -1; ToolSettings *ts = CTX_data_tool_settings(C); ARegion *ar = CTX_wm_region(C); ScrArea *sa = CTX_wm_area(C); - Object *obedit = CTX_data_edit_object(C); - Object *ob = CTX_data_active_object(C); + bGPdata *gpd = CTX_data_gpencil_data(C); PropertyRNA *prop; + t->depsgraph = depsgraph; t->scene = sce; + t->view_layer = view_layer; t->sa = sa; t->ar = ar; - t->obedit = obedit; t->settings = ts; t->reports = op ? op->reports : NULL; - if (obedit) { - copy_m3_m4(t->obedit_mat, obedit->obmat); - normalize_m3(t->obedit_mat); - } - - t->data = NULL; - t->ext = NULL; - t->helpline = HLP_NONE; t->flag = 0; + t->obedit_type = (object_mode == OB_MODE_EDIT) ? object_type : -1; + + /* Many kinds of transform only use a single handle. */ + if (t->data_container == NULL) { + t->data_container = MEM_callocN(sizeof(*t->data_container), __func__); + t->data_container_len = 1; + } + t->redraw = TREDRAW_HARD; /* redraw first time */ if (event) { @@ -1176,12 +1280,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->transform = NULL; t->handleEvent = NULL; - t->total = 0; + t->data_len_all = 0; t->val = 0.0f; zero_v3(t->vec); - zero_v3(t->center); zero_v3(t->center_global); unit_m3(t->mat); @@ -1230,9 +1333,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve prop_id = "use_even_offset"; } - if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id)) && - RNA_property_is_set(op->ptr, prop)) - { + if (prop_id && (prop = RNA_struct_find_property(op->ptr, prop_id))) { SET_FLAG_FROM_TEST(t->flag, RNA_property_boolean_get(op->ptr, prop), T_ALT_TRANSFORM); } } @@ -1245,34 +1346,37 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->animtimer = (animscreen) ? animscreen->animtimer : NULL; /* turn manipulator off during transform */ - // FIXME: but don't do this when USING the manipulator... if (t->flag & T_MODAL) { - t->twtype = v3d->twtype; - v3d->twtype = 0; + t->twflag = v3d->twflag; + v3d->twflag = 0; } - if (v3d->flag & V3D_ALIGN) t->flag |= T_V3D_ALIGN; - t->around = v3d->around; + if (t->scene->toolsettings->transform_flag & SCE_XFORM_AXIS_ALIGN) { + t->flag |= T_V3D_ALIGN; + } + t->around = t->scene->toolsettings->transform_pivot_point; /* bend always uses the cursor */ if (t->mode == TFM_BEND) { t->around = V3D_AROUND_CURSOR; } - t->current_orientation = v3d->twmode; + t->current_orientation = t->scene->orientation_type; + t->custom_orientation = BKE_scene_transform_orientation_find( + t->scene, t->scene->orientation_index_custom); /* exceptional case */ if (t->around == V3D_AROUND_LOCAL_ORIGINS) { if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { const bool use_island = transdata_check_local_islands(t, t->around); - if (obedit && !use_island) { + if ((t->obedit_type != -1) && !use_island) { t->options |= CTX_NO_PET; } } } - if (ob && ob->mode & OB_MODE_ALL_PAINT) { + if (object_mode & OB_MODE_ALL_PAINT) { Paint *p = BKE_paint_get_active_from_context(C); if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { t->options |= CTX_PAINT_CURVE; @@ -1301,7 +1405,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->view = &ar->v2d; t->around = sima->around; - if (ED_space_image_show_uvedit(sima, t->obedit)) { + if (ED_space_image_show_uvedit(sima, OBACT(t->view_layer))) { /* UV transform */ } else if (sima->mode == SI_MODE_MASK) { @@ -1350,11 +1454,22 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (op && ((prop = RNA_struct_find_property(op->ptr, "constraint_orientation")) && RNA_property_is_set(op->ptr, prop))) { - t->current_orientation = RNA_property_enum_get(op->ptr, prop); + short orientation = RNA_property_enum_get(op->ptr, prop); + TransformOrientation *custom_orientation = NULL; - if (t->current_orientation >= V3D_MANIP_CUSTOM + BIF_countTransformOrientation(C)) { - t->current_orientation = V3D_MANIP_GLOBAL; + if (orientation >= V3D_MANIP_CUSTOM) { + if (orientation >= V3D_MANIP_CUSTOM + BIF_countTransformOrientation(C)) { + orientation = V3D_MANIP_GLOBAL; + } + else { + custom_orientation = BKE_scene_transform_orientation_find( + t->scene, orientation - V3D_MANIP_CUSTOM); + orientation = V3D_MANIP_CUSTOM; + } } + + t->current_orientation = orientation; + t->custom_orientation = custom_orientation; } if (op && ((prop = RNA_struct_find_property(op->ptr, "release_confirm")) && @@ -1380,7 +1495,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } // Need stuff to take it from edit mesh or whatnot here else if (t->spacetype == SPACE_VIEW3D) { - if (t->obedit && t->obedit->type == OB_MESH && (((Mesh *)t->obedit->data)->editflag & ME_EDIT_MIRROR_X)) { + /* TODO(campbell): xform, get mirror from each object. */ + if (t->obedit_type == OB_MESH && (((Mesh *)OBACT(t->view_layer)->data)->editflag & ME_EDIT_MIRROR_X)) { t->flag |= T_MIRROR; t->mirror = 1; } @@ -1401,7 +1517,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (t->spacetype == SPACE_ACTION) { t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_action); } - else if (t->obedit) { + else if (t->obedit_type != -1) { t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional); } else if (t->options & CTX_GPENCIL_STROKES) { @@ -1416,7 +1532,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } } - else if (t->obedit == NULL && ts->proportional_objects) { + else if ((t->obedit_type == -1) && ts->proportional_objects) { t->flag |= T_PROP_EDIT; } } @@ -1462,8 +1578,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve setTransformViewAspect(t, t->aspect); if (op && (prop = RNA_struct_find_property(op->ptr, "center_override")) && RNA_property_is_set(op->ptr, prop)) { - RNA_property_float_get_array(op->ptr, prop, t->center); - mul_v3_v3(t->center, t->aspect); + RNA_property_float_get_array(op->ptr, prop, t->center_global); + mul_v3_v3(t->center_global, t->aspect); t->flag |= T_OVERRIDE_CENTER; } @@ -1471,11 +1587,47 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve initNumInput(&t->num); } + +static void freeTransCustomData( + TransInfo *t, TransDataContainer *tc, + TransCustomData *custom_data) +{ + if (custom_data->free_cb) { + /* Can take over freeing t->data and data_2d etc... */ + custom_data->free_cb(t, tc, custom_data); + BLI_assert(custom_data->data == NULL); + } + else if ((custom_data->data != NULL) && custom_data->use_free) { + MEM_freeN(custom_data->data); + custom_data->data = NULL; + } + /* In case modes are switched in the same transform session. */ + custom_data->free_cb = false; + custom_data->use_free = false; +} + +static void freeTransCustomDataContainer(TransInfo *t, TransDataContainer *tc, TransCustomDataContainer *tcdc) +{ + TransCustomData *custom_data = &tcdc->first_elem; + for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) { + freeTransCustomData(t, tc, custom_data); + } +} + +/** + * Needed for mode switching. + */ +void freeTransCustomDataForMode(TransInfo *t) +{ + freeTransCustomData(t, NULL, &t->custom.mode); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + freeTransCustomData(t, tc, &tc->custom.mode); + } +} + /* Here I would suggest only TransInfo related issues, like free data & reset vars. Not redraws */ void postTrans(bContext *C, TransInfo *t) { - TransData *td; - if (t->draw_handle_view) ED_region_draw_cb_exit(t->ar->type, t->draw_handle_view); if (t->draw_handle_apply) @@ -1485,46 +1637,41 @@ void postTrans(bContext *C, TransInfo *t) if (t->draw_handle_cursor) WM_paint_cursor_end(CTX_wm_manager(C), t->draw_handle_cursor); + if (t->flag & T_MODAL_CURSOR_SET) { + WM_cursor_modal_restore(CTX_wm_window(C)); + } + /* Free all custom-data */ - { - TransCustomData *custom_data = &t->custom.first_elem; - for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) { - if (custom_data->free_cb) { - /* Can take over freeing t->data and data2d etc... */ - custom_data->free_cb(t, custom_data); - BLI_assert(custom_data->data == NULL); - } - else if ((custom_data->data != NULL) && custom_data->use_free) { - MEM_freeN(custom_data->data); - custom_data->data = NULL; - } - } + freeTransCustomDataContainer(t, NULL, &t->custom); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + freeTransCustomDataContainer(t, tc, &tc->custom); } /* postTrans can be called when nothing is selected, so data is NULL already */ - if (t->data) { - - /* free data malloced per trans-data */ - if ((t->obedit && ELEM(t->obedit->type, OB_CURVE, OB_SURF)) || - (t->spacetype == SPACE_IPO)) - { - int a; - for (a = 0, td = t->data; a < t->total; a++, td++) { - if (td->flag & TD_BEZTRIPLE) { - MEM_freeN(td->hdata); + if (t->data_len_all != 0) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* free data malloced per trans-data */ + if (ELEM(t->obedit_type, OB_CURVE, OB_SURF) || + (t->spacetype == SPACE_IPO)) + { + TransData *td = tc->data; + for (int a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_BEZTRIPLE) { + MEM_freeN(td->hdata); + } } } + MEM_freeN(tc->data); + + MEM_SAFE_FREE(tc->data_ext); + MEM_SAFE_FREE(tc->data_2d); } - MEM_freeN(t->data); } - BLI_freelistN(&t->tsnap.points); + MEM_SAFE_FREE(t->data_container); + t->data_container = NULL; - if (t->ext) MEM_freeN(t->ext); - if (t->data2d) { - MEM_freeN(t->data2d); - t->data2d = NULL; - } + BLI_freelistN(&t->tsnap.points); if (t->spacetype == SPACE_IMAGE) { if (t->options & (CTX_MASK | CTX_PAINT_CURVE)) { @@ -1540,7 +1687,7 @@ void postTrans(bContext *C, TransInfo *t) View3D *v3d = t->sa->spacedata.first; /* restore manipulator */ if (t->flag & T_MODAL) { - v3d->twtype = t->twtype; + v3d->twflag = t->twflag; } } @@ -1553,9 +1700,11 @@ void postTrans(bContext *C, TransInfo *t) void applyTransObjects(TransInfo *t) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td; - for (td = t->data; td < t->data + t->total; td++) { + for (td = tc->data; td < tc->data + tc->data_len; td++) { copy_v3_v3(td->iloc, td->loc); if (td->ext->rot) { copy_v3_v3(td->ext->irot, td->ext->rot); @@ -1604,25 +1753,29 @@ static void restoreElement(TransData *td) void restoreTransObjects(TransInfo *t) { - TransData *td; - TransData2D *td2d; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { - for (td = t->data; td < t->data + t->total; td++) { - restoreElement(td); - } + TransData *td; + TransData2D *td2d; - for (td2d = t->data2d; t->data2d && td2d < t->data2d + t->total; td2d++) { - if (td2d->h1) { - td2d->h1[0] = td2d->ih1[0]; - td2d->h1[1] = td2d->ih1[1]; + for (td = tc->data; td < tc->data + tc->data_len; td++) { + restoreElement(td); } - if (td2d->h2) { - td2d->h2[0] = td2d->ih2[0]; - td2d->h2[1] = td2d->ih2[1]; + + for (td2d = tc->data_2d; tc->data_2d && td2d < tc->data_2d + tc->data_len; td2d++) { + if (td2d->h1) { + td2d->h1[0] = td2d->ih1[0]; + td2d->h1[1] = td2d->ih1[1]; + } + if (td2d->h2) { + td2d->h2[0] = td2d->ih2[0]; + td2d->h2[1] = td2d->ih2[1]; + } } - } - unit_m3(t->mat); + unit_m3(t->mat); + + } recalcData(t); } @@ -1630,32 +1783,21 @@ void restoreTransObjects(TransInfo *t) void calculateCenter2D(TransInfo *t) { BLI_assert(!is_zero_v3(t->aspect)); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - float vec[3]; - - copy_v3_v3(vec, t->center); - mul_m4_v3(ob->obmat, vec); - projectFloatView(t, vec, t->center2d); - } - else { - projectFloatView(t, t->center, t->center2d); - } + projectFloatView(t, t->center_global, t->center2d); } -void calculateCenterGlobal( - TransInfo *t, const float center_local[3], - float r_center_global[3]) +void calculateCenterLocal( + TransInfo *t, const float center_global[3]) { /* setting constraint center */ /* note, init functions may over-ride t->center */ - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_v3_m4v3(r_center_global, ob->obmat, center_local); - } - else { - copy_v3_v3(r_center_global, center_local); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->use_local_mat) { + mul_v3_m4v3(tc->center_local, tc->imat, center_global); + } + else { + copy_v3_v3(tc->center_local, center_global); + } } } @@ -1663,20 +1805,11 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) { const float *cursor; - cursor = ED_view3d_cursor3d_get(t->scene, t->view); + cursor = ED_view3d_cursor3d_get(t->scene, t->view)->location; copy_v3_v3(r_center, cursor); /* If edit or pose mode, move cursor in local space */ - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - float mat[3][3], imat[3][3]; - - sub_v3_v3v3(r_center, r_center, ob->obmat[3]); - copy_m3_m4(mat, ob->obmat); - invert_m3_m3(imat, mat); - mul_m3_v3(imat, r_center); - } - else if (t->options & CTX_PAINT_CURVE) { + if (t->options & CTX_PAINT_CURVE) { if (ED_view3d_project_float_global(t->ar, cursor, r_center, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { r_center[0] = t->ar->winx / 2.0f; r_center[1] = t->ar->winy / 2.0f; @@ -1750,13 +1883,21 @@ void calculateCenterMedian(TransInfo *t, float r_center[3]) { float partial[3] = {0.0f, 0.0f, 0.0f}; int total = 0; - int i; - for (i = 0; i < t->total; i++) { - if (t->data[i].flag & TD_SELECTED) { - if (!(t->data[i].flag & TD_NOCENTER)) { - add_v3_v3(partial, t->data[i].center); - total++; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (int i = 0; i < tc->data_len; i++) { + if (tc->data[i].flag & TD_SELECTED) { + if (!(tc->data[i].flag & TD_NOCENTER)) { + if (tc->use_local_mat) { + float v[3]; + mul_v3_m4v3(v, tc->mat, tc->data[i].center); + add_v3_v3(partial, v); + } + else { + add_v3_v3(partial, tc->data[i].center); + } + total++; + } } } } @@ -1768,22 +1909,29 @@ void calculateCenterMedian(TransInfo *t, float r_center[3]) void calculateCenterBound(TransInfo *t, float r_center[3]) { - float max[3]; - float min[3]; - int i; - for (i = 0; i < t->total; i++) { - if (i) { - if (t->data[i].flag & TD_SELECTED) { - if (!(t->data[i].flag & TD_NOCENTER)) - minmax_v3v3_v3(min, max, t->data[i].center); + float max[3], min[3]; + bool changed = false; + INIT_MINMAX(min, max); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + for (int i = 0; i < tc->data_len; i++) { + if (tc->data[i].flag & TD_SELECTED) { + if (!(tc->data[i].flag & TD_NOCENTER)) { + if (tc->use_local_mat) { + float v[3]; + mul_v3_m4v3(v, tc->mat, tc->data[i].center); + minmax_v3v3_v3(min, max, v); + } + else { + minmax_v3v3_v3(min, max, tc->data[i].center); + } + changed = true; + } } } - else { - copy_v3_v3(max, t->data[i].center); - copy_v3_v3(min, t->data[i].center); - } } - mid_v3_v3v3(r_center, min, max); + if (changed) { + mid_v3_v3v3(r_center, min, max); + } } /** @@ -1791,26 +1939,30 @@ void calculateCenterBound(TransInfo *t, float r_center[3]) */ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t); + bool ok = false; - if (t->obedit) { - if (ED_object_editmode_calc_active_center(t->obedit, select_only, r_center)) { + if (tc->obedit) { + if (ED_object_editmode_calc_active_center(tc->obedit, select_only, r_center)) { + mul_m4_v3(tc->obedit->obmat, r_center); ok = true; } } else if (t->flag & T_POSE) { - Scene *scene = t->scene; - Object *ob = OBACT; + ViewLayer *view_layer = t->view_layer; + Object *ob = OBACT(view_layer); if (ob) { bPoseChannel *pchan = BKE_pose_channel_active(ob); if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) { copy_v3_v3(r_center, pchan->pose_head); + mul_m4_v3(tc->obedit->obmat, r_center); ok = true; } } } else if (t->options & CTX_PAINT_CURVE) { - Paint *p = BKE_paint_get_active(t->scene); + Paint *p = BKE_paint_get_active(t->scene, t->view_layer); Brush *br = p->brush; PaintCurve *pc = br->paint_curve; copy_v3_v3(r_center, pc->points[pc->add_index - 1].bez.vec[1]); @@ -1819,9 +1971,10 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) } else { /* object mode */ - Scene *scene = t->scene; - Object *ob = OBACT; - if (ob && (!select_only || (ob->flag & SELECT))) { + ViewLayer *view_layer = t->view_layer; + Object *ob = OBACT(view_layer); + Base *base = BASACT(view_layer); + if (ob && ((!select_only) || ((base->flag & BASE_SELECTED) != 0))) { copy_v3_v3(r_center, ob->obmat[3]); ok = true; } @@ -1868,14 +2021,13 @@ static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[ void calculateCenter(TransInfo *t) { if ((t->flag & T_OVERRIDE_CENTER) == 0) { - calculateCenter_FromAround(t, t->around, t->center); + calculateCenter_FromAround(t, t->around, t->center_global); } - calculateCenterGlobal(t, t->center, t->center_global); + calculateCenterLocal(t, t->center_global); /* avoid calculating again */ { TransCenterData *cd = &t->center_cache[t->around]; - copy_v3_v3(cd->local, t->center); copy_v3_v3(cd->global, t->center_global); cd->is_set = true; } @@ -1893,16 +2045,15 @@ void calculateCenter(TransInfo *t) normalize_v3(axis); /* 6.0 = 6 grid units */ - axis[0] = t->center[0] - 6.0f * axis[0]; - axis[1] = t->center[1] - 6.0f * axis[1]; - axis[2] = t->center[2] - 6.0f * axis[2]; + axis[0] = t->center_global[0] - 6.0f * axis[0]; + axis[1] = t->center_global[1] - 6.0f * axis[1]; + axis[2] = t->center_global[2] - 6.0f * axis[2]; projectFloatView(t, axis, t->center2d); /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */ if (t->mode == TFM_TRANSLATION) { - copy_v3_v3(t->center, axis); - copy_v3_v3(t->center_global, t->center); + copy_v3_v3(t->center_global, axis); } } } @@ -1936,8 +2087,7 @@ const TransCenterData *transformCenter_from_type(TransInfo *t, int around) BLI_assert(around <= V3D_AROUND_ACTIVE); TransCenterData *cd = &t->center_cache[around]; if (cd->is_set == false) { - calculateCenter_FromAround(t, around, cd->local); - calculateCenterGlobal(t, cd->local, cd->global); + calculateCenter_FromAround(t, around, cd->global); cd->is_set = true; } return cd; @@ -1945,7 +2095,6 @@ const TransCenterData *transformCenter_from_type(TransInfo *t, int around) void calculatePropRatio(TransInfo *t) { - TransData *td = t->data; int i; float dist; const bool connected = (t->flag & T_PROP_CONNECTED) != 0; @@ -1954,75 +2103,79 @@ void calculatePropRatio(TransInfo *t) if (t->flag & T_PROP_EDIT) { const char *pet_id = NULL; - for (i = 0; i < t->total; i++, td++) { - if (td->flag & TD_SELECTED) { - td->factor = 1.0f; - } - else if (t->flag & T_MIRROR && td->loc[0] * t->mirror < -0.00001f) { - td->flag |= TD_SKIP; - td->factor = 0.0f; - restoreElement(td); - } - else if ((connected && (td->flag & TD_NOTCONNECTED || td->dist > t->prop_size)) || - (connected == 0 && td->rdist > t->prop_size)) - { - /* - * The elements are sorted according to their dist member in the array, - * that means we can stop when it finds one element outside of the propsize. - * do not set 'td->flag |= TD_NOACTION', the prop circle is being changed. - */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_SELECTED) { + td->factor = 1.0f; + } + else if (t->flag & T_MIRROR && td->loc[0] * t->mirror < -0.00001f) { + td->flag |= TD_SKIP; + td->factor = 0.0f; + restoreElement(td); + } + else if ((connected && (td->flag & TD_NOTCONNECTED || td->dist > t->prop_size)) || + (connected == 0 && td->rdist > t->prop_size)) + { + /* + * The elements are sorted according to their dist member in the array, + * that means we can stop when it finds one element outside of the propsize. + * do not set 'td->flag |= TD_NOACTION', the prop circle is being changed. + */ - td->factor = 0.0f; - restoreElement(td); - } - else { - /* Use rdist for falloff calculations, it is the real distance */ - td->flag &= ~TD_NOACTION; - - if (connected) - dist = (t->prop_size - td->dist) / t->prop_size; - else - dist = (t->prop_size - td->rdist) / t->prop_size; - - /* - * Clamp to positive numbers. - * Certain corner cases with connectivity and individual centers - * can give values of rdist larger than propsize. - */ - if (dist < 0.0f) - dist = 0.0f; + td->factor = 0.0f; + restoreElement(td); + } + else { + /* Use rdist for falloff calculations, it is the real distance */ + td->flag &= ~TD_NOACTION; + + if (connected) + dist = (t->prop_size - td->dist) / t->prop_size; + else + dist = (t->prop_size - td->rdist) / t->prop_size; + + /* + * Clamp to positive numbers. + * Certain corner cases with connectivity and individual centers + * can give values of rdist larger than propsize. + */ + if (dist < 0.0f) + dist = 0.0f; - switch (t->prop_mode) { - case PROP_SHARP: - td->factor = dist * dist; - break; - case PROP_SMOOTH: - td->factor = 3.0f * dist * dist - 2.0f * dist * dist * dist; - break; - case PROP_ROOT: - td->factor = sqrtf(dist); - break; - case PROP_LIN: - td->factor = dist; - break; - case PROP_CONST: - td->factor = 1.0f; - break; - case PROP_SPHERE: - td->factor = sqrtf(2 * dist - dist * dist); - break; - case PROP_RANDOM: - td->factor = BLI_frand() * dist; - break; - case PROP_INVSQUARE: - td->factor = dist * (2.0f - dist); - break; - default: - td->factor = 1; - break; + switch (t->prop_mode) { + case PROP_SHARP: + td->factor = dist * dist; + break; + case PROP_SMOOTH: + td->factor = 3.0f * dist * dist - 2.0f * dist * dist * dist; + break; + case PROP_ROOT: + td->factor = sqrtf(dist); + break; + case PROP_LIN: + td->factor = dist; + break; + case PROP_CONST: + td->factor = 1.0f; + break; + case PROP_SPHERE: + td->factor = sqrtf(2 * dist - dist * dist); + break; + case PROP_RANDOM: + td->factor = BLI_frand() * dist; + break; + case PROP_INVSQUARE: + td->factor = dist * (2.0f - dist); + break; + default: + td->factor = 1; + break; + } } } } + switch (t->prop_mode) { case PROP_SHARP: pet_id = N_("(Sharp)"); @@ -2057,8 +2210,11 @@ void calculatePropRatio(TransInfo *t) } } else { - for (i = 0; i < t->total; i++, td++) { - td->factor = 1.0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + td->factor = 1.0; + } } } } diff --git a/source/blender/editors/transform/transform_input.c b/source/blender/editors/transform/transform_input.c index b60c8bf47f6..270ef08be50 100644 --- a/source/blender/editors/transform/transform_input.c +++ b/source/blender/editors/transform/transform_input.c @@ -30,10 +30,13 @@ #include "DNA_screen_types.h" +#include "BKE_context.h" + #include "BLI_math.h" #include "BLI_utildefines.h" #include "WM_types.h" +#include "WM_api.h" #include "transform.h" @@ -341,6 +344,31 @@ void initMouseInputMode(TransInfo *t, MouseInput *mi, MouseInputMode mode) break; } + /* setup for the mouse cursor: either set a custom one, + * or hide it if it will be drawn with the helpline */ + wmWindow *win = CTX_wm_window(t->context); + switch (t->helpline) { + case HLP_NONE: + /* INPUT_VECTOR, INPUT_CUSTOM_RATIO, INPUT_CUSTOM_RATIO_FLIP */ + if (t->flag & T_MODAL) { + t->flag |= T_MODAL_CURSOR_SET; + WM_cursor_modal_set(win, BC_NSEW_SCROLLCURSOR); + } + break; + case HLP_SPRING: + case HLP_ANGLE: + case HLP_TRACKBALL: + case HLP_HARROW: + case HLP_VARROW: + if (t->flag & T_MODAL) { + t->flag |= T_MODAL_CURSOR_SET; + WM_cursor_modal_set(win, CURSOR_NONE); + } + break; + default: + break; + } + /* if we've allocated new data, free the old data * less hassle then checking before every alloc above */ if (mi_data_prev && (mi_data_prev != mi->data)) { @@ -391,6 +419,17 @@ void applyMouseInput(TransInfo *t, MouseInput *mi, const int mval[2], float outp mi->apply(t, mi, mval_db, output); } + if (!is_zero_v3(t->values_modal_offset)) { + float values_ofs[3]; + if (t->con.mode & CON_APPLY) { + mul_v3_m3v3(values_ofs, t->spacemtx, t->values_modal_offset); + } + else { + copy_v3_v3(values_ofs, t->values_modal_offset); + } + add_v3_v3(t->values, values_ofs); + } + if (mi->post) { mi->post(t, output); } diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c deleted file mode 100644 index 5997777f635..00000000000 --- a/source/blender/editors/transform/transform_manipulator.c +++ /dev/null @@ -1,1981 +0,0 @@ -/* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation - * All rights reserved. - * - * The Original Code is: all of this file. - * - * Contributor(s): none yet. - * - * ***** END GPL LICENSE BLOCK ***** - */ - -/** \file blender/editors/transform/transform_manipulator.c - * \ingroup edtransform - */ - - -#include <stdlib.h> -#include <string.h> -#include <math.h> -#include <float.h> - -#include "DNA_armature_types.h" -#include "DNA_curve_types.h" -#include "DNA_gpencil_types.h" -#include "DNA_lattice_types.h" -#include "DNA_meta_types.h" -#include "DNA_screen_types.h" -#include "DNA_scene_types.h" -#include "DNA_view3d_types.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "RNA_access.h" - -#include "BKE_action.h" -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_global.h" -#include "BKE_particle.h" -#include "BKE_pointcache.h" -#include "BKE_editmesh.h" -#include "BKE_lattice.h" -#include "BKE_gpencil.h" - -#include "BIF_gl.h" - -#include "WM_api.h" -#include "WM_types.h" - -#include "ED_armature.h" -#include "ED_curve.h" -#include "ED_particle.h" -#include "ED_view3d.h" -#include "ED_gpencil.h" - -#include "UI_resources.h" - -/* local module include */ -#include "transform.h" - -#include "GPU_select.h" - -/* return codes for select, and drawing flags */ - -#define MAN_TRANS_X (1 << 0) -#define MAN_TRANS_Y (1 << 1) -#define MAN_TRANS_Z (1 << 2) -#define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z) - -#define MAN_ROT_X (1 << 3) -#define MAN_ROT_Y (1 << 4) -#define MAN_ROT_Z (1 << 5) -#define MAN_ROT_V (1 << 6) -#define MAN_ROT_T (1 << 7) -#define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z | MAN_ROT_V | MAN_ROT_T) - -#define MAN_SCALE_X (1 << 8) -#define MAN_SCALE_Y (1 << 9) -#define MAN_SCALE_Z (1 << 10) -#define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z) - -/* color codes */ - -#define MAN_RGB 0 -#define MAN_GHOST 1 -#define MAN_MOVECOL 2 - -/* threshold for testing view aligned manipulator axis */ -#define TW_AXIS_DOT_MIN 0.02f -#define TW_AXIS_DOT_MAX 0.1f - -struct TransformBounds { - float center[3]; /* Center for transform widget. */ - float min[3], max[3]; /* Boundbox of selection for transform widget. */ -}; - -/* transform widget center calc helper for below */ -static void calc_tw_center(struct TransformBounds *tbounds, const float co[3]) -{ - minmax_v3v3_v3(tbounds->min, tbounds->max, co); - add_v3_v3(tbounds->center, co); -} - -static void protectflag_to_drawflags(short protectflag, short *drawflags) -{ - if (protectflag & OB_LOCK_LOCX) - *drawflags &= ~MAN_TRANS_X; - if (protectflag & OB_LOCK_LOCY) - *drawflags &= ~MAN_TRANS_Y; - if (protectflag & OB_LOCK_LOCZ) - *drawflags &= ~MAN_TRANS_Z; - - if (protectflag & OB_LOCK_ROTX) - *drawflags &= ~MAN_ROT_X; - if (protectflag & OB_LOCK_ROTY) - *drawflags &= ~MAN_ROT_Y; - if (protectflag & OB_LOCK_ROTZ) - *drawflags &= ~MAN_ROT_Z; - - if (protectflag & OB_LOCK_SCALEX) - *drawflags &= ~MAN_SCALE_X; - if (protectflag & OB_LOCK_SCALEY) - *drawflags &= ~MAN_SCALE_Y; - if (protectflag & OB_LOCK_SCALEZ) - *drawflags &= ~MAN_SCALE_Z; -} - -/* for pose mode */ -static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan) -{ - protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); -} - -/* for editmode*/ -static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo) -{ - if (ebo->flag & BONE_EDITMODE_LOCKED) { - protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag); - } -} - -/* could move into BLI_math however this is only useful for display/editing purposes */ -static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle) -{ - /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */ - - float cross_vec[3]; - float quat[4]; - - /* this is an un-scientific method to get a vector to cross with - * XYZ intentionally YZX */ - cross_vec[0] = axis[1]; - cross_vec[1] = axis[2]; - cross_vec[2] = axis[0]; - - /* X-axis */ - cross_v3_v3v3(gmat[0], cross_vec, axis); - normalize_v3(gmat[0]); - axis_angle_to_quat(quat, axis, angle); - mul_qt_v3(quat, gmat[0]); - - /* Y-axis */ - axis_angle_to_quat(quat, axis, M_PI_2); - copy_v3_v3(gmat[1], gmat[0]); - mul_qt_v3(quat, gmat[1]); - - /* Z-axis */ - copy_v3_v3(gmat[2], axis); - - normalize_m3(gmat); -} - - -static bool test_rotmode_euler(short rotmode) -{ - return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1; -} - -bool gimbal_axis(Object *ob, float gmat[3][3]) -{ - if (ob->mode & OB_MODE_POSE) { - bPoseChannel *pchan = BKE_pose_channel_active(ob); - - if (pchan) { - float mat[3][3], tmat[3][3], obmat[3][3]; - if (test_rotmode_euler(pchan->rotmode)) { - eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode); - } - else if (pchan->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle); - } - else { /* quat */ - return 0; - } - - - /* apply bone transformation */ - mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat); - - if (pchan->parent) { - float parent_mat[3][3]; - - copy_m3_m4(parent_mat, pchan->parent->pose_mat); - mul_m3_m3m3(mat, parent_mat, tmat); - - /* needed if object transformation isn't identity */ - copy_m3_m4(obmat, ob->obmat); - mul_m3_m3m3(gmat, obmat, mat); - } - else { - /* needed if object transformation isn't identity */ - copy_m3_m4(obmat, ob->obmat); - mul_m3_m3m3(gmat, obmat, tmat); - } - - normalize_m3(gmat); - return 1; - } - } - else { - if (test_rotmode_euler(ob->rotmode)) { - eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode); - } - else if (ob->rotmode == ROT_MODE_AXISANGLE) { - axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle); - } - else { /* quat */ - return 0; - } - - if (ob->parent) { - float parent_mat[3][3]; - copy_m3_m4(parent_mat, ob->parent->obmat); - normalize_m3(parent_mat); - mul_m3_m3m3(gmat, parent_mat, gmat); - } - return 1; - } - - return 0; -} - - -/* centroid, boundbox, of selection */ -/* returns total items selected */ -static int calc_manipulator_stats(const bContext *C, struct TransformBounds *tbounds) -{ - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - View3D *v3d = sa->spacedata.first; - RegionView3D *rv3d = ar->regiondata; - Base *base; - Object *ob = OBACT; - bGPdata *gpd = CTX_data_gpencil_data(C); - const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); - int a, totsel = 0; - - /* transform widget matrix */ - unit_m4(rv3d->twmat); - - rv3d->twdrawflag = 0xFFFF; - - /* transform widget centroid/center */ - INIT_MINMAX(tbounds->min, tbounds->max); - zero_v3(tbounds->center); - - if (is_gp_edit) { - float diff_mat[4][4]; - float fpt[3]; - - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - /* only editable and visible layers are considered */ - if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } - - for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } - - /* we're only interested in selected points here... */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - /* Change selection status of all points, then make the stroke match */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - if (gpl->parent == NULL) { - calc_tw_center(tbounds, &pt->x); - totsel++; - } - else { - mul_v3_m4v3(fpt, diff_mat, &pt->x); - calc_tw_center(tbounds, fpt); - totsel++; - } - } - } - } - } - } - } - - - /* selection center */ - if (totsel) { - mul_v3_fl(tbounds->center, 1.0f / (float)totsel); - } - } - else if (obedit) { - ob = obedit; - if ((ob->lay & v3d->lay) == 0) return 0; - - if (obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMEditSelection ese; - float vec[3] = {0, 0, 0}; - - /* USE LAST SELECTE WITH ACTIVE */ - if ((v3d->around == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) { - BM_editselection_center(&ese, vec); - calc_tw_center(tbounds, vec); - totsel = 1; - } - else { - BMesh *bm = em->bm; - BMVert *eve; - - BMIter iter; - - BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { - if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - totsel++; - calc_tw_center(tbounds, eve->co); - } - } - } - } - } /* end editmesh */ - else if (obedit->type == OB_ARMATURE) { - bArmature *arm = obedit->data; - EditBone *ebo; - - if ((v3d->around == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) { - /* doesn't check selection or visibility intentionally */ - if (ebo->flag & BONE_TIPSEL) { - calc_tw_center(tbounds, ebo->tail); - totsel++; - } - if ((ebo->flag & BONE_ROOTSEL) || - ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */ - { - calc_tw_center(tbounds, ebo->head); - totsel++; - } - protectflag_to_drawflags_ebone(rv3d, ebo); - } - else { - for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { - if (EBONE_VISIBLE(arm, ebo)) { - if (ebo->flag & BONE_TIPSEL) { - calc_tw_center(tbounds, ebo->tail); - totsel++; - } - if ((ebo->flag & BONE_ROOTSEL) && - /* don't include same point multiple times */ - ((ebo->flag & BONE_CONNECTED) && - (ebo->parent != NULL) && - (ebo->parent->flag & BONE_TIPSEL) && - EBONE_VISIBLE(arm, ebo->parent)) == 0) - { - calc_tw_center(tbounds, ebo->head); - totsel++; - } - if (ebo->flag & BONE_SELECTED) { - protectflag_to_drawflags_ebone(rv3d, ebo); - } - } - } - } - } - else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { - Curve *cu = obedit->data; - float center[3]; - - if (v3d->around == V3D_AROUND_ACTIVE && ED_curve_active_center(cu, center)) { - calc_tw_center(tbounds, center); - totsel++; - } - else { - Nurb *nu; - BezTriple *bezt; - BPoint *bp; - ListBase *nurbs = BKE_curve_editNurbs_get(cu); - - nu = nurbs->first; - while (nu) { - if (nu->type == CU_BEZIER) { - bezt = nu->bezt; - a = nu->pntsu; - while (a--) { - /* exceptions - * if handles are hidden then only check the center points. - * If the center knot is selected then only use this as the center point. - */ - if (cu->drawflag & CU_HIDE_HANDLES) { - if (bezt->f2 & SELECT) { - calc_tw_center(tbounds, bezt->vec[1]); - totsel++; - } - } - else if (bezt->f2 & SELECT) { - calc_tw_center(tbounds, bezt->vec[1]); - totsel++; - } - else { - if (bezt->f1 & SELECT) { - calc_tw_center( - tbounds, - bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]); - totsel++; - } - if (bezt->f3 & SELECT) { - calc_tw_center( - tbounds, - bezt->vec[(v3d->around == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]); - totsel++; - } - } - bezt++; - } - } - else { - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { - if (bp->f1 & SELECT) { - calc_tw_center(tbounds, bp->vec); - totsel++; - } - bp++; - } - } - nu = nu->next; - } - } - } - else if (obedit->type == OB_MBALL) { - MetaBall *mb = (MetaBall *)obedit->data; - MetaElem *ml; - - if ((v3d->around == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) { - calc_tw_center(tbounds, &ml->x); - totsel++; - } - else { - for (ml = mb->editelems->first; ml; ml = ml->next) { - if (ml->flag & SELECT) { - calc_tw_center(tbounds, &ml->x); - totsel++; - } - } - } - } - else if (obedit->type == OB_LATTICE) { - Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt; - BPoint *bp; - - if ((v3d->around == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) { - calc_tw_center(tbounds, bp->vec); - totsel++; - } - else { - bp = lt->def; - a = lt->pntsu * lt->pntsv * lt->pntsw; - while (a--) { - if (bp->f1 & SELECT) { - calc_tw_center(tbounds, bp->vec); - totsel++; - } - bp++; - } - } - } - - /* selection center */ - if (totsel) { - mul_v3_fl(tbounds->center, 1.0f / (float)totsel); - mul_m4_v3(obedit->obmat, tbounds->center); - mul_m4_v3(obedit->obmat, tbounds->min); - mul_m4_v3(obedit->obmat, tbounds->max); - } - } - else if (ob && (ob->mode & OB_MODE_POSE)) { - bPoseChannel *pchan; - int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed - bool ok = false; - - if ((ob->lay & v3d->lay) == 0) return 0; - - if ((v3d->around == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) { - /* doesn't check selection or visibility intentionally */ - Bone *bone = pchan->bone; - if (bone) { - calc_tw_center(tbounds, pchan->pose_head); - protectflag_to_drawflags_pchan(rv3d, pchan); - totsel = 1; - ok = true; - } - } - else { - totsel = count_set_pose_transflags(&mode, 0, ob); - - if (totsel) { - /* use channels to get stats */ - for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { - Bone *bone = pchan->bone; - if (bone && (bone->flag & BONE_TRANSFORM)) { - calc_tw_center(tbounds, pchan->pose_head); - protectflag_to_drawflags_pchan(rv3d, pchan); - } - } - ok = true; - } - } - - if (ok) { - mul_v3_fl(tbounds->center, 1.0f / (float)totsel); - mul_m4_v3(ob->obmat, tbounds->center); - mul_m4_v3(ob->obmat, tbounds->min); - mul_m4_v3(ob->obmat, tbounds->max); - } - } - else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { - /* pass */ - } - else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) { - PTCacheEdit *edit = PE_get_current(scene, ob); - PTCacheEditPoint *point; - PTCacheEditKey *ek; - int k; - - if (edit) { - point = edit->points; - for (a = 0; a < edit->totpoint; a++, point++) { - if (point->flag & PEP_HIDE) continue; - - for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) { - if (ek->flag & PEK_SELECT) { - calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co); - totsel++; - } - } - } - - /* selection center */ - if (totsel) - mul_v3_fl(tbounds->center, 1.0f / (float)totsel); - } - } - else { - - /* we need the one selected object, if its not active */ - ob = OBACT; - if (ob && !(ob->flag & SELECT)) ob = NULL; - - for (base = scene->base.first; base; base = base->next) { - if (TESTBASELIB(v3d, base)) { - if (ob == NULL) - ob = base->object; - calc_tw_center(tbounds, base->object->obmat[3]); - protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag); - totsel++; - } - } - - /* selection center */ - if (totsel) { - mul_v3_fl(tbounds->center, 1.0f / (float)totsel); - } - } - - /* global, local or normal orientation? */ - if (ob && totsel && !is_gp_edit) { - - switch (v3d->twmode) { - - case V3D_MANIP_GLOBAL: - { - break; /* nothing to do */ - } - case V3D_MANIP_GIMBAL: - { - float mat[3][3]; - if (gimbal_axis(ob, mat)) { - copy_m4_m3(rv3d->twmat, mat); - break; - } - /* if not gimbal, fall through to normal */ - ATTR_FALLTHROUGH; - } - case V3D_MANIP_NORMAL: - { - if (obedit || ob->mode & OB_MODE_POSE) { - float mat[3][3]; - ED_getTransformOrientationMatrix(C, mat, v3d->around); - copy_m4_m3(rv3d->twmat, mat); - break; - } - /* no break we define 'normal' as 'local' in Object mode */ - ATTR_FALLTHROUGH; - } - case V3D_MANIP_LOCAL: - { - if (ob->mode & OB_MODE_POSE) { - /* each bone moves on its own local axis, but to avoid confusion, - * use the active pones axis for display [#33575], this works as expected on a single bone - * and users who select many bones will understand whats going on and what local means - * when they start transforming */ - float mat[3][3]; - ED_getTransformOrientationMatrix(C, mat, v3d->around); - copy_m4_m3(rv3d->twmat, mat); - break; - } - copy_m4_m4(rv3d->twmat, ob->obmat); - normalize_m4(rv3d->twmat); - break; - } - case V3D_MANIP_VIEW: - { - float mat[3][3]; - copy_m3_m4(mat, rv3d->viewinv); - normalize_m3(mat); - copy_m4_m3(rv3d->twmat, mat); - break; - } - default: /* V3D_MANIP_CUSTOM */ - { - float mat[3][3]; - if (applyTransformOrientation(C, mat, NULL, v3d->twmode - V3D_MANIP_CUSTOM)) { - copy_m4_m3(rv3d->twmat, mat); - } - break; - } - } - - } - - return totsel; -} - -/* don't draw axis perpendicular to the view */ -static void test_manipulator_axis(const bContext *C) -{ - RegionView3D *rv3d = CTX_wm_region_view3d(C); - float view_vec[3], axis_vec[3]; - float idot; - int i; - - const int twdrawflag_axis[3] = { - (MAN_TRANS_X | MAN_SCALE_X), - (MAN_TRANS_Y | MAN_SCALE_Y), - (MAN_TRANS_Z | MAN_SCALE_Z)}; - - ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec); - - for (i = 0; i < 3; i++) { - normalize_v3_v3(axis_vec, rv3d->twmat[i]); - rv3d->tw_idot[i] = idot = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec)); - if (idot < TW_AXIS_DOT_MIN) { - rv3d->twdrawflag &= ~twdrawflag_axis[i]; - } - } -} - - -/* ******************** DRAWING STUFFIES *********** */ - -static float screen_aligned(RegionView3D *rv3d, float mat[4][4]) -{ - glTranslate3fv(mat[3]); - - /* sets view screen aligned */ - glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]); - - return len_v3(mat[0]); /* draw scale */ -} - - -/** - * \param radring: Radius of doughnut rings. - * \param radhole: Radius hole. - * \param start: Starting segment (based on \a nrings). - * \param end: End segment. - * \param nsides: Number of points in ring. - * \param nrings: Number of rings. - */ -static void partial_doughnut(float radring, float radhole, int start, int end, int nsides, int nrings) -{ - float theta, phi, theta1; - float cos_theta, sin_theta; - float cos_theta1, sin_theta1; - float ring_delta, side_delta; - int i, j, do_caps = true; - - if (start == 0 && end == nrings) do_caps = false; - - ring_delta = 2.0f * (float)M_PI / (float)nrings; - side_delta = 2.0f * (float)M_PI / (float)nsides; - - theta = (float)M_PI + 0.5f * ring_delta; - cos_theta = cosf(theta); - sin_theta = sinf(theta); - - for (i = nrings - 1; i >= 0; i--) { - theta1 = theta + ring_delta; - cos_theta1 = cosf(theta1); - sin_theta1 = sinf(theta1); - - if (do_caps && i == start) { // cap - glBegin(GL_POLYGON); - phi = 0.0; - for (j = nsides; j >= 0; j--) { - float cos_phi, sin_phi, dist; - - phi += side_delta; - cos_phi = cosf(phi); - sin_phi = sinf(phi); - dist = radhole + radring * cos_phi; - - glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); - } - glEnd(); - } - if (i >= start && i <= end) { - glBegin(GL_QUAD_STRIP); - phi = 0.0; - for (j = nsides; j >= 0; j--) { - float cos_phi, sin_phi, dist; - - phi += side_delta; - cos_phi = cosf(phi); - sin_phi = sinf(phi); - dist = radhole + radring * cos_phi; - - glVertex3f(cos_theta1 * dist, -sin_theta1 * dist, radring * sin_phi); - glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); - } - glEnd(); - } - - if (do_caps && i == end) { // cap - glBegin(GL_POLYGON); - phi = 0.0; - for (j = nsides; j >= 0; j--) { - float cos_phi, sin_phi, dist; - - phi -= side_delta; - cos_phi = cosf(phi); - sin_phi = sinf(phi); - dist = radhole + radring * cos_phi; - - glVertex3f(cos_theta * dist, -sin_theta * dist, radring * sin_phi); - } - glEnd(); - } - - - theta = theta1; - cos_theta = cos_theta1; - sin_theta = sin_theta1; - } -} - -static char axisBlendAngle(float idot) -{ - if (idot > TW_AXIS_DOT_MAX) { - return 255; - } - else if (idot < TW_AXIS_DOT_MIN) { - return 0; - } - else { - return (char)(255.0f * (idot - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN)); - } -} - -/* three colors can be set: - * gray for ghosting - * moving: in transform theme color - * else the red/green/blue - */ -static void manipulator_setcolor(View3D *v3d, char axis, int colcode, unsigned char alpha) -{ - unsigned char col[4] = {0}; - col[3] = alpha; - - if (colcode == MAN_GHOST) { - col[3] = 70; - } - else if (colcode == MAN_MOVECOL) { - UI_GetThemeColor3ubv(TH_TRANSFORM, col); - } - else { - switch (axis) { - case 'C': - UI_GetThemeColor3ubv(TH_TRANSFORM, col); - if (v3d->twmode == V3D_MANIP_LOCAL) { - col[0] = col[0] > 200 ? 255 : col[0] + 55; - col[1] = col[1] > 200 ? 255 : col[1] + 55; - col[2] = col[2] > 200 ? 255 : col[2] + 55; - } - else if (v3d->twmode == V3D_MANIP_NORMAL) { - col[0] = col[0] < 55 ? 0 : col[0] - 55; - col[1] = col[1] < 55 ? 0 : col[1] - 55; - col[2] = col[2] < 55 ? 0 : col[2] - 55; - } - break; - case 'X': - UI_GetThemeColor3ubv(TH_AXIS_X, col); - break; - case 'Y': - UI_GetThemeColor3ubv(TH_AXIS_Y, col); - break; - case 'Z': - UI_GetThemeColor3ubv(TH_AXIS_Z, col); - break; - default: - BLI_assert(0); - break; - } - } - - glColor4ubv(col); -} - -static void manipulator_axis_order(RegionView3D *rv3d, int r_axis_order[3]) -{ - float axis_values[3]; - float vec[3]; - - ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], vec); - - axis_values[0] = -dot_v3v3(rv3d->twmat[0], vec); - axis_values[1] = -dot_v3v3(rv3d->twmat[1], vec); - axis_values[2] = -dot_v3v3(rv3d->twmat[2], vec); - - axis_sort_v3(axis_values, r_axis_order); -} - -/* viewmatrix should have been set OK, also no shademode! */ -static void draw_manipulator_axes_single(View3D *v3d, RegionView3D *rv3d, int colcode, - int flagx, int flagy, int flagz, int axis, - const bool is_picksel) -{ - switch (axis) { - case 0: - /* axes */ - if (flagx) { - if (is_picksel) { - if (flagx & MAN_SCALE_X) GPU_select_load_id(MAN_SCALE_X); - else if (flagx & MAN_TRANS_X) GPU_select_load_id(MAN_TRANS_X); - } - else { - manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); - } - glBegin(GL_LINES); - glVertex3f(0.2f, 0.0f, 0.0f); - glVertex3f(1.0f, 0.0f, 0.0f); - glEnd(); - } - break; - case 1: - if (flagy) { - if (is_picksel) { - if (flagy & MAN_SCALE_Y) GPU_select_load_id(MAN_SCALE_Y); - else if (flagy & MAN_TRANS_Y) GPU_select_load_id(MAN_TRANS_Y); - } - else { - manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); - } - glBegin(GL_LINES); - glVertex3f(0.0f, 0.2f, 0.0f); - glVertex3f(0.0f, 1.0f, 0.0f); - glEnd(); - } - break; - case 2: - if (flagz) { - if (is_picksel) { - if (flagz & MAN_SCALE_Z) GPU_select_load_id(MAN_SCALE_Z); - else if (flagz & MAN_TRANS_Z) GPU_select_load_id(MAN_TRANS_Z); - } - else { - manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); - } - glBegin(GL_LINES); - glVertex3f(0.0f, 0.0f, 0.2f); - glVertex3f(0.0f, 0.0f, 1.0f); - glEnd(); - } - break; - } -} -static void draw_manipulator_axes(View3D *v3d, RegionView3D *rv3d, int colcode, - int flagx, int flagy, int flagz, - const int axis_order[3], const bool is_picksel) -{ - int i; - for (i = 0; i < 3; i++) { - draw_manipulator_axes_single(v3d, rv3d, colcode, flagx, flagy, flagz, axis_order[i], is_picksel); - } -} - -static void preOrthoFront(const bool ortho, float twmat[4][4], int axis) -{ - if (ortho == false) { - float omat[4][4]; - copy_m4_m4(omat, twmat); - orthogonalize_m4(omat, axis); - glPushMatrix(); - glMultMatrixf(omat); - glFrontFace(is_negative_m4(omat) ? GL_CW : GL_CCW); - } -} - -static void postOrtho(const bool ortho) -{ - if (ortho == false) { - glPopMatrix(); - } -} - -BLI_INLINE bool manipulator_rotate_is_visible(const int drawflags) -{ - return (drawflags & (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z)); -} - -static void draw_manipulator_rotate( - View3D *v3d, RegionView3D *rv3d, const int drawflags, const int combo, - const bool is_moving, const bool is_picksel) -{ - double plane[4]; - float matt[4][4]; - float size, unitmat[4][4]; - float cywid = 0.33f * 0.01f * (float)U.tw_handlesize; - float cusize = cywid * 0.65f; - int arcs = (G.debug_value != 2); - const int colcode = (is_moving) ? MAN_MOVECOL : MAN_RGB; - bool ortho; - - /* skip drawing if all axes are locked */ - if (manipulator_rotate_is_visible(drawflags) == false) return; - - /* Init stuff */ - glDisable(GL_DEPTH_TEST); - unit_m4(unitmat); - - /* prepare for screen aligned draw */ - size = len_v3(rv3d->twmat[0]); - glPushMatrix(); - glTranslate3fv(rv3d->twmat[3]); - - if (arcs) { - /* clipplane makes nice handles, calc here because of multmatrix but with translate! */ - copy_v3db_v3fl(plane, rv3d->viewinv[2]); - plane[3] = -0.02f * size; // clip just a bit more - glClipPlane(GL_CLIP_PLANE0, plane); - } - /* sets view screen aligned */ - glRotatef(-360.0f * saacos(rv3d->viewquat[0]) / (float)M_PI, rv3d->viewquat[1], rv3d->viewquat[2], rv3d->viewquat[3]); - - /* Screen aligned help circle */ - if (arcs) { - if (is_picksel == false) { - UI_ThemeColorShade(TH_BACK, -30); - drawcircball(GL_LINE_LOOP, unitmat[3], size, unitmat); - } - } - - /* Screen aligned trackball rot circle */ - if (drawflags & MAN_ROT_T) { - if (is_picksel) GPU_select_load_id(MAN_ROT_T); - else UI_ThemeColor(TH_TRANSFORM); - - drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); - } - - /* Screen aligned view rot circle */ - if (drawflags & MAN_ROT_V) { - if (is_picksel) GPU_select_load_id(MAN_ROT_V); - else UI_ThemeColor(TH_TRANSFORM); - drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); - - if (is_moving) { - float vec[3]; - vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]); - vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]); - vec[2] = 0.0f; - normalize_v3_length(vec, 1.2f * size); - glBegin(GL_LINES); - glVertex3f(0.0f, 0.0f, 0.0f); - glVertex3fv(vec); - glEnd(); - } - } - glPopMatrix(); - - - ortho = is_orthogonal_m4(rv3d->twmat); - - /* apply the transform delta */ - if (is_moving) { - copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] - // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); - if (ortho) { - glMultMatrixf(matt); - glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW); - } - } - else { - if (ortho) { - glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); - glMultMatrixf(rv3d->twmat); - } - } - - /* axes */ - if (arcs == 0) { - if (!is_picksel) { - if ((combo & V3D_MANIP_SCALE) == 0) { - /* axis */ - if ((drawflags & MAN_ROT_X) || (is_moving && (drawflags & MAN_ROT_Z))) { - preOrthoFront(ortho, rv3d->twmat, 2); - manipulator_setcolor(v3d, 'X', colcode, 255); - glBegin(GL_LINES); - glVertex3f(0.2f, 0.0f, 0.0f); - glVertex3f(1.0f, 0.0f, 0.0f); - glEnd(); - postOrtho(ortho); - } - if ((drawflags & MAN_ROT_Y) || (is_moving && (drawflags & MAN_ROT_X))) { - preOrthoFront(ortho, rv3d->twmat, 0); - manipulator_setcolor(v3d, 'Y', colcode, 255); - glBegin(GL_LINES); - glVertex3f(0.0f, 0.2f, 0.0f); - glVertex3f(0.0f, 1.0f, 0.0f); - glEnd(); - postOrtho(ortho); - } - if ((drawflags & MAN_ROT_Z) || (is_moving && (drawflags & MAN_ROT_Y))) { - preOrthoFront(ortho, rv3d->twmat, 1); - manipulator_setcolor(v3d, 'Z', colcode, 255); - glBegin(GL_LINES); - glVertex3f(0.0f, 0.0f, 0.2f); - glVertex3f(0.0f, 0.0f, 1.0f); - glEnd(); - postOrtho(ortho); - } - } - } - } - - if (arcs == 0 && is_moving) { - - /* Z circle */ - if (drawflags & MAN_ROT_Z) { - preOrthoFront(ortho, matt, 2); - if (is_picksel) GPU_select_load_id(MAN_ROT_Z); - else manipulator_setcolor(v3d, 'Z', colcode, 255); - drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); - postOrtho(ortho); - } - /* X circle */ - if (drawflags & MAN_ROT_X) { - preOrthoFront(ortho, matt, 0); - if (is_picksel) GPU_select_load_id(MAN_ROT_X); - else manipulator_setcolor(v3d, 'X', colcode, 255); - glRotatef(90.0, 0.0, 1.0, 0.0); - drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); - glRotatef(-90.0, 0.0, 1.0, 0.0); - postOrtho(ortho); - } - /* Y circle */ - if (drawflags & MAN_ROT_Y) { - preOrthoFront(ortho, matt, 1); - if (is_picksel) GPU_select_load_id(MAN_ROT_Y); - else manipulator_setcolor(v3d, 'Y', colcode, 255); - glRotatef(-90.0, 1.0, 0.0, 0.0); - drawcircball(GL_LINE_LOOP, unitmat[3], 1.0, unitmat); - glRotatef(90.0, 1.0, 0.0, 0.0); - postOrtho(ortho); - } - } - // donut arcs - if (arcs) { - glEnable(GL_CLIP_PLANE0); - - /* Z circle */ - if (drawflags & MAN_ROT_Z) { - preOrthoFront(ortho, rv3d->twmat, 2); - if (is_picksel) GPU_select_load_id(MAN_ROT_Z); - else manipulator_setcolor(v3d, 'Z', colcode, 255); - partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); - postOrtho(ortho); - } - /* X circle */ - if (drawflags & MAN_ROT_X) { - preOrthoFront(ortho, rv3d->twmat, 0); - if (is_picksel) GPU_select_load_id(MAN_ROT_X); - else manipulator_setcolor(v3d, 'X', colcode, 255); - glRotatef(90.0, 0.0, 1.0, 0.0); - partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); - glRotatef(-90.0, 0.0, 1.0, 0.0); - postOrtho(ortho); - } - /* Y circle */ - if (drawflags & MAN_ROT_Y) { - preOrthoFront(ortho, rv3d->twmat, 1); - if (is_picksel) GPU_select_load_id(MAN_ROT_Y); - else manipulator_setcolor(v3d, 'Y', colcode, 255); - glRotatef(-90.0, 1.0, 0.0, 0.0); - partial_doughnut(cusize / 4.0f, 1.0f, 0, 48, 8, 48); - glRotatef(90.0, 1.0, 0.0, 0.0); - postOrtho(ortho); - } - - glDisable(GL_CLIP_PLANE0); - } - - if (arcs == 0) { - - /* Z handle on X axis */ - if (drawflags & MAN_ROT_Z) { - preOrthoFront(ortho, rv3d->twmat, 2); - glPushMatrix(); - if (is_picksel) GPU_select_load_id(MAN_ROT_Z); - else manipulator_setcolor(v3d, 'Z', colcode, 255); - - partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); - - glPopMatrix(); - postOrtho(ortho); - } - - /* Y handle on X axis */ - if (drawflags & MAN_ROT_Y) { - preOrthoFront(ortho, rv3d->twmat, 1); - glPushMatrix(); - if (is_picksel) GPU_select_load_id(MAN_ROT_Y); - else manipulator_setcolor(v3d, 'Y', colcode, 255); - - glRotatef(90.0, 1.0, 0.0, 0.0); - glRotatef(90.0, 0.0, 0.0, 1.0); - partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); - - glPopMatrix(); - postOrtho(ortho); - } - - /* X handle on Z axis */ - if (drawflags & MAN_ROT_X) { - preOrthoFront(ortho, rv3d->twmat, 0); - glPushMatrix(); - if (is_picksel) GPU_select_load_id(MAN_ROT_X); - else manipulator_setcolor(v3d, 'X', colcode, 255); - - glRotatef(-90.0, 0.0, 1.0, 0.0); - glRotatef(90.0, 0.0, 0.0, 1.0); - partial_doughnut(0.7f * cusize, 1.0f, 31, 33, 8, 64); - - glPopMatrix(); - postOrtho(ortho); - } - - } - - /* restore */ - glLoadMatrixf(rv3d->viewmat); - if (v3d->zbuf) glEnable(GL_DEPTH_TEST); - -} - -static void drawsolidcube(float size) -{ - const float cube[8][3] = { - {-1.0, -1.0, -1.0}, - {-1.0, -1.0, 1.0}, - {-1.0, 1.0, 1.0}, - {-1.0, 1.0, -1.0}, - { 1.0, -1.0, -1.0}, - { 1.0, -1.0, 1.0}, - { 1.0, 1.0, 1.0}, - { 1.0, 1.0, -1.0}, - }; - float n[3] = {0.0f}; - - glPushMatrix(); - glScalef(size, size, size); - - glBegin(GL_QUADS); - n[0] = -1.0; - glNormal3fv(n); - glVertex3fv(cube[0]); glVertex3fv(cube[1]); glVertex3fv(cube[2]); glVertex3fv(cube[3]); - n[0] = 0; - glEnd(); - - glBegin(GL_QUADS); - n[1] = -1.0; - glNormal3fv(n); - glVertex3fv(cube[0]); glVertex3fv(cube[4]); glVertex3fv(cube[5]); glVertex3fv(cube[1]); - n[1] = 0; - glEnd(); - - glBegin(GL_QUADS); - n[0] = 1.0; - glNormal3fv(n); - glVertex3fv(cube[4]); glVertex3fv(cube[7]); glVertex3fv(cube[6]); glVertex3fv(cube[5]); - n[0] = 0; - glEnd(); - - glBegin(GL_QUADS); - n[1] = 1.0; - glNormal3fv(n); - glVertex3fv(cube[7]); glVertex3fv(cube[3]); glVertex3fv(cube[2]); glVertex3fv(cube[6]); - n[1] = 0; - glEnd(); - - glBegin(GL_QUADS); - n[2] = 1.0; - glNormal3fv(n); - glVertex3fv(cube[1]); glVertex3fv(cube[5]); glVertex3fv(cube[6]); glVertex3fv(cube[2]); - n[2] = 0; - glEnd(); - - glBegin(GL_QUADS); - n[2] = -1.0; - glNormal3fv(n); - glVertex3fv(cube[7]); glVertex3fv(cube[4]); glVertex3fv(cube[0]); glVertex3fv(cube[3]); - glEnd(); - - glPopMatrix(); -} - - -static void draw_manipulator_scale( - View3D *v3d, RegionView3D *rv3d, const int drawflags, const int combo, const int colcode, - const bool is_moving, const bool is_picksel) -{ - float cywid = 0.25f * 0.01f * (float)U.tw_handlesize; - float cusize = cywid * 0.75f, dz; - int axis_order[3] = {2, 0, 1}; - int i; - - /* when called while moving in mixed mode, do not draw when... */ - if ((drawflags & MAN_SCALE_C) == 0) return; - - manipulator_axis_order(rv3d, axis_order); - - glDisable(GL_DEPTH_TEST); - - /* not in combo mode */ - if ((combo & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) == 0) { - float size, unitmat[4][4]; - int shift = 0; // XXX - - /* center circle, do not add to selection when shift is pressed (planar constraint) */ - if (is_picksel && shift == 0) GPU_select_load_id(MAN_SCALE_C); - else manipulator_setcolor(v3d, 'C', colcode, 255); - - glPushMatrix(); - size = screen_aligned(rv3d, rv3d->twmat); - unit_m4(unitmat); - drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); - glPopMatrix(); - - dz = 1.0; - } - else { - dz = 1.0f - 4.0f * cusize; - } - - if (is_moving) { - float matt[4][4]; - - copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] - // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); - glMultMatrixf(matt); - glFrontFace(is_negative_m4(matt) ? GL_CW : GL_CCW); - } - else { - glMultMatrixf(rv3d->twmat); - glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); - } - - /* axis */ - - /* in combo mode, this is always drawn as first type */ - draw_manipulator_axes(v3d, rv3d, colcode, - drawflags & MAN_SCALE_X, drawflags & MAN_SCALE_Y, drawflags & MAN_SCALE_Z, - axis_order, is_picksel); - - - for (i = 0; i < 3; i++) { - switch (axis_order[i]) { - case 0: /* X cube */ - if (drawflags & MAN_SCALE_X) { - glTranslatef(dz, 0.0, 0.0); - if (is_picksel) GPU_select_load_id(MAN_SCALE_X); - else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); - drawsolidcube(cusize); - glTranslatef(-dz, 0.0, 0.0); - } - break; - case 1: /* Y cube */ - if (drawflags & MAN_SCALE_Y) { - glTranslatef(0.0, dz, 0.0); - if (is_picksel) GPU_select_load_id(MAN_SCALE_Y); - else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); - drawsolidcube(cusize); - glTranslatef(0.0, -dz, 0.0); - } - break; - case 2: /* Z cube */ - if (drawflags & MAN_SCALE_Z) { - glTranslatef(0.0, 0.0, dz); - if (is_picksel) GPU_select_load_id(MAN_SCALE_Z); - else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); - drawsolidcube(cusize); - glTranslatef(0.0, 0.0, -dz); - } - break; - } - } - -#if 0 // XXX - /* if shiftkey, center point as last, for selectbuffer order */ - if (is_picksel) { - int shift = 0; // XXX - - if (shift) { - glTranslatef(0.0, -dz, 0.0); - GPU_select_load_id(MAN_SCALE_C); - /* TODO: set glPointSize before drawing center point */ - glBegin(GL_POINTS); - glVertex3f(0.0, 0.0, 0.0); - glEnd(); - } - } -#endif - - /* restore */ - glLoadMatrixf(rv3d->viewmat); - - if (v3d->zbuf) glEnable(GL_DEPTH_TEST); - glFrontFace(GL_CCW); -} - - -static void draw_cone(GLUquadricObj *qobj, float len, float width) -{ - glTranslatef(0.0, 0.0, -0.5f * len); - gluCylinder(qobj, width, 0.0, len, 8, 1); - gluQuadricOrientation(qobj, GLU_INSIDE); - gluDisk(qobj, 0.0, width, 8, 1); - gluQuadricOrientation(qobj, GLU_OUTSIDE); - glTranslatef(0.0, 0.0, 0.5f * len); -} - -static void draw_cylinder(GLUquadricObj *qobj, float len, float width) -{ - - width *= 0.8f; // just for beauty - - glTranslatef(0.0, 0.0, -0.5f * len); - gluCylinder(qobj, width, width, len, 8, 1); - gluQuadricOrientation(qobj, GLU_INSIDE); - gluDisk(qobj, 0.0, width, 8, 1); - gluQuadricOrientation(qobj, GLU_OUTSIDE); - glTranslatef(0.0, 0.0, len); - gluDisk(qobj, 0.0, width, 8, 1); - glTranslatef(0.0, 0.0, -0.5f * len); -} - - -static void draw_manipulator_translate( - View3D *v3d, RegionView3D *rv3d, int drawflags, int combo, int colcode, - const bool UNUSED(is_moving), const bool is_picksel) -{ - GLUquadricObj *qobj; - float cylen = 0.01f * (float)U.tw_handlesize; - float cywid = 0.25f * cylen, dz, size; - float unitmat[4][4]; - int shift = 0; // XXX - int axis_order[3] = {0, 1, 2}; - int i; - - /* when called while moving in mixed mode, do not draw when... */ - if ((drawflags & MAN_TRANS_C) == 0) return; - - manipulator_axis_order(rv3d, axis_order); - - // XXX if (moving) glTranslate3fv(t->vec); - glDisable(GL_DEPTH_TEST); - - /* center circle, do not add to selection when shift is pressed (planar constraint) */ - if (is_picksel && shift == 0) GPU_select_load_id(MAN_TRANS_C); - else manipulator_setcolor(v3d, 'C', colcode, 255); - - glPushMatrix(); - size = screen_aligned(rv3d, rv3d->twmat); - unit_m4(unitmat); - drawcircball(GL_LINE_LOOP, unitmat[3], 0.2f * size, unitmat); - glPopMatrix(); - - /* and now apply matrix, we move to local matrix drawing */ - glMultMatrixf(rv3d->twmat); - - /* axis */ - GPU_select_load_id(-1); - - // translate drawn as last, only axis when no combo with scale, or for ghosting - if ((combo & V3D_MANIP_SCALE) == 0 || colcode == MAN_GHOST) { - draw_manipulator_axes(v3d, rv3d, colcode, - drawflags & MAN_TRANS_X, drawflags & MAN_TRANS_Y, drawflags & MAN_TRANS_Z, - axis_order, is_picksel); - } - - - /* offset in combo mode, for rotate a bit more */ - if (combo & (V3D_MANIP_ROTATE)) dz = 1.0f + 2.0f * cylen; - else if (combo & (V3D_MANIP_SCALE)) dz = 1.0f + 0.5f * cylen; - else dz = 1.0f; - - qobj = gluNewQuadric(); - gluQuadricDrawStyle(qobj, GLU_FILL); - - for (i = 0; i < 3; i++) { - switch (axis_order[i]) { - case 0: /* Z Cone */ - if (drawflags & MAN_TRANS_Z) { - glTranslatef(0.0, 0.0, dz); - if (is_picksel) GPU_select_load_id(MAN_TRANS_Z); - else manipulator_setcolor(v3d, 'Z', colcode, axisBlendAngle(rv3d->tw_idot[2])); - draw_cone(qobj, cylen, cywid); - glTranslatef(0.0, 0.0, -dz); - } - break; - case 1: /* X Cone */ - if (drawflags & MAN_TRANS_X) { - glTranslatef(dz, 0.0, 0.0); - if (is_picksel) GPU_select_load_id(MAN_TRANS_X); - else manipulator_setcolor(v3d, 'X', colcode, axisBlendAngle(rv3d->tw_idot[0])); - glRotatef(90.0, 0.0, 1.0, 0.0); - draw_cone(qobj, cylen, cywid); - glRotatef(-90.0, 0.0, 1.0, 0.0); - glTranslatef(-dz, 0.0, 0.0); - } - break; - case 2: /* Y Cone */ - if (drawflags & MAN_TRANS_Y) { - glTranslatef(0.0, dz, 0.0); - if (is_picksel) GPU_select_load_id(MAN_TRANS_Y); - else manipulator_setcolor(v3d, 'Y', colcode, axisBlendAngle(rv3d->tw_idot[1])); - glRotatef(-90.0, 1.0, 0.0, 0.0); - draw_cone(qobj, cylen, cywid); - glRotatef(90.0, 1.0, 0.0, 0.0); - glTranslatef(0.0, -dz, 0.0); - } - break; - } - } - - gluDeleteQuadric(qobj); - glLoadMatrixf(rv3d->viewmat); - - if (v3d->zbuf) glEnable(GL_DEPTH_TEST); - -} - -static void draw_manipulator_rotate_cyl( - View3D *v3d, RegionView3D *rv3d, int drawflags, const int combo, const int colcode, - const bool is_moving, const bool is_picksel) -{ - GLUquadricObj *qobj; - float size; - float cylen = 0.01f * (float)U.tw_handlesize; - float cywid = 0.25f * cylen; - int axis_order[3] = {2, 0, 1}; - int i; - - /* skip drawing if all axes are locked */ - if (manipulator_rotate_is_visible(drawflags) == false) return; - - manipulator_axis_order(rv3d, axis_order); - - /* prepare for screen aligned draw */ - glPushMatrix(); - size = screen_aligned(rv3d, rv3d->twmat); - - glDisable(GL_DEPTH_TEST); - - qobj = gluNewQuadric(); - - /* Screen aligned view rot circle */ - if (drawflags & MAN_ROT_V) { - float unitmat[4][4]; - - unit_m4(unitmat); - - if (is_picksel) GPU_select_load_id(MAN_ROT_V); - UI_ThemeColor(TH_TRANSFORM); - drawcircball(GL_LINE_LOOP, unitmat[3], 1.2f * size, unitmat); - - if (is_moving) { - float vec[3]; - vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]); - vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]); - vec[2] = 0.0f; - normalize_v3_length(vec, 1.2f * size); - glBegin(GL_LINES); - glVertex3f(0.0, 0.0, 0.0); - glVertex3fv(vec); - glEnd(); - } - } - glPopMatrix(); - - /* apply the transform delta */ - if (is_moving) { - float matt[4][4]; - copy_m4_m4(matt, rv3d->twmat); // to copy the parts outside of [3][3] - // XXX if (t->flag & T_USES_MANIPULATOR) { - // XXX mul_m4_m3m4(matt, t->mat, rv3d->twmat); - // XXX } - glMultMatrixf(matt); - } - else { - glMultMatrixf(rv3d->twmat); - } - - glFrontFace(is_negative_m4(rv3d->twmat) ? GL_CW : GL_CCW); - - /* axis */ - if (is_picksel == false) { - - // only draw axis when combo didn't draw scale axes - if ((combo & V3D_MANIP_SCALE) == 0) { - draw_manipulator_axes(v3d, rv3d, colcode, - drawflags & MAN_ROT_X, drawflags & MAN_ROT_Y, drawflags & MAN_ROT_Z, - axis_order, is_picksel); - } - - /* only has to be set when not in picking */ - gluQuadricDrawStyle(qobj, GLU_FILL); - } - - for (i = 0; i < 3; i++) { - switch (axis_order[i]) { - case 0: /* X cylinder */ - if (drawflags & MAN_ROT_X) { - glTranslatef(1.0, 0.0, 0.0); - if (is_picksel) GPU_select_load_id(MAN_ROT_X); - glRotatef(90.0, 0.0, 1.0, 0.0); - manipulator_setcolor(v3d, 'X', colcode, 255); - draw_cylinder(qobj, cylen, cywid); - glRotatef(-90.0, 0.0, 1.0, 0.0); - glTranslatef(-1.0, 0.0, 0.0); - } - break; - case 1: /* Y cylinder */ - if (drawflags & MAN_ROT_Y) { - glTranslatef(0.0, 1.0, 0.0); - if (is_picksel) GPU_select_load_id(MAN_ROT_Y); - glRotatef(-90.0, 1.0, 0.0, 0.0); - manipulator_setcolor(v3d, 'Y', colcode, 255); - draw_cylinder(qobj, cylen, cywid); - glRotatef(90.0, 1.0, 0.0, 0.0); - glTranslatef(0.0, -1.0, 0.0); - } - break; - case 2: /* Z cylinder */ - if (drawflags & MAN_ROT_Z) { - glTranslatef(0.0, 0.0, 1.0); - if (is_picksel) GPU_select_load_id(MAN_ROT_Z); - manipulator_setcolor(v3d, 'Z', colcode, 255); - draw_cylinder(qobj, cylen, cywid); - glTranslatef(0.0, 0.0, -1.0); - } - break; - } - } - - /* restore */ - - gluDeleteQuadric(qobj); - glLoadMatrixf(rv3d->viewmat); - - if (v3d->zbuf) glEnable(GL_DEPTH_TEST); - -} - - -/* ********************************************* */ - -/* main call, does calc centers & orientation too */ -static int drawflags = 0xFFFF; // only for the calls below, belongs in scene...? - -void BIF_draw_manipulator(const bContext *C) -{ - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d = sa->spacedata.first; - RegionView3D *rv3d = ar->regiondata; - int totsel; - - const bool is_picksel = false; - - if (!(v3d->twflag & V3D_USE_MANIPULATOR)) return; - - if ((v3d->twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE)) == 0) return; - - { - struct TransformBounds tbounds; - v3d->twflag &= ~V3D_DRAW_MANIPULATOR; - - totsel = calc_manipulator_stats(C, &tbounds); - if (totsel == 0) return; - - v3d->twflag |= V3D_DRAW_MANIPULATOR; - - /* now we can define center */ - switch (v3d->around) { - case V3D_AROUND_CENTER_BOUNDS: - case V3D_AROUND_ACTIVE: - { - bGPdata *gpd = CTX_data_gpencil_data(C); - Object *ob = OBACT; - - if (((v3d->around == V3D_AROUND_ACTIVE) && (scene->obedit == NULL)) && - ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) && - (ob && !(ob->mode & OB_MODE_POSE))) - { - copy_v3_v3(rv3d->twmat[3], ob->obmat[3]); - } - else { - mid_v3_v3v3(rv3d->twmat[3], tbounds.min, tbounds.max); - } - break; - } - case V3D_AROUND_LOCAL_ORIGINS: - case V3D_AROUND_CENTER_MEAN: - copy_v3_v3(rv3d->twmat[3], tbounds.center); - break; - case V3D_AROUND_CURSOR: - copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)); - break; - } - - mul_mat3_m4_fl(rv3d->twmat, ED_view3d_pixel_size(rv3d, rv3d->twmat[3]) * U.tw_size); - } - - /* when looking through a selected camera, the manipulator can be at the - * exact same position as the view, skip so we don't break selection */ - if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f) - return; - - test_manipulator_axis(C); - drawflags = rv3d->twdrawflag; /* set in calc_manipulator_stats */ - - if (v3d->twflag & V3D_DRAW_MANIPULATOR) { - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_BLEND); - glLineWidth(1.0f); - - if (v3d->twtype & V3D_MANIP_ROTATE) { - if (G.debug_value == 3) { - if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) - draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, v3d->twtype, MAN_MOVECOL, true, is_picksel); - else - draw_manipulator_rotate_cyl(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel); - } - else { - draw_manipulator_rotate(v3d, rv3d, drawflags, v3d->twtype, false, is_picksel); - } - } - if (v3d->twtype & V3D_MANIP_SCALE) { - draw_manipulator_scale(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel); - } - if (v3d->twtype & V3D_MANIP_TRANSLATE) { - draw_manipulator_translate(v3d, rv3d, drawflags, v3d->twtype, MAN_RGB, false, is_picksel); - } - - glDisable(GL_BLEND); - } -} - -static int manipulator_selectbuf(Scene *scene, ScrArea *sa, ARegion *ar, const int mval[2], float hotspot) -{ - View3D *v3d = sa->spacedata.first; - RegionView3D *rv3d = ar->regiondata; - rcti rect; - GLuint buffer[64]; // max 4 items per select, so large enuf - short hits; - const bool is_picksel = true; - const bool do_passes = GPU_select_query_check_active(); - - /* when looking through a selected camera, the manipulator can be at the - * exact same position as the view, skip so we don't break selection */ - if (fabsf(mat4_to_scale(rv3d->twmat)) < 1e-7f) - return 0; - - rect.xmin = mval[0] - hotspot; - rect.xmax = mval[0] + hotspot; - rect.ymin = mval[1] - hotspot; - rect.ymax = mval[1] + hotspot; - - ED_view3d_draw_setup_view(NULL, scene, ar, v3d, NULL, NULL, &rect); - - if (do_passes) - GPU_select_begin(buffer, 64, &rect, GPU_SELECT_NEAREST_FIRST_PASS, 0); - else - GPU_select_begin(buffer, 64, &rect, GPU_SELECT_ALL, 0); - - /* do the drawing */ - if (v3d->twtype & V3D_MANIP_ROTATE) { - if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, false, is_picksel); - } - if (v3d->twtype & V3D_MANIP_SCALE) - draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - if (v3d->twtype & V3D_MANIP_TRANSLATE) - draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - - hits = GPU_select_end(); - - if (do_passes && (hits > 0)) { - GPU_select_begin(buffer, 64, &rect, GPU_SELECT_NEAREST_SECOND_PASS, hits); - - /* do the drawing */ - if (v3d->twtype & V3D_MANIP_ROTATE) { - if (G.debug_value == 3) draw_manipulator_rotate_cyl(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - else draw_manipulator_rotate(v3d, rv3d, MAN_ROT_C & rv3d->twdrawflag, v3d->twtype, false, is_picksel); - } - if (v3d->twtype & V3D_MANIP_SCALE) - draw_manipulator_scale(v3d, rv3d, MAN_SCALE_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - if (v3d->twtype & V3D_MANIP_TRANSLATE) - draw_manipulator_translate(v3d, rv3d, MAN_TRANS_C & rv3d->twdrawflag, v3d->twtype, MAN_RGB, false, is_picksel); - - GPU_select_end(); - } - - ED_view3d_draw_setup_view(NULL, scene, ar, v3d, NULL, NULL, NULL); - - if (hits == 1) return buffer[3]; - else if (hits > 1) { - GLuint val, dep, mindep = 0, mindeprot = 0, minval = 0, minvalrot = 0; - int a; - - /* we compare the hits in buffer, but value centers highest */ - /* we also store the rotation hits separate (because of arcs) and return hits on other widgets if there are */ - - for (a = 0; a < hits; a++) { - dep = buffer[4 * a + 1]; - val = buffer[4 * a + 3]; - - if (val == MAN_TRANS_C) { - return MAN_TRANS_C; - } - else if (val == MAN_SCALE_C) { - return MAN_SCALE_C; - } - else { - if (val & MAN_ROT_C) { - if (minvalrot == 0 || dep < mindeprot) { - mindeprot = dep; - minvalrot = val; - } - } - else { - if (minval == 0 || dep < mindep) { - mindep = dep; - minval = val; - } - } - } - } - - if (minval) - return minval; - else - return minvalrot; - } - return 0; -} - -static const char *manipulator_get_operator_name(int man_val) -{ - if (man_val & MAN_TRANS_C) { - return "TRANSFORM_OT_translate"; - } - else if (man_val == MAN_ROT_T) { - return "TRANSFORM_OT_trackball"; - } - else if (man_val & MAN_ROT_C) { - return "TRANSFORM_OT_rotate"; - } - else if (man_val & MAN_SCALE_C) { - return "TRANSFORM_OT_resize"; - } - - return NULL; -} - -/* return 0; nothing happened */ -int BIF_do_manipulator(bContext *C, const struct wmEvent *event, wmOperator *op) -{ - Scene *scene = CTX_data_scene(C); - ScrArea *sa = CTX_wm_area(C); - View3D *v3d = sa->spacedata.first; - ARegion *ar = CTX_wm_region(C); - int constraint_axis[3] = {0, 0, 0}; - int val; - const bool use_planar = RNA_boolean_get(op->ptr, "use_planar_constraint"); - - if (!(v3d->twflag & V3D_USE_MANIPULATOR)) return 0; - if (!(v3d->twflag & V3D_DRAW_MANIPULATOR)) return 0; - - /* Force orientation */ - RNA_enum_set(op->ptr, "constraint_orientation", v3d->twmode); - - // find the hotspots first test narrow hotspot - val = manipulator_selectbuf(scene, sa, ar, event->mval, 0.5f * (float)U.tw_hotspot); - if (val) { - wmOperatorType *ot; - PointerRNA props_ptr; - PropertyRNA *prop; - const char *opname; - - // drawflags still global, for drawing call above - drawflags = manipulator_selectbuf(scene, sa, ar, event->mval, 0.2f * (float)U.tw_hotspot); - if (drawflags == 0) drawflags = val; - - /* Planar constraint doesn't make sense for rotation, give other keymaps a chance */ - if ((drawflags & MAN_ROT_C) && use_planar) { - return 0; - } - - opname = manipulator_get_operator_name(drawflags); - ot = WM_operatortype_find(opname, true); - WM_operator_properties_create_ptr(&props_ptr, ot); - - if (drawflags & MAN_TRANS_C) { - switch (drawflags) { - case MAN_TRANS_C: - break; - case MAN_TRANS_X: - if (use_planar) { - constraint_axis[1] = 1; - constraint_axis[2] = 1; - } - else - constraint_axis[0] = 1; - break; - case MAN_TRANS_Y: - if (use_planar) { - constraint_axis[0] = 1; - constraint_axis[2] = 1; - } - else - constraint_axis[1] = 1; - break; - case MAN_TRANS_Z: - if (use_planar) { - constraint_axis[0] = 1; - constraint_axis[1] = 1; - } - else - constraint_axis[2] = 1; - break; - } - RNA_boolean_set_array(&props_ptr, "constraint_axis", constraint_axis); - } - else if (drawflags & MAN_SCALE_C) { - switch (drawflags) { - case MAN_SCALE_X: - if (use_planar) { - constraint_axis[1] = 1; - constraint_axis[2] = 1; - } - else - constraint_axis[0] = 1; - break; - case MAN_SCALE_Y: - if (use_planar) { - constraint_axis[0] = 1; - constraint_axis[2] = 1; - } - else - constraint_axis[1] = 1; - break; - case MAN_SCALE_Z: - if (use_planar) { - constraint_axis[0] = 1; - constraint_axis[1] = 1; - } - else - constraint_axis[2] = 1; - break; - } - RNA_boolean_set_array(&props_ptr, "constraint_axis", constraint_axis); - } - else if (drawflags == MAN_ROT_T) { - /* pass */ - } - else if (drawflags & MAN_ROT_C) { - switch (drawflags) { - case MAN_ROT_X: - constraint_axis[0] = 1; - break; - case MAN_ROT_Y: - constraint_axis[1] = 1; - break; - case MAN_ROT_Z: - constraint_axis[2] = 1; - break; - } - RNA_boolean_set_array(&props_ptr, "constraint_axis", constraint_axis); - } - - /* pass operator properties on to transform operators */ - prop = RNA_struct_find_property(op->ptr, "use_accurate"); - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(&props_ptr, prop, RNA_property_boolean_get(op->ptr, prop)); - } - prop = RNA_struct_find_property(op->ptr, "release_confirm"); - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_boolean_set(&props_ptr, prop, RNA_property_boolean_get(op->ptr, prop)); - } - prop = RNA_struct_find_property(op->ptr, "constraint_orientation"); - if (RNA_property_is_set(op->ptr, prop)) { - RNA_property_enum_set(&props_ptr, prop, RNA_property_enum_get(op->ptr, prop)); - } - - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); - WM_operator_properties_free(&props_ptr); - } - /* after transform, restore drawflags */ - drawflags = 0xFFFF; - - return val; -} - diff --git a/source/blender/editors/transform/transform_manipulator_2d.c b/source/blender/editors/transform/transform_manipulator_2d.c new file mode 100644 index 00000000000..d2743f47000 --- /dev/null +++ b/source/blender/editors/transform/transform_manipulator_2d.c @@ -0,0 +1,382 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/transform/transform_manipulator_2d.c + * \ingroup edtransform + * + * \name 2D Transform Manipulator + * + * Used for UV/Image Editor + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_context.h" +#include "BKE_editmesh.h" + +#include "RNA_access.h" + +#include "UI_resources.h" +#include "UI_view2d.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm.h" /* XXX */ + +#include "ED_image.h" +#include "ED_screen.h" +#include "ED_uvedit.h" +#include "ED_manipulator_library.h" + +#include "transform.h" /* own include */ + +/* axes as index */ +enum { + MAN2D_AXIS_TRANS_X = 0, + MAN2D_AXIS_TRANS_Y, + + MAN2D_AXIS_LAST, +}; + +typedef struct ManipulatorGroup2D { + wmManipulator *translate_x, + *translate_y; + + wmManipulator *cage; + + /* Current origin in view space, used to update widget origin for possible view changes */ + float origin[2]; + float min[2]; + float max[2]; + +} ManipulatorGroup2D; + + +/* **************** Utilities **************** */ + +/* loop over axes */ +#define MAN2D_ITER_AXES_BEGIN(axis, axis_idx) \ + { \ + wmManipulator *axis; \ + int axis_idx; \ + for (axis_idx = 0; axis_idx < MAN2D_AXIS_LAST; axis_idx++) { \ + axis = manipulator2d_get_axis_from_index(man, axis_idx); + +#define MAN2D_ITER_AXES_END \ + } \ + } ((void)0) + +static wmManipulator *manipulator2d_get_axis_from_index(const ManipulatorGroup2D *man, const short axis_idx) +{ + BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN2D_AXIS_TRANS_X, (float)MAN2D_AXIS_TRANS_Y)); + + switch (axis_idx) { + case MAN2D_AXIS_TRANS_X: + return man->translate_x; + case MAN2D_AXIS_TRANS_Y: + return man->translate_y; + } + + return NULL; +} + +static void manipulator2d_get_axis_color(const int axis_idx, float *r_col, float *r_col_hi) +{ + const float alpha = 0.6f; + const float alpha_hi = 1.0f; + int col_id; + + switch (axis_idx) { + case MAN2D_AXIS_TRANS_X: + col_id = TH_AXIS_X; + break; + case MAN2D_AXIS_TRANS_Y: + col_id = TH_AXIS_Y; + break; + } + + UI_GetThemeColor4fv(col_id, r_col); + + copy_v4_v4(r_col_hi, r_col); + r_col[3] *= alpha; + r_col_hi[3] *= alpha_hi; +} + +static ManipulatorGroup2D *manipulatorgroup2d_init(wmManipulatorGroup *mgroup) +{ + const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_2d", true); + const wmManipulatorType *wt_cage = WM_manipulatortype_find("MANIPULATOR_WT_cage_2d", true); + + ManipulatorGroup2D *man = MEM_callocN(sizeof(ManipulatorGroup2D), __func__); + + man->translate_x = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL); + man->translate_y = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL); + man->cage = WM_manipulator_new_ptr(wt_cage, mgroup, NULL); + + RNA_enum_set(man->cage->ptr, "transform", + ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE | + ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE | + ED_MANIPULATOR_CAGE2D_XFORM_FLAG_ROTATE); + + return man; +} + +/** + * Calculates origin in view space, use with #manipulator2d_origin_to_region. + */ +static void manipulator2d_calc_bounds(const bContext *C, float *r_center, float *r_min, float *r_max) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = ED_space_image(sima); + + float min_buf[2], max_buf[2]; + if (r_min == NULL) { + r_min = min_buf; + } + if (r_max == NULL) { + r_max = max_buf; + } + + if (!ED_uvedit_minmax(CTX_data_scene(C), ima, CTX_data_edit_object(C), r_min, r_max)) { + zero_v2(r_min); + zero_v2(r_max); + } + mid_v2_v2v2(r_center, r_min, r_max); +} + +/** + * Convert origin (or any other point) from view to region space. + */ +BLI_INLINE void manipulator2d_origin_to_region(ARegion *ar, float *r_origin) +{ + UI_view2d_view_to_region_fl(&ar->v2d, r_origin[0], r_origin[1], &r_origin[0], &r_origin[1]); +} + +/** + * Custom handler for manipulator widgets + */ +static int manipulator2d_modal( + bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event), + eWM_ManipulatorTweak UNUSED(tweak_flag)) +{ + ARegion *ar = CTX_wm_region(C); + float origin[3]; + + manipulator2d_calc_bounds(C, origin, NULL, NULL); + manipulator2d_origin_to_region(ar, origin); + WM_manipulator_set_matrix_location(widget, origin); + + ED_region_tag_redraw(ar); + + return OPERATOR_RUNNING_MODAL; +} + +void ED_widgetgroup_manipulator2d_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup) +{ + wmOperatorType *ot_translate = WM_operatortype_find("TRANSFORM_OT_translate", true); + ManipulatorGroup2D *man = manipulatorgroup2d_init(mgroup); + mgroup->customdata = man; + + MAN2D_ITER_AXES_BEGIN(axis, axis_idx) + { + const float offset[3] = {0.0f, 0.2f}; + + float color[4], color_hi[4]; + manipulator2d_get_axis_color(axis_idx, color, color_hi); + + /* custom handler! */ + WM_manipulator_set_fn_custom_modal(axis, manipulator2d_modal); + /* set up widget data */ + RNA_float_set(axis->ptr, "angle", -M_PI_2 * axis_idx); + RNA_float_set(axis->ptr, "length", 0.8f); + WM_manipulator_set_matrix_offset_location(axis, offset); + WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH); + WM_manipulator_set_scale(axis, U.manipulator_size); + WM_manipulator_set_color(axis, color); + WM_manipulator_set_color_highlight(axis, color_hi); + + /* assign operator */ + PointerRNA *ptr = WM_manipulator_operator_set(axis, 0, ot_translate, NULL); + int constraint[3] = {0}; + constraint[(axis_idx + 1) % 2] = 1; + if (RNA_struct_find_property(ptr, "constraint_axis")) + RNA_boolean_set_array(ptr, "constraint_axis", constraint); + RNA_boolean_set(ptr, "release_confirm", 1); + } + MAN2D_ITER_AXES_END; + + { + wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true); + wmOperatorType *ot_rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true); + PointerRNA *ptr; + + /* assign operator */ + ptr = WM_manipulator_operator_set(man->cage, 0, ot_translate, NULL); + RNA_boolean_set(ptr, "release_confirm", 1); + + int constraint_x[3] = {1, 0, 0}; + int constraint_y[3] = {0, 1, 0}; + + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL); + PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm"); + PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis"); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MAX_Y, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MIN_Y, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MAX_Y, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + ptr = WM_manipulator_operator_set(man->cage, ED_MANIPULATOR_CAGE2D_PART_ROTATE, ot_rotate, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); + } +} + +void ED_widgetgroup_manipulator2d_refresh(const bContext *C, wmManipulatorGroup *mgroup) +{ + ManipulatorGroup2D *man = mgroup->customdata; + float origin[3]; + manipulator2d_calc_bounds(C, origin, man->min, man->max); + copy_v2_v2(man->origin, origin); + bool show_cage = !equals_v2v2(man->min, man->max); + + if (show_cage) { + man->cage->flag &= ~WM_MANIPULATOR_HIDDEN; + man->translate_x->flag |= WM_MANIPULATOR_HIDDEN; + man->translate_y->flag |= WM_MANIPULATOR_HIDDEN; + } + else { + man->cage->flag |= WM_MANIPULATOR_HIDDEN; + man->translate_x->flag &= ~WM_MANIPULATOR_HIDDEN; + man->translate_y->flag &= ~WM_MANIPULATOR_HIDDEN; + } + + if (show_cage) { + wmManipulatorOpElem *mpop; + float mid[2]; + const float *min = man->min; + const float *max = man->max; + mid_v2_v2v2(mid, min, max); + + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X); + PropertyRNA *prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override"); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f}); + + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MIN_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MIN_X_MAX_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MIN_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f}); + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_SCALE_MAX_X_MAX_Y); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f}); + + mpop = WM_manipulator_operator_get(man->cage, ED_MANIPULATOR_CAGE2D_PART_ROTATE); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f}); + } +} + +void ED_widgetgroup_manipulator2d_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup) +{ + ARegion *ar = CTX_wm_region(C); + ManipulatorGroup2D *man = mgroup->customdata; + float origin[3] = {UNPACK2(man->origin), 0.0f}; + float origin_aa[3] = {UNPACK2(man->origin), 0.0f}; + + manipulator2d_origin_to_region(ar, origin); + + MAN2D_ITER_AXES_BEGIN(axis, axis_idx) + { + WM_manipulator_set_matrix_location(axis, origin); + } + MAN2D_ITER_AXES_END; + + UI_view2d_view_to_region_m4(&ar->v2d, man->cage->matrix_space); + WM_manipulator_set_matrix_offset_location(man->cage, origin_aa); + man->cage->matrix_offset[0][0] = (man->max[0] - man->min[0]); + man->cage->matrix_offset[1][1] = (man->max[1] - man->min[1]); +} + +/* TODO (Julian) + * - Called on every redraw, better to do a more simple poll and check for selection in _refresh + * - UV editing only, could be expanded for other things. + */ +bool ED_widgetgroup_manipulator2d_poll(const bContext *C, wmManipulatorGroupType *UNUSED(wgt)) +{ + if ((U.manipulator_flag & USER_MANIPULATOR_DRAW) == 0) { + return false; + } + + SpaceImage *sima = CTX_wm_space_image(C); + Object *obedit = CTX_data_edit_object(C); + + if (ED_space_image_show_uvedit(sima, obedit)) { + Image *ima = ED_space_image(sima); + Scene *scene = CTX_data_scene(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + /* check if there's a selected poly */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) + continue; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + return true; + } + } + } + } + + return false; +} diff --git a/source/blender/editors/transform/transform_manipulator_3d.c b/source/blender/editors/transform/transform_manipulator_3d.c new file mode 100644 index 00000000000..b6782470f96 --- /dev/null +++ b/source/blender/editors/transform/transform_manipulator_3d.c @@ -0,0 +1,1732 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/transform/transform_manipulator_3d.c + * \ingroup edtransform + * + * \name 3D Transform Manipulator + * + * Used for 3D View + */ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <float.h> + +#include "DNA_armature_types.h" +#include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_lattice_types.h" +#include "DNA_meta_types.h" +#include "DNA_screen_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "RNA_access.h" + +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_particle.h" +#include "BKE_pointcache.h" +#include "BKE_editmesh.h" +#include "BKE_lattice.h" +#include "BKE_gpencil.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" + +#include "BIF_gl.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "WM_message.h" +#include "WM_toolsystem.h" + +#include "ED_armature.h" +#include "ED_curve.h" +#include "ED_object.h" +#include "ED_particle.h" +#include "ED_view3d.h" +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_manipulator_library.h" + +#include "UI_resources.h" + +/* local module include */ +#include "transform.h" + +#include "MEM_guardedalloc.h" + +#include "GPU_select.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" + +#include "DEG_depsgraph_query.h" + +/* return codes for select, and drawing flags */ + +#define MAN_TRANS_X (1 << 0) +#define MAN_TRANS_Y (1 << 1) +#define MAN_TRANS_Z (1 << 2) +#define MAN_TRANS_C (MAN_TRANS_X | MAN_TRANS_Y | MAN_TRANS_Z) + +#define MAN_ROT_X (1 << 3) +#define MAN_ROT_Y (1 << 4) +#define MAN_ROT_Z (1 << 5) +#define MAN_ROT_C (MAN_ROT_X | MAN_ROT_Y | MAN_ROT_Z) + +#define MAN_SCALE_X (1 << 8) +#define MAN_SCALE_Y (1 << 9) +#define MAN_SCALE_Z (1 << 10) +#define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z) + +/* threshold for testing view aligned manipulator axis */ +#define TW_AXIS_DOT_MIN 0.02f +#define TW_AXIS_DOT_MAX 0.1f + +/* axes as index */ +enum { + MAN_AXIS_TRANS_X = 0, + MAN_AXIS_TRANS_Y, + MAN_AXIS_TRANS_Z, + MAN_AXIS_TRANS_C, + + MAN_AXIS_TRANS_XY, + MAN_AXIS_TRANS_YZ, + MAN_AXIS_TRANS_ZX, +#define MAN_AXIS_RANGE_TRANS_START MAN_AXIS_TRANS_X +#define MAN_AXIS_RANGE_TRANS_END (MAN_AXIS_TRANS_ZX + 1) + + MAN_AXIS_ROT_X, + MAN_AXIS_ROT_Y, + MAN_AXIS_ROT_Z, + MAN_AXIS_ROT_C, + MAN_AXIS_ROT_T, /* trackball rotation */ +#define MAN_AXIS_RANGE_ROT_START MAN_AXIS_ROT_X +#define MAN_AXIS_RANGE_ROT_END (MAN_AXIS_ROT_T + 1) + + MAN_AXIS_SCALE_X, + MAN_AXIS_SCALE_Y, + MAN_AXIS_SCALE_Z, + MAN_AXIS_SCALE_C, + MAN_AXIS_SCALE_XY, + MAN_AXIS_SCALE_YZ, + MAN_AXIS_SCALE_ZX, +#define MAN_AXIS_RANGE_SCALE_START MAN_AXIS_SCALE_X +#define MAN_AXIS_RANGE_SCALE_END (MAN_AXIS_SCALE_ZX + 1) + + MAN_AXIS_LAST = MAN_AXIS_RANGE_SCALE_END, +}; + +/* axis types */ +enum { + MAN_AXES_ALL = 0, + MAN_AXES_TRANSLATE, + MAN_AXES_ROTATE, + MAN_AXES_SCALE, +}; + +/* naming from old blender we may combine. */ +enum { + V3D_MANIP_TRANSLATE = 1, + V3D_MANIP_ROTATE = 2, + V3D_MANIP_SCALE = 4, +}; + + +typedef struct ManipulatorGroup { + bool all_hidden; + int twtype; + + struct wmManipulator *manipulators[MAN_AXIS_LAST]; +} ManipulatorGroup; + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ + +/* loop over axes */ +#define MAN_ITER_AXES_BEGIN(axis, axis_idx) \ + { \ + wmManipulator *axis; \ + int axis_idx; \ + for (axis_idx = 0; axis_idx < MAN_AXIS_LAST; axis_idx++) { \ + axis = manipulator_get_axis_from_index(man, axis_idx); + +#define MAN_ITER_AXES_END \ + } \ + } ((void)0) + +static wmManipulator *manipulator_get_axis_from_index(const ManipulatorGroup *man, const short axis_idx) +{ + BLI_assert(IN_RANGE_INCL(axis_idx, (float)MAN_AXIS_TRANS_X, (float)MAN_AXIS_LAST)); + return man->manipulators[axis_idx]; +} + +static short manipulator_get_axis_type(const int axis_idx) +{ + if (axis_idx >= MAN_AXIS_RANGE_TRANS_START && axis_idx < MAN_AXIS_RANGE_TRANS_END) { + return MAN_AXES_TRANSLATE; + } + if (axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END) { + return MAN_AXES_ROTATE; + } + if (axis_idx >= MAN_AXIS_RANGE_SCALE_START && axis_idx < MAN_AXIS_RANGE_SCALE_END) { + return MAN_AXES_SCALE; + } + BLI_assert(0); + return -1; +} + +static uint manipulator_orientation_axis(const int axis_idx, bool *r_is_plane) +{ + switch (axis_idx) { + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_SCALE_YZ: + if (r_is_plane) { + *r_is_plane = true; + } + ATTR_FALLTHROUGH; + case MAN_AXIS_TRANS_X: + case MAN_AXIS_ROT_X: + case MAN_AXIS_SCALE_X: + return 0; + + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_ZX: + if (r_is_plane) { + *r_is_plane = true; + } + ATTR_FALLTHROUGH; + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_SCALE_Y: + return 1; + + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_SCALE_XY: + if (r_is_plane) { + *r_is_plane = true; + } + ATTR_FALLTHROUGH; + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_ROT_Z: + case MAN_AXIS_SCALE_Z: + return 2; + } + return 3; +} + +static bool manipulator_is_axis_visible( + const RegionView3D *rv3d, const int twtype, + const float idot[3], const int axis_type, const int axis_idx) +{ + bool is_plane = false; + const uint aidx_norm = manipulator_orientation_axis(axis_idx, &is_plane); + /* don't draw axis perpendicular to the view */ + if (aidx_norm < 3) { + float idot_axis = idot[aidx_norm]; + if (is_plane) { + idot_axis = 1.0f - idot_axis; + } + if (idot_axis < TW_AXIS_DOT_MIN) { + return false; + } + } + + if ((axis_type == MAN_AXES_TRANSLATE && !(twtype & V3D_MANIP_TRANSLATE)) || + (axis_type == MAN_AXES_ROTATE && !(twtype & V3D_MANIP_ROTATE)) || + (axis_type == MAN_AXES_SCALE && !(twtype & V3D_MANIP_SCALE))) + { + return false; + } + + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + return (rv3d->twdrawflag & MAN_TRANS_X); + case MAN_AXIS_TRANS_Y: + return (rv3d->twdrawflag & MAN_TRANS_Y); + case MAN_AXIS_TRANS_Z: + return (rv3d->twdrawflag & MAN_TRANS_Z); + case MAN_AXIS_TRANS_C: + return (rv3d->twdrawflag & MAN_TRANS_C); + case MAN_AXIS_ROT_X: + return (rv3d->twdrawflag & MAN_ROT_X); + case MAN_AXIS_ROT_Y: + return (rv3d->twdrawflag & MAN_ROT_Y); + case MAN_AXIS_ROT_Z: + return (rv3d->twdrawflag & MAN_ROT_Z); + case MAN_AXIS_ROT_C: + case MAN_AXIS_ROT_T: + return (rv3d->twdrawflag & MAN_ROT_C); + case MAN_AXIS_SCALE_X: + return (rv3d->twdrawflag & MAN_SCALE_X); + case MAN_AXIS_SCALE_Y: + return (rv3d->twdrawflag & MAN_SCALE_Y); + case MAN_AXIS_SCALE_Z: + return (rv3d->twdrawflag & MAN_SCALE_Z); + case MAN_AXIS_SCALE_C: + return (rv3d->twdrawflag & MAN_SCALE_C && (twtype & V3D_MANIP_TRANSLATE) == 0); + case MAN_AXIS_TRANS_XY: + return (rv3d->twdrawflag & MAN_TRANS_X && + rv3d->twdrawflag & MAN_TRANS_Y && + (twtype & V3D_MANIP_ROTATE) == 0); + case MAN_AXIS_TRANS_YZ: + return (rv3d->twdrawflag & MAN_TRANS_Y && + rv3d->twdrawflag & MAN_TRANS_Z && + (twtype & V3D_MANIP_ROTATE) == 0); + case MAN_AXIS_TRANS_ZX: + return (rv3d->twdrawflag & MAN_TRANS_Z && + rv3d->twdrawflag & MAN_TRANS_X && + (twtype & V3D_MANIP_ROTATE) == 0); + case MAN_AXIS_SCALE_XY: + return (rv3d->twdrawflag & MAN_SCALE_X && + rv3d->twdrawflag & MAN_SCALE_Y && + (twtype & V3D_MANIP_TRANSLATE) == 0 && + (twtype & V3D_MANIP_ROTATE) == 0); + case MAN_AXIS_SCALE_YZ: + return (rv3d->twdrawflag & MAN_SCALE_Y && + rv3d->twdrawflag & MAN_SCALE_Z && + (twtype & V3D_MANIP_TRANSLATE) == 0 && + (twtype & V3D_MANIP_ROTATE) == 0); + case MAN_AXIS_SCALE_ZX: + return (rv3d->twdrawflag & MAN_SCALE_Z && + rv3d->twdrawflag & MAN_SCALE_X && + (twtype & V3D_MANIP_TRANSLATE) == 0 && + (twtype & V3D_MANIP_ROTATE) == 0); + } + return false; +} + +static void manipulator_get_axis_color( + const int axis_idx, const float idot[3], + float r_col[4], float r_col_hi[4]) +{ + /* alpha values for normal/highlighted states */ + const float alpha = 0.6f; + const float alpha_hi = 1.0f; + float alpha_fac; + + bool is_plane = false; + const int axis_idx_norm = manipulator_orientation_axis(axis_idx, &is_plane); + /* get alpha fac based on axis angle, to fade axis out when hiding it because it points towards view */ + if (axis_idx_norm < 3) { + float idot_axis = idot[axis_idx_norm]; + if (is_plane) { + idot_axis = 1.0f - idot_axis; + } + alpha_fac = (idot_axis > TW_AXIS_DOT_MAX) ? + 1.0f : (idot_axis < TW_AXIS_DOT_MIN) ? + 0.0f : ((idot_axis - TW_AXIS_DOT_MIN) / (TW_AXIS_DOT_MAX - TW_AXIS_DOT_MIN)); + } + else { + /* trackball rotation axis is a special case, we only draw a slight overlay */ + alpha_fac = (axis_idx == MAN_AXIS_ROT_T) ? 0.1f : 1.0f; + } + + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_ROT_X: + case MAN_AXIS_SCALE_X: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_SCALE_YZ: + UI_GetThemeColor4fv(TH_AXIS_X, r_col); + break; + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_ZX: + UI_GetThemeColor4fv(TH_AXIS_Y, r_col); + break; + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_ROT_Z: + case MAN_AXIS_SCALE_Z: + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_SCALE_XY: + UI_GetThemeColor4fv(TH_AXIS_Z, r_col); + break; + case MAN_AXIS_TRANS_C: + case MAN_AXIS_ROT_C: + case MAN_AXIS_SCALE_C: + case MAN_AXIS_ROT_T: + copy_v4_fl(r_col, 1.0f); + break; + } + + copy_v4_v4(r_col_hi, r_col); + + r_col[3] = alpha * alpha_fac; + r_col_hi[3] = alpha_hi * alpha_fac; +} + +static void manipulator_get_axis_constraint(const int axis_idx, int r_axis[3]) +{ + zero_v3_int(r_axis); + + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_ROT_X: + case MAN_AXIS_SCALE_X: + r_axis[0] = 1; + break; + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_SCALE_Y: + r_axis[1] = 1; + break; + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_ROT_Z: + case MAN_AXIS_SCALE_Z: + r_axis[2] = 1; + break; + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_SCALE_XY: + r_axis[0] = r_axis[1] = 1; + break; + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_SCALE_YZ: + r_axis[1] = r_axis[2] = 1; + break; + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_ZX: + r_axis[2] = r_axis[0] = 1; + break; + default: + break; + } +} + + +/* **************** Preparation Stuff **************** */ + +/* transform widget center calc helper for below */ +static void calc_tw_center(struct TransformBounds *tbounds, const float co[3]) +{ + minmax_v3v3_v3(tbounds->min, tbounds->max, co); + add_v3_v3(tbounds->center, co); + + for (int i = 0; i < 3; i++) { + const float d = dot_v3v3(tbounds->axis[i], co); + tbounds->axis_min[i] = min_ff(d, tbounds->axis_min[i]); + tbounds->axis_max[i] = max_ff(d, tbounds->axis_max[i]); + } +} + +static void protectflag_to_drawflags(short protectflag, short *drawflags) +{ + if (protectflag & OB_LOCK_LOCX) + *drawflags &= ~MAN_TRANS_X; + if (protectflag & OB_LOCK_LOCY) + *drawflags &= ~MAN_TRANS_Y; + if (protectflag & OB_LOCK_LOCZ) + *drawflags &= ~MAN_TRANS_Z; + + if (protectflag & OB_LOCK_ROTX) + *drawflags &= ~MAN_ROT_X; + if (protectflag & OB_LOCK_ROTY) + *drawflags &= ~MAN_ROT_Y; + if (protectflag & OB_LOCK_ROTZ) + *drawflags &= ~MAN_ROT_Z; + + if (protectflag & OB_LOCK_SCALEX) + *drawflags &= ~MAN_SCALE_X; + if (protectflag & OB_LOCK_SCALEY) + *drawflags &= ~MAN_SCALE_Y; + if (protectflag & OB_LOCK_SCALEZ) + *drawflags &= ~MAN_SCALE_Z; +} + +/* for pose mode */ +static void protectflag_to_drawflags_pchan(RegionView3D *rv3d, const bPoseChannel *pchan) +{ + protectflag_to_drawflags(pchan->protectflag, &rv3d->twdrawflag); +} + +/* for editmode*/ +static void protectflag_to_drawflags_ebone(RegionView3D *rv3d, const EditBone *ebo) +{ + if (ebo->flag & BONE_EDITMODE_LOCKED) { + protectflag_to_drawflags(OB_LOCK_LOC | OB_LOCK_ROT | OB_LOCK_SCALE, &rv3d->twdrawflag); + } +} + +/* could move into BLI_math however this is only useful for display/editing purposes */ +static void axis_angle_to_gimbal_axis(float gmat[3][3], const float axis[3], const float angle) +{ + /* X/Y are arbitrary axies, most importantly Z is the axis of rotation */ + + float cross_vec[3]; + float quat[4]; + + /* this is an un-scientific method to get a vector to cross with + * XYZ intentionally YZX */ + cross_vec[0] = axis[1]; + cross_vec[1] = axis[2]; + cross_vec[2] = axis[0]; + + /* X-axis */ + cross_v3_v3v3(gmat[0], cross_vec, axis); + normalize_v3(gmat[0]); + axis_angle_to_quat(quat, axis, angle); + mul_qt_v3(quat, gmat[0]); + + /* Y-axis */ + axis_angle_to_quat(quat, axis, M_PI_2); + copy_v3_v3(gmat[1], gmat[0]); + mul_qt_v3(quat, gmat[1]); + + /* Z-axis */ + copy_v3_v3(gmat[2], axis); + + normalize_m3(gmat); +} + + +static bool test_rotmode_euler(short rotmode) +{ + return (ELEM(rotmode, ROT_MODE_AXISANGLE, ROT_MODE_QUAT)) ? 0 : 1; +} + +bool gimbal_axis(Object *ob, float gmat[3][3]) +{ + if (ob->mode & OB_MODE_POSE) { + bPoseChannel *pchan = BKE_pose_channel_active(ob); + + if (pchan) { + float mat[3][3], tmat[3][3], obmat[3][3]; + if (test_rotmode_euler(pchan->rotmode)) { + eulO_to_gimbal_axis(mat, pchan->eul, pchan->rotmode); + } + else if (pchan->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(mat, pchan->rotAxis, pchan->rotAngle); + } + else { /* quat */ + return 0; + } + + + /* apply bone transformation */ + mul_m3_m3m3(tmat, pchan->bone->bone_mat, mat); + + if (pchan->parent) { + float parent_mat[3][3]; + + copy_m3_m4(parent_mat, pchan->parent->pose_mat); + mul_m3_m3m3(mat, parent_mat, tmat); + + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, mat); + } + else { + /* needed if object transformation isn't identity */ + copy_m3_m4(obmat, ob->obmat); + mul_m3_m3m3(gmat, obmat, tmat); + } + + normalize_m3(gmat); + return 1; + } + } + else { + if (test_rotmode_euler(ob->rotmode)) { + eulO_to_gimbal_axis(gmat, ob->rot, ob->rotmode); + } + else if (ob->rotmode == ROT_MODE_AXISANGLE) { + axis_angle_to_gimbal_axis(gmat, ob->rotAxis, ob->rotAngle); + } + else { /* quat */ + return 0; + } + + if (ob->parent) { + float parent_mat[3][3]; + copy_m3_m4(parent_mat, ob->parent->obmat); + normalize_m3(parent_mat); + mul_m3_m3m3(gmat, parent_mat, gmat); + } + return 1; + } + + return 0; +} + + +/* centroid, boundbox, of selection */ +/* returns total items selected */ +int ED_transform_calc_manipulator_stats( + const bContext *C, + const struct TransformCalcParams *params, + struct TransformBounds *tbounds) +{ + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = CTX_data_edit_object(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + Base *base; + Object *ob = OBACT(view_layer); + bGPdata *gpd = CTX_data_gpencil_data(C); + const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + int a, totsel = 0; + const int pivot_point = scene->toolsettings->transform_pivot_point; + + /* transform widget matrix */ + unit_m4(rv3d->twmat); + + unit_m3(rv3d->tw_axis_matrix); + zero_v3(rv3d->tw_axis_min); + zero_v3(rv3d->tw_axis_max); + + rv3d->twdrawflag = 0xFFFF; + + /* global, local or normal orientation? + * if we could check 'totsel' now, this should be skipped with no selection. */ + if (ob && !is_gp_edit) { + const short orientation_type = params->orientation_type ? (params->orientation_type - 1) : scene->orientation_type; + + switch (orientation_type) { + + case V3D_MANIP_GLOBAL: + { + break; /* nothing to do */ + } + case V3D_MANIP_GIMBAL: + { + float mat[3][3]; + if (gimbal_axis(ob, mat)) { + copy_m4_m3(rv3d->twmat, mat); + break; + } + /* if not gimbal, fall through to normal */ + ATTR_FALLTHROUGH; + } + case V3D_MANIP_NORMAL: + { + if (obedit || ob->mode & OB_MODE_POSE) { + float mat[3][3]; + ED_getTransformOrientationMatrix(C, mat, pivot_point); + copy_m4_m3(rv3d->twmat, mat); + break; + } + /* no break we define 'normal' as 'local' in Object mode */ + ATTR_FALLTHROUGH; + } + case V3D_MANIP_LOCAL: + { + if (ob->mode & OB_MODE_POSE) { + /* each bone moves on its own local axis, but to avoid confusion, + * use the active pones axis for display [#33575], this works as expected on a single bone + * and users who select many bones will understand whats going on and what local means + * when they start transforming */ + float mat[3][3]; + ED_getTransformOrientationMatrix(C, mat, pivot_point); + copy_m4_m3(rv3d->twmat, mat); + break; + } + copy_m4_m4(rv3d->twmat, ob->obmat); + normalize_m4(rv3d->twmat); + break; + } + case V3D_MANIP_VIEW: + { + float mat[3][3]; + copy_m3_m4(mat, rv3d->viewinv); + normalize_m3(mat); + copy_m4_m3(rv3d->twmat, mat); + break; + } + case V3D_MANIP_CURSOR: + { + float mat[3][3]; + ED_view3d_cursor3d_calc_mat3(scene, v3d, mat); + copy_m4_m3(rv3d->twmat, mat); + break; + } + case V3D_MANIP_CUSTOM: + { + TransformOrientation *custom_orientation = BKE_scene_transform_orientation_find( + scene, scene->orientation_index_custom); + float mat[3][3]; + + if (applyTransformOrientation(custom_orientation, mat, NULL)) { + copy_m4_m3(rv3d->twmat, mat); + } + break; + } + } + } + + /* transform widget centroid/center */ + INIT_MINMAX(tbounds->min, tbounds->max); + zero_v3(tbounds->center); + + copy_m3_m4(tbounds->axis, rv3d->twmat); + if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) { + float diff_mat[3][3]; + copy_m3_m4(diff_mat, ob->obmat); + normalize_m3(diff_mat); + invert_m3(diff_mat); + mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat); + normalize_m3(tbounds->axis); + } + + for (int i = 0; i < 3; i++) { + tbounds->axis_min[i] = +FLT_MAX; + tbounds->axis_max[i] = -FLT_MAX; + } + + if (is_gp_edit) { + float diff_mat[4][4]; + float fpt[3]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { + + /* calculate difference matrix if parent object */ + if (gpl->parent != NULL) { + ED_gpencil_parent_location(gpl, diff_mat); + } + + for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + + /* we're only interested in selected points here... */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + /* Change selection status of all points, then make the stroke match */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (gpl->parent == NULL) { + calc_tw_center(tbounds, &pt->x); + totsel++; + } + else { + mul_v3_m4v3(fpt, diff_mat, &pt->x); + calc_tw_center(tbounds, fpt); + totsel++; + } + } + } + } + } + } + } + + + /* selection center */ + if (totsel) { + mul_v3_fl(tbounds->center, 1.0f / (float)totsel); /* centroid! */ + } + } + else if (obedit) { + ob = obedit; + if (obedit->type == OB_MESH) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMEditSelection ese; + float vec[3] = {0, 0, 0}; + + /* USE LAST SELECTE WITH ACTIVE */ + if ((pivot_point == V3D_AROUND_ACTIVE) && BM_select_history_active_get(em->bm, &ese)) { + BM_editselection_center(&ese, vec); + calc_tw_center(tbounds, vec); + totsel = 1; + } + else { + BMesh *bm = em->bm; + BMVert *eve; + + BMIter iter; + + BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) { + if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { + totsel++; + calc_tw_center(tbounds, eve->co); + } + } + } + } + } /* end editmesh */ + else if (obedit->type == OB_ARMATURE) { + bArmature *arm = obedit->data; + EditBone *ebo; + + if ((pivot_point == V3D_AROUND_ACTIVE) && (ebo = arm->act_edbone)) { + /* doesn't check selection or visibility intentionally */ + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(tbounds, ebo->tail); + totsel++; + } + if ((ebo->flag & BONE_ROOTSEL) || + ((ebo->flag & BONE_TIPSEL) == false)) /* ensure we get at least one point */ + { + calc_tw_center(tbounds, ebo->head); + totsel++; + } + protectflag_to_drawflags_ebone(rv3d, ebo); + } + else { + for (ebo = arm->edbo->first; ebo; ebo = ebo->next) { + if (EBONE_VISIBLE(arm, ebo)) { + if (ebo->flag & BONE_TIPSEL) { + calc_tw_center(tbounds, ebo->tail); + totsel++; + } + if ((ebo->flag & BONE_ROOTSEL) && + /* don't include same point multiple times */ + ((ebo->flag & BONE_CONNECTED) && + (ebo->parent != NULL) && + (ebo->parent->flag & BONE_TIPSEL) && + EBONE_VISIBLE(arm, ebo->parent)) == 0) + { + calc_tw_center(tbounds, ebo->head); + totsel++; + } + if (ebo->flag & BONE_SELECTED) { + protectflag_to_drawflags_ebone(rv3d, ebo); + } + } + } + } + } + else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { + Curve *cu = obedit->data; + float center[3]; + + if ((pivot_point == V3D_AROUND_ACTIVE) && ED_curve_active_center(cu, center)) { + calc_tw_center(tbounds, center); + totsel++; + } + else { + Nurb *nu; + BezTriple *bezt; + BPoint *bp; + ListBase *nurbs = BKE_curve_editNurbs_get(cu); + + nu = nurbs->first; + while (nu) { + if (nu->type == CU_BEZIER) { + bezt = nu->bezt; + a = nu->pntsu; + while (a--) { + /* exceptions + * if handles are hidden then only check the center points. + * If the center knot is selected then only use this as the center point. + */ + if (cu->drawflag & CU_HIDE_HANDLES) { + if (bezt->f2 & SELECT) { + calc_tw_center(tbounds, bezt->vec[1]); + totsel++; + } + } + else if (bezt->f2 & SELECT) { + calc_tw_center(tbounds, bezt->vec[1]); + totsel++; + } + else { + if (bezt->f1 & SELECT) { + calc_tw_center( + tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 0]); + totsel++; + } + if (bezt->f3 & SELECT) { + calc_tw_center( + tbounds, bezt->vec[(pivot_point == V3D_AROUND_LOCAL_ORIGINS) ? 1 : 2]); + totsel++; + } + } + bezt++; + } + } + else { + bp = nu->bp; + a = nu->pntsu * nu->pntsv; + while (a--) { + if (bp->f1 & SELECT) { + calc_tw_center(tbounds, bp->vec); + totsel++; + } + bp++; + } + } + nu = nu->next; + } + } + } + else if (obedit->type == OB_MBALL) { + MetaBall *mb = (MetaBall *)obedit->data; + MetaElem *ml; + + if ((pivot_point == V3D_AROUND_ACTIVE) && (ml = mb->lastelem)) { + calc_tw_center(tbounds, &ml->x); + totsel++; + } + else { + for (ml = mb->editelems->first; ml; ml = ml->next) { + if (ml->flag & SELECT) { + calc_tw_center(tbounds, &ml->x); + totsel++; + } + } + } + } + else if (obedit->type == OB_LATTICE) { + Lattice *lt = ((Lattice *)obedit->data)->editlatt->latt; + BPoint *bp; + + if ((pivot_point == V3D_AROUND_ACTIVE) && (bp = BKE_lattice_active_point_get(lt))) { + calc_tw_center(tbounds, bp->vec); + totsel++; + } + else { + bp = lt->def; + a = lt->pntsu * lt->pntsv * lt->pntsw; + while (a--) { + if (bp->f1 & SELECT) { + calc_tw_center(tbounds, bp->vec); + totsel++; + } + bp++; + } + } + } + + /* selection center */ + if (totsel) { + mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid! + mul_m4_v3(obedit->obmat, tbounds->center); + mul_m4_v3(obedit->obmat, tbounds->min); + mul_m4_v3(obedit->obmat, tbounds->max); + } + } + else if (ob && (ob->mode & OB_MODE_POSE)) { + bPoseChannel *pchan; + int mode = TFM_ROTATION; // mislead counting bones... bah. We don't know the manipulator mode, could be mixed + bool ok = false; + + if ((pivot_point == V3D_AROUND_ACTIVE) && (pchan = BKE_pose_channel_active(ob))) { + /* doesn't check selection or visibility intentionally */ + Bone *bone = pchan->bone; + if (bone) { + calc_tw_center(tbounds, pchan->pose_head); + protectflag_to_drawflags_pchan(rv3d, pchan); + totsel = 1; + ok = true; + } + } + else { + totsel = count_set_pose_transflags(ob, mode, V3D_AROUND_CENTER_BOUNDS, NULL); + + if (totsel) { + /* use channels to get stats */ + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + if (bone && (bone->flag & BONE_TRANSFORM)) { + calc_tw_center(tbounds, pchan->pose_head); + protectflag_to_drawflags_pchan(rv3d, pchan); + } + } + ok = true; + } + } + + if (ok) { + mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid! + mul_m4_v3(ob->obmat, tbounds->center); + mul_m4_v3(ob->obmat, tbounds->min); + mul_m4_v3(ob->obmat, tbounds->max); + } + } + else if (ob && (ob->mode & OB_MODE_ALL_PAINT)) { + /* pass */ + } + else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT) { + PTCacheEdit *edit = PE_get_current(scene, ob); + PTCacheEditPoint *point; + PTCacheEditKey *ek; + int k; + + if (edit) { + point = edit->points; + for (a = 0; a < edit->totpoint; a++, point++) { + if (point->flag & PEP_HIDE) continue; + + for (k = 0, ek = point->keys; k < point->totkey; k++, ek++) { + if (ek->flag & PEK_SELECT) { + calc_tw_center(tbounds, (ek->flag & PEK_USE_WCO) ? ek->world_co : ek->co); + totsel++; + } + } + } + + /* selection center */ + if (totsel) + mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid! + } + } + else { + + /* we need the one selected object, if its not active */ + base = BASACT(view_layer); + ob = OBACT(view_layer); + if (base && ((base->flag & BASE_SELECTED) == 0)) ob = NULL; + + for (base = view_layer->object_bases.first; base; base = base->next) { + if (!TESTBASELIB(base)) { + continue; + } + if (ob == NULL) { + ob = base->object; + } + if (params->use_only_center || base->object->bb == NULL) { + calc_tw_center(tbounds, base->object->obmat[3]); + } + else { + for (uint j = 0; j < 8; j++) { + float co[3]; + mul_v3_m4v3(co, base->object->obmat, base->object->bb->vec[j]); + calc_tw_center(tbounds, co); + } + } + protectflag_to_drawflags(base->object->protectflag, &rv3d->twdrawflag); + totsel++; + } + + /* selection center */ + if (totsel) { + mul_v3_fl(tbounds->center, 1.0f / (float)totsel); // centroid! + } + } + + if (totsel == 0) { + unit_m4(rv3d->twmat); + } + else { + copy_v3_v3(rv3d->tw_axis_min, tbounds->axis_min); + copy_v3_v3(rv3d->tw_axis_max, tbounds->axis_max); + copy_m3_m3(rv3d->tw_axis_matrix, tbounds->axis); + } + + return totsel; +} + +static void manipulator_get_idot(RegionView3D *rv3d, float r_idot[3]) +{ + float view_vec[3], axis_vec[3]; + ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec); + for (int i = 0; i < 3; i++) { + normalize_v3_v3(axis_vec, rv3d->twmat[i]); + r_idot[i] = 1.0f - fabsf(dot_v3v3(view_vec, axis_vec)); + } +} + +static void manipulator_prepare_mat( + const bContext *C, View3D *v3d, RegionView3D *rv3d, const struct TransformBounds *tbounds) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + switch (scene->toolsettings->transform_pivot_point) { + case V3D_AROUND_CENTER_BOUNDS: + case V3D_AROUND_ACTIVE: + { + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = OBACT(view_layer); + + if (((scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) && + (OBEDIT_FROM_OBACT(ob) == NULL)) && + ((gpd == NULL) || !(gpd->flag & GP_DATA_STROKE_EDITMODE)) && + (!(ob->mode & OB_MODE_POSE))) + { + copy_v3_v3(rv3d->twmat[3], ob->obmat[3]); + } + else { + mid_v3_v3v3(rv3d->twmat[3], tbounds->min, tbounds->max); + } + break; + } + case V3D_AROUND_LOCAL_ORIGINS: + case V3D_AROUND_CENTER_MEAN: + copy_v3_v3(rv3d->twmat[3], tbounds->center); + break; + case V3D_AROUND_CURSOR: + copy_v3_v3(rv3d->twmat[3], ED_view3d_cursor3d_get(scene, v3d)->location); + break; + } +} + +/** + * Sets up \a r_start and \a r_len to define arrow line range. + * Needed to adjust line drawing for combined manipulator axis types. + */ +static void manipulator_line_range(const int twtype, const short axis_type, float *r_start, float *r_len) +{ + const float ofs = 0.2f; + + *r_start = 0.2f; + *r_len = 1.0f; + + switch (axis_type) { + case MAN_AXES_TRANSLATE: + if (twtype & V3D_MANIP_SCALE) { + *r_start = *r_len - ofs + 0.075f; + } + if (twtype & V3D_MANIP_ROTATE) { + *r_len += ofs; + } + break; + case MAN_AXES_SCALE: + if (twtype & (V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE)) { + *r_len -= ofs + 0.025f; + } + break; + } + + *r_len -= *r_start; +} + +static void manipulator_xform_message_subscribe( + wmManipulatorGroup *mgroup, struct wmMsgBus *mbus, + Scene *scene, bScreen *UNUSED(screen), ScrArea *UNUSED(sa), ARegion *ar, const void *type_fn) +{ + /* Subscribe to view properties */ + wmMsgSubscribeValue msg_sub_value_mpr_tag_refresh = { + .owner = ar, + .user_data = mgroup->parent_mmap, + .notify = WM_manipulator_do_msg_notify_tag_refresh, + }; + + PointerRNA scene_ptr; + RNA_id_pointer_create(&scene->id, &scene_ptr); + + { + extern PropertyRNA rna_Scene_transform_orientation; + extern PropertyRNA rna_Scene_cursor_location; + const PropertyRNA *props[] = { + &rna_Scene_transform_orientation, + (scene->toolsettings->transform_pivot_point == V3D_AROUND_CURSOR) ? &rna_Scene_cursor_location : NULL, + }; + for (int i = 0; i < ARRAY_SIZE(props); i++) { + if (props[i]) { + WM_msg_subscribe_rna(mbus, &scene_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__); + } + } + } + + PointerRNA toolsettings_ptr; + RNA_pointer_create(&scene->id, &RNA_ToolSettings, scene->toolsettings, &toolsettings_ptr); + + if (type_fn == TRANSFORM_WGT_manipulator) { + extern PropertyRNA rna_ToolSettings_transform_pivot_point; + const PropertyRNA *props[] = { + &rna_ToolSettings_transform_pivot_point + }; + for (int i = 0; i < ARRAY_SIZE(props); i++) { + WM_msg_subscribe_rna(mbus, &toolsettings_ptr, props[i], &msg_sub_value_mpr_tag_refresh, __func__); + } + } + else if (type_fn == VIEW3D_WGT_xform_cage) { + /* pass */ + } + else { + BLI_assert(0); + } + + WM_msg_subscribe_rna_anon_prop(mbus, Window, view_layer, &msg_sub_value_mpr_tag_refresh); +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name Transform Manipulator + * \{ */ + +static ManipulatorGroup *manipulatorgroup_init(wmManipulatorGroup *mgroup) +{ + ManipulatorGroup *man; + + man = MEM_callocN(sizeof(ManipulatorGroup), "manipulator_data"); + + const wmManipulatorType *wt_arrow = WM_manipulatortype_find("MANIPULATOR_WT_arrow_3d", true); + const wmManipulatorType *wt_dial = WM_manipulatortype_find("MANIPULATOR_WT_dial_3d", true); + const wmManipulatorType *wt_prim = WM_manipulatortype_find("MANIPULATOR_WT_primitive_3d", true); + +#define MANIPULATOR_NEW_ARROW(v, draw_style) { \ + man->manipulators[v] = WM_manipulator_new_ptr(wt_arrow, mgroup, NULL); \ + RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \ +} ((void)0) +#define MANIPULATOR_NEW_DIAL(v, draw_options) { \ + man->manipulators[v] = WM_manipulator_new_ptr(wt_dial, mgroup, NULL); \ + RNA_enum_set(man->manipulators[v]->ptr, "draw_options", draw_options); \ +} ((void)0) +#define MANIPULATOR_NEW_PRIM(v, draw_style) { \ + man->manipulators[v] = WM_manipulator_new_ptr(wt_prim, mgroup, NULL); \ + RNA_enum_set(man->manipulators[v]->ptr, "draw_style", draw_style); \ +} ((void)0) + + /* add/init widgets - order matters! */ + MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_T, ED_MANIPULATOR_DIAL_DRAW_FLAG_FILL); + + MANIPULATOR_NEW_DIAL(MAN_AXIS_SCALE_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP); + + MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_X, ED_MANIPULATOR_ARROW_STYLE_BOX); + MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Y, ED_MANIPULATOR_ARROW_STYLE_BOX); + MANIPULATOR_NEW_ARROW(MAN_AXIS_SCALE_Z, ED_MANIPULATOR_ARROW_STYLE_BOX); + + MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + MANIPULATOR_NEW_PRIM(MAN_AXIS_SCALE_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + + MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_X, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP); + MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Y, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP); + MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_Z, ED_MANIPULATOR_DIAL_DRAW_FLAG_CLIP); + + /* init screen aligned widget last here, looks better, behaves better */ + MANIPULATOR_NEW_DIAL(MAN_AXIS_ROT_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP); + + MANIPULATOR_NEW_DIAL(MAN_AXIS_TRANS_C, ED_MANIPULATOR_DIAL_DRAW_FLAG_NOP); + + MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_X, ED_MANIPULATOR_ARROW_STYLE_NORMAL); + MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Y, ED_MANIPULATOR_ARROW_STYLE_NORMAL); + MANIPULATOR_NEW_ARROW(MAN_AXIS_TRANS_Z, ED_MANIPULATOR_ARROW_STYLE_NORMAL); + + MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_XY, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_YZ, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + MANIPULATOR_NEW_PRIM(MAN_AXIS_TRANS_ZX, ED_MANIPULATOR_PRIMITIVE_STYLE_PLANE); + + return man; +} + +/** + * Custom handler for manipulator widgets + */ +static int manipulator_modal( + bContext *C, wmManipulator *widget, const wmEvent *UNUSED(event), + eWM_ManipulatorTweak UNUSED(tweak_flag)) +{ + const ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + struct TransformBounds tbounds; + + + if (ED_transform_calc_manipulator_stats( + C, &(struct TransformCalcParams){ + .use_only_center = true, + }, &tbounds)) + { + manipulator_prepare_mat(C, v3d, rv3d, &tbounds); + WM_manipulator_set_matrix_location(widget, rv3d->twmat[3]); + } + + ED_region_tag_redraw(ar); + + return OPERATOR_RUNNING_MODAL; +} + +static void WIDGETGROUP_manipulator_setup(const bContext *C, wmManipulatorGroup *mgroup) +{ + ManipulatorGroup *man = manipulatorgroup_init(mgroup); + struct { + wmOperatorType *translate, *rotate, *trackball, *resize; + } ot_store = {NULL}; + + mgroup->customdata = man; + + { + /* TODO: support mixing modes again? - it's supported but tool system makes it unobvious. */ + man->twtype = 0; + ScrArea *sa = CTX_wm_area(C); + bToolRef_Runtime *tref_rt = sa->runtime.tool ? sa->runtime.tool->runtime : NULL; + wmKeyMap *km = tref_rt ? WM_keymap_find_all(C, tref_rt->keymap, sa->spacetype, RGN_TYPE_WINDOW) : NULL; + /* Weak, check first event */ + wmKeyMapItem *kmi = km ? km->items.first : NULL; + + if (kmi == NULL) { + man->twtype |= V3D_MANIP_TRANSLATE | V3D_MANIP_ROTATE | V3D_MANIP_SCALE; + } + else if (STREQ(kmi->idname, "TRANSFORM_OT_translate")) { + man->twtype |= V3D_MANIP_TRANSLATE; + } + else if (STREQ(kmi->idname, "TRANSFORM_OT_rotate")) { + man->twtype |= V3D_MANIP_ROTATE; + } + else if (STREQ(kmi->idname, "TRANSFORM_OT_resize")) { + man->twtype |= V3D_MANIP_SCALE; + } + BLI_assert(man->twtype != 0); + } + + /* *** set properties for axes *** */ + + MAN_ITER_AXES_BEGIN(axis, axis_idx) + { + const short axis_type = manipulator_get_axis_type(axis_idx); + int constraint_axis[3] = {1, 0, 0}; + PointerRNA *ptr; + + manipulator_get_axis_constraint(axis_idx, constraint_axis); + + /* custom handler! */ + WM_manipulator_set_fn_custom_modal(axis, manipulator_modal); + + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_SCALE_X: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_SCALE_Z: + WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH); + break; + case MAN_AXIS_ROT_X: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_ROT_Z: + /* increased line width for better display */ + WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH + 1.0f); + WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true); + break; + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_XY: + case MAN_AXIS_SCALE_YZ: + case MAN_AXIS_SCALE_ZX: + { + const float ofs_ax = 7.0f; + const float ofs[3] = {ofs_ax, ofs_ax, 0.0f}; + WM_manipulator_set_scale(axis, 0.07f); + WM_manipulator_set_matrix_offset_location(axis, ofs); + WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true); + break; + } + case MAN_AXIS_TRANS_C: + case MAN_AXIS_ROT_C: + case MAN_AXIS_SCALE_C: + case MAN_AXIS_ROT_T: + WM_manipulator_set_line_width(axis, MANIPULATOR_AXIS_LINE_WIDTH); + if (axis_idx == MAN_AXIS_ROT_T) { + WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_HOVER, true); + } + else if (axis_idx == MAN_AXIS_ROT_C) { + WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_VALUE, true); + } + else { + WM_manipulator_set_scale(axis, 0.2f); + } + break; + } + + switch (axis_type) { + case MAN_AXES_TRANSLATE: + if (ot_store.translate == NULL) { + ot_store.translate = WM_operatortype_find("TRANSFORM_OT_translate", true); + } + ptr = WM_manipulator_operator_set(axis, 0, ot_store.translate, NULL); + break; + case MAN_AXES_ROTATE: + { + wmOperatorType *ot_rotate; + if (axis_idx == MAN_AXIS_ROT_T) { + if (ot_store.trackball == NULL) { + ot_store.trackball = WM_operatortype_find("TRANSFORM_OT_trackball", true); + } + ot_rotate = ot_store.trackball; + } + else { + if (ot_store.rotate == NULL) { + ot_store.rotate = WM_operatortype_find("TRANSFORM_OT_rotate", true); + } + ot_rotate = ot_store.rotate; + } + ptr = WM_manipulator_operator_set(axis, 0, ot_rotate, NULL); + break; + } + case MAN_AXES_SCALE: + { + if (ot_store.resize == NULL) { + ot_store.resize = WM_operatortype_find("TRANSFORM_OT_resize", true); + } + ptr = WM_manipulator_operator_set(axis, 0, ot_store.resize, NULL); + break; + } + } + + { + PropertyRNA *prop; + if ((prop = RNA_struct_find_property(ptr, "constraint_axis"))) { + RNA_property_boolean_set_array(ptr, prop, constraint_axis); + } + } + + RNA_boolean_set(ptr, "release_confirm", 1); + } + MAN_ITER_AXES_END; +} + +static void WIDGETGROUP_manipulator_refresh(const bContext *C, wmManipulatorGroup *mgroup) +{ + ManipulatorGroup *man = mgroup->customdata; + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + struct TransformBounds tbounds; + + /* skip, we don't draw anything anyway */ + if ((man->all_hidden = + (ED_transform_calc_manipulator_stats( + C, &(struct TransformCalcParams){ + .use_only_center = true, + }, &tbounds) == 0))) + { + return; + } + + manipulator_prepare_mat(C, v3d, rv3d, &tbounds); + + /* *** set properties for axes *** */ + + MAN_ITER_AXES_BEGIN(axis, axis_idx) + { + const short axis_type = manipulator_get_axis_type(axis_idx); + const int aidx_norm = manipulator_orientation_axis(axis_idx, NULL); + + WM_manipulator_set_matrix_location(axis, rv3d->twmat[3]); + + switch (axis_idx) { + case MAN_AXIS_TRANS_X: + case MAN_AXIS_TRANS_Y: + case MAN_AXIS_TRANS_Z: + case MAN_AXIS_SCALE_X: + case MAN_AXIS_SCALE_Y: + case MAN_AXIS_SCALE_Z: + { + float start_co[3] = {0.0f, 0.0f, 0.0f}; + float len; + + manipulator_line_range(man->twtype, axis_type, &start_co[2], &len); + + WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]); + RNA_float_set(axis->ptr, "length", len); + WM_manipulator_set_matrix_offset_location(axis, start_co); + WM_manipulator_set_flag(axis, WM_MANIPULATOR_DRAW_OFFSET_SCALE, true); + break; + } + case MAN_AXIS_ROT_X: + case MAN_AXIS_ROT_Y: + case MAN_AXIS_ROT_Z: + WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->twmat[aidx_norm]); + break; + case MAN_AXIS_TRANS_XY: + case MAN_AXIS_TRANS_YZ: + case MAN_AXIS_TRANS_ZX: + case MAN_AXIS_SCALE_XY: + case MAN_AXIS_SCALE_YZ: + case MAN_AXIS_SCALE_ZX: + { + const float *y_axis = rv3d->twmat[aidx_norm - 1 < 0 ? 2 : aidx_norm - 1]; + const float *z_axis = rv3d->twmat[aidx_norm]; + WM_manipulator_set_matrix_rotation_from_yz_axis(axis, y_axis, z_axis); + break; + } + } + } + MAN_ITER_AXES_END; +} + +static void WIDGETGROUP_manipulator_message_subscribe( + const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus) +{ + Scene *scene = CTX_data_scene(C); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, TRANSFORM_WGT_manipulator); +} + +static void WIDGETGROUP_manipulator_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup) +{ + ManipulatorGroup *man = mgroup->customdata; + // ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + // View3D *v3d = sa->spacedata.first; + RegionView3D *rv3d = ar->regiondata; + float idot[3]; + + /* when looking through a selected camera, the manipulator can be at the + * exact same position as the view, skip so we don't break selection */ + if (man->all_hidden || fabsf(ED_view3d_pixel_size(rv3d, rv3d->twmat[3])) < 1e-6f) { + MAN_ITER_AXES_BEGIN(axis, axis_idx) + { + WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true); + } + MAN_ITER_AXES_END; + return; + } + manipulator_get_idot(rv3d, idot); + + /* *** set properties for axes *** */ + + MAN_ITER_AXES_BEGIN(axis, axis_idx) + { + const short axis_type = manipulator_get_axis_type(axis_idx); + /* XXX maybe unset _HIDDEN flag on redraw? */ + if (manipulator_is_axis_visible(rv3d, man->twtype, idot, axis_type, axis_idx)) { + WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, false); + } + else { + WM_manipulator_set_flag(axis, WM_MANIPULATOR_HIDDEN, true); + continue; + } + + float color[4], color_hi[4]; + manipulator_get_axis_color(axis_idx, idot, color, color_hi); + WM_manipulator_set_color(axis, color); + WM_manipulator_set_color_highlight(axis, color_hi); + + switch (axis_idx) { + case MAN_AXIS_TRANS_C: + case MAN_AXIS_ROT_C: + case MAN_AXIS_SCALE_C: + case MAN_AXIS_ROT_T: + WM_manipulator_set_matrix_rotation_from_z_axis(axis, rv3d->viewinv[2]); + break; + } + } + MAN_ITER_AXES_END; +} + +static bool WIDGETGROUP_manipulator_poll(const struct bContext *C, struct wmManipulatorGroupType *wgt) +{ + /* it's a given we only use this in 3D view */ + bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C); + if ((tref_rt == NULL) || + !STREQ(wgt->idname, tref_rt->manipulator_group)) + { + WM_manipulator_group_type_unlink_delayed_ptr(wgt); + return false; + } + return true; +} + +void TRANSFORM_WGT_manipulator(wmManipulatorGroupType *wgt) +{ + wgt->name = "Transform Manipulator"; + wgt->idname = "TRANSFORM_WGT_manipulator"; + + wgt->flag |= WM_MANIPULATORGROUPTYPE_3D; + + wgt->mmap_params.spaceid = SPACE_VIEW3D; + wgt->mmap_params.regionid = RGN_TYPE_WINDOW; + + wgt->poll = WIDGETGROUP_manipulator_poll; + wgt->setup = WIDGETGROUP_manipulator_setup; + wgt->refresh = WIDGETGROUP_manipulator_refresh; + wgt->message_subscribe = WIDGETGROUP_manipulator_message_subscribe; + wgt->draw_prepare = WIDGETGROUP_manipulator_draw_prepare; +} + +/** \} */ + + +/* -------------------------------------------------------------------- */ +/** \name Scale Cage Manipulator + * \{ */ + +struct XFormCageWidgetGroup { + wmManipulator *manipulator; +}; + +static bool WIDGETGROUP_xform_cage_poll(const bContext *C, wmManipulatorGroupType *wgt) +{ + bToolRef_Runtime *tref_rt = WM_toolsystem_runtime_from_context((bContext *)C); + if (!STREQ(wgt->idname, tref_rt->manipulator_group)) { + WM_manipulator_group_type_unlink_delayed_ptr(wgt); + return false; + } + return true; +} + +static void WIDGETGROUP_xform_cage_setup(const bContext *UNUSED(C), wmManipulatorGroup *mgroup) +{ + struct XFormCageWidgetGroup *xmgroup = MEM_mallocN(sizeof(struct XFormCageWidgetGroup), __func__); + const wmManipulatorType *wt_cage = WM_manipulatortype_find("MANIPULATOR_WT_cage_3d", true); + xmgroup->manipulator = WM_manipulator_new_ptr(wt_cage, mgroup, NULL); + wmManipulator *mpr = xmgroup->manipulator; + + RNA_enum_set(mpr->ptr, "transform", + ED_MANIPULATOR_CAGE2D_XFORM_FLAG_SCALE | + ED_MANIPULATOR_CAGE2D_XFORM_FLAG_TRANSLATE); + + mpr->color[0] = 1; + mpr->color_hi[0] = 1; + + mgroup->customdata = xmgroup; + + { + wmOperatorType *ot_resize = WM_operatortype_find("TRANSFORM_OT_resize", true); + PointerRNA *ptr; + + /* assign operator */ + PropertyRNA *prop_release_confirm = NULL; + PropertyRNA *prop_constraint_axis = NULL; + + int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z; + for (int x = 0; x < 3; x++) { + for (int y = 0; y < 3; y++) { + for (int z = 0; z < 3; z++) { + int constraint[3] = {x != 1, y != 1, z != 1}; + ptr = WM_manipulator_operator_set(mpr, i, ot_resize, NULL); + if (prop_release_confirm == NULL) { + prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm"); + prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis"); + } + RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint); + i++; + } + } + } + } +} + +static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmManipulatorGroup *mgroup) +{ + ScrArea *sa = CTX_wm_area(C); + View3D *v3d = sa->spacedata.first; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + struct XFormCageWidgetGroup *xmgroup = mgroup->customdata; + wmManipulator *mpr = xmgroup->manipulator; + + struct TransformBounds tbounds; + + if ((ED_transform_calc_manipulator_stats( + C, &(struct TransformCalcParams) { + .use_local_axis = true, + }, &tbounds) == 0) || + equals_v3v3(rv3d->tw_axis_min, rv3d->tw_axis_max)) + { + WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, true); + } + else { + manipulator_prepare_mat(C, v3d, rv3d, &tbounds); + + WM_manipulator_set_flag(mpr, WM_MANIPULATOR_HIDDEN, false); + WM_manipulator_set_flag(mpr, WM_MANIPULATOR_GRAB_CURSOR, true); + + float dims[3]; + sub_v3_v3v3(dims, rv3d->tw_axis_max, rv3d->tw_axis_min); + RNA_float_set_array(mpr->ptr, "dimensions", dims); + mul_v3_fl(dims, 0.5f); + + copy_m4_m3(mpr->matrix_offset, rv3d->tw_axis_matrix); + mid_v3_v3v3(mpr->matrix_offset[3], rv3d->tw_axis_max, rv3d->tw_axis_min); + mul_m3_v3(rv3d->tw_axis_matrix, mpr->matrix_offset[3]); + + PropertyRNA *prop_center_override = NULL; + float center[3]; + float center_global[3]; + int i = ED_MANIPULATOR_CAGE3D_PART_SCALE_MIN_X_MIN_Y_MIN_Z; + for (int x = 0; x < 3; x++) { + center[0] = (float)(1 - x) * dims[0]; + for (int y = 0; y < 3; y++) { + center[1] = (float)(1 - y) * dims[1]; + for (int z = 0; z < 3; z++) { + center[2] = (float)(1 - z) * dims[2]; + struct wmManipulatorOpElem *mpop = WM_manipulator_operator_get(mpr, i); + if (prop_center_override == NULL) { + prop_center_override = RNA_struct_find_property(&mpop->ptr, "center_override"); + } + mul_v3_m4v3(center_global, mpr->matrix_offset, center); + RNA_property_float_set_array(&mpop->ptr, prop_center_override, center_global); + i++; + } + } + } + } +} + +static void WIDGETGROUP_xform_cage_message_subscribe( + const bContext *C, wmManipulatorGroup *mgroup, struct wmMsgBus *mbus) +{ + Scene *scene = CTX_data_scene(C); + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + manipulator_xform_message_subscribe(mgroup, mbus, scene, screen, sa, ar, VIEW3D_WGT_xform_cage); +} + +static void WIDGETGROUP_xform_cage_draw_prepare(const bContext *C, wmManipulatorGroup *mgroup) +{ + struct XFormCageWidgetGroup *xmgroup = mgroup->customdata; + wmManipulator *mpr = xmgroup->manipulator; + + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + if (ob && ob->mode & OB_MODE_EDIT) { + copy_m4_m4(mpr->matrix_space, ob->obmat); + } + else { + unit_m4(mpr->matrix_space); + } +} + +void VIEW3D_WGT_xform_cage(wmManipulatorGroupType *wgt) +{ + wgt->name = "Transform Cage"; + wgt->idname = "VIEW3D_WGT_xform_cage"; + + wgt->flag |= WM_MANIPULATORGROUPTYPE_3D; + + wgt->mmap_params.spaceid = SPACE_VIEW3D; + wgt->mmap_params.regionid = RGN_TYPE_WINDOW; + + wgt->poll = WIDGETGROUP_xform_cage_poll; + wgt->setup = WIDGETGROUP_xform_cage_setup; + wgt->refresh = WIDGETGROUP_xform_cage_refresh; + wgt->message_subscribe = WIDGETGROUP_xform_cage_message_subscribe; + wgt->draw_prepare = WIDGETGROUP_xform_cage_draw_prepare; +} + +/** \} */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 4157b69fded..1e49d1545af 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -160,11 +160,12 @@ const EnumPropertyItem rna_enum_transform_mode_types[] = static int select_orientation_exec(bContext *C, wmOperator *op) { + Scene *scene = CTX_data_scene(C); int orientation = RNA_enum_get(op->ptr, "orientation"); - BIF_selectTransformOrientationValue(C, orientation); + BIF_selectTransformOrientationValue(scene, orientation); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, CTX_wm_view3d(C)); + WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); return OPERATOR_FINISHED; } @@ -205,13 +206,10 @@ static void TRANSFORM_OT_select_orientation(struct wmOperatorType *ot) static int delete_orientation_exec(bContext *C, wmOperator *UNUSED(op)) { - View3D *v3d = CTX_wm_view3d(C); - int selected_index = (v3d->twmode - V3D_MANIP_CUSTOM); + Scene *scene = CTX_data_scene(C); + BIF_removeTransformOrientationIndex(C, scene->orientation_index_custom); - BIF_removeTransformOrientationIndex(C, selected_index); - - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); - WM_event_add_notifier(C, NC_SCENE | NA_EDITED, CTX_data_scene(C)); + WM_event_add_notifier(C, NC_SCENE | NA_EDITED, scene); return OPERATOR_FINISHED; } @@ -223,18 +221,12 @@ static int delete_orientation_invoke(bContext *C, wmOperator *op, const wmEvent static int delete_orientation_poll(bContext *C) { - int selected_index = -1; - View3D *v3d = CTX_wm_view3d(C); + Scene *scene = CTX_data_scene(C); if (ED_operator_areaactive(C) == 0) return 0; - - if (v3d) { - selected_index = (v3d->twmode - V3D_MANIP_CUSTOM); - } - - return selected_index >= 0; + return (scene->orientation_type >= V3D_MANIP_CUSTOM) && (scene->orientation_index_custom != -1); } static void TRANSFORM_OT_delete_orientation(struct wmOperatorType *ot) @@ -289,6 +281,9 @@ static void TRANSFORM_OT_create_orientation(struct wmOperatorType *ot) RNA_def_string(ot->srna, "name", NULL, MAX_NAME, "Name", "Name of the new custom orientation"); RNA_def_boolean(ot->srna, "use_view", false, "Use View", "Use the current view instead of the active object to create the new orientation"); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "use", false, "Use after creation", "Select orientation after its creation"); RNA_def_boolean(ot->srna, "overwrite", false, "Overwrite previous", "Overwrite previously created orientation with same name"); @@ -319,7 +314,8 @@ static void transformops_loopsel_hack(bContext *C, wmOperator *op) /* still switch if we were originally in face select mode */ if ((ts->selectmode != selectmode_orig) && (selectmode_orig != SCE_SELECT_FACE)) { - BMEditMesh *em = BKE_editmesh_from_object(scene->obedit); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); em->selectmode = ts->selectmode = selectmode_orig; EDBM_selectmode_set(em); } @@ -483,7 +479,10 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - if (RNA_struct_property_is_set(op->ptr, "value")) { + /* When modal, allow 'value' to set initial offset. */ + if ((event == NULL) && + RNA_struct_property_is_set(op->ptr, "value")) + { return transform_exec(C, op); } else { @@ -491,6 +490,15 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_modal_handler(C, op); op->flag |= OP_IS_MODAL_GRAB_CURSOR; // XXX maybe we want this with the manipulator only? + + /* Use when modal input has some transformation to begin with. */ + { + TransInfo *t = op->customdata; + if (UNLIKELY(!is_zero_v4(t->values_modal_offset))) { + transformApply(C, t); + } + } + return OPERATOR_RUNNING_MODAL; } } @@ -555,6 +563,10 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) RNA_def_boolean(ot->srna, "gpencil_strokes", 0, "Edit Grease Pencil", "Edit selected Grease Pencil strokes"); } + if (flags & P_CURSOR_EDIT) { + RNA_def_boolean(ot->srna, "cursor_transform", 0, "Transform Cursor", ""); + } + if ((flags & P_OPTIONS) && !(flags & P_NO_TEXSPACE)) { RNA_def_boolean(ot->srna, "texture_space", 0, "Edit Texture Space", "Edit Object data texture space"); prop = RNA_def_boolean(ot->srna, "remove_on_cancel", 0, "Remove on Cancel", "Remove elements on cancel"); @@ -562,7 +574,7 @@ void Transform_Properties(struct wmOperatorType *ot, int flags) } if (flags & P_CORRECT_UV) { - RNA_def_boolean(ot->srna, "correct_uv", 0, "Correct UVs", "Correct UV coordinates when transforming"); + RNA_def_boolean(ot->srna, "correct_uv", true, "Correct UVs", "Correct UV coordinates when transforming"); } if (flags & P_CENTER) { @@ -599,7 +611,12 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot) RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); - Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | P_GPENCIL_EDIT); + WM_operatortype_props_advanced_begin(ot); + + Transform_Properties( + ot, + P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_OPTIONS | + P_GPENCIL_EDIT | P_CURSOR_EDIT); } static void TRANSFORM_OT_resize(struct wmOperatorType *ot) @@ -619,6 +636,8 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot) RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties( ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_GPENCIL_EDIT | P_CENTER); } @@ -650,6 +669,8 @@ static void TRANSFORM_OT_skin_resize(struct wmOperatorType *ot) RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_OPTIONS | P_NO_TEXSPACE); } @@ -671,6 +692,8 @@ static void TRANSFORM_OT_trackball(struct wmOperatorType *ot) /* Maybe we could use float_vector_xyz here too? */ RNA_def_float_rotation(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); } @@ -691,6 +714,8 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot) RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties( ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_GEO_SNAP | P_GPENCIL_EDIT | P_CENTER); } @@ -715,6 +740,8 @@ static void TRANSFORM_OT_tilt(struct wmOperatorType *ot) RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP); } @@ -735,6 +762,8 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot) RNA_def_float_rotation(ot->srna, "value", 1, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); } @@ -755,6 +784,8 @@ static void TRANSFORM_OT_shear(struct wmOperatorType *ot) RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT); // XXX Shear axis? } @@ -776,6 +807,8 @@ static void TRANSFORM_OT_push_pull(struct wmOperatorType *ot) RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Distance", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_CENTER); } @@ -798,6 +831,8 @@ static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_even_offset", true, "Offset Even", "Scale the offset to give more even thickness"); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP); } @@ -819,6 +854,8 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, 0, 1, "Factor", "", 0, 1); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_PROPORTIONAL | P_MIRROR | P_SNAP | P_GPENCIL_EDIT | P_CENTER); } @@ -863,6 +900,9 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); RNA_def_boolean(ot->srna, "use_even", false, "Even", "Make the edge loop match the shape of the adjacent edge loop"); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "flipped", false, "Flipped", "When Even mode is active, flips between the two adjacent edge loops"); RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp", @@ -889,6 +929,9 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); RNA_def_boolean(ot->srna, "use_even", false, "Even", "Make the edge loop match the shape of the adjacent edge loop"); + + WM_operatortype_props_advanced_begin(ot); + RNA_def_boolean(ot->srna, "flipped", false, "Flipped", "When Even mode is active, flips between the two adjacent edge loops"); RNA_def_boolean(ot->srna, "use_clamp", true, "Clamp", @@ -914,6 +957,8 @@ static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_SNAP); } @@ -955,6 +1000,8 @@ static void TRANSFORM_OT_edge_bevelweight(struct wmOperatorType *ot) RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_SNAP); } @@ -975,6 +1022,8 @@ static void TRANSFORM_OT_seq_slide(struct wmOperatorType *ot) RNA_def_float_vector_xyz(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties(ot, P_SNAP); } @@ -1000,6 +1049,8 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot) RNA_def_float_vector(ot->srna, "value", 4, NULL, -FLT_MAX, FLT_MAX, "Values", "", -FLT_MAX, FLT_MAX); + WM_operatortype_props_advanced_begin(ot); + Transform_Properties( ot, P_AXIS | P_CONSTRAINT | P_PROPORTIONAL | P_MIRROR | P_ALIGN_SNAP | P_GPENCIL_EDIT | P_CENTER); } @@ -1063,9 +1114,8 @@ void transform_keymap_for_space(wmKeyConfig *keyconf, wmKeyMap *keymap, int spac kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", TABKEY, KM_PRESS, KM_SHIFT, 0); RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_snap"); - kmi = WM_keymap_add_item(keymap, "WM_OT_context_menu_enum", TABKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); - RNA_string_set(kmi->ptr, "data_path", "tool_settings.snap_element"); - + /* Will fall-through to texture-space transform. */ + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_transform_axis_target", TKEY, KM_PRESS, KM_SHIFT, 0); kmi = WM_keymap_add_item(keymap, OP_TRANSLATION, TKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "texture_space", true); diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index c38fb15fc89..19df46455d7 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -38,6 +38,7 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" +#include "DNA_workspace_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -52,6 +53,8 @@ #include "BKE_report.h" #include "BKE_main.h" #include "BKE_screen.h" +#include "BKE_scene.h" +#include "BKE_workspace.h" #include "BLT_translation.h" @@ -63,14 +66,15 @@ void BIF_clearTransformOrientation(bContext *C) { + Scene *scene = CTX_data_scene(C); + ListBase *transform_orientations = &scene->transform_spaces; View3D *v3d = CTX_wm_view3d(C); - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; - BLI_freelistN(transform_spaces); + BLI_freelistN(transform_orientations); - // Need to loop over all view3d - if (v3d && v3d->twmode >= V3D_MANIP_CUSTOM) { - v3d->twmode = V3D_MANIP_GLOBAL; /* fallback to global */ + if (v3d && scene->orientation_type == V3D_MANIP_CUSTOM) { + scene->orientation_type = V3D_MANIP_GLOBAL; /* fallback to global */ + scene->orientation_index_custom = -1; } } @@ -318,23 +322,24 @@ void BIF_createTransformOrientation(bContext *C, ReportList *reports, TransformOrientation *addMatrixSpace(bContext *C, float mat[3][3], const char *name, const bool overwrite) { - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; TransformOrientation *ts = NULL; + Scene *scene = CTX_data_scene(C); + ListBase *transform_orientations = &scene->transform_spaces; char name_unique[sizeof(ts->name)]; if (overwrite) { - ts = findOrientationName(transform_spaces, name); + ts = findOrientationName(transform_orientations, name); } else { BLI_strncpy(name_unique, name, sizeof(name_unique)); - uniqueOrientationName(transform_spaces, name_unique); + uniqueOrientationName(transform_orientations, name_unique); name = name_unique; } /* if not, create a new one */ if (ts == NULL) { ts = MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix"); - BLI_addtail(transform_spaces, ts); + BLI_addtail(transform_orientations, ts); BLI_strncpy(ts->name, name, sizeof(ts->name)); } @@ -346,70 +351,54 @@ TransformOrientation *addMatrixSpace(bContext *C, float mat[3][3], void BIF_removeTransformOrientation(bContext *C, TransformOrientation *target) { - Scene *scene = CTX_data_scene(C); - ListBase *transform_spaces = &scene->transform_spaces; - const int i = BLI_findindex(transform_spaces, target); - - if (i != -1) { - Main *bmain = CTX_data_main(C); - BKE_screen_view3d_main_twmode_remove(&bmain->screen, scene, i); - BLI_freelinkN(transform_spaces, target); - } + BKE_scene_transform_orientation_remove(CTX_data_scene(C), target); } void BIF_removeTransformOrientationIndex(bContext *C, int index) { - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; - TransformOrientation *ts = BLI_findlink(transform_spaces, index); - - if (ts) { - BIF_removeTransformOrientation(C, ts); - } + TransformOrientation *target = BKE_scene_transform_orientation_find(CTX_data_scene(C), index); + BIF_removeTransformOrientation(C, target); } void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target) { - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; - const int i = BLI_findindex(transform_spaces, target); + Scene *scene = CTX_data_scene(C); + int index = BKE_scene_transform_orientation_get_index(scene, target); - if (i != -1) { - View3D *v3d = CTX_wm_view3d(C); - v3d->twmode = V3D_MANIP_CUSTOM + i; - } + BLI_assert(index != -1); + + scene->orientation_type = V3D_MANIP_CUSTOM; + scene->orientation_index_custom = index; } -void BIF_selectTransformOrientationValue(bContext *C, int orientation) +/** + * Activate a transform orientation in a 3D view based on an enum value. + * + * \param orientation: If this is #V3D_MANIP_CUSTOM or greater, the custom transform orientation + * with index \a orientation - #V3D_MANIP_CUSTOM gets activated. + */ +void BIF_selectTransformOrientationValue(Scene *scene, int orientation) { - View3D *v3d = CTX_wm_view3d(C); - if (v3d) /* currently using generic poll */ - v3d->twmode = orientation; + const bool is_custom = orientation >= V3D_MANIP_CUSTOM; + scene->orientation_type = is_custom ? V3D_MANIP_CUSTOM : orientation; + scene->orientation_index_custom = is_custom ? (orientation - V3D_MANIP_CUSTOM) : -1; } int BIF_countTransformOrientation(const bContext *C) { - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; - return BLI_listbase_count(transform_spaces); + Scene *scene = CTX_data_scene(C); + ListBase *transform_orientations = &scene->transform_spaces; + return BLI_listbase_count(transform_orientations); } -bool applyTransformOrientation(const bContext *C, float mat[3][3], char *r_name, int index) +bool applyTransformOrientation(const TransformOrientation *ts, float r_mat[3][3], char *r_name) { - ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; - TransformOrientation *ts = BLI_findlink(transform_spaces, index); - - BLI_assert(index >= 0); - - if (ts) { - if (r_name) { - BLI_strncpy(r_name, ts->name, MAX_NAME); - } - - copy_m3_m3(mat, ts->mat); - return true; - } - else { - /* invalid index, can happen sometimes */ - return false; + if (r_name) { + BLI_strncpy(r_name, ts->name, MAX_NAME); } + copy_m3_m3(r_mat, ts->mat); + + return true; } static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it) @@ -492,8 +481,16 @@ void initTransformOrientation(bContext *C, TransInfo *t) unit_m3(t->spacemtx); } break; - default: /* V3D_MANIP_CUSTOM */ - if (applyTransformOrientation(C, t->spacemtx, t->spacename, t->current_orientation - V3D_MANIP_CUSTOM)) { + case V3D_MANIP_CURSOR: + { + BLI_strncpy(t->spacename, IFACE_("cursor"), sizeof(t->spacename)); + ED_view3d_cursor3d_calc_mat3(t->scene, CTX_wm_view3d(C), t->spacemtx); + break; + } + case V3D_MANIP_CUSTOM: + BLI_strncpy(t->spacename, t->custom_orientation->name, sizeof(t->spacename)); + + if (applyTransformOrientation(t->custom_orientation, t->spacemtx, t->spacename)) { /* pass */ } else { @@ -586,10 +583,10 @@ static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3], const short around) { - Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); Base *base; - Object *ob = OBACT; + Object *ob = OBACT(view_layer); int result = ORIENTATION_NONE; const bool activeOnly = (around == V3D_AROUND_ACTIVE); @@ -1061,16 +1058,16 @@ int getTransformOrientation_ex(const bContext *C, float normal[3], float plane[3 } else { /* we need the one selected object, if its not active */ - View3D *v3d = CTX_wm_view3d(C); - ob = OBACT; - if (ob && (ob->flag & SELECT)) { + base = BASACT(view_layer); + ob = OBACT(view_layer); + if (base && ((base->flag & BASE_SELECTED) != 0)) { /* pass */ } else { /* first selected */ ob = NULL; - for (base = scene->base.first; base; base = base->next) { - if (TESTBASELIB(v3d, base)) { + for (base = view_layer->object_bases.first; base; base = base->next) { + if (TESTBASELIB(base)) { ob = base->object; break; } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 5ea826521ed..704582deaca 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -49,7 +49,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BIF_gl.h" +#include "GPU_immediate.h" #include "BKE_DerivedMesh.h" #include "BKE_global.h" @@ -70,6 +70,8 @@ #include "ED_view3d.h" #include "ED_transform_snap_object_context.h" +#include "DEG_depsgraph.h" + #include "UI_resources.h" #include "UI_view2d.h" @@ -164,35 +166,41 @@ void drawSnapping(const struct bContext *C, TransInfo *t) invert_m4_m4(imat, rv3d->viewmat); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + for (p = t->tsnap.points.first; p; p = p->next) { if (p == t->tsnap.selectedPoint) { - glColor4ubv(selectedCol); + immUniformColor4ubv(selectedCol); } else { - glColor4ubv(col); + immUniformColor4ubv(col); } - drawcircball(GL_LINE_LOOP, p->co, ED_view3d_pixel_size(rv3d, p->co) * size * 0.75f, imat); + imm_drawcircball(p->co, ED_view3d_pixel_size(rv3d, p->co) * size * 0.75f, imat, pos); } if (t->tsnap.status & POINT_INIT) { - glColor4ubv(activeCol); + immUniformColor4ubv(activeCol); - drawcircball(GL_LINE_LOOP, t->tsnap.snapPoint, ED_view3d_pixel_size(rv3d, t->tsnap.snapPoint) * size, imat); + imm_drawcircball(t->tsnap.snapPoint, ED_view3d_pixel_size(rv3d, t->tsnap.snapPoint) * size, imat, pos); } /* draw normal if needed */ if (usingSnappingNormal(t) && validSnappingNormal(t)) { - glColor4ubv(activeCol); - - glBegin(GL_LINES); - glVertex3f(t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]); - glVertex3f(t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0], - t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1], - t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]); - glEnd(); + immUniformColor4ubv(activeCol); + + immBegin(GWN_PRIM_LINES, 2); + immVertex3f(pos, t->tsnap.snapPoint[0], t->tsnap.snapPoint[1], t->tsnap.snapPoint[2]); + immVertex3f(pos, t->tsnap.snapPoint[0] + t->tsnap.snapNormal[0], + t->tsnap.snapPoint[1] + t->tsnap.snapNormal[1], + t->tsnap.snapPoint[2] + t->tsnap.snapNormal[2]); + immEnd(); } + immUnbindProgram(); + if (v3d->zbuf) glEnable(GL_DEPTH_TEST); } @@ -200,35 +208,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IMAGE) { if (validSnap(t)) { /* This will not draw, and Im nor sure why - campbell */ -#if 0 - float xuser_asp, yuser_asp; - int wi, hi; - float w, h; - - calc_image_view(G.sima, 'f'); // float - myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax); - glLoadIdentity(); - - ED_space_image_get_aspect(t->sa->spacedata.first, &xuser_aspx, &yuser_asp); - ED_space_image_width(t->sa->spacedata.first, &wi, &hi); - w = (((float)wi) / IMG_SIZE_FALLBACK) * G.sima->zoom * xuser_asp; - h = (((float)hi) / IMG_SIZE_FALLBACK) * G.sima->zoom * yuser_asp; - - cpack(0xFFFFFF); - glTranslate2fv(t->tsnap.snapPoint); - - //glRectf(0, 0, 1, 1); - - setlinestyle(0); - cpack(0x0); - fdrawline(-0.020 / w, 0, -0.1 / w, 0); - fdrawline(0.1 / w, 0, 0.020 / w, 0); - fdrawline(0, -0.020 / h, 0, -0.1 / h); - fdrawline(0, 0.1 / h, 0, 0.020 / h); - - glTranslatef(-t->tsnap.snapPoint[0], -t->tsnap.snapPoint[1], 0.0f); - setlinestyle(0); -#endif + /* TODO: see 2.7x for non-working code */ } } else if (t->spacetype == SPACE_NODE) { @@ -241,23 +221,29 @@ void drawSnapping(const struct bContext *C, TransInfo *t) glEnable(GL_BLEND); + unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + for (p = t->tsnap.points.first; p; p = p->next) { if (p == t->tsnap.selectedPoint) { - glColor4ubv(selectedCol); + immUniformColor4ubv(selectedCol); } else { - glColor4ubv(col); + immUniformColor4ubv(col); } - ED_node_draw_snap(&ar->v2d, p->co, size, 0); + ED_node_draw_snap(&ar->v2d, p->co, size, 0, pos); } if (t->tsnap.status & POINT_INIT) { - glColor4ubv(activeCol); + immUniformColor4ubv(activeCol); - ED_node_draw_snap(&ar->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder); + ED_node_draw_snap(&ar->v2d, t->tsnap.snapPoint, size, t->tsnap.snapNodeBorder, pos); } + immUnbindProgram(); + glDisable(GL_BLEND); } } @@ -284,93 +270,88 @@ eRedrawFlag handleSnapping(TransInfo *t, const wmEvent *event) void applyProject(TransInfo *t) { - Main *bmain = CTX_data_main(t->context); - /* XXX FLICKER IN OBJECT MODE */ if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) { - TransData *td = t->data; float tvec[3]; - float imat[4][4]; int i; - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - invert_m4_m4(imat, ob->obmat); - } + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + float iloc[3], loc[3], no[3]; + float mval_fl[2]; - for (i = 0; i < t->total; i++, td++) { - float iloc[3], loc[3], no[3]; - float mval_fl[2]; - float dist_px = TRANSFORM_DIST_MAX_PX; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_SKIP) + continue; - if (td->flag & TD_SKIP) - continue; + if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) + continue; - if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) - continue; - - copy_v3_v3(iloc, td->loc); - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, iloc); - } - else if (t->flag & T_OBJECT) { - BKE_object_eval_transform_all(bmain->eval_ctx, t->scene, td->ob); - copy_v3_v3(iloc, td->ob->obmat[3]); - } + copy_v3_v3(iloc, td->loc); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, iloc); + } + else if (t->flag & T_OBJECT) { + BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); + copy_v3_v3(iloc, td->ob->obmat[3]); + } - if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { - if (snapObjectsTransform( - t, mval_fl, &dist_px, - loc, no)) - { -// if (t->flag & (T_EDIT|T_POSE)) { -// mul_m4_v3(imat, loc); -// } + if (ED_view3d_project_float_global(t->ar, iloc, mval_fl, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + if (ED_transform_snap_object_project_view3d( + t->tsnap.object_context, + SCE_SNAP_MODE_FACE, + &(const struct SnapObjectParams){ + .snap_select = t->tsnap.modeSelect, + .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .use_occlusion_test = false, + }, + mval_fl, 0, loc, no)) + { +#if 0 + if (tc->use_local_mat) { + mul_m4_v3(tc->imat, loc); + } +#endif - sub_v3_v3v3(tvec, loc, iloc); + sub_v3_v3v3(tvec, loc, iloc); - mul_m3_v3(td->smtx, tvec); + mul_m3_v3(td->smtx, tvec); - add_v3_v3(td->loc, tvec); + add_v3_v3(td->loc, tvec); - if (t->tsnap.align && (t->flag & T_OBJECT)) { - /* handle alignment as well */ - const float *original_normal; - float mat[3][3]; + if (t->tsnap.align && (t->flag & T_OBJECT)) { + /* handle alignment as well */ + const float *original_normal; + float mat[3][3]; - /* In pose mode, we want to align normals with Y axis of bones... */ - original_normal = td->axismtx[2]; + /* In pose mode, we want to align normals with Y axis of bones... */ + original_normal = td->axismtx[2]; - rotation_between_vecs_to_mat3(mat, original_normal, no); + rotation_between_vecs_to_mat3(mat, original_normal, no); - transform_data_ext_rotate(td, mat, true); + transform_data_ext_rotate(td, mat, true); - /* TODO support constraints for rotation too? see ElementRotation */ + /* TODO support constraints for rotation too? see ElementRotation */ + } } } - } - //XXX constraintTransLim(t, td); + //XXX constraintTransLim(t, td); + } } } } void applyGridAbsolute(TransInfo *t) { - Main *bmain = CTX_data_main(t->context); - float grid_size = 0.0f; GearsType grid_action; - TransData *td; - float (*obmat)[4] = NULL; - bool use_obmat = false; int i; - if (!(activeSnap(t) && (ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID)))) + if (!(activeSnap(t) && (t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)))) return; grid_action = BIG_GEARS; @@ -386,57 +367,58 @@ void applyGridAbsolute(TransInfo *t) if (grid_size == 0.0f) return; - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - obmat = ob->obmat; - use_obmat = true; - } + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td; - for (i = 0, td = t->data; i < t->total; i++, td++) { - float iloc[3], loc[3], tvec[3]; + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { + float iloc[3], loc[3], tvec[3]; - if (td->flag & TD_NOACTION) - break; + if (td->flag & TD_NOACTION) + break; - if (td->flag & TD_SKIP) - continue; + if (td->flag & TD_SKIP) + continue; - if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) - continue; + if ((t->flag & T_PROP_EDIT) && (td->factor == 0.0f)) + continue; - copy_v3_v3(iloc, td->loc); - if (use_obmat) { - mul_m4_v3(obmat, iloc); - } - else if (t->flag & T_OBJECT) { - BKE_object_eval_transform_all(bmain->eval_ctx, t->scene, td->ob); - copy_v3_v3(iloc, td->ob->obmat[3]); - } + copy_v3_v3(iloc, td->loc); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, iloc); + } + else if (t->flag & T_OBJECT) { + BKE_object_eval_transform_all(t->depsgraph, t->scene, td->ob); + copy_v3_v3(iloc, td->ob->obmat[3]); + } - mul_v3_v3fl(loc, iloc, 1.0f / grid_size); - loc[0] = roundf(loc[0]); - loc[1] = roundf(loc[1]); - loc[2] = roundf(loc[2]); - mul_v3_fl(loc, grid_size); + mul_v3_v3fl(loc, iloc, 1.0f / grid_size); + loc[0] = roundf(loc[0]); + loc[1] = roundf(loc[1]); + loc[2] = roundf(loc[2]); + mul_v3_fl(loc, grid_size); - sub_v3_v3v3(tvec, loc, iloc); - mul_m3_v3(td->smtx, tvec); - add_v3_v3(td->loc, tvec); + sub_v3_v3v3(tvec, loc, iloc); + mul_m3_v3(td->smtx, tvec); + add_v3_v3(td->loc, tvec); + } } } void applySnapping(TransInfo *t, float *vec) { - /* project is not applied this way */ - if (t->tsnap.project) + if (t->tsnap.project && t->tsnap.mode == SCE_SNAP_MODE_FACE) { + /* Each Trans Data already makes the snap to face */ return; + } if (t->tsnap.status & SNAP_FORCED) { t->tsnap.targetSnap(t); t->tsnap.applySnap(t, vec); } - else if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID) && activeSnap(t)) { + else if (((t->tsnap.mode & ~(SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) != 0) && + activeSnap(t)) + { double current = PIL_check_seconds_timer(); // Time base quirky code to go around findnearest slowness @@ -520,9 +502,10 @@ static void initSnappingMode(TransInfo *t) { Main *bmain = CTX_data_main(t->context); ToolSettings *ts = t->settings; - Object *obedit = t->obedit; - Scene *scene = t->scene; - Base *base_act = scene->basact; + /* All obedit types will match. */ + const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1; + ViewLayer *view_layer = t->view_layer; + Base *base_act = view_layer->basact; if (t->spacetype == SPACE_NODE) { /* force project off when not supported */ @@ -538,7 +521,7 @@ static void initSnappingMode(TransInfo *t) } else { /* force project off when not supported */ - if (ts->snap_mode != SCE_SNAP_MODE_FACE) + if ((ts->snap_mode & SCE_SNAP_MODE_FACE) == 0) t->tsnap.project = 0; t->tsnap.mode = ts->snap_mode; @@ -551,10 +534,10 @@ static void initSnappingMode(TransInfo *t) /* Edit mode */ if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs + ((obedit_type != -1) && ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs { /* Exclude editmesh if using proportional edit */ - if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) { + if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) { t->tsnap.modeSelect = SNAP_NOT_ACTIVE; } else { @@ -563,17 +546,19 @@ static void initSnappingMode(TransInfo *t) } /* Particles edit mode*/ else if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT)) + ((obedit_type == -1) && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { t->tsnap.modeSelect = SNAP_ALL; } /* Object mode */ else if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit == NULL) ) // Object Mode + (obedit_type == -1) ) // Object Mode { /* In "Edit Strokes" mode, Snap tool can perform snap to selected or active objects (see T49632) * TODO: perform self snap in gpencil_strokes */ - t->tsnap.modeSelect = ((t->options & CTX_GPENCIL_STROKES) != 0) ? SNAP_ALL : SNAP_NOT_SELECTED; + t->tsnap.modeSelect = ( + ((t->options & (CTX_GPENCIL_STROKES | CTX_CURSOR)) != 0) ? + SNAP_ALL : SNAP_NOT_SELECTED); } else { /* Grid if snap is not possible */ @@ -603,8 +588,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( - bmain, t->scene, 0, - t->ar, t->view); + bmain, t->scene, t->depsgraph, 0, t->ar, t->view); ED_transform_snap_object_context_set_editmesh_callbacks( t->tsnap.object_context, @@ -883,7 +867,8 @@ static float TranslationBetween(TransInfo *UNUSED(t), const float p1[3], const f return len_squared_v3v3(p1, p2); } -static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) +static float RotationBetween( + TransInfo *t, const float p1[3], const float p2[3]) { float angle, start[3], end[3]; @@ -894,7 +879,7 @@ static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { float axis[3], tmp[3]; - t->con.applyRot(t, NULL, axis, NULL); + t->con.applyRot(t, NULL, NULL, axis, NULL); project_v3_v3v3(tmp, end, axis); sub_v3_v3v3(end, end, tmp); @@ -974,18 +959,22 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) mval[0] = t->mval[0]; mval[1] = t->mval[1]; - if (t->tsnap.mode == SCE_SNAP_MODE_VOLUME) { - found = peelObjectsTransform( - t, mval, - (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, - loc, no, NULL); - } - else { + if (t->tsnap.mode & + (SCE_SNAP_MODE_VERTEX | + SCE_SNAP_MODE_EDGE | + SCE_SNAP_MODE_FACE)) + { zero_v3(no); /* objects won't set this */ found = snapObjectsTransform( t, mval, &dist_px, loc, no); } + if ((found == false) && (t->tsnap.mode & SCE_SNAP_MODE_VOLUME)) { + found = peelObjectsTransform( + t, mval, + (t->settings->snap_flag & SCE_SNAP_PEEL_OBJECT) != 0, + loc, no, NULL); + } if (found == true) { copy_v3_v3(t->tsnap.snapPoint, loc); @@ -997,36 +986,39 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) t->tsnap.status &= ~POINT_INIT; } } - else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type == OB_MESH) { - /* same as above but for UV's */ - Image *ima = ED_space_image(t->sa->spacedata.first); - float co[2]; + else if (t->spacetype == SPACE_IMAGE && t->obedit_type == OB_MESH) { + if (t->tsnap.mode & SCE_SNAP_MODE_VERTEX) { + Image *ima = ED_space_image(t->sa->spacedata.first); + float co[2]; - UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); + UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); - if (ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint)) { - t->tsnap.snapPoint[0] *= t->aspect[0]; - t->tsnap.snapPoint[1] *= t->aspect[1]; + if (ED_uvedit_nearest_uv(t->scene, TRANS_DATA_CONTAINER_FIRST_EVIL(t)->obedit, ima, co, t->tsnap.snapPoint)) { + t->tsnap.snapPoint[0] *= t->aspect[0]; + t->tsnap.snapPoint[1] *= t->aspect[1]; - t->tsnap.status |= POINT_INIT; - } - else { - t->tsnap.status &= ~POINT_INIT; + t->tsnap.status |= POINT_INIT; + } + else { + t->tsnap.status &= ~POINT_INIT; + } } } else if (t->spacetype == SPACE_NODE) { - float loc[2]; - float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here - char node_border; + if (t->tsnap.mode & (SCE_SNAP_MODE_NODE_X | SCE_SNAP_MODE_NODE_Y)) { + float loc[2]; + float dist_px = SNAP_MIN_DISTANCE; // Use a user defined value here + char node_border; - if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) { - copy_v2_v2(t->tsnap.snapPoint, loc); - t->tsnap.snapNodeBorder = node_border; + if (snapNodesTransform(t, t->mval, loc, &dist_px, &node_border)) { + copy_v2_v2(t->tsnap.snapPoint, loc); + t->tsnap.snapNodeBorder = node_border; - t->tsnap.status |= POINT_INIT; - } - else { - t->tsnap.status &= ~POINT_INIT; + t->tsnap.status |= POINT_INIT; + } + else { + t->tsnap.status &= ~POINT_INIT; + } } } } @@ -1079,11 +1071,6 @@ static void TargetSnapActive(TransInfo *t) /* Only need to calculate once */ if ((t->tsnap.status & TARGET_INIT) == 0) { if (calculateCenterActive(t, true, t->tsnap.snapTarget)) { - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->tsnap.snapTarget); - } - TargetSnapOffset(t, NULL); t->tsnap.status |= TARGET_INIT; @@ -1101,23 +1088,30 @@ static void TargetSnapMedian(TransInfo *t) { // Only need to calculate once if ((t->tsnap.status & TARGET_INIT) == 0) { - TransData *td = NULL; - int i; + int i_accum = 0; t->tsnap.snapTarget[0] = 0; t->tsnap.snapTarget[1] = 0; t->tsnap.snapTarget[2] = 0; - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { - add_v3_v3(t->tsnap.snapTarget, td->center); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + int i; + for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { + /* TODO(campbell): perform the global transformation once per TransDataContainer */ + if (tc->use_local_mat) { + float v[3]; + mul_v3_m4v3(v, tc->mat, td->center); + add_v3_v3(t->tsnap.snapTarget, v); + } + else { + add_v3_v3(t->tsnap.snapTarget, td->center); + } + } + i_accum += i; } - mul_v3_fl(t->tsnap.snapTarget, 1.0 / i); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->tsnap.snapTarget); - } + mul_v3_fl(t->tsnap.snapTarget, 1.0 / i_accum); TargetSnapOffset(t, NULL); @@ -1130,24 +1124,44 @@ static void TargetSnapClosest(TransInfo *t) // Only valid if a snap point has been selected if (t->tsnap.status & POINT_INIT) { float dist_closest = 0.0f; - TransData *closest = NULL, *td = NULL; + TransData *closest = NULL; /* Object mode */ if (t->flag & T_OBJECT) { int i; - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { - struct BoundBox *bb = BKE_object_boundbox_get(td->ob); - - /* use boundbox if possible */ - if (bb) { - int j; - - for (j = 0; j < 8; j++) { + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; + for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { + struct BoundBox *bb = BKE_object_boundbox_get(td->ob); + + /* use boundbox if possible */ + if (bb) { + int j; + + for (j = 0; j < 8; j++) { + float loc[3]; + float dist; + + copy_v3_v3(loc, bb->vec[j]); + mul_m4_v3(td->ext->obmat, loc); + + dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); + + if ((dist != TRANSFORM_DIST_INVALID) && + (closest == NULL || fabsf(dist) < fabsf(dist_closest))) + { + copy_v3_v3(t->tsnap.snapTarget, loc); + closest = td; + dist_closest = dist; + } + } + } + /* use element center otherwise */ + else { float loc[3]; float dist; - copy_v3_v3(loc, bb->vec[j]); - mul_m4_v3(td->ext->obmat, loc); + copy_v3_v3(loc, td->center); dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); @@ -1156,17 +1170,25 @@ static void TargetSnapClosest(TransInfo *t) { copy_v3_v3(t->tsnap.snapTarget, loc); closest = td; - dist_closest = dist; } } } - /* use element center otherwise */ - else { + } + } + else { + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; + int i; + for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { float loc[3]; float dist; copy_v3_v3(loc, td->center); + if (tc->use_local_mat) { + mul_m4_v3(tc->mat, loc); + } + dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); if ((dist != TRANSFORM_DIST_INVALID) && @@ -1174,34 +1196,11 @@ static void TargetSnapClosest(TransInfo *t) { copy_v3_v3(t->tsnap.snapTarget, loc); closest = td; + dist_closest = dist; } } } } - else { - int i; - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { - float loc[3]; - float dist; - - copy_v3_v3(loc, td->center); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, loc); - } - - dist = t->tsnap.distance(t, loc, t->tsnap.snapPoint); - - if ((dist != TRANSFORM_DIST_INVALID) && - (closest == NULL || fabsf(dist) < fabsf(dist_closest))) - { - copy_v3_v3(t->tsnap.snapTarget, loc); - closest = td; - dist_closest = dist; - } - } - } TargetSnapOffset(t, closest); @@ -1214,16 +1213,15 @@ bool snapObjectsTransform( float *dist_px, float r_loc[3], float r_no[3]) { - return ED_transform_snap_object_project_view3d_ex( + return ED_transform_snap_object_project_view3d( t->tsnap.object_context, t->scene->toolsettings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .use_object_edit_cage = (t->flag & T_EDIT) != 0, + .use_occlusion_test = t->scene->toolsettings->snap_mode != SCE_SNAP_MODE_FACE, }, - mval, dist_px, NULL, - r_loc, r_no, NULL, - NULL, NULL); + mval, dist_px, r_loc, r_no); } @@ -1329,15 +1327,14 @@ static bool snapNodeTest(View2D *v2d, bNode *node, eSnapSelect snap_select) static NodeBorder snapNodeBorder(int snap_node_mode) { - switch (snap_node_mode) { - case SCE_SNAP_MODE_NODE_X: - return NODE_LEFT | NODE_RIGHT; - case SCE_SNAP_MODE_NODE_Y: - return NODE_TOP | NODE_BOTTOM; - case SCE_SNAP_MODE_NODE_XY: - return NODE_LEFT | NODE_RIGHT | NODE_TOP | NODE_BOTTOM; + NodeBorder flag = 0; + if (snap_node_mode & SCE_SNAP_MODE_NODE_X) { + flag |= NODE_LEFT | NODE_RIGHT; } - return 0; + if (snap_node_mode & SCE_SNAP_MODE_NODE_Y) { + flag |= NODE_TOP | NODE_BOTTOM; + } + return flag; } static bool snapNode( @@ -1445,9 +1442,13 @@ void snapGridIncrement(TransInfo *t, float *val) { GearsType action; - /* only do something if using absolute or incremental grid snapping */ - if (!ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID)) + /* only do something if using absolute or incremental grid snapping + * and there is no valid snap point */ + if (!(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)) || + validSnap(t)) + { return; + } action = activeSnap(t) ? BIG_GEARS : NO_GEARS; @@ -1487,7 +1488,7 @@ static void applyGridIncrement(TransInfo *t, float *val, int max_index, const fl const float *asp = use_aspect ? t->aspect : asp_local; int i; - BLI_assert(ELEM(t->tsnap.mode, SCE_SNAP_MODE_INCREMENT, SCE_SNAP_MODE_GRID)); + BLI_assert(t->tsnap.mode & (SCE_SNAP_MODE_INCREMENT | SCE_SNAP_MODE_GRID)); BLI_assert(max_index <= 2); /* Early bailing out if no need to snap */ diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 1293d26bc65..e19320fa220 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -41,16 +41,25 @@ #include "DNA_curve_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" +#include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" -#include "BKE_DerivedMesh.h" +#include "BKE_bvhutils.h" +#include "BKE_armature.h" +#include "BKE_curve.h" #include "BKE_object.h" #include "BKE_anim.h" /* for duplis */ #include "BKE_editmesh.h" #include "BKE_main.h" #include "BKE_tracking.h" +#include "BKE_context.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "ED_transform.h" #include "ED_transform_snap_object_context.h" @@ -63,6 +72,8 @@ /** Internal Data Types * \{ */ +#define MAX_CLIPPLANE_LEN 3 + enum eViewProj { VIEW_PROJ_NONE = -1, VIEW_PROJ_ORTHO = 0, @@ -70,15 +81,13 @@ enum eViewProj { }; typedef struct SnapData { - short snap_to; + short snap_to_flag; float mval[2]; - float ray_origin[3]; - float ray_start[3]; - float ray_dir[3]; float pmat[4][4]; /* perspective matrix */ - float win_half[2];/* win x and y */ + float win_size[2];/* win x and y */ enum eViewProj view_proj; - float depth_range[2]; + float clip_plane[MAX_CLIPPLANE_LEN][4]; + short clip_plane_len; } SnapData; typedef struct SnapObjectData { @@ -90,9 +99,11 @@ typedef struct SnapObjectData { typedef struct SnapObjectData_Mesh { SnapObjectData sd; - BVHTreeFromMesh *bvh_trees[3]; - MPoly *mpoly; - bool poly_allocated; + BVHTreeFromMesh treedata; + BVHTree *bvhtree[2]; /* from loose verts and from loose edges */ + uint has_looptris : 1; + uint has_loose_edge : 1; + uint has_loose_vert : 1; } SnapObjectData_Mesh; @@ -105,6 +116,8 @@ typedef struct SnapObjectData_EditMesh { struct SnapObjectContext { Main *bmain; Scene *scene; + Depsgraph *depsgraph; + int flag; /* Optional: when performing screen-space projection. @@ -152,23 +165,25 @@ typedef void(*IterSnapObjsCallback)(SnapObjectContext *sctx, bool is_obedit, Obj */ static void iter_snap_objects( SnapObjectContext *sctx, - const eSnapSelect snap_select, - Object *obedit, + const struct SnapObjectParams *params, IterSnapObjsCallback sob_callback, void *data) { - Base *base_act = sctx->scene->basact; - for (Base *base = sctx->scene->base.first; base != NULL; base = base->next) { - if ((BASE_VISIBLE_BGMODE(sctx->v3d_data.v3d, sctx->scene, base)) && - (base->flag & BA_SNAP_FIX_DEPS_FIASCO) == 0 && - !((snap_select == SNAP_NOT_SELECTED && (base->flag & (SELECT | BA_WAS_SEL))) || + ViewLayer *view_layer = DEG_get_evaluated_view_layer(sctx->depsgraph); + Object *obedit = params->use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL; + const eSnapSelect snap_select = params->snap_select; + + Base *base_act = view_layer->basact; + for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { + if ((BASE_VISIBLE(base)) && (base->flag_legacy & BA_SNAP_FIX_DEPS_FIASCO) == 0 && + !((snap_select == SNAP_NOT_SELECTED && ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL))) || (snap_select == SNAP_NOT_ACTIVE && base == base_act))) { bool use_obedit; Object *obj = base->object; if (obj->transflag & OB_DUPLI) { DupliObject *dupli_ob; - ListBase *lb = object_duplilist(sctx->bmain->eval_ctx, sctx->scene, obj); + ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj); for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { use_obedit = obedit && dupli_ob->ob->data == obedit->data; sob_callback(sctx, use_obedit, use_obedit ? obedit : dupli_ob->ob, dupli_ob->mat, data); @@ -183,47 +198,6 @@ static void iter_snap_objects( } -/** - * Generates a struct with the immutable parameters that will be used on all objects. - * - * \param snap_to: Element to snap, Vertice, Edge or Face. - * \param view_proj: ORTHO or PERSP. - * Currently only works one at a time, but can eventually operate as flag. - * - * \param mval: Mouse coords. - * (When NULL, ray-casting is handled without any projection matrix correction.) - * \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min. - * \param ray_start: ray_origin moved for the start clipping plane (clip_min). - * \param ray_direction: Unit length direction of the ray. - * \param depth_range: distances of clipe plane min and clip plane max; - */ -static void snap_data_set( - SnapData *snapdata, - const ARegion *ar, const unsigned short snap_to, const enum eViewProj view_proj, - const float mval[2], const float ray_origin[3], const float ray_start[3], - const float ray_direction[3], const float depth_range[2]) -{ - copy_m4_m4(snapdata->pmat, ((RegionView3D *)ar->regiondata)->persmat); - snapdata->win_half[0] = ar->winx / 2; - snapdata->win_half[1] = ar->winy / 2; - copy_v2_v2(snapdata->mval, mval); - snapdata->snap_to = snap_to; - copy_v3_v3(snapdata->ray_origin, ray_origin); - copy_v3_v3(snapdata->ray_start, ray_start); - copy_v3_v3(snapdata->ray_dir, ray_direction); - snapdata->view_proj = view_proj; - copy_v2_v2(snapdata->depth_range, depth_range); -} - - -MINLINE float depth_get(const float co[3], const float ray_start[3], const float ray_dir[3]) -{ - float dvec[3]; - sub_v3_v3v3(dvec, co, ray_start); - return dot_v3v3(dvec, ray_dir); -} - - static bool walk_parent_bvhroot_cb(const BVHTreeAxisRange *bounds, void *userdata) { BVHTreeRay *ray = userdata; @@ -253,9 +227,6 @@ static bool isect_ray_bvhroot_v3(struct BVHTree *tree, const float ray_start[3], } } - -static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt); - /** \} */ /* -------------------------------------------------------------------- */ @@ -338,14 +309,6 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH mul_m3_v3((float(*)[3])data->timat, normal); normalize_v3(normal); - /* currently unused, and causes issues when looptri's haven't been calculated. - * since theres some overhead in ensuring this data is valid, it may need to be optional. */ -#if 0 - if (data->dm) { - hit->index = dm_looptri_to_poly_index(data->dm, &data->dm_looptri[hit->index]); - } -#endif - struct SnapObjectHitDepth *hit_item = hit_depth_create( depth, location, normal, hit->index, data->ob, data->obmat, data->ob_uuid); @@ -354,10 +317,10 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH } -static bool raycastDerivedMesh( +static bool raycastMesh( SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], - Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index, + Object *ob, Mesh *me, float obmat[4][4], const unsigned int ob_index, /* read/write args */ float *ray_depth, /* return args */ @@ -366,7 +329,7 @@ static bool raycastDerivedMesh( { bool retval = false; - if (dm->getNumPolys(dm) == 0) { + if (me->totpoly == 0) { return retval; } @@ -392,7 +355,7 @@ static bool raycastDerivedMesh( } /* Test BoundBox */ - BoundBox *bb = BKE_object_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob); if (bb) { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( @@ -403,7 +366,6 @@ static bool raycastDerivedMesh( } SnapObjectData_Mesh *sod = NULL; - BVHTreeFromMesh *treedata; void **sod_p; if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { @@ -414,46 +376,35 @@ static bool raycastDerivedMesh( sod->sd.type = SNAP_MESH; } - if (sod->bvh_trees[2] == NULL) { - sod->bvh_trees[2] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); - } - - treedata = sod->bvh_trees[2]; + BVHTreeFromMesh *treedata = &sod->treedata; - if (treedata) { - /* the tree is owned by the DM and may have been freed since we last used! */ - if (treedata->tree) { - if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); + /* The tree is owned by the DM and may have been freed since we last used. */ + if (treedata->tree) { + BLI_assert(treedata->cached); + if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { + free_bvhtree_from_mesh(treedata); + } + else { + /* Update Pointers. */ + if (treedata->vert && treedata->vert_allocated == false) { + treedata->vert = me->mvert; } - else { - if (treedata->vert == NULL) { - treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated); - } - if (treedata->loop == NULL) { - treedata->loop = DM_get_loop_array(dm, &treedata->loop_allocated); - } - if (treedata->looptri == NULL) { - if (sod->mpoly == NULL) { - sod->mpoly = DM_get_poly_array(dm, &sod->poly_allocated); - } - treedata->looptri = dm->getLoopTriArray(dm); - treedata->looptri_allocated = false; - } + if (treedata->loop && treedata->loop_allocated == false) { + treedata->loop = me->mloop; + } + if (treedata->looptri && treedata->looptri_allocated == false) { + treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); } } + } - if (treedata->tree == NULL) { - bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_LOOPTRI, 4); + if (treedata->tree == NULL) { + BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); - if (treedata->tree == NULL) { - return retval; - } + if (treedata->tree == NULL) { + return retval; } } - else { - return retval; - } /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already * been *inside* boundbox, leading to snap failures (see T38409). @@ -525,7 +476,7 @@ static bool raycastDerivedMesh( retval = true; if (r_index) { - *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]); + *r_index = treedata->looptri[hit.index].poly; } } } @@ -566,29 +517,28 @@ static bool raycastEditMesh( } treedata = sod->bvh_trees[2]; - if (treedata) { - if (treedata->tree == NULL) { - BLI_bitmap *elem_mask = NULL; - int looptri_num_active = -1; - - if (sctx->callbacks.edit_mesh.test_face_fn) { - elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); - looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( - em->bm, elem_mask, - sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); - } - bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL); + if (treedata->tree == NULL) { + BLI_bitmap *elem_mask = NULL; + int looptri_num_active = -1; - if (elem_mask) { - MEM_freeN(elem_mask); - } + if (sctx->callbacks.edit_mesh.test_face_fn) { + elem_mask = BLI_BITMAP_NEW(em->tottri, __func__); + looptri_num_active = BM_iter_mesh_bitmap_from_filter_tessface( + em->bm, elem_mask, + sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL); + + if (elem_mask) { + MEM_freeN(elem_mask); } if (treedata->tree == NULL) { return retval; } } else { - return retval; + /* COW hack: Update pointers */ + treedata->em = em; } float imat[4][4]; @@ -679,7 +629,7 @@ static bool raycastEditMesh( retval = true; if (r_index) { - *r_index = hit.index; + *r_index = BM_elem_index_get(em->looptris[hit.index][0]->f); } } } @@ -698,7 +648,7 @@ static bool raycastObj( SnapObjectContext *sctx, const float ray_start[3], const float ray_dir[3], Object *ob, float obmat[4][4], const unsigned int ob_index, - bool use_obedit, + bool use_obedit, bool use_occlusion_test, /* read/write args */ float *ray_depth, /* return args */ @@ -708,44 +658,46 @@ static bool raycastObj( { bool retval = false; - if (ob->type == OB_MESH) { - BMEditMesh *em; - - if (use_obedit) { - em = BKE_editmesh_from_object(ob); - retval = raycastEditMesh( - sctx, - ray_start, ray_dir, - ob, em, obmat, ob_index, - ray_depth, r_loc, r_no, r_index, r_hit_list); + if (use_occlusion_test) { + if (use_obedit && sctx->use_v3d && + !(sctx->v3d_data.v3d->flag & V3D_ZBUF_SELECT)) + { + /* Use of occlude geometry in editing mode disabled. */ + return false; } - else { - /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. - * still set the 'em' to NULL, since we only want the 'dm'. */ - DerivedMesh *dm; - em = BKE_editmesh_from_object(ob); - if (em) { - editbmesh_get_derived_cage_and_final(sctx->scene, ob, em, CD_MASK_BAREMESH, &dm); + } + + switch (ob->type) { + case OB_MESH: + if (use_obedit) { + BMEditMesh *em = BKE_editmesh_from_object(ob); + retval = raycastEditMesh( + sctx, + ray_start, ray_dir, + ob, em, obmat, ob_index, + ray_depth, r_loc, r_no, r_index, r_hit_list); } else { - dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); + retval = raycastMesh( + sctx, + ray_start, ray_dir, + ob, ob->data, obmat, ob_index, + ray_depth, r_loc, r_no, r_index, r_hit_list); } - retval = raycastDerivedMesh( - sctx, - ray_start, ray_dir, - ob, dm, obmat, ob_index, - ray_depth, r_loc, r_no, r_index, r_hit_list); - } + break; } if (retval) { if (r_ob) { *r_ob = ob; + } + if (r_obmat) { copy_m4_m4(r_obmat, obmat); } + return true; } - return retval; + return false; } @@ -762,16 +714,19 @@ struct RaycastObjUserData { Object **r_ob; float (*r_obmat)[4]; ListBase *r_hit_list; + bool use_occlusion_test; bool ret; }; static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data) { struct RaycastObjUserData *dt = data; + dt->ret |= raycastObj( sctx, dt->ray_start, dt->ray_dir, - ob, obmat, dt->ob_index++, is_obedit, + ob, obmat, dt->ob_index++, + is_obedit, dt->use_occlusion_test, dt->ray_depth, dt->r_loc, dt->r_no, dt->r_index, dt->r_ob, dt->r_obmat, @@ -808,8 +763,8 @@ static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, */ static bool raycastObjects( SnapObjectContext *sctx, + const struct SnapObjectParams *params, const float ray_start[3], const float ray_dir[3], - const eSnapSelect snap_select, const bool use_object_edit_cage, /* read/write args */ float *ray_depth, /* return args */ @@ -817,8 +772,6 @@ static bool raycastObjects( Object **r_ob, float r_obmat[4][4], ListBase *r_hit_list) { - Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; - struct RaycastObjUserData data = { .ray_start = ray_start, .ray_dir = ray_dir, @@ -830,10 +783,11 @@ static bool raycastObjects( .r_ob = r_ob, .r_obmat = r_obmat, .r_hit_list = r_hit_list, + .use_occlusion_test = params->use_occlusion_test, .ret = false, }; - iter_snap_objects(sctx, snap_select, obedit, raycast_obj_cb, &data); + iter_snap_objects(sctx, params, raycast_obj_cb, &data); return data.ret; } @@ -845,65 +799,106 @@ static bool raycastObjects( /** Snap Nearest utilities * \{ */ -static void copy_dm_vert_no(const int index, float r_no[3], const BVHTreeFromMesh *data) +static void cb_mvert_co_get( + const int index, const float **co, const BVHTreeFromMesh *data) +{ + *co = data->vert[index].co; +} + +static void cb_bvert_co_get( + const int index, const float **co, const BMEditMesh *data) +{ + BMVert *eve = BM_vert_at_index(data->bm, index); + *co = eve->co; +} + +static void cb_mvert_no_copy( + const int index, float r_no[3], const BVHTreeFromMesh *data) { const MVert *vert = data->vert + index; normal_short_to_float_v3(r_no, vert->no); } -static void copy_bvert_no(const int index, float r_no[3], const BVHTreeFromEditMesh *data) +static void cb_bvert_no_copy( + const int index, float r_no[3], const BMEditMesh *data) { - BMVert *eve = BM_vert_at_index(data->em->bm, index); + BMVert *eve = BM_vert_at_index(data->bm, index); copy_v3_v3(r_no, eve->no); } -static void get_dm_edge_verts(const int index, const float *v_pair[2], const BVHTreeFromMesh *data) +static void cb_medge_verts_get( + const int index, int v_index[2], const BVHTreeFromMesh *data) +{ + const MEdge *edge = &data->edge[index]; + + v_index[0] = edge->v1; + v_index[1] = edge->v2; + +} + +static void cb_bedge_verts_get( + const int index, int v_index[2], const BMEditMesh *data) { - const MVert *vert = data->vert; - const MEdge *edge = data->edge + index; + BMEdge *eed = BM_edge_at_index(data->bm, index); + + v_index[0] = BM_elem_index_get(eed->v1); + v_index[1] = BM_elem_index_get(eed->v2); +} - v_pair[0] = vert[edge->v1].co; - v_pair[1] = vert[edge->v2].co; +static void cb_mlooptri_edges_get( + const int index, int v_index[3], const BVHTreeFromMesh *data) +{ + const MEdge *medge = data->edge; + const MLoop *mloop = data->loop; + const MLoopTri *lt = &data->looptri[index]; + for (int j = 2, j_next = 0; j_next < 3; j = j_next++) { + const MEdge *ed = &medge[mloop[lt->tri[j]].e]; + unsigned int tri_edge[2] = {mloop[lt->tri[j]].v, mloop[lt->tri[j_next]].v}; + if (ELEM(ed->v1, tri_edge[0], tri_edge[1]) && + ELEM(ed->v2, tri_edge[0], tri_edge[1])) + { + //printf("real edge found\n"); + v_index[j] = mloop[lt->tri[j]].e; + } + else + v_index[j] = -1; + } } -static void get_bedge_verts(const int index, const float *v_pair[2], const BVHTreeFromEditMesh *data) +static void cb_mlooptri_verts_get( + const int index, int v_index[3], const BVHTreeFromMesh *data) { - BMEdge *eed = BM_edge_at_index(data->em->bm, index); + const MLoop *loop = data->loop; + const MLoopTri *looptri = &data->looptri[index]; - v_pair[0] = eed->v1->co; - v_pair[1] = eed->v2->co; + v_index[0] = loop[looptri->tri[0]].v; + v_index[1] = loop[looptri->tri[1]].v; + v_index[2] = loop[looptri->tri[2]].v; } static bool test_projected_vert_dist( - const float depth_range[2], const float mval[2], const float co[3], - float pmat[4][4], const float win_half[2], const bool is_persp, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + const bool is_persp, const float co[3], float *dist_px_sq, float r_co[3]) { - float depth; - if (is_persp) { - depth = mul_project_m4_v3_zfac(pmat, co); - if (depth < depth_range[0] || depth > depth_range[1]) { - return false; - } + if (!isect_point_planes_v3_negated(clip_plane, clip_plane_len, co)) { + return false; } float co2d[2] = { - (dot_m4_v3_row_x(pmat, co) + pmat[3][0]), - (dot_m4_v3_row_y(pmat, co) + pmat[3][1]), + (dot_m4_v3_row_x(precalc->pmat, co) + precalc->pmat[3][0]), + (dot_m4_v3_row_y(precalc->pmat, co) + precalc->pmat[3][1]), }; if (is_persp) { - mul_v2_fl(co2d, 1 / depth); + float w = mul_project_m4_v3_zfac(precalc->pmat, co); + mul_v2_fl(co2d, 1.0f / w); } - co2d[0] += 1.0f; - co2d[1] += 1.0f; - co2d[0] *= win_half[0]; - co2d[1] *= win_half[1]; - - const float dist_sq = len_squared_v2v2(mval, co2d); + const float dist_sq = len_squared_v2v2(precalc->mval, co2d); if (dist_sq < *dist_px_sq) { copy_v3_v3(r_co, co); *dist_px_sq = dist_sq; @@ -913,395 +908,487 @@ static bool test_projected_vert_dist( } static bool test_projected_edge_dist( - const float depth_range[2], const float mval[2], - float pmat[4][4], const float win_half[2], const bool is_persp, - const float ray_start[3], const float ray_dir[3], - const float va[3], const float vb[3], + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + const bool is_persp, const float va[3], const float vb[3], float *dist_px_sq, float r_co[3]) { + float near_co[3], lambda; + if (!isect_ray_seg_v3( + precalc->ray_origin, + precalc->ray_direction, + va, vb, &lambda)) + { + copy_v3_v3(near_co, va); + } + else { + if (lambda <= 0.0f) { + copy_v3_v3(near_co, va); + } + else if (lambda >= 1.0f) { + copy_v3_v3(near_co, vb); + } + else { + interp_v3_v3v3(near_co, va, vb, lambda); + } + } - float tmp_co[3], depth; - dist_squared_ray_to_seg_v3(ray_start, ray_dir, va, vb, tmp_co, &depth); - return test_projected_vert_dist(depth_range, mval, tmp_co, pmat, win_half, is_persp, dist_px_sq, r_co); + return test_projected_vert_dist( + precalc, clip_plane, clip_plane_len, + is_persp, near_co, dist_px_sq, r_co); } -typedef struct Nearest2dPrecalc { - float ray_origin_local[3]; - float ray_direction_local[3]; - float ray_inv_dir[3]; - float ray_min_dist; - float pmat[4][4]; /* perspective matrix multiplied by object matrix */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** Walk DFS + * \{ */ + +typedef void (*Nearest2DGetVertCoCallback)(const int index, const float **co, void *data); +typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, int v_index[2], void *data); +typedef void (*Nearest2DGetTriVertsCallback)(const int index, int v_index[3], void *data); +typedef void (*Nearest2DGetTriEdgesCallback)(const int index, int e_index[3], void *data); /* Equal the previous one */ +typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data); + +typedef struct Nearest2dUserData { bool is_persp; - float win_half[2]; - float mval[2]; - bool sign[3]; -} Nearest2dPrecalc; + void *userdata; + Nearest2DGetVertCoCallback get_vert_co; + Nearest2DGetEdgeVertsCallback get_edge_verts_index; + Nearest2DGetTriVertsCallback get_tri_verts_index; + Nearest2DGetTriEdgesCallback get_tri_edges_index; + Nearest2DCopyVertNoCallback copy_vert_no; -/** - * \param lpmat: Perspective matrix multiplied by object matrix - */ -static void dist_squared_to_projected_aabb_precalc( - struct Nearest2dPrecalc *neasrest_precalc, - float lpmat[4][4], bool is_persp, const float win_half[2], - const float ray_min_dist, const float mval[2], - const float ray_origin_local[3], const float ray_direction_local[3]) +} Nearest2dUserData; + + +static void cb_snap_vert( + void *userdata, int index, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + BVHTreeNearest *nearest) { - copy_m4_m4(neasrest_precalc->pmat, lpmat); - neasrest_precalc->is_persp = is_persp; - copy_v2_v2(neasrest_precalc->win_half, win_half); - neasrest_precalc->ray_min_dist = ray_min_dist; + struct Nearest2dUserData *data = userdata; - copy_v3_v3(neasrest_precalc->ray_origin_local, ray_origin_local); - copy_v3_v3(neasrest_precalc->ray_direction_local, ray_direction_local); - copy_v2_v2(neasrest_precalc->mval, mval); + const float *co; + data->get_vert_co(index, &co, data->userdata); - for (int i = 0; i < 3; i++) { - neasrest_precalc->ray_inv_dir[i] = - (neasrest_precalc->ray_direction_local[i] != 0.0f) ? - (1.0f / neasrest_precalc->ray_direction_local[i]) : FLT_MAX; - neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f); + if (test_projected_vert_dist( + precalc, + clip_plane, + clip_plane_len, + data->is_persp, co, + &nearest->dist_sq, + nearest->co)) + { + data->copy_vert_no(index, nearest->no, data->userdata); + nearest->index = index; } } -/* Returns the distance from a 2d coordinate to a BoundBox (Projected) */ -static float dist_squared_to_projected_aabb( - struct Nearest2dPrecalc *data, - const float bbmin[3], const float bbmax[3], - bool r_axis_closest[3]) +static void cb_snap_edge( + void *userdata, int index, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + BVHTreeNearest *nearest) { - float local_bvmin[3], local_bvmax[3]; - if (data->sign[0]) { - local_bvmin[0] = bbmax[0]; - local_bvmax[0] = bbmin[0]; - } - else { - local_bvmin[0] = bbmin[0]; - local_bvmax[0] = bbmax[0]; - } - if (data->sign[1]) { - local_bvmin[1] = bbmax[1]; - local_bvmax[1] = bbmin[1]; - } - else { - local_bvmin[1] = bbmin[1]; - local_bvmax[1] = bbmax[1]; - } - if (data->sign[2]) { - local_bvmin[2] = bbmax[2]; - local_bvmax[2] = bbmin[2]; - } - else { - local_bvmin[2] = bbmin[2]; - local_bvmax[2] = bbmax[2]; - } + struct Nearest2dUserData *data = userdata; - const float tmin[3] = { - (local_bvmin[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0], - (local_bvmin[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1], - (local_bvmin[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2], - }; - const float tmax[3] = { - (local_bvmax[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0], - (local_bvmax[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1], - (local_bvmax[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2], - }; - /* `va` and `vb` are the coordinates of the AABB edge closest to the ray */ - float va[3], vb[3]; - /* `rtmin` and `rtmax` are the minimum and maximum distances of the ray hits on the AABB */ - float rtmin, rtmax; - int main_axis; - - if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) { - rtmax = tmax[0]; - va[0] = vb[0] = local_bvmax[0]; - main_axis = 3; - r_axis_closest[0] = data->sign[0]; - } - else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) { - rtmax = tmax[1]; - va[1] = vb[1] = local_bvmax[1]; - main_axis = 2; - r_axis_closest[1] = data->sign[1]; - } - else { - rtmax = tmax[2]; - va[2] = vb[2] = local_bvmax[2]; - main_axis = 1; - r_axis_closest[2] = data->sign[2]; - } + int vindex[2]; + data->get_edge_verts_index(index, vindex, data->userdata); - if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) { - rtmin = tmin[0]; - va[0] = vb[0] = local_bvmin[0]; - main_axis -= 3; - r_axis_closest[0] = !data->sign[0]; - } - else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) { - rtmin = tmin[1]; - va[1] = vb[1] = local_bvmin[1]; - main_axis -= 1; - r_axis_closest[1] = !data->sign[1]; - } - else { - rtmin = tmin[2]; - va[2] = vb[2] = local_bvmin[2]; - main_axis -= 2; - r_axis_closest[2] = !data->sign[2]; - } - if (main_axis < 0) { - main_axis += 3; - } + const float *v_pair[2]; + data->get_vert_co(vindex[0], &v_pair[0], data->userdata); + data->get_vert_co(vindex[1], &v_pair[1], data->userdata); -#define IGNORE_BEHIND_RAY -#ifdef IGNORE_BEHIND_RAY - float depth_max = depth_get(local_bvmax, data->ray_origin_local, data->ray_direction_local); - if (depth_max < data->ray_min_dist) { - return FLT_MAX; + if (test_projected_edge_dist( + precalc, + clip_plane, + clip_plane_len, + data->is_persp, + v_pair[0], v_pair[1], + &nearest->dist_sq, + nearest->co)) + { + sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]); + nearest->index = index; } -#endif -#undef IGNORE_BEHIND_RAY +} - /* if rtmin <= rtmax, ray intersect `AABB` */ - if (rtmin <= rtmax) { - return 0; +static void cb_snap_edge_verts( + void *userdata, int index, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + BVHTreeNearest *nearest) +{ + struct Nearest2dUserData *data = userdata; + + int vindex[2]; + data->get_edge_verts_index(index, vindex, data->userdata); + + for (int i = 2; i--;) { + if (vindex[i] == nearest->index) { + continue; + } + cb_snap_vert( + userdata, vindex[i], precalc, + clip_plane, clip_plane_len, nearest); } +} + +static void cb_snap_tri_edges( + void *userdata, int index, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + BVHTreeNearest *nearest) +{ + struct Nearest2dUserData *data = userdata; - if (data->sign[main_axis]) { - va[main_axis] = local_bvmax[main_axis]; - vb[main_axis] = local_bvmin[main_axis]; + int eindex[3]; + data->get_tri_edges_index(index, eindex, data->userdata); + for (int i = 3; i--;) { + if (eindex[i] != -1) { + if (eindex[i] == nearest->index) { + continue; + } + cb_snap_edge( + userdata, eindex[i], precalc, + clip_plane, clip_plane_len, nearest); + } } - else { - va[main_axis] = local_bvmin[main_axis]; - vb[main_axis] = local_bvmax[main_axis]; +} + +static void cb_snap_tri_verts( + void *userdata, int index, + const struct DistProjectedAABBPrecalc *precalc, + const float (*clip_plane)[4], const int clip_plane_len, + BVHTreeNearest *nearest) +{ + struct Nearest2dUserData *data = userdata; + + int vindex[3]; + data->get_tri_verts_index(index, vindex, data->userdata); + for (int i = 3; i--;) { + if (vindex[i] == nearest->index) { + continue; + } + cb_snap_vert( + userdata, vindex[i], precalc, + clip_plane, clip_plane_len, nearest); } - float scale = fabsf(local_bvmax[main_axis] - local_bvmin[main_axis]); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internal Object Snapping API + * \{ */ - float (*pmat)[4] = data->pmat; +static short snap_mesh_polygon( + SnapObjectContext *sctx, SnapData *snapdata, + Object *ob, float obmat[4][4], + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], float r_no[3], int *r_index) +{ + short elem = 0; + + float lpmat[4][4]; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); - float va2d[2] = { - (dot_m4_v3_row_x(pmat, va) + pmat[3][0]), - (dot_m4_v3_row_y(pmat, va) + pmat[3][1]), + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } + + Nearest2dUserData nearest2d = { + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, }; - float vb2d[2] = { - (va2d[0] + pmat[main_axis][0] * scale), - (va2d[1] + pmat[main_axis][1] * scale), + + BVHTreeNearest nearest = { + .index = -1, + .dist_sq = SQUARE(*dist_px), }; - if (data->is_persp) { - float depth_a = mul_project_m4_v3_zfac(pmat, va); - float depth_b = depth_a + pmat[main_axis][3] * scale; - va2d[0] /= depth_a; - va2d[1] /= depth_a; - vb2d[0] /= depth_b; - vb2d[1] /= depth_b; - } - - va2d[0] += 1.0f; - va2d[1] += 1.0f; - vb2d[0] += 1.0f; - vb2d[1] += 1.0f; - - va2d[0] *= data->win_half[0]; - va2d[1] *= data->win_half[1]; - vb2d[0] *= data->win_half[0]; - vb2d[1] *= data->win_half[1]; - - float dvec[2], edge[2], lambda, rdist; - sub_v2_v2v2(dvec, data->mval, va2d); - sub_v2_v2v2(edge, vb2d, va2d); - lambda = dot_v2v2(dvec, edge); - if (lambda != 0.0f) { - lambda /= len_squared_v2(edge); - if (lambda <= 0.0f) { - rdist = len_squared_v2v2(data->mval, va2d); - r_axis_closest[main_axis] = true; - } - else if (lambda >= 1.0f) { - rdist = len_squared_v2v2(data->mval, vb2d); - r_axis_closest[main_axis] = false; + SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + BLI_assert(sod != NULL); + + if (sod->type == SNAP_MESH) { + BVHTreeFromMesh *treedata = &((SnapObjectData_Mesh *)sod)->treedata; + + nearest2d.userdata = treedata; + nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; + nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; + nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; + + MPoly *mp = &((Mesh *)ob->data)->mpoly[*r_index]; + const MLoop *ml; + if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + elem = SCE_SNAP_MODE_EDGE; + treedata->edge = ((Mesh *)ob->data)->medge; + ml = &treedata->loop[mp->loopstart]; + for (int i = mp->totloop; i--; ml++) { + cb_snap_edge( + &nearest2d, ml->e, &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + &nearest); + } } else { - va2d[0] += edge[0] * lambda; - va2d[1] += edge[1] * lambda; - rdist = len_squared_v2v2(data->mval, va2d); - r_axis_closest[main_axis] = lambda < 0.5f; + elem = SCE_SNAP_MODE_VERTEX; + ml = &treedata->loop[mp->loopstart]; + for (int i = mp->totloop; i--; ml++) { + cb_snap_vert( + &nearest2d, ml->v, &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + &nearest); + } } } else { - rdist = len_squared_v2v2(data->mval, va2d); + BLI_assert(sod->type == SNAP_EDIT_MESH); + BMEditMesh *em = BKE_editmesh_from_object(ob); + + nearest2d.userdata = em; + nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; + nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; + nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; + + BM_mesh_elem_table_ensure(em->bm, BM_FACE); + BMFace *f = BM_face_at_index(em->bm, *r_index); + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + elem = SCE_SNAP_MODE_EDGE; + BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE); + do { + cb_snap_edge( + &nearest2d, BM_elem_index_get(l_iter->e), + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + &nearest); + } while ((l_iter = l_iter->next) != l_first); + } + else { + elem = SCE_SNAP_MODE_VERTEX; + BM_mesh_elem_table_ensure(em->bm, BM_VERT); + do { + cb_snap_vert( + &nearest2d, BM_elem_index_get(l_iter->v), + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + &nearest); + } while ((l_iter = l_iter->next) != l_first); + } } - return rdist; -} -static float dist_squared_to_projected_aabb_simple( - float lpmat[4][4], const float win_half[2], - const float ray_min_dist, const float mval[2], - const float ray_origin_local[3], const float ray_direction_local[3], - const float bbmin[3], const float bbmax[3]) -{ - struct Nearest2dPrecalc data; - dist_squared_to_projected_aabb_precalc( - &data, lpmat, true, win_half, ray_min_dist, - mval, ray_origin_local, ray_direction_local); + if (nearest.index != -1) { + *dist_px = sqrtf(nearest.dist_sq); - bool dummy[3] = {true, true, true}; - return dist_squared_to_projected_aabb(&data, bbmin, bbmax, dummy); -} + copy_v3_v3(r_loc, nearest.co); + mul_m4_v3(obmat, r_loc); -/** \} */ + if (r_no) { + float imat[4][4]; + invert_m4_m4(imat, obmat); -/* -------------------------------------------------------------------- */ -/** Walk DFS - * \{ */ + copy_v3_v3(r_no, nearest.no); + mul_transposed_mat3_m4_v3(imat, r_no); + normalize_v3(r_no); + } -typedef void (*Nearest2DGetEdgeVertsCallback)(const int index, const float *v_pair[2], void *data); -typedef void (*Nearest2DCopyVertNoCallback)(const int index, float r_no[3], void *data); + if (r_index) { + *r_index = nearest.index; + } -typedef struct Nearest2dUserData { - struct Nearest2dPrecalc data_precalc; + return elem; + } - float dist_px_sq; + return 0; +} - bool r_axis_closest[3]; - float depth_range[2]; +static short snap_mesh_edge_verts_mixed( + SnapObjectContext *sctx, SnapData *snapdata, + Object *ob, float obmat[4][4], float original_dist_px, + /* read/write args */ + float *dist_px, + /* return args */ + float r_loc[3], float r_no[3], int *r_index) +{ + short elem = SCE_SNAP_MODE_EDGE; - void *userdata; - Nearest2DGetEdgeVertsCallback get_edge_verts; - Nearest2DCopyVertNoCallback copy_vert_no; + if (ob->type != OB_MESH) { + return elem; + } - int index; - float co[3]; - float no[3]; -} Nearest2dUserData; + float lpmat[4][4]; + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); -static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data) -{ - Nearest2dUserData *data = user_data; - const float bbmin[3] = {bounds[0].min, bounds[1].min, bounds[2].min}; - const float bbmax[3] = {bounds[0].max, bounds[1].max, bounds[2].max}; - const float rdist = dist_squared_to_projected_aabb( - &data->data_precalc, bbmin, bbmax, data->r_axis_closest); - return rdist < data->dist_px_sq; -} + Nearest2dUserData nearest2d = { + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + }; -static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata) -{ - struct Nearest2dUserData *data = userdata; - struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc; - const float co[3] = { - (bounds[0].min + bounds[0].max) / 2, - (bounds[1].min + bounds[1].max) / 2, - (bounds[2].min + bounds[2].max) / 2, + BVHTreeNearest nearest = { + .index = -1, + .dist_sq = SQUARE(original_dist_px), }; - if (test_projected_vert_dist( - data->depth_range, - neasrest_precalc->mval, co, - neasrest_precalc->pmat, - neasrest_precalc->win_half, - neasrest_precalc->is_persp, - &data->dist_px_sq, - data->co)) - { - data->copy_vert_no(index, data->no, data->userdata); - data->index = index; + SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + BLI_assert(sod != NULL); + + if (sod->type == SNAP_MESH) { + nearest2d.userdata = &((SnapObjectData_Mesh *)sod)->treedata; + nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get; + nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get; + nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy; + } + else { + BLI_assert(sod->type == SNAP_EDIT_MESH); + nearest2d.userdata = BKE_editmesh_from_object(ob); + nearest2d.get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get; + nearest2d.get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get; + nearest2d.copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy; } - return true; -} -static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata) -{ - struct Nearest2dUserData *data = userdata; - struct Nearest2dPrecalc *neasrest_precalc = &data->data_precalc; + int vindex[2]; + nearest2d.get_edge_verts_index(*r_index, vindex, nearest2d.userdata); const float *v_pair[2]; - data->get_edge_verts(index, v_pair, data->userdata); - - if (test_projected_edge_dist( - data->depth_range, - neasrest_precalc->mval, - neasrest_precalc->pmat, - neasrest_precalc->win_half, - neasrest_precalc->is_persp, - neasrest_precalc->ray_origin_local, - neasrest_precalc->ray_direction_local, - v_pair[0], v_pair[1], - &data->dist_px_sq, - data->co)) + nearest2d.get_vert_co(vindex[0], &v_pair[0], nearest2d.userdata); + nearest2d.get_vert_co(vindex[1], &v_pair[1], nearest2d.userdata); + + float lambda; + if (!isect_ray_seg_v3( + neasrest_precalc.ray_origin, + neasrest_precalc.ray_direction, + v_pair[0], v_pair[1], &lambda)) { - sub_v3_v3v3(data->no, v_pair[0], v_pair[1]); - data->index = index; + /* do nothing */; } - return true; -} + else if (lambda < 0.25f || 0.75f < lambda) { + int v_id = lambda < 0.5f ? 0 : 1; -static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata) -{ - const bool *r_axis_closest = ((struct Nearest2dUserData *)userdata)->r_axis_closest; - return r_axis_closest[axis]; -} + if (test_projected_vert_dist( + &neasrest_precalc, NULL, 0, + nearest2d.is_persp, v_pair[v_id], + &nearest.dist_sq, nearest.co)) + { + nearest.index = vindex[v_id]; + nearest2d.copy_vert_no(vindex[v_id], nearest.no, nearest2d.userdata); + elem = SCE_SNAP_MODE_VERTEX; + } + } -/** \} */ + if (nearest.index != -1) { + *dist_px = sqrtf(nearest.dist_sq); -/* -------------------------------------------------------------------- */ -/** \name Internal Object Snapping API - * \{ */ + copy_v3_v3(r_loc, nearest.co); + mul_m4_v3(obmat, r_loc); + + if (r_no) { + float imat[4][4]; + invert_m4_m4(imat, obmat); -static bool snapArmature( + copy_v3_v3(r_no, nearest.no); + mul_transposed_mat3_m4_v3(imat, r_no); + normalize_v3(r_no); + } + + if (r_index) { + *r_index = nearest.index; + } + } + + return elem; +} + +static short snapArmature( SnapData *snapdata, - Object *ob, bArmature *arm, float obmat[4][4], + Object *ob, float obmat[4][4], bool use_obedit, /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float *UNUSED(r_no)) + float r_loc[3], float *UNUSED(r_no), int *r_index) { - bool retval = false; + short retval = 0; + + if (snapdata->snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ + return retval; + } - float ray_start_local[3], ray_normal_local[3]; /* Used only in the snap to edges */ - if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { - float imat[4][4]; - invert_m4_m4(imat, obmat); + float lpmat[4][4], dist_px_sq = SQUARE(*dist_px); + mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - copy_v3_v3(ray_start_local, snapdata->ray_origin); - copy_v3_v3(ray_normal_local, snapdata->ray_dir); - mul_m4_v3(imat, ray_start_local); - mul_mat3_m4_v3(imat, ray_normal_local); + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + + if (use_obedit == false) { + /* Test BoundBox */ + BoundBox *bb = BKE_armature_boundbox_get(ob); + if (bb) { + bool dummy[3]; + /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */ + float bb_dist_px_sq = dist_squared_to_projected_aabb( + &neasrest_precalc, bb->vec[0], bb->vec[6], dummy); + + if (bb_dist_px_sq > dist_px_sq) { + return retval; + } + } } - else if (snapdata->snap_to != SCE_SNAP_MODE_VERTEX) { /* Currently only edge and vert */ - return retval; + + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); } bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - float lpmat[4][4], dist_px_sq; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - dist_px_sq = SQUARE(*dist_px); + bArmature *arm = ob->data; if (arm->edbo) { for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) { if (eBone->layer & arm->layer) { /* skip hidden or moving (selected) bones */ if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_VERTEX: - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, eBone->head, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, eBone->tail, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - break; - case SCE_SNAP_MODE_EDGE: - retval |= test_projected_edge_dist( - snapdata->depth_range, snapdata->mval, lpmat, - snapdata->win_half, is_persp, ray_start_local, ray_normal_local, - eBone->head, eBone->tail, - &dist_px_sq, r_loc); - break; + bool has_vert_snap = false; + + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + has_vert_snap = test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, eBone->head, &dist_px_sq, r_loc); + has_vert_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, eBone->tail, &dist_px_sq, r_loc); + + if (has_vert_snap) { + retval = SCE_SNAP_MODE_VERTEX; + } + } + if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (test_projected_edge_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, eBone->head, eBone->tail, + &dist_px_sq, r_loc)) + { + retval = SCE_SNAP_MODE_EDGE; + } } } } @@ -1312,193 +1399,249 @@ static bool snapArmature( Bone *bone = pchan->bone; /* skip hidden bones */ if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { + bool has_vert_snap = false; const float *head_vec = pchan->pose_head; const float *tail_vec = pchan->pose_tail; - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_VERTEX: - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, head_vec, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, tail_vec, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - break; - case SCE_SNAP_MODE_EDGE: - retval |= test_projected_edge_dist( - snapdata->depth_range, snapdata->mval, lpmat, - snapdata->win_half, is_persp, ray_start_local, ray_normal_local, - head_vec, tail_vec, - &dist_px_sq, r_loc); - break; + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + has_vert_snap = test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, head_vec, &dist_px_sq, r_loc); + has_vert_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, tail_vec, &dist_px_sq, r_loc); + + if (has_vert_snap) { + retval = SCE_SNAP_MODE_VERTEX; + } + } + if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (test_projected_edge_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, head_vec, tail_vec, + &dist_px_sq, r_loc)) + { + retval = SCE_SNAP_MODE_EDGE; + } } } } } + if (retval) { *dist_px = sqrtf(dist_px_sq); mul_m4_v3(obmat, r_loc); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - return true; + if (r_index) { + /* Does not support index. */ + *r_index = -1; + } + return retval; } - return false; + + return 0; } -static bool snapCurve( +static short snapCurve( SnapData *snapdata, - Object *ob, Curve *cu, float obmat[4][4], + Object *ob, float obmat[4][4], bool use_obedit, /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float *UNUSED(r_no)) + float r_loc[3], float *UNUSED(r_no), int *r_index) { - bool retval = false; + bool has_snap = false; /* only vertex snapping mode (eg control points and handles) supported for now) */ - if (snapdata->snap_to != SCE_SNAP_MODE_VERTEX) { - return retval; + if (snapdata->snap_to_flag != SCE_SNAP_MODE_VERTEX) { + return 0; } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - float lpmat[4][4], dist_px_sq; + Curve *cu = ob->data; + float dist_px_sq = SQUARE(*dist_px); + + float lpmat[4][4]; mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - dist_px_sq = SQUARE(*dist_px); - for (Nurb *nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + + if (use_obedit == false) { + /* Test BoundBox */ + BoundBox *bb = BKE_curve_boundbox_get(ob); + if (bb) { + bool dummy[3]; + /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */ + float bb_dist_px_sq = dist_squared_to_projected_aabb( + &neasrest_precalc, bb->vec[0], bb->vec[6], dummy); + + if (bb_dist_px_sq > dist_px_sq) { + return 0; + } + } + } + + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } + + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + + for (Nurb *nu = (use_obedit ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { for (int u = 0; u < nu->pntsu; u++) { - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_VERTEX: - { - if (ob->mode == OB_MODE_EDIT) { - if (nu->bezt) { - /* don't snap to selected (moving) or hidden */ - if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { - break; - } - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1], - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ - if (!(nu->bezt[u].f1 & SELECT) && - !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) - { - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[0], - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - } - if (!(nu->bezt[u].f3 & SELECT) && - !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) - { - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[2], - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - } + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (use_obedit) { + if (nu->bezt) { + /* don't snap to selected (moving) or hidden */ + if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) { + break; } - else { - /* don't snap to selected (moving) or hidden */ - if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { - break; - } - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bp[u].vec, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bezt[u].vec[1], &dist_px_sq, + r_loc); + /* don't snap if handle is selected (moving), or if it is aligning to a moving handle */ + if (!(nu->bezt[u].f1 & SELECT) && + !(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT)) + { + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bezt[u].vec[0], &dist_px_sq, + r_loc); + } + if (!(nu->bezt[u].f3 & SELECT) && + !(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT)) + { + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bezt[u].vec[2], &dist_px_sq, r_loc); } } else { - /* curve is not visible outside editmode if nurb length less than two */ - if (nu->pntsu > 1) { - if (nu->bezt) { - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bezt[u].vec[1], - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - } - else { - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, nu->bp[u].vec, - lpmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); - } + /* don't snap to selected (moving) or hidden */ + if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) { + break; + } + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bp[u].vec, &dist_px_sq, + r_loc); + } + } + else { + /* curve is not visible outside editmode if nurb length less than two */ + if (nu->pntsu > 1) { + if (nu->bezt) { + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bezt[u].vec[1], &dist_px_sq, + r_loc); + } + else { + has_snap |= test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, nu->bp[u].vec, &dist_px_sq, + r_loc); } } - break; } - default: - break; } } } - if (retval) { + if (has_snap) { *dist_px = sqrtf(dist_px_sq); mul_m4_v3(obmat, r_loc); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - return true; + if (r_index) { + /* Does not support index yet. */ + *r_index = -1; + } + return SCE_SNAP_MODE_VERTEX; } - return false; + + return 0; } /* may extend later (for now just snaps to empty center) */ -static bool snapEmpty( +static short snapEmpty( SnapData *snapdata, Object *ob, float obmat[4][4], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float *UNUSED(r_no)) + float r_loc[3], float *UNUSED(r_no), int *r_index) { - bool retval = false; + short retval = 0; if (ob->transflag & OB_DUPLI) { return retval; } /* for now only vertex supported */ - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_VERTEX: + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } + + bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + float dist_px_sq = SQUARE(*dist_px); + float co[3]; + copy_v3_v3(co, obmat[3]); + if (test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, co, &dist_px_sq, r_loc)) { - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; - float dist_px_sq = SQUARE(*dist_px); - float tmp_co[3]; - copy_v3_v3(tmp_co, obmat[3]); - if (test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, tmp_co, - snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc)) - { - *dist_px = sqrtf(dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - retval = true; - } - break; + *dist_px = sqrtf(dist_px_sq); + retval = SCE_SNAP_MODE_VERTEX; } - default: - break; } - return retval; + if (retval) { + if (r_index) { + /* Does not support index. */ + *r_index = -1; + } + return retval; + } + + return 0; } -static bool snapCamera( +static short snapCamera( const SnapObjectContext *sctx, SnapData *snapdata, Object *object, float obmat[4][4], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float *UNUSED(r_no)) + float r_loc[3], float *UNUSED(r_no), int *r_index) { + short retval = 0; + + Depsgraph *depsgraph = sctx->depsgraph; Scene *scene = sctx->scene; bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; float dist_px_sq = SQUARE(*dist_px); float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; - bool retval = false; MovieClip *clip = BKE_object_movieclip_get(scene, object, false); MovieTracking *tracking; @@ -1509,139 +1652,127 @@ static bool snapCamera( return retval; } + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } + tracking = &clip->tracking; - BKE_tracking_get_camera_object_matrix(scene, object, orig_camera_mat); + BKE_tracking_get_camera_object_matrix(depsgraph, scene, object, orig_camera_mat); invert_m4_m4(orig_camera_imat, orig_camera_mat); invert_m4_m4(imat, obmat); - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_VERTEX: + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + struct DistProjectedAABBPrecalc neasrest_precalc; + dist_squared_to_projected_aabb_precalc( + &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + + MovieTrackingObject *tracking_object; + for (tracking_object = tracking->objects.first; + tracking_object; + tracking_object = tracking_object->next) { - MovieTrackingObject *tracking_object; - - for (tracking_object = tracking->objects.first; - tracking_object; - tracking_object = tracking_object->next) - { - ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); - MovieTrackingTrack *track; - float reconstructed_camera_mat[4][4], - reconstructed_camera_imat[4][4]; - float (*vertex_obmat)[4]; - - if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { - BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, - CFRA, reconstructed_camera_mat); - - invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); - } + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + MovieTrackingTrack *track; + float reconstructed_camera_mat[4][4], + reconstructed_camera_imat[4][4]; + float (*vertex_obmat)[4]; - for (track = tracksbase->first; track; track = track->next) { - float bundle_pos[3]; + if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) { + BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object, + CFRA, reconstructed_camera_mat); - if ((track->flag & TRACK_HAS_BUNDLE) == 0) { - continue; - } + invert_m4_m4(reconstructed_camera_imat, reconstructed_camera_mat); + } - copy_v3_v3(bundle_pos, track->bundle_pos); - if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { - vertex_obmat = orig_camera_mat; - } - else { - mul_m4_v3(reconstructed_camera_imat, bundle_pos); - vertex_obmat = obmat; - } + for (track = tracksbase->first; track; track = track->next) { + float bundle_pos[3]; - mul_m4_v3(vertex_obmat, bundle_pos); - retval |= test_projected_vert_dist( - snapdata->depth_range, snapdata->mval, bundle_pos, - snapdata->pmat, snapdata->win_half, is_persp, &dist_px_sq, - r_loc); + if ((track->flag & TRACK_HAS_BUNDLE) == 0) { + continue; } - } - break; + copy_v3_v3(bundle_pos, track->bundle_pos); + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + vertex_obmat = orig_camera_mat; + } + else { + mul_m4_v3(reconstructed_camera_imat, bundle_pos); + vertex_obmat = obmat; + } + + mul_m4_v3(vertex_obmat, bundle_pos); + if (test_projected_vert_dist( + &neasrest_precalc, + clip_planes_local, snapdata->clip_plane_len, + is_persp, bundle_pos, &dist_px_sq, r_loc)) + { + retval = SCE_SNAP_MODE_VERTEX; + } + } } - default: - break; } if (retval) { *dist_px = sqrtf(dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); - return true; + if (r_index) { + /* Does not support index. */ + *r_index = -1; + } + return retval; } - return false; -} -static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt) -{ - const int *index_mp_to_orig = dm->getPolyDataArray(dm, CD_ORIGINDEX); - return index_mp_to_orig ? index_mp_to_orig[lt->poly] : lt->poly; + return 0; } -static bool snapDerivedMesh( +static short snapMesh( SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, DerivedMesh *dm, float obmat[4][4], + Object *ob, Mesh *me, float obmat[4][4], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3]) + float r_loc[3], float r_no[3], int *r_index) { - bool retval = false; + BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { - if (dm->getNumEdges(dm) == 0) { - return retval; + if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) { + if (me->totedge == 0) { + return 0; } } else { - if (dm->getNumVerts(dm) == 0) { - return retval; + if (me->totvert == 0) { + return 0; } } - float imat[4][4]; - float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_normal_local[3]; - float local_scale; - - invert_m4_m4(imat, obmat); - transpose_m3_m4(timat, imat); - - copy_v3_v3(ray_normal_local, snapdata->ray_dir); - - mul_mat3_m4_v3(imat, ray_normal_local); - - /* local scale in normal direction */ - local_scale = normalize_v3(ray_normal_local); - float lpmat[4][4]; - float ray_org_local[3]; - float ray_min_dist; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - ray_min_dist = snapdata->depth_range[0] * local_scale; - copy_v3_v3(ray_org_local, snapdata->ray_origin); - mul_m4_v3(imat, ray_org_local); + float dist_px_sq = SQUARE(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_object_boundbox_get(ob); + BoundBox *bb = BKE_mesh_boundbox_get(ob); if (bb) { /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */ - float dist_px_sq = dist_squared_to_projected_aabb_simple( - lpmat, snapdata->win_half, ray_min_dist, snapdata->mval, - ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]); - if (dist_px_sq > SQUARE(*dist_px)) { - return retval; + + struct DistProjectedAABBPrecalc data_precalc; + dist_squared_to_projected_aabb_precalc( + &data_precalc, lpmat, snapdata->win_size, snapdata->mval); + + bool dummy[3]; + float bb_dist_px_sq = dist_squared_to_projected_aabb( + &data_precalc, bb->vec[0], bb->vec[6], dummy); + + if (bb_dist_px_sq > dist_px_sq) { + return 0; } } SnapObjectData_Mesh *sod = NULL; - BVHTreeFromMesh *treedata = NULL; void **sod_p; if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { @@ -1650,140 +1781,207 @@ static bool snapDerivedMesh( else { sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); sod->sd.type = SNAP_MESH; + /* start assuming that it has each of these element types */ + sod->has_looptris = true; + sod->has_loose_edge = true; + sod->has_loose_vert = true; } - int tree_index = -1; - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_EDGE: - tree_index = 1; - break; - case SCE_SNAP_MODE_VERTEX: - tree_index = 0; - break; + BVHTreeFromMesh *treedata, dummy_treedata; + BVHTree **bvhtree; + treedata = &sod->treedata; + bvhtree = sod->bvhtree; + + /* the tree is owned by the DM and may have been freed since we last used! */ + if ((sod->has_looptris && treedata->tree && !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) || + (sod->has_loose_edge && bvhtree[0] && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[0])) || + (sod->has_loose_vert && bvhtree[1] && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[1]))) + { + BLI_assert(!treedata->tree || !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)); + BLI_assert(!bvhtree[0] || !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[0])); + BLI_assert(!bvhtree[1] || !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[1])); + + free_bvhtree_from_mesh(treedata); + bvhtree[0] = NULL; + bvhtree[1] = NULL; } - if (tree_index != -1) { - if (sod->bvh_trees[tree_index] == NULL) { - sod->bvh_trees[tree_index] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + + if (sod->has_looptris && treedata->tree == NULL) { + BKE_bvhtree_from_mesh_get(treedata, me, BVHTREE_FROM_LOOPTRI, 4); + sod->has_looptris = (treedata->tree != NULL); + if (sod->has_looptris) { + /* Make sure that the array of edges is referenced in the callbacks. */ + treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ } - treedata = sod->bvh_trees[tree_index]; + } + if (sod->has_loose_edge && bvhtree[0] == NULL) { + bvhtree[0] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEEDGES, 2); + sod->has_loose_edge = bvhtree[0] != NULL; - /* the tree is owned by the DM and may have been freed since we last used! */ - if (treedata && treedata->tree) { - if (treedata->cached && !bvhcache_has_tree(dm->bvhCache, treedata->tree)) { - free_bvhtree_from_mesh(treedata); - } - else { - if (treedata->vert == NULL) { - treedata->vert = DM_get_vert_array(dm, &treedata->vert_allocated); - } - if ((tree_index == 1) && (treedata->edge == NULL)) { - treedata->edge = DM_get_edge_array(dm, &treedata->edge_allocated); - } - } + if (sod->has_loose_edge) { + BLI_assert(treedata->vert_allocated == false); + treedata->vert = dummy_treedata.vert; + treedata->vert_allocated = dummy_treedata.vert_allocated; + + BLI_assert(treedata->edge_allocated == false); + treedata->edge = dummy_treedata.edge; + treedata->edge_allocated = dummy_treedata.edge_allocated; } } + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sod->has_loose_vert && bvhtree[1] == NULL) { + bvhtree[1] = BKE_bvhtree_from_mesh_get(&dummy_treedata, me, BVHTREE_FROM_LOOSEVERTS, 2); + sod->has_loose_vert = bvhtree[1] != NULL; - if (treedata) { - if (treedata->tree == NULL) { - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_EDGE: - bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_EDGES, 2); - break; - case SCE_SNAP_MODE_VERTEX: - bvhtree_from_mesh_get(treedata, dm, BVHTREE_FROM_VERTS, 2); - break; + if (sod->has_loose_vert) { + BLI_assert(treedata->vert_allocated == false); + treedata->vert = dummy_treedata.vert; + treedata->vert_allocated = dummy_treedata.vert_allocated; } } - if (treedata->tree == NULL) { - return retval; - } } else { - return retval; + /* Not necessary, just to keep the data more consistent. */ + sod->has_loose_vert = false; + } + + /* Update pointers. */ + if (treedata->vert_allocated == false) { + treedata->vert = me->mvert; /* CustomData_get_layer(&me->vdata, CD_MVERT);? */ + } + if (treedata->tree || bvhtree[0]) { + if (treedata->edge_allocated == false) { + /* If raycast has been executed before, `treedata->edge` can be NULL. */ + treedata->edge = me->medge; /* CustomData_get_layer(&me->edata, CD_MEDGE);? */ + } + if (treedata->loop && treedata->loop_allocated == false) { + treedata->loop = me->mloop; /* CustomData_get_layer(&me->edata, CD_MLOOP);? */ + } + if (treedata->looptri && treedata->looptri_allocated == false) { + treedata->looptri = BKE_mesh_runtime_looptri_ensure(me); + } } - /* Warning: the depth_max is currently being used only in perspective view. - * It is not correct to limit the maximum depth for elements obtained with nearest - * since this limitation depends on the normal and the size of the occlusion face. - * And more... ray_depth is being confused with Z-depth here... (varies only the precision) */ - const float ray_depth_max_global = *ray_depth + snapdata->depth_range[0]; + Nearest2dUserData nearest2d = { + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .userdata = treedata, + .get_vert_co = (Nearest2DGetVertCoCallback)cb_mvert_co_get, + .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_medge_verts_get, + .get_tri_verts_index = (Nearest2DGetTriVertsCallback)cb_mlooptri_verts_get, + .get_tri_edges_index = (Nearest2DGetTriEdgesCallback)cb_mlooptri_edges_get, + .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_mvert_no_copy, + }; - Nearest2dUserData neasrest2d = { - .dist_px_sq = SQUARE(*dist_px), - .r_axis_closest = {1.0f, 1.0f, 1.0f}, - .depth_range = {snapdata->depth_range[0], ray_depth_max_global}, - .userdata = treedata, - .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_dm_edge_verts, - .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_dm_vert_no, - .index = -1}; + BVHTreeNearest nearest = { + .index = -1, + .dist_sq = dist_px_sq, + }; + int last_index = nearest.index; + short elem = SCE_SNAP_MODE_VERTEX; - dist_squared_to_projected_aabb_precalc( - &neasrest2d.data_precalc, lpmat, - snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, - ray_min_dist, snapdata->mval, ray_org_local, ray_normal_local); + float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; + transpose_m4_m4(tobmat, obmat); + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } + + if (bvhtree[1] && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + /* snap to loose verts */ + BLI_bvhtree_find_nearest_projected( + bvhtree[1], lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_vert, &nearest2d); + + last_index = nearest.index; + } + + if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (bvhtree[0]) { + /* snap to loose edges */ + BLI_bvhtree_find_nearest_projected( + bvhtree[0], lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_edge, &nearest2d); + } + + if (treedata->tree) { + /* snap to looptris */ + BLI_bvhtree_find_nearest_projected( + treedata->tree, lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_tri_edges, &nearest2d); + } + + if (last_index != nearest.index) { + elem = SCE_SNAP_MODE_EDGE; + } + } + else { + BLI_assert(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX); + if (bvhtree[0]) { + /* snap to loose edges */ + BLI_bvhtree_find_nearest_projected( + bvhtree[0], lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_edge_verts, &nearest2d); + } - BVHTree_WalkLeafCallback cb_walk_leaf = - (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; + if (treedata->tree) { + /* snap to looptris */ + BLI_bvhtree_find_nearest_projected( + treedata->tree, lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_tri_verts, &nearest2d); + } + } - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); + if (nearest.index != -1) { + *dist_px = sqrtf(nearest.dist_sq); - if (neasrest2d.index != -1) { - copy_v3_v3(r_loc, neasrest2d.co); + copy_v3_v3(r_loc, nearest.co); mul_m4_v3(obmat, r_loc); + if (r_no) { - copy_v3_v3(r_no, neasrest2d.no); - mul_m3_v3(timat, r_no); + float imat[4][4]; + invert_m4_m4(imat, obmat); + + copy_v3_v3(r_no, nearest.no); + mul_transposed_mat3_m4_v3(imat, r_no); normalize_v3(r_no); } - *dist_px = sqrtf(neasrest2d.dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + if (r_index) { + *r_index = nearest.index; + } - retval = true; + return elem; } - return retval; + return 0; } -static bool snapEditMesh( +static short snapEditMesh( SnapObjectContext *sctx, SnapData *snapdata, Object *ob, BMEditMesh *em, float obmat[4][4], /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3]) + float r_loc[3], float r_no[3], int *r_index) { - bool retval = false; + BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); - if (snapdata->snap_to == SCE_SNAP_MODE_EDGE) { + if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_EDGE) { if (em->bm->totedge == 0) { - return retval; + return 0; } } else { if (em->bm->totvert == 0) { - return retval; + return 0; } } - float imat[4][4]; - float timat[3][3]; /* transpose inverse matrix for normals */ - float ray_normal_local[3]; - - invert_m4_m4(imat, obmat); - transpose_m3_m4(timat, imat); - - copy_v3_v3(ray_normal_local, snapdata->ray_dir); - - mul_mat3_m4_v3(imat, ray_normal_local); - - /* local scale in normal direction */ - float local_scale = normalize_v3(ray_normal_local); - SnapObjectData_EditMesh *sod = NULL; - BVHTreeFromEditMesh *treedata = NULL; + BVHTreeFromEditMesh *treedata_vert = NULL, *treedata_edge = NULL; void **sod_p; if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { @@ -1794,109 +1992,113 @@ static bool snapEditMesh( sod->sd.type = SNAP_EDIT_MESH; } - int tree_index = -1; - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_EDGE: - tree_index = 1; - break; - case SCE_SNAP_MODE_VERTEX: - tree_index = 0; - break; - } - if (tree_index != -1) { - if (sod->bvh_trees[tree_index] == NULL) { - sod->bvh_trees[tree_index] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*treedata)); + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sod->bvh_trees[0] == NULL) { + sod->bvh_trees[0] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees)); + } + treedata_vert = sod->bvh_trees[0]; + if (treedata_vert->tree == NULL) { + BLI_bitmap *verts_mask = NULL; + int verts_num_active = -1; + if (sctx->callbacks.edit_mesh.test_vert_fn) { + verts_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); + verts_num_active = BM_iter_mesh_bitmap_from_filter( + BM_VERTS_OF_MESH, em->bm, verts_mask, + (bool(*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, + sctx->callbacks.edit_mesh.user_data); + } + bvhtree_from_editmesh_verts_ex(treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6); + MEM_SAFE_FREE(verts_mask); } - treedata = sod->bvh_trees[tree_index]; } - if (treedata) { - if (treedata->tree == NULL) { - BLI_bitmap *elem_mask = NULL; - switch (snapdata->snap_to) { - case SCE_SNAP_MODE_EDGE: - { - int edges_num_active = -1; - if (sctx->callbacks.edit_mesh.test_edge_fn) { - elem_mask = BLI_BITMAP_NEW(em->bm->totedge, __func__); - edges_num_active = BM_iter_mesh_bitmap_from_filter( - BM_EDGES_OF_MESH, em->bm, elem_mask, - (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn, - sctx->callbacks.edit_mesh.user_data); - } - bvhtree_from_editmesh_edges_ex(treedata, em, elem_mask, edges_num_active, 0.0f, 2, 6); - break; - } - case SCE_SNAP_MODE_VERTEX: - { - int verts_num_active = -1; - if (sctx->callbacks.edit_mesh.test_vert_fn) { - elem_mask = BLI_BITMAP_NEW(em->bm->totvert, __func__); - verts_num_active = BM_iter_mesh_bitmap_from_filter( - BM_VERTS_OF_MESH, em->bm, elem_mask, - (bool (*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, - sctx->callbacks.edit_mesh.user_data); - } - bvhtree_from_editmesh_verts_ex(treedata, em, elem_mask, verts_num_active, 0.0f, 2, 6); - break; - } - } - if (elem_mask) { - MEM_freeN(elem_mask); - } + if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sod->bvh_trees[1] == NULL) { + sod->bvh_trees[1] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees)); } - if (treedata->tree == NULL) { - return retval; + treedata_edge = sod->bvh_trees[1]; + if (treedata_edge->tree == NULL) { + BLI_bitmap *edges_mask = NULL; + int edges_num_active = -1; + if (sctx->callbacks.edit_mesh.test_edge_fn) { + edges_mask = BLI_BITMAP_NEW(em->bm->totedge, __func__); + edges_num_active = BM_iter_mesh_bitmap_from_filter( + BM_EDGES_OF_MESH, em->bm, edges_mask, + (bool(*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn, + sctx->callbacks.edit_mesh.user_data); + + } + bvhtree_from_editmesh_edges_ex(treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6); + MEM_SAFE_FREE(edges_mask); } } - else { - return retval; - } - float ray_org_local[3]; - copy_v3_v3(ray_org_local, snapdata->ray_origin); - mul_m4_v3(imat, ray_org_local); + Nearest2dUserData nearest2d = { + .is_persp = snapdata->view_proj == VIEW_PROJ_PERSP, + .userdata = em, + .get_vert_co = (Nearest2DGetVertCoCallback)cb_bvert_co_get, + .get_edge_verts_index = (Nearest2DGetEdgeVertsCallback)cb_bedge_verts_get, + .copy_vert_no = (Nearest2DCopyVertNoCallback)cb_bvert_no_copy, + }; - Nearest2dUserData neasrest2d = { - .dist_px_sq = SQUARE(*dist_px), - .r_axis_closest = {1.0f, 1.0f, 1.0f}, - .depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]}, - .userdata = treedata, - .get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_bedge_verts, - .copy_vert_no = (Nearest2DCopyVertNoCallback)copy_bvert_no, - .index = -1}; + BVHTreeNearest nearest = { + .index = -1, + .dist_sq = SQUARE(*dist_px), + }; + int last_index = nearest.index; + short elem = SCE_SNAP_MODE_VERTEX; - float lpmat[4][4]; + float lpmat[4][4], tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; mul_m4_m4m4(lpmat, snapdata->pmat, obmat); - dist_squared_to_projected_aabb_precalc( - &neasrest2d.data_precalc, lpmat, - snapdata->view_proj == VIEW_PROJ_PERSP, snapdata->win_half, - (snapdata->depth_range[0] * local_scale), snapdata->mval, - ray_org_local, ray_normal_local); + transpose_m4_m4(tobmat, obmat); + + for (int i = snapdata->clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + } - BVHTree_WalkLeafCallback cb_walk_leaf = - (snapdata->snap_to == SCE_SNAP_MODE_VERTEX) ? - cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge; + if (treedata_vert && snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + BM_mesh_elem_table_ensure(em->bm, BM_VERT); + BLI_bvhtree_find_nearest_projected( + treedata_vert->tree, lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_vert, &nearest2d); - BLI_bvhtree_walk_dfs( - treedata->tree, - cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest2d); + last_index = nearest.index; + } + + if (treedata_edge && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT); + BLI_bvhtree_find_nearest_projected( + treedata_edge->tree, lpmat, snapdata->win_size, snapdata->mval, + clip_planes_local, snapdata->clip_plane_len, + &nearest, cb_snap_edge, &nearest2d); + + if (last_index != nearest.index) { + elem = SCE_SNAP_MODE_EDGE; + } + } + + if (nearest.index != -1) { + *dist_px = sqrtf(nearest.dist_sq); - if (neasrest2d.index != -1) { - copy_v3_v3(r_loc, neasrest2d.co); + copy_v3_v3(r_loc, nearest.co); mul_m4_v3(obmat, r_loc); if (r_no) { - copy_v3_v3(r_no, neasrest2d.no); - mul_m3_v3(timat, r_no); + float imat[4][4]; + invert_m4_m4(imat, obmat); + + copy_v3_v3(r_no, nearest.no); + mul_transposed_mat3_m4_v3(imat, r_no); normalize_v3(r_no); } - *dist_px = sqrtf(neasrest2d.dist_px_sq); - *ray_depth = depth_get(r_loc, snapdata->ray_start, snapdata->ray_dir); + if (r_index) { + *r_index = nearest.index; + } - retval = true; + return elem; } - return retval; + return 0; } /** @@ -1904,112 +2106,108 @@ static bool snapEditMesh( * * \note Duplicate args here are documented at #snapObjectsRay */ -static bool snapObject( +static short snapObject( SnapObjectContext *sctx, SnapData *snapdata, - Object *ob, float obmat[4][4], - bool use_obedit, + Object *ob, float obmat[4][4], bool use_obedit, /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3], + float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { - bool retval = false; - - if (ob->type == OB_MESH) { - BMEditMesh *em; - - if (use_obedit) { - em = BKE_editmesh_from_object(ob); - retval = snapEditMesh( - sctx, snapdata, ob, em, obmat, - ray_depth, dist_px, - r_loc, r_no); - } - else { - /* in this case we want the mesh from the editmesh, avoids stale data. see: T45978. - * still set the 'em' to NULL, since we only want the 'dm'. */ - DerivedMesh *dm; - em = BKE_editmesh_from_object(ob); - if (em) { - editbmesh_get_derived_cage_and_final(sctx->scene, ob, em, CD_MASK_BAREMESH, &dm); + short retval = 0; + + switch (ob->type) { + case OB_MESH: + if (use_obedit) { + BMEditMesh *em = BKE_editmesh_from_object(ob); + retval = snapEditMesh( + sctx, snapdata, ob, em, obmat, + dist_px, + r_loc, r_no, r_index); } else { - dm = mesh_get_derived_final(sctx->scene, ob, CD_MASK_BAREMESH); + retval = snapMesh( + sctx, snapdata, ob, ob->data, obmat, + dist_px, + r_loc, r_no, r_index); } - retval = snapDerivedMesh( - sctx, snapdata, ob, dm, obmat, - ray_depth, dist_px, - r_loc, r_no); + break; - dm->release(dm); - } - } - else if (snapdata->snap_to != SCE_SNAP_MODE_FACE) { - if (ob->type == OB_ARMATURE) { + case OB_ARMATURE: retval = snapArmature( snapdata, - ob, ob->data, obmat, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_CURVE) { + ob, obmat, use_obedit, + dist_px, + r_loc, r_no, r_index); + break; + + case OB_CURVE: retval = snapCurve( snapdata, - ob, ob->data, obmat, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_EMPTY) { + ob, obmat, use_obedit, + dist_px, + r_loc, r_no, r_index); + break; + + case OB_EMPTY: retval = snapEmpty( - snapdata, - ob, obmat, - ray_depth, dist_px, - r_loc, r_no); - } - else if (ob->type == OB_CAMERA) { + snapdata, ob, obmat, + dist_px, + r_loc, r_no, r_index); + break; + + case OB_CAMERA: retval = snapCamera( sctx, snapdata, ob, obmat, - ray_depth, dist_px, - r_loc, r_no); - } + dist_px, + r_loc, r_no, r_index); + break; } if (retval) { if (r_ob) { *r_ob = ob; + } + if (r_obmat) { copy_m4_m4(r_obmat, obmat); } + return retval; } - return retval; + return 0; } struct SnapObjUserData { SnapData *snapdata; /* read/write args */ - float *ray_depth; float *dist_px; /* return args */ float *r_loc; float *r_no; + int *r_index; Object **r_ob; float (*r_obmat)[4]; - bool ret; + short ret; }; static void sanp_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data) { struct SnapObjUserData *dt = data; - dt->ret |= snapObject( + + short elem = snapObject( sctx, dt->snapdata, ob, obmat, is_obedit, /* read/write args */ - dt->ray_depth, dt->dist_px, + dt->dist_px, /* return args */ - dt->r_loc, dt->r_no, + dt->r_loc, dt->r_no, dt->r_index, dt->r_ob, dt->r_obmat); + + if (elem) { + dt->ret = elem; + } } @@ -2041,29 +2239,27 @@ static void sanp_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, flo * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). * */ -static bool snapObjectsRay( +static short snapObjectsRay( SnapObjectContext *sctx, SnapData *snapdata, - const eSnapSelect snap_select, const bool use_object_edit_cage, + const struct SnapObjectParams *params, /* read/write args */ - float *ray_depth, float *dist_px, + float *dist_px, /* return args */ - float r_loc[3], float r_no[3], + float r_loc[3], float r_no[3], int *r_index, Object **r_ob, float r_obmat[4][4]) { - Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL; - struct SnapObjUserData data = { .snapdata = snapdata, - .ray_depth = ray_depth, .dist_px = dist_px, .r_loc = r_loc, .r_no = r_no, .r_ob = r_ob, + .r_index = r_index, .r_obmat = r_obmat, - .ret = false, + .ret = 0, }; - iter_snap_objects(sctx, snap_select, obedit, sanp_obj_cb, &data); + iter_snap_objects(sctx, params, sanp_obj_cb, &data); return data.ret; } @@ -2075,7 +2271,7 @@ static bool snapObjectsRay( * \{ */ SnapObjectContext *ED_transform_snap_object_context_create( - Main *bmain, Scene *scene, int flag) + Main *bmain, Scene *scene, Depsgraph *depsgraph, int flag) { SnapObjectContext *sctx = MEM_callocN(sizeof(*sctx), __func__); @@ -2083,6 +2279,7 @@ SnapObjectContext *ED_transform_snap_object_context_create( sctx->bmain = bmain; sctx->scene = scene; + sctx->depsgraph = depsgraph; sctx->cache.object_map = BLI_ghash_ptr_new(__func__); sctx->cache.mem_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__); @@ -2091,11 +2288,11 @@ SnapObjectContext *ED_transform_snap_object_context_create( } SnapObjectContext *ED_transform_snap_object_context_create_view3d( - Main *bmain, Scene *scene, int flag, + Main *bmain, Scene *scene, Depsgraph *depsgraph, int flag, /* extra args for view3d */ const ARegion *ar, const View3D *v3d) { - SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, flag); + SnapObjectContext *sctx = ED_transform_snap_object_context_create(bmain, scene, depsgraph, flag); sctx->use_v3d = true; sctx->v3d_data.ar = ar; @@ -2110,13 +2307,8 @@ static void snap_object_data_free(void *sod_v) case SNAP_MESH: { SnapObjectData_Mesh *sod = sod_v; - for (int i = 0; i < ARRAY_SIZE(sod->bvh_trees); i++) { - if (sod->bvh_trees[i]) { - free_bvhtree_from_mesh(sod->bvh_trees[i]); - } - } - if (sod->poly_allocated) { - MEM_freeN(sod->mpoly); + if (sod->treedata.tree) { + free_bvhtree_from_mesh(&sod->treedata); } break; } @@ -2164,9 +2356,8 @@ bool ED_transform_snap_object_project_ray_ex( Object **r_ob, float r_obmat[4][4]) { return raycastObjects( - sctx, + sctx, params, ray_start, ray_normal, - params->snap_select, params->use_object_edit_cage, ray_depth, r_loc, r_no, r_index, r_ob, r_obmat, NULL); } @@ -2193,9 +2384,8 @@ bool ED_transform_snap_object_project_ray_all( #endif bool retval = raycastObjects( - sctx, + sctx, params, ray_start, ray_normal, - params->snap_select, params->use_object_edit_cage, &ray_depth, NULL, NULL, NULL, NULL, NULL, r_hit_list); @@ -2256,61 +2446,156 @@ bool ED_transform_snap_object_project_ray( r_co, r_no); } -static bool transform_snap_context_project_view3d_mixed_impl( +static short transform_snap_context_project_view3d_mixed_impl( SnapObjectContext *sctx, const unsigned short snap_to_flag, const struct SnapObjectParams *params, const float mval[2], float *dist_px, - bool use_depth, - float r_co[3], float r_no[3]) + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) { - float ray_depth = BVH_RAYCAST_DIST_MAX; - bool is_hit = false; + BLI_assert( + (snap_to_flag & ( + SCE_SNAP_MODE_VERTEX | + SCE_SNAP_MODE_EDGE | + SCE_SNAP_MODE_FACE)) != 0); - const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE}; + short retval = 0; + bool has_hit = false; + int index = -1; - BLI_assert(snap_to_flag != 0); - BLI_assert((snap_to_flag & ~(1 | 2 | 4)) == 0); + float loc[3], no[3], obmat[4][4]; + Object *ob = NULL; - if (use_depth) { - const float dist_px_orig = dist_px ? *dist_px : 0; - for (int i = 2; i >= 0; i--) { - if (snap_to_flag & (1 << i)) { - if (i == 0) { - BLI_assert(dist_px != NULL); - *dist_px = dist_px_orig; - } - if (ED_transform_snap_object_project_view3d( - sctx, - elem_type[i], params, - mval, dist_px, &ray_depth, - r_co, r_no)) - { - /* 0.01 is a random but small value to prioritizing - * the first elements of the loop */ - ray_depth += 0.01f; - is_hit = true; - } - } + const ARegion *ar = sctx->v3d_data.ar; + const RegionView3D *rv3d = ar->regiondata; + + bool use_occlusion_test = + params->use_occlusion_test && + !(sctx->v3d_data.v3d->shading.flag & V3D_SHADING_XRAY); + + if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) { + float ray_start[3], ray_normal[3]; + + if (!ED_view3d_win_to_ray_ex( + sctx->depsgraph, + sctx->v3d_data.ar, sctx->v3d_data.v3d, + mval, NULL, ray_normal, ray_start, true)) + { + return false; + } + + float dummy_ray_depth = BVH_RAYCAST_DIST_MAX; + + has_hit = raycastObjects( + sctx, params, + ray_start, ray_normal, + &dummy_ray_depth, loc, no, + &index, &ob, obmat, NULL); + + if (has_hit && (snap_to_flag & SCE_SNAP_MODE_FACE)) { + retval = SCE_SNAP_MODE_FACE; } } - else { - for (int i = 0; i < 3; i++) { - if (snap_to_flag & (1 << i)) { - if (ED_transform_snap_object_project_view3d( - sctx, - elem_type[i], params, - mval, dist_px, &ray_depth, - r_co, r_no)) - { - is_hit = true; - break; - } + + if (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE)) { + short elem; + float dist_px_tmp = *dist_px; + + SnapData snapdata; + copy_m4_m4(snapdata.pmat, rv3d->persmat); + snapdata.win_size[0] = ar->winx; + snapdata.win_size[1] = ar->winy; + copy_v2_v2(snapdata.mval, mval); + snapdata.snap_to_flag = snap_to_flag; + snapdata.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; + + planes_from_projmat( + snapdata.pmat, + NULL, NULL, NULL, NULL, + snapdata.clip_plane[0], snapdata.clip_plane[1]); + + snapdata.clip_plane_len = 2; + + if (has_hit) { + /* Compute the new clip_pane but do not add it yet. */ + float new_clipplane[4]; + plane_from_point_normal_v3(new_clipplane, loc, no); + if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) { + /* The plane is facing the wrong direction. */ + negate_v4(new_clipplane); } + + /* Try to snap only to the polygon. */ + elem = snap_mesh_polygon( + sctx, &snapdata, ob, obmat, + &dist_px_tmp, loc, no, &index); + + if (elem) { + retval = elem; + } + + /* Add the new clip plane to the beginning of the list. */ + for (int i = snapdata.clip_plane_len; i != 0; i--) { + copy_v4_v4(snapdata.clip_plane[i], snapdata.clip_plane[i - 1]); + } + copy_v4_v4(snapdata.clip_plane[0], new_clipplane); + snapdata.clip_plane_len++; + } + + elem = snapObjectsRay( + sctx, &snapdata, params, + &dist_px_tmp, loc, no, &index, &ob, obmat); + + if (elem) { + retval = elem; + } + + if ((retval == SCE_SNAP_MODE_EDGE) && + (snapdata.snap_to_flag & SCE_SNAP_MODE_VERTEX)) + { + retval = snap_mesh_edge_verts_mixed( + sctx, &snapdata, + ob, obmat, *dist_px, + &dist_px_tmp, loc, no, &index); + } + + *dist_px = dist_px_tmp; + } + + if (retval) { + copy_v3_v3(r_loc, loc); + if (r_no) { + copy_v3_v3(r_no, no); + } + if (r_ob) { + *r_ob = ob; + } + if (r_obmat) { + copy_m4_m4(r_obmat, obmat); + } + if (r_index) { + *r_index = index; } + return retval; } - return is_hit; + return 0; +} + +bool ED_transform_snap_object_project_view3d_ex( + SnapObjectContext *sctx, + const unsigned short snap_to, + const struct SnapObjectParams *params, + const float mval[2], float *dist_px, + float r_loc[3], float r_no[3], int *r_index, + Object **r_ob, float r_obmat[4][4]) +{ + return transform_snap_context_project_view3d_mixed_impl( + sctx, + snap_to, params, + mval, dist_px, + r_loc, r_no, r_index, r_ob, r_obmat) != 0; } /** @@ -2321,86 +2606,15 @@ static bool transform_snap_context_project_view3d_mixed_impl( * \param sctx: Snap context. * \param mval_fl: Screenspace coordinate. * \param dist_px: Maximum distance to snap (in pixels). - * \param use_depth: Snap to the closest element, use when using more than one snap type. * \param r_co: hit location. * \param r_no: hit normal (optional). * \return Snap success */ -bool ED_transform_snap_object_project_view3d_mixed( - SnapObjectContext *sctx, - const unsigned short snap_to_flag, - const struct SnapObjectParams *params, - const float mval_fl[2], float *dist_px, - bool use_depth, - float r_co[3], float r_no[3]) -{ - return transform_snap_context_project_view3d_mixed_impl( - sctx, - snap_to_flag, params, - mval_fl, dist_px, use_depth, - r_co, r_no); -} - -bool ED_transform_snap_object_project_view3d_ex( - SnapObjectContext *sctx, - const unsigned short snap_to, - const struct SnapObjectParams *params, - const float mval[2], float *dist_px, - float *ray_depth, - float r_loc[3], float r_no[3], int *r_index, - Object **r_ob, float r_obmat[4][4]) -{ - float ray_origin[3], ray_start[3], ray_normal[3], depth_range[2], ray_end[3]; - - const ARegion *ar = sctx->v3d_data.ar; - const RegionView3D *rv3d = ar->regiondata; - - ED_view3d_win_to_origin(ar, mval, ray_origin); - ED_view3d_win_to_vector(ar, mval, ray_normal); - - ED_view3d_clip_range_get( - sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata, - &depth_range[0], &depth_range[1], false); - - madd_v3_v3v3fl(ray_start, ray_origin, ray_normal, depth_range[0]); - madd_v3_v3v3fl(ray_end, ray_origin, ray_normal, depth_range[1]); - - if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) { - return false; - } - - float ray_depth_fallback; - if (ray_depth == NULL) { - ray_depth_fallback = BVH_RAYCAST_DIST_MAX; - ray_depth = &ray_depth_fallback; - } - - if (snap_to == SCE_SNAP_MODE_FACE) { - return raycastObjects( - sctx, - ray_start, ray_normal, - params->snap_select, params->use_object_edit_cage, - ray_depth, r_loc, r_no, r_index, r_ob, r_obmat, NULL); - } - else { - SnapData snapdata; - const enum eViewProj view_proj = ((RegionView3D *)ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; - snap_data_set(&snapdata, ar, snap_to, view_proj, mval, - ray_origin, ray_start, ray_normal, depth_range); - - return snapObjectsRay( - sctx, &snapdata, - params->snap_select, params->use_object_edit_cage, - ray_depth, dist_px, r_loc, r_no, r_ob, r_obmat); - } -} - bool ED_transform_snap_object_project_view3d( SnapObjectContext *sctx, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], float *dist_px, - float *ray_depth, float r_loc[3], float r_no[3]) { return ED_transform_snap_object_project_view3d_ex( @@ -2408,7 +2622,6 @@ bool ED_transform_snap_object_project_view3d( snap_to, params, mval, dist_px, - ray_depth, r_loc, r_no, NULL, NULL, NULL); } @@ -2426,6 +2639,7 @@ bool ED_transform_snap_object_project_all_view3d_ex( float ray_start[3], ray_normal[3]; if (!ED_view3d_win_to_ray_ex( + sctx->depsgraph, sctx->v3d_data.ar, sctx->v3d_data.v3d, mval, NULL, ray_normal, ray_start, true)) { |