Commit: 97b08dcdbb39b0cba2573d4b709b0aeb53c66e00
Author: mattoverby
Date: Wed Aug 5 14:27:40 2020 -0500
Branches: soc-2020-soft-body
https://developer.blender.org/rB97b08dcdbb39b0cba2573d4b709b0aeb53c66e00
update cg solver
===================================================================
M extern/softbody/src/admmpd_collision.cpp
M extern/softbody/src/admmpd_collision.h
M extern/softbody/src/admmpd_energy.cpp
M extern/softbody/src/admmpd_linsolve.cpp
M extern/softbody/src/admmpd_linsolve.h
M extern/softbody/src/admmpd_mesh.cpp
M extern/softbody/src/admmpd_mesh.h
M extern/softbody/src/admmpd_solver.cpp
M extern/softbody/src/admmpd_solver.h
M extern/softbody/src/admmpd_types.h
M intern/softbody/admmpd_api.cpp
M release/scripts/startup/bl_ui/properties_physics_softbody.py
M source/blender/blenkernel/intern/softbody.c
M source/blender/makesdna/DNA_object_force_types.h
M source/blender/makesrna/intern/rna_object_force.c
===================================================================
diff --git a/extern/softbody/src/admmpd_collision.cpp
b/extern/softbody/src/admmpd_collision.cpp
index fee3456e022..1d37ffc688e 100644
--- a/extern/softbody/src/admmpd_collision.cpp
+++ b/extern/softbody/src/admmpd_collision.cpp
@@ -459,7 +459,7 @@ void EmbeddedMeshCollision::graph(
void EmbeddedMeshCollision::linearize(
const Eigen::MatrixXd *x,
std::vector<Eigen::Triplet<double> > *trips,
- std::vector<double> *d)
+ std::vector<double> *d) const
{
BLI_assert(x != NULL);
BLI_assert(x->cols() == 3);
@@ -475,7 +475,7 @@ void EmbeddedMeshCollision::linearize(
for (int i=0; i<np; ++i)
{
const Vector2i &pair_idx = vf_pairs[i];
- VFCollisionPair &pair =
per_vertex_pairs[pair_idx[0]][pair_idx[1]];
+ const VFCollisionPair &pair =
per_vertex_pairs[pair_idx[0]][pair_idx[1]];
int emb_p_idx = pair.p_idx;
// Vector3d p_pt =
meshdata.mesh->get_mapped_facet_vertex(x,emb_p_idx);
diff --git a/extern/softbody/src/admmpd_collision.h
b/extern/softbody/src/admmpd_collision.h
index d4a3b57f20c..4228a53f8f4 100644
--- a/extern/softbody/src/admmpd_collision.h
+++ b/extern/softbody/src/admmpd_collision.h
@@ -25,8 +25,6 @@ struct VFCollisionPair {
class Collision {
public:
- typedef Discregrid::CubicLagrangeDiscreteGrid SDFType;
-
struct ObstacleData {
bool has_obs() const { return F.rows()>0; }
Eigen::MatrixXd V;
@@ -65,11 +63,12 @@ public:
const unsigned int *faces,
int nf);
- // Linearize the constraints about x and return Jacobian.
+ // Linearizes active collision pairs about x
+ // for the constraint Cx=d
virtual void linearize(
const Eigen::MatrixXd *x,
std::vector<Eigen::Triplet<double> > *trips,
- std::vector<double> *d) = 0;
+ std::vector<double> *d) const = 0;
// Given a point and a mesh, perform
// discrete collision detection.
@@ -79,6 +78,7 @@ public:
const Eigen::Vector3d &pt_t1,
const ObstacleData *obs) const;
+ // Perform self collision detection
virtual std::pair<bool,VFCollisionPair>
detect_against_self(
int pt_idx,
@@ -101,13 +101,13 @@ public:
void graph(
std::vector<std::set<int> > &g);
-
+
// Linearizes the collision pairs about x
- // for the constraint Kx=l
+ // for the constraint Cx=d
void linearize(
const Eigen::MatrixXd *x,
std::vector<Eigen::Triplet<double> > *trips,
- std::vector<double> *d);
+ std::vector<double> *d) const;
// Updates the tetmesh BVH for self collisions.
void update_bvh(
diff --git a/extern/softbody/src/admmpd_energy.cpp
b/extern/softbody/src/admmpd_energy.cpp
index 394769d6291..eb4cb9c9534 100644
--- a/extern/softbody/src/admmpd_energy.cpp
+++ b/extern/softbody/src/admmpd_energy.cpp
@@ -147,6 +147,18 @@ void EnergyTerm::update_tri(
double w2 = weight*weight;
zi_T = (kv*p + w2*zi_T) / (w2 + kv);
+ // Apply strain limiting
+ bool check_strain = lame.m_limit[0] > 0.0 || lame.m_limit[1] < 99.0;
+ if (check_strain)
+ {
+ double l_col0 = zi_T.col(0).norm();
+ double l_col1 = zi_T.col(1).norm();
+ if (l_col0 < lame.m_limit[0]) { zi_T.col(0) *= (
lame.m_limit[0]/l_col0 ); }
+ if (l_col1 < lame.m_limit[0]) { zi_T.col(1) *= (
lame.m_limit[0]/l_col1 ); }
+ if (l_col0 > lame.m_limit[1]) { zi_T.col(0) *= (
lame.m_limit[1]/l_col0 ); }
+ if (l_col1 > lame.m_limit[1]) { zi_T.col(1) *= (
lame.m_limit[1]/l_col1 ); }
+ }
+
ui += (Dix - zi_T.transpose());
u->block<2,3>(index,0) = ui;
z->block<2,3>(index,0) = zi_T.transpose();
diff --git a/extern/softbody/src/admmpd_linsolve.cpp
b/extern/softbody/src/admmpd_linsolve.cpp
index e926c36db15..5330e07cfaf 100644
--- a/extern/softbody/src/admmpd_linsolve.cpp
+++ b/extern/softbody/src/admmpd_linsolve.cpp
@@ -8,109 +8,327 @@
#include <set>
#include <unordered_set>
#include <random>
+#include <thread>
#include "BLI_assert.h"
#include "BLI_task.h"
namespace admmpd {
using namespace Eigen;
-void ConjugateGradients::init_solve(
+// Throws an exception for a given function with message
+static inline void throw_err(const std::string &f, const std::string &m)
+{
+ throw std::runtime_error("LinearSolver::"+f+": "+m);
+}
+
+LDLT::LDLT() :
+ last_pk(-1)
+ {}
+
+void LDLT::init_solve(
+ const Mesh *mesh,
+ const Options *options,
+ const Collision *collision,
+ SolverData *data)
+{
+ (void)(collision);
+ int nx = std::max((int)data->x.rows(),1);
+
+ // Get the P matrix
+ std::set<int> pin_inds;
+ std::vector<Triplet<double> > trips;
+ std::vector<double> q_coeffs;
+ bool replicate = false;
+ bool new_P = mesh->linearize_pins(trips, q_coeffs, pin_inds, replicate);
+
+ // If we've changed the stiffness but not the pins,
+ // the P matrix is still changing
+ double pk = options->mult_pk * data->A_diag_max;
+ if (std::abs(pk-last_pk) > 1e-8 && trips.size()>0)
+ new_P = true;
+
+ // Compute P
+ last_pk = pk;
+ int np = q_coeffs.size();
+ SparseMatrix<double> P;
+ MatrixXd q;
+ if (m_Ptq.rows() != nx) { m_Ptq.resize(nx,3); }
+ if (np==0)
+ { // no springs
+ P.resize(1,nx);
+ P.setZero();
+ q = MatrixXd::Zero(1,3);
+ m_Ptq.setZero();
+ }
+ else
+ {
+ P.resize(np,nx);
+ P.setFromTriplets(trips.begin(), trips.end());
+ q.resize(np,3);
+ for (int i=0; i<np; ++i)
+ {
+ q(i,0) = q_coeffs[i*3+0];
+ q(i,1) = q_coeffs[i*3+1];
+ q(i,2) = q_coeffs[i*3+2];
+ }
+ m_Ptq = pk * P.transpose() * q;
+ }
+
+ // Compute A + P'P and factorize:
+ // 1) A not computed
+ // 2) P has changed
+ // 3) factorization not set
+ if (m_A_PtP.nonZeros()==0 || new_P || !m_ldlt_A_PtP)
+ {
+ if (!m_ldlt_A_PtP)
+ m_ldlt_A_PtP = std::make_unique<Cholesky>();
+
+ m_A_PtP = SparseMatrix<double>(data->A) + pk * P.transpose()*P;
+ m_ldlt_A_PtP->compute(m_A_PtP);
+ if(m_ldlt_A_PtP->info() != Eigen::Success) {
+ throw_err("init_solve","facorization failed");
+ }
+ geom::make_n3<double>(m_A_PtP,m_A_PtP_3); // replicate
+ }
+} // end init solve
+
+void LDLT::solve(
+ const Mesh *mesh,
const Options *options,
+ const Collision *collision,
SolverData *data)
{
- int nx3 = 3*data->x.rows();
- if (nx3==0)
+ if (!m_ldlt_A_PtP)
+ throw_err("solve","you must call init_solve before solve");
+
+ int nx = std::max((int)data->x.rows(),1);
+
+ // Linearize collision constraints
+ RowSparseMatrix<double> C;
+ VectorXd d;
+ if (collision != nullptr)
+ {
+ std::vector<double> d_coeffs;
+ std::vector<Eigen::Triplet<double> > trips;
+ collision->linearize(&data->x, &trips, &d_coeffs);
+
+ int nc = d_coeffs.size();
+ if (nc>0)
+ {
+ d = Map<VectorXd>(d_coeffs.data(), d_coeffs.size());
+ C.resize(nc,nx*3);
+ C.setFromTriplets(trips.begin(),trips.end());
+ }
+ }
+
+ // Compute RHS
+ m_rhs.noalias() = data->M_xbar + data->DtW2*(data->z-data->u) + m_Ptq;
+
+ // If there are no collision constraints,
+ // we can use our initial factorization.
+ if (C.nonZeros()==0)
+ {
+ data->x.noalias() = m_ldlt_A_PtP->solve(m_rhs);
return;
+ }
- A3_PtP_CtC.resize(nx3,nx3);
- b.resize(data->x.rows(),data->x.cols());
- CtC.resize(nx3,nx3);
- Ctd.resize(nx3);
- Ptq.resize(nx3);
- b3_Ptq_Ctd.resize(nx3);
- r.resize(nx3);
- z.resize(nx3);
- p.resize(nx3);
- Ap.resize(nx3);
- double pk = options->mult_pk * data->A_diag_max;
- Ptq = pk * data->P.transpose()*data->q;
+ // Otherwise we have to solve the full system:
+ // (A + PtP + CtC) x = b + Ptq + Ctd
+ double ck = options->mult_ck * data->A_diag_max;
+ SparseMatrix<double> A3 = m_A_PtP_3 + ck * C.transpose()*C;
+ VectorXd Ctd = ck * C.transpose() * d;
+ VectorXd rhs3(nx*3);
+ for (int i=0; i<nx; ++i)
+ {
+ rhs3.segment<3>(i*3) =
+ m_rhs.row(i).transpose() +
+ Ctd.segment<3>(i*3);
+ }
+
+ Cholesky ldlt_full(A3);
+ VectorXd x3 = ldlt_full.solve(rhs3);
+ for (int i=0; i<nx; ++i)
+ data->x.row(i) = x3.segment<3>(i*3);
+
+} // end solve
+
+ConjugateGradients::ConjugateGradients() :
+ factor_A_PtP(true),
+ last_pk(-1)
+ {}
+
+void ConjugateGradients::init_solve(
+ const Mesh *mesh,
+ const Options *options,
+ const Collision *collision,
+ SolverData *data)
+{
+ int nx = data->x.rows();
+ if (nx==0) { return; }
+
+ if (!factor_A_PtP)
+ throw_err("init_solve","todo: not factor A+P'P");
+
+ if (!m_ldlt)
+ m_ldlt = std::make_unique<LDLT>();
+
+ // We'll just use our LDLT implementation
+ // to decide when to refactor the matrix
+ m_ldlt->init_solve(mesh,options,collision,data);
+
+ rhs.resize(nx,3);
+ Ctd.resize(nx,3);
+ r.resize(nx,3);
+ z.resize(nx,3);
+ p.resize(nx,3);
+ p3.resize(nx*3);
+ Ap.resize(nx,3);
}
void ConjugateGradients::solve(
- const Options *options,
- SolverData *data,
- Collision *collision)
+ const Mesh *mesh,
+ const Options *options,
+ const Collision *collision,
+ SolverData *data)
{
- (void)(collision); // unused
+ auto map_vector_to_matrix = [](const VectorXd &x3, MatrixXd &x)
+ {
+ int nx = x3.rows()/3;
+ if (x.rows() != nx) { x.resize(nx,3); }
+ for (int i=0; i<nx; ++i)
+ x.row(i) = x3.segment<3>(i*3);
+ };
+
+ auto map_matrix_to_vector = [](const MatrixXd &x, VectorXd &x3)
+ {
+ int nx = x.rows();
+ if (x3.rows() != nx*3) { x3.resize(nx*3); }
+ for (int i=0; i<nx; ++i)
+ x3.segment<3>(i*3) = x.row(i);
+ };
+
+ auto mat_inner = [](const MatrixXd &A, const MatrixXd &B)
+ {
+ double sum = 0.0;
+ int rows = std::min(A.rows(), B.rows());
+ int dim = std::min(A.cols(), B.cols());
+ for (int i=0; i<rows; ++i)
+ {
+ for (int j=0; j<dim; ++j)
+ {
+ sum += A(i,j)*B(i,j);
+ }
+ }
+ return sum;
+ };
+
BLI_assert(data != NULL);
BLI_assert(options != NULL);
int nx = data->x.rows();
BLI_assert(nx > 0);
- BLI_assert(data->C.cols() == nx*3);
- BLI_assert(data->d.rows() > 0);
- BLI_assert(data->C.rows() == data->d.rows());
- // Update A matrix with collision constraints
- double ck = options->mult_ck * data->A_diag_max;
- RowSparseMatrix<double> *A;
- if (data->C.nonZeros()>0)
- {
- Ctd = ck * data->C.transpose()*data->d;
- CtC = ck * data->C.transpose()*data->C;
- A3_PtP_CtC = data->A3_plus_PtP + CtC;
- A = &A3_PtP_CtC;
- }
- else
+ if (!m_ldlt)
+ throw_err("solve","you must call init_solve before solve");
+
+ // Linearize collision constraints
+ RowSparseMatrix<double> C;
+ VectorXd d;
+ if (collision != nullptr)
{
- Ctd.setZero();
- A = &data->A3_plus_PtP;
+ std::vector<double> d_coeffs;
@@ Diff output truncated at 10240 characters. @@
_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs