static void meshdeform_matrix_add_cell(MeshDeformBind *mdb, LinearSolver *context, int x, int y, int z) { MDefBoundIsect *isect; float weight, totweight; int i, a, acenter; acenter = meshdeform_index(mdb, x, y, z, 0); if (mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR) return; EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[acenter], 1.0f); 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 || mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR) continue; isect = mdb->boundisect[acenter][i - 1]; if (!isect) { weight = (1.0f / mdb->width[0]) / totweight; EIG_linear_solver_matrix_add(context, mdb->varidx[acenter], mdb->varidx[a], -weight); } } }
static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3) { float t1, t2, t3; float *varea = sys->varea, *v1, *v2, *v3; v1 = sys->verts[i1]; v2 = sys->verts[i2]; v3 = sys->verts[i3]; /* instead of *0.5 we divided by the number of faces of the edge, it still * needs to be verified that this is indeed the correct thing to do! */ t1 = cotangent_tri_weight_v3(v1, v2, v3) / laplacian_edge_count(sys->edgehash, i2, i3); t2 = cotangent_tri_weight_v3(v2, v3, v1) / laplacian_edge_count(sys->edgehash, i3, i1); t3 = cotangent_tri_weight_v3(v3, v1, v2) / laplacian_edge_count(sys->edgehash, i1, i2); EIG_linear_solver_matrix_add(sys->context, i1, i1, (t2 + t3) * varea[i1]); EIG_linear_solver_matrix_add(sys->context, i2, i2, (t1 + t3) * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i3, i3, (t1 + t2) * varea[i3]); EIG_linear_solver_matrix_add(sys->context, i1, i2, -t3 * varea[i1]); EIG_linear_solver_matrix_add(sys->context, i2, i1, -t3 * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i2, i3, -t1 * varea[i2]); EIG_linear_solver_matrix_add(sys->context, i3, i2, -t1 * varea[i3]); EIG_linear_solver_matrix_add(sys->context, i3, i1, -t2 * varea[i3]); EIG_linear_solver_matrix_add(sys->context, i1, i3, -t2 * varea[i1]); if (sys->storeweights) { sys->fweights[f][0] = t1 * varea[i1]; sys->fweights[f][1] = t2 * varea[i2]; sys->fweights[f][2] = t3 * varea[i3]; } }
/** * 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 no[3]; float w2, w3; int i = 3, j, ti; int idv[3]; for (ti = 0; ti < sys->total_tris; ti++) { const unsigned int *vidt = sys->tris[ti]; const float *co[3]; co[0] = sys->co[vidt[0]]; co[1] = sys->co[vidt[1]]; co[2] = sys->co[vidt[2]]; normal_tri_v3(no, UNPACK3(co)); add_v3_v3(sys->no[vidt[0]], no); add_v3_v3(sys->no[vidt[1]], no); add_v3_v3(sys->no[vidt[2]], no); for (j = 0; j < 3; j++) { const float *v1, *v2, *v3; idv[0] = vidt[j]; idv[1] = vidt[(j + 1) % i]; idv[2] = vidt[(j + 2) % i]; v1 = sys->co[idv[0]]; v2 = sys->co[idv[1]]; v3 = sys->co[idv[2]]; w2 = cotangent_tri_weight_v3(v3, v1, v2); w3 = cotangent_tri_weight_v3(v2, v3, v1); sys->delta[idv[0]][0] += v1[0] * (w2 + w3); sys->delta[idv[0]][1] += v1[1] * (w2 + w3); sys->delta[idv[0]][2] += v1[2] * (w2 + w3); sys->delta[idv[0]][0] -= v2[0] * w2; sys->delta[idv[0]][1] -= v2[1] * w2; sys->delta[idv[0]][2] -= v2[2] * w2; sys->delta[idv[0]][0] -= v3[0] * w3; sys->delta[idv[0]][1] -= v3[1] * w3; sys->delta[idv[0]][2] -= v3[2] * w3; EIG_linear_solver_matrix_add(sys->context, idv[0], idv[1], -w2); EIG_linear_solver_matrix_add(sys->context, idv[0], idv[2], -w3); EIG_linear_solver_matrix_add(sys->context, idv[0], idv[0], w2 + w3); } } }
static void fill_laplacian_matrix(LaplacianSystem *sys) { int i; unsigned int idv1, idv2; for (i = 0; i < sys->numPolys; i++) { const MPoly *mp = &sys->mpoly[i]; const MLoop *l_next = &sys->mloop[mp->loopstart]; const MLoop *l_term = l_next + mp->totloop; const MLoop *l_prev = l_term - 2; const MLoop *l_curr = l_term - 1; for (; l_next != l_term; l_prev = l_curr, l_curr = l_next, l_next++) { const unsigned int l_curr_index = l_curr - sys->mloop; /* Is ring if number of faces == number of edges around vertice*/ if (sys->numNeEd[l_curr->v] == sys->numNeFa[l_curr->v] && sys->zerola[l_curr->v] == 0) { EIG_linear_solver_matrix_add(sys->context, l_curr->v, l_next->v, sys->fweights[l_curr_index][2] * sys->vweights[l_curr->v]); EIG_linear_solver_matrix_add(sys->context, l_curr->v, l_prev->v, sys->fweights[l_curr_index][1] * sys->vweights[l_curr->v]); } if (sys->numNeEd[l_next->v] == sys->numNeFa[l_next->v] && sys->zerola[l_next->v] == 0) { EIG_linear_solver_matrix_add(sys->context, l_next->v, l_curr->v, sys->fweights[l_curr_index][2] * sys->vweights[l_next->v]); EIG_linear_solver_matrix_add(sys->context, l_next->v, l_prev->v, sys->fweights[l_curr_index][0] * sys->vweights[l_next->v]); } if (sys->numNeEd[l_prev->v] == sys->numNeFa[l_prev->v] && sys->zerola[l_prev->v] == 0) { EIG_linear_solver_matrix_add(sys->context, l_prev->v, l_curr->v, sys->fweights[l_curr_index][1] * sys->vweights[l_prev->v]); EIG_linear_solver_matrix_add(sys->context, l_prev->v, l_next->v, sys->fweights[l_curr_index][0] * sys->vweights[l_prev->v]); } } } for (i = 0; i < sys->numEdges; i++) { idv1 = sys->medges[i].v1; idv2 = sys->medges[i].v2; /* Is boundary */ if (sys->numNeEd[idv1] != sys->numNeFa[idv1] && sys->numNeEd[idv2] != sys->numNeFa[idv2] && sys->zerola[idv1] == 0 && sys->zerola[idv2] == 0) { EIG_linear_solver_matrix_add(sys->context, idv1, idv2, sys->eweights[i] * sys->vlengths[idv1]); EIG_linear_solver_matrix_add(sys->context, idv2, idv1, sys->eweights[i] * sys->vlengths[idv2]); } } }
static void laplacian_system_construct_end(LaplacianSystem *sys) { int (*face)[3]; int a, totvert = sys->totvert, totface = sys->totface; laplacian_begin_solve(sys, 0); sys->varea = MEM_callocN(sizeof(float) * totvert, "LaplacianSystemVarea"); sys->edgehash = BLI_edgehash_new_ex(__func__, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(sys->totface)); for (a = 0, face = sys->faces; a < sys->totface; a++, face++) { laplacian_increase_edge_count(sys->edgehash, (*face)[0], (*face)[1]); laplacian_increase_edge_count(sys->edgehash, (*face)[1], (*face)[2]); laplacian_increase_edge_count(sys->edgehash, (*face)[2], (*face)[0]); } if (sys->areaweights) for (a = 0, face = sys->faces; a < sys->totface; a++, face++) laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]); for (a = 0; a < totvert; a++) { if (sys->areaweights) { if (sys->varea[a] != 0.0f) sys->varea[a] = 0.5f / sys->varea[a]; } else sys->varea[a] = 1.0f; /* for heat weighting */ if (sys->heat.H) EIG_linear_solver_matrix_add(sys->context, a, a, sys->heat.H[a]); } if (sys->storeweights) sys->fweights = MEM_callocN(sizeof(float) * 3 * totface, "LaplacianFWeight"); for (a = 0, face = sys->faces; a < totface; a++, face++) laplacian_triangle_weights(sys, a, (*face)[0], (*face)[1], (*face)[2]); MEM_freeN(sys->faces); sys->faces = NULL; if (sys->varea) { MEM_freeN(sys->varea); sys->varea = NULL; } BLI_edgehash_free(sys->edgehash, NULL); sys->edgehash = NULL; }
static void laplacianDeformPreview(LaplacianSystem *sys, float (*vertexCos)[3]) { int vid, i, j, n, na; n = sys->total_verts; na = sys->total_anchors; if (!sys->is_matrix_computed) { sys->context = EIG_linear_least_squares_solver_new(n + na, n, 3); for (i = 0; i < n; i++) { EIG_linear_solver_variable_set(sys->context, 0, i, sys->co[i][0]); EIG_linear_solver_variable_set(sys->context, 1, i, sys->co[i][1]); EIG_linear_solver_variable_set(sys->context, 2, i, sys->co[i][2]); } for (i = 0; i < na; i++) { vid = sys->index_anchors[i]; EIG_linear_solver_variable_set(sys->context, 0, vid, vertexCos[vid][0]); EIG_linear_solver_variable_set(sys->context, 1, vid, vertexCos[vid][1]); EIG_linear_solver_variable_set(sys->context, 2, vid, vertexCos[vid][2]); } initLaplacianMatrix(sys); computeImplictRotations(sys); for (i = 0; i < n; i++) { EIG_linear_solver_right_hand_side_add(sys->context, 0, i, sys->delta[i][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, i, sys->delta[i][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, i, sys->delta[i][2]); } for (i = 0; i < na; i++) { vid = sys->index_anchors[i]; EIG_linear_solver_right_hand_side_add(sys->context, 0, n + i, vertexCos[vid][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, n + i, vertexCos[vid][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, n + i, vertexCos[vid][2]); EIG_linear_solver_matrix_add(sys->context, n + i, vid, 1.0f); } if (EIG_linear_solver_solve(sys->context)) { sys->has_solution = true; for (j = 1; j <= sys->repeat; j++) { rotateDifferentialCoordinates(sys); for (i = 0; i < na; i++) { vid = sys->index_anchors[i]; EIG_linear_solver_right_hand_side_add(sys->context, 0, n + i, vertexCos[vid][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, n + i, vertexCos[vid][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, n + i, vertexCos[vid][2]); } if (!EIG_linear_solver_solve(sys->context)) { sys->has_solution = false; break; } } if (sys->has_solution) { for (vid = 0; vid < sys->total_verts; vid++) { vertexCos[vid][0] = EIG_linear_solver_variable_get(sys->context, 0, vid); vertexCos[vid][1] = EIG_linear_solver_variable_get(sys->context, 1, vid); vertexCos[vid][2] = EIG_linear_solver_variable_get(sys->context, 2, vid); } } else { sys->has_solution = false; } } else { sys->has_solution = false; } sys->is_matrix_computed = true; } else if (sys->has_solution) { for (i = 0; i < n; i++) { EIG_linear_solver_right_hand_side_add(sys->context, 0, i, sys->delta[i][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, i, sys->delta[i][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, i, sys->delta[i][2]); } for (i = 0; i < na; i++) { vid = sys->index_anchors[i]; EIG_linear_solver_right_hand_side_add(sys->context, 0, n + i, vertexCos[vid][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, n + i, vertexCos[vid][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, n + i, vertexCos[vid][2]); EIG_linear_solver_matrix_add(sys->context, n + i, vid, 1.0f); } if (EIG_linear_solver_solve(sys->context)) { sys->has_solution = true; for (j = 1; j <= sys->repeat; j++) { rotateDifferentialCoordinates(sys); for (i = 0; i < na; i++) { vid = sys->index_anchors[i]; EIG_linear_solver_right_hand_side_add(sys->context, 0, n + i, vertexCos[vid][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, n + i, vertexCos[vid][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, n + i, vertexCos[vid][2]); } if (!EIG_linear_solver_solve(sys->context)) { sys->has_solution = false; break; } } if (sys->has_solution) { for (vid = 0; vid < sys->total_verts; vid++) { vertexCos[vid][0] = EIG_linear_solver_variable_get(sys->context, 0, vid); vertexCos[vid][1] = EIG_linear_solver_variable_get(sys->context, 1, vid); vertexCos[vid][2] = EIG_linear_solver_variable_get(sys->context, 2, vid); } } else { sys->has_solution = false; } } else { sys->has_solution = false; } } }
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; sys = init_laplacian_system(dm->getNumEdges(dm), dm->getNumPolys(dm), dm->getNumLoops(dm), numVerts); if (!sys) { return; } sys->mpoly = dm->getPolyArray(dm); sys->mloop = dm->getLoopArray(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; memset_laplacian_system(sys, 0); sys->context = EIG_linear_least_squares_solver_new(numVerts, numVerts, 3); init_laplacian_matrix(sys); for (iter = 0; iter < smd->repeat; iter++) { for (i = 0; i < numVerts; i++) { EIG_linear_solver_variable_set(sys->context, 0, i, vertexCos[i][0]); EIG_linear_solver_variable_set(sys->context, 1, i, vertexCos[i][1]); EIG_linear_solver_variable_set(sys->context, 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); } dv = dvert; for (i = 0; i < numVerts; i++) { EIG_linear_solver_right_hand_side_add(sys->context, 0, i, vertexCos[i][0]); EIG_linear_solver_right_hand_side_add(sys->context, 1, i, vertexCos[i][1]); EIG_linear_solver_right_hand_side_add(sys->context, 2, i, vertexCos[i][2]); if (iter == 0) { 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]) { EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f + fabsf(smd->lambda) * wpaint); } else { EIG_linear_solver_matrix_add(sys->context, 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]) { EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f + fabsf(smd->lambda) * wpaint / (4.0f * sys->ring_areas[i])); } else { EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f + fabsf(smd->lambda_border) * wpaint * 2.0f); } } } else { EIG_linear_solver_matrix_add(sys->context, i, i, 1.0f); } } } if (iter == 0) { fill_laplacian_matrix(sys); } if (EIG_linear_solver_solve(sys->context)) { validate_solution(sys, smd->flag, smd->lambda, smd->lambda_border); } } EIG_linear_solver_delete(sys->context); sys->context = NULL; delete_laplacian_system(sys); }