tornavis/source/blender/blenkernel/intern/collision.cc

1768 lines
54 KiB
C++

/* SPDX-FileCopyrightText: Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include "MEM_guardedalloc.h"
#include "DNA_cloth_types.h"
#include "DNA_collection_types.h"
#include "DNA_effect_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_force_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_blenlib.h"
#include "BLI_linklist.h"
#include "BLI_math_geom.h"
#include "BLI_math_vector.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BLI_utildefines.h"
#include "BKE_cloth.hh"
#include "BKE_collection.h"
#include "BKE_effect.h"
#include "BKE_layer.h"
#include "BKE_modifier.hh"
#include "BKE_scene.h"
#include "BKE_collision.h"
#include "BLI_kdopbvh.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_physics.hh"
#include "DEG_depsgraph_query.hh"
#ifdef WITH_ELTOPO
# include "eltopo-capi.h"
#endif
struct ColDetectData {
ClothModifierData *clmd;
CollisionModifierData *collmd;
BVHTreeOverlap *overlap;
CollPair *collisions;
bool culling;
bool use_normal;
bool collided;
};
struct SelfColDetectData {
ClothModifierData *clmd;
BVHTreeOverlap *overlap;
CollPair *collisions;
bool collided;
};
/***********************************
* Collision modifier code start
***********************************/
void collision_move_object(CollisionModifierData *collmd,
const float step,
const float prevstep,
const bool moving_bvh)
{
uint i = 0;
/* the collider doesn't move this frame */
if (collmd->is_static) {
for (i = 0; i < collmd->mvert_num; i++) {
zero_v3(collmd->current_v[i]);
}
return;
}
for (i = 0; i < collmd->mvert_num; i++) {
interp_v3_v3v3(collmd->current_x[i], collmd->x[i], collmd->xnew[i], prevstep);
interp_v3_v3v3(collmd->current_xnew[i], collmd->x[i], collmd->xnew[i], step);
sub_v3_v3v3(collmd->current_v[i], collmd->current_xnew[i], collmd->current_x[i]);
}
bvhtree_update_from_mvert(collmd->bvhtree,
collmd->current_xnew,
collmd->current_x,
collmd->tri,
collmd->tri_num,
moving_bvh);
}
BVHTree *bvhtree_build_from_mvert(const float (*positions)[3],
const MVertTri *tri,
int tri_num,
float epsilon)
{
BVHTree *tree = BLI_bvhtree_new(tri_num, epsilon, 4, 26);
/* fill tree */
int i;
const MVertTri *vt;
for (i = 0, vt = tri; i < tri_num; i++, vt++) {
float co[3][3];
copy_v3_v3(co[0], positions[vt->tri[0]]);
copy_v3_v3(co[1], positions[vt->tri[1]]);
copy_v3_v3(co[2], positions[vt->tri[2]]);
BLI_bvhtree_insert(tree, i, co[0], 3);
}
/* balance tree */
BLI_bvhtree_balance(tree);
return tree;
}
void bvhtree_update_from_mvert(BVHTree *bvhtree,
const float (*positions)[3],
const float (*positions_moving)[3],
const MVertTri *tri,
int tri_num,
bool moving)
{
if ((bvhtree == nullptr) || (positions == nullptr)) {
return;
}
if (positions_moving == nullptr) {
moving = false;
}
const MVertTri *vt;
int i;
for (i = 0, vt = tri; i < tri_num; i++, vt++) {
float co[3][3];
bool ret;
copy_v3_v3(co[0], positions[vt->tri[0]]);
copy_v3_v3(co[1], positions[vt->tri[1]]);
copy_v3_v3(co[2], positions[vt->tri[2]]);
/* copy new locations into array */
if (moving) {
float co_moving[3][3];
/* update moving positions */
copy_v3_v3(co_moving[0], positions_moving[vt->tri[0]]);
copy_v3_v3(co_moving[1], positions_moving[vt->tri[1]]);
copy_v3_v3(co_moving[2], positions_moving[vt->tri[2]]);
ret = BLI_bvhtree_update_node(bvhtree, i, &co[0][0], &co_moving[0][0], 3);
}
else {
ret = BLI_bvhtree_update_node(bvhtree, i, &co[0][0], nullptr, 3);
}
/* check if tree is already full */
if (ret == false) {
break;
}
}
BLI_bvhtree_update_tree(bvhtree);
}
/* ***************************
* Collision modifier code end
* *************************** */
BLI_INLINE int next_ind(int i)
{
return (++i < 3) ? i : 0;
}
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];
float dist = FLT_MAX;
float tmp_co1[3], tmp_co2[3];
float isect_a[3], isect_b[3];
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(a[2], a3);
copy_v3_v3(b[0], b1);
copy_v3_v3(b[1], b2);
copy_v3_v3(b[2], b3);
/* Find intersections. */
int tri_a_edge_isect_count;
const bool is_intersecting = isect_tri_tri_v3_ex(
a, b, isect_a, isect_b, &tri_a_edge_isect_count);
/* Determine collision side. */
if (culling) {
normal_tri_v3(normal, b[0], b[1], b[2]);
mid_v3_v3v3v3(cent, b[0], b[1], b[2]);
if (!is_intersecting) {
for (int i = 0; i < 3; i++) {
sub_v3_v3v3(tmp_vec, a[i], cent);
if (dot_v3v3(tmp_vec, normal) < 0.0f) {
backside = true;
break;
}
}
}
else if (tri_a_edge_isect_count != 1) {
/* It is not Edge intersection. */
backside = true;
}
}
else if (use_normal) {
normal_tri_v3(normal, b[0], b[1], b[2]);
}
if (tri_a_edge_isect_count == 1) {
/* Edge intersection. */
copy_v3_v3(r_a, isect_a);
copy_v3_v3(r_b, isect_b);
if (use_normal) {
copy_v3_v3(r_vec, normal);
}
else {
sub_v3_v3v3(r_vec, r_b, r_a);
}
return 0.0f;
}
if (backside) {
float maxdist = 0.0f;
bool found = false;
/* Point projections. */
for (int i = 0; i < 3; i++) {
if (isect_ray_tri_v3(a[i], normal, b[0], b[1], b[2], &tmp, nullptr)) {
if (tmp > maxdist) {
maxdist = tmp;
copy_v3_v3(r_a, a[i]);
madd_v3_v3v3fl(r_b, a[i], normal, tmp);
found = true;
}
}
}
negate_v3(normal);
for (int i = 0; i < 3; i++) {
if (isect_ray_tri_v3(b[i], normal, a[0], a[1], a[2], &tmp, nullptr)) {
if (tmp > maxdist) {
maxdist = tmp;
madd_v3_v3v3fl(r_a, b[i], normal, tmp);
copy_v3_v3(r_b, b[i]);
found = true;
}
}
}
negate_v3(normal);
/* 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);
for (int j = 0; j < 3; j++) {
if (isect_line_plane_v3(tmp_co1, a[j], a[next_ind(j)], b[i], dir) &&
point_in_slice_seg(tmp_co1, a[j], a[next_ind(j)]) &&
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 < 3; 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);
}
}
for (int i = 0; i < 3; i++) {
closest_on_tri_to_point_v3(tmp_co1, b[i], a[0], a[1], a[2]);
tmp = len_squared_v3v3(tmp_co1, b[i]);
if (tmp < dist) {
dist = tmp;
copy_v3_v3(r_a, tmp_co1);
copy_v3_v3(r_b, b[i]);
}
}
/* Closest edge. */
if (!is_intersecting) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
isect_seg_seg_v3(a[i], a[next_ind(i)], 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 (!is_intersecting) {
sub_v3_v3v3(r_vec, r_a, r_b);
dist = sqrtf(dist);
}
else {
sub_v3_v3v3(r_vec, r_b, r_a);
dist = 0.0f;
}
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;
}
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, nullptr)) {
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, nullptr)) {
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],
const float p1[3],
const float p2[3],
const float p3[3],
float *w1,
float *w2,
float *w3)
{
/* dot_v3v3 */
#define INPR(v1, v2) ((v1)[0] * (v2)[0] + (v1)[1] * (v2)[1] + (v1)[2] * (v2)[2])
double tempV1[3], tempV2[3], tempV4[3];
double a, b, c, d, e, f;
sub_v3db_v3fl_v3fl(tempV1, p1, p3);
sub_v3db_v3fl_v3fl(tempV2, p2, p3);
sub_v3db_v3fl_v3fl(tempV4, pv, p3);
a = INPR(tempV1, tempV1);
b = INPR(tempV1, tempV2);
c = INPR(tempV2, tempV2);
e = INPR(tempV1, tempV4);
f = INPR(tempV2, tempV4);
d = (a * c - b * b);
if (fabs(d) < double(ALMOST_ZERO)) {
*w1 = *w2 = *w3 = 1.0 / 3.0;
return;
}
w1[0] = float((e * c - b * f) / d);
if (w1[0] < 0) {
w1[0] = 0;
}
w2[0] = float((f - b * double(w1[0])) / c);
if (w2[0] < 0) {
w2[0] = 0;
}
w3[0] = 1.0f - w1[0] - w2[0];
#undef INPR
}
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdouble-promotion"
#endif
DO_INLINE void collision_interpolateOnTriangle(float to[3],
const float v1[3],
const float v2[3],
const float v3[3],
const double w1,
const double w2,
const double w3)
{
zero_v3(to);
VECADDMUL(to, v1, w1);
VECADDMUL(to, v2, w2);
VECADDMUL(to, v3, w3);
}
static void cloth_collision_impulse_vert(const float clamp_sq,
const float impulse[3],
ClothVertex *vert)
{
float impulse_len_sq = len_squared_v3(impulse);
if ((clamp_sq > 0.0f) && (impulse_len_sq > clamp_sq)) {
return;
}
if (fabsf(vert->impulse[0]) < fabsf(impulse[0])) {
vert->impulse[0] = impulse[0];
}
if (fabsf(vert->impulse[1]) < fabsf(impulse[1])) {
vert->impulse[1] = impulse[1];
}
if (fabsf(vert->impulse[2]) < fabsf(impulse[2])) {
vert->impulse[2] = impulse[2];
}
vert->impulse_count++;
}
static int cloth_collision_response_static(ClothModifierData *clmd,
CollisionModifierData *collmd,
Object *collob,
CollPair *collpair,
uint collision_count,
const float dt)
{
int result = 0;
Cloth *cloth = clmd->clothObject;
const float clamp_sq = square_f(clmd->coll_parms->clamp * dt);
const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
const float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
const float min_distance = (clmd->coll_parms->epsilon + epsilon2) * (8.0f / 9.0f);
const bool is_hair = (clmd->hairdata != nullptr);
for (int i = 0; i < collision_count; i++, collpair++) {
float i1[3], i2[3], i3[3];
float v1[3], v2[3], relativeVelocity[3];
zero_v3(i1);
zero_v3(i2);
zero_v3(i3);
/* Only handle static collisions here. */
if (collpair->flag & (COLLISION_IN_FUTURE | COLLISION_INACTIVE)) {
continue;
}
/* Compute barycentric coordinates and relative "velocity" for both collision points. */
float w1 = collpair->aw1, w2 = collpair->aw2, w3 = collpair->aw3;
float u1 = collpair->bw1, u2 = collpair->bw2, u3 = collpair->bw3;
if (is_hair) {
interp_v3_v3v3(v1, cloth->verts[collpair->ap1].tv, cloth->verts[collpair->ap2].tv, w2);
}
else {
collision_interpolateOnTriangle(v1,
cloth->verts[collpair->ap1].tv,
cloth->verts[collpair->ap2].tv,
cloth->verts[collpair->ap3].tv,
w1,
w2,
w3);
}
collision_interpolateOnTriangle(v2,
collmd->current_v[collpair->bp1],
collmd->current_v[collpair->bp2],
collmd->current_v[collpair->bp3],
u1,
u2,
u3);
sub_v3_v3v3(relativeVelocity, v2, v1);
/* Calculate the normal component of the relative velocity
* (actually only the magnitude - the direction is stored in 'normal'). */
const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
const float d = min_distance - collpair->distance;
/* If magrelVel < 0 the edges are approaching each other. */
if (magrelVel > 0.0f) {
/* Calculate Impulse magnitude to stop all motion in normal direction. */
float magtangent = 0, repulse = 0;
double impulse = 0.0;
float vrel_t_pre[3];
float temp[3];
/* Calculate tangential velocity. */
copy_v3_v3(temp, collpair->normal);
mul_v3_fl(temp, magrelVel);
sub_v3_v3v3(vrel_t_pre, relativeVelocity, temp);
/* Decrease in magnitude of relative tangential velocity due to coulomb friction
* in original formula "magrelVel" should be the
* "change of relative velocity in normal direction". */
magtangent = min_ff(collob->pd->pdef_cfrict * 0.01f * magrelVel, len_v3(vrel_t_pre));
/* Apply friction impulse. */
if (magtangent > ALMOST_ZERO) {
normalize_v3(vrel_t_pre);
impulse = magtangent / 1.5;
VECADDMUL(i1, vrel_t_pre, double(w1) * impulse);
VECADDMUL(i2, vrel_t_pre, double(w2) * impulse);
if (!is_hair) {
VECADDMUL(i3, vrel_t_pre, double(w3) * impulse);
}
}
/* Apply velocity stopping impulse. */
impulse = magrelVel / 1.5f;
VECADDMUL(i1, collpair->normal, double(w1) * impulse);
VECADDMUL(i2, collpair->normal, double(w2) * impulse);
if (!is_hair) {
VECADDMUL(i3, collpair->normal, double(w3) * impulse);
}
if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) {
repulse = std::min(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel);
/* Stay on the safe side and clamp repulse. */
if (impulse > ALMOST_ZERO) {
repulse = min_ff(repulse, 5.0f * impulse);
}
repulse = max_ff(impulse, repulse);
impulse = repulse / 1.5f;
VECADDMUL(i1, collpair->normal, impulse);
VECADDMUL(i2, collpair->normal, impulse);
if (!is_hair) {
VECADDMUL(i3, collpair->normal, impulse);
}
}
result = 1;
}
else if (d > ALMOST_ZERO) {
/* Stay on the safe side and clamp repulse. */
float repulse = d / time_multiplier;
float impulse = repulse / 4.5f;
VECADDMUL(i1, collpair->normal, w1 * impulse);
VECADDMUL(i2, collpair->normal, w2 * impulse);
if (!is_hair) {
VECADDMUL(i3, collpair->normal, w3 * impulse);
}
result = 1;
}
if (result) {
cloth_collision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]);
cloth_collision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]);
if (!is_hair) {
cloth_collision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]);
}
}
}
return result;
}
static int cloth_selfcollision_response_static(ClothModifierData *clmd,
CollPair *collpair,
uint collision_count,
const float dt)
{
int result = 0;
Cloth *cloth = clmd->clothObject;
const float clamp_sq = square_f(clmd->coll_parms->self_clamp * dt);
const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
const float min_distance = (2.0f * clmd->coll_parms->selfepsilon) * (8.0f / 9.0f);
for (int i = 0; i < collision_count; i++, collpair++) {
float ia[3][3] = {{0.0f}};
float ib[3][3] = {{0.0f}};
float v1[3], v2[3], relativeVelocity[3];
/* Only handle static collisions here. */
if (collpair->flag & (COLLISION_IN_FUTURE | COLLISION_INACTIVE)) {
continue;
}
/* Retrieve barycentric coordinates for both collision points. */
float w1 = collpair->aw1, w2 = collpair->aw2, w3 = collpair->aw3;
float u1 = collpair->bw1, u2 = collpair->bw2, u3 = collpair->bw3;
/* Calculate relative "velocity". */
collision_interpolateOnTriangle(v1,
cloth->verts[collpair->ap1].tv,
cloth->verts[collpair->ap2].tv,
cloth->verts[collpair->ap3].tv,
w1,
w2,
w3);
collision_interpolateOnTriangle(v2,
cloth->verts[collpair->bp1].tv,
cloth->verts[collpair->bp2].tv,
cloth->verts[collpair->bp3].tv,
u1,
u2,
u3);
sub_v3_v3v3(relativeVelocity, v2, v1);
/* Calculate the normal component of the relative velocity
* (actually only the magnitude - the direction is stored in 'normal'). */
const float magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
const float d = min_distance - collpair->distance;
/* TODO: Impulses should be weighed by mass as this is self col,
* this has to be done after mass distribution is implemented. */
/* If magrelVel < 0 the edges are approaching each other. */
if (magrelVel > 0.0f) {
/* Calculate Impulse magnitude to stop all motion in normal direction. */
float magtangent = 0, repulse = 0;
double impulse = 0.0;
float vrel_t_pre[3];
float temp[3];
/* Calculate tangential velocity. */
copy_v3_v3(temp, collpair->normal);
mul_v3_fl(temp, magrelVel);
sub_v3_v3v3(vrel_t_pre, relativeVelocity, temp);
/* Decrease in magnitude of relative tangential velocity due to coulomb friction
* in original formula "magrelVel" should be the
* "change of relative velocity in normal direction". */
magtangent = min_ff(clmd->coll_parms->self_friction * 0.01f * magrelVel, len_v3(vrel_t_pre));
/* Apply friction impulse. */
if (magtangent > ALMOST_ZERO) {
normalize_v3(vrel_t_pre);
impulse = magtangent / 1.5;
VECADDMUL(ia[0], vrel_t_pre, double(w1) * impulse);
VECADDMUL(ia[1], vrel_t_pre, double(w2) * impulse);
VECADDMUL(ia[2], vrel_t_pre, double(w3) * impulse);
VECADDMUL(ib[0], vrel_t_pre, double(u1) * -impulse);
VECADDMUL(ib[1], vrel_t_pre, double(u2) * -impulse);
VECADDMUL(ib[2], vrel_t_pre, double(u3) * -impulse);
}
/* Apply velocity stopping impulse. */
impulse = magrelVel / 3.0f;
VECADDMUL(ia[0], collpair->normal, double(w1) * impulse);
VECADDMUL(ia[1], collpair->normal, double(w2) * impulse);
VECADDMUL(ia[2], collpair->normal, double(w3) * impulse);
VECADDMUL(ib[0], collpair->normal, double(u1) * -impulse);
VECADDMUL(ib[1], collpair->normal, double(u2) * -impulse);
VECADDMUL(ib[2], collpair->normal, double(u3) * -impulse);
if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) {
repulse = MIN2(d / time_multiplier, 0.1f * d * time_multiplier - magrelVel);
if (impulse > ALMOST_ZERO) {
repulse = min_ff(repulse, 5.0 * impulse);
}
repulse = max_ff(impulse, repulse);
impulse = repulse / 1.5f;
VECADDMUL(ia[0], collpair->normal, double(w1) * impulse);
VECADDMUL(ia[1], collpair->normal, double(w2) * impulse);
VECADDMUL(ia[2], collpair->normal, double(w3) * impulse);
VECADDMUL(ib[0], collpair->normal, double(u1) * -impulse);
VECADDMUL(ib[1], collpair->normal, double(u2) * -impulse);
VECADDMUL(ib[2], collpair->normal, double(u3) * -impulse);
}
result = 1;
}
else if (d > ALMOST_ZERO) {
/* Stay on the safe side and clamp repulse. */
float repulse = d * 1.0f / time_multiplier;
float impulse = repulse / 9.0f;
VECADDMUL(ia[0], collpair->normal, w1 * impulse);
VECADDMUL(ia[1], collpair->normal, w2 * impulse);
VECADDMUL(ia[2], collpair->normal, w3 * impulse);
VECADDMUL(ib[0], collpair->normal, u1 * -impulse);
VECADDMUL(ib[1], collpair->normal, u2 * -impulse);
VECADDMUL(ib[2], collpair->normal, u3 * -impulse);
result = 1;
}
if (result) {
cloth_collision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]);
cloth_collision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]);
cloth_collision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]);
cloth_collision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]);
cloth_collision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]);
cloth_collision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]);
}
}
return result;
}
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
static bool cloth_bvh_collision_is_active(const ClothModifierData * /*clmd*/,
const Cloth *cloth,
const MVertTri *tri_a)
{
const ClothVertex *verts = cloth->verts;
/* Fully pinned triangles don't need collision processing. */
const int flags_a = verts[tri_a->tri[0]].flags & verts[tri_a->tri[1]].flags &
verts[tri_a->tri[2]].flags;
if (flags_a & (CLOTH_VERT_FLAG_PINNED | CLOTH_VERT_FLAG_NOOBJCOLL)) {
return false;
}
return true;
}
static void cloth_collision(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict /*tls*/)
{
ColDetectData *data = (ColDetectData *)userdata;
ClothModifierData *clmd = data->clmd;
CollisionModifierData *collmd = data->collmd;
CollPair *collpair = data->collisions;
const MVertTri *tri_a, *tri_b;
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];
tri_a = &clmd->clothObject->tri[data->overlap[index].indexA];
tri_b = &collmd->tri[data->overlap[index].indexB];
/* Compute distance and normal. */
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]],
collmd->current_xnew[tri_b->tri[1]],
collmd->current_xnew[tri_b->tri[2]],
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];
collpair[index].ap2 = tri_a->tri[1];
collpair[index].ap3 = tri_a->tri[2];
collpair[index].bp1 = tri_b->tri[0];
collpair[index].bp2 = tri_b->tri[1];
collpair[index].bp3 = tri_b->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;
/* Compute barycentric coordinates for both collision points. */
collision_compute_barycentric(pa,
verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
&collpair[index].aw1,
&collpair[index].aw2,
&collpair[index].aw3);
collision_compute_barycentric(pb,
collmd->current_xnew[tri_b->tri[0]],
collmd->current_xnew[tri_b->tri[1]],
collmd->current_xnew[tri_b->tri[2]],
&collpair[index].bw1,
&collpair[index].bw2,
&collpair[index].bw3);
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
}
static bool cloth_bvh_selfcollision_is_active(const ClothModifierData *clmd,
const Cloth *cloth,
const MVertTri *tri_a,
const MVertTri *tri_b)
{
const ClothVertex *verts = cloth->verts;
/* Skip when either triangle is excluded. */
const int flags_a = verts[tri_a->tri[0]].flags & verts[tri_a->tri[1]].flags &
verts[tri_a->tri[2]].flags;
const int flags_b = verts[tri_b->tri[0]].flags & verts[tri_b->tri[1]].flags &
verts[tri_b->tri[2]].flags;
if ((flags_a | flags_b) & CLOTH_VERT_FLAG_NOSELFCOLL) {
return false;
}
/* Skip when both triangles are pinned. */
if ((flags_a & flags_b) & CLOTH_VERT_FLAG_PINNED) {
return false;
}
/* Ignore overlap of neighboring triangles and triangles connected by a sewing edge. */
bool sewing_active = (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW);
for (uint i = 0; i < 3; i++) {
for (uint j = 0; j < 3; j++) {
if (tri_a->tri[i] == tri_b->tri[j]) {
return false;
}
if (sewing_active) {
if (cloth->sew_edge_graph.contains({tri_a->tri[i], tri_b->tri[j]})) {
return false;
}
}
}
}
return true;
}
static void cloth_selfcollision(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict /*tls*/)
{
SelfColDetectData *data = (SelfColDetectData *)userdata;
ClothModifierData *clmd = data->clmd;
CollPair *collpair = data->collisions;
const MVertTri *tri_a, *tri_b;
ClothVertex *verts1 = clmd->clothObject->verts;
float distance = 0.0f;
float epsilon = clmd->coll_parms->selfepsilon;
float pa[3], pb[3], vect[3];
/* Collision math is currently not symmetric, so ensure a stable order for each pair. */
int indexA = data->overlap[index].indexA, indexB = data->overlap[index].indexB;
if (indexA > indexB) {
SWAP(int, indexA, indexB);
}
tri_a = &clmd->clothObject->tri[indexA];
tri_b = &clmd->clothObject->tri[indexB];
BLI_assert(cloth_bvh_selfcollision_is_active(clmd, clmd->clothObject, tri_a, tri_b));
/* Compute distance and normal. */
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];
collpair[index].ap2 = tri_a->tri[1];
collpair[index].ap3 = tri_a->tri[2];
collpair[index].bp1 = tri_b->tri[0];
collpair[index].bp2 = tri_b->tri[1];
collpair[index].bp3 = tri_b->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;
/* Compute barycentric coordinates for both collision points. */
collision_compute_barycentric(pa,
verts1[tri_a->tri[0]].tx,
verts1[tri_a->tri[1]].tx,
verts1[tri_a->tri[2]].tx,
&collpair[index].aw1,
&collpair[index].aw2,
&collpair[index].aw3);
collision_compute_barycentric(pb,
verts1[tri_b->tri[0]].tx,
verts1[tri_b->tri[1]].tx,
verts1[tri_b->tri[2]].tx,
&collpair[index].bw1,
&collpair[index].bw2,
&collpair[index].bw3);
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
}
static void hair_collision(void *__restrict userdata,
const int index,
const TaskParallelTLS *__restrict /*tls*/)
{
ColDetectData *data = (ColDetectData *)userdata;
ClothModifierData *clmd = data->clmd;
CollisionModifierData *collmd = data->collmd;
CollPair *collpair = data->collisions;
const MVertTri *tri_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. */
const blender::int2 &edge_coll = reinterpret_cast<const blender::int2 *>(
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[0]].tx,
verts1[edge_coll[1]].tx,
collmd->current_x[tri_coll->tri[0]],
collmd->current_x[tri_coll->tri[1]],
collmd->current_x[tri_coll->tri[2]],
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[0];
collpair[index].ap2 = edge_coll[1];
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;
/* Compute barycentric coordinates for the collision points. */
collpair[index].aw2 = line_point_factor_v3(
pa, verts1[edge_coll[0]].tx, verts1[edge_coll[1]].tx);
collpair[index].aw1 = 1.0f - collpair[index].aw2;
collision_compute_barycentric(pb,
collmd->current_xnew[tri_coll->tri[0]],
collmd->current_xnew[tri_coll->tri[1]],
collmd->current_xnew[tri_coll->tri[2]],
&collpair[index].bw1,
&collpair[index].bw2,
&collpair[index].bw3);
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
}
static void add_collision_object(ListBase *relations,
Object *ob,
int level,
const ModifierType modifier_type)
{
/* only get objects with collision modifier */
ModifierData *cmd = BKE_modifiers_findby_type(ob, modifier_type);
if (cmd) {
CollisionRelation *relation = MEM_cnew<CollisionRelation>(__func__);
relation->ob = ob;
BLI_addtail(relations, relation);
}
/* objects in dupli groups, one level only for now */
/* TODO: this doesn't really work, we are not taking into account the
* dupli transforms and can get objects in the list multiple times. */
if (ob->instance_collection && level == 0) {
Collection *collection = ob->instance_collection;
/* add objects */
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) {
add_collision_object(relations, object, level + 1, modifier_type);
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
}
}
ListBase *BKE_collision_relations_create(Depsgraph *depsgraph,
Collection *collection,
uint modifier_type)
{
const Scene *scene = DEG_get_input_scene(depsgraph);
ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph);
Base *base = BKE_collection_or_layer_objects(scene, view_layer, collection);
const bool for_render = (DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
const int base_flag = (for_render) ? BASE_ENABLED_RENDER : BASE_ENABLED_VIEWPORT;
ListBase *relations = MEM_cnew<ListBase>(__func__);
for (; base; base = base->next) {
if (base->flag & base_flag) {
add_collision_object(relations, base->object, 0, ModifierType(modifier_type));
}
}
return relations;
}
void BKE_collision_relations_free(ListBase *relations)
{
if (relations) {
BLI_freelistN(relations);
MEM_freeN(relations);
}
}
Object **BKE_collision_objects_create(Depsgraph *depsgraph,
Object *self,
Collection *collection,
uint *numcollobj,
uint modifier_type)
{
ListBase *relations = DEG_get_collision_relations(depsgraph, collection, modifier_type);
if (!relations) {
*numcollobj = 0;
return nullptr;
}
int maxnum = BLI_listbase_count(relations);
int num = 0;
Object **objects = MEM_cnew_array<Object *>(maxnum, __func__);
LISTBASE_FOREACH (CollisionRelation *, relation, relations) {
/* Get evaluated object. */
Object *ob = (Object *)DEG_get_evaluated_id(depsgraph, &relation->ob->id);
if (modifier_type == eModifierType_Collision && !(ob->pd && ob->pd->deflect)) {
continue;
}
if (ob != self) {
objects[num] = ob;
num++;
}
}
if (num == 0) {
MEM_freeN(objects);
objects = nullptr;
}
*numcollobj = num;
return objects;
}
void BKE_collision_objects_free(Object **objects)
{
if (objects) {
MEM_freeN(objects);
}
}
ListBase *BKE_collider_cache_create(Depsgraph *depsgraph, Object *self, Collection *collection)
{
ListBase *relations = DEG_get_collision_relations(
depsgraph, collection, eModifierType_Collision);
ListBase *cache = nullptr;
if (!relations) {
return nullptr;
}
LISTBASE_FOREACH (CollisionRelation *, relation, relations) {
/* Get evaluated object. */
Object *ob = (Object *)DEG_get_evaluated_id(depsgraph, &relation->ob->id);
if (ob == self) {
continue;
}
CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type(
ob, eModifierType_Collision);
if (cmd && cmd->bvhtree) {
if (cache == nullptr) {
cache = MEM_cnew<ListBase>(__func__);
}
ColliderCache *col = MEM_cnew<ColliderCache>(__func__);
col->ob = ob;
col->collmd = cmd;
/* make sure collider is properly set up */
collision_move_object(cmd, 1.0, 0.0, true);
BLI_addtail(cache, col);
}
}
return cache;
}
void BKE_collider_cache_free(ListBase **colliders)
{
if (*colliders) {
BLI_freelistN(*colliders);
MEM_freeN(*colliders);
*colliders = nullptr;
}
}
static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData *clmd,
CollisionModifierData *collmd,
CollPair **collisions,
int numresult,
BVHTreeOverlap *overlap,
bool culling,
bool use_normal)
{
const bool is_hair = (clmd->hairdata != nullptr);
*collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult, "collision array");
ColDetectData data{};
data.clmd = clmd;
data.collmd = collmd;
data.overlap = overlap;
data.collisions = *collisions;
data.culling = culling;
data.use_normal = use_normal;
data.collided = false;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = true;
BLI_task_parallel_range(
0, numresult, &data, is_hair ? hair_collision : cloth_collision, &settings);
return data.collided;
}
static bool cloth_bvh_selfcollisions_nearcheck(ClothModifierData *clmd,
CollPair *collisions,
int numresult,
BVHTreeOverlap *overlap)
{
SelfColDetectData data{};
data.clmd = clmd;
data.overlap = overlap;
data.collisions = collisions;
data.collided = false;
TaskParallelSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = true;
BLI_task_parallel_range(0, numresult, &data, cloth_selfcollision, &settings);
return data.collided;
}
static int cloth_bvh_objcollisions_resolve(ClothModifierData *clmd,
Object **collobjs,
CollPair **collisions,
uint *collision_counts,
const uint numcollobj,
const float dt)
{
Cloth *cloth = clmd->clothObject;
int i = 0, j = 0, mvert_num = 0;
ClothVertex *verts = nullptr;
int ret = 0;
int result = 0;
mvert_num = clmd->clothObject->mvert_num;
verts = cloth->verts;
result = 1;
for (j = 0; j < 2; j++) {
result = 0;
for (i = 0; i < numcollobj; i++) {
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)BKE_modifiers_findby_type(
collob, eModifierType_Collision);
if (collmd->bvhtree) {
result += cloth_collision_response_static(
clmd, collmd, collob, collisions[i], collision_counts[i], dt);
}
}
/* Apply impulses in parallel. */
if (result) {
for (i = 0; i < mvert_num; i++) {
// calculate "velocities" (just xnew = xold + v; no dt in v)
if (verts[i].impulse_count) {
add_v3_v3(verts[i].tv, verts[i].impulse);
add_v3_v3(verts[i].dcvel, verts[i].impulse);
zero_v3(verts[i].impulse);
verts[i].impulse_count = 0;
ret++;
}
}
}
else {
break;
}
}
return ret;
}
static int cloth_bvh_selfcollisions_resolve(ClothModifierData *clmd,
CollPair *collisions,
int collision_count,
const float dt)
{
Cloth *cloth = clmd->clothObject;
int i = 0, j = 0, mvert_num = 0;
ClothVertex *verts = nullptr;
int ret = 0;
int result = 0;
mvert_num = clmd->clothObject->mvert_num;
verts = cloth->verts;
for (j = 0; j < 2; j++) {
result = 0;
result += cloth_selfcollision_response_static(clmd, collisions, collision_count, dt);
/* Apply impulses in parallel. */
if (result) {
for (i = 0; i < mvert_num; i++) {
if (verts[i].impulse_count) {
// VECADDMUL ( verts[i].tv, verts[i].impulse, 1.0f / verts[i].impulse_count );
add_v3_v3(verts[i].tv, verts[i].impulse);
add_v3_v3(verts[i].dcvel, verts[i].impulse);
zero_v3(verts[i].impulse);
verts[i].impulse_count = 0;
ret++;
}
}
}
if (!result) {
break;
}
}
return ret;
}
static bool cloth_bvh_obj_overlap_cb(void *userdata, int index_a, int /*index_b*/, int /*thread*/)
{
ClothModifierData *clmd = (ClothModifierData *)userdata;
Cloth *clothObject = clmd->clothObject;
const MVertTri *tri_a = &clothObject->tri[index_a];
return cloth_bvh_collision_is_active(clmd, clothObject, tri_a);
}
static bool cloth_bvh_self_overlap_cb(void *userdata, int index_a, int index_b, int /*thread*/)
{
/* This shouldn't happen, but just in case. Note that equal combinations
* (eg. (0,1) & (1,0)) would be filtered out by BLI_bvhtree_overlap_self. */
if (index_a != index_b) {
ClothModifierData *clmd = (ClothModifierData *)userdata;
Cloth *clothObject = clmd->clothObject;
const MVertTri *tri_a, *tri_b;
tri_a = &clothObject->tri[index_a];
tri_b = &clothObject->tri[index_b];
if (cloth_bvh_selfcollision_is_active(clmd, clothObject, tri_a, tri_b)) {
return true;
}
}
return false;
}
int cloth_bvh_collision(
Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt)
{
Cloth *cloth = clmd->clothObject;
BVHTree *cloth_bvh = cloth->bvhtree;
uint i = 0, mvert_num = 0;
int rounds = 0;
ClothVertex *verts = nullptr;
int ret = 0, ret2 = 0;
Object **collobjs = nullptr;
uint numcollobj = 0;
uint *coll_counts_obj = nullptr;
BVHTreeOverlap **overlap_obj = nullptr;
uint coll_count_self = 0;
BVHTreeOverlap *overlap_self = nullptr;
bool bvh_updated = false;
if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_COLLOBJ) || cloth_bvh == nullptr) {
return 0;
}
verts = cloth->verts;
mvert_num = cloth->mvert_num;
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
bvhtree_update_from_cloth(clmd, false, false);
bvh_updated = true;
/* Enable self collision if this is a hair sim */
const bool is_hair = (clmd->hairdata != nullptr);
collobjs = BKE_collision_objects_create(depsgraph,
is_hair ? nullptr : ob,
clmd->coll_parms->group,
&numcollobj,
eModifierType_Collision);
if (collobjs) {
coll_counts_obj = MEM_cnew_array<uint>(numcollobj, "CollCounts");
overlap_obj = MEM_cnew_array<BVHTreeOverlap *>(numcollobj, "BVHOverlap");
for (i = 0; i < numcollobj; i++) {
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)BKE_modifiers_findby_type(
collob, eModifierType_Collision);
if (!collmd->bvhtree) {
continue;
}
/* Move object to position (step) in time. */
collision_move_object(collmd, step + dt, step, false);
overlap_obj[i] = BLI_bvhtree_overlap(cloth_bvh,
collmd->bvhtree,
&coll_counts_obj[i],
is_hair ? nullptr : cloth_bvh_obj_overlap_cb,
clmd);
}
}
}
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
if (cloth->bvhselftree != cloth->bvhtree || !bvh_updated) {
bvhtree_update_from_cloth(clmd, false, true);
}
overlap_self = BLI_bvhtree_overlap_self(
cloth->bvhselftree, &coll_count_self, cloth_bvh_self_overlap_cb, clmd);
}
do {
ret2 = 0;
/* Object collisions. */
if ((clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) && collobjs) {
CollPair **collisions;
bool collided = false;
collisions = MEM_cnew_array<CollPair *>(numcollobj, "CollPair");
for (i = 0; i < numcollobj; i++) {
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)BKE_modifiers_findby_type(
collob, eModifierType_Collision);
if (!collmd->bvhtree) {
continue;
}
if (coll_counts_obj[i] && overlap_obj[i]) {
collided = cloth_bvh_objcollisions_nearcheck(
clmd,
collmd,
&collisions[i],
coll_counts_obj[i],
overlap_obj[i],
(collob->pd->flag & PFIELD_CLOTH_USE_CULLING),
(collob->pd->flag & PFIELD_CLOTH_USE_NORMAL)) ||
collided;
}
}
if (collided) {
ret += cloth_bvh_objcollisions_resolve(
clmd, collobjs, collisions, coll_counts_obj, numcollobj, dt);
ret2 += ret;
}
for (i = 0; i < numcollobj; i++) {
MEM_SAFE_FREE(collisions[i]);
}
MEM_freeN(collisions);
}
/* Self collisions. */
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
CollPair *collisions = nullptr;
verts = cloth->verts;
mvert_num = cloth->mvert_num;
if (cloth->bvhselftree) {
if (coll_count_self && overlap_self) {
collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * coll_count_self,
"collision array");
if (cloth_bvh_selfcollisions_nearcheck(clmd, collisions, coll_count_self, overlap_self))
{
ret += cloth_bvh_selfcollisions_resolve(clmd, collisions, coll_count_self, dt);
ret2 += ret;
}
}
}
MEM_SAFE_FREE(collisions);
}
/* Apply all collision resolution. */
if (ret2) {
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);
}
}
rounds++;
} while (ret2 && (clmd->coll_parms->loop_count > rounds));
if (overlap_obj) {
for (i = 0; i < numcollobj; i++) {
MEM_SAFE_FREE(overlap_obj[i]);
}
MEM_freeN(overlap_obj);
}
MEM_SAFE_FREE(coll_counts_obj);
MEM_SAFE_FREE(overlap_self);
BKE_collision_objects_free(collobjs);
return MIN2(ret, 1);
}
BLI_INLINE void max_v3_v3v3(float r[3], const float a[3], const float b[3])
{
r[0] = max_ff(a[0], b[0]);
r[1] = max_ff(a[1], b[1]);
r[2] = max_ff(a[2], b[2]);
}
void collision_get_collider_velocity(float vel_old[3],
float vel_new[3],
CollisionModifierData *collmd,
CollPair *collpair)
{
float u1, u2, u3;
/* compute barycentric coordinates */
collision_compute_barycentric(collpair->pb,
collmd->current_x[collpair->bp1],
collmd->current_x[collpair->bp2],
collmd->current_x[collpair->bp3],
&u1,
&u2,
&u3);
collision_interpolateOnTriangle(vel_new,
collmd->current_v[collpair->bp1],
collmd->current_v[collpair->bp2],
collmd->current_v[collpair->bp3],
u1,
u2,
u3);
/* XXX assume constant velocity of the collider for now */
copy_v3_v3(vel_old, vel_new);
}