diff options
-rw-r--r-- | source/blender/blenkernel/BKE_subdiv_ccg.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/subdiv_ccg.c | 168 |
2 files changed, 167 insertions, 4 deletions
diff --git a/source/blender/blenkernel/BKE_subdiv_ccg.h b/source/blender/blenkernel/BKE_subdiv_ccg.h index c760fb04fd3..ed5ef0e19f5 100644 --- a/source/blender/blenkernel/BKE_subdiv_ccg.h +++ b/source/blender/blenkernel/BKE_subdiv_ccg.h @@ -158,4 +158,7 @@ void BKE_subdiv_ccg_key( void BKE_subdiv_ccg_key_top_level( struct CCGKey *key, const SubdivCCG *subdiv_ccg); +/* Recalculate all normals based on grid element coordinates. */ +void BKE_subdiv_ccg_recalc_normals(SubdivCCG *subdiv_ccg); + #endif /* __BKE_SUBDIV_CCG_H__ */ diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 649b4d3a3dc..d9c0c721cd5 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -35,6 +35,7 @@ #include "MEM_guardedalloc.h" #include "BLI_math_bits.h" +#include "BLI_math_vector.h" #include "BLI_task.h" #include "BKE_DerivedMesh.h" @@ -167,8 +168,11 @@ static void subdiv_ccg_eval_grid_element( const float u, const float v, unsigned char *element) { - /* TODO(sergey): Support displacement. */ - if (data->subdiv_ccg->has_normal) { + if (data->subdiv->displacement_evaluator != NULL) { + BKE_subdiv_eval_final_point( + data->subdiv, ptex_face_index, u, v, (float *)element); + } + else if (data->subdiv_ccg->has_normal) { BKE_subdiv_eval_limit_point_and_normal( data->subdiv, ptex_face_index, u, v, (float *)element, @@ -299,18 +303,24 @@ static bool subdiv_ccg_evaluate_grids( data.subdiv = subdiv; data.coarse_mesh = coarse_mesh; data.face_ptex_offset = BKE_subdiv_face_ptex_offset_get(subdiv); - /* Threaded grids evaluation/ */ + /* Threaded grids evaluation. */ ParallelRangeSettings parallel_range_settings; BLI_parallel_range_settings_defaults(¶llel_range_settings); BLI_task_parallel_range(0, coarse_mesh->totpoly, &data, subdiv_ccg_eval_grids_task, ¶llel_range_settings); + /* If displacement is used, need to calculate normals after all final + * coordinates are known. + */ + if (subdiv->displacement_evaluator != NULL) { + BKE_subdiv_ccg_recalc_normals(subdiv_ccg); + } return true; } /* ============================================================================= - * Public API. + * Creation / evaluation. */ SubdivCCG *BKE_subdiv_to_ccg( @@ -390,3 +400,153 @@ void BKE_subdiv_ccg_key_top_level(CCGKey *key, const SubdivCCG *subdiv_ccg) { BKE_subdiv_ccg_key(key, subdiv_ccg, subdiv_ccg->level); } + +/* ============================================================================= + * Normals. + */ + +typedef struct RecalcInnerNormalsData { + SubdivCCG *subdiv_ccg; + CCGKey *key; +} RecalcInnerNormalsData; + +typedef struct RecalcInnerNormalsTLSData { + float (*face_normals)[3]; +} RecalcInnerNormalsTLSData; + +/* Evaluate high-res face normals, for faces which corresponds to grid elements + * + * {(x, y), {x + 1, y}, {x + 1, y + 1}, {x, y + 1}} + * + * The result is stored in normals storage from TLS. + */ +static void subdiv_ccg_recalc_inner_face_normals( + RecalcInnerNormalsData *data, + RecalcInnerNormalsTLSData *tls, + const int grid_index) +{ + SubdivCCG *subdiv_ccg = data->subdiv_ccg; + CCGKey *key = data->key; + const int grid_size = subdiv_ccg->grid_size; + const int grid_size_1 = grid_size - 1; + CCGElem *grid = subdiv_ccg->grids[grid_index]; + if (tls->face_normals == NULL) { + tls->face_normals = MEM_malloc_arrayN( + grid_size_1 * grid_size_1, + 3 * sizeof(float), + "CCG TLS normals"); + } + for (int y = 0; y < grid_size -1; y++) { + for (int x = 0; x < grid_size - 1; x++) { + CCGElem *grid_elements[4] = { + CCG_grid_elem(key, grid, x, y + 1), + CCG_grid_elem(key, grid, x + 1, y + 1), + CCG_grid_elem(key, grid, x + 1, y), + CCG_grid_elem(key, grid, x, y) + }; + float *co[4] = { + CCG_elem_co(key, grid_elements[0]), + CCG_elem_co(key, grid_elements[1]), + CCG_elem_co(key, grid_elements[2]), + CCG_elem_co(key, grid_elements[3]) + }; + const int face_index = y * grid_size_1 + x; + float *face_normal = tls->face_normals[face_index]; + normal_quad_v3(face_normal, co[0], co[1], co[2], co[3]); + } + } +} + +/* Average normals at every grid element, using adjacent faces normals. */ +static void subdiv_ccg_average_inner_face_normals( + RecalcInnerNormalsData *data, + RecalcInnerNormalsTLSData *tls, + const int grid_index) +{ + SubdivCCG *subdiv_ccg = data->subdiv_ccg; + CCGKey *key = data->key; + const int grid_size = subdiv_ccg->grid_size; + const int grid_size_1 = grid_size - 1; + CCGElem *grid = subdiv_ccg->grids[grid_index]; + const float (*face_normals)[3] = tls->face_normals; + for (int y = 0; y < grid_size; y++) { + for (int x = 0; x < grid_size; x++) { + float normal_acc[3] = {0.0f, 0.0f, 0.0f}; + int counter = 0; + /* Accumulate normals of all adjacent faces. */ + if (x < grid_size_1 && y < grid_size_1) { + add_v3_v3(normal_acc, face_normals[y * grid_size_1 + x]); + counter++; + } + if (x >= 1) { + if (y < grid_size_1) { + add_v3_v3(normal_acc, + face_normals[y * grid_size_1 + (x - 1)]); + counter++; + } + if (y >= 1) { + add_v3_v3(normal_acc, + face_normals[(y - 1) * grid_size_1 + (x - 1)]); + counter++; + } + } + if (y >= 1 && x < grid_size_1) { + add_v3_v3(normal_acc, face_normals[(y - 1) * grid_size_1 + x]); + counter++; + } + /* Normalize and store. */ + mul_v3_v3fl(CCG_grid_elem_no(key, grid, x, y), + normal_acc, + 1.0f / (float)counter); + } + } +} + +static void subdiv_ccg_recalc_inner_normal_task( + void *__restrict userdata_v, + const int grid_index, + const ParallelRangeTLS *__restrict tls_v) +{ + RecalcInnerNormalsData *data = userdata_v; + RecalcInnerNormalsTLSData *tls = tls_v->userdata_chunk; + subdiv_ccg_recalc_inner_face_normals(data, tls, grid_index); + subdiv_ccg_average_inner_face_normals(data, tls, grid_index); +} + +static void subdiv_ccg_recalc_inner_normal_finalize( + void *__restrict UNUSED(userdata), + void *__restrict tls_v) +{ + RecalcInnerNormalsTLSData *tls = tls_v; + MEM_SAFE_FREE(tls->face_normals); +} + +/* Recalculate normals which corresponds to non-boundaries elements of grids. */ +static void subdiv_ccg_recalc_inner_grid_normals(SubdivCCG *subdiv_ccg) +{ + CCGKey key; + BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg); + RecalcInnerNormalsData data = { + .subdiv_ccg = subdiv_ccg, + .key = &key}; + RecalcInnerNormalsTLSData tls_data = {NULL}; + ParallelRangeSettings parallel_range_settings; + BLI_parallel_range_settings_defaults(¶llel_range_settings); + parallel_range_settings.userdata_chunk = &tls_data; + parallel_range_settings.userdata_chunk_size = sizeof(tls_data); + parallel_range_settings.func_finalize = + subdiv_ccg_recalc_inner_normal_finalize; + BLI_task_parallel_range(0, subdiv_ccg->num_grids, + &data, + subdiv_ccg_recalc_inner_normal_task, + ¶llel_range_settings); +} + +void BKE_subdiv_ccg_recalc_normals(SubdivCCG *subdiv_ccg) +{ + if (!subdiv_ccg->has_normal) { + /* Grids don't have normals, can do early output. */ + return; + } + subdiv_ccg_recalc_inner_grid_normals(subdiv_ccg); +} |