From 6191b3d2db2d8788e6d8f12cc6e433d247377387 Mon Sep 17 00:00:00 2001 From: over0219 Date: Wed, 15 Jul 2020 14:58:46 -0500 Subject: octree for lattice gen and smaller dt with CD only at init solve --- extern/softbody/src/admmpd_bvh.cpp | 131 +++++++++++ extern/softbody/src/admmpd_bvh.h | 56 ++++- extern/softbody/src/admmpd_bvh_traverse.h | 1 + extern/softbody/src/admmpd_collision.cpp | 12 +- extern/softbody/src/admmpd_embeddedmesh.cpp | 346 ++++++++-------------------- extern/softbody/src/admmpd_solver.cpp | 8 +- extern/softbody/src/admmpd_solver.h | 2 +- extern/softbody/src/admmpd_types.h | 2 +- 8 files changed, 305 insertions(+), 253 deletions(-) (limited to 'extern') diff --git a/extern/softbody/src/admmpd_bvh.cpp b/extern/softbody/src/admmpd_bvh.cpp index fffc8c33a74..6ea7a676d27 100644 --- a/extern/softbody/src/admmpd_bvh.cpp +++ b/extern/softbody/src/admmpd_bvh.cpp @@ -209,6 +209,133 @@ bool AABBTree::traverse_children( } // end traverse children +template +void Octree::clear() +{ + m_root = std::make_shared(); +} + +template +typename Octree::AABB Octree::bounds() const +{ + if (m_root) + return m_root->bounds(); + return AABB(); +} + +template +void Octree::init(const MatrixXT *V, const Eigen::MatrixXi *F, int stopdepth) +{ + BLI_assert(V != nullptr); + BLI_assert(F != nullptr); + BLI_assert(F->cols()==3); + m_root = std::make_shared(); + + if (DIM !=3 || F->cols()!=3) + { + return; + } + + int nf = F->rows(); + AABB global_box; + std::vector boxes(nf); + std::vector queue(nf); + for (int i=0; irow(i); + queue[i]=i; + boxes[i].extend(V->row(f[0]).transpose()); + boxes[i].extend(V->row(f[1]).transpose()); + boxes[i].extend(V->row(f[2]).transpose()); + global_box.extend(boxes[i]); + } + + T halfwidth = global_box.sizes().maxCoeff()*0.5; + m_root.reset( + create_children(global_box.center(),halfwidth,stopdepth,V,F,queue,boxes) + ); +} + +template +typename Octree::Node* Octree::create_children( + const VecType ¢er, T halfwidth, int stopdepth, + const MatrixXT *V, const Eigen::MatrixXi *F, + const std::vector &queue, + const std::vector &boxes) +{ + BLI_assert((int)queue.size()>0); + BLI_assert((int)prim_boxes.size()>0); + BLI_assert(F != nullptr); + BLI_assert(V != nullptr); + BLI_assert(F->cols()==3); + BLI_assert(V->cols()==3); + if (stopdepth >= 0) + { + Node *node = new Node(); + node->center = center; + node->halfwidth = halfwidth; + node->prims.clear(); + AABB box = node->bounds(); + // Set list of intersected prims + int nq = queue.size(); + for (int i=0; iprims.emplace_back(p_idx); + } + // Create children only if prims intersect + int np = node->prims.size(); + for (int i=0; i0; ++i) + { + + T step = node->halfwidth * 0.5; + VecType offset = VecType::Zero(); + offset[0] = ((i & 1) ? step : -step); + offset[1] = ((i & 2) ? step : -step); + if (DIM==3) offset[2] = ((i & 4) ? step : -step); + + node->children[i] = create_children( + node->center+offset, step, stopdepth-1, + V, F, node->prims, boxes); + } + return node; + } + return nullptr; +} + +template +Octree::Node::Node() : + center(VecType::Zero()), + halfwidth(0) +{ + for (int i=0; i +Octree::Node::~Node() +{ + for (int i=0; i +bool Octree::Node::is_leaf() const +{ + return children[0] == nullptr; +} + +template +typename Octree::AABB Octree::Node::bounds() const +{ + AABB box; + box.extend(center + VecType::Ones()*halfwidth); + box.extend(center - VecType::Ones()*halfwidth); + return box; +} + // Compile types template class admmpd::AABBTree; template class admmpd::AABBTree; @@ -218,5 +345,9 @@ template class admmpd::TraverserFromFunctionPtrs; template class admmpd::TraverserFromFunctionPtrs; template class admmpd::TraverserFromFunctionPtrs; template class admmpd::TraverserFromFunctionPtrs; +template class admmpd::Octree; +template class admmpd::Octree; +template class admmpd::Octree; +template class admmpd::Octree; } // namespace admmpd \ No newline at end of file diff --git a/extern/softbody/src/admmpd_bvh.h b/extern/softbody/src/admmpd_bvh.h index 19f47545435..20b810ac7be 100644 --- a/extern/softbody/src/admmpd_bvh.h +++ b/extern/softbody/src/admmpd_bvh.h @@ -42,8 +42,6 @@ public: std::function t, std::function s) const; -protected: - struct Node { AABB aabb; @@ -60,6 +58,8 @@ protected: } }; +protected: + std::shared_ptr root; void create_children( @@ -77,6 +77,58 @@ protected: }; // class AABBtree + +// Octree is actually a quadtree if DIM=2 +template +class Octree +{ +protected: + typedef Eigen::AlignedBox AABB; + typedef Eigen::Matrix VecType; + typedef Eigen::Matrix MatrixXT; + static const int nchild = std::pow(2,DIM); +public: + + // Removes all BVH data + void clear(); + + // Creates the Octree up to depth with smart subdivision + // (only create children if it contains prims) and does not + // create a cell if it is outside the mesh. + // ** Assumes a closed mesh and only defined for 3D + void init(const MatrixXT *V, const Eigen::MatrixXi *F, int stopdepth); + + // Returns bounding box of the root node + AABB bounds() const; + + struct Node + { + VecType center; + T halfwidth; + Node *children[4*DIM]; + std::vector prims; // includes childen + bool is_leaf() const; + AABB bounds() const; + Node(); + ~Node(); + }; + + // Return ptr to the root node + // Becomes invalidated after init() + std::shared_ptr root() { return m_root; } + +protected: + + std::shared_ptr m_root; + + Node* create_children( + const VecType ¢er, T halfwidth, int stopdepth, + const MatrixXT *V, const Eigen::MatrixXi *F, + const std::vector &queue, + const std::vector &boxes); + +}; // class Octree + } // namespace admmpd #endif // ADMMPD_BVH_H_ diff --git a/extern/softbody/src/admmpd_bvh_traverse.h b/extern/softbody/src/admmpd_bvh_traverse.h index ecbb85f3277..dce83d07a20 100644 --- a/extern/softbody/src/admmpd_bvh_traverse.h +++ b/extern/softbody/src/admmpd_bvh_traverse.h @@ -136,6 +136,7 @@ public: struct Output { std::vector< std::pair > hits; // [prim,t] int num_hits() const { return hits.size(); } + bool is_inside() const { return hits.size()%2==1; } } output; PointInTriangleMeshTraverse( diff --git a/extern/softbody/src/admmpd_collision.cpp b/extern/softbody/src/admmpd_collision.cpp index 5e14bc3a756..a05b27287cc 100644 --- a/extern/softbody/src/admmpd_collision.cpp +++ b/extern/softbody/src/admmpd_collision.cpp @@ -308,9 +308,10 @@ void EmbeddedMeshCollision::linearize( for (int i=0; iget_mapped_vertex(x,emb_p_idx); // // If we collided with an obstacle @@ -334,8 +335,17 @@ void EmbeddedMeshCollision::linearize( }; q_n = ((q_tris[1]-q_tris[0]).cross(q_tris[2]-q_tris[0])); q_n.normalize(); + + // Update constraint linearization + pair.q_pt = geom::point_on_triangle(p_pt, + q_tris[0], q_tris[1], q_tris[2]); } + // Is the constraint active? + bool active = (p_pt-pair.q_pt).dot(q_n) <= 0.0; + if (!active) + continue; + // Get the four deforming verts that embed // the surface vertices, and add constraints on those. RowVector4d bary = mesh->emb_barys.row(emb_p_idx); diff --git a/extern/softbody/src/admmpd_embeddedmesh.cpp b/extern/softbody/src/admmpd_embeddedmesh.cpp index b16d92b640a..7e677b42455 100644 --- a/extern/softbody/src/admmpd_embeddedmesh.cpp +++ b/extern/softbody/src/admmpd_embeddedmesh.cpp @@ -18,55 +18,6 @@ namespace admmpd { using namespace Eigen; -typedef struct KeepTetThreadData { - const AABBTree *tree; // of embedded faces - const MatrixXd *pts; // of embedded verts - const MatrixXi *faces; // embedded faces - const std::vector *tet_x; - const std::vector *tets; - std::vector *keep_tet; // 0 or 1 -} KeepTetThreadData; - -static void parallel_keep_tet( - void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - KeepTetThreadData *td = (KeepTetThreadData*)userdata; - RowVector4i tet = td->tets->at(i); - Vector3d tet_pts[4] = { - td->tet_x->at(tet[0]), - td->tet_x->at(tet[1]), - td->tet_x->at(tet[2]), - td->tet_x->at(tet[3]) - }; - - // Returns true if the tet intersects the - // surface mesh. Even if it doesn't, we want to keep - // the tet if it's totally enclosed in the mesh. - TetIntersectsMeshTraverse tet_hits_mesh( - tet_pts, td->pts, td->faces); - bool hit = td->tree->traverse(tet_hits_mesh); - if (!hit) - { - // We only need to check if one vertex of the - // tet is inside the mesh. If a subset of - // vertices are inside the mesh, then there would - // be tri/tri intersection. - PointInTriangleMeshTraverse pt_in_mesh( - tet_pts[0], td->pts, td->faces); - td->tree->traverse(pt_in_mesh); - if (pt_in_mesh.output.num_hits() % 2 == 1) - { - hit = true; - } - } - - if (hit) { td->keep_tet->at(i) = 1; } - else { td->keep_tet->at(i) = 0; } - -} // end parallel test if keep tet - // Gen lattice with subdivision struct LatticeData { //SDF *emb_sdf; @@ -114,110 +65,87 @@ static inline void merge_close_vertices(LatticeData *data, double eps=1e-12) } } -static inline void subdivide_cube( - LatticeData *data, - const std::vector &cv, - const std::vector &faces, - int level) +static inline void add_tets_from_box( + const Vector3d &min, + const Vector3d &max, + std::vector &verts, + std::vector &tets) { - BLI_assert((int)cv.size()==8); - auto add_tets_from_box = [&]() + std::vector v = { + // Top plane, clockwise looking down + max, + Vector3d(min[0], max[1], max[2]), + Vector3d(min[0], max[1], min[2]), + Vector3d(max[0], max[1], min[2]), + // Bottom plane, clockwise looking down + Vector3d(max[0], min[1], max[2]), + Vector3d(min[0], min[1], max[2]), + min, + Vector3d(max[0], min[1], min[2]) + }; + // Add vertices and get indices of the box + std::vector b; + for(int i=0; i<8; ++i) { - Vector3d max = cv[5]; - Vector3d min = cv[3]; - std::vector v = { - // Top plane, clockwise looking down - max, - Vector3d(min[0], max[1], max[2]), - Vector3d(min[0], max[1], min[2]), - Vector3d(max[0], max[1], min[2]), - // Bottom plane, clockwise looking down - Vector3d(max[0], min[1], max[2]), - Vector3d(min[0], min[1], max[2]), - min, - Vector3d(max[0], min[1], min[2]) - }; - // Add vertices and get indices of the box - std::vector b; - for(int i=0; i<8; ++i) - { - b.emplace_back(data->verts.size()); - data->verts.emplace_back(v[i]); - } - // From the box, create five new tets - std::vector new_tets = { - Vector4i( b[0], b[5], b[7], b[4] ), - Vector4i( b[5], b[7], b[2], b[0] ), - Vector4i( b[5], b[0], b[2], b[1] ), - Vector4i( b[7], b[2], b[0], b[3] ), - Vector4i( b[5], b[2], b[7], b[6] ) - }; - for(int i=0; i<5; ++i) - data->tets.emplace_back(new_tets[i]); + b.emplace_back(verts.size()); + verts.emplace_back(v[i]); + } + // From the box, create five new tets + std::vector new_tets = { + Vector4i( b[0], b[5], b[7], b[4] ), + Vector4i( b[5], b[7], b[2], b[0] ), + Vector4i( b[5], b[0], b[2], b[1] ), + Vector4i( b[7], b[2], b[0], b[3] ), + Vector4i( b[5], b[2], b[7], b[6] ) }; + for(int i=0; i<5; ++i) + tets.emplace_back(new_tets[i]); +}; - // Add this cube because we're at bottom - if (level==0) +static void gather_octree_tets( + Octree::Node *node, + const MatrixXd *V, const MatrixXi *F, + AABBTree *face_tree, + std::vector &verts, + std::vector &tets + ) +{ + if (node == nullptr) { - add_tets_from_box(); return; } - // Only subdivide if the box contains the surface. - AlignedBox aabb; - aabb.extend(cv[3]); aabb.extend(cv[5]); - aabb.extend(aabb.min()-Vector3d::Ones()*1e-8); - aabb.extend(aabb.max()+Vector3d::Ones()*1e-8); - std::vector new_faces; - int nf = faces.size(); - for (int i=0; iis_leaf(); + bool has_prims = (int)node->prims.size()>0; + if (is_leaf) { - int f_idx = faces[i]; - RowVector3i f = data->F->row(f_idx); - Vector3d v0 = data->V->row(f[0]); - Vector3d v1 = data->V->row(f[1]); - Vector3d v2 = data->V->row(f[2]); - if (geom::aabb_triangle_intersect(aabb.min(),aabb.max(),v0,v1,v2)) - new_faces.emplace_back(f_idx); + Vector3d bmin = node->center-Vector3d::Ones()*node->halfwidth; + Vector3d bmax = node->center+Vector3d::Ones()*node->halfwidth; + + // If we have primitives in the cell, + // create tets. Otherwise, launch a ray + // to determine if we are inside or outside + // the mesh. If we're outside, don't create tets. + if (has_prims) + { + add_tets_from_box(bmin,bmax,verts,tets); + } + else + { + PointInTriangleMeshTraverse pt_in_mesh(node->center,V,F); + face_tree->traverse(pt_in_mesh); + if (pt_in_mesh.output.is_inside()) + add_tets_from_box(bmin,bmax,verts,tets); + } + return; } - if (new_faces.size()==0) + for (int i=0; i<8; ++i) { - add_tets_from_box(); - return; + gather_octree_tets(node->children[i],V,F,face_tree,verts,tets); } - // cv are the cube vertices, listed clockwise - // with the bottom plane first, then top plane. - // This is basically a dumb version of an octree. - Vector3d vfront = 0.25*(cv[0]+cv[1]+cv[5]+cv[4]); // front (+z) - Vector3d vback = 0.25*(cv[3]+cv[2]+cv[6]+cv[7]); // back (-z) - Vector3d vleft = 0.25*(cv[0]+cv[3]+cv[7]+cv[4]); // left (-x) - Vector3d vright = 0.25*(cv[1]+cv[2]+cv[6]+cv[5]); // right (+x) - Vector3d vtop = 0.25*(cv[4]+cv[5]+cv[6]+cv[7]); // top (+y) - Vector3d vbot = 0.25*(cv[0]+cv[1]+cv[2]+cv[3]); // bot (-y) - Vector3d vcent = 0.125*(cv[0]+cv[1]+cv[2]+cv[3]+cv[4]+cv[5]+cv[6]+cv[7]); // center - Vector3d v01 = 0.5*(cv[0]+cv[1]); - Vector3d v03 = 0.5*(cv[0]+cv[3]); - Vector3d v04 = 0.5*(cv[0]+cv[4]); - Vector3d v12 = 0.5*(cv[1]+cv[2]); - Vector3d v15 = 0.5*(cv[1]+cv[5]); - Vector3d v23 = 0.5*(cv[2]+cv[3]); - Vector3d v26 = 0.5*(cv[2]+cv[6]); - Vector3d v37 = 0.5*(cv[3]+cv[7]); - Vector3d v45 = 0.5*(cv[4]+cv[5]); - Vector3d v56 = 0.5*(cv[5]+cv[6]); - Vector3d v67 = 0.5*(cv[6]+cv[7]); - Vector3d v47 = 0.5*(cv[4]+cv[7]); - subdivide_cube(data, { cv[0], v01, vbot, v03, v04, vfront, vcent, vleft }, new_faces, level-1); - subdivide_cube(data, { v01, cv[1], v12, vbot, vfront, v15, vright, vcent }, new_faces, level-1); - subdivide_cube(data, { vbot, v12, cv[2], v23, vcent, vright, v26, vback }, new_faces, level-1); - subdivide_cube(data, { v03, vbot, v23, cv[3], vleft, vcent, vback, v37 }, new_faces, level-1); - subdivide_cube(data, { v04, vfront, vcent, vleft, cv[4], v45, vtop, v47 }, new_faces, level-1); - subdivide_cube(data, { vfront, v15, vright, vcent, v45, cv[5], v56, vtop }, new_faces, level-1); - subdivide_cube(data, { vcent, vright, v26, vback, vtop, v56, cv[6], v67 }, new_faces, level-1); - subdivide_cube(data, { vleft, vcent, vback, v37, v47, vtop, v67, cv[7] }, new_faces, level-1); +} // end gather octree tets -} bool EmbeddedMesh::generate( const Eigen::MatrixXd &V, // embedded verts @@ -231,124 +159,54 @@ bool EmbeddedMesh::generate( if (F.rows()==0 || V.rows()==0) throw std::runtime_error("EmbeddedMesh::generate Error: Missing data"); - AlignedBox aabb; - int nev = V.rows(); - for (int i=0; i b0 = { - Vector3d(min[0], min[1], max[2]), - Vector3d(max[0], min[1], max[2]), - Vector3d(max[0], min[1], min[2]), - min, - Vector3d(min[0], max[1], max[2]), - max, - Vector3d(max[0], max[1], min[2]), - Vector3d(min[0], max[1], min[2]) - }; - - std::vector faces_in_box(F.rows()); - std::iota(faces_in_box.begin(), faces_in_box.end(), 0); - LatticeData data; data.V = &V; data.F = &F; - subdivide_cube(&data,b0,faces_in_box,subdiv_levels); - merge_close_vertices(&data); - // We only want to keep tets that are either - // a) intersecting the surface mesh - // b) totally inside the surface mesh - std::set refd_verts; - { - int nf = F.rows(); - std::vector > face_aabb(nf); - for (int i=0; i octree; + octree.init(&V,&F,subdiv_levels); - int nt0 = data.tets.size(); - std::vector keep_tet(nt0,1); - emb_rest_tree.init(face_aabb); - KeepTetThreadData thread_data = { - .tree = &emb_rest_tree, - .pts = &V, - .faces = &F, - .tet_x = &data.verts, - .tets = &data.tets, - .keep_tet = &keep_tet - }; - if (trim_lattice) - { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range(0, nt0, &thread_data, parallel_keep_tet, &settings); - } + int nf = F.rows(); + std::vector > face_boxes(nf); + for (int i=0; i::iterator it = data.tets.begin(); - it != data.tets.end(); ++tet_idx) - { - bool keep = keep_tet[tet_idx]; - if (keep) - { - const Vector4i &t = *it; - refd_verts.emplace(t[0]); - refd_verts.emplace(t[1]); - refd_verts.emplace(t[2]); - refd_verts.emplace(t[3]); - ++it; - } - else { it = data.tets.erase(it); } - } + AABBTree face_tree; + face_tree.init(face_boxes); - } // end remove unnecessary tets + Octree::Node *root = octree.root().get(); + gather_octree_tets(root,&V,&F,&face_tree,data.verts,data.tets); + merge_close_vertices(&data); - // Copy data into matrices and remove unreferenced + int nv = data.verts.size(); + lat_rest_x.resize(nv,3); + for (int i=0; i vtx_old_to_new; - int ntv0 = data.verts.size(); // original num verts - int ntv1 = refd_verts.size(); // reduced num verts - BLI_assert(ntv1 <= ntv0); - lat_rest_x.resize(ntv1,3); - int vtx_count = 0; - for (int i=0; i0) - { - for(int j=0; j<3; ++j){ - lat_rest_x(vtx_count,j) = data.verts[i][j]; - } - vtx_old_to_new[i] = vtx_count; - vtx_count++; - } + for(int j=0; j<3; ++j){ + lat_rest_x(i,j) = data.verts[i][j]; } - - // Copy tets to matrix data and update vertices - int nt = data.tets.size(); - lat_tets.resize(nt,4); - for(int i=0; i0); - lat_tets(i,j) = vtx_old_to_new[old_idx]; - } + } + int nt = data.tets.size(); + lat_tets.resize(nt,4); + for(int i=0; imax_admm_iters; ++iters) @@ -77,9 +80,6 @@ int Solver::solve( // Update ADMM z/u solve_local_step(options,data); - // Perform collision detection and linearization - update_constraints(options,data,collision); - // Solve Ax=b s.t. Cx=d ConjugateGradients().solve(options,data,collision); //GaussSeidel().solve(options,data,collision); @@ -190,7 +190,7 @@ void Solver::solve_local_step( BLI_task_parallel_range(0, ne, &thread_data, parallel_zu_update, &settings); } // end local step -void Solver::update_constraints( +void Solver::linearize_collision_constraints( const Options *options, SolverData *data, Collision *collision) diff --git a/extern/softbody/src/admmpd_solver.h b/extern/softbody/src/admmpd_solver.h index 0344122f0b5..584f54be43c 100644 --- a/extern/softbody/src/admmpd_solver.h +++ b/extern/softbody/src/admmpd_solver.h @@ -34,7 +34,7 @@ public: protected: - void update_constraints( + void linearize_collision_constraints( const Options *options, SolverData *data, Collision *collision); diff --git a/extern/softbody/src/admmpd_types.h b/extern/softbody/src/admmpd_types.h index 7e08b328396..c2ae0e67c88 100644 --- a/extern/softbody/src/admmpd_types.h +++ b/extern/softbody/src/admmpd_types.h @@ -28,7 +28,7 @@ struct Options { double poisson; // Poisson ratio // TODO variable per-tet Eigen::Vector3d grav; Options() : - timestep_s(1.0/24.0), + timestep_s(1.0/100.0), max_admm_iters(30), max_cg_iters(10), max_gs_iters(100), -- cgit v1.2.3