[Bf-blender-cvs] [6b62157e38e] soc-2020-soft-body: sdf is generated on the fly now

mattoverby noreply at git.blender.org
Thu Aug 20 23:21:53 CEST 2020


Commit: 6b62157e38e85cf82badeab957d557b681ce8db3
Author: mattoverby
Date:   Thu Aug 20 16:21:49 2020 -0500
Branches: soc-2020-soft-body
https://developer.blender.org/rB6b62157e38e85cf82badeab957d557b681ce8db3

sdf is generated on the fly now

===================================================================

M	extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
M	extern/softbody/src/admmpd_bvh.h
M	extern/softbody/src/admmpd_collision.cpp
M	extern/softbody/src/admmpd_collision.h
M	intern/softbody/admmpd_api.cpp
M	intern/softbody/admmpd_api.h
M	source/blender/blenkernel/intern/softbody.c

===================================================================

diff --git a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
index d6d87d3a791..ba8cea8079f 100755
--- a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
+++ b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp
@@ -9,7 +9,7 @@ class CubicLagrangeDiscreteGrid : public DiscreteGrid
 {
 public:
 
-	CubicLagrangeDiscreteGrid(){}
+	CubicLagrangeDiscreteGrid(){ this->m_n_cells = 0; }
 	CubicLagrangeDiscreteGrid(std::string const& filename);
 	CubicLagrangeDiscreteGrid(Eigen::AlignedBox3d const& domain,
 		std::array<unsigned int, 3> const& resolution);
diff --git a/extern/softbody/src/admmpd_bvh.h b/extern/softbody/src/admmpd_bvh.h
index 647442bc4d7..2ad53d0fad8 100644
--- a/extern/softbody/src/admmpd_bvh.h
+++ b/extern/softbody/src/admmpd_bvh.h
@@ -61,6 +61,12 @@ public:
 		}
 	};
 
+	// Return AABB of root
+	Eigen::AlignedBox<T,DIM> bounds() const {
+		if (m_root) { return m_root->aabb; }
+		return Eigen::AlignedBox<T,DIM>();
+	}
+
 	// Return ptr to the root node
 	// Becomes invalidated after init()
 	std::shared_ptr<Node> root() { return m_root; }
diff --git a/extern/softbody/src/admmpd_collision.cpp b/extern/softbody/src/admmpd_collision.cpp
index e984e83098b..09af127a6c2 100644
--- a/extern/softbody/src/admmpd_collision.cpp
+++ b/extern/softbody/src/admmpd_collision.cpp
@@ -26,83 +26,124 @@ VFCollisionPair::VFCollisionPair() :
 	q_bary(0,0,0)
 	{}
 
-void Collision::ObstacleData::clear() {
-	V = MatrixXd();
-	F = MatrixXi();
-	sdf = Discregrid::CubicLagrangeDiscreteGrid();
+bool Collision::ObstacleData::has_obs() const
+{
+	// The obstacle list may include non-closed obstacles,
+	// in which an SDF cannot be computed. This function
+	// instead returns true only if an SDF can be generated.
+	int n_box = box.size();
+	for (int i=0; i<n_box; ++i) {
+		if (!box[i].isEmpty()) {
+			return true;
+		}
+	}
+	return false;
 }
 
-
-bool Collision::set_obstacles(
-	const float *v0,
-	const float *v1,
-	int nv,
-	const unsigned int *faces,
-	int nf,
-	std::string *err)
+bool Collision::ObstacleData::compute_sdf(int idx)
 {
-	(void)(v0);
-	if (nv==0 || nf==0)
-	{
-		if (err) { *err = "Collision obstacle has no verts or faces"; }
-		obsdata.clear();
+	if (idx < 0 || idx >x1.size()) {
 		return false;
 	}
 
-	std::vector<double> v1_dbl(nv*3);
-    Eigen::AlignedBox<double,3> domain;
-
-	if (obsdata.V.rows() != nv)
-		obsdata.V.resize(nv,3);
-	for (int i=0; i<nv; ++i)
-	{
-		for (int j=0; j<3; ++j)
-		{
-			obsdata.V(i,j) = v1[i*3+j];
-			v1_dbl[i*3+j] = v1[i*3+j];
-		}
-		domain.extend(obsdata.V.row(i).transpose());
-	}
-
-	if (obsdata.F.rows() != nf)
-		obsdata.F.resize(nf,3);
-	for (int i=0; i<nf; ++i)
-	{
-		for (int j=0; j<3; ++j)
-			obsdata.F(i,j) = faces[i*3+j];
+	// There was an error in init
+	if (box[idx].isEmpty()) {
+		return false;
 	}
 
-	// Is the mesh closed?
-	Discregrid::TriangleMesh tm(v1_dbl.data(), faces, nv, nf);
+	// Test that the mesh is closed
+	Discregrid::TriangleMesh tm(
+		(double const*)x1[idx].data(),
+		(unsigned int const*)F[idx].data(),
+		x1[idx].rows(), F[idx].rows());
 	if (!tm.is_closed()) {
-		if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; }
-		obsdata.clear();
 		return false;
 	}
 
 	// Generate signed distance field
-	{
-		Discregrid::MeshDistance md(tm);
-		domain.max() += 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
-		domain.min() -= 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones();
-		std::array<unsigned int, 3> resolution;
-		resolution[0] = 30; resolution[1] = 30; resolution[2] = 30;
-		obsdata.sdf = Discregrid::CubicLagrangeDiscreteGrid(domain, resolution);
-		auto func = Discregrid::DiscreteGrid::ContinuousFunction{};
-		std::vector<std::thread::id> thread_map;
-		md.set_thread_map(&thread_map);
-		func = [&md](Eigen::Vector3d const& xi) {
-			return md.signedDistanceCached(xi);
-		};
-		obsdata.sdf.addFunction(func, &thread_map, false);
+	Discregrid::MeshDistance md(tm);
+	std::array<unsigned int, 3> resolution;
+	resolution[0] = 30; resolution[1] = 30; resolution[2] = 30;
+	sdf[idx] = Discregrid::CubicLagrangeDiscreteGrid(box[idx], resolution);
+	auto func = Discregrid::DiscreteGrid::ContinuousFunction{};
+	std::vector<std::thread::id> thread_map;
+	md.set_thread_map(&thread_map);
+	func = [&md](Eigen::Vector3d const& xi) {
+		return md.signedDistanceCached(xi);
+	};
+	sdf[idx].addFunction(func, &thread_map, false);
+
+	if (sdf[idx].nCells()==0) {
+		return false;
 	}
+	return true;
+}
 
-	if (obsdata.sdf.nCells()==0) {
-		if (err) { *err = "SDF gen failed for collision obstacle"; }
-		obsdata.clear();
+bool Collision::set_obstacles(
+	std::vector<Eigen::MatrixXd> &v0,
+	std::vector<Eigen::MatrixXd> &v1,
+	std::vector<Eigen::MatrixXi> &F,
+	std::string *err)
+{
+	if (v0.size() != v1.size() || v0.size() != F.size()) {
+		if (err) { *err = "Bad dimensions on obstacle input"; }
 		return false;
 	}
 
+	// Copy the obstacle data from the input to the stored
+	// data container. If the vertex locations have changed,
+	// we need to recompute the SDF. Otherwise, leave it as is.
+	int n_obs_new = v0.size();
+	int n_obs_old = obsdata.x0.size();
+    obsdata.sdf.resize(n_obs_new);
+	obsdata.x0.resize(n_obs_new);
+	obsdata.x1.resize(n_obs_new);
+	obsdata.F.resize(n_obs_new);
+	obsdata.box.resize(n_obs_new);
+
+	// We can use isApprox for testing if the obstacle has
+	// moved from the last call to set_obstacles. The SDF
+	// has limited accuracy anyway...
+	double approx_eps = 1e-6;
+	for (int i=0; i<n_obs_new; ++i) {
+
+		bool reset_obs = false;
+		if (i >= n_obs_old) {
+			reset_obs=true; // is new obs
+		}
+		else if (!obsdata.x1[i].isApprox(v1[i],approx_eps) ||
+				!obsdata.x0[i].isApprox(v0[i],approx_eps)) {
+			reset_obs = true; // is different than before
+		}
+
+		if (reset_obs) {
+
+			obsdata.box[i].setEmpty();
+			int nv = v1[i].rows();
+			for (int j=0; j<nv; ++j) {
+				obsdata.box[i].extend(v1[i].row(j).transpose());
+			}
+			obsdata.box[i].max() += 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones();
+			obsdata.box[i].min() -= 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones();
+
+			obsdata.sdf[i] = SDFType(); // clear old sdf
+			obsdata.x0[i] = v0[i];
+			obsdata.x1[i] = v1[i];
+			obsdata.F[i] = F[i].cast<unsigned int>();
+
+			// Determine if the triangle mesh is closed or not.
+			// We want to provide a warning if it is.
+			Discregrid::TriangleMesh tm(
+				(double const*)obsdata.x1[i].data(),
+				(unsigned int const*)obsdata.F[i].data(),
+				obsdata.x1[i].rows(), obsdata.F[i].rows());
+			if (!tm.is_closed()) {
+				obsdata.box[i].setEmpty();
+				if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; }
+			}
+		}
+	}
+
 	return true;
 
 } // end add obstacle
@@ -121,29 +162,31 @@ Collision::detect_against_obs(
 	(void)(data);
 	(void)(pt_t0);
 
-	std::pair<bool,VFCollisionPair> ret = 
-		std::make_pair(false, VFCollisionPair());
-
-	if (!obs->has_obs())
-		return ret;
+	int n_obs = obs->num_obs();
+	if (n_obs==0) {
+		return std::make_pair(false, VFCollisionPair());
+	}
 
-	// So I feel bad because we're using the SDF only
-	// for inside/outside query. Unfortunately this implementation
-	// doesn't store the face indices within the grid cells, so
-	// the interpolate function won't return the nearest
-	// face at the gradient + distance.
-	Vector3d n;
-	double dist = obs->sdf.interpolate(0, pt_t1, &n);
-	if (dist > 0)
+	for (int i=0; i<n_obs; ++i)
+	{
+		if (obs->sdf[i].nCells()==0) {
+			continue; // not initialized
+		}
+		Vector3d n;
+		double dist = obs->sdf[i].interpolate(0, pt_t1, &n);
+		if (dist > 0) { continue; } // not colliding
+
+		std::pair<bool,VFCollisionPair> ret = 
+			std::make_pair(true, VFCollisionPair());
+		ret.first = true;
+		ret.second.q_idx = -1;
+		ret.second.q_is_obs = true;
+		ret.second.q_bary.setZero();
+		ret.second.q_pt = pt_t1 - dist*n;
+		ret.second.q_n = n.normalized();
 		return ret;
-
-	ret.first = true;
-	ret.second.q_idx = -1;
-	ret.second.q_is_obs = true;
-	ret.second.q_bary.setZero();
-	ret.second.q_pt = pt_t1 - dist*n;
-	ret.second.q_n = n.normalized();
-	return ret;
+	}
+	return std::make_pair(false, VFCollisionPair());
 }
 
 int EmbeddedMeshCollision::detect(
@@ -159,8 +202,26 @@ int EmbeddedMeshCollision::detect(
 	if (mesh->type() != MESHTYPE_EMBEDDED)
 		return 0;
 
-	// Do we even need to process collisions?
-	if (!this->obsdata.has_obs() && !options->self_collision)
+	// Compute SDFs if the mesh is intersecting
+	// the associated obstacle. The sdf generation is internally threaded,
+	// but it might be faster to thread the different SDFs.
+	bool has_obs_intersection = false;
+	int n_obs = obsdata.num_obs();
+	AlignedBox<double,3> mesh_box = data->col.prim_tree.bounds();
+	for (int i=0; i<n_obs; ++i) {
+		AlignedBox<double,3> &box = obsdata.box[i];
+		if (box.isEmpty()) { continue; }
+		if (!box.intersects(mesh_box)) { continue; }
+		has_obs_intersection = true;
+		// Do we need to generate a new SDF?
+		if (obsdata.sdf[i].nCells()==0) {
+			obsdata.compute_sdf(i);
+		}
+	}
+
+	// Do we even need to process collisions and launch
+	// the per-vertex threads?
+	if (!has_obs_intersection && !options->self_collision)
 	{
 		if (x1->col(2).minCoeff() > options->floor)
 		{
@@ -236,7 +297,7 @@ int EmbeddedMeshCollision::detect(
 		}
 
 		// Detect against obstacles
-		if (td->obsdata->has_obs())
+		if (td->obsdata->num_obs()>0)
 		{
 			std::pair<bool,VFCollisionPair> pt_hit_obs =
 				td->collision->detect_against_obs(
diff --git a/extern/softbody/src/admmpd_collision.h b/extern/softbody/src/admmpd_collision.h
index 25d9feb295b..4869b2b7268 100644
--- a/extern/softbody/src/admmpd_collision.h
+++ b/extern/softbody/src/admmpd_collision.h
@@ -23,11 +23,15 @@ struct VFCollisionPair {
 class Collision {
 public:
     struct ObstacleData {
-        bool has_obs() const { return F.rows()>0; }
-        void clear();
-        Eigen::MatrixXd V;
-        Eigen::MatrixXi F;
-        SDFType sdf;
+        int num_obs() const { return sdf.size(); }
+        bool has_obs() const;
+        bool 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list