1
// ---------------------------------------------------------
3
// impactzonesolver.cpp
6
// Encapsulates two impact zone solvers: inelastic impact zones, and rigid impact zones.
8
// ---------------------------------------------------------
10
#include <collisionpipeline.h>
11
#include <dynamicsurface.h>
12
#include <impactzonesolver.h>
13
#include <krylov_solvers.h>
15
#include <sparse_matrix.h>
20
// ---------------------------------------------------------
22
/// Combine impact zones which have overlapping vertex stencils
24
// ---------------------------------------------------------
26
void merge_impact_zones( std::vector<ImpactZone>& new_impact_zones, std::vector<ImpactZone>& master_impact_zones )
29
bool merge_ocurred = true;
31
for ( size_t i = 0; i < master_impact_zones.size(); ++i )
33
master_impact_zones[i].m_all_solved = true;
36
for ( size_t i = 0; i < new_impact_zones.size(); ++i )
38
new_impact_zones[i].m_all_solved = false;
42
while ( merge_ocurred )
45
merge_ocurred = false;
47
for ( size_t i = 0; i < new_impact_zones.size(); ++i )
49
bool i_is_disjoint = true;
51
for ( size_t j = 0; j < master_impact_zones.size(); ++j )
53
// check if impact zone i and j share any vertices
55
if ( master_impact_zones[j].share_vertices( new_impact_zones[i] ) )
58
bool found_new_collision = false;
60
// steal all of j's collisions
61
for ( size_t c = 0; c < new_impact_zones[i].m_collisions.size(); ++c )
64
bool same_collision_exists = false;
66
for ( size_t m = 0; m < master_impact_zones[j].m_collisions.size(); ++m )
68
if ( master_impact_zones[j].m_collisions[m].same_vertices( new_impact_zones[i].m_collisions[c] ) )
71
same_collision_exists = true;
77
if ( !same_collision_exists )
79
master_impact_zones[j].m_collisions.push_back( new_impact_zones[i].m_collisions[c] );
80
found_new_collision = true;
84
// did we find any collisions in zone i that zone j didn't already have?
85
if ( found_new_collision )
87
master_impact_zones[j].m_all_solved &= new_impact_zones[i].m_all_solved;
91
i_is_disjoint = false;
99
// copy the impact zone
102
for ( size_t c = 0; c < new_impact_zones[i].m_collisions.size(); ++c )
104
new_zone.m_collisions.push_back( new_impact_zones[i].m_collisions[c] );
107
new_zone.m_all_solved = new_impact_zones[i].m_all_solved;
109
master_impact_zones.push_back( new_zone );
113
new_impact_zones = master_impact_zones;
114
master_impact_zones.clear();
118
master_impact_zones = new_impact_zones;
122
// ---------------------------------------------------------
124
/// Helper function: multiply transpose(A) * D * B
126
// ---------------------------------------------------------
128
void AtDB(const SparseMatrixDynamicCSR &A, const double* diagD, const SparseMatrixDynamicCSR &B, SparseMatrixDynamicCSR &C)
133
for(int k=0; k<A.m; ++k)
135
const DynamicSparseVector& r = A.row[k];
137
for( DynamicSparseVector::const_iterator p=r.begin(); p != r.end(); ++p )
140
double multiplier = p->value * diagD[k];
141
C.add_sparse_row( i, B.row[k], multiplier );
146
} // unnamed namespace
149
// ---------------------------------------------------------
153
// ---------------------------------------------------------
155
ImpactZoneSolver::ImpactZoneSolver( DynamicSurface& surface) :
156
m_surface( surface ),
157
m_rigid_zone_infinite_mass( 1000.0 )
161
// ---------------------------------------------------------
163
/// Iteratively project out relative normal velocities for a set of collisions in an impact zone until all collisions are solved.
165
// ---------------------------------------------------------
167
bool ImpactZoneSolver::iterated_inelastic_projection( ImpactZone& iz, double dt )
169
assert( m_surface.m_masses.size() == m_surface.get_num_vertices() );
171
static const unsigned int MAX_PROJECTION_ITERATIONS = 20;
173
for ( unsigned int i = 0; i < MAX_PROJECTION_ITERATIONS; ++i )
175
bool success = inelastic_projection( iz );
179
if ( m_surface.m_verbose ) { std::cout << "failure in inelastic projection" << std::endl; }
183
bool collision_still_exists = false;
185
for ( size_t c = 0; c < iz.m_collisions.size(); ++c )
188
// run collision detection on this pair again
190
Collision& collision = iz.m_collisions[c];
191
const Vec4st& vs = collision.m_vertex_indices;
193
m_surface.set_newposition( vs[0], m_surface.get_position(vs[0]) + dt * m_surface.m_velocities[vs[0]]);
194
m_surface.set_newposition( vs[1], m_surface.get_position(vs[1]) + dt * m_surface.m_velocities[vs[1]]);
195
m_surface.set_newposition( vs[2], m_surface.get_position(vs[2]) + dt * m_surface.m_velocities[vs[2]]);
196
m_surface.set_newposition( vs[3], m_surface.get_position(vs[3]) + dt * m_surface.m_velocities[vs[3]]);
198
if ( m_surface.m_verbose ) { std::cout << "checking collision " << vs << std::endl; }
200
if ( collision.m_is_edge_edge )
203
double s0, s2, rel_disp;
206
assert( vs[0] < vs[1] && vs[2] < vs[3] ); // should have been sorted by original collision detection
208
if ( segment_segment_collision( m_surface.get_position(vs[0]), m_surface.get_newposition(vs[0]), vs[0],
209
m_surface.get_position(vs[1]), m_surface.get_newposition(vs[1]), vs[1],
210
m_surface.get_position(vs[2]), m_surface.get_newposition(vs[2]), vs[2],
211
m_surface.get_position(vs[3]), m_surface.get_newposition(vs[3]), vs[3],
216
collision.m_normal = normal;
217
collision.m_alphas = Vec4d( -s0, -(1-s0), s2, (1-s2) );
218
collision.m_relative_displacement = rel_disp;
219
collision_still_exists = true;
226
double s1, s2, s3, rel_disp;
229
assert( vs[1] < vs[2] && vs[2] < vs[3] && vs[1] < vs[3] ); // should have been sorted by original collision detection
231
if ( point_triangle_collision( m_surface.get_position(vs[0]), m_surface.get_newposition(vs[0]), vs[0],
232
m_surface.get_position(vs[1]), m_surface.get_newposition(vs[1]), vs[1],
233
m_surface.get_position(vs[2]), m_surface.get_newposition(vs[2]), vs[2],
234
m_surface.get_position(vs[3]), m_surface.get_newposition(vs[3]), vs[3],
239
collision.m_normal = normal;
240
collision.m_alphas = Vec4d( 1, -s1, -s2, -s3 );
241
collision.m_relative_displacement = rel_disp;
242
collision_still_exists = true;
249
if ( false == collision_still_exists )
256
if ( m_surface.m_verbose )
258
std::cout << "reached max iterations for this zone" << std::endl;
266
// ---------------------------------------------------------
268
/// Project out relative normal velocities for a set of collisions in an impact zone.
270
// ---------------------------------------------------------
272
bool ImpactZoneSolver::inelastic_projection( const ImpactZone& iz )
275
if ( m_surface.m_verbose )
277
std::cout << " ----- using sparse solver " << std::endl;
280
const size_t k = iz.m_collisions.size(); // notation from [Harmon et al 2008]: k == number of collisions
282
std::vector<size_t> zone_vertices;
283
iz.get_all_vertices( zone_vertices );
285
const size_t n = zone_vertices.size(); // n == number of distinct colliding vertices
287
if ( m_surface.m_verbose ) { std::cout << "GCT: " << 3*n << "x" << k << std::endl; }
289
SparseMatrixDynamicCSR GCT( to_int(3*n), to_int(k) );
292
// construct matrix grad C transpose
293
for ( int i = 0; i < to_int(k); ++i )
296
const Collision& coll = iz.m_collisions[i];
298
for ( unsigned int v = 0; v < 4; ++v )
300
// block row j ( == block column j of grad C )
301
size_t j = coll.m_vertex_indices[v];
303
std::vector<size_t>::iterator zone_vertex_iter = find( zone_vertices.begin(), zone_vertices.end(), j );
305
assert( zone_vertex_iter != zone_vertices.end() );
307
int mat_j = to_int( zone_vertex_iter - zone_vertices.begin() );
309
GCT(mat_j*3, i) = coll.m_alphas[v] * coll.m_normal[0];
310
GCT(mat_j*3+1, i) = coll.m_alphas[v] * coll.m_normal[1];
311
GCT(mat_j*3+2, i) = coll.m_alphas[v] * coll.m_normal[2];
317
inv_masses.reserve(3*n);
318
Array1d column_velocities;
319
column_velocities.reserve(3*n);
321
for ( size_t i = 0; i < n; ++i )
324
inv_masses.push_back( 1.0 / m_surface.m_masses[zone_vertices[i]] );
325
inv_masses.push_back( 1.0 / m_surface.m_masses[zone_vertices[i]] );
326
inv_masses.push_back( 1.0 / m_surface.m_masses[zone_vertices[i]] );
328
column_velocities.push_back( m_surface.m_velocities[zone_vertices[i]][0] );
329
column_velocities.push_back( m_surface.m_velocities[zone_vertices[i]][1] );
330
column_velocities.push_back( m_surface.m_velocities[zone_vertices[i]][2] );
334
// minimize | M^(-1/2) * GC^T x - M^(1/2) * v |^2
340
KrylovSolverStatus solver_result;
342
// normal equations: GC * M^(-1) GCT * x = GC * v
345
SparseMatrixDynamicCSR A( to_int(k), to_int(k) );
347
AtDB( GCT, inv_masses.data, GCT, A );
350
GCT.apply_transpose( column_velocities.data, b.data );
352
if ( m_surface.m_verbose ) { std::cout << "system built" << std::endl; }
354
MINRES_CR_Solver solver;
355
SparseMatrixStaticCSR solver_matrix( A ); // convert dynamic to static
356
solver.max_iterations = 1000;
357
solver_result = solver.solve( solver_matrix, b.data, x.data );
359
if ( solver_result != KRYLOV_CONVERGED )
361
if ( m_surface.m_verbose )
363
std::cout << "CR solver failed: ";
364
if ( solver_result == KRYLOV_BREAKDOWN )
366
std::cout << "KRYLOV_BREAKDOWN" << std::endl;
370
std::cout << "KRYLOV_EXCEEDED_MAX_ITERATIONS" << std::endl;
373
double residual_norm = BLAS::abs_max(solver.r);
374
std::cout << "residual_norm: " << residual_norm << std::endl;
382
Array1d applied_impulses(3*n);
383
GCT.apply( x.data, applied_impulses.data );
385
static const double IMPULSE_MULTIPLIER = 0.8;
387
for ( size_t i = 0; i < applied_impulses.size(); ++i )
389
column_velocities[i] -= IMPULSE_MULTIPLIER * inv_masses[i] * applied_impulses[i];
392
for ( size_t i = 0; i < n; ++i )
394
m_surface.m_velocities[zone_vertices[i]][0] = column_velocities[3*i];
395
m_surface.m_velocities[zone_vertices[i]][1] = column_velocities[3*i + 1];
396
m_surface.m_velocities[zone_vertices[i]][2] = column_velocities[3*i + 2];
405
// ---------------------------------------------------------
407
/// Handle all collisions simultaneously by iteratively solving individual impact zones until no new collisions are detected.
409
// ---------------------------------------------------------
411
bool ImpactZoneSolver::inelastic_impact_zones(double dt)
415
std::vector<Vec3d> old_velocities = m_surface.m_velocities;
417
std::vector<ImpactZone> impact_zones;
419
bool finished_detecting_collisions = false;
421
std::vector<Collision> total_collisions;
422
finished_detecting_collisions = m_surface.m_collision_pipeline->detect_collisions(total_collisions);
424
while ( false == total_collisions.empty() )
426
// insert each new collision constraint into its own impact zone
427
std::vector<ImpactZone> new_impact_zones;
428
for ( size_t i = 0; i < total_collisions.size(); ++i )
431
new_zone.m_collisions.push_back( total_collisions[i] );
432
new_impact_zones.push_back( new_zone );
435
// now we have one zone for each collision
436
assert( new_impact_zones.size() == total_collisions.size() );
438
// merge all impact zones that share vertices
439
merge_impact_zones( new_impact_zones, impact_zones );
441
// remove impact zones which have been solved
442
for ( int i = 0; i < (int) impact_zones.size(); ++i )
444
if ( impact_zones[i].m_all_solved )
446
impact_zones.erase( impact_zones.begin() + i );
451
for ( int i = 0; i < (int) impact_zones.size(); ++i )
453
assert( false == impact_zones[i].m_all_solved );
456
bool all_zones_solved_ok = true;
458
// for each impact zone
459
for ( size_t i = 0; i < impact_zones.size(); ++i )
462
// reset impact zone to pre-response m_velocities
463
for ( size_t j = 0; j < impact_zones[i].m_collisions.size(); ++j )
465
const Vec4st& vs = impact_zones[i].m_collisions[j].m_vertex_indices;
466
m_surface.m_velocities[vs[0]] = old_velocities[vs[0]];
467
m_surface.m_velocities[vs[1]] = old_velocities[vs[1]];
468
m_surface.m_velocities[vs[2]] = old_velocities[vs[2]];
469
m_surface.m_velocities[vs[3]] = old_velocities[vs[3]];
472
// apply inelastic projection
474
all_zones_solved_ok &= iterated_inelastic_projection( impact_zones[i], dt );
476
// reset predicted positions
477
for ( size_t j = 0; j < impact_zones[i].m_collisions.size(); ++j )
479
const Vec4st& vs = impact_zones[i].m_collisions[j].m_vertex_indices;
481
m_surface.set_newposition( vs[0], m_surface.get_position(vs[0]) + dt * m_surface.m_velocities[vs[0]] );
482
m_surface.set_newposition( vs[1], m_surface.get_position(vs[1]) + dt * m_surface.m_velocities[vs[1]] );
483
m_surface.set_newposition( vs[2], m_surface.get_position(vs[2]) + dt * m_surface.m_velocities[vs[2]] );
484
m_surface.set_newposition( vs[3], m_surface.get_position(vs[3]) + dt * m_surface.m_velocities[vs[3]] );
491
if ( false == all_zones_solved_ok )
493
if ( m_surface.m_verbose )
495
std::cout << "at least one impact zone had a solver problem" << std::endl;
501
total_collisions.clear();
503
if ( !finished_detecting_collisions )
505
if ( m_surface.m_verbose ) { std::cout << "attempting to finish global collision detection" << std::endl; }
506
finished_detecting_collisions = m_surface.m_collision_pipeline->detect_collisions( total_collisions );
507
impact_zones.clear();
511
bool detect_ok = m_surface.m_collision_pipeline->detect_new_collisions( impact_zones, total_collisions );
526
// ---------------------------------------------------------
528
/// Rigid Impact Zones, as described in [Bridson, Fedkiw, Anderson 2002].
530
// ---------------------------------------------------------
533
bool ImpactZoneSolver::rigid_impact_zones(double dt)
536
extern RunStats g_stats;
537
g_stats.add_to_int( "ImpactZoneSolver:rigid_impact_zones", 1 );
540
std::vector<Vec3d> old_velocities = m_surface.m_velocities;
542
std::vector<ImpactZone> impact_zones;
544
bool finished_detecting_collisions = false;
546
std::vector<Collision> total_collisions;
547
finished_detecting_collisions = m_surface.m_collision_pipeline->detect_collisions(total_collisions);
549
while ( false == total_collisions.empty() )
551
// insert each new collision constraint into its own impact zone
552
std::vector<ImpactZone> new_impact_zones;
553
for ( size_t i = 0; i < total_collisions.size(); ++i )
556
new_zone.m_collisions.push_back( total_collisions[i] );
557
new_impact_zones.push_back( new_zone );
559
} // for loop over total_collisions
561
assert( new_impact_zones.size() == total_collisions.size() );
563
// merge all impact zones that share vertices
564
merge_impact_zones( new_impact_zones, impact_zones );
566
for ( int i = 0; i < (int) impact_zones.size(); ++i )
568
if ( impact_zones[i].m_all_solved )
570
impact_zones[i].m_all_solved = false;
571
impact_zones.erase( impact_zones.begin() + i );
576
for ( int i = 0; i < (int) impact_zones.size(); ++i )
578
assert( false == impact_zones[i].m_all_solved );
581
// for each impact zone
582
for ( size_t i = 0; i < impact_zones.size(); ++i )
585
std::vector<size_t> zone_vertices;
586
impact_zones[i].get_all_vertices( zone_vertices );
587
bool rigid_motion_ok = calculate_rigid_motion(dt, zone_vertices);
589
if ( !rigid_motion_ok )
591
std::cout << "rigid impact zone fails" << std::endl;
597
total_collisions.clear();
599
if ( !finished_detecting_collisions )
601
finished_detecting_collisions = m_surface.m_collision_pipeline->detect_collisions( total_collisions );
602
impact_zones.clear();
606
bool detect_ok = m_surface.m_collision_pipeline->detect_new_collisions( impact_zones, total_collisions );
607
std::cout << "new collisions detected: " << total_collisions.size() << std::endl;
622
// ---------------------------------------------------------
624
/// Compute the best-fit rigid motion for the set of moving vertices
626
// ---------------------------------------------------------
628
bool ImpactZoneSolver::calculate_rigid_motion(double dt, std::vector<size_t>& vs)
634
for(size_t i = 0; i < vs.size(); i++)
638
double m = m_surface.m_masses[idx];
640
if ( m_surface.vertex_is_solid(idx) )
642
m = m_rigid_zone_infinite_mass;
645
assert( m != std::numeric_limits<double>::infinity() );
649
m_surface.m_velocities[idx] = ( m_surface.get_newposition(idx) - m_surface.get_position(idx) ) / dt;
651
xcm += m * m_surface.get_position(idx);
652
vcm += m * m_surface.m_velocities[idx];
656
double min_dist_t0 = 1e+30;
657
double min_dist_t1 = 1e+30;
658
for(size_t i = 0; i < vs.size(); i++)
660
for(size_t j = i+1; j < vs.size(); j++)
662
min_dist_t0 = min( min_dist_t0, dist( m_surface.get_position(vs[i]), m_surface.get_position(vs[j]) ) );
663
min_dist_t1 = min( min_dist_t1, dist( m_surface.get_newposition(vs[i]), m_surface.get_newposition(vs[j]) ) );
674
for(size_t i = 0; i < vs.size(); i++)
678
double m = m_surface.m_masses[idx];
680
if ( m_surface.vertex_is_solid(idx) )
682
m = m_rigid_zone_infinite_mass;
685
assert( m != std::numeric_limits<double>::infinity() );
687
Vec3d xdiff = m_surface.get_position(idx) - xcm;
688
Vec3d vdiff = m_surface.m_velocities[idx] - vcm;
690
L += m * cross(xdiff, vdiff);
693
Mat33d I(0,0,0,0,0,0,0,0,0);
695
for(size_t i = 0; i < vs.size(); i++)
698
double m = m_surface.m_masses[idx];
700
if ( m_surface.vertex_is_solid(idx) )
702
m = m_rigid_zone_infinite_mass;
705
assert( m != std::numeric_limits<double>::infinity() );
707
Vec3d xdiff = m_surface.get_position(idx) - xcm;
708
Mat33d tens = outer(-xdiff, xdiff);
710
double d = mag2(xdiff);
718
double det = determinant(I);
720
Vec3d w = inverse(I) * L;
721
double wmag = mag(w);
730
Vec3d wnorm = w/wmag;
732
double cosdtw = cos(dt * wmag);
733
Vec3d sindtww = sin(dt * wmag) * wnorm;
735
Vec3d xrigid = xcm + dt * vcm;
737
double max_velocity_mag = -1.0;
739
for(size_t i = 0; i < vs.size(); i++)
743
Vec3d xdiff = m_surface.get_position(idx) - xcm;
744
Vec3d xf = dot(xdiff, wnorm) * wnorm;
745
Vec3d xr = xdiff - xf;
747
m_surface.set_newposition( idx, xrigid + xf + cosdtw * xr + cross(sindtww, xr) );
749
m_surface.m_velocities[idx] = ( m_surface.get_newposition(idx) - m_surface.get_position(idx) ) / dt;
751
max_velocity_mag = max( max_velocity_mag, mag( m_surface.m_velocities[idx] ) );
756
for(size_t i = 0; i < vs.size(); i++)
758
for(size_t j = i+1; j < vs.size(); j++)
760
min_dist_t1 = min( min_dist_t1, dist( m_surface.get_newposition(vs[i]), m_surface.get_newposition(vs[j]) ) );