Cloth: Collision improvements

This commit includes several performance, stability, and reliability
improvements to cloth collisions.

Most notably:
* The implementation of a new self-collisions system.
* Multithreading of collision detection.
* Implementation of single sided collisions and normal overrides.
* Replacement of the `plNearestPoints` function from Bullet with a
dedicated solution.

Further, this also includes several bug fixes, and algorithmic
improvements.

Reviewed By: brecht

Differential Revision: http://developer.blender.org/D3712
This commit is contained in:
Luca Rood
2018-09-26 17:18:16 +02:00
parent a27d97d1b7
commit 0666ece2e2
16 changed files with 1072 additions and 942 deletions

View File

@@ -224,12 +224,33 @@ class PHYSICS_PT_cloth_shape(PhysicButtonsPanel, Panel):
col.prop_search(cloth, "rest_shape_key", key, "key_blocks", text="Rest Shape Key")
class PHYSICS_PT_cloth_object_collision(PhysicButtonsPanel, Panel):
bl_label = "Object Collision"
class PHYSICS_PT_cloth_collision(PhysicButtonsPanel, Panel):
bl_label = "Collision"
bl_parent_id = 'PHYSICS_PT_cloth'
bl_options = {'DEFAULT_CLOSED'}
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
def draw(self, context):
layout = self.layout
layout.use_property_split = True
cloth = context.cloth.collision_settings
md = context.cloth
ob = context.object
layout.active = (cloth.use_collision or cloth.use_self_collision) and cloth_panel_enabled(md)
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
col.prop(cloth, "collision_quality", text="Quality")
class PHYSICS_PT_cloth_object_collision(PhysicButtonsPanel, Panel):
bl_label = "Object Collision"
bl_parent_id = 'PHYSICS_PT_cloth_collision'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
def draw_header(self, context):
cloth = context.cloth.collision_settings
@@ -248,20 +269,18 @@ class PHYSICS_PT_cloth_object_collision(PhysicButtonsPanel, Panel):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
col.prop(cloth, "collision_quality", text="Quality")
col.prop(cloth, "distance_min", slider=True, text="Distance")
col.prop(cloth, "repel_force", slider=True, text="Repel")
col = flow.column()
col.prop(cloth, "distance_repel", slider=True, text="Repel Distance")
col.prop(cloth, "friction")
col.prop(cloth, "impulse_clamp")
col = flow.column()
col.prop(cloth, "group")
class PHYSICS_PT_cloth_self_collision(PhysicButtonsPanel, Panel):
bl_label = "Self Collision"
bl_parent_id = 'PHYSICS_PT_cloth'
bl_options = {'DEFAULT_CLOSED'}
bl_parent_id = 'PHYSICS_PT_cloth_collision'
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
def draw_header(self, context):
@@ -283,9 +302,14 @@ class PHYSICS_PT_cloth_self_collision(PhysicButtonsPanel, Panel):
flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True)
col = flow.column()
col.prop(cloth, "self_collision_quality", text="Quality")
col.prop(cloth, "self_friction", text="Friction")
col = flow.column()
col.prop(cloth, "self_distance_min", slider=True, text="Distance")
col = flow.column()
col.prop(cloth, "self_impulse_clamp")
col = flow.column()
col.prop_search(cloth, "vertex_group_self_collisions", ob, "vertex_groups", text="Vertex Group")
@@ -363,6 +387,7 @@ classes = (
PHYSICS_PT_cloth_damping,
PHYSICS_PT_cloth_cache,
PHYSICS_PT_cloth_shape,
PHYSICS_PT_cloth_collision,
PHYSICS_PT_cloth_object_collision,
PHYSICS_PT_cloth_self_collision,
PHYSICS_PT_cloth_property_weights,

View File

@@ -377,7 +377,7 @@ class PHYSICS_PT_collision_particle(PhysicButtonsPanel, Panel):
class PHYSICS_PT_collision_softbody(PhysicButtonsPanel, Panel):
bl_label = "Softbody"
bl_label = "Softbody And Cloth"
bl_parent_id = "PHYSICS_PT_collision"
COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_OPENGL'}
@@ -414,6 +414,15 @@ class PHYSICS_PT_collision_softbody(PhysicButtonsPanel, Panel):
col = flow.column()
col.prop(settings, "thickness_inner", text="Inner", slider=True)
col = flow.column()
col.prop(settings, "cloth_friction")
col = flow.column()
col.prop(settings, "use_culling")
col = flow.column()
col.prop(settings, "use_normal")
classes = (
PHYSICS_PT_field,

View File

@@ -117,6 +117,7 @@ typedef struct ClothVertex {
float goal; /* goal, from SB */
float impulse[3]; /* used in collision.c */
float xrest[3]; /* rest position of the vertex */
float dcvel[3]; /* delta velocities to be applied by collision response */
unsigned int impulse_count; /* same as above */
float avg_spring_len; /* average length of connected springs */
float struct_stiff;
@@ -222,8 +223,7 @@ typedef struct ColliderContacts {
} ColliderContacts;
// needed for implicit.c
int cloth_bvh_objcollision (struct Depsgraph *depsgraph, struct Object *ob, struct ClothModifierData *clmd, float step, float dt );
int cloth_points_objcollision(struct Depsgraph *depsgraph, struct Object *ob, struct ClothModifierData *clmd, float step, float dt);
int cloth_bvh_collision(struct Depsgraph *depsgraph, struct Object *ob, struct ClothModifierData *clmd, 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);
@@ -244,8 +244,7 @@ void clothModifier_do(struct ClothModifierData *clmd, struct Depsgraph *depsgrap
int cloth_uses_vgroup(struct ClothModifierData *clmd);
// needed for collision.c
void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
void bvhselftree_update_from_cloth(struct ClothModifierData *clmd, bool moving);
void bvhtree_update_from_cloth(struct ClothModifierData *clmd, bool moving, bool self);
// needed for button_object.c
void cloth_clear_cache(

View File

@@ -63,6 +63,7 @@ typedef enum {
COLLISION_USE_COLLFACE = (1 << 2),
COLLISION_IS_EDGES = (1 << 3),
#endif
COLLISION_INACTIVE = (1 << 4),
} COLLISION_FLAGS;
@@ -73,7 +74,7 @@ typedef enum {
typedef struct CollPair {
unsigned int face1; // cloth face
unsigned int face2; // object face
double distance; // magnitude of vector
float distance;
float normal[3];
float vector[3]; // unnormalized collision vector: p2-p1
float pa[3], pb[3]; // collision point p1 on face1, p2 on face2

View File

@@ -124,8 +124,7 @@ void cloth_init(ClothModifierData *clmd )
clmd->coll_parms->epsilon = 0.015f;
clmd->coll_parms->flags = CLOTH_COLLSETTINGS_FLAG_ENABLED;
clmd->coll_parms->collision_list = NULL;
clmd->coll_parms->self_loop_count = 1.0;
clmd->coll_parms->selfepsilon = 0.75;
clmd->coll_parms->selfepsilon = 0.015;
clmd->coll_parms->vgroup_selfcol = 0;
/* These defaults are copied from softbody.c's
@@ -153,44 +152,6 @@ void cloth_init(ClothModifierData *clmd )
clmd->point_cache->step = 1;
}
static BVHTree *bvhselftree_build_from_cloth (ClothModifierData *clmd, float epsilon)
{
unsigned int i;
BVHTree *bvhtree;
Cloth *cloth;
ClothVertex *verts;
if (!clmd)
return NULL;
cloth = clmd->clothObject;
if (!cloth)
return NULL;
verts = cloth->verts;
/* in the moment, return zero if no faces there */
if (!cloth->mvert_num)
return NULL;
/* create quadtree with k=26 */
bvhtree = BLI_bvhtree_new(cloth->mvert_num, epsilon, 4, 6);
/* fill tree */
for (i = 0; i < cloth->mvert_num; i++, verts++) {
const float *co;
co = verts->xold;
BLI_bvhtree_insert(bvhtree, i, co, 1);
}
/* balance tree */
BLI_bvhtree_balance(bvhtree);
return bvhtree;
}
static BVHTree *bvhtree_build_from_cloth (ClothModifierData *clmd, float epsilon)
{
unsigned int i;
@@ -234,14 +195,21 @@ static BVHTree *bvhtree_build_from_cloth (ClothModifierData *clmd, float epsilon
return bvhtree;
}
void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving)
void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving, bool self)
{
unsigned int i = 0;
Cloth *cloth = clmd->clothObject;
BVHTree *bvhtree = cloth->bvhtree;
BVHTree *bvhtree;
ClothVertex *verts = cloth->verts;
const MVertTri *vt;
if (self) {
bvhtree = cloth->bvhselftree;
}
else {
bvhtree = cloth->bvhtree;
}
if (!bvhtree)
return;
@@ -253,12 +221,12 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving)
float co[3][3], co_moving[3][3];
bool ret;
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);
@@ -267,6 +235,10 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving)
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);
}
@@ -280,47 +252,6 @@ void bvhtree_update_from_cloth(ClothModifierData *clmd, bool moving)
}
}
void bvhselftree_update_from_cloth(ClothModifierData *clmd, bool moving)
{
unsigned int i = 0;
Cloth *cloth = clmd->clothObject;
BVHTree *bvhtree = cloth->bvhselftree;
ClothVertex *verts = cloth->verts;
const MVertTri *vt;
if (!bvhtree)
return;
vt = cloth->tri;
/* update vertex position in bvh tree */
if (verts && vt) {
for (i = 0; i < cloth->mvert_num; i++, verts++) {
const float *co, *co_moving;
bool ret;
co = verts->txold;
/* copy new locations into array */
if (moving) {
/* update moving positions */
co_moving = verts->tx;
ret = BLI_bvhtree_update_node(bvhtree, i, co, co_moving, 1);
}
else {
ret = BLI_bvhtree_update_node(bvhtree, i, co, NULL, 1);
}
/* check if tree is already full */
if (ret == false) {
break;
}
}
BLI_bvhtree_update_tree(bvhtree);
}
}
void cloth_clear_cache(Object *ob, ClothModifierData *clmd, float framenr)
{
PTCacheID pid;
@@ -357,6 +288,7 @@ static int do_init_cloth(Object *ob, ClothModifierData *clmd, Mesh *result, int
BKE_cloth_solver_set_positions(clmd);
clmd->clothObject->last_frame= MINFRAME-1;
clmd->sim_parms->dt = 1.0f / clmd->sim_parms->stepsPerFrame;
}
return 1;
@@ -444,9 +376,6 @@ void clothModifier_do(ClothModifierData *clmd, Depsgraph *depsgraph, Scene *scen
cache->flag &= ~PTCACHE_REDO_NEEDED;
}
// unused in the moment, calculated separately in implicit.c
clmd->sim_parms->dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
/* simulation is only active during a specific period */
if (framenr < startframe) {
BKE_ptcache_invalidate(cache);
@@ -795,8 +724,6 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, Mesh *mesh, fl
ClothVertex *verts = NULL;
float (*shapekey_rest)[3] = NULL;
float tnull[3] = {0, 0, 0};
Cloth *cloth = NULL;
float maxdist = 0;
// If we have a clothObject, free it.
if ( clmd->clothObject != NULL ) {
@@ -809,8 +736,6 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, Mesh *mesh, fl
clmd->clothObject = MEM_callocN ( sizeof ( Cloth ), "cloth" );
if ( clmd->clothObject ) {
clmd->clothObject->old_solver_type = 255;
// clmd->clothObject->old_collision_type = 255;
cloth = clmd->clothObject;
clmd->clothObject->edgeset = NULL;
}
else if (!clmd->clothObject) {
@@ -889,13 +814,8 @@ static int cloth_from_object(Object *ob, ClothModifierData *clmd, Mesh *mesh, fl
if (!first)
BKE_cloth_solver_set_positions(clmd);
clmd->clothObject->bvhtree = bvhtree_build_from_cloth ( clmd, MAX2(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel) );
for (i = 0; i < mesh->totvert; i++) {
maxdist = MAX2(maxdist, clmd->coll_parms->selfepsilon* ( cloth->verts[i].avg_spring_len*2.0f));
}
clmd->clothObject->bvhselftree = bvhselftree_build_from_cloth ( clmd, maxdist );
clmd->clothObject->bvhtree = bvhtree_build_from_cloth (clmd, clmd->coll_parms->epsilon);
clmd->clothObject->bvhselftree = bvhtree_build_from_cloth(clmd, clmd->coll_parms->selfepsilon);
return 1;
}

View File

@@ -43,7 +43,8 @@
#include "BLI_utildefines.h"
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_edgehash.h"
#include "BLI_task.h"
#include "BLI_threads.h"
#include "BKE_cloth.h"
#include "BKE_collection.h"
@@ -52,9 +53,6 @@
#include "BKE_modifier.h"
#include "BKE_scene.h"
#ifdef WITH_BULLET
#include "Bullet-C-Api.h"
#endif
#include "BLI_kdopbvh.h"
#include "BKE_collision.h"
@@ -67,6 +65,23 @@
#endif
typedef struct ColDetectData {
ClothModifierData *clmd;
CollisionModifierData *collmd;
BVHTreeOverlap *overlap;
CollPair *collisions;
bool culling;
bool use_normal;
bool collided;
} ColDetectData;
typedef struct SelfColDetectData {
ClothModifierData *clmd;
BVHTreeOverlap *overlap;
CollPair *collisions;
bool collided;
} SelfColDetectData;
/***********************************
Collision modifier code start
***********************************/
@@ -74,7 +89,7 @@ Collision modifier code start
/* step is limited from 0 (frame start position) to 1 (frame end position) */
void collision_move_object(CollisionModifierData *collmd, float step, float prevstep)
{
float tv[3] = {0, 0, 0};
float oldx[3];
unsigned int i = 0;
/* the collider doesn't move this frame */
@@ -87,15 +102,14 @@ void collision_move_object(CollisionModifierData *collmd, float step, float prev
}
for (i = 0; i < collmd->mvert_num; i++) {
sub_v3_v3v3(tv, collmd->xnew[i].co, collmd->x[i].co);
VECADDS(collmd->current_x[i].co, collmd->x[i].co, tv, prevstep);
VECADDS(collmd->current_xnew[i].co, collmd->x[i].co, tv, step);
sub_v3_v3v3(collmd->current_v[i].co, collmd->current_xnew[i].co, collmd->current_x[i].co);
interp_v3_v3v3(oldx, collmd->x[i].co, collmd->xnew[i].co, prevstep);
interp_v3_v3v3(collmd->current_x[i].co, collmd->x[i].co, collmd->xnew[i].co, step);
sub_v3_v3v3(collmd->current_v[i].co, collmd->current_x[i].co, oldx);
}
bvhtree_update_from_mvert(
collmd->bvhtree, collmd->current_x, collmd->current_xnew,
collmd->tri, collmd->tri_num, true);
collmd->bvhtree, collmd->current_x, NULL,
collmd->tri, collmd->tri_num, false);
}
BVHTree *bvhtree_build_from_mvert(
@@ -178,6 +192,264 @@ void bvhtree_update_from_mvert(
Collision modifier code end
***********************************/
static void clamp_point_seg(float a[3], float b[3], float p[3])
{
float ap[3], bp[3], ab[3];
sub_v3_v3v3(ap, p, a);
sub_v3_v3v3(bp, p, b);
sub_v3_v3v3(ab, b, a);
if (dot_v3v3(ap, bp) > 0.0f) {
if (dot_v3v3(ap, ab) > 0.0f) {
copy_v3_v3(p, b);
}
else {
copy_v3_v3(p, a);
}
}
}
static bool isect_seg_seg(float a1[3], float a2[3], float b1[3], float b2[3], float r_a[3], float r_b[3])
{
if (isect_line_line_epsilon_v3(a1, a2, b1, b2, r_a, r_b, 0.0f)) {
clamp_point_seg(a1, a2, r_a);
clamp_point_seg(b1, b2, r_b);
return true;
}
return false;
}
BLI_INLINE int next_ind(int i)
{
return (++i < 3) ? i : 0;
}
static float compute_collision_point(float a1[3], float a2[3], float a3[3], float b1[3], float b2[3], 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];
int isect_count = 0;
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. */
for (int i = 0; i < 3; i++) {
if (isect_line_segment_tri_v3(a[i], a[next_ind(i)], b[0], b[1], b[2], &tmp, NULL)) {
interp_v3_v3v3(isect_a, a[i], a[next_ind(i)], tmp);
isect_count++;
}
}
if (isect_count == 0) {
for (int i = 0; i < 3; i++) {
if (isect_line_segment_tri_v3(b[i], b[next_ind(i)], a[0], a[1], a[2], &tmp, NULL)) {
isect_count++;
}
}
}
else if (isect_count == 1) {
for (int i = 0; i < 3; i++) {
if (isect_line_segment_tri_v3(b[i], b[next_ind(i)], a[0], a[1], a[2], &tmp, NULL)) {
interp_v3_v3v3(isect_b, b[i], b[next_ind(i)], tmp);
break;
}
}
}
/* 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 (isect_count == 2) {
backside = true;
}
else if (isect_count == 0) {
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 (use_normal) {
normal_tri_v3(normal, b[0], b[1], b[2]);
}
if (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, NULL)) {
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, NULL)) {
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 (isect_count == 0) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (isect_seg_seg(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 (isect_count == 0) {
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;
}
// w3 is not perfect
static void collision_compute_barycentric ( float pv[3], float p1[3], float p2[3], float p3[3], float *w1, float *w2, float *w3 )
{
@@ -232,141 +504,131 @@ DO_INLINE void collision_interpolateOnTriangle ( float to[3], float v1[3], float
VECADDMUL(to, v3, w3);
}
static int cloth_collision_response_static ( ClothModifierData *clmd, CollisionModifierData *collmd, CollPair *collpair, CollPair *collision_end )
static int cloth_collision_response_static(ClothModifierData *clmd, CollisionModifierData *collmd, Object *collob,
CollPair *collpair, uint collision_count, const float dt)
{
int result = 0;
Cloth *cloth1;
float w1, w2, w3, u1, u2, u3;
float v1[3], v2[3], relativeVelocity[3];
float magrelVel;
float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree );
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
cloth1 = clmd->clothObject;
for ( ; collpair != collision_end; collpair++ ) {
for (int i = 0; i < collision_count; i++, collpair++) {
float i1[3], i2[3], i3[3];
zero_v3(i1);
zero_v3(i2);
zero_v3(i3);
/* only handle static collisions here */
if ( collpair->flag & COLLISION_IN_FUTURE )
/* Only handle static collisions here. */
if (collpair->flag & (COLLISION_IN_FUTURE | COLLISION_INACTIVE)) {
continue;
}
/* compute barycentric coordinates for both collision points */
collision_compute_barycentric ( collpair->pa,
cloth1->verts[collpair->ap1].txold,
cloth1->verts[collpair->ap2].txold,
cloth1->verts[collpair->ap3].txold,
&w1, &w2, &w3 );
/* 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);
/* was: txold */
collision_compute_barycentric ( collpair->pb,
collmd->current_x[collpair->bp1].co,
collmd->current_x[collpair->bp2].co,
collmd->current_x[collpair->bp3].co,
&u1, &u2, &u3 );
collision_compute_barycentric(collpair->pb,
collmd->current_x[collpair->bp1].co,
collmd->current_x[collpair->bp2].co,
collmd->current_x[collpair->bp3].co,
&u1, &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(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, collmd->current_v[collpair->bp3].co, u1, u2, u3 );
collision_interpolateOnTriangle(v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, 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'). */
magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
/* printf("magrelVel: %f\n", magrelVel); */
/* Calculate masses of points.
* TODO */
/* If v_n_mag < 0 the edges are approaching each other. */
if ( magrelVel > ALMOST_ZERO ) {
/* 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, d = 0;
double impulse = 0.0;
float vrel_t_pre[3];
float temp[3], spf;
float temp[3];
float time_multiplier;
/* calculate tangential velocity */
copy_v3_v3 ( temp, collpair->normal );
/* 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->friction * 0.01f * magrelVel, len_v3(vrel_t_pre));
* 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.0f + w1*w1 + w2*w2 + w3*w3 ); /* 2.0 * */
VECADDMUL ( i1, vrel_t_pre, w1 * impulse );
VECADDMUL ( i2, vrel_t_pre, w2 * impulse );
VECADDMUL ( i3, vrel_t_pre, w3 * impulse );
impulse = magtangent / 1.5;
VECADDMUL(i1, vrel_t_pre, w1 * impulse);
VECADDMUL(i2, vrel_t_pre, w2 * impulse);
VECADDMUL(i3, vrel_t_pre, w3 * impulse);
}
/* Apply velocity stopping impulse
* I_c = m * v_N / 2.0
* no 2.0 * magrelVel normally, but looks nicer DG */
impulse = magrelVel / ( 1.0 + w1*w1 + w2*w2 + w3*w3 );
/* Apply velocity stopping impulse. */
impulse = magrelVel / 1.5f;
VECADDMUL ( i1, collpair->normal, w1 * impulse );
VECADDMUL(i1, collpair->normal, w1 * impulse);
cloth1->verts[collpair->ap1].impulse_count++;
VECADDMUL ( i2, collpair->normal, w2 * impulse );
VECADDMUL(i2, collpair->normal, w2 * impulse);
cloth1->verts[collpair->ap2].impulse_count++;
VECADDMUL ( i3, collpair->normal, w3 * impulse );
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap3].impulse_count++;
/* Apply repulse impulse if distance too short
* I_r = -min(dt*kd, m(0, 1d/dt - v_n))
* DG: this formula ineeds to be changed for this code since we apply impulses/repulses like this:
* v += impulse; x_new = x + v;
* We don't use dt!!
* DG TODO: Fix usage of dt here! */
spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;
time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
d = clmd->coll_parms->epsilon*8.0f/9.0f + epsilon2*8.0f/9.0f - collpair->distance;
if ( ( magrelVel < 0.1f*d*spf ) && ( d > ALMOST_ZERO ) ) {
repulse = MIN2 ( d*1.0f/spf, 0.1f*d*spf - magrelVel );
/* stay on the safe side and clamp repulse */
if ( impulse > ALMOST_ZERO )
repulse = min_ff( repulse, 5.0*impulse );
if ((magrelVel < 0.1f * d * time_multiplier) && (d > ALMOST_ZERO)) {
repulse = MIN2(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.0f + w1*w1 + w2*w2 + w3*w3 ); /* original 2.0 / 0.25 */
VECADDMUL ( i1, collpair->normal, impulse );
VECADDMUL ( i2, collpair->normal, impulse );
VECADDMUL ( i3, collpair->normal, impulse );
impulse = repulse / 1.5f;
VECADDMUL(i1, collpair->normal, impulse);
VECADDMUL(i2, collpair->normal, impulse);
VECADDMUL(i3, collpair->normal, impulse);
}
result = 1;
}
else {
/* Apply repulse impulse if distance too short
* I_r = -min(dt*kd, max(0, 1d/dt - v_n))
* DG: this formula ineeds to be changed for this code since we apply impulses/repulses like this:
* v += impulse; x_new = x + v;
* We don't use dt!! */
float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;
float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
float d;
float d = clmd->coll_parms->epsilon*8.0f/9.0f + epsilon2*8.0f/9.0f - (float)collpair->distance;
if ( d > ALMOST_ZERO) {
/* stay on the safe side and clamp repulse */
float repulse = d*1.0f/spf;
d = clmd->coll_parms->epsilon*8.0f/9.0f + epsilon2*8.0f/9.0f - collpair->distance;
float impulse = repulse / ( 3.0f * ( 1.0f + w1*w1 + w2*w2 + w3*w3 )); /* original 2.0 / 0.25 */
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, impulse );
VECADDMUL ( i2, collpair->normal, impulse );
VECADDMUL ( i3, collpair->normal, impulse );
VECADDMUL(i1, collpair->normal, w1 * impulse);
VECADDMUL(i2, collpair->normal, w2 * impulse);
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap1].impulse_count++;
cloth1->verts[collpair->ap2].impulse_count++;
@@ -377,20 +639,190 @@ static int cloth_collision_response_static ( ClothModifierData *clmd, CollisionM
}
if (result) {
int i = 0;
float clamp = clmd->coll_parms->clamp * dt;
for (i = 0; i < 3; i++) {
if (cloth1->verts[collpair->ap1].impulse_count > 0 && ABS(cloth1->verts[collpair->ap1].impulse[i]) < ABS(i1[i]))
cloth1->verts[collpair->ap1].impulse[i] = i1[i];
if ((clamp > 0.0f) &&
((len_v3(i1) > clamp) ||
(len_v3(i2) > clamp) ||
(len_v3(i3) > clamp)))
{
return 0;
}
if (cloth1->verts[collpair->ap2].impulse_count > 0 && ABS(cloth1->verts[collpair->ap2].impulse[i]) < ABS(i2[i]))
cloth1->verts[collpair->ap2].impulse[i] = i2[i];
for (int j = 0; j < 3; j++) {
if (cloth1->verts[collpair->ap1].impulse_count > 0 && ABS(cloth1->verts[collpair->ap1].impulse[j]) < ABS(i1[j]))
cloth1->verts[collpair->ap1].impulse[j] = i1[j];
if (cloth1->verts[collpair->ap3].impulse_count > 0 && ABS(cloth1->verts[collpair->ap3].impulse[i]) < ABS(i3[i]))
cloth1->verts[collpair->ap3].impulse[i] = i3[i];
if (cloth1->verts[collpair->ap2].impulse_count > 0 && ABS(cloth1->verts[collpair->ap2].impulse[j]) < ABS(i2[j]))
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];
}
}
}
return result;
}
static int cloth_selfcollision_response_static(ClothModifierData *clmd, CollPair *collpair,
uint collision_count, const float dt)
{
int result = 0;
Cloth *cloth1;
float w1, w2, w3, u1, u2, u3;
float v1[3], v2[3], relativeVelocity[3];
float magrelVel;
cloth1 = clmd->clothObject;
for (int i = 0; i < collision_count; i++, collpair++) {
float i1[3], i2[3], i3[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 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);
collision_compute_barycentric(collpair->pb,
cloth1->verts[collpair->bp1].tx,
cloth1->verts[collpair->bp2].tx,
cloth1->verts[collpair->bp3].tx,
&u1, &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, cloth1->verts[collpair->bp1].tv, cloth1->verts[collpair->bp2].tv, cloth1->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'). */
magrelVel = dot_v3v3(relativeVelocity, collpair->normal);
/* 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, d = 0;
double impulse = 0.0;
float vrel_t_pre[3];
float temp[3], time_multiplier;
/* 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(i1, vrel_t_pre, w1 * impulse);
VECADDMUL(i2, vrel_t_pre, w2 * impulse);
VECADDMUL(i3, vrel_t_pre, w3 * impulse);
}
/* Apply velocity stopping impulse. */
impulse = magrelVel / 3.0f;
VECADDMUL(i1, collpair->normal, w1 * impulse);
cloth1->verts[collpair->ap1].impulse_count++;
VECADDMUL(i2, collpair->normal, w2 * impulse);
cloth1->verts[collpair->ap2].impulse_count++;
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap3].impulse_count++;
time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance;
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(i1, collpair->normal, w1 * impulse);
VECADDMUL(i2, collpair->normal, w2 * impulse);
VECADDMUL(i3, collpair->normal, w3 * impulse);
}
result = 1;
}
else {
float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
float d;
d = clmd->coll_parms->selfepsilon * 8.0f / 9.0f * 2.0f - collpair->distance;
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(i1, collpair->normal, w1 * impulse);
VECADDMUL(i2, collpair->normal, w2 * impulse);
VECADDMUL(i3, collpair->normal, w3 * impulse);
cloth1->verts[collpair->ap1].impulse_count++;
cloth1->verts[collpair->ap2].impulse_count++;
cloth1->verts[collpair->ap3].impulse_count++;
result = 1;
}
}
if (result) {
float clamp = clmd->coll_parms->self_clamp * dt;
if ((clamp > 0.0f) &&
((len_v3(i1) > clamp) ||
(len_v3(i2) > clamp) ||
(len_v3(i3) > clamp)))
{
return 0;
}
for (int j = 0; j < 3; j++) {
if (cloth1->verts[collpair->ap1].impulse_count > 0 && ABS(cloth1->verts[collpair->ap1].impulse[j]) < ABS(i1[j]))
cloth1->verts[collpair->ap1].impulse[j] = i1[j];
if (cloth1->verts[collpair->ap2].impulse_count > 0 && ABS(cloth1->verts[collpair->ap2].impulse[j]) < ABS(i2[j]))
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];
}
}
}
return result;
}
@@ -398,89 +830,118 @@ static int cloth_collision_response_static ( ClothModifierData *clmd, CollisionM
# pragma GCC diagnostic pop
#endif
//Determines collisions on overlap, collisions are written to collpair[i] and collision+number_collision_found is returned
static CollPair* cloth_collision(ModifierData *md1, ModifierData *md2,
BVHTreeOverlap *overlap, CollPair *collpair, float UNUSED(dt))
static void cloth_collision(
void *__restrict userdata,
const int index,
const ParallelRangeTLS *__restrict UNUSED(tls))
{
ClothModifierData *clmd = (ClothModifierData *)md1;
CollisionModifierData *collmd = (CollisionModifierData *) md2;
/* Cloth *cloth = clmd->clothObject; */ /* UNUSED */
ColDetectData *data = (ColDetectData *)userdata;
ClothModifierData *clmd = data->clmd;
CollisionModifierData *collmd = data->collmd;
CollPair *collpair = data->collisions;
const MVertTri *tri_a, *tri_b;
#ifdef WITH_BULLET
ClothVertex *verts1 = clmd->clothObject->verts;
#endif
double distance = 0;
float distance = 0.0f;
float epsilon1 = clmd->coll_parms->epsilon;
float epsilon2 = BLI_bvhtree_get_epsilon ( collmd->bvhtree );
float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree);
float pa[3], pb[3], vect[3];
tri_a = &clmd->clothObject->tri[overlap->indexA];
tri_b = &collmd->tri[overlap->indexB];
tri_a = &clmd->clothObject->tri[data->overlap[index].indexA];
tri_b = &collmd->tri[data->overlap[index].indexB];
/* fill face_a */
collpair->ap1 = tri_a->tri[0];
collpair->ap2 = tri_a->tri[1];
collpair->ap3 = tri_a->tri[2];
/* 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_x[tri_b->tri[0]].co, collmd->current_x[tri_b->tri[1]].co, collmd->current_x[tri_b->tri[2]].co,
data->culling, data->use_normal, pa, pb, vect);
/* fill face_b */
collpair->bp1 = tri_b->tri[0];
collpair->bp2 = tri_b->tri[1];
collpair->bp3 = tri_b->tri[2];
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];
#ifdef WITH_BULLET
// calc distance + normal
distance = plNearestPoints (
verts1[collpair->ap1].txold, verts1[collpair->ap2].txold, verts1[collpair->ap3].txold, collmd->current_x[collpair->bp1].co, collmd->current_x[collpair->bp2].co, collmd->current_x[collpair->bp3].co, collpair->pa, collpair->pb, collpair->vector );
#else
// just be sure that we don't add anything
distance = 2.0 * (double)( epsilon1 + epsilon2 + ALMOST_ZERO );
#endif
copy_v3_v3(collpair[index].pa, pa);
copy_v3_v3(collpair[index].pb, pb);
copy_v3_v3(collpair[index].vector, vect);
// distance -1 means no collision result
if (distance != -1.0 && (distance <= (double)(epsilon1 + epsilon2 + ALMOST_ZERO))) {
normalize_v3_v3(collpair->normal, collpair->vector);
normalize_v3_v3(collpair[index].normal, collpair[index].vector);
collpair->distance = distance;
collpair->flag = 0;
collpair++;
}/*
else {
float w1, w2, w3, u1, u2, u3;
float v1[3], v2[3], relativeVelocity[3];
collpair[index].distance = distance;
collpair[index].flag = 0;
// calc relative velocity
// compute barycentric coordinates for both collision points
collision_compute_barycentric ( collpair->pa,
verts1[collpair->ap1].txold,
verts1[collpair->ap2].txold,
verts1[collpair->ap3].txold,
&w1, &w2, &w3 );
// was: txold
collision_compute_barycentric ( collpair->pb,
collmd->current_x[collpair->bp1].co,
collmd->current_x[collpair->bp2].co,
collmd->current_x[collpair->bp3].co,
&u1, &u2, &u3 );
// Calculate relative "velocity".
collision_interpolateOnTriangle ( v1, verts1[collpair->ap1].tv, verts1[collpair->ap2].tv, verts1[collpair->ap3].tv, w1, w2, w3 );
collision_interpolateOnTriangle ( v2, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 );
sub_v3_v3v3(relativeVelocity, v2, v1);
if (sqrt(dot_v3v3(relativeVelocity, relativeVelocity)) >= distance)
{
// check for collision in the future
collpair->flag |= COLLISION_IN_FUTURE;
collpair++;
}
}*/
data->collided = true;
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
}
static void cloth_selfcollision(
void *__restrict userdata,
const int index,
const ParallelRangeTLS *__restrict UNUSED(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];
tri_a = &clmd->clothObject->tri[data->overlap[index].indexA];
tri_b = &clmd->clothObject->tri[data->overlap[index].indexB];
for (uint i = 0; i < 3; i++) {
for (uint j = 0; j < 3; j++) {
if (tri_a->tri[i] == tri_b->tri[j]) {
collpair[index].flag = COLLISION_INACTIVE;
return;
}
}
}
if (((verts1[tri_a->tri[0]].flags & verts1[tri_a->tri[1]].flags & verts1[tri_a->tri[2]].flags) |
(verts1[tri_b->tri[0]].flags & verts1[tri_b->tri[1]].flags & verts1[tri_b->tri[2]].flags)) & CLOTH_VERT_FLAG_NOSELFCOLL)
{
collpair[index].flag = COLLISION_INACTIVE;
return;
}
/* 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);
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;
}
else {
collpair[index].flag = COLLISION_INACTIVE;
}
return collpair;
}
static void add_collision_object(ListBase *relations, Object *ob, int level, unsigned int modifier_type)
@@ -628,25 +1089,49 @@ void BKE_collider_cache_free(ListBase **colliders)
}
}
static void cloth_bvh_objcollisions_nearcheck ( ClothModifierData * clmd, CollisionModifierData *collmd,
CollPair **collisions, CollPair **collisions_index, int numresult, BVHTreeOverlap *overlap, double dt)
static bool cloth_bvh_objcollisions_nearcheck(ClothModifierData * clmd, CollisionModifierData *collmd,
CollPair **collisions, int numresult,
BVHTreeOverlap *overlap, bool culling, bool use_normal)
{
int i;
*collisions = (CollPair *)MEM_mallocN(sizeof(CollPair) * numresult, "collision array");
*collisions = (CollPair *) MEM_mallocN(sizeof(CollPair) * numresult * 4, "collision array" ); // * 4 since cloth_collision_static can return more than 1 collision
*collisions_index = *collisions;
ColDetectData data = {.clmd = clmd,
.collmd = collmd,
.overlap = overlap,
.collisions = *collisions,
.culling = culling,
.use_normal = use_normal,
.collided = false};
for ( i = 0; i < numresult; i++ ) {
*collisions_index = cloth_collision((ModifierData *)clmd, (ModifierData *)collmd,
overlap+i, *collisions_index, dt);
}
ParallelRangeSettings settings;
BLI_parallel_range_settings_defaults(&settings);
settings.use_threading = true;
BLI_task_parallel_range(0, numresult, &data, cloth_collision, &settings);
return data.collided;
}
static int cloth_bvh_objcollisions_resolve ( ClothModifierData * clmd, CollisionModifierData *collmd, CollPair *collisions, CollPair *collisions_index)
static bool cloth_bvh_selfcollisions_nearcheck(ClothModifierData * clmd, CollPair *collisions,
int numresult, BVHTreeOverlap *overlap)
{
SelfColDetectData data = {.clmd = clmd,
.overlap = overlap,
.collisions = collisions,
.collided = false};
ParallelRangeSettings 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, /*numfaces = 0, */ mvert_num = 0;
int i = 0, j = 0, mvert_num = 0;
ClothVertex *verts = NULL;
int ret = 0;
int result = 0;
@@ -654,26 +1139,68 @@ static int cloth_bvh_objcollisions_resolve ( ClothModifierData * clmd, Collision
mvert_num = clmd->clothObject->mvert_num;
verts = cloth->verts;
// process all collisions (calculate impulses, TODO: also repulses if distance too short)
result = 1;
for ( j = 0; j < 2; j++ ) { /* 5 is just a value that ensures convergence */
for (j = 0; j < 2; j++) {
result = 0;
if ( collmd->bvhtree ) {
result += cloth_collision_response_static ( clmd, collmd, collisions, collisions_index );
for (i = 0; i < numcollobj; i++) {
Object *collob= collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision);
// 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) {
// VECADDMUL ( verts[i].tv, verts[i].impulse, 1.0f / verts[i].impulse_count );
VECADD ( verts[i].tv, verts[i].tv, verts[i].impulse);
zero_v3(verts[i].impulse);
verts[i].impulse_count = 0;
if ( collmd->bvhtree ) {
result += cloth_collision_response_static(clmd, collmd, collob, collisions[i], collision_counts[i], dt);
}
}
ret++;
}
/* 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) {
VECADD ( verts[i].tv, verts[i].tv, verts[i].impulse);
VECADD ( verts[i].dcvel, 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 = NULL;
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 );
VECADD ( verts[i].tv, verts[i].tv, verts[i].impulse);
VECADD ( verts[i].dcvel, verts[i].dcvel, verts[i].impulse);
zero_v3(verts[i].impulse);
verts[i].impulse_count = 0;
ret++;
}
}
}
@@ -685,91 +1212,86 @@ static int cloth_bvh_objcollisions_resolve ( ClothModifierData * clmd, Collision
return ret;
}
// cloth - object collisions
int cloth_bvh_objcollision(Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt )
int cloth_bvh_collision(Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt)
{
Cloth *cloth= clmd->clothObject;
BVHTree *cloth_bvh= cloth->bvhtree;
unsigned int i=0, /* numfaces = 0, */ /* UNUSED */ mvert_num = 0, k, l, j;
int rounds = 0; // result counts applied collisions; ic is for debug output;
Cloth *cloth = clmd->clothObject;
BVHTree *cloth_bvh = cloth->bvhtree;
uint i = 0, mvert_num = 0;
int rounds = 0;
ClothVertex *verts = NULL;
int ret = 0, ret2 = 0;
Object **collobjs = NULL;
unsigned int numcollobj = 0;
uint *coll_counts_obj = NULL;
BVHTreeOverlap **overlap_obj = NULL;
uint coll_count_self = 0;
BVHTreeOverlap *overlap_self = NULL;
if ((clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_COLLOBJ) || cloth_bvh==NULL)
return 0;
verts = cloth->verts;
/* numfaces = cloth->numfaces; */ /* UNUSED */
mvert_num = cloth->mvert_num;
////////////////////////////////////////////////////////////
// static collisions
////////////////////////////////////////////////////////////
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
bvhtree_update_from_cloth(clmd, true);
bvhtree_update_from_cloth(clmd, false, false);
collobjs = BKE_collision_objects_create(depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision);
if (!collobjs) {
return 0;
}
if (collobjs) {
coll_counts_obj = MEM_callocN(sizeof(uint) * numcollobj, "CollCounts");
overlap_obj = MEM_callocN(sizeof(*overlap_obj) * numcollobj, "BVHOverlap");
/* 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);
}
}
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
bvhselftree_update_from_cloth(clmd, false);
}
do {
ret2 = 0;
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
CollPair **collisions, **collisions_index;
collisions = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair");
collisions_index = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair");
/* Check all collision objects. */
for (i = 0; i < numcollobj; i++) {
Object *collob= collobjs[i];
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision);
BVHTreeOverlap *overlap = NULL;
unsigned int result = 0;
if (!collmd->bvhtree) {
continue;
}
/* Search for overlapping collision pairs. */
overlap = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &result, NULL, NULL);
/* Move object to position (step) in time. */
collision_move_object(collmd, step + dt, step);
/* Go to next object if no overlap is there. */
if (result && overlap) {
/* Check if collisions really happen (costly near check). */
cloth_bvh_objcollisions_nearcheck(clmd, collmd, &collisions[i], &collisions_index[i],
result, overlap, dt/(float)clmd->coll_parms->loop_count);
overlap_obj[i] = BLI_bvhtree_overlap(cloth_bvh, collmd->bvhtree, &coll_counts_obj[i], NULL, NULL);
}
}
}
/* Resolve nearby collisions. */
ret += cloth_bvh_objcollisions_resolve(clmd, collmd, collisions[i], collisions_index[i]);
ret2 += ret;
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
bvhtree_update_from_cloth(clmd, false, true);
overlap_self = BLI_bvhtree_overlap(cloth->bvhselftree, cloth->bvhselftree, &coll_count_self, NULL, NULL);
}
do {
ret2 = 0;
/* Object collisions. */
if ((clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) && collobjs) {
CollPair **collisions;
bool collided = false;
collisions = MEM_callocN(sizeof(CollPair *) * numcollobj, "CollPair");
for (i = 0; i < numcollobj; i++) {
Object *collob = collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision);
if (!collmd->bvhtree) {
continue;
}
MEM_SAFE_FREE(overlap);
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++) {
@@ -777,14 +1299,32 @@ int cloth_bvh_objcollision(Depsgraph *depsgraph, Object *ob, ClothModifierData *
}
MEM_freeN(collisions);
MEM_freeN(collisions_index);
}
////////////////////////////////////////////////////////////
// update positions
// this is needed for bvh_calc_DOP_hull_moving() [kdop.c]
////////////////////////////////////////////////////////////
/* Self collisions. */
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF) {
CollPair *collisions = NULL;
/* Verts come from clmd. */
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) {
@@ -794,117 +1334,27 @@ int cloth_bvh_objcollision(Depsgraph *depsgraph, Object *ob, ClothModifierData *
VECADD(verts[i].tx, verts[i].txold, verts[i].tv);
}
////////////////////////////////////////////////////////////
}
rounds++;
////////////////////////////////////////////////////////////
// Test on *simple* selfcollisions
////////////////////////////////////////////////////////////
if ( clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_SELF ) {
for (l = 0; l < (unsigned int)clmd->coll_parms->self_loop_count; l++) {
/* TODO: add coll quality rounds again */
BVHTreeOverlap *overlap = NULL;
unsigned int result = 0;
// collisions = 1;
verts = cloth->verts; // needed for openMP
/* numfaces = cloth->numfaces; */ /* UNUSED */
mvert_num = cloth->mvert_num;
verts = cloth->verts;
if ( cloth->bvhselftree ) {
// search for overlapping collision pairs
overlap = BLI_bvhtree_overlap(cloth->bvhselftree, cloth->bvhselftree, &result, NULL, NULL);
/* Could be parallelized (using BLI_task)... */
for ( k = 0; k < result; k++ ) {
float temp[3];
float length = 0;
float mindistance;
i = overlap[k].indexA;
j = overlap[k].indexB;
mindistance = clmd->coll_parms->selfepsilon* ( cloth->verts[i].avg_spring_len + cloth->verts[j].avg_spring_len );
if (clmd->sim_parms->vgroup_mass > 0) {
if ( ( cloth->verts [i].flags & CLOTH_VERT_FLAG_PINNED ) &&
( cloth->verts [j].flags & CLOTH_VERT_FLAG_PINNED ) )
{
continue;
}
}
if ((cloth->verts[i].flags & CLOTH_VERT_FLAG_NOSELFCOLL) ||
(cloth->verts[j].flags & CLOTH_VERT_FLAG_NOSELFCOLL))
{
continue;
}
sub_v3_v3v3(temp, verts[i].tx, verts[j].tx);
if ( ( ABS ( temp[0] ) > mindistance ) || ( ABS ( temp[1] ) > mindistance ) || ( ABS ( temp[2] ) > mindistance ) ) continue;
if (BLI_edgeset_haskey(cloth->edgeset, i, j)) {
continue;
}
length = normalize_v3(temp );
if ( length < mindistance ) {
float correction = mindistance - length;
if ( cloth->verts [i].flags & CLOTH_VERT_FLAG_PINNED ) {
mul_v3_fl(temp, -correction);
VECADD ( verts[j].tx, verts[j].tx, temp );
}
else if ( cloth->verts [j].flags & CLOTH_VERT_FLAG_PINNED ) {
mul_v3_fl(temp, correction);
VECADD ( verts[i].tx, verts[i].tx, temp );
}
else {
mul_v3_fl(temp, correction * -0.5f);
VECADD ( verts[j].tx, verts[j].tx, temp );
sub_v3_v3v3(verts[i].tx, verts[i].tx, temp);
}
ret = 1;
ret2 += ret;
}
else {
// check for approximated time collisions
}
}
if ( overlap )
MEM_freeN ( overlap );
}
}
////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////
// SELFCOLLISIONS: update velocities
////////////////////////////////////////////////////////////
if (ret2) {
for (i = 0; i < cloth->mvert_num; i++) {
if ( ! ( verts [i].flags & CLOTH_VERT_FLAG_PINNED ) ) {
sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
}
}
}
////////////////////////////////////////////////////////////
}
}
while ( ret2 && ( clmd->coll_parms->loop_count>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 1|MIN2 ( ret, 1 );
return MIN2(ret, 1);
}
BLI_INLINE void max_v3_v3v3(float r[3], const float a[3], const float b[3])
@@ -930,106 +1380,6 @@ void collision_get_collider_velocity(float vel_old[3], float vel_new[3], Collisi
copy_v3_v3(vel_old, vel_new);
}
static bool cloth_points_collision_response_static(ClothModifierData *clmd, CollisionModifierData *collmd, PartDeflect *pd,
CollPair *collpair, CollPair *collision_end, float dt)
{
bool result = false;
float restitution = (1.0f - clmd->coll_parms->damping) * (1.0f - pd->pdef_sbdamp);
float inv_dt = 1.0f / dt;
Cloth *cloth1 = clmd->clothObject;
// float w1, w2;
float u1, u2, u3;
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 );
for ( ; collpair != collision_end; collpair++ ) {
float margin_distance = (float)(collpair->distance - (double)epsilon2);
float impulse[3];
float mag_v_rel;
if (margin_distance > 0.0f)
continue;
zero_v3(impulse);
/* only handle static collisions here */
if ( collpair->flag & COLLISION_IN_FUTURE )
continue;
/* compute barycentric coordinates for both collision points */
// w1 = 1.0f - collpair->time;
// w2 = collpair->time;
/* was: txold */
collision_compute_barycentric ( collpair->pb,
collmd->current_x[collpair->bp1].co,
collmd->current_x[collpair->bp2].co,
collmd->current_x[collpair->bp3].co,
&u1, &u2, &u3 );
/* Calculate relative velocity */
copy_v3_v3(v1, cloth1->verts[collpair->ap1].tv);
collision_interpolateOnTriangle ( v2_new, collmd->current_v[collpair->bp1].co, collmd->current_v[collpair->bp2].co, collmd->current_v[collpair->bp3].co, u1, u2, u3 );
/* XXX assume constant velocity of the collider for now */
copy_v3_v3(v2_old, v2_new);
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);
/**** DEBUG ****/
BKE_sim_debug_data_add_dot(collpair->pa, 0.9, 0.2, 0.2, "collision", 833, collpair->face1, collpair->face2);
BKE_sim_debug_data_add_dot(collpair->pb, 0.2, 0.9, 0.2, "collision", 834, collpair->face1, collpair->face2);
BKE_sim_debug_data_add_line(collpair->pa, collpair->pb, 0.8, 0.8, 0.8, "collision", 835, collpair->face1, collpair->face2);
/********/
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);
repulse = -margin_distance * inv_dt + dot_v3v3(v1, collpair->normal);
if (margin_distance < -epsilon2) {
bounce = -v_nor_new + v_nor_old * restitution;
mul_v3_v3fl(impulse, collpair->normal, max_ff(repulse, bounce));
}
else {
bounce = 0.0f;
mul_v3_v3fl(impulse, collpair->normal, repulse);
}
cloth1->verts[collpair->ap1].impulse_count++;
result = true;
}
if (result) {
int i = 0;
for (i = 0; i < 3; i++) {
if (cloth1->verts[collpair->ap1].impulse_count > 0 && fabsf(cloth1->verts[collpair->ap1].impulse[i]) < fabsf(impulse[i]))
cloth1->verts[collpair->ap1].impulse[i] = impulse[i];
}
}
}
return result;
}
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])
{
@@ -1181,160 +1531,6 @@ static void cloth_points_objcollisions_nearcheck(
}
}
static int cloth_points_objcollisions_resolve(
ClothModifierData *clmd, CollisionModifierData *collmd, PartDeflect *pd,
CollPair *collisions, CollPair *collisions_index, float dt)
{
Cloth *cloth = clmd->clothObject;
int i = 0, mvert_num = clmd->clothObject->mvert_num;
ClothVertex *verts = cloth->verts;
int ret = 0;
// process all collisions
if ( collmd->bvhtree ) {
bool result = cloth_points_collision_response_static(clmd, collmd, pd, collisions, collisions_index, 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) {
// VECADDMUL ( verts[i].tv, verts[i].impulse, 1.0f / verts[i].impulse_count );
VECADD ( verts[i].tv, verts[i].tv, verts[i].impulse);
zero_v3(verts[i].impulse);
verts[i].impulse_count = 0;
ret++;
}
}
}
}
return ret;
}
// cloth - object collisions
int cloth_points_objcollision(Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt)
{
Cloth *cloth= clmd->clothObject;
BVHTree *cloth_bvh;
int rounds = 0; // result counts applied collisions; ic is for debug output;
float round_dt = dt / (float)clmd->coll_parms->loop_count;
unsigned int i = 0, mvert_num = 0;
ClothVertex *verts = NULL;
int ret = 0, ret2 = 0;
Object **collobjs = NULL;
unsigned int numcollobj = 0;
verts = cloth->verts;
mvert_num = cloth->mvert_num;
////////////////////////////////////////////////////////////
// static collisions
////////////////////////////////////////////////////////////
// create temporary cloth points bvh
cloth_bvh = BLI_bvhtree_new(mvert_num, max_ff(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6);
/* fill tree */
for (i = 0; i < mvert_num; i++) {
float co[2][3];
copy_v3_v3(co[0], verts[i].x);
copy_v3_v3(co[1], verts[i].tx);
BLI_bvhtree_insert(cloth_bvh, i, co[0], 2);
}
/* balance tree */
BLI_bvhtree_balance(cloth_bvh);
collobjs = BKE_collision_objects_create(depsgraph, ob, clmd->coll_parms->group, &numcollobj, eModifierType_Collision);
if (!collobjs)
return 0;
/* 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 );
}
do {
CollPair **collisions, **collisions_index;
ret2 = 0;
collisions = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair");
collisions_index = MEM_callocN(sizeof(CollPair *) *numcollobj, "CollPair");
// check all collision objects
for (i = 0; i < numcollobj; i++) {
Object *collob= collobjs[i];
CollisionModifierData *collmd = (CollisionModifierData *)modifiers_findByType(collob, eModifierType_Collision);
BVHTreeOverlap *overlap = NULL;
unsigned int result = 0;
float epsilon;
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) {
/* check if collisions really happen (costly near check) */
cloth_points_objcollisions_nearcheck(clmd, collmd, &collisions[i], &collisions_index[i],
result, overlap, epsilon, round_dt);
// resolve nearby collisions
ret += cloth_points_objcollisions_resolve(clmd, collmd, collob->pd, collisions[i], collisions_index[i], round_dt);
ret2 += ret;
}
if (overlap)
MEM_freeN ( overlap );
}
rounds++;
for (i = 0; i < numcollobj; i++) {
if (collisions[i])
MEM_freeN(collisions[i]);
}
MEM_freeN(collisions);
MEM_freeN(collisions_index);
////////////////////////////////////////////////////////////
// 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;
}
}
VECADD ( verts[i].tx, verts[i].txold, verts[i].tv );
}
////////////////////////////////////////////////////////////
}
while ( ret2 && ( clmd->coll_parms->loop_count>rounds ) );
BKE_collision_objects_free(collobjs);
BLI_bvhtree_free(cloth_bvh);
return 1|MIN2 ( ret, 1 );
}
void cloth_find_point_contacts(Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt,
ColliderContacts **r_collider_contacts, int *r_totcolliders)
{
@@ -1364,7 +1560,7 @@ void cloth_find_point_contacts(Depsgraph *depsgraph, Object *ob, ClothModifierDa
}
// create temporary cloth points bvh
cloth_bvh = BLI_bvhtree_new(mvert_num, max_ff(clmd->coll_parms->epsilon, clmd->coll_parms->distance_repel), 4, 6);
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];

View File

@@ -112,6 +112,7 @@ PartDeflect *object_add_collision_fields(int type)
pd->pdef_sbdamp = 0.1f;
pd->pdef_sbift = 0.2f;
pd->pdef_sboft = 0.02f;
pd->pdef_cfrict = 5.0f;
pd->seed = ((uint)(ceil(PIL_check_seconds_timer())) + 1) % 128;
pd->f_strength = 1.0f;
pd->f_damp = 1.0f;
@@ -132,7 +133,7 @@ PartDeflect *object_add_collision_fields(int type)
pd->f_flow = 1.0f;
break;
}
pd->flag = PFIELD_DO_LOCATION | PFIELD_DO_ROTATION;
pd->flag = PFIELD_DO_LOCATION | PFIELD_DO_ROTATION | PFIELD_CLOTH_USE_CULLING;
return pd;
}

View File

@@ -373,6 +373,8 @@ bool clip_segment_v3_plane_n(
const float p1[3], const float p2[3], const float plane_array[][4], const int plane_tot,
float r_p1[3], float r_p2[3]);
bool point_in_slice_seg(float p[3], float l1[3], float l2[3]);
/****************************** Interpolation ********************************/
void interp_weights_tri_v3(float w[3], const float a[3], const float b[3], const float c[3], const float p[3]);
void interp_weights_quad_v3(float w[4], const float a[3], const float b[3], const float c[3], const float d[3], const float p[3]);

View File

@@ -2979,19 +2979,27 @@ static bool point_in_slice(const float p[3], const float v1[3], const float l1[3
return (h >= 0.0f && h <= 1.0f);
}
#if 0
/* adult sister defining the slice planes by the origin and the normal
* NOTE |normal| may not be 1 but defining the thickness of the slice */
static int point_in_slice_as(float p[3], float origin[3], float normal[3])
static bool point_in_slice_as(float p[3], float origin[3], float normal[3])
{
float h, rp[3];
sub_v3_v3v3(rp, p, origin);
h = dot_v3v3(normal, rp) / dot_v3v3(normal, normal);
if (h < 0.0f || h > 1.0f) return 0;
return 1;
if (h < 0.0f || h > 1.0f) return false;
return true;
}
bool point_in_slice_seg(float p[3], float l1[3], float l2[3])
{
float normal[3];
sub_v3_v3v3(normal, l2, l1);
return point_in_slice_as(p, l1, normal);
}
#if 0
/*mama (knowing the squared length of the normal) */
static int point_in_slice_m(float p[3], float origin[3], float normal[3], float lns)
{

View File

@@ -2090,4 +2090,22 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
if (!MAIN_VERSION_ATLEAST(bmain, 280, 24)) {
if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "pdef_cfrict")) {
for (Object *ob = bmain->object.first; ob; ob = ob->id.next) {
if (ob->pd) {
ob->pd->pdef_cfrict = 5.0f;
}
for (ModifierData *md = ob->modifiers.first; md; md = md->next) {
if (md->type == eModifierType_Cloth) {
ClothModifierData *clmd = (ClothModifierData *)md;
clmd->coll_parms->selfepsilon = 0.015f;
}
}
}
}
}
}

View File

@@ -121,14 +121,17 @@ typedef struct ClothCollSettings {
float friction; /* Friction/damping applied on contact with other object.*/
float damping; /* Collision restitution on contact with other object.*/
float selfepsilon; /* for selfcollision */
float repel_force, distance_repel;
float repel_force DNA_DEPRECATED;
float distance_repel DNA_DEPRECATED;
int flags; /* collision flags defined in BKE_cloth.h */
short self_loop_count; /* How many iterations for the selfcollision loop */
short self_loop_count DNA_DEPRECATED; /* How many iterations for the selfcollision loop */
short loop_count; /* How many iterations for the collision loop. */
int pad;
struct Collection *group; /* Only use colliders from this group of objects */
short vgroup_selfcol; /* vgroup to paint which vertices are used for self collisions */
short pad2[3];
float clamp; /* Impulse clamp for object collisions. */
float self_clamp; /* Impulse clamp for self collisions. */
} ClothCollSettings;

View File

@@ -120,6 +120,9 @@ typedef struct PartDeflect {
float drawvec_falloff_max[3], pad2; /* Runtime only */
struct Object *f_source; /* force source object */
float pdef_cfrict; /* Friction of cloth collisions. */
float pad;
} PartDeflect;
typedef struct EffectorWeights {
@@ -337,6 +340,8 @@ typedef struct SoftBody {
#define PFIELD_GUIDE_PATH_WEIGHT (1<<16) /* apply curve weights */
#define PFIELD_SMOKE_DENSITY (1<<17) /* multiply smoke force by density */
#define PFIELD_GRAVITATION (1<<18) /* used for (simple) force */
#define PFIELD_CLOTH_USE_CULLING (1<<19) /* Enable cloth collision side detection based on normal. */
#define PFIELD_CLOTH_USE_NORMAL (1<<20) /* Replace collision direction with collider normal. */
/* pd->falloff */
#define PFIELD_FALL_SPHERE 0

View File

@@ -774,26 +774,11 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Enable Collision", "Enable collisions with other objects");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "repel_force", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "repel_force");
RNA_def_property_range(prop, 0.0f, 20.0f);
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_ui_text(prop, "Repulsion Force", "Repulsion force to apply on cloth when close to colliding");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "distance_repel", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "distance_repel");
RNA_def_property_range(prop, 0.001f, 10.0f);
RNA_def_property_float_default(prop, 0.005f);
RNA_def_property_ui_text(prop, "Repulsion Distance",
"Maximum distance to apply repulsion force, must be greater than minimum distance");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "distance_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "epsilon");
RNA_def_property_range(prop, 0.001f, 1.0f);
RNA_def_property_ui_text(prop, "Minimum Distance",
"Minimum distance between collision objects before collision response takes in");
"Minimum distance between collision objects before collision response takes effect");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "friction", PROP_FLOAT, PROP_NONE);
@@ -816,6 +801,12 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
"How many collision iterations should be done. (higher is better quality but slower)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "impulse_clamp", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "clamp");
RNA_def_property_range(prop, 0.0f, 100.0f);
RNA_def_property_ui_text(prop, "Impulse Clamping", "Clamp collision impulses to avoid instability (0.0 to disable clamping)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
/* self collision */
prop = RNA_def_property(srna, "use_self_collision", PROP_BOOLEAN, PROP_NONE);
@@ -825,22 +816,13 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
prop = RNA_def_property(srna, "self_distance_min", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "selfepsilon");
RNA_def_property_range(prop, 0.5f, 1.0f);
RNA_def_property_ui_text(prop, "Self Minimum Distance", "0.5 means no distance at all, 1.0 is maximum distance");
RNA_def_property_range(prop, 0.001f, 0.1f);
RNA_def_property_ui_text(prop, "Self Minimum Distance", "Minimum distance between cloth faces before collision response takes effect");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "self_friction", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.0f, 80.0f);
RNA_def_property_ui_text(prop, "Self Friction", "Friction/damping with self contact");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "self_collision_quality", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "self_loop_count");
RNA_def_property_range(prop, 1, SHRT_MAX);
RNA_def_property_ui_range(prop, 1, 10, 1, -1);
RNA_def_property_ui_text(prop, "Self Collision Quality",
"How many self collision iterations should be done "
"(higher is better quality but slower)");
RNA_def_property_ui_text(prop, "Self Friction", "Friction with self contact");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "group", PROP_POINTER, PROP_NONE);
@@ -854,6 +836,12 @@ static void rna_def_cloth_collision_settings(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Selfcollision Vertex Group",
"Vertex group to define vertices which are not used during self collisions");
RNA_def_property_update(prop, 0, "rna_cloth_update");
prop = RNA_def_property(srna, "self_impulse_clamp", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "self_clamp");
RNA_def_property_range(prop, 0.0f, 100.0f);
RNA_def_property_ui_text(prop, "Impulse Clamping", "Clamp collision impulses to avoid instability (0.0 to disable clamping)");
RNA_def_property_update(prop, 0, "rna_cloth_update");
}
void RNA_def_cloth(BlenderRNA *brna)

View File

@@ -958,6 +958,22 @@ static void rna_def_collision(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Absorption",
"How much of effector force gets lost during collision with this object (in percent)");
RNA_def_property_update(prop, 0, "rna_CollisionSettings_update");
prop = RNA_def_property(srna, "cloth_friction", PROP_FLOAT, PROP_NONE);
RNA_def_property_float_sdna(prop, NULL, "pdef_cfrict");
RNA_def_property_range(prop, 0.0f, 80.0f);
RNA_def_property_ui_text(prop, "Friction", "Friction for cloth collisions");
RNA_def_property_update(prop, 0, "rna_CollisionSettings_update");
prop = RNA_def_property(srna, "use_culling", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_CLOTH_USE_CULLING);
RNA_def_property_ui_text(prop, "Single Sided", "Cloth collision acts with respect to the collider normals (improves penetration recovery)");
RNA_def_property_update(prop, 0, "rna_CollisionSettings_update");
prop = RNA_def_property(srna, "use_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", PFIELD_CLOTH_USE_NORMAL);
RNA_def_property_ui_text(prop, "Override Normals", "Cloth collision impulses act in the direction of the collider normals (more reliable in some cases)");
RNA_def_property_update(prop, 0, "rna_CollisionSettings_update");
}
static void rna_def_effector_weight(BlenderRNA *brna)

View File

@@ -146,113 +146,108 @@ static void deformVerts(
mvert_num = mesh_src->totvert;
if (current_time > collmd->time_xnew) {
unsigned int i;
/* check if mesh has changed */
if (collmd->x && (mvert_num != collmd->mvert_num))
freeData((ModifierData *)collmd);
if (collmd->time_xnew == -1000) { /* first time */
collmd->x = MEM_dupallocN(mesh_src->mvert); /* frame start position */
for (i = 0; i < mvert_num; i++) {
/* we save global positions */
mul_m4_v3(ob->obmat, collmd->x[i].co);
}
collmd->xnew = MEM_dupallocN(collmd->x); // frame end position
collmd->current_x = MEM_dupallocN(collmd->x); // inter-frame
collmd->current_xnew = MEM_dupallocN(collmd->x); // inter-frame
collmd->current_v = MEM_dupallocN(collmd->x); // inter-frame
collmd->mvert_num = mvert_num;
{
const MLoop *mloop = mesh_src->mloop;
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh_src);
collmd->tri_num = BKE_mesh_runtime_looptri_len(mesh_src);
MVertTri *tri = MEM_malloc_arrayN(collmd->tri_num, sizeof(*tri), __func__);
BKE_mesh_runtime_verttri_from_looptri(tri, mloop, looptri, collmd->tri_num);
collmd->tri = tri;
}
/* create bounding box hierarchy */
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
collmd->time_x = collmd->time_xnew = current_time;
collmd->is_static = true;
}
else if (mvert_num == collmd->mvert_num) {
/* put positions to old positions */
tempVert = collmd->x;
collmd->x = collmd->xnew;
collmd->xnew = tempVert;
collmd->time_x = collmd->time_xnew;
memcpy(collmd->xnew, mesh_src->mvert, mvert_num * sizeof(MVert));
bool is_static = true;
for (i = 0; i < mvert_num; i++) {
/* we save global positions */
mul_m4_v3(ob->obmat, collmd->xnew[i].co);
/* detect motion */
is_static = is_static && equals_v3v3(collmd->x[i].co, collmd->xnew[i].co);
}
memcpy(collmd->current_xnew, collmd->x, mvert_num * sizeof(MVert));
memcpy(collmd->current_x, collmd->x, mvert_num * sizeof(MVert));
/* check if GUI setting has changed for bvh */
if (collmd->bvhtree) {
if (ob->pd->pdef_sboft != BLI_bvhtree_get_epsilon(collmd->bvhtree)) {
BLI_bvhtree_free(collmd->bvhtree);
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->current_x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
}
}
/* happens on file load (ONLY when i decomment changes in readfile.c) */
if (!collmd->bvhtree) {
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->current_x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
}
else if (!collmd->is_static || !is_static) {
/* recalc static bounding boxes */
bvhtree_update_from_mvert(
collmd->bvhtree,
collmd->current_x, collmd->current_xnew,
collmd->tri, collmd->tri_num,
true);
}
collmd->is_static = is_static;
collmd->time_xnew = current_time;
}
else if (mvert_num != collmd->mvert_num) {
freeData((ModifierData *)collmd);
}
}
else if (current_time < collmd->time_xnew) {
if (current_time < collmd->time_xnew) {
freeData((ModifierData *)collmd);
}
else {
else if (current_time == collmd->time_xnew) {
if (mvert_num != collmd->mvert_num) {
freeData((ModifierData *)collmd);
}
}
/* check if mesh has changed */
if (collmd->x && (mvert_num != collmd->mvert_num))
freeData((ModifierData *)collmd);
if (collmd->time_xnew == -1000) { /* first time */
collmd->x = MEM_dupallocN(mesh_src->mvert); /* frame start position */
for (uint i = 0; i < mvert_num; i++) {
/* we save global positions */
mul_m4_v3(ob->obmat, collmd->x[i].co);
}
collmd->xnew = MEM_dupallocN(collmd->x); // frame end position
collmd->current_x = MEM_dupallocN(collmd->x); // inter-frame
collmd->current_xnew = MEM_dupallocN(collmd->x); // inter-frame
collmd->current_v = MEM_dupallocN(collmd->x); // inter-frame
collmd->mvert_num = mvert_num;
{
const MLoop *mloop = mesh_src->mloop;
const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(mesh_src);
collmd->tri_num = BKE_mesh_runtime_looptri_len(mesh_src);
MVertTri *tri = MEM_mallocN(sizeof(*tri) * collmd->tri_num, __func__);
BKE_mesh_runtime_verttri_from_looptri(tri, mloop, looptri, collmd->tri_num);
collmd->tri = tri;
}
/* create bounding box hierarchy */
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
collmd->time_x = collmd->time_xnew = current_time;
collmd->is_static = true;
}
else if (mvert_num == collmd->mvert_num) {
/* put positions to old positions */
tempVert = collmd->x;
collmd->x = collmd->xnew;
collmd->xnew = tempVert;
collmd->time_x = collmd->time_xnew;
memcpy(collmd->xnew, mesh_src->mvert, mvert_num * sizeof(MVert));
bool is_static = true;
for (uint i = 0; i < mvert_num; i++) {
/* we save global positions */
mul_m4_v3(ob->obmat, collmd->xnew[i].co);
/* detect motion */
is_static = is_static && equals_v3v3(collmd->x[i].co, collmd->xnew[i].co);
}
memcpy(collmd->current_xnew, collmd->x, mvert_num * sizeof(MVert));
memcpy(collmd->current_x, collmd->x, mvert_num * sizeof(MVert));
/* check if GUI setting has changed for bvh */
if (collmd->bvhtree) {
if (ob->pd->pdef_sboft != BLI_bvhtree_get_epsilon(collmd->bvhtree)) {
BLI_bvhtree_free(collmd->bvhtree);
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->current_x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
}
}
/* happens on file load (ONLY when i decomment changes in readfile.c) */
if (!collmd->bvhtree) {
collmd->bvhtree = bvhtree_build_from_mvert(
collmd->current_x,
collmd->tri, collmd->tri_num,
ob->pd->pdef_sboft);
}
else if (!collmd->is_static || !is_static) {
/* recalc static bounding boxes */
bvhtree_update_from_mvert(
collmd->bvhtree,
collmd->current_x, collmd->current_xnew,
collmd->tri, collmd->tri_num,
true);
}
collmd->is_static = is_static;
collmd->time_xnew = current_time;
}
else if (mvert_num != collmd->mvert_num) {
freeData((ModifierData *)collmd);
}
}
if (mesh_src != mesh) {

View File

@@ -884,20 +884,13 @@ static void cloth_calc_volume_force(ClothModifierData *clmd)
}
#endif
/* old collision stuff for cloth, use for continuity
* until a good replacement is ready
*/
static void cloth_collision_solve_extra(
Depsgraph *depsgraph, Scene *scene, Object *ob, ClothModifierData *clmd, ListBase *effectors,
float frame, float step, float dt)
static void cloth_solve_collisions(Depsgraph *depsgraph, Object *ob, ClothModifierData *clmd, float step, float dt)
{
Cloth *cloth = clmd->clothObject;
Implicit_Data *id = cloth->implicit;
ClothVertex *verts = cloth->verts;
int mvert_num = cloth->mvert_num;
const float spf = (float)clmd->sim_parms->stepsPerFrame / clmd->sim_parms->timescale;
bool do_extra_solve;
const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale);
int i;
if (!(clmd->coll_parms->flags & (CLOTH_COLLSETTINGS_FLAG_ENABLED | CLOTH_COLLSETTINGS_FLAG_SELF)))
@@ -906,69 +899,26 @@ static void cloth_collision_solve_extra(
if (!clmd->clothObject->bvhtree)
return;
// update verts to current positions
BPH_mass_spring_solve_positions(id, dt);
/* Update verts to current positions. */
for (i = 0; i < mvert_num; i++) {
BPH_mass_spring_get_new_position(id, i, verts[i].tx);
sub_v3_v3v3(verts[i].tv, verts[i].tx, verts[i].txold);
copy_v3_v3(verts[i].v, verts[i].tv);
zero_v3(verts[i].dcvel);
}
#if 0 /* unused */
for (i=0, cv=cloth->verts; i<cloth->mvert_num; i++, cv++) {
copy_v3_v3(initial_cos[i], cv->tx);
}
#endif
// call collision function
// TODO: check if "step" or "step+dt" is correct - dg
do_extra_solve = cloth_bvh_objcollision(
depsgraph, ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale);
// copy corrected positions back to simulation
for (i = 0; i < mvert_num; i++) {
float curx[3];
BPH_mass_spring_get_position(id, i, curx);
// correct velocity again, just to be sure we had to change it due to adaptive collisions
sub_v3_v3v3(verts[i].tv, verts[i].tx, curx);
}
if (do_extra_solve) {
// cloth_calc_helper_forces(ob, clmd, initial_cos, step/clmd->sim_parms->timescale, dt/clmd->sim_parms->timescale);
if (cloth_bvh_collision(depsgraph, ob, clmd, step / clmd->sim_parms->timescale, dt / clmd->sim_parms->timescale)) {
for (i = 0; i < mvert_num; i++) {
float newv[3];
if ((clmd->sim_parms->vgroup_mass > 0) && (verts [i].flags & CLOTH_VERT_FLAG_PINNED))
if ((clmd->sim_parms->vgroup_mass > 0) && (verts[i].flags & CLOTH_VERT_FLAG_PINNED))
continue;
BPH_mass_spring_set_new_position(id, i, verts[i].tx);
mul_v3_v3fl(newv, verts[i].tv, spf);
BPH_mass_spring_set_new_velocity(id, i, newv);
BPH_mass_spring_get_new_velocity(id, i, verts[i].tv);
madd_v3_v3fl(verts[i].tv, verts[i].dcvel, time_multiplier);
BPH_mass_spring_set_new_velocity(id, i, verts[i].tv);
}
}
// X = Xnew;
BPH_mass_spring_apply_result(id);
if (do_extra_solve) {
ImplicitSolverResult result;
/* initialize forces to zero */
BPH_mass_spring_clear_forces(id);
// calculate forces
cloth_calc_force(scene, clmd, frame, effectors, step);
// calculate new velocity and position
BPH_mass_spring_solve_velocities(id, dt, &result);
// cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
/* note: positions are advanced only once in the main solver step! */
BPH_mass_spring_apply_result(id);
}
}
static void cloth_clear_result(ClothModifierData *clmd)
@@ -981,7 +931,7 @@ static void cloth_clear_result(ClothModifierData *clmd)
sres->avg_iterations = 0.0f;
}
static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, int steps)
static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *result, float dt)
{
ClothSolverResult *sres = clmd->solver_result;
@@ -990,22 +940,22 @@ static void cloth_record_result(ClothModifierData *clmd, ImplicitSolverResult *r
if (result->status == BPH_SOLVER_SUCCESS) {
sres->min_error = min_ff(sres->min_error, result->error);
sres->max_error = max_ff(sres->max_error, result->error);
sres->avg_error += result->error / (float)steps;
sres->avg_error += result->error * dt;
}
sres->min_iterations = min_ii(sres->min_iterations, result->iterations);
sres->max_iterations = max_ii(sres->max_iterations, result->iterations);
sres->avg_iterations += (float)result->iterations / (float)steps;
sres->avg_iterations += (float)result->iterations * dt;
}
else {
/* error only makes sense for successful iterations */
if (result->status == BPH_SOLVER_SUCCESS) {
sres->min_error = sres->max_error = result->error;
sres->avg_error += result->error / (float)steps;
sres->avg_error += result->error * dt;
}
sres->min_iterations = sres->max_iterations = result->iterations;
sres->avg_iterations += (float)result->iterations / (float)steps;
sres->avg_iterations += (float)result->iterations * dt;
}
sres->status |= result->status;
@@ -1025,7 +975,7 @@ int BPH_cloth_solve(Depsgraph *depsgraph, Object *ob, float frame, ClothModifier
Cloth *cloth = clmd->clothObject;
ClothVertex *verts = cloth->verts/*, *cv*/;
unsigned int mvert_num = cloth->mvert_num;
float dt = clmd->sim_parms->timescale / clmd->sim_parms->stepsPerFrame;
float dt = clmd->sim_parms->dt * clmd->sim_parms->timescale;
Implicit_Data *id = cloth->implicit;
ColliderContacts *contacts = NULL;
int totcolliders = 0;
@@ -1053,12 +1003,6 @@ int BPH_cloth_solve(Depsgraph *depsgraph, Object *ob, float frame, ClothModifier
while (step < tf) {
ImplicitSolverResult result;
/* 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);
}
if (is_hair) {
/* determine contact points */
if (clmd->coll_parms->flags & CLOTH_COLLSETTINGS_FLAG_ENABLED) {
@@ -1081,18 +1025,18 @@ int BPH_cloth_solve(Depsgraph *depsgraph, Object *ob, float frame, ClothModifier
// calculate new velocity and position
BPH_mass_spring_solve_velocities(id, dt, &result);
cloth_record_result(clmd, &result, clmd->sim_parms->stepsPerFrame);
cloth_record_result(clmd, &result, dt);
/* Calculate collision impulses. */
if (!is_hair) {
cloth_solve_collisions(depsgraph, ob, clmd, step, dt);
}
if (is_hair) {
cloth_continuum_step(clmd, dt);
}
BPH_mass_spring_solve_positions(id, dt);
if (!is_hair) {
cloth_collision_solve_extra(depsgraph, scene, ob, clmd, effectors, frame, step, dt);
}
BPH_mass_spring_apply_result(id);
/* move pinned verts to correct position */