static void meshdeform_matrix_add_rhs(MeshDeformBind *mdb, int x, int y, int z, int cagevert) { MDefBoundIsect *isect; float rhs, weight, totweight; int i, a, acenter; acenter= meshdeform_index(mdb, x, y, z, 0); if(mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) return; totweight= meshdeform_boundary_total_weight(mdb, x, y, z); for(i=1; i<=6; i++) { a= meshdeform_index(mdb, x, y, z, i); if(a == -1) continue; isect= mdb->boundisect[acenter][i-1]; if (isect) { weight= (1.0f/isect->len)/totweight; rhs= weight*meshdeform_boundary_phi(mdb, isect, cagevert); nlRightHandSideAdd(0, mdb->varidx[acenter], rhs); } } }
void laplacian_add_right_hand_side(LaplacianSystem *UNUSED(sys), int v, float value) { nlRightHandSideAdd(0, v, value); }
static void laplaciansmoothModifier_do( LaplacianSmoothModifierData *smd, Object *ob, DerivedMesh *dm, float (*vertexCos)[3], int numVerts) { LaplacianSystem *sys; MDeformVert *dvert = NULL; MDeformVert *dv = NULL; float w, wpaint; int i, iter; int defgrp_index; DM_ensure_tessface(dm); sys = init_laplacian_system(dm->getNumEdges(dm), dm->getNumTessFaces(dm), numVerts); if (!sys) { return; } sys->mfaces = dm->getTessFaceArray(dm); sys->medges = dm->getEdgeArray(dm); sys->vertexCos = vertexCos; sys->min_area = 0.00001f; modifier_get_vgroup(ob, dm, smd->defgrp_name, &dvert, &defgrp_index); sys->vert_centroid[0] = 0.0f; sys->vert_centroid[1] = 0.0f; sys->vert_centroid[2] = 0.0f; for (iter = 0; iter < smd->repeat; iter++) { memset_laplacian_system(sys, 0); nlNewContext(); sys->context = nlGetCurrent(); nlSolverParameteri(NL_NB_VARIABLES, numVerts); nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); nlSolverParameteri(NL_NB_ROWS, numVerts); nlSolverParameteri(NL_NB_RIGHT_HAND_SIDES, 3); init_laplacian_matrix(sys); nlBegin(NL_SYSTEM); for (i = 0; i < numVerts; i++) { nlSetVariable(0, i, vertexCos[i][0]); nlSetVariable(1, i, vertexCos[i][1]); nlSetVariable(2, i, vertexCos[i][2]); if (iter == 0) { add_v3_v3(sys->vert_centroid, vertexCos[i]); } } if (iter == 0 && numVerts > 0) { mul_v3_fl(sys->vert_centroid, 1.0f / (float)numVerts); } nlBegin(NL_MATRIX); dv = dvert; for (i = 0; i < numVerts; i++) { nlRightHandSideAdd(0, i, vertexCos[i][0]); nlRightHandSideAdd(1, i, vertexCos[i][1]); nlRightHandSideAdd(2, i, vertexCos[i][2]); if (dv) { wpaint = defvert_find_weight(dv, defgrp_index); dv++; } else { wpaint = 1.0f; } if (sys->zerola[i] == 0) { if (smd->flag & MOD_LAPLACIANSMOOTH_NORMALIZED) { w = sys->vweights[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda) * wpaint / w; w = sys->vlengths[i]; sys->vlengths[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda_border) * wpaint * 2.0f / w; if (sys->numNeEd[i] == sys->numNeFa[i]) { nlMatrixAdd(i, i, 1.0f + fabsf(smd->lambda) * wpaint); } else { nlMatrixAdd(i, i, 1.0f + fabsf(smd->lambda_border) * wpaint * 2.0f); } } else { w = sys->vweights[i] * sys->ring_areas[i]; sys->vweights[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda) * wpaint / (4.0f * w); w = sys->vlengths[i]; sys->vlengths[i] = (w == 0.0f) ? 0.0f : -fabsf(smd->lambda_border) * wpaint * 2.0f / w; if (sys->numNeEd[i] == sys->numNeFa[i]) { nlMatrixAdd(i, i, 1.0f + fabsf(smd->lambda) * wpaint / (4.0f * sys->ring_areas[i])); } else { nlMatrixAdd(i, i, 1.0f + fabsf(smd->lambda_border) * wpaint * 2.0f); } } } else { nlMatrixAdd(i, i, 1.0f); } } fill_laplacian_matrix(sys); nlEnd(NL_MATRIX); nlEnd(NL_SYSTEM); if (nlSolveAdvanced(NULL, NL_TRUE)) { validate_solution(sys, smd->flag, smd->lambda, smd->lambda_border); } nlDeleteContext(sys->context); sys->context = NULL; } delete_laplacian_system(sys); }
/** * This method computes the Laplacian Matrix and Differential Coordinates for all vertex in the mesh. * The Linear system is LV = d * Where L is Laplacian Matrix, V as the vertexes in Mesh, d is the differential coordinates * The Laplacian Matrix is computes as a * Lij = sum(Wij) (if i == j) * Lij = Wij (if i != j) * Wij is weight between vertex Vi and vertex Vj, we use cotangent weight * * The Differential Coordinate is computes as a * di = Vi * sum(Wij) - sum(Wij * Vj) * Where : * di is the Differential Coordinate i * sum (Wij) is the sum of all weights between vertex Vi and its vertexes neighbors (Vj) * sum (Wij * Vj) is the sum of the product between vertex neighbor Vj and weight Wij for all neighborhood. * * This Laplacian Matrix is described in the paper: * Desbrun M. et.al, Implicit fairing of irregular meshes using diffusion and curvature flow, SIGGRAPH '99, pag 317-324, * New York, USA * * The computation of Laplace Beltrami operator on Hybrid Triangle/Quad Meshes is described in the paper: * Pinzon A., Romero E., Shape Inflation With an Adapted Laplacian Operator For Hybrid Quad/Triangle Meshes, * Conference on Graphics Patterns and Images, SIBGRAPI, 2013 * * The computation of Differential Coordinates is described in the paper: * Sorkine, O. Laplacian Surface Editing. Proceedings of the EUROGRAPHICS/ACM SIGGRAPH Symposium on Geometry Processing, * 2004. p. 179-188. */ static void initLaplacianMatrix(LaplacianSystem *sys) { float v1[3], v2[3], v3[3], v4[3], no[3]; float w2, w3, w4; int i, j, fi; bool has_4_vert; unsigned int idv1, idv2, idv3, idv4; for (fi = 0; fi < sys->total_faces; fi++) { const unsigned int *vidf = sys->faces[fi]; idv1 = vidf[0]; idv2 = vidf[1]; idv3 = vidf[2]; idv4 = vidf[3]; has_4_vert = vidf[3] ? 1 : 0; if (has_4_vert) { normal_quad_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3], sys->co[idv4]); add_v3_v3(sys->no[idv4], no); i = 4; } else { normal_tri_v3(no, sys->co[idv1], sys->co[idv2], sys->co[idv3]); i = 3; } add_v3_v3(sys->no[idv1], no); add_v3_v3(sys->no[idv2], no); add_v3_v3(sys->no[idv3], no); for (j = 0; j < i; j++) { idv1 = vidf[j]; idv2 = vidf[(j + 1) % i]; idv3 = vidf[(j + 2) % i]; idv4 = has_4_vert ? vidf[(j + 3) % i] : 0; copy_v3_v3(v1, sys->co[idv1]); copy_v3_v3(v2, sys->co[idv2]); copy_v3_v3(v3, sys->co[idv3]); if (has_4_vert) { copy_v3_v3(v4, sys->co[idv4]); } if (has_4_vert) { w2 = (cotan_weight(v4, v1, v2) + cotan_weight(v3, v1, v2)) / 2.0f; w3 = (cotan_weight(v2, v3, v1) + cotan_weight(v4, v1, v3)) / 2.0f; w4 = (cotan_weight(v2, v4, v1) + cotan_weight(v3, v4, v1)) / 2.0f; sys->delta[idv1][0] -= v4[0] * w4; sys->delta[idv1][1] -= v4[1] * w4; sys->delta[idv1][2] -= v4[2] * w4; nlRightHandSideAdd(0, idv1, -v4[0] * w4); nlRightHandSideAdd(1, idv1, -v4[1] * w4); nlRightHandSideAdd(2, idv1, -v4[2] * w4); nlMatrixAdd(idv1, idv4, -w4); } else { w2 = cotan_weight(v3, v1, v2); w3 = cotan_weight(v2, v3, v1); w4 = 0.0f; } sys->delta[idv1][0] += v1[0] * (w2 + w3 + w4); sys->delta[idv1][1] += v1[1] * (w2 + w3 + w4); sys->delta[idv1][2] += v1[2] * (w2 + w3 + w4); sys->delta[idv1][0] -= v2[0] * w2; sys->delta[idv1][1] -= v2[1] * w2; sys->delta[idv1][2] -= v2[2] * w2; sys->delta[idv1][0] -= v3[0] * w3; sys->delta[idv1][1] -= v3[1] * w3; sys->delta[idv1][2] -= v3[2] * w3; nlMatrixAdd(idv1, idv2, -w2); nlMatrixAdd(idv1, idv3, -w3); nlMatrixAdd(idv1, idv1, w2 + w3 + w4); } } }