Split Normals I (1/5): basis for split normals (nearly nothing user-visible here):
authorBastien Montagne <montagne29@wanadoo.fr>
Sun, 13 Apr 2014 10:18:51 +0000 (12:18 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Sun, 13 Apr 2014 10:19:00 +0000 (12:19 +0200)
* Add a new calcLoopNormals function to DerivedMesh struct, and implement it for CDDM and CCGDM (subsurf).
  EditDerivedBMesh (edit mode DM) only gets a dummy one in this commit.
* Add a tessellated version of CD_LOOPNORMAL layer (CD_TESSLOOPNORMAL), with relevant code to handle it
  (tessellation, rna access, etc.).
* Change auto_smooth options of Mesh (angle now in radian internaly, and toggle is now used to enable/disable
  split normals in DM creation process). Note BI render code is not touched here, hence its behavior regarding
  this option is now incoherent, will be addressed in a separate commit.

Reviewers: campbellbarton

CC: brecht
Differential Revision: https://developer.blender.org/D365

17 files changed:
release/scripts/startup/bl_ui/space_view3d.py
source/blender/blenkernel/BKE_DerivedMesh.h
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_cdderivedmesh.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/cdderivedmesh.c
source/blender/blenkernel/intern/customdata.c
source/blender/blenkernel/intern/editderivedmesh.c
source/blender/blenkernel/intern/mesh_evaluate.c
source/blender/blenkernel/intern/subsurf_ccg.c
source/blender/blenloader/intern/versioning_270.c
source/blender/blenloader/intern/versioning_defaults.c
source/blender/editors/space_view3d/drawobject.c
source/blender/makesdna/DNA_customdata_types.h
source/blender/makesdna/DNA_mesh_types.h
source/blender/makesrna/intern/rna_mesh.c

index d43e6d1f697dcf7059575999ba339fc6a141de15..56f612abe3a3b430e7c2835dc12e760f0e420f7c 100644 (file)
@@ -2760,6 +2760,15 @@ class VIEW3D_PT_view3d_name(Panel):
                 row.label(text="", icon='BONE_DATA')
                 row.prop(bone, "name", text="")
 
+        elif ob.type == 'MESH':
+            me = ob.data
+            row = layout.row()
+            row.prop(me, "use_auto_smooth")
+            row = row.row()
+            if not me.use_auto_smooth:
+                row.active = False
+            row.prop(me, "auto_smooth_angle", text="")
+
 
 class VIEW3D_PT_view3d_display(Panel):
     bl_space_type = 'VIEW_3D'
@@ -2936,14 +2945,14 @@ class VIEW3D_PT_view3d_meshdisplay(Panel):
 
         col.separator()
         col.label(text="Normals:")
-        row = col.row()
+        row = col.row(align=True)
 
-        sub = row.row(align=True)
-        sub.prop(mesh, "show_normal_vertex", text="", icon='VERTEXSEL')
-        sub.prop(mesh, "show_normal_face", text="", icon='FACESEL')
+        row.prop(mesh, "show_normal_vertex", text="", icon='VERTEXSEL')
+        row.prop(mesh, "show_normal_loop", text="", icon='VERTEXSEL')
+        row.prop(mesh, "show_normal_face", text="", icon='FACESEL')
 
         sub = row.row(align=True)
-        sub.active = mesh.show_normal_vertex or mesh.show_normal_face
+        sub.active = mesh.show_normal_vertex or mesh.show_normal_face or mesh.show_normal_loop
         sub.prop(context.scene.tool_settings, "normal_size", text="Size")
 
         col.separator()
index 3afd7d851cbcf2b641f3aec5638ebb1131abfadd..5206f8bf2992505b4a12c468b5af90fbb1d6e073 100644 (file)
@@ -193,6 +193,9 @@ struct DerivedMesh {
        /** Calculate vert and face normals */
        void (*calcNormals)(DerivedMesh *dm);
 
+       /** Calculate loop (split) normals */
+       void (*calcLoopNormals)(DerivedMesh *dm, const float split_angle);
+
        /** Recalculates mesh tessellation */
        void (*recalcTessellation)(DerivedMesh *dm);
 
index 3eb2fabdd564a1351c0bce25b150be0904a52eb8..9a67284304cca8278a405bd82eb4808443a69bb5 100644 (file)
@@ -42,7 +42,7 @@ extern "C" {
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION         270
-#define BLENDER_SUBVERSION      1
+#define BLENDER_SUBVERSION      2
 /* 262 was the last editmesh release but it has compatibility code for bmesh data */
 #define BLENDER_MINVERSION      262
 #define BLENDER_MINSUBVERSION   0
index 4e6d69503c0a371dea01fbe665c2710105201f24..dffc2b665c22613825602ef6a5fbf7dfc5f2c981 100644 (file)
@@ -100,6 +100,8 @@ void CDDM_calc_normals_mapping(struct DerivedMesh *dm);
 void CDDM_calc_normals(struct DerivedMesh *dm);
 void CDDM_calc_normals_tessface(struct DerivedMesh *dm);
 
+void CDDM_calc_loop_normals(struct DerivedMesh *dm, const float split_angle);
+
 /* calculates edges for a CDDerivedMesh (from face data)
  * this completely replaces the current edge data in the DerivedMesh
  * builds edges from the tessellated face data.
index 9c57b7c9a712a4c111fcdee2d50585a8226f99cd..d6c3734fa2d735ba7807d60d597577d041cc87e1 100644 (file)
@@ -215,7 +215,7 @@ void BKE_mesh_loops_to_mface_corners(
         struct CustomData *pdata, unsigned int lindex[4], int findex,
         const int polyindex, const int mf_len,
         const int numTex, const int numCol,
-        const bool hasPCol, const bool hasOrigSpace);
+        const bool hasPCol, const bool hasOrigSpace, const bool hasLNor);
 void BKE_mesh_loops_to_tessdata(
         struct CustomData *fdata, struct CustomData *ldata, struct CustomData *pdata, struct MFace *mface,
         int *polyindices, unsigned int (*loopindices)[4], const int num_faces);
index 84b54ff2b84c9c1b689d208fca950cbdba783428..0c4e71448cd3a138e5e841c4e6019e3d090fdb07 100644 (file)
@@ -399,6 +399,11 @@ void DM_ensure_normals(DerivedMesh *dm)
        BLI_assert((dm->dirty & DM_DIRTY_NORMALS) == 0);
 }
 
+static void DM_calc_loop_normals(DerivedMesh *dm, float split_angle)
+{
+       dm->calcLoopNormals(dm, split_angle);
+}
+
 /* note: until all modifiers can take MPoly's as input,
  * use this at the start of modifiers  */
 void DM_ensure_tessface(DerivedMesh *dm)
@@ -453,7 +458,8 @@ void DM_update_tessface_data(DerivedMesh *dm)
        if (CustomData_has_layer(fdata, CD_MTFACE) ||
            CustomData_has_layer(fdata, CD_MCOL) ||
            CustomData_has_layer(fdata, CD_PREVIEW_MCOL) ||
-           CustomData_has_layer(fdata, CD_ORIGSPACE))
+           CustomData_has_layer(fdata, CD_ORIGSPACE) ||
+           CustomData_has_layer(fdata, CD_TESSLOOPNORMAL))
        {
                loopindex = MEM_mallocN(sizeof(*loopindex) * totface, __func__);
 
@@ -1471,6 +1477,9 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos
        /* XXX Same as above... For now, only weights preview in WPaint mode. */
        const bool do_mod_wmcol = do_init_wmcol;
 
+       const bool do_loop_normals = (me->flag & ME_AUTOSMOOTH);
+       const float loop_normals_split_angle = me->smoothresh;
+
        VirtualModifierData virtualModifierData;
 
        ModifierApplyFlag app_flags = useRenderParams ? MOD_APPLY_RENDER : 0;
@@ -1865,7 +1874,21 @@ static void mesh_calc_modifiers(Scene *scene, Object *ob, float (*inputVertexCos
                        add_orco_dm(ob, NULL, *deform_r, NULL, CD_ORCO);
        }
 
-       {
+       if (do_loop_normals) {
+               /* Compute loop normals (note: will compute poly and vert normals as well, if needed!) */
+               DM_calc_loop_normals(finaldm, loop_normals_split_angle);
+
+               if (finaldm->getNumTessFaces(finaldm) == 0) {
+                       finaldm->recalcTessellation(finaldm);
+               }
+               /* Even if tessellation is not needed, we have for sure modified loop normals layer! */
+               else {
+                       /* A tessellation already exists, it should always have a CD_ORIGINDEX. */
+                       BLI_assert(CustomData_has_layer(&finaldm->faceData, CD_ORIGINDEX));
+                       DM_update_tessface_data(finaldm);
+               }
+       }
+       else {
                /* calculating normals can re-calculate tessfaces in some cases */
 #if 0
                int num_tessface = finaldm->getNumTessFaces(finaldm);
@@ -1982,7 +2005,7 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D
        ModifierData *md, *previewmd = NULL;
        float (*deformedVerts)[3] = NULL;
        CustomDataMask mask, previewmask = 0, append_mask = 0;
-       DerivedMesh *dm, *orcodm = NULL;
+       DerivedMesh *dm = NULL, *orcodm = NULL;
        int i, numVerts = 0, cageIndex = modifiers_getCageIndex(scene, ob, NULL, 1);
        CDMaskLink *datamasks, *curr;
        int required_mode = eModifierMode_Realtime | eModifierMode_Editmode;
@@ -1998,13 +2021,15 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D
        const bool do_mod_wmcol = do_init_wmcol;
        VirtualModifierData virtualModifierData;
 
+       const bool do_loop_normals = (((Mesh *)(ob->data))->flag & ME_AUTOSMOOTH);
+       const float loop_normals_split_angle = ((Mesh *)(ob->data))->smoothresh;
+
        modifiers_clearErrors(ob);
 
        if (cage_r && cageIndex == -1) {
                *cage_r = getEditDerivedBMesh(em, ob, NULL);
        }
 
-       dm = NULL;
        md = modifiers_getVirtualModifierList(ob, &virtualModifierData);
 
        /* copied from mesh_calc_modifiers */
@@ -2212,6 +2237,14 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D
                        DM_update_statvis_color(scene, ob, *final_r);
        }
 
+       if (do_loop_normals) {
+               /* Compute loop normals */
+               DM_calc_loop_normals(*final_r, loop_normals_split_angle);
+               if (cage_r && *cage_r && (*cage_r != *final_r)) {
+                       DM_calc_loop_normals(*cage_r, loop_normals_split_angle);
+               }
+       }
+
        /* --- */
        /* BMESH_ONLY, ensure tessface's used for drawing,
         * but don't recalculate if the last modifier in the stack gives us tessfaces
@@ -2229,8 +2262,10 @@ static void editbmesh_calc_modifiers(Scene *scene, Object *ob, BMEditMesh *em, D
        }
        /* --- */
 
-       /* same as mesh_calc_modifiers */
-       dm_ensure_display_normals(*final_r);
+       /* same as mesh_calc_modifiers (if using loop normals, poly nors have already been computed). */
+       if (!do_loop_normals) {
+               dm_ensure_display_normals(*final_r);
+       }
 
        /* add an orco layer if needed */
        if (dataMask & CD_MASK_ORCO)
@@ -2542,7 +2577,8 @@ DMCoNo *mesh_get_mapped_verts_nors(Scene *scene, Object *ob)
 /* ******************* GLSL ******************** */
 
 typedef struct {
-       float *precomputedFaceNormals;
+       float (*precomputedFaceNormals)[3];
+       short (*precomputedLoopNormals)[4][3];
        MTFace *mtface;     /* texture coordinates */
        MFace *mface;       /* indices */
        MVert *mvert;       /* vertices & normals */
@@ -2594,11 +2630,14 @@ static void GetNormal(const SMikkTSpaceContext *pContext, float r_no[3], const i
 {
        //assert(vert_index >= 0 && vert_index < 4);
        SGLSLMeshToTangent *pMesh = (SGLSLMeshToTangent *) pContext->m_pUserData;
+       const bool smoothnormal = (pMesh->mface[face_num].flag & ME_SMOOTH) != 0;
 
-       const int smoothnormal = (pMesh->mface[face_num].flag & ME_SMOOTH);
-       if (!smoothnormal) {    // flat
+       if (pMesh->precomputedLoopNormals) {
+               normal_short_to_float_v3(r_no, pMesh->precomputedLoopNormals[face_num][vert_index]);
+       }
+       else if (!smoothnormal) {    // flat
                if (pMesh->precomputedFaceNormals) {
-                       copy_v3_v3(r_no, &pMesh->precomputedFaceNormals[3 * face_num]);
+                       copy_v3_v3(r_no, pMesh->precomputedFaceNormals[face_num]);
                }
                else {
                        MFace *mf = &pMesh->mface[face_num];
@@ -2638,12 +2677,17 @@ void DM_add_tangent_layer(DerivedMesh *dm)
        MFace *mface;
        float (*orco)[3] = NULL, (*tangent)[4];
        int /* totvert, */ totface;
-       float *nors;
+       float (*fnors)[3];
+       short (*tlnors)[4][3];
 
        if (CustomData_get_layer_index(&dm->faceData, CD_TANGENT) != -1)
                return;
 
-       nors = dm->getTessFaceDataArray(dm, CD_NORMAL);
+       fnors = dm->getTessFaceDataArray(dm, CD_NORMAL);
+       /* Note, we assume we do have tessellated loop normals at this point (in case it is object-enabled),
+        * have to check this is valid...
+        */
+       tlnors = dm->getTessFaceDataArray(dm, CD_TESSLOOPNORMAL);
 
        /* check we have all the needed layers */
        /* totvert = dm->getNumVerts(dm); */ /* UNUSED */
@@ -2669,7 +2713,8 @@ void DM_add_tangent_layer(DerivedMesh *dm)
                SMikkTSpaceContext sContext = {NULL};
                SMikkTSpaceInterface sInterface = {NULL};
 
-               mesh2tangent.precomputedFaceNormals = nors;
+               mesh2tangent.precomputedFaceNormals = fnors;
+               mesh2tangent.precomputedLoopNormals = tlnors;
                mesh2tangent.mtface = mtface;
                mesh2tangent.mface = mface;
                mesh2tangent.mvert = mvert;
index 0c58b7e66e757cb493dd979f0f44f36ef0eace62..9af6220e4b284350bc20361774d399c589ea63dc 100644 (file)
@@ -1738,6 +1738,7 @@ static CDDerivedMesh *cdDM_create(const char *desc)
        dm->getTessFaceDataArray = DM_get_tessface_data_layer;
 
        dm->calcNormals = CDDM_calc_normals;
+       dm->calcLoopNormals = CDDM_calc_loop_normals;
        dm->recalcTessellation = CDDM_recalc_tessellation;
 
        dm->getVertCos = cdDM_getVertCos;
@@ -2289,8 +2290,7 @@ void CDDM_calc_normals_mapping_ex(DerivedMesh *dm, const bool only_face_normals)
                CustomData_free_layers(&dm->faceData, CD_NORMAL, dm->numTessFaceData);
        }
 
-
-       face_nors = MEM_mallocN(sizeof(float) * 3 * dm->numTessFaceData, "face_nors");
+       face_nors = MEM_mallocN(sizeof(*face_nors) * dm->numTessFaceData, "face_nors");
 
        /* calculate face normals */
        BKE_mesh_calc_normals_mapping_ex(cddm->mvert, dm->numVertData, CDDM_get_loops(dm), CDDM_get_polys(dm),
@@ -2298,8 +2298,7 @@ void CDDM_calc_normals_mapping_ex(DerivedMesh *dm, const bool only_face_normals)
                                         CustomData_get_layer(&dm->faceData, CD_ORIGINDEX), face_nors,
                                         only_face_normals);
 
-       CustomData_add_layer(&dm->faceData, CD_NORMAL, CD_ASSIGN,
-                            face_nors, dm->numTessFaceData);
+       CustomData_add_layer(&dm->faceData, CD_NORMAL, CD_ASSIGN, face_nors, dm->numTessFaceData);
 
        cddm->dm.dirty &= ~DM_DIRTY_NORMALS;
 }
@@ -2353,6 +2352,48 @@ void CDDM_calc_normals(DerivedMesh *dm)
 
 #endif
 
+void CDDM_calc_loop_normals(DerivedMesh *dm, const float split_angle)
+{
+       MVert *mverts = dm->getVertArray(dm);
+       MEdge *medges = dm->getEdgeArray(dm);
+       MLoop *mloops = dm->getLoopArray(dm);
+       MPoly *mpolys = dm->getPolyArray(dm);
+
+       CustomData *ldata, *pdata;
+
+       float (*lnors)[3];
+       float (*pnors)[3];
+
+       const int numVerts = dm->getNumVerts(dm);
+       const int numEdges = dm->getNumEdges(dm);
+       const int numLoops = dm->getNumLoops(dm);
+       const int numPolys = dm->getNumPolys(dm);
+
+       ldata = dm->getLoopDataLayout(dm);
+       if (CustomData_has_layer(ldata, CD_NORMAL)) {
+               lnors = CustomData_get_layer(ldata, CD_NORMAL);
+       }
+       else {
+               lnors = CustomData_add_layer(ldata, CD_NORMAL, CD_CALLOC, NULL, numLoops);
+       }
+
+       /* Compute poly (always needed) and vert normals. */
+       /* Note we can't use DM_ensure_normals, since it won't keep computed poly nors... */
+       pdata = dm->getPolyDataLayout(dm);
+       pnors = CustomData_get_layer(pdata, CD_NORMAL);
+       if (!pnors) {
+               pnors = CustomData_add_layer(pdata, CD_NORMAL, CD_CALLOC, NULL, numPolys);
+       }
+       BKE_mesh_calc_normals_poly(mverts, numVerts, mloops, mpolys, numLoops, numPolys, pnors,
+                                  (dm->dirty & DM_DIRTY_NORMALS) ? false : true);
+
+       dm->dirty &= ~DM_DIRTY_NORMALS;
+
+       BKE_mesh_normals_loop_split(mverts, numVerts, medges, numEdges, mloops, lnors, numLoops,
+                                   mpolys, pnors, numPolys, split_angle);
+}
+
+
 void CDDM_calc_normals_tessface(DerivedMesh *dm)
 {
        CDDerivedMesh *cddm = (CDDerivedMesh *)dm;
index 41eb2f5982efceb0cd42c9a22fc2f3d0561e5385..61261959ea8db13d49a25b844f5673c3e4b6c50e 100644 (file)
@@ -1170,6 +1170,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = {
        {sizeof(FreestyleFace), "FreestyleFace", 1, NULL, NULL, NULL, NULL, NULL, NULL},
        /* 39: CD_MLOOPTANGENT */
        {sizeof(float[4]), "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
+       /* 40: CD_TESSLOOPNORMAL */
+       {sizeof(short[4][3]), "", 0, NULL, NULL, NULL, NULL, NULL, NULL},
 };
 
 /* note, numbers are from trunk and need updating for bmesh */
@@ -1185,7 +1187,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = {
        /* 25-29 */ "CDMPoly", "CDMLoop", "CDShapeKeyIndex", "CDShapeKey", "CDBevelWeight",
        /* 30-34 */ "CDSubSurfCrease", "CDOrigSpaceLoop", "CDPreviewLoopCol", "CDBMElemPyPtr", "CDPaintMask",
        /* 35-36 */ "CDGridPaintMask", "CDMVertSkin",
-       /* 37-38 */ "CDFreestyleEdge", "CDFreestyleFace", "CDMLoopTangent",
+       /* 37-40 */ "CDFreestyleEdge", "CDFreestyleFace", "CDMLoopTangent", "CDTessLoopNormal",
 };
 
 
@@ -1217,9 +1219,9 @@ const CustomDataMask CD_MASK_BMESH =
     CD_MASK_PROP_STR | CD_MASK_SHAPEKEY | CD_MASK_SHAPE_KEYINDEX | CD_MASK_MDISPS |
     CD_MASK_CREASE | CD_MASK_BWEIGHT | CD_MASK_RECAST | CD_MASK_PAINT_MASK |
     CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
-const CustomDataMask CD_MASK_FACECORNERS =
+const CustomDataMask CD_MASK_FACECORNERS =  /* XXX Not used anywhere! */
     CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_MTEXPOLY | CD_MASK_MLOOPUV |
-    CD_MASK_MLOOPCOL;
+    CD_MASK_MLOOPCOL | CD_MASK_NORMAL | CD_MASK_MLOOPTANGENT;
 const CustomDataMask CD_MASK_EVERYTHING =
     CD_MASK_MVERT | CD_MASK_MSTICKY /* DEPRECATED */ | CD_MASK_MDEFORMVERT | CD_MASK_MEDGE | CD_MASK_MFACE |
     CD_MASK_MTFACE | CD_MASK_MCOL | CD_MASK_ORIGINDEX | CD_MASK_NORMAL /* | CD_MASK_POLYINDEX */ | CD_MASK_PROP_FLT |
@@ -1229,7 +1231,9 @@ const CustomDataMask CD_MASK_EVERYTHING =
     CD_MASK_MPOLY | CD_MASK_MLOOP | CD_MASK_SHAPE_KEYINDEX | CD_MASK_SHAPEKEY | CD_MASK_BWEIGHT | CD_MASK_CREASE |
     CD_MASK_ORIGSPACE_MLOOP | CD_MASK_PREVIEW_MLOOPCOL | CD_MASK_BM_ELEM_PYPTR |
     /* BMESH ONLY END */
-    CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN | CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE;
+    CD_MASK_PAINT_MASK | CD_MASK_GRID_PAINT_MASK | CD_MASK_MVERT_SKIN |
+    CD_MASK_FREESTYLE_EDGE | CD_MASK_FREESTYLE_FACE |
+    CD_MASK_MLOOPTANGENT | CD_MASK_TESSLOOPNORMAL;
 
 static const LayerTypeInfo *layerType_getInfo(int type)
 {
@@ -2283,6 +2287,9 @@ void CustomData_to_bmeshpoly(CustomData *fdata, CustomData *pdata, CustomData *l
                else if (fdata->layers[i].type == CD_MDISPS) {
                        CustomData_add_layer_named(ldata, CD_MDISPS, CD_CALLOC, NULL, totloop, fdata->layers[i].name);
                }
+               else if (fdata->layers[i].type == CD_TESSLOOPNORMAL) {
+                       CustomData_add_layer_named(ldata, CD_NORMAL, CD_CALLOC, NULL, totloop, fdata->layers[i].name);
+               }
        }
 }
 
@@ -2304,6 +2311,9 @@ void CustomData_from_bmeshpoly(CustomData *fdata, CustomData *pdata, CustomData
                else if (ldata->layers[i].type == CD_ORIGSPACE_MLOOP) {
                        CustomData_add_layer_named(fdata, CD_ORIGSPACE, CD_CALLOC, NULL, total, ldata->layers[i].name);
                }
+               else if (ldata->layers[i].type == CD_NORMAL) {
+                       CustomData_add_layer_named(fdata, CD_TESSLOOPNORMAL, CD_CALLOC, NULL, total, ldata->layers[i].name);
+               }
        }
 
        CustomData_bmesh_update_active_layers(fdata, pdata, ldata);
index b7c5c6052541efb698b08c012eb7acf34c4c7f7f..152540041f73082e3c9469cf735655480edb4ef3 100644 (file)
@@ -171,6 +171,11 @@ static void emDM_calcNormals(DerivedMesh *dm)
        dm->dirty &= ~DM_DIRTY_NORMALS;
 }
 
+static void emDM_calcLoopNormals(DerivedMesh *dm, const float split_angle)
+{
+       /* Do nothing for now! */
+}
+
 static void emDM_recalcTessellation(DerivedMesh *UNUSED(dm))
 {
        /* do nothing */
@@ -1654,6 +1659,7 @@ DerivedMesh *getEditDerivedBMesh(BMEditMesh *em,
        bmdm->dm.getTessFaceDataArray = emDM_getTessFaceDataArray;
 
        bmdm->dm.calcNormals = emDM_calcNormals;
+       bmdm->dm.calcLoopNormals = emDM_calcLoopNormals;
        bmdm->dm.recalcTessellation = emDM_recalcTessellation;
 
        bmdm->dm.foreachMappedVert = emDM_foreachMappedVert;
index 5d10c12b8e0e3a6aa1e91c8ffa65270033ac8852..edd0a8540e3960c8e091b293eed353ade7eec5a0 100644 (file)
@@ -574,6 +574,7 @@ void BKE_mesh_normals_loop_split(MVert *mverts, const int UNUSED(numVerts), MEdg
 #undef IS_EDGE_SHARP
 }
 
+
 /** \} */
 
 
@@ -1103,7 +1104,8 @@ void BKE_mesh_loops_to_mface_corners(
         const int numTex, /* CustomData_number_of_layers(pdata, CD_MTEXPOLY) */
         const int numCol, /* CustomData_number_of_layers(ldata, CD_MLOOPCOL) */
         const bool hasPCol, /* CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL) */
-        const bool hasOrigSpace /* CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP) */
+        const bool hasOrigSpace, /* CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP) */
+        const bool hasLNor /* CustomData_has_layer(ldata, CD_NORMAL) */
 )
 {
        MTFace *texface;
@@ -1152,6 +1154,14 @@ void BKE_mesh_loops_to_mface_corners(
                        copy_v2_v2(of->uv[j], lof->uv);
                }
        }
+
+       if (hasLNor) {
+               short (*tlnors)[3] = CustomData_get(fdata, findex, CD_TESSLOOPNORMAL);
+
+               for (j = 0; j < mf_len; j++) {
+                       normal_float_to_short_v3(tlnors[j], CustomData_get(ldata, (int)lindex[j], CD_NORMAL));
+               }
+       }
 }
 
 /**
@@ -1172,6 +1182,7 @@ void BKE_mesh_loops_to_tessdata(CustomData *fdata, CustomData *ldata, CustomData
        const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL);
        const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL);
        const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP);
+       const bool hasLoopNormal = CustomData_has_layer(ldata, CD_NORMAL);
        int findex, i, j;
        int *pidx;
        unsigned int (*lidx)[4];
@@ -1225,6 +1236,17 @@ void BKE_mesh_loops_to_tessdata(CustomData *fdata, CustomData *ldata, CustomData
                        }
                }
        }
+
+       if (hasLoopNormal) {
+               short (*fnors)[4][3] = CustomData_get_layer(fdata, CD_TESSLOOPNORMAL);
+               float (*lnors)[3] = CustomData_get_layer(ldata, CD_NORMAL);
+
+               for (findex = 0, lidx = loopindices; findex < num_faces; lidx++, findex++, fnors++) {
+                       for (j = (*lidx)[3] ? 4 : 3; j--;) {
+                               normal_float_to_short_v3((*fnors)[j], lnors[(*lidx)[j]]);
+                       }
+               }
+       }
 }
 
 /**
@@ -1516,6 +1538,7 @@ int BKE_mesh_mpoly_to_mface(struct CustomData *fdata, struct CustomData *ldata,
        const int numCol = CustomData_number_of_layers(ldata, CD_MLOOPCOL);
        const bool hasPCol = CustomData_has_layer(ldata, CD_PREVIEW_MLOOPCOL);
        const bool hasOrigSpace = CustomData_has_layer(ldata, CD_ORIGSPACE_MLOOP);
+       const bool hasLNor = CustomData_has_layer(ldata, CD_NORMAL);
 
        /* over-alloc, ngons will be skipped */
        mface = MEM_mallocN(sizeof(*mface) * (size_t)totpoly, __func__);
@@ -1575,7 +1598,7 @@ int BKE_mesh_mpoly_to_mface(struct CustomData *fdata, struct CustomData *ldata,
 
                                BKE_mesh_loops_to_mface_corners(fdata, ldata, pdata,
                                                                lindex, k, i, 3,
-                                                               numTex, numCol, hasPCol, hasOrigSpace);
+                                                               numTex, numCol, hasPCol, hasOrigSpace, hasLNor);
                                test_index_face(mf, fdata, k, 3);
                        }
                        else {
@@ -1595,7 +1618,7 @@ int BKE_mesh_mpoly_to_mface(struct CustomData *fdata, struct CustomData *ldata,
 
                                BKE_mesh_loops_to_mface_corners(fdata, ldata, pdata,
                                                                lindex, k, i, 4,
-                                                               numTex, numCol, hasPCol, hasOrigSpace);
+                                                               numTex, numCol, hasPCol, hasOrigSpace, hasLNor);
                                test_index_face(mf, fdata, k, 4);
                        }
 
@@ -1651,6 +1674,16 @@ static void bm_corners_to_loops_ex(ID *id, CustomData *fdata, CustomData *ldata,
                }
        }
 
+       if (CustomData_has_layer(fdata, CD_TESSLOOPNORMAL)) {
+               float (*lnors)[3] = CustomData_get(ldata, loopstart, CD_NORMAL);
+               short (*tlnors)[3] = CustomData_get(fdata, findex, CD_TESSLOOPNORMAL);
+               const int max = mf->v4 ? 4 : 3;
+
+               for (i = 0; i < max; i++, lnors++, tlnors++) {
+                       normal_short_to_float_v3(*lnors, *tlnors);
+               }
+       }
+
        if (CustomData_has_layer(fdata, CD_MDISPS)) {
                MDisps *ld = CustomData_get(ldata, loopstart, CD_MDISPS);
                MDisps *fd = CustomData_get(fdata, findex, CD_MDISPS);
index 87de748eace9b7d6ee9e7b451558e5eadfa932af..0b2d772978da9c090f0e80444533cfb33436afc2 100644 (file)
@@ -3243,6 +3243,7 @@ static CCGDerivedMesh *getCCGDerivedMesh(CCGSubSurf *ss,
        ccgdm->dm.getPBVH = ccgDM_getPBVH;
 
        ccgdm->dm.calcNormals = ccgDM_calcNormals;
+       ccgdm->dm.calcLoopNormals = CDDM_calc_loop_normals;
        ccgdm->dm.recalcTessellation = ccgDM_recalcTessellation;
 
        ccgdm->dm.getVertCos = ccgdm_getVertCos;
index d310c836f59b96fe2a0f87d2972a4a0029c4158c..2dd227ef036d28e26ee041d37c8dd9636b206c24 100644 (file)
 #include "DNA_space_types.h"
 #include "DNA_screen_types.h"
 #include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
 #include "DNA_modifier_types.h"
 #include "DNA_sdna_types.h"
 
 #include "DNA_genfile.h"
 
+#include "BLI_math.h"
 
 #include "BKE_main.h"
 #include "BKE_node.h"
@@ -160,4 +162,13 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
                        }
                }
        }
+
+       if (!MAIN_VERSION_ATLEAST(main, 270, 2)) {
+               Mesh *me;
+
+               /* Mesh smoothresh deg->rad. */
+               for (me = main->mesh.first; me; me = me->id.next) {
+                       me->smoothresh = DEG2RADF(me->smoothresh);
+               }
+       }
 }
index b14402e72dba65d790fc4b00e000eb9c2e58757d..d6caf16f7aa00ad7fdf3e74501e0e0f528679079 100644 (file)
@@ -26,6 +26,7 @@
  */
 
 #include "BLI_utildefines.h"
+#include "BLI_math.h"
 
 #include "DNA_freestyle_types.h"
 #include "DNA_linestyle_types.h"
@@ -33,6 +34,7 @@
 #include "DNA_screen_types.h"
 #include "DNA_space_types.h"
 #include "DNA_userdef_types.h"
+#include "DNA_mesh_types.h"
 
 #include "BKE_main.h"
 
@@ -45,6 +47,7 @@ void BLO_update_defaults_startup_blend(Main *main)
        Scene *scene;
        SceneRenderLayer *srl;
        FreestyleLineStyle *linestyle;
+       Mesh *me;
 
        for (scene = main->scene.first; scene; scene = scene->id.next) {
                scene->r.im_format.planes = R_IMF_PLANES_RGBA;
@@ -75,5 +78,9 @@ void BLO_update_defaults_startup_blend(Main *main)
                        }
                }
        }
+
+       for (me = main->mesh.first; me; me = me->id.next) {
+               me->smoothresh = DEG2RADF(180.0f);
+       }
 }
 
index 6b055d34b1353585938fd3ad0d45310b3c3d00c5..24e00dd49e21415fe733ba30383a70342f8f53a2 100644 (file)
@@ -2403,6 +2403,48 @@ static int draw_dm_test_freestyle_face_mark(BMesh *bm, BMFace *efa)
 
 #endif
 
+/* Draw loop normals. */
+static void draw_dm_loop_normals(BMEditMesh *em, Scene *scene, Object *ob, DerivedMesh *dm)
+{
+       /* XXX Would it be worth adding a dm->foreachMappedLoop func just for this? I doubt it... */
+
+       /* We can't use dm->getLoopDataLayout(dm) here, we want to always access dm->loopData, EditDerivedBMesh would
+        * return loop data from bmesh itself. */
+       float (*lnors)[3] = DM_get_loop_data_layer(dm, CD_NORMAL);
+
+       if (lnors) {
+               drawDMNormal_userData data;
+               MLoop *mloops = dm->getLoopArray(dm);
+               MVert *mverts = dm->getVertArray(dm);
+               int i, totloops = dm->getNumLoops(dm);
+
+               data.bm = em->bm;
+               data.normalsize = scene->toolsettings->normalsize;
+
+               calcDrawDMNormalScale(ob, &data);
+
+               glBegin(GL_LINES);
+               for (i = 0; i < totloops; i++, mloops++, lnors++) {
+                       float no[3];
+                       float *co = mverts[mloops->v].co;
+
+                       if (!data.uniform_scale) {
+                               mul_v3_m3v3(no, data.tmat, (float *)lnors);
+                               normalize_v3(no);
+                               mul_m3_v3(data.imat, no);
+                       }
+                       else {
+                               copy_v3_v3(no,(float *)lnors);
+                       }
+                       mul_v3_fl(no, data.normalsize);
+                       add_v3_v3(no, co);
+                       glVertex3fv(co);
+                       glVertex3fv(no);
+               }
+               glEnd();
+       }
+}
+
 /* Draw faces with color set based on selection
  * return 2 for the active face so it renders with stipple enabled */
 static DMDrawOption draw_dm_faces_sel__setDrawOptions(void *userData, int index)
@@ -3351,6 +3393,10 @@ static void draw_em_fancy(Scene *scene, ARegion *ar, View3D *v3d,
                        UI_ThemeColor(TH_VNORMAL);
                        draw_dm_vert_normals(em, scene, ob, cageDM);
                }
+               if (me->drawflag & ME_DRAW_LNORMALS) {
+                       UI_ThemeColor(TH_VNORMAL);
+                       draw_dm_loop_normals(em, scene, ob, cageDM);
+               }
 
                if ((me->drawflag & (ME_DRAWEXTRA_EDGELEN |
                                     ME_DRAWEXTRA_FACEAREA |
index b6ae49cad6103c56652e4323106381cd97fb27ac..d1844b34a0a8a8334a357dfa5e0ba38bfcda2f48 100644 (file)
@@ -63,10 +63,9 @@ typedef struct CustomDataExternal {
  * layers, each with a data type (e.g. MTFace, MDeformVert, etc.). */
 typedef struct CustomData {
        CustomDataLayer *layers;      /* CustomDataLayers, ordered by type */
-       int typemap[40];              /* runtime only! - maps types to indices of first layer of that type,
+       int typemap[41];              /* runtime only! - maps types to indices of first layer of that type,
                                       * MUST be >= CD_NUMTYPES, but we cant use a define here.
                                       * Correct size is ensured in CustomData_update_typemap assert() */
-       int pad[1];
        int totlayer, maxlayer;       /* number of layers, size of layers array */
        int totsize;                  /* in editmode, total size of all data layers */
        struct BLI_mempool *pool;     /* (BMesh Only): Memory pool for allocation of blocks */
@@ -119,7 +118,8 @@ enum {
        CD_FREESTYLE_EDGE   = 37,
        CD_FREESTYLE_FACE   = 38,
        CD_MLOOPTANGENT     = 39,
-       CD_NUMTYPES         = 40,
+       CD_TESSLOOPNORMAL   = 40,
+       CD_NUMTYPES         = 41,
 };
 
 /* Bits for CustomDataMask */
@@ -164,7 +164,8 @@ enum {
 #define CD_MASK_MVERT_SKIN             (1LL << CD_MVERT_SKIN)
 #define CD_MASK_FREESTYLE_EDGE (1LL << CD_FREESTYLE_EDGE)
 #define CD_MASK_FREESTYLE_FACE (1LL << CD_FREESTYLE_FACE)
-#define CD_MASK_MLOOPTANGENT     (1LL << CD_MLOOPTANGENT)
+#define CD_MASK_MLOOPTANGENT    (1LL << CD_MLOOPTANGENT)
+#define CD_MASK_TESSLOOPNORMAL  (1LL << CD_TESSLOOPNORMAL)
 
 /* CustomData.flag */
 enum {
index b942197e52c4cad063d5b2f9fd3763759858f5dc..e535e6012b3ea3ed7e6060713e5c15d6fafaacf4 100644 (file)
@@ -116,8 +116,9 @@ typedef struct Mesh {
        float rot[3];
 
        int drawflag;
-       short texflag, pad2[3];
-       short smoothresh, flag;
+       short texflag, flag;
+       float smoothresh;
+       int pad2;
 
        /* customdata flag, for bevel-weight and crease, which are now optional */
        char cd_flag, pad;
@@ -212,6 +213,9 @@ typedef struct TFace {
 /* draw stats */
 #define ME_DRAW_STATVIS (1 << 17)
 
+/* draw loop normals */
+#define ME_DRAW_LNORMALS (1 << 18)
+
 /* Subsurf Type */
 #define ME_CC_SUBSURF          0
 #define ME_SIMPLE_SUBSURF      1
index bdd94b73c443ebaf1f307f016a6f15ae2731343e..99c35bdcff28b71aac7d3a590878715ba3eedb71 100644 (file)
@@ -413,6 +413,21 @@ static void rna_MeshTessFace_normal_get(PointerRNA *ptr, float *values)
                normal_tri_v3(values, me->mvert[mface->v1].co, me->mvert[mface->v2].co, me->mvert[mface->v3].co);
 }
 
+static void rna_MeshTessFace_split_normals_get(PointerRNA *ptr, float *values)
+{
+       Mesh *me = rna_mesh(ptr);
+       MFace *mface = (MFace *)ptr->data;
+       const short (*vec)[4][3] = CustomData_get(&me->fdata, (int)(mface - me->mface), CD_TESSLOOPNORMAL);
+       int i = 4;
+
+       if (!vec) {
+               while (i--) zero_v3(&values[i * 3]);
+       }
+       else {
+               while (i--) normal_short_to_float_v3(&values[i * 3], (const short*)(*vec)[i]);
+       }
+}
+
 static float rna_MeshTessFace_area_get(PointerRNA *ptr)
 {
        Mesh *me = rna_mesh(ptr);
@@ -1105,20 +1120,6 @@ static void rna_TextureFace_image_set(PointerRNA *ptr, PointerRNA value)
        tf->tpage = (struct Image *)id;
 }
 
-static void rna_Mesh_auto_smooth_angle_set(PointerRNA *ptr, float value)
-{
-       Mesh *me = rna_mesh(ptr);
-       value = RAD2DEGF(value);
-       CLAMP(value, 1.0f, 80.0f);
-       me->smoothresh = (int)value;
-}
-
-static float rna_Mesh_auto_smooth_angle_get(PointerRNA *ptr)
-{
-       Mesh *me = rna_mesh(ptr);
-       return DEG2RADF((float)me->smoothresh);
-}
-
 static int rna_MeshTessFace_verts_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
 {
        MFace *face = (MFace *)ptr->data;
@@ -1817,6 +1818,7 @@ static void rna_def_mface(BlenderRNA *brna)
 {
        StructRNA *srna;
        PropertyRNA *prop;
+       const int splitnor_dim[] = {4, 3};
 
        srna = RNA_def_struct(brna, "MeshTessFace", NULL);
        RNA_def_struct_sdna(srna, "MFace");
@@ -1868,6 +1870,16 @@ static void rna_def_mface(BlenderRNA *brna)
        RNA_def_property_float_funcs(prop, "rna_MeshTessFace_normal_get", NULL, NULL);
        RNA_def_property_ui_text(prop, "Face Normal", "Local space unit length normal vector for this face");
 
+       prop = RNA_def_property(srna, "split_normals", PROP_FLOAT, PROP_DIRECTION);
+       RNA_def_property_multi_array(prop, 2, splitnor_dim);
+       RNA_def_property_range(prop, -1.0f, 1.0f);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_float_funcs(prop, "rna_MeshTessFace_split_normals_get", NULL, NULL);
+       RNA_def_property_ui_text(prop, "Split Normals",
+                                "Local space unit length split normals vectors of the vertices of this face "
+                                "(must be computed beforehand using calc_normals_split or calc_tangents, "
+                                "and then calc_tessface)");
+
        prop = RNA_def_property(srna, "area", PROP_FLOAT, PROP_UNSIGNED);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_float_funcs(prop, "rna_MeshTessFace_area_get", NULL, NULL);
@@ -3113,19 +3125,17 @@ static void rna_def_mesh(BlenderRNA *brna)
        RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_AUTOSMOOTH);
        RNA_def_property_ui_text(prop, "Auto Smooth",
                                 "Treat all set-smoothed faces with angles less than the specified angle "
-                                "as 'smooth' during render");
+                                "as 'smooth', unless they are linked by a sharp edge");
+       RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
 
-#if 1 /* expose as radians */
        prop = RNA_def_property(srna, "auto_smooth_angle", PROP_FLOAT, PROP_ANGLE);
-       RNA_def_property_float_funcs(prop, "rna_Mesh_auto_smooth_angle_get", "rna_Mesh_auto_smooth_angle_set", NULL);
-       RNA_def_property_ui_range(prop, DEG2RAD(1.0), DEG2RAD(80), 1.0, 1);
-#else
-       prop = RNA_def_property(srna, "auto_smooth_angle", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "smoothresh");
-       RNA_def_property_range(prop, 1, 80);
-#endif
+       RNA_def_property_float_sdna(prop, NULL, "smoothresh");
+       RNA_def_property_float_default(prop, DEG2RADF(180.0f));
+       RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
+       RNA_def_property_ui_range(prop, DEG2RADF(0.0f), DEG2RADF(180.0f), 1.0, 1);
        RNA_def_property_ui_text(prop, "Auto Smooth Angle",
                                 "Maximum angle between face normals that 'Auto Smooth' will operate on");
+       RNA_def_property_update(prop, 0, "rna_Mesh_update_data");
 
        prop = RNA_def_property(srna, "show_double_sided", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_TWOSIDED);
@@ -3183,6 +3193,11 @@ static void rna_def_mesh(BlenderRNA *brna)
        RNA_def_property_ui_text(prop, "Draw Normals", "Display face normals as lines");
        RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
 
+       prop = RNA_def_property(srna, "show_normal_loop", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "drawflag", ME_DRAW_LNORMALS);
+       RNA_def_property_ui_text(prop, "Draw Split Normals", "Display vertex-per-face normals as lines");
+       RNA_def_property_update(prop, 0, "rna_Mesh_update_draw");
+
        prop = RNA_def_property(srna, "show_normal_vertex", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "drawflag", ME_DRAW_VNORMALS);
        RNA_def_property_ui_text(prop, "Draw Vertex Normals", "Display vertex normals as lines");