Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------release/scripts/addons0
-rw-r--r--source/blender/blenkernel/BKE_cloth.h28
-rw-r--r--source/blender/blenkernel/intern/cloth.c417
-rw-r--r--source/blender/physics/intern/BPH_mass_spring.cpp35
-rw-r--r--source/blender/physics/intern/implicit.h5
-rw-r--r--source/blender/physics/intern/implicit_blender.c138
6 files changed, 495 insertions, 128 deletions
diff --git a/release/scripts/addons b/release/scripts/addons
-Subproject 371960484a38fc64e0a2635170a41a0d8ab2f6b
+Subproject 6c3a46dc113de870a03191e4c0685238b0823ac
diff --git a/source/blender/blenkernel/BKE_cloth.h b/source/blender/blenkernel/BKE_cloth.h
index a099ae46d78..baf60397504 100644
--- a/source/blender/blenkernel/BKE_cloth.h
+++ b/source/blender/blenkernel/BKE_cloth.h
@@ -133,11 +133,17 @@ ClothVertex;
typedef struct ClothSpring {
int ij; /* Pij from the paper, one end of the spring. */
int kl; /* Pkl from the paper, one end of the spring. */
- int mn;
- float restlen; /* The original length of the spring. */
- int type; /* types defined in BKE_cloth.h ("springType") */
- int flags; /* defined in BKE_cloth.h, e.g. deactivated due to tearing */
- float stiffness; /* stiffness factor from the vertex groups */
+ int mn; /* For hair springs: third vertex index; For bending springs: edge index; */
+ int *pa; /* Array of vert indices for poly a (for bending springs). */
+ int *pb; /* Array of vert indices for poly b (for bending springs). */
+ int la; /* Length of *pa. */
+ int lb; /* Length of *pb. */
+ float restlen; /* The original length of the spring. */
+ float restang; /* The original angle of the bending springs. */
+ int type; /* Types defined in BKE_cloth.h ("springType"). */
+ int flags; /* Defined in BKE_cloth.h, e.g. deactivated due to tearing. */
+ float lin_stiffness; /* Linear stiffness factor from the vertex groups. */
+ float ang_stiffness; /* Angular stiffness factor from the vertex groups. */
float editrestlen;
/* angular bending spring target and derivatives */
@@ -186,12 +192,12 @@ typedef enum {
/* Spring types as defined in the paper.*/
typedef enum {
- CLOTH_SPRING_TYPE_STRUCTURAL = (1 << 1),
- CLOTH_SPRING_TYPE_SHEAR = (1 << 2),
- CLOTH_SPRING_TYPE_BENDING = (1 << 3),
- CLOTH_SPRING_TYPE_GOAL = (1 << 4),
- CLOTH_SPRING_TYPE_SEWING = (1 << 5),
- CLOTH_SPRING_TYPE_BENDING_ANG = (1 << 6),
+ CLOTH_SPRING_TYPE_STRUCTURAL = (1 << 1),
+ CLOTH_SPRING_TYPE_SHEAR = (1 << 2),
+ CLOTH_SPRING_TYPE_BENDING = (1 << 3),
+ CLOTH_SPRING_TYPE_GOAL = (1 << 4),
+ CLOTH_SPRING_TYPE_SEWING = (1 << 5),
+ CLOTH_SPRING_TYPE_BENDING_HAIR = (1 << 6),
} CLOTH_SPRING_TYPES;
/* SPRING FLAGS */
diff --git a/source/blender/blenkernel/intern/cloth.c b/source/blender/blenkernel/intern/cloth.c
index 54236785509..ce723d4a7f2 100644
--- a/source/blender/blenkernel/intern/cloth.c
+++ b/source/blender/blenkernel/intern/cloth.c
@@ -68,6 +68,12 @@ static void cloth_update_spring_lengths( ClothModifierData *clmd, Mesh *mesh );
static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh );
static void cloth_apply_vgroup ( ClothModifierData *clmd, Mesh *mesh );
+typedef struct BendSpringRef {
+ int index;
+ int polys;
+ ClothSpring *spring;
+} BendSpringRef;
+
/******************************************************************************
*
* External interface called by modifier.c clothModifier functions.
@@ -536,6 +542,9 @@ void cloth_free_modifier(ClothModifierData *clmd )
while (search) {
ClothSpring *spring = search->link;
+ MEM_SAFE_FREE(spring->pa);
+ MEM_SAFE_FREE(spring->pb);
+
MEM_freeN ( spring );
search = search->next;
}
@@ -602,6 +611,9 @@ void cloth_free_modifier_extern(ClothModifierData *clmd )
while (search) {
ClothSpring *spring = search->link;
+ MEM_SAFE_FREE(spring->pa);
+ MEM_SAFE_FREE(spring->pb);
+
MEM_freeN ( spring );
search = search->next;
}
@@ -961,13 +973,16 @@ static void cloth_free_edgelist(LinkNodePair *edgelist, unsigned int mvert_num)
}
}
-static void cloth_free_errorsprings(Cloth *cloth, LinkNodePair *edgelist)
+static void cloth_free_errorsprings(Cloth *cloth, LinkNodePair *edgelist, BendSpringRef *spring_ref)
{
if ( cloth->springs != NULL ) {
LinkNode *search = cloth->springs;
while (search) {
ClothSpring *spring = search->link;
+ MEM_SAFE_FREE(spring->pa);
+ MEM_SAFE_FREE(spring->pb);
+
MEM_freeN ( spring );
search = search->next;
}
@@ -978,12 +993,49 @@ static void cloth_free_errorsprings(Cloth *cloth, LinkNodePair *edgelist)
cloth_free_edgelist(edgelist, cloth->mvert_num);
+ MEM_SAFE_FREE(spring_ref);
+
if (cloth->edgeset) {
BLI_edgeset_free(cloth->edgeset);
cloth->edgeset = NULL;
}
}
+BLI_INLINE void cloth_bend_poly_dir(ClothVertex *verts, int i, int j, int *inds, int len, float r_dir[3])
+{
+ float cent[3] = {0};
+ float fact = 1.0f / len;
+
+ for (int x = 0; x < len; x++) {
+ madd_v3_v3fl(cent, verts[inds[x]].xrest, fact);
+ }
+
+ normal_tri_v3(r_dir, verts[i].xrest, verts[j].xrest, cent);
+}
+
+static float cloth_spring_angle(ClothVertex *verts, int i, int j, int *i_a, int *i_b, int len_a, int len_b)
+{
+ float dir_a[3], dir_b[3];
+ float tmp[3], vec_e[3];
+ float sin, cos;
+
+ /* Poly vectors. */
+ cloth_bend_poly_dir(verts, j, i, i_a, len_a, dir_a);
+ cloth_bend_poly_dir(verts, i, j, i_b, len_b, dir_b);
+
+ /* Edge vector. */
+ sub_v3_v3v3(vec_e, verts[i].xrest, verts[j].xrest);
+ normalize_v3(vec_e);
+
+ /* Compute angle. */
+ cos = dot_v3v3(dir_a, dir_b);
+
+ cross_v3_v3v3(tmp, dir_a, dir_b);
+ sin = dot_v3v3(tmp, vec_e);
+
+ return atan2f(sin, cos);
+}
+
static void cloth_hair_update_bending_targets(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
@@ -1008,7 +1060,7 @@ static void cloth_hair_update_bending_targets(ClothModifierData *clmd)
ClothHairData *hair_ij, *hair_kl;
bool is_root = spring->kl != prev_mn;
- if (spring->type != CLOTH_SPRING_TYPE_BENDING_ANG) {
+ if (spring->type != CLOTH_SPRING_TYPE_BENDING_HAIR) {
continue;
}
@@ -1083,7 +1135,7 @@ static void cloth_hair_update_bending_rest_targets(ClothModifierData *clmd)
ClothHairData *hair_ij, *hair_kl;
bool is_root = spring->kl != prev_mn;
- if (spring->type != CLOTH_SPRING_TYPE_BENDING_ANG) {
+ if (spring->type != CLOTH_SPRING_TYPE_BENDING_HAIR) {
continue;
}
@@ -1123,18 +1175,24 @@ static void cloth_update_springs( ClothModifierData *clmd )
while (search) {
ClothSpring *spring = search->link;
- spring->stiffness = 0.0f;
+ spring->lin_stiffness = 0.0f;
+
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ if (spring->type & CLOTH_SPRING_TYPE_BENDING) {
+ spring->ang_stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
+ }
+ }
- if (spring->type == CLOTH_SPRING_TYPE_STRUCTURAL) {
- spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
+ if (spring->type & CLOTH_SPRING_TYPE_STRUCTURAL) {
+ spring->lin_stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
}
- else if (spring->type == CLOTH_SPRING_TYPE_SHEAR) {
- spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;
+ else if (spring->type & CLOTH_SPRING_TYPE_SHEAR) {
+ spring->lin_stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;
}
else if (spring->type == CLOTH_SPRING_TYPE_BENDING) {
- spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
+ spring->lin_stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
}
- else if (spring->type == CLOTH_SPRING_TYPE_BENDING_ANG) {
+ else if (spring->type == CLOTH_SPRING_TYPE_BENDING_HAIR) {
ClothVertex *v1 = &cloth->verts[spring->ij];
ClothVertex *v2 = &cloth->verts[spring->kl];
if (clmd->hairdata) {
@@ -1142,7 +1200,7 @@ static void cloth_update_springs( ClothModifierData *clmd )
v1->bend_stiff = clmd->hairdata[spring->ij].bending_stiffness;
v2->bend_stiff = clmd->hairdata[spring->kl].bending_stiffness;
}
- spring->stiffness = (v1->bend_stiff + v2->bend_stiff) / 2.0f;
+ spring->lin_stiffness = (v1->bend_stiff + v2->bend_stiff) / 2.0f;
}
else if (spring->type == CLOTH_SPRING_TYPE_GOAL) {
/* Warning: Appending NEW goal springs does not work because implicit solver would need reset! */
@@ -1197,16 +1255,23 @@ static void cloth_update_spring_lengths( ClothModifierData *clmd, Mesh *mesh )
while (search) {
ClothSpring *spring = search->link;
- if ( spring->type != CLOTH_SPRING_TYPE_SEWING ) {
- if ( spring->type & (CLOTH_SPRING_TYPE_STRUCTURAL | CLOTH_SPRING_TYPE_SHEAR | CLOTH_SPRING_TYPE_BENDING) )
+ if (spring->type != CLOTH_SPRING_TYPE_SEWING) {
+ if (spring->type & (CLOTH_SPRING_TYPE_STRUCTURAL | CLOTH_SPRING_TYPE_SHEAR | CLOTH_SPRING_TYPE_BENDING)) {
shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
- else
+ }
+ else {
shrink_factor = 1.0f;
+ }
spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
+
+ if (spring->type & CLOTH_SPRING_TYPE_BENDING) {
+ spring->restang = cloth_spring_angle(cloth->verts, spring->ij, spring->kl,
+ spring->pa, spring->pb, spring->la, spring->lb);
+ }
}
- if ( spring->type == CLOTH_SPRING_TYPE_STRUCTURAL ) {
+ if (spring->type & CLOTH_SPRING_TYPE_STRUCTURAL) {
clmd->sim_parms->avg_spring_len += spring->restlen;
cloth->verts[spring->ij].avg_spring_len += spring->restlen;
cloth->verts[spring->kl].avg_spring_len += spring->restlen;
@@ -1216,12 +1281,14 @@ static void cloth_update_spring_lengths( ClothModifierData *clmd, Mesh *mesh )
search = search->next;
}
- if (struct_springs > 0)
+ if (struct_springs > 0) {
clmd->sim_parms->avg_spring_len /= struct_springs;
+ }
for (i = 0; i < mvert_num; i++) {
- if (cloth->verts[i].spring_count > 0)
+ if (cloth->verts[i].spring_count > 0) {
cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count);
+ }
}
}
@@ -1260,12 +1327,103 @@ void cloth_parallel_transport_hair_frame(float mat[3][3], const float dir_old[3]
mul_m3_m3m3(mat, rot, mat);
}
+/* Add a shear and a bend spring between two verts within a poly. */
+static bool cloth_add_shear_bend_spring(ClothModifierData *clmd, LinkNodePair *edgelist,
+ const MLoop *mloop, const MPoly *mpoly, int i, int j, int k)
+{
+ Cloth *cloth = clmd->clothObject;
+ ClothSpring *spring;
+ const MLoop *tmp_loop;
+ float shrink_factor;
+ int x, y;
+
+ /* Combined shear/bend properties. */
+ spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
+
+ if (!spring) {
+ return false;
+ }
+
+ spring_verts_ordered_set(spring,
+ mloop[mpoly[i].loopstart + j].v,
+ mloop[mpoly[i].loopstart + k].v);
+
+ shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
+ spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
+ spring->type |= CLOTH_SPRING_TYPE_SHEAR;
+ spring->lin_stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;
+
+ if (edgelist) {
+ BLI_linklist_append(&edgelist[spring->ij], spring);
+ BLI_linklist_append(&edgelist[spring->kl], spring);
+ }
+
+ /* Bending specific properties. */
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ spring->type |= CLOTH_SPRING_TYPE_BENDING;
+
+ spring->la = k - j + 1;
+ spring->lb = mpoly[i].totloop - k + j + 1;
+
+ spring->pa = MEM_mallocN(sizeof(*spring->pa) * spring->la, "spring poly");
+ if (!spring->pa) {
+ return false;
+ }
+
+ spring->pb = MEM_mallocN(sizeof(*spring->pb) * spring->lb, "spring poly");
+ if (!spring->pb) {
+ return false;
+ }
+
+ tmp_loop = mloop + mpoly[i].loopstart;
+
+ for (x = 0; x < spring->la; x++) {
+ spring->pa[x] = tmp_loop[j + x].v;
+ }
+
+ for (x = 0; x <= j; x++) {
+ spring->pb[x] = tmp_loop[x].v;
+ }
+
+ for (y = k; y < mpoly[i].totloop; x++, y++) {
+ spring->pb[x] = tmp_loop[y].v;
+ }
+
+ spring->mn = -1;
+
+ spring->restang = cloth_spring_angle(cloth->verts, spring->ij, spring->kl,
+ spring->pa, spring->pb, spring->la, spring->lb);
+
+ spring->ang_stiffness = (cloth->verts[spring->ij].bend_stiff + cloth->verts[spring->kl].bend_stiff) / 2.0f;
+ }
+
+ BLI_linklist_prepend(&cloth->springs, spring);
+
+ return true;
+}
+
+BLI_INLINE bool cloth_bend_set_poly_vert_array(int **poly, int len, const MLoop *mloop)
+{
+ int *p = MEM_mallocN(sizeof(int) * len, "spring poly");
+
+ if (!p) {
+ return false;
+ }
+
+ for (int i = 0; i < len; i++, mloop++) {
+ p[i] = mloop->v;
+ }
+
+ *poly = p;
+
+ return true;
+}
+
static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
{
Cloth *cloth = clmd->clothObject;
ClothSpring *spring = NULL, *tspring = NULL, *tspring2 = NULL;
unsigned int struct_springs = 0, shear_springs=0, bend_springs = 0, struct_springs_real = 0;
- unsigned int i = 0;
unsigned int mvert_num = (unsigned int)mesh->totvert;
unsigned int numedges = (unsigned int)mesh->totedge;
unsigned int numpolys = (unsigned int)mesh->totpoly;
@@ -1274,9 +1432,10 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
const MPoly *mpoly = mesh->mpoly;
const MLoop *mloop = mesh->mloop;
int index2 = 0; // our second vertex index
- LinkNodePair *edgelist;
+ LinkNodePair *edgelist = NULL;
EdgeSet *edgeset = NULL;
LinkNode *search = NULL, *search2 = NULL;
+ BendSpringRef *spring_ref = NULL;
// error handling
if ( numedges==0 )
@@ -1290,13 +1449,23 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
cloth->springs = NULL;
cloth->edgeset = NULL;
- edgelist = MEM_callocN(sizeof(*edgelist) * mvert_num, "cloth_edgelist_alloc" );
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ spring_ref = MEM_callocN(sizeof(*spring_ref) * numedges, "temp bend spring reference");
- if (!edgelist)
- return 0;
+ if (!spring_ref) {
+ return 0;
+ }
+ }
+ else {
+ edgelist = MEM_callocN(sizeof(*edgelist) * mvert_num, "cloth_edgelist_alloc" );
+
+ if (!edgelist) {
+ return 0;
+ }
+ }
- // structural springs
- for ( i = 0; i < numedges; i++ ) {
+ /* Structural springs. */
+ for (int i = 0; i < numedges; i++) {
spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
if ( spring ) {
@@ -1304,13 +1473,13 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && medge[i].flag & ME_LOOSEEDGE) {
// handle sewing (loose edges will be pulled together)
spring->restlen = 0.0f;
- spring->stiffness = 1.0f;
+ spring->lin_stiffness = 1.0f;
spring->type = CLOTH_SPRING_TYPE_SEWING;
}
else {
shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
- spring->stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
+ spring->lin_stiffness = (cloth->verts[spring->kl].struct_stiff + cloth->verts[spring->ij].struct_stiff) / 2.0f;
spring->type = CLOTH_SPRING_TYPE_STRUCTURAL;
clmd->sim_parms->avg_spring_len += spring->restlen;
@@ -1325,9 +1494,13 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
struct_springs++;
BLI_linklist_prepend ( &cloth->springs, spring );
+
+ if (spring_ref) {
+ spring_ref[i].spring = spring;
+ }
}
else {
- cloth_free_errorsprings(cloth, edgelist);
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
return 0;
}
}
@@ -1335,86 +1508,147 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
if (struct_springs_real > 0)
clmd->sim_parms->avg_spring_len /= struct_springs_real;
- for (i = 0; i < mvert_num; i++) {
+ for (int i = 0; i < mvert_num; i++) {
if (cloth->verts[i].spring_count > 0)
cloth->verts[i].avg_spring_len = cloth->verts[i].avg_spring_len * 0.49f / ((float)cloth->verts[i].spring_count);
}
- // shear springs
- for (i = 0; i < numpolys; i++) {
- /* triangle faces already have shear springs due to structural geometry */
- if (mpoly[i].totloop == 4) {
- int j;
+ edgeset = BLI_edgeset_new_ex(__func__, numedges);
+ cloth->edgeset = edgeset;
+
+ if (numpolys) {
+ for (int i = 0; i < numpolys; i++) {
+ /* Shear springs. */
+ /* Triangle faces already have shear springs due to structural geometry. */
+ if (mpoly[i].totloop > 3) {
+ for (int j = 1; j < mpoly[i].totloop - 1; j++) {
+ if (j > 1) {
+ if (cloth_add_shear_bend_spring(clmd, edgelist, mloop, mpoly, i, 0, j)) {
+ shear_springs++;
+
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ bend_springs++;
+ }
+ }
+ else {
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
+ return 0;
+ }
+ }
- for (j = 0; j != 2; j++) {
- spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
+ for (int k = j + 2; k < mpoly[i].totloop; k++) {
+ if (cloth_add_shear_bend_spring(clmd, edgelist, mloop, mpoly, i, j, k)) {
+ shear_springs++;
- if (!spring) {
- cloth_free_errorsprings(cloth, edgelist);
- return 0;
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ bend_springs++;
+ }
+ }
+ else {
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
+ return 0;
+ }
+ }
}
+ }
- spring_verts_ordered_set(
- spring,
- mloop[mpoly[i].loopstart + (j + 0)].v,
- mloop[mpoly[i].loopstart + (j + 2)].v);
+ /* Angular bending springs along struct springs. */
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_ANGULAR) {
+ const MLoop *ml = mloop + mpoly[i].loopstart;
- shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
- spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
- spring->type = CLOTH_SPRING_TYPE_SHEAR;
- spring->stiffness = (cloth->verts[spring->kl].shear_stiff + cloth->verts[spring->ij].shear_stiff) / 2.0f;
+ for (int j = 0; j < mpoly[i].totloop; j++, ml++) {
+ BendSpringRef *curr_ref = &spring_ref[ml->e];
+ curr_ref->polys++;
+
+ /* First poly found for this edge, store poly index. */
+ if (curr_ref->polys == 1) {
+ curr_ref->index = i;
+ }
+ /* Second poly found for this egde, add bending data. */
+ else if (curr_ref->polys == 2) {
+ spring = curr_ref->spring;
+
+ spring->type |= CLOTH_SPRING_TYPE_BENDING;
+
+ spring->la = mpoly[curr_ref->index].totloop;
+ spring->lb = mpoly[i].totloop;
+
+ if (!cloth_bend_set_poly_vert_array(&spring->pa, spring->la, &mloop[mpoly[curr_ref->index].loopstart]) ||
+ !cloth_bend_set_poly_vert_array(&spring->pb, spring->lb, &mloop[mpoly[i].loopstart]))
+ {
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
+ return 0;
+ }
+
+ spring->mn = ml->e;
- BLI_linklist_append(&edgelist[spring->ij], spring);
- BLI_linklist_append(&edgelist[spring->kl], spring);
+ spring->restang = cloth_spring_angle(cloth->verts, spring->ij, spring->kl,
+ spring->pa, spring->pb, spring->la, spring->lb);
- shear_springs++;
+ spring->ang_stiffness = (cloth->verts[spring->ij].bend_stiff + cloth->verts[spring->kl].bend_stiff) / 2.0f;
- BLI_linklist_prepend(&cloth->springs, spring);
+ bend_springs++;
+ }
+ /* Third poly found for this egde, remove bending data. */
+ else if (curr_ref->polys == 3) {
+ spring = curr_ref->spring;
+
+ spring->type &= ~CLOTH_SPRING_TYPE_BENDING;
+ MEM_freeN(spring->pa);
+ MEM_freeN(spring->pb);
+ spring->pa = NULL;
+ spring->pb = NULL;
+
+ bend_springs--;
+ }
+ }
}
}
- }
- edgeset = BLI_edgeset_new_ex(__func__, numedges);
- cloth->edgeset = edgeset;
+ /* Linear bending springs. */
+ if (clmd->sim_parms->bending_model == CLOTH_BENDING_LINEAR) {
+ search2 = cloth->springs;
- if (numpolys) {
- // bending springs
- search2 = cloth->springs;
- for ( i = struct_springs; i < struct_springs+shear_springs; i++ ) {
- if ( !search2 )
- break;
+ for (int i = struct_springs; i < struct_springs+shear_springs; i++) {
+ if (!search2) {
+ break;
+ }
- tspring2 = search2->link;
- search = edgelist[tspring2->kl].list;
- while ( search ) {
- tspring = search->link;
- index2 = ( ( tspring->ij==tspring2->kl ) ? ( tspring->kl ) : ( tspring->ij ) );
+ tspring2 = search2->link;
+ search = edgelist[tspring2->kl].list;
+
+ while (search) {
+ tspring = search->link;
+ index2 = ((tspring->ij == tspring2->kl) ? (tspring->kl) : (tspring->ij));
+
+ /* Check for existing spring. */
+ /* Check also if startpoint is equal to endpoint. */
+ if ((index2 != tspring2->ij) &&
+ !BLI_edgeset_haskey(edgeset, tspring2->ij, index2))
+ {
+ spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring");
+
+ if (!spring) {
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
+ return 0;
+ }
- // check for existing spring
- // check also if startpoint is equal to endpoint
- if ((index2 != tspring2->ij) &&
- !BLI_edgeset_haskey(edgeset, tspring2->ij, index2))
- {
- spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
+ spring_verts_ordered_set(spring, tspring2->ij, index2);
+ shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
+ spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
+ spring->type = CLOTH_SPRING_TYPE_BENDING;
+ spring->lin_stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
+ BLI_edgeset_insert(edgeset, spring->ij, spring->kl);
+ bend_springs++;
- if (!spring) {
- cloth_free_errorsprings(cloth, edgelist);
- return 0;
+ BLI_linklist_prepend(&cloth->springs, spring);
}
- spring_verts_ordered_set(spring, tspring2->ij, index2);
- shrink_factor = cloth_shrink_factor(clmd, cloth->verts, spring->ij, spring->kl);
- spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest) * shrink_factor;
- spring->type = CLOTH_SPRING_TYPE_BENDING;
- spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
- BLI_edgeset_insert(edgeset, spring->ij, spring->kl);
- bend_springs++;
-
- BLI_linklist_prepend ( &cloth->springs, spring );
+ search = search->next;
}
- search = search->next;
+
+ search2 = search2->next;
}
- search2 = search2->next;
}
}
else if (struct_springs > 2) {
@@ -1429,7 +1663,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
if (!spring) {
- cloth_free_errorsprings(cloth, edgelist);
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
return 0;
}
@@ -1437,8 +1671,8 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
spring->kl = tspring->ij;
spring->mn = tspring->kl;
spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
- spring->type = CLOTH_SPRING_TYPE_BENDING_ANG;
- spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
+ spring->type = CLOTH_SPRING_TYPE_BENDING_HAIR;
+ spring->lin_stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
bend_springs++;
BLI_linklist_prepend ( &cloth->springs, spring );
@@ -1466,7 +1700,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
spring = (ClothSpring *)MEM_callocN ( sizeof ( ClothSpring ), "cloth spring" );
if (!spring) {
- cloth_free_errorsprings(cloth, edgelist);
+ cloth_free_errorsprings(cloth, edgelist, spring_ref);
return 0;
}
@@ -1474,7 +1708,7 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
spring->kl = tspring->kl;
spring->restlen = len_v3v3(cloth->verts[spring->kl].xrest, cloth->verts[spring->ij].xrest);
spring->type = CLOTH_SPRING_TYPE_BENDING;
- spring->stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
+ spring->lin_stiffness = (cloth->verts[spring->kl].bend_stiff + cloth->verts[spring->ij].bend_stiff) / 2.0f;
bend_springs++;
BLI_linklist_prepend ( &cloth->springs, spring );
@@ -1491,17 +1725,18 @@ static int cloth_build_springs ( ClothModifierData *clmd, Mesh *mesh )
/* note: the edges may already exist so run reinsert */
/* insert other near springs in edgeset AFTER bending springs are calculated (for selfcolls) */
- for (i = 0; i < numedges; i++) { /* struct springs */
+ for (int i = 0; i < numedges; i++) { /* struct springs */
BLI_edgeset_add(edgeset, medge[i].v1, medge[i].v2);
}
- for (i = 0; i < numpolys; i++) { /* edge springs */
+ for (int i = 0; i < numpolys; i++) { /* edge springs */
if (mpoly[i].totloop == 4) {
BLI_edgeset_add(edgeset, mloop[mpoly[i].loopstart + 0].v, mloop[mpoly[i].loopstart + 2].v);
BLI_edgeset_add(edgeset, mloop[mpoly[i].loopstart + 1].v, mloop[mpoly[i].loopstart + 3].v);
}
}
+ MEM_SAFE_FREE(spring_ref);
cloth->numsprings = struct_springs + shear_springs + bend_springs;
diff --git a/source/blender/physics/intern/BPH_mass_spring.cpp b/source/blender/physics/intern/BPH_mass_spring.cpp
index 668e40e71cc..6ea2eeca6f8 100644
--- a/source/blender/physics/intern/BPH_mass_spring.cpp
+++ b/source/blender/physics/intern/BPH_mass_spring.cpp
@@ -67,7 +67,7 @@ static int cloth_count_nondiag_blocks(Cloth *cloth)
for (link = cloth->springs; link; link = link->next) {
ClothSpring *spring = (ClothSpring *)link->link;
switch (spring->type) {
- case CLOTH_SPRING_TYPE_BENDING_ANG:
+ case CLOTH_SPRING_TYPE_BENDING_HAIR:
/* angular bending combines 3 vertices */
nondiag += 3;
break;
@@ -346,14 +346,29 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
s->flags &= ~CLOTH_SPRING_FLAG_NEEDED;
- // calculate force of structural + shear springs
- if ((s->type & CLOTH_SPRING_TYPE_STRUCTURAL) || (s->type & CLOTH_SPRING_TYPE_SEWING)) {
+ /* Calculate force of bending springs. */
+ if (s->type & CLOTH_SPRING_TYPE_BENDING) {
+#ifdef CLOTH_FORCE_SPRING_BEND
+ float k, scaling;
+
+ s->flags |= CLOTH_SPRING_FLAG_NEEDED;
+
+ scaling = parms->bending + s->ang_stiffness * fabsf(parms->max_bend - parms->bending);
+ k = scaling * s->restlen * 0.1f; /* Multiplying by 0.1, just to scale the forces to more reasonable values. */
+
+ BPH_mass_spring_force_spring_angular(data, s->ij, s->kl, s->pa, s->pb, s->la, s->lb,
+ s->restang, k, parms->bending_damping);
+#endif
+ }
+
+ /* Calculate force of structural + shear springs. */
+ if (s->type & (CLOTH_SPRING_TYPE_STRUCTURAL | CLOTH_SPRING_TYPE_SEWING)) {
#ifdef CLOTH_FORCE_SPRING_STRUCTURAL
float k_tension, scaling_tension;
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
- scaling_tension = parms->tension + s->stiffness * fabsf(parms->max_tension - parms->tension);
+ scaling_tension = parms->tension + s->lin_stiffness * fabsf(parms->max_tension - parms->tension);
k_tension = scaling_tension / (parms->avg_spring_len + FLT_EPSILON);
if (s->type & CLOTH_SPRING_TYPE_SEWING) {
@@ -365,7 +380,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
}
else {
float k_compression, scaling_compression;
- scaling_compression = parms->compression + s->stiffness * fabsf(parms->max_compression - parms->compression);
+ scaling_compression = parms->compression + s->lin_stiffness * fabsf(parms->max_compression - parms->compression);
k_compression = scaling_compression / (parms->avg_spring_len + FLT_EPSILON);
BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen,
@@ -381,7 +396,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
- scaling = parms->shear + s->stiffness * fabsf(parms->max_shear - parms->shear);
+ scaling = parms->shear + s->lin_stiffness * fabsf(parms->max_shear - parms->shear);
k = scaling / (parms->avg_spring_len + FLT_EPSILON);
BPH_mass_spring_force_spring_linear(data, s->ij, s->kl, s->restlen, k, parms->shear_damp,
@@ -394,7 +409,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
s->flags |= CLOTH_SPRING_FLAG_NEEDED;
- scaling = parms->bending + s->stiffness * fabsf(parms->max_bend - parms->bending);
+ scaling = parms->bending + s->lin_stiffness * fabsf(parms->max_bend - parms->bending);
kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
// Fix for [#45084] for cloth stiffness must have cb proportional to kb
@@ -403,7 +418,7 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
BPH_mass_spring_force_spring_bending(data, s->ij, s->kl, s->restlen, kb, cb);
#endif
}
- else if (s->type & CLOTH_SPRING_TYPE_BENDING_ANG) {
+ else if (s->type & CLOTH_SPRING_TYPE_BENDING_HAIR) {
#ifdef CLOTH_FORCE_SPRING_BEND
float kb, cb, scaling;
@@ -413,14 +428,14 @@ BLI_INLINE void cloth_calc_spring_force(ClothModifierData *clmd, ClothSpring *s)
* this is crap, but needed due to cloth/hair mixing ...
* max_bend factor is not even used for hair, so ...
*/
- scaling = s->stiffness * parms->bending;
+ scaling = s->lin_stiffness * parms->bending;
kb = scaling / (20.0f * (parms->avg_spring_len + FLT_EPSILON));
// Fix for [#45084] for cloth stiffness must have cb proportional to kb
cb = kb * parms->bending_damping;
/* XXX assuming same restlen for ij and jk segments here, this can be done correctly for hair later */
- BPH_mass_spring_force_spring_bending_angular(data, s->ij, s->kl, s->mn, s->target, kb, cb);
+ BPH_mass_spring_force_spring_bending_hair(data, s->ij, s->kl, s->mn, s->target, kb, cb);
#if 0
{
diff --git a/source/blender/physics/intern/implicit.h b/source/blender/physics/intern/implicit.h
index ffe9dbbec04..f99812a8aa9 100644
--- a/source/blender/physics/intern/implicit.h
+++ b/source/blender/physics/intern/implicit.h
@@ -118,10 +118,13 @@ bool BPH_mass_spring_force_spring_linear(struct Implicit_Data *data, int i, int
float stiffness_tension, float damping_tension,
float stiffness_compression, float damping_compression,
bool resist_compress, bool new_compress, float clamp_force);
+/* Angular spring force between two polygons */
+bool BPH_mass_spring_force_spring_angular(struct Implicit_Data *data, int i, int j, int *i_a, int *i_b, int len_a, int len_b,
+ float restang, float stiffness, float damping);
/* Bending force, forming a triangle at the base of two structural springs */
bool BPH_mass_spring_force_spring_bending(struct Implicit_Data *data, int i, int j, float restlen, float kb, float cb);
/* Angular bending force based on local target vectors */
-bool BPH_mass_spring_force_spring_bending_angular(struct Implicit_Data *data, int i, int j, int k,
+bool BPH_mass_spring_force_spring_bending_hair(struct Implicit_Data *data, int i, int j, int k,
const float target[3], float stiffness, float damping);
/* Global goal spring */
bool BPH_mass_spring_force_spring_goal(struct Implicit_Data *data, int i, const float goal_x[3], const float goal_v[3],
diff --git a/source/blender/physics/intern/implicit_blender.c b/source/blender/physics/intern/implicit_blender.c
index 677e566ff39..0d897893ff5 100644
--- a/source/blender/physics/intern/implicit_blender.c
+++ b/source/blender/physics/intern/implicit_blender.c
@@ -1664,6 +1664,114 @@ bool BPH_mass_spring_force_spring_bending(Implicit_Data *data, int i, int j, flo
}
}
+BLI_INLINE void poly_avg(lfVector *data, int *inds, int len, float r_avg[3])
+{
+ float fact = 1.0f / (float)len;
+
+ zero_v3(r_avg);
+
+ for (int i = 0; i < len; i++) {
+ madd_v3_v3fl(r_avg, data[inds[i]], fact);
+ }
+}
+
+BLI_INLINE void poly_norm(lfVector *data, int i, int j, int *inds, int len, float r_dir[3])
+{
+ float mid[3];
+
+ poly_avg(data, inds, len, mid);
+
+ normal_tri_v3(r_dir, data[i], data[j], mid);
+}
+
+BLI_INLINE void edge_avg(lfVector *data, int i, int j, float r_avg[3])
+{
+ r_avg[0] = (data[i][0] + data[j][0]) * 0.5f;
+ r_avg[1] = (data[i][1] + data[j][1]) * 0.5f;
+ r_avg[2] = (data[i][2] + data[j][2]) * 0.5f;
+}
+
+BLI_INLINE void edge_norm(lfVector *data, int i, int j, float r_dir[3])
+{
+ sub_v3_v3v3(r_dir, data[i], data[j]);
+ normalize_v3(r_dir);
+}
+
+BLI_INLINE float bend_angle(float dir_a[3], float dir_b[3], float dir_e[3])
+{
+ float cos, sin;
+ float tmp[3];
+
+ cos = dot_v3v3(dir_a, dir_b);
+
+ cross_v3_v3v3(tmp, dir_a, dir_b);
+ sin = dot_v3v3(tmp, dir_e);
+
+ return atan2f(sin, cos);
+}
+
+BLI_INLINE void spring_angle(Implicit_Data *data, int i, int j, int *i_a, int *i_b, int len_a, int len_b,
+ float r_dir_a[3], float r_dir_b[3],
+ float *r_angle, float r_vel_a[3], float r_vel_b[3])
+{
+ float dir_e[3], vel_e[3];
+
+ poly_norm(data->X, j, i, i_a, len_a, r_dir_a);
+ poly_norm(data->X, i, j, i_b, len_b, r_dir_b);
+
+ edge_norm(data->X, i, j, dir_e);
+
+ *r_angle = bend_angle(r_dir_a, r_dir_b, dir_e);
+
+ poly_avg(data->V, i_a, len_a, r_vel_a);
+ poly_avg(data->V, i_b, len_b, r_vel_b);
+
+ edge_avg(data->V, i, j, vel_e);
+
+ sub_v3_v3(r_vel_a, vel_e);
+ sub_v3_v3(r_vel_b, vel_e);
+}
+
+/* Angular springs roughly based on the bending model proposed by Baraff and Witkin in "Large Steps in Cloth Simulation". */
+bool BPH_mass_spring_force_spring_angular(Implicit_Data *data, int i, int j, int *i_a, int *i_b, int len_a, int len_b,
+ float restang, float stiffness, float damping)
+{
+ float angle, dir_a[3], dir_b[3], vel_a[3], vel_b[3];
+ float f_a[3], f_b[3], f_e[3];
+ float force;
+ int x;
+
+ spring_angle(data, i, j, i_a, i_b, len_a, len_b,
+ dir_a, dir_b, &angle, vel_a, vel_b);
+
+ /* spring force */
+ force = stiffness * (angle - restang);
+
+ /* damping force */
+ force += -damping * (dot_v3v3(vel_a, dir_a) + dot_v3v3(vel_b, dir_b));
+
+ mul_v3_v3fl(f_a, dir_a, force / len_a);
+ mul_v3_v3fl(f_b, dir_b, force / len_b);
+
+ for (x = 0; x < len_a; x++) {
+ add_v3_v3(data->F[i_a[x]], f_a);
+ }
+
+ for (x = 0; x < len_b; x++) {
+ add_v3_v3(data->F[i_b[x]], f_b);
+ }
+
+ mul_v3_v3fl(f_a, dir_a, force * 0.5f);
+ mul_v3_v3fl(f_b, dir_b, force * 0.5f);
+
+ add_v3_v3v3(f_e, f_a, f_b);
+
+ sub_v3_v3(data->F[i], f_e);
+ sub_v3_v3(data->F[j], f_e);
+
+ return true;
+}
+
/* Jacobian of a direction vector.
* Basically the part of the differential orthogonal to the direction,
* inversely proportional to the length of the edge.
@@ -1687,7 +1795,7 @@ BLI_INLINE void spring_grad_dir(Implicit_Data *data, int i, int j, float edge[3]
}
}
-BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k,
+BLI_INLINE void spring_hairbend_forces(Implicit_Data *data, int i, int j, int k,
const float goal[3],
float stiffness, float damping,
int q, const float dx[3], const float dv[3],
@@ -1736,7 +1844,7 @@ BLI_INLINE void spring_angbend_forces(Implicit_Data *data, int i, int j, int k,
}
/* Finite Differences method for estimating the jacobian of the force */
-BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j, int k,
+BLI_INLINE void spring_hairbend_estimate_dfdx(Implicit_Data *data, int i, int j, int k,
const float goal[3],
float stiffness, float damping,
int q, float dfdx[3][3])
@@ -1755,11 +1863,11 @@ BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j,
/* XXX TODO offset targets to account for position dependency */
for (a = 0; a < 3; ++a) {
- spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
+ spring_hairbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_pos[a], dvec_null[a], f);
copy_v3_v3(dfdx[a], f);
- spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
+ spring_hairbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_neg[a], dvec_null[a], f);
sub_v3_v3(dfdx[a], f);
@@ -1770,7 +1878,7 @@ BLI_INLINE void spring_angbend_estimate_dfdx(Implicit_Data *data, int i, int j,
}
/* Finite Differences method for estimating the jacobian of the force */
-BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j, int k,
+BLI_INLINE void spring_hairbend_estimate_dfdv(Implicit_Data *data, int i, int j, int k,
const float goal[3],
float stiffness, float damping,
int q, float dfdv[3][3])
@@ -1789,11 +1897,11 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j,
/* XXX TODO offset targets to account for position dependency */
for (a = 0; a < 3; ++a) {
- spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
+ spring_hairbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_pos[a], f);
copy_v3_v3(dfdv[a], f);
- spring_angbend_forces(data, i, j, k, goal, stiffness, damping,
+ spring_hairbend_forces(data, i, j, k, goal, stiffness, damping,
q, dvec_null[a], dvec_neg[a], f);
sub_v3_v3(dfdv[a], f);
@@ -1806,7 +1914,7 @@ BLI_INLINE void spring_angbend_estimate_dfdv(Implicit_Data *data, int i, int j,
/* Angular spring that pulls the vertex toward the local target
* See "Artistic Simulation of Curly Hair" (Pixar technical memo #12-03a)
*/
-bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, int j, int k,
+bool BPH_mass_spring_force_spring_bending_hair(Implicit_Data *data, int i, int j, int k,
const float target[3], float stiffness, float damping)
{
float goal[3];
@@ -1822,18 +1930,18 @@ bool BPH_mass_spring_force_spring_bending_angular(Implicit_Data *data, int i, in
world_to_root_v3(data, j, goal, target);
- spring_angbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk);
+ spring_hairbend_forces(data, i, j, k, goal, stiffness, damping, k, vecnull, vecnull, fk);
negate_v3_v3(fj, fk); /* counterforce */
- spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi);
- spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj);
- spring_angbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk);
+ spring_hairbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, i, dfk_dxi);
+ spring_hairbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, j, dfk_dxj);
+ spring_hairbend_estimate_dfdx(data, i, j, k, goal, stiffness, damping, k, dfk_dxk);
copy_m3_m3(dfj_dxi, dfk_dxi); negate_m3(dfj_dxi);
copy_m3_m3(dfj_dxj, dfk_dxj); negate_m3(dfj_dxj);
- spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi);
- spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj);
- spring_angbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk);
+ spring_hairbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, i, dfk_dvi);
+ spring_hairbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, j, dfk_dvj);
+ spring_hairbend_estimate_dfdv(data, i, j, k, goal, stiffness, damping, k, dfk_dvk);
copy_m3_m3(dfj_dvi, dfk_dvi); negate_m3(dfj_dvi);
copy_m3_m3(dfj_dvj, dfk_dvj); negate_m3(dfj_dvj);