/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Contributor(s): Martin Poirier * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/editors/transform/transform_orientations.c * \ingroup edtransform */ #include #include #include #include "MEM_guardedalloc.h" #include "DNA_armature_types.h" #include "DNA_curve_types.h" #include "DNA_meta_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_view3d_types.h" #include "BLI_math.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_path_util.h" #include "BLI_utildefines.h" #include "BKE_action.h" #include "BKE_curve.h" #include "BKE_context.h" #include "BKE_editmesh.h" #include "BKE_report.h" #include "BKE_main.h" #include "BKE_screen.h" #include "BLF_translation.h" #include "ED_armature.h" #include "RNA_define.h" #include "UI_interface.h" #include "transform.h" /* *********************** TransSpace ************************** */ void BIF_clearTransformOrientation(bContext *C) { View3D *v3d = CTX_wm_view3d(C); ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; BLI_freelistN(transform_spaces); // Need to loop over all view3d if (v3d && v3d->twmode >= V3D_MANIP_CUSTOM) { v3d->twmode = V3D_MANIP_GLOBAL; /* fallback to global */ } } static TransformOrientation *findOrientationName(ListBase *lb, const char *name) { return BLI_findstring(lb, name, offsetof(TransformOrientation, name)); } static bool uniqueOrientationNameCheck(void *arg, const char *name) { return findOrientationName((ListBase *)arg, name) != NULL; } static void uniqueOrientationName(ListBase *lb, char *name) { BLI_uniquename_cb(uniqueOrientationNameCheck, lb, CTX_DATA_(BLF_I18NCONTEXT_ID_SCENE, "Space"), '.', name, sizeof(((TransformOrientation *)NULL)->name)); } static TransformOrientation *createViewSpace(bContext *C, ReportList *UNUSED(reports), const char *name, const bool overwrite) { RegionView3D *rv3d = CTX_wm_region_view3d(C); float mat[3][3]; if (!rv3d) return NULL; copy_m3_m4(mat, rv3d->viewinv); normalize_m3(mat); if (name[0] == 0) { View3D *v3d = CTX_wm_view3d(C); if (rv3d->persp == RV3D_CAMOB && v3d->camera) { /* If an object is used as camera, then this space is the same as object space! */ name = v3d->camera->id.name + 2; } else { name = "Custom View"; } } return addMatrixSpace(C, mat, name, overwrite); } static TransformOrientation *createObjectSpace(bContext *C, ReportList *UNUSED(reports), const char *name, const bool overwrite) { Base *base = CTX_data_active_base(C); Object *ob; float mat[3][3]; if (base == NULL) return NULL; ob = base->object; copy_m3_m4(mat, ob->obmat); normalize_m3(mat); /* use object name if no name is given */ if (name[0] == 0) { name = ob->id.name + 2; } return addMatrixSpace(C, mat, name, overwrite); } static TransformOrientation *createBoneSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite) { float mat[3][3]; float normal[3], plane[3]; getTransformOrientation(C, normal, plane, 0); if (createSpaceNormalTangent(mat, normal, plane) == 0) { BKE_reports_prepend(reports, "Cannot use zero-length bone"); return NULL; } if (name[0] == 0) { name = "Bone"; } return addMatrixSpace(C, mat, name, overwrite); } static TransformOrientation *createCurveSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite) { float mat[3][3]; float normal[3], plane[3]; getTransformOrientation(C, normal, plane, 0); if (createSpaceNormalTangent(mat, normal, plane) == 0) { BKE_reports_prepend(reports, "Cannot use zero-length curve"); return NULL; } if (name[0] == 0) { name = "Curve"; } return addMatrixSpace(C, mat, name, overwrite); } static TransformOrientation *createMeshSpace(bContext *C, ReportList *reports, const char *name, const bool overwrite) { float mat[3][3]; float normal[3], plane[3]; int type; type = getTransformOrientation(C, normal, plane, 0); switch (type) { case ORIENTATION_VERT: if (createSpaceNormal(mat, normal) == 0) { BKE_reports_prepend(reports, "Cannot use vertex with zero-length normal"); return NULL; } if (name[0] == 0) { name = "Vertex"; } break; case ORIENTATION_EDGE: if (createSpaceNormalTangent(mat, normal, plane) == 0) { BKE_reports_prepend(reports, "Cannot use zero-length edge"); return NULL; } if (name[0] == 0) { name = "Edge"; } break; case ORIENTATION_FACE: if (createSpaceNormalTangent(mat, normal, plane) == 0) { BKE_reports_prepend(reports, "Cannot use zero-area face"); return NULL; } if (name[0] == 0) { name = "Face"; } break; default: return NULL; } return addMatrixSpace(C, mat, name, overwrite); } bool createSpaceNormal(float mat[3][3], const float normal[3]) { float tangent[3] = {0.0f, 0.0f, 1.0f}; copy_v3_v3(mat[2], normal); if (normalize_v3(mat[2]) == 0.0f) { return false; /* error return */ } cross_v3_v3v3(mat[0], mat[2], tangent); if (is_zero_v3(mat[0])) { tangent[0] = 1.0f; tangent[1] = tangent[2] = 0.0f; cross_v3_v3v3(mat[0], tangent, mat[2]); } cross_v3_v3v3(mat[1], mat[2], mat[0]); normalize_m3(mat); return true; } /** * \note To recreate an orientation from the matrix: * - (plane == mat[1]) * - (normal == mat[2]) */ bool createSpaceNormalTangent(float mat[3][3], const float normal[3], const float tangent[3]) { if (normalize_v3_v3(mat[2], normal) == 0.0f) { return false; /* error return */ } /* negate so we can use values from the matrix as input */ negate_v3_v3(mat[1], tangent); /* preempt zero length tangent from causing trouble */ if (is_zero_v3(mat[1])) { mat[1][2] = 1.0f; } cross_v3_v3v3(mat[0], mat[2], mat[1]); if (normalize_v3(mat[0]) == 0.0f) { return false; /* error return */ } cross_v3_v3v3(mat[1], mat[2], mat[0]); normalize_v3(mat[1]); /* final matrix must be normalized, do inline */ // normalize_m3(mat); return true; } void BIF_createTransformOrientation(bContext *C, ReportList *reports, const char *name, const bool use_view, const bool activate, const bool overwrite) { TransformOrientation *ts = NULL; if (use_view) { ts = createViewSpace(C, reports, name, overwrite); } else { Object *obedit = CTX_data_edit_object(C); Object *ob = CTX_data_active_object(C); if (obedit) { if (obedit->type == OB_MESH) ts = createMeshSpace(C, reports, name, overwrite); else if (obedit->type == OB_ARMATURE) ts = createBoneSpace(C, reports, name, overwrite); else if (obedit->type == OB_CURVE) ts = createCurveSpace(C, reports, name, overwrite); } else if (ob && (ob->mode & OB_MODE_POSE)) { ts = createBoneSpace(C, reports, name, overwrite); } else { ts = createObjectSpace(C, reports, name, overwrite); } } if (activate && ts != NULL) { BIF_selectTransformOrientation(C, ts); } } 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; char name_unique[sizeof(ts->name)]; if (overwrite) { ts = findOrientationName(transform_spaces, name); } else { BLI_strncpy(name_unique, name, sizeof(name_unique)); uniqueOrientationName(transform_spaces, 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_strncpy(ts->name, name, sizeof(ts->name)); } /* copy matrix into transform space */ copy_m3_m3(ts->mat, mat); return ts; } 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); } } 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); } } void BIF_selectTransformOrientation(bContext *C, TransformOrientation *target) { ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; const int i = BLI_findindex(transform_spaces, target); if (i != -1) { View3D *v3d = CTX_wm_view3d(C); v3d->twmode = V3D_MANIP_CUSTOM + i; } } void BIF_selectTransformOrientationValue(bContext *C, int orientation) { View3D *v3d = CTX_wm_view3d(C); if (v3d) /* currently using generic poll */ v3d->twmode = orientation; } int BIF_countTransformOrientation(const bContext *C) { ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; return BLI_countlist(transform_spaces); } bool applyTransformOrientation(const bContext *C, float mat[3][3], char *r_name) { View3D *v3d = CTX_wm_view3d(C); int selected_index = (v3d->twmode - V3D_MANIP_CUSTOM); ListBase *transform_spaces = &CTX_data_scene(C)->transform_spaces; TransformOrientation *ts = BLI_findlink(transform_spaces, selected_index); BLI_assert(selected_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; } } static int count_bone_select(bArmature *arm, ListBase *lb, const bool do_it) { Bone *bone; bool do_next; int total = 0; for (bone = lb->first; bone; bone = bone->next) { bone->flag &= ~BONE_TRANSFORM; do_next = do_it; if (do_it) { if (bone->layer & arm->layer) { if (bone->flag & BONE_SELECTED) { bone->flag |= BONE_TRANSFORM; total++; /* no transform on children if one parent bone is selected */ do_next = false; } } } total += count_bone_select(arm, &bone->childbase, do_next); } return total; } void initTransformOrientation(bContext *C, TransInfo *t) { View3D *v3d = CTX_wm_view3d(C); Object *ob = CTX_data_active_object(C); Object *obedit = CTX_data_active_object(C); switch (t->current_orientation) { case V3D_MANIP_GLOBAL: unit_m3(t->spacemtx); BLI_strncpy(t->spacename, IFACE_("global"), sizeof(t->spacename)); break; case V3D_MANIP_GIMBAL: unit_m3(t->spacemtx); if (gimbal_axis(ob, t->spacemtx)) { BLI_strncpy(t->spacename, IFACE_("gimbal"), sizeof(t->spacename)); break; } /* fall-through */ /* no gimbal fallthrough to normal */ case V3D_MANIP_NORMAL: if (obedit || (ob && ob->mode & OB_MODE_POSE)) { BLI_strncpy(t->spacename, IFACE_("normal"), sizeof(t->spacename)); ED_getTransformOrientationMatrix(C, t->spacemtx, (v3d->around == V3D_ACTIVE)); break; } /* fall-through */ /* we define 'normal' as 'local' in Object mode */ case V3D_MANIP_LOCAL: BLI_strncpy(t->spacename, IFACE_("local"), sizeof(t->spacename)); if (ob) { copy_m3_m4(t->spacemtx, ob->obmat); normalize_m3(t->spacemtx); } else { unit_m3(t->spacemtx); } break; case V3D_MANIP_VIEW: if (t->ar->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = t->ar->regiondata; float mat[3][3]; BLI_strncpy(t->spacename, IFACE_("view"), sizeof(t->spacename)); copy_m3_m4(mat, rv3d->viewinv); normalize_m3(mat); copy_m3_m3(t->spacemtx, mat); } else { unit_m3(t->spacemtx); } break; default: /* V3D_MANIP_CUSTOM */ if (applyTransformOrientation(C, t->spacemtx, t->spacename)) { /* pass */ } else { unit_m3(t->spacemtx); } break; } } /** * utility function - get first n, selected vert/edge/faces */ static unsigned int bm_mesh_elems_select_get_n__internal( BMesh *bm, BMElem **elems, const unsigned int n, const BMIterType itype, const char htype) { BMIter iter; BMElem *ele; unsigned int i; BLI_assert(ELEM(htype, BM_VERT, BM_EDGE, BM_FACE)); BLI_assert(ELEM(itype, BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH)); if (!BLI_listbase_is_empty(&bm->selected)) { /* quick check */ BMEditSelection *ese; i = 0; for (ese = bm->selected.last; ese; ese = ese->prev) { /* shouldn't need this check */ if (BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) { /* only use contiguous selection */ if (ese->htype != htype) { i = 0; break; } elems[i++] = ese->ele; if (n == i) { break; } } else { BLI_assert(0); } } if (i == 0) { /* pass */ } else { return i; } } i = 0; BM_ITER_MESH (ele, &iter, bm, itype) { BLI_assert(ele->head.htype == htype); if (BM_elem_flag_test(ele, BM_ELEM_SELECT)) { elems[i++] = ele; if (n == i) { break; } } } return i; } static unsigned int bm_mesh_verts_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n) { return bm_mesh_elems_select_get_n__internal( bm, (BMElem **)elems, min_ii(n, bm->totvertsel), BM_VERTS_OF_MESH, BM_VERT); } static unsigned int bm_mesh_edges_select_get_n(BMesh *bm, BMEdge **elems, const unsigned int n) { return bm_mesh_elems_select_get_n__internal( bm, (BMElem **)elems, min_ii(n, bm->totedgesel), BM_EDGES_OF_MESH, BM_EDGE); } #if 0 static unsigned int bm_mesh_faces_select_get_n(BMesh *bm, BMVert **elems, const unsigned int n) { return bm_mesh_elems_select_get_n__internal( bm, (BMElem **)elems, min_ii(n, bm->totfacesel), BM_FACES_OF_MESH, BM_FACE); } #endif int getTransformOrientation(const bContext *C, float normal[3], float plane[3], const bool activeOnly) { Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); Object *obedit = CTX_data_edit_object(C); Base *base; Object *ob = OBACT; int result = ORIENTATION_NONE; zero_v3(normal); zero_v3(plane); if (obedit) { float imat[3][3], mat[3][3]; /* we need the transpose of the inverse for a normal... */ copy_m3_m4(imat, ob->obmat); invert_m3_m3(mat, imat); transpose_m3(mat); ob = obedit; if (ob->type == OB_MESH) { BMEditMesh *em = BKE_editmesh_from_object(ob); BMEditSelection ese; float vec[3] = {0, 0, 0}; /* USE LAST SELECTED WITH ACTIVE */ if (activeOnly && BM_select_history_active_get(em->bm, &ese)) { BM_editselection_normal(&ese, normal); BM_editselection_plane(&ese, plane); switch (ese.htype) { case BM_VERT: result = ORIENTATION_VERT; break; case BM_EDGE: result = ORIENTATION_EDGE; break; case BM_FACE: result = ORIENTATION_FACE; break; } } else { if (em->bm->totfacesel >= 1) { BMFace *efa; BMIter iter; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { BM_face_calc_plane(efa, vec); add_v3_v3(normal, efa->no); add_v3_v3(plane, vec); } } result = ORIENTATION_FACE; } else if (em->bm->totvertsel == 3) { BMVert *v_tri[3]; if (bm_mesh_verts_select_get_n(em->bm, v_tri, 3) == 3) { BMEdge *e = NULL; float no_test[3]; normal_tri_v3(normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); /* check if the normal is pointing opposite to vert normals */ no_test[0] = v_tri[0]->no[0] + v_tri[1]->no[0] + v_tri[2]->no[0]; no_test[1] = v_tri[0]->no[1] + v_tri[1]->no[1] + v_tri[2]->no[1]; no_test[2] = v_tri[0]->no[2] + v_tri[1]->no[2] + v_tri[2]->no[2]; if (dot_v3v3(no_test, normal) < 0.0f) { negate_v3(normal); } if (em->bm->totedgesel >= 1) { /* find an edge thats apart of v_tri (no need to search all edges) */ float e_length; int j; for (j = 0; j < 3; j++) { BMEdge *e_test = BM_edge_exists(v_tri[j], v_tri[(j + 1) % 3]); if (e_test && BM_elem_flag_test(e_test, BM_ELEM_SELECT)) { const float e_test_length = BM_edge_calc_length_squared(e_test); if ((e == NULL) || (e_length < e_test_length)) { e = e_test; e_length = e_test_length; } } } } if (e) { BMVert *v_pair[2]; if (BM_edge_is_boundary(e)) { BM_edge_ordered_verts(e, &v_pair[0], &v_pair[1]); } else { v_pair[0] = e->v1; v_pair[1] = e->v2; } sub_v3_v3v3(plane, v_pair[0]->co, v_pair[1]->co); } else { BM_vert_tri_calc_plane(v_tri, plane); } } else { BLI_assert(0); } result = ORIENTATION_FACE; } else if (em->bm->totedgesel == 1 || em->bm->totvertsel == 2) { BMVert *v_pair[2] = {NULL, NULL}; BMEdge *eed = NULL; if (em->bm->totedgesel == 1) { if (bm_mesh_edges_select_get_n(em->bm, &eed, 1) == 1) { v_pair[0] = eed->v1; v_pair[1] = eed->v2; } } else { BLI_assert(em->bm->totvertsel == 2); bm_mesh_verts_select_get_n(em->bm, v_pair, 2); } /* should never fail */ if (LIKELY(v_pair[0] && v_pair[1])) { bool v_pair_swap = false; /* Logic explained: * * - Edges and vert-pairs treated the same way. * - Point the Z axis along the edge vector (towards the active vertex). * - Point the Y axis outwards (the same direction as the normals). * * Note that this is at odds a little with face select (and 3 vertices) * which point the Z axis along the normal, however in both cases Z is the dominant axis. */ /* be deterministic where possible and ensure v_pair[0] is active */ if (BM_mesh_active_vert_get(em->bm) == v_pair[1]) { v_pair_swap = true; } else if (eed && BM_edge_is_boundary(eed)) { /* pradictable direction for boundary edges */ if (eed->l->v != v_pair[0]) { v_pair_swap = true; } } if (v_pair_swap) { SWAP(BMVert *, v_pair[0], v_pair[1]); } add_v3_v3v3(plane, v_pair[0]->no, v_pair[1]->no); sub_v3_v3v3(normal, v_pair[0]->co, v_pair[1]->co); /* flip the plane normal so we point outwards */ negate_v3(plane); } result = ORIENTATION_EDGE; } else if (em->bm->totvertsel == 1) { BMVert *v = NULL; if (bm_mesh_verts_select_get_n(em->bm, &v, 1) == 1) { copy_v3_v3(normal, v->no); if (BM_vert_is_edge_pair(v)) { bool v_pair_swap = false; BMEdge *e_pair[2] = {v->e, BM_DISK_EDGE_NEXT(v->e, v)}; BMVert *v_pair[2] = {BM_edge_other_vert(e_pair[0], v), BM_edge_other_vert(e_pair[1], v)}; float dir_pair[2][3]; if (BM_edge_is_boundary(e_pair[0])) { if (e_pair[0]->l->v != v) { v_pair_swap = true; } } else { if (BM_edge_calc_length_squared(e_pair[0]) < BM_edge_calc_length_squared(e_pair[1])) { v_pair_swap = true; } } if (v_pair_swap) { SWAP(BMVert *, v_pair[0], v_pair[1]); } sub_v3_v3v3(dir_pair[0], v->co, v_pair[0]->co); sub_v3_v3v3(dir_pair[1], v_pair[1]->co, v->co); normalize_v3(dir_pair[0]); normalize_v3(dir_pair[1]); add_v3_v3v3(plane, dir_pair[0], dir_pair[1]); } } if (is_zero_v3(plane)) { result = ORIENTATION_VERT; } else { result = ORIENTATION_EDGE; } } else if (em->bm->totvertsel > 3) { BMIter iter; BMVert *v; zero_v3(normal); BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { add_v3_v3(normal, v->no); } } normalize_v3(normal); result = ORIENTATION_VERT; } } /* not needed but this matches 2.68 and older behavior */ negate_v3(plane); } /* end editmesh */ else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) { Curve *cu = obedit->data; Nurb *nu = NULL; BezTriple *bezt = NULL; int a; ListBase *nurbs = BKE_curve_editNurbs_get(cu); if (activeOnly && BKE_curve_nurb_vert_active_get(cu, &nu, (void *)&bezt)) { if (nu->type == CU_BEZIER) { BKE_nurb_bezt_calc_normal(nu, bezt, normal); BKE_nurb_bezt_calc_plane(nu, bezt, plane); } } else { const bool use_handle = (cu->drawflag & CU_HIDE_HANDLES) == 0; for (nu = nurbs->first; nu; nu = nu->next) { /* only bezier has a normal */ if (nu->type == CU_BEZIER) { bezt = nu->bezt; a = nu->pntsu; while (a--) { short flag = 0; #define SEL_F1 (1 << 0) #define SEL_F2 (1 << 1) #define SEL_F3 (1 << 2) if (use_handle) { if (bezt->f1 & SELECT) flag |= SEL_F1; if (bezt->f2 & SELECT) flag |= SEL_F2; if (bezt->f3 & SELECT) flag |= SEL_F3; } else { flag = (bezt->f2 & SELECT) ? (SEL_F1 | SEL_F2 | SEL_F3) : 0; } /* exception */ if (flag) { float tvec[3]; if ((v3d->around == V3D_LOCAL) || ELEM(flag, SEL_F2, SEL_F1 | SEL_F3, SEL_F1 | SEL_F2 | SEL_F3)) { BKE_nurb_bezt_calc_normal(nu, bezt, tvec); add_v3_v3(normal, tvec); } else { /* ignore bezt->f2 in this case */ if (flag & SEL_F1) { sub_v3_v3v3(tvec, bezt->vec[0], bezt->vec[1]); normalize_v3(tvec); add_v3_v3(normal, tvec); } if (flag & SEL_F3) { sub_v3_v3v3(tvec, bezt->vec[1], bezt->vec[2]); normalize_v3(tvec); add_v3_v3(normal, tvec); } } BKE_nurb_bezt_calc_plane(nu, bezt, tvec); add_v3_v3(plane, tvec); } #undef SEL_F1 #undef SEL_F2 #undef SEL_F3 bezt++; } } } } if (!is_zero_v3(normal)) { result = ORIENTATION_FACE; } } else if (obedit->type == OB_MBALL) { MetaBall *mb = obedit->data; MetaElem *ml; bool ok = false; float tmat[3][3]; if (activeOnly && (ml = mb->lastelem)) { quat_to_mat3(tmat, ml->quat); add_v3_v3(normal, tmat[2]); add_v3_v3(plane, tmat[1]); ok = true; } else { for (ml = mb->editelems->first; ml; ml = ml->next) { if (ml->flag & SELECT) { quat_to_mat3(tmat, ml->quat); add_v3_v3(normal, tmat[2]); add_v3_v3(plane, tmat[1]); ok = true; } } } if (ok) { if (!is_zero_v3(plane)) { result = ORIENTATION_FACE; } } } else if (obedit->type == OB_ARMATURE) { bArmature *arm = obedit->data; EditBone *ebone; bool ok = false; float tmat[3][3]; if (activeOnly && (ebone = arm->act_edbone)) { ED_armature_ebone_to_mat3(ebone, tmat); add_v3_v3(normal, tmat[2]); add_v3_v3(plane, tmat[1]); ok = true; } else { for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { if (arm->layer & ebone->layer) { if (ebone->flag & BONE_SELECTED) { ED_armature_ebone_to_mat3(ebone, tmat); add_v3_v3(normal, tmat[2]); add_v3_v3(plane, tmat[1]); ok = true; } } } } if (ok) { if (!is_zero_v3(plane)) { result = ORIENTATION_EDGE; } } } /* Vectors from edges don't need the special transpose inverse multiplication */ if (result == ORIENTATION_EDGE) { mul_mat3_m4_v3(ob->obmat, normal); mul_mat3_m4_v3(ob->obmat, plane); } else { mul_m3_v3(mat, normal); mul_m3_v3(mat, plane); } } else if (ob && (ob->mode & OB_MODE_POSE)) { bArmature *arm = ob->data; bPoseChannel *pchan; float imat[3][3], mat[3][3]; bool ok = false; if (activeOnly && (pchan = BKE_pose_channel_active(ob))) { add_v3_v3(normal, pchan->pose_mat[2]); add_v3_v3(plane, pchan->pose_mat[1]); ok = true; } else { int totsel; totsel = count_bone_select(arm, &arm->bonebase, true); if (totsel) { /* use channels to get stats */ for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone && pchan->bone->flag & BONE_TRANSFORM) { add_v3_v3(normal, pchan->pose_mat[2]); add_v3_v3(plane, pchan->pose_mat[1]); } } ok = true; } } /* use for both active & all */ if (ok) { /* we need the transpose of the inverse for a normal... */ copy_m3_m4(imat, ob->obmat); invert_m3_m3(mat, imat); transpose_m3(mat); mul_m3_v3(mat, normal); mul_m3_v3(mat, plane); result = ORIENTATION_EDGE; } } else if (ob && (ob->mode & (OB_MODE_ALL_PAINT | OB_MODE_PARTICLE_EDIT))) { /* pass */ } else { /* we need the one selected object, if its not active */ ob = OBACT; if (ob && (ob->flag & SELECT)) { /* pass */ } else { /* first selected */ ob = NULL; for (base = scene->base.first; base; base = base->next) { if (TESTBASELIB(v3d, base)) { ob = base->object; break; } } } if (ob) { copy_v3_v3(normal, ob->obmat[2]); copy_v3_v3(plane, ob->obmat[1]); } result = ORIENTATION_NORMAL; } return result; } void ED_getTransformOrientationMatrix(const bContext *C, float orientation_mat[3][3], const bool activeOnly) { float normal[3] = {0.0, 0.0, 0.0}; float plane[3] = {0.0, 0.0, 0.0}; int type; type = getTransformOrientation(C, normal, plane, activeOnly); switch (type) { case ORIENTATION_NORMAL: if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) { type = ORIENTATION_NONE; } break; case ORIENTATION_VERT: if (createSpaceNormal(orientation_mat, normal) == 0) { type = ORIENTATION_NONE; } break; case ORIENTATION_EDGE: if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) { type = ORIENTATION_NONE; } break; case ORIENTATION_FACE: if (createSpaceNormalTangent(orientation_mat, normal, plane) == 0) { type = ORIENTATION_NONE; } break; } if (type == ORIENTATION_NONE) { unit_m3(orientation_mat); } }