Index: source/blender/bmesh/intern/bmesh_mesh.c =================================================================== --- source/blender/bmesh/intern/bmesh_mesh.c (revision 55557) +++ source/blender/bmesh/intern/bmesh_mesh.c (working copy) @@ -286,7 +286,7 @@ /* Zero out vertex normals */ BM_ITER_MESH (v, &verts, bm, BM_VERTS_OF_MESH) { - if (skip_hidden && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + if ((skip_hidden && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) || v->lock_no) continue; zero_v3(v->no); @@ -323,6 +323,9 @@ float dotprod; float fac; + if (l->v->lock_no) + continue; + /* calculate the dot product of the two edges that * meet at the loop's vertex */ e1diff = edgevec[BM_elem_index_get(l->prev->e)]; @@ -345,7 +348,7 @@ /* normalize the accumulated vertex normals */ BM_ITER_MESH (v, &verts, bm, BM_VERTS_OF_MESH) { - if (skip_hidden && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) + if ((skip_hidden && BM_elem_flag_test(v, BM_ELEM_HIDDEN)) || v->lock_no) continue; if (UNLIKELY(normalize_v3(v->no) == 0.0f)) { Index: source/blender/bmesh/intern/bmesh_mesh_conv.c =================================================================== --- source/blender/bmesh/intern/bmesh_mesh_conv.c (revision 55557) +++ source/blender/bmesh/intern/bmesh_mesh_conv.c (working copy) @@ -287,6 +287,7 @@ } normal_short_to_float_v3(v->no, mvert->no); + v->lock_no = (mvert->flag & ME_VERT_NORMAL_LOCKED) ? true : false; /* Copy Custom Data */ CustomData_to_bmesh_block(&me->vdata, &bm->vdata, i, &v->head.data, true); Index: source/blender/bmesh/intern/bmesh_construct.c =================================================================== --- source/blender/bmesh/intern/bmesh_construct.c (revision 55557) +++ source/blender/bmesh/intern/bmesh_construct.c (working copy) @@ -1023,7 +1023,8 @@ const char hflag = eve->head.hflag; return ( ((hflag & BM_ELEM_SELECT) ? SELECT : 0) | - ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) + ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | + ((eve->lock_no) ? ME_VERT_NORMAL_LOCKED : 0 ) ); } Index: source/blender/bmesh/bmesh_class.h =================================================================== --- source/blender/bmesh/bmesh_class.h (revision 55557) +++ source/blender/bmesh/bmesh_class.h (working copy) @@ -91,7 +91,7 @@ typedef struct BMVert { BMHeader head; struct BMFlagLayer *oflags; /* keep after header, an array of flags, mostly used by the operator stack */ - + bool lock_no; float co[3]; float no[3]; struct BMEdge *e; Index: source/blender/makesrna/intern/rna_mesh.c =================================================================== --- source/blender/makesrna/intern/rna_mesh.c (revision 55557) +++ source/blender/makesrna/intern/rna_mesh.c (working copy) @@ -1507,6 +1507,10 @@ RNA_def_property_float_funcs(prop, "rna_MeshVertex_normal_get", "rna_MeshVertex_normal_set", NULL); RNA_def_property_ui_text(prop, "Normal", "Vertex Normal"); + prop = RNA_def_property(srna, "lock_normal", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_VERT_NORMAL_LOCKED); + RNA_def_property_ui_text(prop, "Lock normal", ""); + prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SELECT); RNA_def_property_ui_text(prop, "Select", ""); Index: source/blender/editors/mesh/mesh_ops.c =================================================================== --- source/blender/editors/mesh/mesh_ops.c (revision 55557) +++ source/blender/editors/mesh/mesh_ops.c (working copy) @@ -161,6 +161,8 @@ WM_operatortype_append(MESH_OT_wireframe); WM_operatortype_append(MESH_OT_edge_split); + WM_operatortype_append(MESH_OT_normals_lock); + #ifdef WITH_BULLET WM_operatortype_append(MESH_OT_convex_hull); #endif Index: source/blender/editors/mesh/mesh_intern.h =================================================================== --- source/blender/editors/mesh/mesh_intern.h (revision 55557) +++ source/blender/editors/mesh/mesh_intern.h (working copy) @@ -139,6 +139,7 @@ void MESH_OT_solidify(struct wmOperatorType *ot); void MESH_OT_select_nth(struct wmOperatorType *ot); void MESH_OT_select_next_loop(struct wmOperatorType *ot); +void MESH_OT_normals_lock(struct wmOperatorType *ot); extern struct EnumPropertyItem *corner_type_items; Index: source/blender/editors/mesh/editmesh_tools.c =================================================================== --- source/blender/editors/mesh/editmesh_tools.c (revision 55557) +++ source/blender/editors/mesh/editmesh_tools.c (working copy) @@ -1539,7 +1539,7 @@ static int edbm_flip_normals_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BMEdit_FromObject(obedit); + BMEditMesh *em = BMEdit_FromObject(obedit); if (!EDBM_op_callf(em, op, "reverse_faces faces=%hf", BM_ELEM_SELECT)) return OPERATOR_CANCELLED; @@ -1564,6 +1564,65 @@ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +#define NO_LOCK 1 +#define NO_UNLOCK 2 +#define NO_TOGGLE 3 + +static int edbm_normals_lock_exec(bContext *C, wmOperator *op) +{ + Object* obedit = CTX_data_edit_object(C); + BMEditMesh *em = BMEdit_FromObject(obedit); + BMVert* eve; + BMIter iter; + const int mode = RNA_enum_get(op->ptr, "mode"); + + BM_ITER_MESH(eve, &iter, em->bm, BM_VERTS_OF_MESH) + { + if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) + continue; + switch (mode) + { + case NO_LOCK: + eve->lock_no = true; + break; + case NO_UNLOCK: + eve->lock_no = false; + break; + case NO_TOGGLE: + eve->lock_no = !eve->lock_no; + break; + } + } + + return OPERATOR_FINISHED; +} + +static EnumPropertyItem normal_lock_modes[] = { + {NO_LOCK, "LOCK", 0, "Lock", ""}, + {NO_UNLOCK, "UNLOCK", 0, "Unlock", ""}, + {NO_TOGGLE, "TOGGLE", 0, "Toggle", ""}, + {0, NULL, 0, NULL, NULL} +}; + +void MESH_OT_normals_lock(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Lock/Unlock Normals"; + ot->idname = "MESH_OT_normals_lock"; + + /* api callbacks */ + ot->exec = edbm_normals_lock_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", normal_lock_modes, 1, "Mode", ""); +} + + + static const EnumPropertyItem direction_items[] = { {false, "CW", 0, "Clockwise", ""}, {true, "CCW", 0, "Counter Clockwise", ""}, Index: source/blender/editors/transform/transform_generics.c =================================================================== --- source/blender/editors/transform/transform_generics.c (revision 55557) +++ source/blender/editors/transform/transform_generics.c (working copy) @@ -1397,6 +1397,9 @@ if (td->val) { *td->val = td->ival; } + if (td->norm) { + copy_v3_v3(td->norm, td->inorm); + } if (td->ext && (td->flag & TD_NO_EXT) == 0) { if (td->ext->rot) { Index: source/blender/editors/transform/transform.c =================================================================== --- source/blender/editors/transform/transform.c (revision 55557) +++ source/blender/editors/transform/transform.c (working copy) @@ -3398,7 +3398,7 @@ t->idx_max = 0; t->num.idx_max = 0; t->snap[0] = 0.0f; - t->snap[1] = (float)((5.0 / 180) * M_PI); + t->snap[1] = (float)((5.0 / 180) * M_PI); t->snap[2] = t->snap[1] * 0.2f; t->num.increment = 1.0f; @@ -3424,24 +3424,35 @@ { center = td->center; } + else if (around == V3D_LOCAL) + { + center = td->iloc; + } else { center = t->center; } - if (t->flag & T_POINTS) { - mul_m3_m3m3(totmat, mat, td->mtx); + if (t->flag & T_POINTS) { + + /* Prepare object-space rotation matrix */ + mul_m3_m3m3(totmat, mat, td->mtx); mul_m3_m3m3(smat, td->smtx, totmat); - + + /* Rotate the vector between initial location and the center of the rotation */ sub_v3_v3v3(vec, td->iloc, center); mul_m3_v3(smat, vec); - - add_v3_v3v3(td->loc, vec, center); - + add_v3_v3v3(td->loc, vec, center); + + /* Handle Transform Locks */ sub_v3_v3v3(vec, td->loc, td->iloc); protectedTransBits(td->protectflag, vec); add_v3_v3v3(td->loc, td->iloc, vec); - + /* Rotate locked normals */ + copy_v3_v3(vec, td->inorm); + mul_m3_v3(smat, vec); + copy_v3_v3(td->norm, vec); + if (td->flag & TD_USEQUAT) { mul_serie_m3(fmat, td->mtx, mat, td->smtx, NULL, NULL, NULL, NULL, NULL); mat3_to_quat(quat, fmat); // Actual transform @@ -3651,7 +3662,7 @@ if (td->flag & TD_SKIP) continue; - + if (t->con.applyRot) { t->con.applyRot(t, td, axis, NULL); vec_rot_to_mat3(mat, axis, angle * td->factor); @@ -3659,8 +3670,8 @@ else if (t->flag & T_PROP_EDIT) { vec_rot_to_mat3(mat, axis, angle * td->factor); } - - ElementRotation(t, td, mat, t->around); + + ElementRotation(t, td, mat, t->around); } } @@ -3763,7 +3774,7 @@ mul_m3_m3m3(mat, smat, totmat); } - ElementRotation(t, td, mat, t->around); + ElementRotation(t, td, mat, t->around); } } Index: source/blender/editors/transform/transform.h =================================================================== --- source/blender/editors/transform/transform.h (revision 55557) +++ source/blender/editors/transform/transform.h (working copy) @@ -256,7 +256,9 @@ float center[3]; /* Individual data center */ float mtx[3][3]; /* Transformation matrix from data space to global space */ float smtx[3][3]; /* Transformation matrix from global space to data space */ - float axismtx[3][3];/* Axis orientation matrix of the data */ + float axismtx[3][3];/* Axis orientation matrix of the data */ + float *norm; /* If set, normal of vertex */ + float inorm[3]; /* If set, initial normal of vertex */ struct Object *ob; struct bConstraint *con; /* for objects/bones, the first constraint in its constraint stack */ TransDataExtension *ext; /* for objects, poses. 1 single malloc per TransInfo! */ Index: source/blender/editors/transform/transform_conversions.c =================================================================== --- source/blender/editors/transform/transform_conversions.c (revision 55557) +++ source/blender/editors/transform/transform_conversions.c (working copy) @@ -1859,7 +1859,7 @@ } /* loop-in-a-loop I know, but we need it! (ton) */ -static void get_face_center(float r_cent[3], BMVert *eve) +static void get_face_center(float cent_r[3], BMVert *eve) { BMFace *efa; @@ -1867,20 +1867,20 @@ BM_ITER_ELEM (efa, &iter, eve, BM_FACES_OF_VERT) { if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_face_calc_center_mean(efa, r_cent); + BM_face_calc_center_mean(efa, cent_r); break; } } } -static void get_edge_center(float r_cent[3], BMVert *eve) +static void get_edge_center(float cent_r[3], BMVert *eve) { BMEdge *eed; BMIter iter; BM_ITER_ELEM (eed, &iter, eve, BM_EDGES_OF_VERT) { if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) { - mid_v3_v3v3(r_cent, eed->v1->co, eed->v2->co); + mid_v3_v3v3(cent_r, eed->v1->co, eed->v2->co); break; } } @@ -1906,7 +1906,7 @@ } copy_v3_v3(td->iloc, td->loc); - // Setting normals + // Setting normals (felix: needed at all?) copy_v3_v3(td->axismtx[2], eve->no); td->axismtx[0][0] = td->axismtx[0][1] = @@ -1915,6 +1915,10 @@ td->axismtx[1][1] = td->axismtx[1][2] = 0.0f; + /* Set normals */ + td->norm = eve->no; + copy_v3_v3(td->inorm, eve->no); + td->ext = NULL; td->val = NULL; td->extra = NULL; @@ -5742,10 +5746,10 @@ /* * motion tracking * */ -enum transDataTracking_Mode { +enum { transDataTracking_ModeTracks = 0, - transDataTracking_ModeCurves = 1 -}; + transDataTracking_ModeCurves = 1, +} transDataTracking_Mode; typedef struct TransDataTracking { int mode, flag; Index: source/blender/editors/space_view3d/drawobject.c =================================================================== --- source/blender/editors/space_view3d/drawobject.c (revision 55557) +++ source/blender/editors/space_view3d/drawobject.c (working copy) @@ -2074,6 +2074,11 @@ if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { float no[3], n[3]; + if (eve->lock_no) + UI_ThemeColor(TH_REDALERT); // TODO: Add to Theme (felix) + else + UI_ThemeColor(TH_VNORMAL); + if (no_f) { copy_v3_v3(no, no_f); } @@ -2998,7 +3003,6 @@ draw_dm_face_normals(em, scene, ob, cageDM); } if (me->drawflag & ME_DRAW_VNORMALS) { - UI_ThemeColor(TH_VNORMAL); draw_dm_vert_normals(em, scene, ob, cageDM); } Index: source/blender/makesdna/DNA_meshdata_types.h =================================================================== --- source/blender/makesdna/DNA_meshdata_types.h (revision 55557) +++ source/blender/makesdna/DNA_meshdata_types.h (working copy) @@ -283,6 +283,7 @@ #define ME_SPHERETEST 2 #define ME_VERT_TMP_TAG 4 #define ME_HIDE 16 +#define ME_VERT_NORMAL_LOCKED 32 #define ME_VERT_MERGED (1<<6) #define ME_VERT_PBVH_UPDATE (1<<7)