Cleanup: remove redundant doxygen \file argument
[blender.git] / source / blender / blenkernel / intern / subdiv_mesh.c
index 4d9e8998cf05353dcc5de6bcd37ac73a8e797426..7e6a7eabd45290a53efeb70937762f3c9f71b389 100644 (file)
@@ -1,6 +1,4 @@
 /*
 /*
- * ***** 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
  * 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
  *
  * The Original Code is Copyright (C) 2018 by Blender Foundation.
  * All rights reserved.
  *
  * The Original Code is Copyright (C) 2018 by Blender Foundation.
  * All rights reserved.
- *
- * Contributor(s): Sergey Sharybin.
- *
- * ***** END GPL LICENSE BLOCK *****
  */
 
  */
 
-/** \file blender/blenkernel/intern/subdiv_mesh.c
- *  \ingroup bke
+/** \file \ingroup bke
  */
 
 #include "BKE_subdiv_mesh.h"
  */
 
 #include "BKE_subdiv_mesh.h"
@@ -36,9 +29,7 @@
 #include "DNA_key_types.h"
 
 #include "BLI_alloca.h"
 #include "DNA_key_types.h"
 
 #include "BLI_alloca.h"
-#include "BLI_bitmap.h"
 #include "BLI_math_vector.h"
 #include "BLI_math_vector.h"
-#include "BLI_task.h"
 
 #include "BKE_mesh.h"
 #include "BKE_key.h"
 
 #include "BKE_mesh.h"
 #include "BKE_key.h"
@@ -65,6 +56,24 @@ typedef struct SubdivMeshContext {
        /* UV layers interpolation. */
        int num_uv_layers;
        MLoopUV *uv_layers[MAX_MTFACE];
        /* UV layers interpolation. */
        int num_uv_layers;
        MLoopUV *uv_layers[MAX_MTFACE];
+       /* Accumulated values.
+        *
+        * Averaging is happening for vertices along the coarse edges and corners.
+        * This is needed for both displacement and normals.
+        *
+        * Displacement is being accumulated to a verticies coordinates, since those
+        * are not needed during traversal of edge/corner vertices.
+        *
+        * For normals we are using dedicated array, since we can not use same
+        * vertices (normals are `short`, which will cause a lot of precision
+        * issues). */
+       float (*accumulated_normals)[3];
+       /* Per-subdivided vertex counter of averaged values. */
+       int *accumulated_counters;
+       /* Denotes whether normals can be evaluated from a limit surface. One case
+        * when it's not possible is when displacement is used. */
+       bool can_evaluate_normals;
+       bool have_displacement;
 } SubdivMeshContext;
 
 static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx)
 } SubdivMeshContext;
 
 static void subdiv_mesh_ctx_cache_uv_layers(SubdivMeshContext *ctx)
@@ -94,6 +103,30 @@ static void subdiv_mesh_ctx_cache_custom_data_layers(SubdivMeshContext *ctx)
        subdiv_mesh_ctx_cache_uv_layers(ctx);
 }
 
        subdiv_mesh_ctx_cache_uv_layers(ctx);
 }
 
+static void subdiv_mesh_prepare_accumulator(
+        SubdivMeshContext *ctx, int num_vertices)
+{
+       if (!ctx->can_evaluate_normals && !ctx->have_displacement) {
+               return;
+       }
+       /* TODO(sergey): Technically, this is overallocating, we don't need memory
+        * for an inner subdivision vertices. */
+       ctx->accumulated_normals = MEM_calloc_arrayN(
+               sizeof(*ctx->accumulated_normals),
+               num_vertices,
+               "subdiv accumulated normals");
+       ctx->accumulated_counters = MEM_calloc_arrayN(
+               sizeof(*ctx->accumulated_counters),
+               num_vertices,
+               "subdiv accumulated counters");
+}
+
+static void subdiv_mesh_context_free(SubdivMeshContext *ctx)
+{
+       MEM_SAFE_FREE(ctx->accumulated_normals);
+       MEM_SAFE_FREE(ctx->accumulated_counters);
+}
+
 /* =============================================================================
  * Loop custom data copy helpers.
  */
 /* =============================================================================
  * Loop custom data copy helpers.
  */
@@ -120,8 +153,7 @@ static void loops_of_ptex_get(
        /* Loop which look in the (opposite) V direction of the current
         * ptex face.
         *
        /* Loop which look in the (opposite) V direction of the current
         * ptex face.
         *
-        * TODO(sergey): Get rid of using module on every iteration.
-        */
+        * TODO(sergey): Get rid of using module on every iteration. */
        const int last_ptex_loop_index =
                coarse_poly->loopstart +
                (ptex_of_poly_index + coarse_poly->totloop - 1) %
        const int last_ptex_loop_index =
                coarse_poly->loopstart +
                (ptex_of_poly_index + coarse_poly->totloop - 1) %
@@ -143,14 +175,12 @@ static void loops_of_ptex_get(
  */
 
 /* TODO(sergey): Somehow de-duplicate with loops storage, without too much
  */
 
 /* TODO(sergey): Somehow de-duplicate with loops storage, without too much
- * exception cases all over the code.
- */
+ * exception cases all over the code. */
 
 typedef struct VerticesForInterpolation {
        /* This field points to a vertex data which is to be used for interpolation.
         * The idea is to avoid unnecessary allocations for regular faces, where
 
 typedef struct VerticesForInterpolation {
        /* This field points to a vertex data which is to be used for interpolation.
         * The idea is to avoid unnecessary allocations for regular faces, where
-        * we can simply
-        */
+        * we can simply use corner verticies. */
        const CustomData *vertex_data;
        /* Vertices data calculated for ptex corners. There are always 4 elements
         * in this custom data, aligned the following way:
        const CustomData *vertex_data;
        /* Vertices data calculated for ptex corners. There are always 4 elements
         * in this custom data, aligned the following way:
@@ -160,13 +190,11 @@ typedef struct VerticesForInterpolation {
         *   index 2 -> uv (1, 1)
         *   index 3 -> uv (1, 0)
         *
         *   index 2 -> uv (1, 1)
         *   index 3 -> uv (1, 0)
         *
-        * Is allocated for non-regular faces (triangles and n-gons).
-        */
+        * Is allocated for non-regular faces (triangles and n-gons). */
        CustomData vertex_data_storage;
        bool vertex_data_storage_allocated;
        /* Infices within vertex_data to interpolate for. The indices are aligned
        CustomData vertex_data_storage;
        bool vertex_data_storage_allocated;
        /* Infices within vertex_data to interpolate for. The indices are aligned
-        * with uv coordinates in a similar way as indices in loop_data_storage.
-        */
+        * with uv coordinates in a similar way as indices in loop_data_storage. */
        int vertex_indices[4];
 } VerticesForInterpolation;
 
        int vertex_indices[4];
 } VerticesForInterpolation;
 
@@ -205,8 +233,7 @@ static void vertex_interpolation_init(
                vertex_interpolation->vertex_indices[3] = 3;
                vertex_interpolation->vertex_data_storage_allocated = true;
                /* Interpolate center of poly right away, it stays unchanged for all
                vertex_interpolation->vertex_indices[3] = 3;
                vertex_interpolation->vertex_data_storage_allocated = true;
                /* Interpolate center of poly right away, it stays unchanged for all
-                * ptex faces.
-                */
+                * ptex faces. */
                const float weight = 1.0f / (float)coarse_poly->totloop;
                float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
                int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
                const float weight = 1.0f / (float)coarse_poly->totloop;
                float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
                int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
@@ -249,8 +276,7 @@ static void vertex_interpolation_from_corner(
                 * middle points.
                 *
                 * TODO(sergey): Re-use one of interpolation results from previous
                 * middle points.
                 *
                 * TODO(sergey): Re-use one of interpolation results from previous
-                * iteration.
-                */
+                * iteration. */
                const float weights[2] = {0.5f, 0.5f};
                const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop;
                const int last_loop_index = loops_of_ptex.last_loop - coarse_mloop;
                const float weights[2] = {0.5f, 0.5f};
                const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop;
                const int last_loop_index = loops_of_ptex.last_loop - coarse_mloop;
@@ -291,8 +317,7 @@ static void vertex_interpolation_end(
 typedef struct LoopsForInterpolation {
  /* This field points to a loop data which is to be used for interpolation.
         * The idea is to avoid unnecessary allocations for regular faces, where
 typedef struct LoopsForInterpolation {
  /* This field points to a loop data which is to be used for interpolation.
         * The idea is to avoid unnecessary allocations for regular faces, where
-        * we can simply
-        */
+        * we can simply interpolate corner verticies. */
        const CustomData *loop_data;
        /* Loops data calculated for ptex corners. There are always 4 elements
         * in this custom data, aligned the following way:
        const CustomData *loop_data;
        /* Loops data calculated for ptex corners. There are always 4 elements
         * in this custom data, aligned the following way:
@@ -302,13 +327,11 @@ typedef struct LoopsForInterpolation {
         *   index 2 -> uv (1, 1)
         *   index 3 -> uv (1, 0)
         *
         *   index 2 -> uv (1, 1)
         *   index 3 -> uv (1, 0)
         *
-        * Is allocated for non-regular faces (triangles and n-gons).
-        */
+        * Is allocated for non-regular faces (triangles and n-gons). */
        CustomData loop_data_storage;
        bool loop_data_storage_allocated;
        /* Infices within loop_data to interpolate for. The indices are aligned with
        CustomData loop_data_storage;
        bool loop_data_storage_allocated;
        /* Infices within loop_data to interpolate for. The indices are aligned with
-        * uv coordinates in a similar way as indices in loop_data_storage.
-        */
+        * uv coordinates in a similar way as indices in loop_data_storage. */
        int loop_indices[4];
 } LoopsForInterpolation;
 
        int loop_indices[4];
 } LoopsForInterpolation;
 
@@ -341,8 +364,7 @@ static void loop_interpolation_init(
                loop_interpolation->loop_indices[3] = 3;
                loop_interpolation->loop_data_storage_allocated = true;
                /* Interpolate center of poly right away, it stays unchanged for all
                loop_interpolation->loop_indices[3] = 3;
                loop_interpolation->loop_data_storage_allocated = true;
                /* Interpolate center of poly right away, it stays unchanged for all
-                * ptex faces.
-                */
+                * ptex faces. */
                const float weight = 1.0f / (float)coarse_poly->totloop;
                float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
                int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
                const float weight = 1.0f / (float)coarse_poly->totloop;
                float *weights = BLI_array_alloca(weights, coarse_poly->totloop);
                int *indices = BLI_array_alloca(indices, coarse_poly->totloop);
@@ -385,13 +407,14 @@ static void loop_interpolation_from_corner(
                 * middle points.
                 *
                 * TODO(sergey): Re-use one of interpolation results from previous
                 * middle points.
                 *
                 * TODO(sergey): Re-use one of interpolation results from previous
-                * iteration.
-                */
+                * iteration. */
                const float weights[2] = {0.5f, 0.5f};
                const float weights[2] = {0.5f, 0.5f};
-               const int first_indices[2] = {
-                       loops_of_ptex.first_loop - coarse_mloop,
-                       (loops_of_ptex.first_loop + 1 - coarse_mloop) %
-                               coarse_poly->totloop};
+               const int base_loop_index = coarse_poly->loopstart;
+               const int first_loop_index = loops_of_ptex.first_loop - coarse_mloop;
+               const int second_loop_index =
+                       base_loop_index +
+                       (first_loop_index - base_loop_index + 1) % coarse_poly->totloop;
+               const int first_indices[2] = {first_loop_index, second_loop_index};
                const int last_indices[2] = {
                        loops_of_ptex.last_loop - coarse_mloop,
                        loops_of_ptex.first_loop - coarse_mloop};
                const int last_indices[2] = {
                        loops_of_ptex.last_loop - coarse_mloop,
                        loops_of_ptex.first_loop - coarse_mloop};
@@ -465,27 +488,34 @@ static void eval_final_point_and_vertex_normal(
 }
 
 /* =============================================================================
 }
 
 /* =============================================================================
- * Displacement helpers
+ * Accumulation helpers.
  */
 
  */
 
-static void subdiv_accumulate_vertex_displacement(
-        Subdiv *subdiv,
+static void subdiv_accumulate_vertex_normal_and_displacement(
+        SubdivMeshContext *ctx,
         const int ptex_face_index,
         const float u, const float v,
         MVert *subdiv_vert)
 {
         const int ptex_face_index,
         const float u, const float v,
         MVert *subdiv_vert)
 {
+       Subdiv *subdiv = ctx->subdiv;
+       const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
        float dummy_P[3], dPdu[3], dPdv[3], D[3];
        BKE_subdiv_eval_limit_point_and_derivatives(
                subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
        float dummy_P[3], dPdu[3], dPdv[3], D[3];
        BKE_subdiv_eval_limit_point_and_derivatives(
                subdiv, ptex_face_index, u, v, dummy_P, dPdu, dPdv);
-       BKE_subdiv_eval_displacement(subdiv,
-                                    ptex_face_index, u, v,
-                                    dPdu, dPdv,
-                                    D);
-       add_v3_v3(subdiv_vert->co, D);
-       if (subdiv_vert->flag & ME_VERT_TMP_TAG) {
-               mul_v3_fl(subdiv_vert->co, 0.5f);
+       /* Accumulate normal. */
+       if (ctx->can_evaluate_normals) {
+               float N[3];
+               cross_v3_v3v3(N, dPdu, dPdv);
+               normalize_v3(N);
+               add_v3_v3(ctx->accumulated_normals[subdiv_vertex_index], N);
+       }
+       /* Accumulate displacement if needed. */
+       if (ctx->have_displacement) {
+               BKE_subdiv_eval_displacement(
+                       subdiv, ptex_face_index, u, v, dPdu, dPdv, D);
+               add_v3_v3(subdiv_vert->co, D);
        }
        }
-       subdiv_vert->flag |= ME_VERT_TMP_TAG;
+       ++ctx->accumulated_counters[subdiv_vertex_index];
 }
 
 /* =============================================================================
 }
 
 /* =============================================================================
@@ -508,6 +538,7 @@ static bool subdiv_mesh_topology_info(
                num_loops,
                num_polygons);
        subdiv_mesh_ctx_cache_custom_data_layers(subdiv_context);
                num_loops,
                num_polygons);
        subdiv_mesh_ctx_cache_custom_data_layers(subdiv_context);
+       subdiv_mesh_prepare_accumulator(subdiv_context, num_vertices);
        return true;
 }
 
        return true;
 }
 
@@ -524,7 +555,6 @@ static void subdiv_vertex_data_copy(
        Mesh *subdiv_mesh = ctx->subdiv_mesh;
        const int coarse_vertex_index = coarse_vertex - coarse_mesh->mvert;
        const int subdiv_vertex_index = subdiv_vertex - subdiv_mesh->mvert;
        Mesh *subdiv_mesh = ctx->subdiv_mesh;
        const int coarse_vertex_index = coarse_vertex - coarse_mesh->mvert;
        const int subdiv_vertex_index = subdiv_vertex - subdiv_mesh->mvert;
-       subdiv_vertex->flag &= ~ME_VERT_TMP_TAG;
        CustomData_copy_data(&coarse_mesh->vdata,
                             &ctx->subdiv_mesh->vdata,
                             coarse_vertex_index,
        CustomData_copy_data(&coarse_mesh->vdata,
                             &ctx->subdiv_mesh->vdata,
                             coarse_vertex_index,
@@ -543,7 +573,6 @@ static void subdiv_vertex_data_interpolate(
                                  u * (1.0f - v),
                                  u * v,
                                  (1.0f - u) * v};
                                  u * (1.0f - v),
                                  u * v,
                                  (1.0f - u) * v};
-       subdiv_vertex->flag &= ~ME_VERT_TMP_TAG;
        CustomData_interp(vertex_interpolation->vertex_data,
                          &ctx->subdiv_mesh->vdata,
                          vertex_interpolation->vertex_indices,
        CustomData_interp(vertex_interpolation->vertex_data,
                          &ctx->subdiv_mesh->vdata,
                          vertex_interpolation->vertex_indices,
@@ -562,19 +591,29 @@ static void evaluate_vertex_and_apply_displacement_copy(
         const MVert *coarse_vert,
         MVert *subdiv_vert)
 {
         const MVert *coarse_vert,
         MVert *subdiv_vert)
 {
+       const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
+       const float inv_num_accumulated =
+               1.0f / ctx->accumulated_counters[subdiv_vertex_index];
        /* Displacement is accumulated in subdiv vertex position.
        /* Displacement is accumulated in subdiv vertex position.
-        * need to back it up before copying data from original vertex.
-        */
-       float D[3];
-       copy_v3_v3(D, subdiv_vert->co);
+        * Needs to be backed up before copying data from original vertex. */
+       float D[3] = {0.0f, 0.0f, 0.0f};
+       if (ctx->have_displacement) {
+               copy_v3_v3(D, subdiv_vert->co);
+               mul_v3_fl(D, inv_num_accumulated);
+       }
+       /* Copy custom data and evaluate position. */
        subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert);
        subdiv_vertex_data_copy(ctx, coarse_vert, subdiv_vert);
-       BKE_subdiv_eval_limit_point_and_short_normal(
-               ctx->subdiv,
-               ptex_face_index,
-               u, v,
-               subdiv_vert->co, subdiv_vert->no);
+       BKE_subdiv_eval_limit_point(
+               ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co);
        /* Apply displacement. */
        add_v3_v3(subdiv_vert->co, D);
        /* Apply displacement. */
        add_v3_v3(subdiv_vert->co, D);
+       /* Copy normal from accumulated storage. */
+       if (ctx->can_evaluate_normals) {
+               float N[3];
+               copy_v3_v3(N, ctx->accumulated_normals[subdiv_vertex_index]);
+               normalize_v3(N);
+               normal_float_to_short_v3(subdiv_vert->no, N);
+       }
 }
 
 static void evaluate_vertex_and_apply_displacement_interpolate(
 }
 
 static void evaluate_vertex_and_apply_displacement_interpolate(
@@ -584,22 +623,31 @@ static void evaluate_vertex_and_apply_displacement_interpolate(
         VerticesForInterpolation *vertex_interpolation,
         MVert *subdiv_vert)
 {
         VerticesForInterpolation *vertex_interpolation,
         MVert *subdiv_vert)
 {
+       const int subdiv_vertex_index = subdiv_vert - ctx->subdiv_mesh->mvert;
+       const float inv_num_accumulated =
+               1.0f / ctx->accumulated_counters[subdiv_vertex_index];
        /* Displacement is accumulated in subdiv vertex position.
        /* Displacement is accumulated in subdiv vertex position.
-        * need to back it up before copying data from original vertex.
-        */
-       float D[3];
-       copy_v3_v3(D, subdiv_vert->co);
-       subdiv_vertex_data_interpolate(ctx,
-                                      subdiv_vert,
-                                      vertex_interpolation,
-                                      u, v);
-       BKE_subdiv_eval_limit_point_and_short_normal(
-               ctx->subdiv,
-               ptex_face_index,
-               u, v,
-               subdiv_vert->co, subdiv_vert->no);
+        * Needs to be backed up before copying data from original vertex. */
+       float D[3] = {0.0f, 0.0f, 0.0f};
+       if (ctx->have_displacement) {
+               copy_v3_v3(D, subdiv_vert->co);
+               mul_v3_fl(D, inv_num_accumulated);
+       }
+       /* Interpolate custom data and evaluate position. */
+       subdiv_vertex_data_interpolate(
+               ctx, subdiv_vert, vertex_interpolation, u, v);
+       BKE_subdiv_eval_limit_point(
+               ctx->subdiv, ptex_face_index, u, v, subdiv_vert->co);
        /* Apply displacement. */
        add_v3_v3(subdiv_vert->co, D);
        /* Apply displacement. */
        add_v3_v3(subdiv_vert->co, D);
+       /* Copy normal from accumulated storage. */
+       if (ctx->can_evaluate_normals) {
+               float N[3];
+               copy_v3_v3(N, ctx->accumulated_normals[subdiv_vertex_index]);
+               mul_v3_fl(N, inv_num_accumulated);
+               normalize_v3(N);
+               normal_float_to_short_v3(subdiv_vert->no, N);
+       }
 }
 
 static void subdiv_mesh_vertex_every_corner_or_edge(
 }
 
 static void subdiv_mesh_vertex_every_corner_or_edge(
@@ -610,12 +658,11 @@ static void subdiv_mesh_vertex_every_corner_or_edge(
         const int subdiv_vertex_index)
 {
        SubdivMeshContext *ctx = foreach_context->user_data;
         const int subdiv_vertex_index)
 {
        SubdivMeshContext *ctx = foreach_context->user_data;
-       Subdiv *subdiv = ctx->subdiv;
        Mesh *subdiv_mesh = ctx->subdiv_mesh;
        MVert *subdiv_mvert = subdiv_mesh->mvert;
        MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
        Mesh *subdiv_mesh = ctx->subdiv_mesh;
        MVert *subdiv_mvert = subdiv_mesh->mvert;
        MVert *subdiv_vert = &subdiv_mvert[subdiv_vertex_index];
-       subdiv_accumulate_vertex_displacement(
-               subdiv, ptex_face_index, u, v, subdiv_vert);
+       subdiv_accumulate_vertex_normal_and_displacement(
+               ctx, ptex_face_index, u, v, subdiv_vert);
 }
 
 static void subdiv_mesh_vertex_every_corner(
 }
 
 static void subdiv_mesh_vertex_every_corner(
@@ -969,8 +1016,7 @@ static void subdiv_mesh_vertex_loose(
 
 /* Get neighbor edges of the given one.
  * - neighbors[0] is an edge adjacent to edge->v1.
 
 /* Get neighbor edges of the given one.
  * - neighbors[0] is an edge adjacent to edge->v1.
- * - neighbors[1] is an edge adjacent to edge->v1.
- */
+ * - neighbors[1] is an edge adjacent to edge->v2. */
 static void find_edge_neighbors(const SubdivMeshContext *ctx,
                                 const MEdge *edge,
                                 const MEdge *neighbors[2])
 static void find_edge_neighbors(const SubdivMeshContext *ctx,
                                 const MEdge *edge,
                                 const MEdge *neighbors[2])
@@ -979,6 +1025,7 @@ static void find_edge_neighbors(const SubdivMeshContext *ctx,
        const MEdge *coarse_medge = coarse_mesh->medge;
        neighbors[0] = NULL;
        neighbors[1] = NULL;
        const MEdge *coarse_medge = coarse_mesh->medge;
        neighbors[0] = NULL;
        neighbors[1] = NULL;
+       int neighbor_counters[2] = {0, 0};
        for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) {
                const MEdge *current_edge = &coarse_medge[edge_index];
                if (current_edge == edge) {
        for (int edge_index = 0; edge_index < coarse_mesh->totedge; edge_index++) {
                const MEdge *current_edge = &coarse_medge[edge_index];
                if (current_edge == edge) {
@@ -986,11 +1033,22 @@ static void find_edge_neighbors(const SubdivMeshContext *ctx,
                }
                if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) {
                        neighbors[0] = current_edge;
                }
                if (ELEM(edge->v1, current_edge->v1, current_edge->v2)) {
                        neighbors[0] = current_edge;
+                       ++neighbor_counters[0];
                }
                if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) {
                        neighbors[1] = current_edge;
                }
                if (ELEM(edge->v2, current_edge->v1, current_edge->v2)) {
                        neighbors[1] = current_edge;
+                       ++neighbor_counters[1];
                }
        }
                }
        }
+       /* Vertices which has more than one neighbor are considered infinitely
+        * sharp. This is also how topology factory treats vertices of a surface
+        * which are adjacent to a loose edge. */
+       if (neighbor_counters[0] > 1) {
+               neighbors[0] = NULL;
+       }
+       if (neighbor_counters[1] > 1) {
+               neighbors[1] = NULL;
+       }
 }
 
 static void points_for_loose_edges_interpolation_get(
 }
 
 static void points_for_loose_edges_interpolation_get(
@@ -1032,6 +1090,44 @@ static void points_for_loose_edges_interpolation_get(
        }
 }
 
        }
 }
 
+static void subdiv_mesh_vertex_of_loose_edge_interpolate(
+        SubdivMeshContext *ctx,
+        const MEdge *coarse_edge,
+        const float u,
+        const int subdiv_vertex_index)
+{
+       const Mesh *coarse_mesh = ctx->coarse_mesh;
+       Mesh *subdiv_mesh = ctx->subdiv_mesh;
+       if (u == 0.0f) {
+               CustomData_copy_data(&coarse_mesh->vdata,
+                                    &subdiv_mesh->vdata,
+                                    coarse_edge->v1,
+                                    subdiv_vertex_index,
+                                    1);
+       }
+       else if (u == 1.0f) {
+               CustomData_copy_data(&coarse_mesh->vdata,
+                                    &subdiv_mesh->vdata,
+                                    coarse_edge->v2,
+                                    subdiv_vertex_index,
+                                    1);
+       }
+       else {
+               BLI_assert(u > 0.0f);
+               BLI_assert(u < 1.0f);
+               const float interpolation_weights[2] = {1.0f - u, u};
+               const int coarse_vertex_indices[2] = {coarse_edge->v1, coarse_edge->v2};
+               CustomData_interp(&coarse_mesh->vdata,
+                                 &subdiv_mesh->vdata,
+                                 coarse_vertex_indices,
+                                 interpolation_weights, NULL,
+                                 2, subdiv_vertex_index);
+               if (ctx->vert_origindex != NULL) {
+                       ctx->vert_origindex[subdiv_vertex_index] = ORIGINDEX_NONE;
+               }
+       }
+}
+
 static void subdiv_mesh_vertex_of_loose_edge(
         const struct SubdivForeachContext *foreach_context,
         void *UNUSED(tls),
 static void subdiv_mesh_vertex_of_loose_edge(
         const struct SubdivForeachContext *foreach_context,
         void *UNUSED(tls),
@@ -1054,7 +1150,10 @@ static void subdiv_mesh_vertex_of_loose_edge(
        /* Perform interpolation. */
        float weights[4];
        key_curve_position_weights(u, weights, KEY_BSPLINE);
        /* Perform interpolation. */
        float weights[4];
        key_curve_position_weights(u, weights, KEY_BSPLINE);
-
+       /* Interpolate custom data. */
+       subdiv_mesh_vertex_of_loose_edge_interpolate(
+               ctx, coarse_edge, u, subdiv_vertex_index);
+       /* Initialize  */
        MVert *subdiv_vertex = &subdiv_mvert[subdiv_vertex_index];
        interp_v3_v3v3v3v3(subdiv_vertex->co,
                           points[0],
        MVert *subdiv_vertex = &subdiv_mvert[subdiv_vertex_index];
        interp_v3_v3v3v3v3(subdiv_vertex->co,
                           points[0],
@@ -1064,25 +1163,28 @@ static void subdiv_mesh_vertex_of_loose_edge(
                           weights);
        /* Reset flags and such. */
        subdiv_vertex->flag = 0;
                           weights);
        /* Reset flags and such. */
        subdiv_vertex->flag = 0;
+       /* TODO(sergey): This matches old behavior, but we can as well interpolate
+        * it. Maybe even using vertex varying attributes. */
        subdiv_vertex->bweight = 0.0f;
        subdiv_vertex->bweight = 0.0f;
-       /* Reset normal. */
-       subdiv_vertex->no[0] = 0.0f;
-       subdiv_vertex->no[1] = 0.0f;
-       subdiv_vertex->no[2] = 1.0f;
+       /* Reset normal, initialize it in a similar way as edit mode does for a
+        * vertices adjacent to a loose edges. */
+       normal_float_to_short_v3(subdiv_vertex->no, subdiv_vertex->co);
 }
 
 /* =============================================================================
  * Initialization.
  */
 
 }
 
 /* =============================================================================
  * Initialization.
  */
 
-static void setup_foreach_callbacks(SubdivForeachContext *foreach_context,
-                                    const Subdiv *subdiv)
+static void setup_foreach_callbacks(const SubdivMeshContext *subdiv_context,
+                                    SubdivForeachContext *foreach_context)
 {
        memset(foreach_context, 0, sizeof(*foreach_context));
        /* General information. */
        foreach_context->topology_info = subdiv_mesh_topology_info;
 {
        memset(foreach_context, 0, sizeof(*foreach_context));
        /* General information. */
        foreach_context->topology_info = subdiv_mesh_topology_info;
-       /* Every boundary geometry. Used for dispalcement averaging. */
-       if (subdiv->displacement_evaluator != NULL) {
+       /* Every boundary geometry. Used for dispalcement and normals averaging. */
+       if (subdiv_context->can_evaluate_normals ||
+           subdiv_context->have_displacement)
+       {
                foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
                foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
        }
                foreach_context->vertex_every_corner = subdiv_mesh_vertex_every_corner;
                foreach_context->vertex_every_edge = subdiv_mesh_vertex_every_edge;
        }
@@ -1119,8 +1221,7 @@ Mesh *BKE_subdiv_to_mesh(
                 * - OpenSubdiv is disabled.
                 * - Something totally bad happened, and OpenSubdiv rejected our
                 *   topology.
                 * - OpenSubdiv is disabled.
                 * - Something totally bad happened, and OpenSubdiv rejected our
                 *   topology.
-                * In either way, we can't safely continue.
-                */
+                * In either way, we can't safely continue. */
                if (coarse_mesh->totpoly) {
                        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
                        return NULL;
                if (coarse_mesh->totpoly) {
                        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
                        return NULL;
@@ -1131,25 +1232,28 @@ Mesh *BKE_subdiv_to_mesh(
        subdiv_context.settings = settings;
        subdiv_context.coarse_mesh = coarse_mesh;
        subdiv_context.subdiv = subdiv;
        subdiv_context.settings = settings;
        subdiv_context.coarse_mesh = coarse_mesh;
        subdiv_context.subdiv = subdiv;
+       subdiv_context.have_displacement =
+               (subdiv->displacement_evaluator != NULL);
+       subdiv_context.can_evaluate_normals = !subdiv_context.have_displacement;
        /* Multi-threaded traversal/evaluation. */
        BKE_subdiv_stats_begin(&subdiv->stats,
                               SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
        SubdivForeachContext foreach_context;
        /* Multi-threaded traversal/evaluation. */
        BKE_subdiv_stats_begin(&subdiv->stats,
                               SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
        SubdivForeachContext foreach_context;
-       setup_foreach_callbacks(&foreach_context, subdiv);
+       setup_foreach_callbacks(&subdiv_context, &foreach_context);
        SubdivMeshTLS tls = {0};
        foreach_context.user_data = &subdiv_context;
        foreach_context.user_data_tls_size = sizeof(SubdivMeshTLS);
        foreach_context.user_data_tls = &tls;
        SubdivMeshTLS tls = {0};
        foreach_context.user_data = &subdiv_context;
        foreach_context.user_data_tls_size = sizeof(SubdivMeshTLS);
        foreach_context.user_data_tls = &tls;
-       BKE_subdiv_foreach_subdiv_geometry(subdiv,
-                                          &foreach_context,
-                                          settings,
-                                          coarse_mesh);
+       BKE_subdiv_foreach_subdiv_geometry(
+               subdiv, &foreach_context, settings, coarse_mesh);
        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
        Mesh *result = subdiv_context.subdiv_mesh;
        // BKE_mesh_validate(result, true, true);
        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH_GEOMETRY);
        Mesh *result = subdiv_context.subdiv_mesh;
        // BKE_mesh_validate(result, true, true);
        BKE_subdiv_stats_end(&subdiv->stats, SUBDIV_STATS_SUBDIV_TO_MESH);
-       if (subdiv->displacement_evaluator != NULL) {
+       if (!subdiv_context.can_evaluate_normals) {
                result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
        }
                result->runtime.cd_dirty_vert |= CD_MASK_NORMAL;
        }
+       /* Free used memoty. */
+       subdiv_mesh_context_free(&subdiv_context);
        return result;
 }
        return result;
 }