Integrate hair collisions with cloth collision

This integrates hair collisions with the new cloth collision system,
greatly improving reliability, and reducing the amount of hair-specific
code paths in the cloth code.

The removes all the point constraint based collision stuff, instead
implementing segment impulse based collisions, using the same collision
response code as the normal cloth solver.

The hair system can now also collide with the emitter if it is a
collision object.

Reviewed By: mano-wii, Sebastian Parborg

Differential Revision: https://developer.blender.org/D6545
This commit is contained in:
Luca Rood 2020-01-16 18:45:23 +01:00 committed by Sebastian Parborg
parent 2cd3298fde
commit d42a7bbd6e
Notes: blender-bot 2023-02-14 01:28:00 +01:00
Referenced by commit 96339c4cef, Fix T73304: Crash using force fields and hair dynamics
Referenced by issue #73745, Particle Hair - brused hair segments can penetrate object despite Deflect Emitter option keeping the hair keys outside (causing trouble with dynamics)
Referenced by issue #73304, Crash using force fields and hair dynamics
Referenced by issue #67958, Hair Dynamics: collision doesn't work (exploding)
4 changed files with 422 additions and 539 deletions

View File

@ -74,11 +74,11 @@ typedef struct ClothSolverResult {
* own connectivity of the mesh based on the actual edges in the mesh.
*/
typedef struct Cloth {
struct ClothVertex *verts; /* The vertices that represent this cloth. */
struct LinkNode *springs; /* The springs connecting the mesh. */
unsigned int numsprings; /* The count of springs. */
unsigned int mvert_num; /* The number of verts == m * n. */
unsigned int tri_num;
struct ClothVertex *verts; /* The vertices that represent this cloth. */
struct LinkNode *springs; /* The springs connecting the mesh. */
unsigned int numsprings; /* The count of springs. */
unsigned int mvert_num; /* The number of verts == m * n. */
unsigned int primitive_num; /* Number of triangles for cloth and edges for hair. */
unsigned char old_solver_type; /* unused, only 1 solver here */
unsigned char pad2;
short pad3;
@ -89,6 +89,7 @@ typedef struct Cloth {
struct EdgeSet *edgeset; /* used for selfcollisions */
int last_frame;
float initial_mesh_volume; /* Initial volume of the mesh. Used for pressure */
struct MEdge *edges; /* Used for hair collisions. */
} Cloth;
/**
@ -265,15 +266,6 @@ int cloth_bvh_collision(struct Depsgraph *depsgraph,
float step,
float dt);
void cloth_find_point_contacts(struct Depsgraph *depsgraph,
struct Object *ob,
struct ClothModifierData *clmd,
float step,
float dt,
ColliderContacts **r_collider_contacts,
int *r_totcolliders);
void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders);
////////////////////////////////////////////////
/////////////////////////////////////////////////

View File

@ -190,22 +190,36 @@ static BVHTree *bvhtree_build_from_cloth(ClothModifierData *clmd, float epsilon)
vt = cloth->tri;
/* in the moment, return zero if no faces there */
if (!cloth->tri_num) {
if (!cloth->primitive_num) {
return NULL;
}
/* create quadtree with k=26 */
bvhtree = BLI_bvhtree_new(cloth->tri_num, epsilon, 4, 26);
bvhtree = BLI_bvhtree_new(cloth->primitive_num, epsilon, 4, 26);
/* fill tree */
for (i = 0; i < cloth->tri_num; i++, vt++) {
float co[3][3];
if (clmd->hairdata == NULL) {
for (i = 0; i < cloth->primitive_num; i++, vt++) {
float co[3][3];
copy_v3_v3(co[0], verts[vt->tri[0]].xold);
copy_v3_v3(co[1], verts[vt->tri[1]].xold);
copy_v3_v3(co[2], verts[vt->tri[2]].xold);
copy_v3_v3(co[0], verts[vt->tri[0]].xold);
copy_v3_v3(co[1], verts[vt->tri[1]].xold);
copy_v3_v3(co[2], verts[vt->tri[2]].xold);
BLI_bvhtree_insert(bvhtree, i, co[0], 3);
BLI_bvhtree_insert(bvhtree, i, co[0], 3);
}
}
else {
MEdge *edges = cloth->edges;
for (i = 0; i < cloth->primitive_num; i++) {
float co[2][3];
copy_v3_v3(co[0], verts[edges[i].v1].xold);
copy_v3_v3(co[1], verts[edges[i].v2].xold);
BLI_bvhtree_insert(bvhtree, i, co[0], 2);
}
}
/* balance tree */
@ -222,6 +236,8 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self)
ClothVertex *verts = cloth->verts;
const MVertTri *vt;
BLI_assert(!(clmd->hairdata != NULL && self));
if (self) {
bvhtree = cloth->bvhselftree;
}
@ -236,39 +252,59 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self)
vt = cloth->tri;
/* update vertex position in bvh tree */
if (verts && vt) {
for (i = 0; i < cloth->tri_num; i++, vt++) {
float co[3][3], co_moving[3][3];
bool ret;
if (clmd->hairdata == NULL) {
if (verts && vt) {
for (i = 0; i < cloth->primitive_num; i++, vt++) {
float co[3][3], co_moving[3][3];
bool ret;
/* copy new locations into array */
if (moving) {
copy_v3_v3(co[0], verts[vt->tri[0]].txold);
copy_v3_v3(co[1], verts[vt->tri[1]].txold);
copy_v3_v3(co[2], verts[vt->tri[2]].txold);
/* copy new locations into array */
if (moving) {
copy_v3_v3(co[0], verts[vt->tri[0]].txold);
copy_v3_v3(co[1], verts[vt->tri[1]].txold);
copy_v3_v3(co[2], verts[vt->tri[2]].txold);
/* update moving positions */
copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx);
copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx);
copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx);
/* update moving positions */
copy_v3_v3(co_moving[0], verts[vt->tri[0]].tx);
copy_v3_v3(co_moving[1], verts[vt->tri[1]].tx);
copy_v3_v3(co_moving[2], verts[vt->tri[2]].tx);
ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3);
}
else {
copy_v3_v3(co[0], verts[vt->tri[0]].tx);
copy_v3_v3(co[1], verts[vt->tri[1]].tx);
copy_v3_v3(co[2], verts[vt->tri[2]].tx);
ret = BLI_bvhtree_update_node(bvhtree, i, co[0], co_moving[0], 3);
}
else {
copy_v3_v3(co[0], verts[vt->tri[0]].tx);
copy_v3_v3(co[1], verts[vt->tri[1]].tx);
copy_v3_v3(co[2], verts[vt->tri[2]].tx);
ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3);
ret = BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 3);
}
/* check if tree is already full */
if (ret == false) {
break;
}
}
/* check if tree is already full */
if (ret == false) {
break;
}
BLI_bvhtree_update_tree(bvhtree);
}
}
else {
if (verts) {
MEdge *edges = cloth->edges;
BLI_bvhtree_update_tree(bvhtree);
for (i = 0; i < cloth->primitive_num; i++) {
float co[2][3];
copy_v3_v3(co[0], verts[edges[i].v1].tx);
copy_v3_v3(co[1], verts[edges[i].v2].tx);
if (!BLI_bvhtree_update_node(bvhtree, i, co[0], NULL, 2)) {
break;
}
}
BLI_bvhtree_update_tree(bvhtree);
}
}
}
@ -900,7 +936,13 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh)
}
/* save face information */
clmd->clothObject->tri_num = looptri_num;
if (clmd->hairdata == NULL) {
clmd->clothObject->primitive_num = looptri_num;
}
else {
clmd->clothObject->primitive_num = mesh->totedge;
}
clmd->clothObject->tri = MEM_mallocN(sizeof(MVertTri) * looptri_num, "clothLoopTris");
if (clmd->clothObject->tri == NULL) {
cloth_free_modifier(clmd);
@ -910,6 +952,8 @@ static void cloth_from_mesh(ClothModifierData *clmd, Mesh *mesh)
}
BKE_mesh_runtime_verttri_from_looptri(clmd->clothObject->tri, mloop, looptri, looptri_num);
clmd->clothObject->edges = mesh->medge;
/* Free the springs since they can't be correct if the vertices
* changed.
*/

View File

@ -33,6 +33,7 @@
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_linklist.h"
#include "BLI_math.h"
#include "BLI_task.h"
#include "BLI_threads.h"
@ -193,17 +194,17 @@ BLI_INLINE int next_ind(int i)
return (++i < 3) ? i : 0;
}
static float compute_collision_point(float a1[3],
const float a2[3],
const float a3[3],
const float b1[3],
const float b2[3],
const float b3[3],
bool culling,
bool use_normal,
float r_a[3],
float r_b[3],
float r_vec[3])
static float compute_collision_point_tri_tri(const float a1[3],
const float a2[3],
const float a3[3],
const float b1[3],
const float b2[3],
const float b3[3],
bool culling,
bool use_normal,
float r_a[3],
float r_b[3],
float r_vec[3])
{
float a[3][3];
float b[3][3];
@ -423,6 +424,179 @@ static float compute_collision_point(float a1[3],
return dist;
}
static float compute_collision_point_edge_tri(const float a1[3],
const float a2[3],
const float b1[3],
const float b2[3],
const float b3[3],
bool culling,
bool use_normal,
float r_a[3],
float r_b[3],
float r_vec[3])
{
float a[2][3];
float b[3][3];
float dist = FLT_MAX;
float tmp_co1[3], tmp_co2[3];
float isect_a[3];
bool isect = false;
float tmp, tmp_vec[3];
float normal[3], cent[3];
bool backside = false;
copy_v3_v3(a[0], a1);
copy_v3_v3(a[1], a2);
copy_v3_v3(b[0], b1);
copy_v3_v3(b[1], b2);
copy_v3_v3(b[2], b3);
normal_tri_v3(normal, b[0], b[1], b[2]);
/* Find intersection. */
if (isect_line_segment_tri_v3(a[0], a[1], b[0], b[1], b[2], &tmp, NULL)) {
interp_v3_v3v3(isect_a, a[0], a[1], tmp);
isect = true;
}
/* Determine collision side. */
if (culling) {
if (isect) {
backside = true;
}
else {
mid_v3_v3v3v3(cent, b[0], b[1], b[2]);
for (int i = 0; i < 2; i++) {
sub_v3_v3v3(tmp_vec, a[i], cent);
if (dot_v3v3(tmp_vec, normal) < 0.0f) {
backside = true;
break;
}
}
}
}
if (isect) {
/* Edge intersection. */
copy_v3_v3(r_a, isect_a);
copy_v3_v3(r_b, isect_a);
copy_v3_v3(r_vec, normal);
return 0.0f;
}
if (backside) {
float maxdist = 0.0f;
bool found = false;
/* Point projections. */
for (int i = 0; i < 2; i++) {
if (isect_ray_tri_v3(a[i], normal, b[0], b[1], b[2], &tmp, NULL)) {
if (tmp > maxdist) {
maxdist = tmp;
copy_v3_v3(r_a, a[i]);
madd_v3_v3v3fl(r_b, a[i], normal, tmp);
found = true;
}
}
}
/* Edge projections. */
for (int i = 0; i < 3; i++) {
float dir[3];
sub_v3_v3v3(tmp_vec, b[next_ind(i)], b[i]);
cross_v3_v3v3(dir, tmp_vec, normal);
if (isect_line_plane_v3(tmp_co1, a[0], a[1], b[i], dir) &&
point_in_slice_seg(tmp_co1, a[0], a[1]) &&
point_in_slice_seg(tmp_co1, b[i], b[next_ind(i)])) {
closest_to_line_v3(tmp_co2, tmp_co1, b[i], b[next_ind(i)]);
sub_v3_v3v3(tmp_vec, tmp_co1, tmp_co2);
tmp = len_v3(tmp_vec);
if ((tmp > maxdist) && (dot_v3v3(tmp_vec, normal) < 0.0f)) {
maxdist = tmp;
copy_v3_v3(r_a, tmp_co1);
copy_v3_v3(r_b, tmp_co2);
found = true;
}
}
}
/* If no point is found, will fallback onto regular proximity test below. */
if (found) {
sub_v3_v3v3(r_vec, r_b, r_a);
if (use_normal) {
if (dot_v3v3(normal, r_vec) >= 0.0f) {
copy_v3_v3(r_vec, normal);
}
else {
negate_v3_v3(r_vec, normal);
}
}
return 0.0f;
}
}
/* Closest point. */
for (int i = 0; i < 2; i++) {
closest_on_tri_to_point_v3(tmp_co1, a[i], b[0], b[1], b[2]);
tmp = len_squared_v3v3(tmp_co1, a[i]);
if (tmp < dist) {
dist = tmp;
copy_v3_v3(r_a, a[i]);
copy_v3_v3(r_b, tmp_co1);
}
}
/* Closest edge. */
if (!isect) {
for (int j = 0; j < 3; j++) {
isect_seg_seg_v3(a[0], a[1], b[j], b[next_ind(j)], tmp_co1, tmp_co2);
tmp = len_squared_v3v3(tmp_co1, tmp_co2);
if (tmp < dist) {
dist = tmp;
copy_v3_v3(r_a, tmp_co1);
copy_v3_v3(r_b, tmp_co2);
}
}
}
if (isect) {
sub_v3_v3v3(r_vec, r_b, r_a);
dist = 0.0f;
}
else {
sub_v3_v3v3(r_vec, r_a, r_b);
dist = sqrtf(dist);
}
if (culling && use_normal) {
copy_v3_v3(r_vec, normal);
}
else if (use_normal) {
if (dot_v3v3(normal, r_vec) >= 0.0f) {
copy_v3_v3(r_vec, normal);
}
else {
negate_v3_v3(r_vec, normal);
}
}
else if (culling && (dot_v3v3(r_vec, normal) < 0.0f)) {
return FLT_MAX;
}
return dist;
}
// w3 is not perfect
static void collision_compute_barycentric(
const float pv[3], float p1[3], float p2[3], float p3[3], float *w1, float *w2, float *w3)
@ -494,6 +668,7 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
float v1[3], v2[3], relativeVelocity[3];
float magrelVel;
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
const bool is_hair = (clmd->hairdata != NULL);
cloth1 = clmd->clothObject;
@ -509,14 +684,32 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
continue;
}
/* Compute barycentric coordinates for both collision points. */
collision_compute_barycentric(collpair->pa,
cloth1->verts[collpair->ap1].tx,
cloth1->verts[collpair->ap2].tx,
cloth1->verts[collpair->ap3].tx,
&w1,
&w2,
&w3);
/* Compute barycentric coordinates and relative "velocity" for both collision points. */
if (is_hair) {
w2 = line_point_factor_v3(
collpair->pa, cloth1->verts[collpair->ap1].tx, cloth1->verts[collpair->ap2].tx);
w1 = 1.0f - w2;
interp_v3_v3v3(v1, cloth1->verts[collpair->ap1].tv, cloth1->verts[collpair->ap2].tv, w2);
}
else {
collision_compute_barycentric(collpair->pa,
cloth1->verts[collpair->ap1].tx,
cloth1->verts[collpair->ap2].tx,
cloth1->verts[collpair->ap3].tx,
&w1,
&w2,
&w3);
collision_interpolateOnTriangle(v1,
cloth1->verts[collpair->ap1].tv,
cloth1->verts[collpair->ap2].tv,
cloth1->verts[collpair->ap3].tv,
w1,
w2,
w3);
}
collision_compute_barycentric(collpair->pb,
collmd->current_xnew[collpair->bp1].co,
@ -526,15 +719,6 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
&u2,
&u3);
/* Calculate relative "velocity". */
collision_interpolateOnTriangle(v1,
cloth1->verts[collpair->ap1].tv,
cloth1->verts[collpair->ap2].tv,
cloth1->verts[collpair->ap3].tv,
w1,
w2,
w3);
collision_interpolateOnTriangle(v2,
collmd->current_v[collpair->bp1].co,
collmd->current_v[collpair->bp2].co,
@ -576,7 +760,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
VECADDMUL(i1, vrel_t_pre, w1 * impulse);
VECADDMUL(i2, vrel_t_pre, w2 * impulse);
VECADDMUL(i3, vrel_t_pre, w3 * impulse);
if (!is_hair) {
VECADDMUL(i3, vrel_t_pre, w3 * impulse);
}
}
/* Apply velocity stopping impulse. */
@ -588,8 +775,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
VECADDMUL(i2, collpair->normal, w2 * impulse);
cloth1->verts[collpair->ap2].impulse_count++;
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap3].impulse_count++;
if (!is_hair) {
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap3].impulse_count++;
}
time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
@ -609,7 +798,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
VECADDMUL(i1, collpair->normal, impulse);
VECADDMUL(i2, collpair->normal, impulse);
VECADDMUL(i3, collpair->normal, impulse);
if (!is_hair) {
VECADDMUL(i3, collpair->normal, impulse);
}
}
result = 1;
@ -627,11 +819,17 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
VECADDMUL(i1, collpair->normal, w1 * impulse);
VECADDMUL(i2, collpair->normal, w2 * impulse);
VECADDMUL(i3, collpair->normal, w3 * impulse);
if (!is_hair) {
VECADDMUL(i3, collpair->normal, w3 * impulse);
}
cloth1->verts[collpair->ap1].impulse_count++;
cloth1->verts[collpair->ap2].impulse_count++;
cloth1->verts[collpair->ap3].impulse_count++;
if (!is_hair) {
cloth1->verts[collpair->ap3].impulse_count++;
}
result = 1;
}
@ -656,9 +854,11 @@ static int cloth_collision_response_static(ClothModifierData *clmd,
cloth1->verts[collpair->ap2].impulse[j] = i2[j];
}
if (cloth1->verts[collpair->ap3].impulse_count > 0 &&
ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) {
cloth1->verts[collpair->ap3].impulse[j] = i3[j];
if (!is_hair) {
if (cloth1->verts[collpair->ap3].impulse_count > 0 &&
ABS(cloth1->verts[collpair->ap3].impulse[j]) < ABS(i3[j])) {
cloth1->verts[collpair->ap3].impulse[j] = i3[j];
}
}
}
}
@ -875,17 +1075,17 @@ static void cloth_collision(void *__restrict userdata,
tri_b = &collmd->tri[data->overlap[index].indexB];
/* Compute distance and normal. */
distance = compute_collision_point(verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
collmd->current_xnew[tri_b->tri[0]].co,
collmd->current_xnew[tri_b->tri[1]].co,
collmd->current_xnew[tri_b->tri[2]].co,
data->culling,
data->use_normal,
pa,
pb,
vect);
distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
collmd->current_xnew[tri_b->tri[0]].co,
collmd->current_xnew[tri_b->tri[1]].co,
collmd->current_xnew[tri_b->tri[2]].co,
data->culling,
data->use_normal,
pa,
pb,
vect);
if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) {
collpair[index].ap1 = tri_a->tri[0];
@ -946,17 +1146,17 @@ static void cloth_selfcollision(void *__restrict userdata,
}
/* Compute distance and normal. */
distance = compute_collision_point(verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
verts1[tri_b->tri[0]].tx,
verts1[tri_b->tri[1]].tx,
verts1[tri_b->tri[2]].tx,
false,
false,
pa,
pb,
vect);
distance = compute_collision_point_tri_tri(verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
verts1[tri_b->tri[0]].tx,
verts1[tri_b->tri[1]].tx,
verts1[tri_b->tri[2]].tx,
false,
false,
pa,
pb,
vect);
if ((distance <= (epsilon * 2.0f + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) {
collpair[index].ap1 = tri_a->tri[0];
@ -983,6 +1183,64 @@ static void cloth_selfcollision(void *__restrict userdata,
}
}
static void hair_collision(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict UNUSED(tls))
{
ColDetectData *data = (ColDetectData *)userdata;
ClothModifierData *clmd = data->clmd;
CollisionModifierData *collmd = data->collmd;
CollPair *collpair = data->collisions;
const MVertTri *tri_coll;
const MEdge *edge_coll;
ClothVertex *verts1 = clmd->clothObject->verts;
float distance = 0.0f;
float epsilon1 = clmd->coll_parms->epsilon;
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
float pa[3], pb[3], vect[3];
/* TODO: This is not efficient. Might be wise to instead build an array before iterating, to
* avoid walking the list every time. */
edge_coll = &clmd->clothObject->edges[data->overlap[index].indexA];
tri_coll = &collmd->tri[data->overlap[index].indexB];
/* Compute distance and normal. */
distance = compute_collision_point_edge_tri(verts1[edge_coll->v1].tx,
verts1[edge_coll->v2].tx,
collmd->current_x[tri_coll->tri[0]].co,
collmd->current_x[tri_coll->tri[1]].co,
collmd->current_x[tri_coll->tri[2]].co,
data->culling,
data->use_normal,
pa,
pb,
vect);
if ((distance <= (epsilon1 + epsilon2 + ALMOST_ZERO)) && (len_squared_v3(vect) > ALMOST_ZERO)) {
collpair[index].ap1 = edge_coll->v1;
collpair[index].ap2 = edge_coll->v2;
collpair[index].bp1 = tri_coll->tri[0];
collpair[index].bp2 = tri_coll->tri[1];
collpair[index].bp3 = tri_coll->tri[2];
copy_v3_v3(collpair[index].pa, pa);
copy_v3_v3(collpair[index].pb, pb);
copy_v3_v3(collpair[index].vector, vect);
normalize_v3_v3(collpair[index].normal, collpair[index].vector);
collpair[index].distance = distance;
collpair[index].flag = 0;
data->collided = true;
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
}
static void add_collision_object(ListBase *relations,
Object *ob,
int level,
@ -1148,6 +1406,7 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd,
bool culling,
bool use_normal)
{
const bool is_hair = (clmd->hairdata != NULL);
*collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult, "collision array");
ColDetectData data = {
@ -1163,7 +1422,8 @@ static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd,
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = true;
BLI_task_parallel_range(0, numresult, &data, cloth_collision, &settings);
BLI_task_parallel_range(
0, numresult, &data, is_hair ? hair_collision : cloth_collision, &settings);
return data.collided;
}
@ -1308,8 +1568,14 @@ int cloth_bvh_collision(
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
bvhtree_update_from_cloth(clmd, false, false);
collobjs = BKE_collision_objects_create(
depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision);
/* Enable self collision if this is a hair sim */
const bool is_hair = (clmd->hairdata != NULL);
collobjs = BKE_collision_objects_create(depsgraph,
is_hair ? NULL : ob,
clmd->coll_parms->group,
&numcollobj,
eModifierType_Collision);
if (collobjs) {
coll_counts_obj = MEM_callocN(sizeof(uint) * numcollobj, "CollCounts");
@ -1474,286 +1740,3 @@ void collision_get_collider_velocity(float vel_old[3],
/* XXX assume constant velocity of the collider for now */
copy_v3_v3(vel_old, vel_new);
}
BLI_INLINE bool cloth_point_face_collision_params(const float p1[3],
const float p2[3],
const float v0[3],
const float v1[3],
const float v2[3],
float r_nor[3],
float *r_lambda,
float r_w[3])
{
float edge1[3], edge2[3], p2face[3], p1p2[3], v0p2[3];
float nor_v0p2, nor_p1p2;
sub_v3_v3v3(edge1, v1, v0);
sub_v3_v3v3(edge2, v2, v0);
cross_v3_v3v3(r_nor, edge1, edge2);
normalize_v3(r_nor);
sub_v3_v3v3(v0p2, p2, v0);
nor_v0p2 = dot_v3v3(v0p2, r_nor);
madd_v3_v3v3fl(p2face, p2, r_nor, -nor_v0p2);
interp_weights_tri_v3(r_w, v0, v1, v2, p2face);
sub_v3_v3v3(p1p2, p2, p1);
nor_p1p2 = dot_v3v3(p1p2, r_nor);
*r_lambda = (nor_p1p2 != 0.0f ? nor_v0p2 / nor_p1p2 : 0.0f);
return r_w[1] >= 0.0f && r_w[2] >= 0.0f && r_w[1] + r_w[2] <= 1.0f;
}
static CollPair *cloth_point_collpair(float p1[3],
const float p2[3],
const MVert *mverts,
int bp1,
int bp2,
int bp3,
int index_cloth,
int index_coll,
float epsilon,
CollPair *collpair)
{
const float *co1 = mverts[bp1].co, *co2 = mverts[bp2].co, *co3 = mverts[bp3].co;
float lambda /*, distance1 */, distance2;
float facenor[3], v1p1[3], v1p2[3];
float w[3];
if (!cloth_point_face_collision_params(p1, p2, co1, co2, co3, facenor, &lambda, w)) {
return collpair;
}
sub_v3_v3v3(v1p1, p1, co1);
// distance1 = dot_v3v3(v1p1, facenor);
sub_v3_v3v3(v1p2, p2, co1);
distance2 = dot_v3v3(v1p2, facenor);
// if (distance2 > epsilon || (distance1 < 0.0f && distance2 < 0.0f))
if (distance2 > epsilon) {
return collpair;
}
collpair->face1 = index_cloth; /* XXX actually not a face, but equivalent index for point */
collpair->face2 = index_coll;
collpair->ap1 = index_cloth;
collpair->ap2 = collpair->ap3 = -1; /* unused */
collpair->bp1 = bp1;
collpair->bp2 = bp2;
collpair->bp3 = bp3;
/* note: using the second point here, which is
* the current updated position that needs to be corrected
*/
copy_v3_v3(collpair->pa, p2);
collpair->distance = distance2;
mul_v3_v3fl(collpair->vector, facenor, -distance2);
interp_v3_v3v3v3(collpair->pb, co1, co2, co3, w);
copy_v3_v3(collpair->normal, facenor);
collpair->time = lambda;
collpair->flag = 0;
collpair++;
return collpair;
}
/* Determines collisions on overlap,
* collisions are written to collpair[i] and collision+number_collision_found is returned. */
static CollPair *cloth_point_collision(ModifierData *md1,
ModifierData *md2,
BVHTreeOverlap *overlap,
float epsilon,
CollPair *collpair,
float UNUSED(dt))
{
ClothModifierData *clmd = (ClothModifierData *)md1;
CollisionModifierData *collmd = (CollisionModifierData *)md2;
/* Cloth *cloth = clmd->clothObject; */ /* UNUSED */
ClothVertex *vert = NULL;
const MVertTri *vt;
const MVert *mverts = collmd->current_x;
vert = &clmd->clothObject->verts[overlap->indexA];
vt = &collmd->tri[overlap->indexB];
collpair = cloth_point_collpair(vert->tx,
vert->x,
mverts,
vt->tri[0],
vt->tri[1],
vt->tri[2],
overlap->indexA,
overlap->indexB,
epsilon,
collpair);
return collpair;
}
static void cloth_points_objcollisions_nearcheck(ClothModifierData *clmd,
CollisionModifierData *collmd,
CollPair **collisions,
CollPair **collisions_index,
int numresult,
BVHTreeOverlap *overlap,
float epsilon,
double dt)
{
int i;
/* can return 2 collisions in total */
*collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult * 2, "collision array");
*collisions_index = *collisions;
for (i = 0; i < numresult; i++) {
*collisions_index = cloth_point_collision(
(ModifierData *)clmd, (ModifierData *)collmd, overlap + i, epsilon, *collisions_index, dt);
}
}
void cloth_find_point_contacts(Depsgraph *depsgraph,
Object *ob,
ClothModifierData *clmd,
float step,
float dt,
ColliderContacts **r_collider_contacts,
int *r_totcolliders)
{
Cloth *cloth = clmd->clothObject;
BVHTree *cloth_bvh;
unsigned int i = 0, mvert_num = 0;
ClothVertex *verts = NULL;
ColliderContacts *collider_contacts;
Object **collobjs = NULL;
unsigned int numcollobj = 0;
verts = cloth->verts;
mvert_num = cloth->mvert_num;
////////////////////////////////////////////////////////////
// static collisions
////////////////////////////////////////////////////////////
/* Check we do have collision objects to test against, before doing anything else. */
collobjs = BKE_collision_objects_create(
depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision);
if (!collobjs) {
*r_collider_contacts = NULL;
*r_totcolliders = 0;
return;
}
// create temporary cloth points bvh
cloth_bvh = BLI_bvhtree_new(mvert_num, clmd->coll_parms->epsilon, 4, 6);
/* fill tree */
for (i = 0; i < mvert_num; i++) {
float co[6];
copy_v3_v3(&co[0 * 3], verts[i].x);
copy_v3_v3(&co[1 * 3], verts[i].tx);
BLI_bvhtree_insert(cloth_bvh, i, co, 2);
}
/* balance tree */
BLI_bvhtree_balance(cloth_bvh);
/* move object to position (step) in time */
for (i = 0; i < numcollobj; i++) {
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(
collob, eModifierType_Collision);
if (!collmd->bvhtree) {
continue;
}
/* move object to position (step) in time */
collision_move_object(collmd, step + dt, step, true);
}
collider_contacts = MEM_callocN(sizeof(ColliderContacts) * numcollobj, "CollPair");
// check all collision objects
for (i = 0; i < numcollobj; i++) {
ColliderContacts *ct = collider_contacts + i;
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(
collob, eModifierType_Collision);
BVHTreeOverlap *overlap;
unsigned int result = 0;
float epsilon;
ct->ob = collob;
ct->collmd = collmd;
ct->collisions = NULL;
ct->totcollisions = 0;
if (!collmd->bvhtree) {
continue;
}
/* search for overlapping collision pairs */
overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL);
epsilon = BLI_bvhtree_get_epsilon(collmd->bvhtree);
// go to next object if no overlap is there
if (result && overlap) {
CollPair *collisions_index;
/* check if collisions really happen (costly near check) */
cloth_points_objcollisions_nearcheck(
clmd, collmd, &ct->collisions, &collisions_index, result, overlap, epsilon, dt);
ct->totcollisions = (int)(collisions_index - ct->collisions);
/* Resolve nearby collisions. */
#if 0
ret += cloth_points_objcollisions_resolve(
clmd, collmd, collob->pd, collisions[i], collisions_index[i], dt);
#endif
}
if (overlap) {
MEM_freeN(overlap);
}
}
BKE_collision_objects_free(collobjs);
BLI_bvhtree_free(cloth_bvh);
////////////////////////////////////////////////////////////
// update positions
// this is needed for bvh_calc_DOP_hull_moving() [kdop.c]
////////////////////////////////////////////////////////////
// verts come from clmd
for (i = 0; i < mvert_num; i++) {
if (clmd->sim_parms->vgroup_mass > 0) {
if (verts[i].flags & CLOTH_VERT_FLAG_PINNED) {
continue;
}
}
add_v3_v3v3(verts[i].tx, verts[i].txold, verts[i].tv);
}
////////////////////////////////////////////////////////////
*r_collider_contacts = collider_contacts;
*r_totcolliders = numcollobj;
}
void cloth_free_contacts(ColliderContacts *collider_contacts, int totcolliders)
{
if (collider_contacts) {
int i;
for (i = 0; i < totcolliders; i++) {
ColliderContacts *ct = collider_contacts + i;
if (ct->collisions) {
MEM_freeN(ct->collisions);
}
}
MEM_freeN(collider_contacts);
}
}

View File

@ -83,7 +83,7 @@ static float cloth_calc_volume(ClothModifierData *clmd)
float vol = 0;
if (clmd->sim_parms->vgroup_pressure > 0) {
for (unsigned int i = 0; i < cloth->tri_num; i++) {
for (unsigned int i = 0; i < cloth->primitive_num; i++) {
bool skip_face = false;
/* We have custom vertex weights for pressure. */
const MVertTri *vt = &tri[i];
@ -103,7 +103,7 @@ static float cloth_calc_volume(ClothModifierData *clmd)
}
}
else {
for (unsigned int i = 0; i < cloth->tri_num; i++) {
for (unsigned int i = 0; i < cloth->primitive_num; i++) {
const MVertTri *vt = &tri[i];
vol += BPH_tri_tetra_volume_signed_6x(data, vt->tri[0], vt->tri[1], vt->tri[2]);
}
@ -174,96 +174,17 @@ void BKE_cloth_solver_set_volume(ClothModifierData *clmd)
cloth->initial_mesh_volume = cloth_calc_volume(clmd);
}
static bool collision_response(ClothModifierData *clmd,
CollisionModifierData *collmd,
CollPair *collpair,
float dt,
float restitution,
float r_impulse[3])
{
Cloth *cloth = clmd->clothObject;
int index = collpair->ap1;
bool result = false;
float v1[3], v2_old[3], v2_new[3], v_rel_old[3], v_rel_new[3];
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
float margin_distance = (float)collpair->distance - epsilon2;
float mag_v_rel;
zero_v3(r_impulse);
if (margin_distance > 0.0f) {
return false; /* XXX tested before already? */
}
/* only handle static collisions here */
if (collpair->flag & COLLISION_IN_FUTURE) {
return false;
}
/* velocity */
copy_v3_v3(v1, cloth->verts[index].v);
collision_get_collider_velocity(v2_old, v2_new, collmd, collpair);
/* relative velocity = velocity of the cloth point relative to the collider */
sub_v3_v3v3(v_rel_old, v1, v2_old);
sub_v3_v3v3(v_rel_new, v1, v2_new);
/* normal component of the relative velocity */
mag_v_rel = dot_v3v3(v_rel_old, collpair->normal);
/* only valid when moving toward the collider */
if (mag_v_rel < -ALMOST_ZERO) {
float v_nor_old, v_nor_new;
float v_tan_old[3], v_tan_new[3];
float bounce, repulse;
/* Collision response based on
* "Simulating Complex Hair with Robust Collision Handling" (Choe, Choi, Ko, ACM SIGGRAPH 2005)
* http://graphics.snu.ac.kr/publications/2005-choe-HairSim/Choe_2005_SCA.pdf
*/
v_nor_old = mag_v_rel;
v_nor_new = dot_v3v3(v_rel_new, collpair->normal);
madd_v3_v3v3fl(v_tan_old, v_rel_old, collpair->normal, -v_nor_old);
madd_v3_v3v3fl(v_tan_new, v_rel_new, collpair->normal, -v_nor_new);
bounce = -v_nor_old * restitution;
repulse = -margin_distance / dt; /* base repulsion velocity in normal direction */
/* XXX this clamping factor is quite arbitrary ...
* not sure if there is a more scientific approach, but seems to give good results
*/
CLAMP(repulse, 0.0f, 4.0f * bounce);
if (margin_distance < -epsilon2) {
mul_v3_v3fl(r_impulse, collpair->normal, max_ff(repulse, bounce) - v_nor_new);
}
else {
bounce = 0.0f;
mul_v3_v3fl(r_impulse, collpair->normal, repulse - v_nor_new);
}
result = true;
}
return result;
}
/* Init constraint matrix
* This is part of the modified CG method suggested by Baraff/Witkin in
* "Large Steps in Cloth Simulation" (Siggraph 1998)
*/
static void cloth_setup_constraints(ClothModifierData *clmd,
ColliderContacts *contacts,
int totcolliders,
float dt)
static void cloth_setup_constraints(ClothModifierData *clmd)
{
Cloth *cloth = clmd->clothObject;
Implicit_Data *data = cloth->implicit;
ClothVertex *verts = cloth->verts;
int mvert_num = cloth->mvert_num;
int i, j, v;
int v;
const float ZERO[3] = {0.0f, 0.0f, 0.0f};
@ -277,37 +198,6 @@ static void cloth_setup_constraints(ClothModifierData *clmd,
verts[v].impulse_count = 0;
}
for (i = 0; i < totcolliders; i++) {
ColliderContacts *ct = &contacts[i];
for (j = 0; j < ct->totcollisions; j++) {
CollPair *collpair = &ct->collisions[j];
// float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - ct->ob->pd->pdef_sbdamp);
float restitution = 0.0f;
int v = collpair->face1;
float impulse[3];
/* pinned verts handled separately */
if (verts[v].flags & CLOTH_VERT_FLAG_PINNED) {
continue;
}
/* XXX cheap way of avoiding instability from multiple collisions in the same step
* this should eventually be supported ...
*/
if (verts[v].impulse_count > 0) {
continue;
}
/* calculate collision response */
if (!collision_response(clmd, ct->collmd, collpair, dt, restitution, impulse)) {
continue;
}
BPH_mass_spring_add_constraint_ndof2(data, v, collpair->normal, impulse);
++verts[v].impulse_count;
}
}
}
/* computes where the cloth would be if it were subject to perfectly stiff edges
@ -691,7 +581,7 @@ static void cloth_calc_force(
pressure_difference *= clmd->sim_parms->pressure_factor;
for (i = 0; i < cloth->tri_num; i++) {
for (i = 0; i < cloth->primitive_num; i++) {
const MVertTri *vt = &tri[i];
if (fabs(pressure_difference) > 1E-6f) {
if (clmd->sim_parms->vgroup_pressure > 0) {
@ -744,13 +634,13 @@ static void cloth_calc_force(
effectors, NULL, clmd->sim_parms->effector_weights, &epoint, winvec[i], NULL);
}
for (i = 0; i < cloth->tri_num; i++) {
for (i = 0; i < cloth->primitive_num; i++) {
const MVertTri *vt = &tri[i];
BPH_mass_spring_force_face_wind(data, vt->tri[0], vt->tri[1], vt->tri[2], winvec);
}
/* Hair has only edges */
if (cloth->tri_num == 0) {
if (cloth->primitive_num == 0) {
#if 0
ClothHairData *hairdata = clmd->hairdata;
ClothHairData *hair_ij, *hair_kl;
@ -1241,8 +1131,6 @@ int BPH_cloth_solve(
unsigned int mvert_num = cloth->mvert_num;
float dt = clmd->sim_parms->dt * clmd->sim_parms->timescale;
Implicit_Data *id = cloth->implicit;
ColliderContacts *contacts = NULL;
int totcolliders = 0;
BKE_sim_debug_data_clear_category("collision");
@ -1269,25 +1157,8 @@ int BPH_cloth_solve(
while (step < tf) {
ImplicitSolverResult result;
if (is_hair) {
/* copy velocities for collision */
for (i = 0; i < mvert_num; i++) {
BPH_mass_spring_get_motion_state(id, i, NULL, verts[i].tv);
copy_v3_v3(verts[i].v, verts[i].tv);
}
/* determine contact points */
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
cloth_find_point_contacts(depsgraph, ob, clmd, 0.0f, tf, &contacts, &totcolliders);
}
/* setup vertex constraints for pinned vertices and contacts */
cloth_setup_constraints(clmd, contacts, totcolliders, dt);
}
else {
/* setup vertex constraints for pinned vertices */
cloth_setup_constraints(clmd, NULL, 0, dt);
}
/* setup vertex constraints for pinned vertices */
cloth_setup_constraints(clmd);
/* initialize forces to zero */
BPH_mass_spring_clear_forces(id);
@ -1300,9 +1171,7 @@ int BPH_cloth_solve(
cloth_record_result(clmd, &result, dt);
/* Calculate collision impulses. */
if (!is_hair) {
cloth_solve_collisions(depsgraph, ob, clmd, step, dt);
}
cloth_solve_collisions(depsgraph, ob, clmd, step, dt);
if (is_hair) {
cloth_continuum_step(clmd, dt);
@ -1327,11 +1196,6 @@ int BPH_cloth_solve(
BPH_mass_spring_get_motion_state(id, i, verts[i].txold, NULL);
}
/* free contact points */
if (contacts) {
cloth_free_contacts(contacts, totcolliders);
}
step += dt;
}