Fix Editderivedmeshes vertices normals computation.

Those derived meshes (used in Edit mode) were using simple sum of neighbor poly normals to get vertex normals,
while everywhere else in Blender we use weighted sum of such poly normals.

Patch: D311

Reviewed and enhanced by Campbell, thanks!
This commit is contained in:
Bastien Montagne 2014-02-12 20:48:09 +01:00
parent f9a60ecacd
commit 70905a6e02
3 changed files with 112 additions and 73 deletions

View File

@ -92,35 +92,16 @@ static void emDM_ensureVertNormals(EditDerivedBMesh *bmdm)
const float (*vertexCos)[3], (*polyNos)[3];
float (*vertexNos)[3];
BMFace *efa;
BMVert *eve;
BMIter fiter;
BMIter viter;
int i;
vertexCos = bmdm->vertexCos;
vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__);
/* calculate vertex normals from poly normals */
emDM_ensurePolyNormals(bmdm);
BM_mesh_elem_index_ensure(bm, BM_FACE);
vertexCos = bmdm->vertexCos;
polyNos = bmdm->polyNos;
vertexCos = bmdm->vertexCos;
vertexNos = MEM_callocN(sizeof(*vertexNos) * bm->totvert, __func__);
BM_ITER_MESH_INDEX (eve, &viter, bm, BM_VERTS_OF_MESH, i) {
float *no = vertexNos[i];
BM_ITER_ELEM (efa, &fiter, eve, BM_FACES_OF_VERT) {
add_v3_v3(no, polyNos[BM_elem_index_get(efa)]);
}
/* following Mesh convention; we use vertex coordinate itself
* for normal in this case */
if (UNLIKELY(normalize_v3(no) == 0.0f)) {
normalize_v3_v3(no, vertexCos[i]);
}
}
BM_verts_calc_normal_vcos(bm, polyNos, vertexCos, vertexNos);
bmdm->vertexNos = (const float (*)[3])vertexNos;
}

View File

@ -272,71 +272,55 @@ void BM_mesh_free(BMesh *bm)
}
/**
* \brief BMesh Compute Normals
*
* Updates the normals of a mesh.
* Helpers for #BM_mesh_normals_update and #BM_verts_calc_normal_vcos
*/
void BM_mesh_normals_update(BMesh *bm)
static void bm_mesh_edges_calc_vectors(BMesh *bm, float (*edgevec)[3], const float (*vcos)[3])
{
float (*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
BMIter eiter;
BMEdge *e;
int index;
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
{
#pragma omp section
{
/* calculate all face normals */
BMIter fiter;
BMFace *f;
if (vcos) {
BM_mesh_elem_index_ensure(bm, BM_VERT);
}
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
BM_face_normal_update(f);
}
BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index) {
BM_elem_index_set(e, index); /* set_inline */
if (e->l) {
const float *v1_co = vcos ? vcos[BM_elem_index_get(e->v1)] : e->v1->co;
const float *v2_co = vcos ? vcos[BM_elem_index_get(e->v2)] : e->v2->co;
sub_v3_v3v3(edgevec[index], v2_co, v1_co);
normalize_v3(edgevec[index]);
}
#pragma omp section
{
/* Zero out vertex normals */
BMIter viter;
BMVert *v;
BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
zero_v3(v->no);
}
}
#pragma omp section
{
/* compute normalized direction vectors for each edge. directions will be
* used below for calculating the weights of the face normals on the vertex
* normals */
BMIter eiter;
BMEdge *e;
int index;
BM_ITER_MESH_INDEX (e, &eiter, bm, BM_EDGES_OF_MESH, index) {
BM_elem_index_set(e, index); /* set_inline */
if (e->l) {
sub_v3_v3v3(edgevec[index], e->v2->co, e->v1->co);
normalize_v3(edgevec[index]);
}
else {
/* the edge vector will not be needed when the edge has no radial */
}
}
bm->elem_index_dirty &= ~BM_EDGE;
else {
/* the edge vector will not be needed when the edge has no radial */
}
}
/* end omp */
bm->elem_index_dirty &= ~BM_EDGE;
}
static void bm_mesh_verts_calc_normals(BMesh *bm, const float (*edgevec)[3], const float (*fnos)[3],
const float (*vcos)[3], float (*vnos)[3])
{
BM_mesh_elem_index_ensure(bm, (vnos) ? (BM_EDGE | BM_VERT) : BM_EDGE);
/* add weighted face normals to vertices */
{
BMIter fiter;
BMFace *f;
BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
int i;
BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
BMLoop *l_first, *l_iter;
const float *f_no = fnos ? fnos[i] : f->no;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
const float *e1diff, *e2diff;
float dotprod;
float fac;
float *v_no = vnos ? vnos[BM_elem_index_get(l_iter->v)] : l_iter->v->no;
/* calculate the dot product of the two edges that
* meet at the loop's vertex */
@ -354,10 +338,9 @@ void BM_mesh_normals_update(BMesh *bm)
fac = saacos(-dotprod);
/* accumulate weighted face normal into the vertex's normal */
madd_v3_v3fl(l_iter->v->no, f->no, fac);
madd_v3_v3fl(v_no, f_no, fac);
} while ((l_iter = l_iter->next) != l_first);
}
MEM_freeN(edgevec);
}
@ -365,15 +348,89 @@ void BM_mesh_normals_update(BMesh *bm)
{
BMIter viter;
BMVert *v;
int i;
BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
if (UNLIKELY(normalize_v3(v->no) == 0.0f)) {
normalize_v3_v3(v->no, v->co);
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
float *v_no = vnos ? vnos[i] : v->no;
if (UNLIKELY(normalize_v3(v_no) == 0.0f)) {
const float *v_co = vcos ? vcos[i] : v->co;
normalize_v3_v3(v_no, v_co);
}
}
}
}
/**
* \brief BMesh Compute Normals
*
* Updates the normals of a mesh.
*/
void BM_mesh_normals_update(BMesh *bm)
{
float (*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
#pragma omp parallel sections if (bm->totvert + bm->totedge + bm->totface >= BM_OMP_LIMIT)
{
#pragma omp section
{
/* calculate all face normals */
BMIter fiter;
BMFace *f;
int i;
BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) {
BM_elem_index_set(f, i); /* set_inline */
BM_face_normal_update(f);
}
bm->elem_index_dirty &= ~BM_FACE;
}
#pragma omp section
{
/* Zero out vertex normals */
BMIter viter;
BMVert *v;
int i;
BM_ITER_MESH_INDEX (v, &viter, bm, BM_VERTS_OF_MESH, i) {
BM_elem_index_set(v, i); /* set_inline */
zero_v3(v->no);
}
bm->elem_index_dirty &= ~BM_VERT;
}
#pragma omp section
{
/* Compute normalized direction vectors for each edge.
* Directions will be used for calculating the weights of the face normals on the vertex normals.
*/
bm_mesh_edges_calc_vectors(bm, edgevec, NULL);
}
}
/* end omp */
/* Add weighted face normals to vertices, and normalize vert normals. */
bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, NULL, NULL, NULL);
MEM_freeN(edgevec);
}
/**
* \brief BMesh Compute Normals from/to external data.
*
* Computes the vertex normals of a mesh into vnos, using given vertex coordinates (vcos) and polygon normals (fnos).
*/
void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3])
{
float (*edgevec)[3] = MEM_mallocN(sizeof(*edgevec) * bm->totedge, __func__);
/* Compute normalized direction vectors for each edge.
* Directions will be used for calculating the weights of the face normals on the vertex normals.
*/
bm_mesh_edges_calc_vectors(bm, edgevec, vcos);
/* Add weighted face normals to vertices, and normalize vert normals. */
bm_mesh_verts_calc_normals(bm, (const float(*)[3])edgevec, fnos, vcos, vnos);
MEM_freeN(edgevec);
}
static void UNUSED_FUNCTION(bm_mdisps_space_set)(Object *ob, BMesh *bm, int from, int to)
{
/* switch multires data out of tangent space */

View File

@ -38,6 +38,7 @@ void BM_mesh_data_free(BMesh *bm);
void BM_mesh_clear(BMesh *bm);
void BM_mesh_normals_update(BMesh *bm);
void BM_verts_calc_normal_vcos(BMesh *bm, const float (*fnos)[3], const float (*vcos)[3], float (*vnos)[3]);
void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag);
void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag);