1 // Copyright Contributors to the OpenVDB Project 2 // SPDX-License-Identifier: MPL-2.0 3 // 4 /// @author Ken Museth, D.J. Hill (openvdb port, added staggered grid support) 5 /// 6 /// @file tools/PointAdvect.h 7 /// 8 /// @brief Class PointAdvect advects points (with position) in a static velocity field 9 10 #ifndef OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED 11 #define OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED 12 13 #include "openvdb/openvdb.h" 14 #include "openvdb/Types.h" // Vec3 types and version number 15 #include "openvdb/Grid.h" // grid 16 #include "openvdb/math/Math.h" // min 17 #include "openvdb/util/NullInterrupter.h" 18 #include "openvdb/thread/Threading.h" 19 #include "Interpolation.h" // sampling 20 #include "VelocityFields.h" // VelocityIntegrator 21 22 #include <tbb/blocked_range.h> // threading 23 #include <tbb/parallel_for.h> // threading 24 #include <tbb/task.h> // for cancel 25 26 #include <vector> 27 28 29 namespace openvdb { 30 OPENVDB_USE_VERSION_NAMESPACE 31 namespace OPENVDB_VERSION_NAME { 32 namespace tools { 33 34 /// Class that holds a Vec3 grid, to be interpreted as the closest point to a constraint 35 /// surface. Supports a method to allow a point to be projected onto the closest point 36 /// on the constraint surface. Uses Caching. 37 template<typename CptGridT = Vec3fGrid> 38 class ClosestPointProjector 39 { 40 public: 41 using CptGridType = CptGridT; 42 using CptAccessor = typename CptGridType::ConstAccessor; 43 using CptValueType = typename CptGridType::ValueType; 44 ClosestPointProjector()45 ClosestPointProjector(): 46 mCptIterations(0) 47 { 48 } ClosestPointProjector(const CptGridType & cptGrid,int n)49 ClosestPointProjector(const CptGridType& cptGrid, int n): 50 mCptGrid(&cptGrid), 51 mCptAccessor(cptGrid.getAccessor()), 52 mCptIterations(n) 53 { 54 } ClosestPointProjector(const ClosestPointProjector & other)55 ClosestPointProjector(const ClosestPointProjector &other): 56 mCptGrid(other.mCptGrid), 57 mCptAccessor(mCptGrid->getAccessor()), 58 mCptIterations(other.mCptIterations) 59 { 60 } setConstraintIterations(unsigned int cptIterations)61 void setConstraintIterations(unsigned int cptIterations) { mCptIterations = cptIterations; } numIterations()62 unsigned int numIterations() { return mCptIterations; } 63 64 // point constraint 65 template <typename LocationType> projectToConstraintSurface(LocationType & W)66 inline void projectToConstraintSurface(LocationType& W) const 67 { 68 /// Entries in the CPT tree are the closest point to the constraint surface. 69 /// The interpolation step in sample introduces error so that the result 70 /// of a single sample may not lie exactly on the surface. The iterations 71 /// in the loop exist to minimize this error. 72 CptValueType result(W[0], W[1],W[2]); 73 for (unsigned int i = 0; i < mCptIterations; ++i) { 74 const Vec3R location = mCptGrid->worldToIndex(Vec3R(result[0], result[1], result[2])); 75 BoxSampler::sample<CptAccessor>(mCptAccessor, location, result); 76 } 77 W[0] = result[0]; 78 W[1] = result[1]; 79 W[2] = result[2]; 80 } 81 82 private: 83 const CptGridType* mCptGrid; // Closest-Point-Transform vector field 84 CptAccessor mCptAccessor; 85 unsigned int mCptIterations; 86 };// end of ClosestPointProjector class 87 88 //////////////////////////////////////// 89 90 91 /// Performs passive or constrained advection of points in a velocity field 92 /// represented by an OpenVDB grid and an optional closest-point-transform (CPT) 93 /// represented in another OpenVDB grid. Note the CPT is assumed to be 94 /// in world coordinates and NOT index coordinates! 95 /// Supports both collocated velocity grids and staggered velocity grids 96 /// 97 /// The @c PointListT template argument refers to any class with the following 98 /// interface (e.g., std::vector<openvdb::Vec3f>): 99 /// @code 100 /// class PointList { 101 /// ... 102 /// public: 103 /// using value_type = internal_vector3_type; // must support [] component access 104 /// openvdb::Index size() const; // number of points in list 105 /// value_type& operator[](int n); // world space position of nth point 106 /// }; 107 /// @endcode 108 /// 109 /// @note All methods (except size) are assumed to be thread-safe and 110 /// the positions are returned as non-const references since the 111 /// advection method needs to modify them! 112 template<typename GridT = Vec3fGrid, 113 typename PointListT = std::vector<typename GridT::ValueType>, 114 bool StaggeredVelocity = false, 115 typename InterrupterType = util::NullInterrupter> 116 class PointAdvect 117 { 118 public: 119 using GridType = GridT; 120 using PointListType = PointListT; 121 using LocationType = typename PointListT::value_type; 122 using VelocityFieldIntegrator = VelocityIntegrator<GridT, StaggeredVelocity>; 123 124 PointAdvect(const GridT& velGrid, InterrupterType* interrupter = nullptr): 125 mVelGrid(&velGrid), 126 mPoints(nullptr), 127 mIntegrationOrder(1), 128 mThreaded(true), 129 mInterrupter(interrupter) 130 { 131 } PointAdvect(const PointAdvect & other)132 PointAdvect(const PointAdvect &other) : 133 mVelGrid(other.mVelGrid), 134 mPoints(other.mPoints), 135 mDt(other.mDt), 136 mAdvIterations(other.mAdvIterations), 137 mIntegrationOrder(other.mIntegrationOrder), 138 mThreaded(other.mThreaded), 139 mInterrupter(other.mInterrupter) 140 { 141 } ~PointAdvect()142 virtual ~PointAdvect() 143 { 144 } 145 /// If the order of the integration is set to zero no advection is performed earlyOut()146 bool earlyOut() const { return (mIntegrationOrder==0);} 147 /// get & set setThreaded(bool threaded)148 void setThreaded(bool threaded) { mThreaded = threaded; } getThreaded()149 bool getThreaded() { return mThreaded; } setIntegrationOrder(unsigned int order)150 void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} 151 152 /// Constrained advection of a list of points over a time = dt * advIterations 153 void advect(PointListT& points, float dt, unsigned int advIterations = 1) 154 { 155 if (this->earlyOut()) return; // nothing to do! 156 mPoints = &points; 157 mDt = dt; 158 mAdvIterations = advIterations; 159 160 if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); 161 if (mThreaded) { 162 tbb::parallel_for(tbb::blocked_range<size_t>(0, mPoints->size()), *this); 163 } else { 164 (*this)(tbb::blocked_range<size_t>(0, mPoints->size())); 165 } 166 if (mInterrupter) mInterrupter->end(); 167 } 168 169 /// Never call this method directly - it is use by TBB and has to be public! operator()170 void operator() (const tbb::blocked_range<size_t> &range) const 171 { 172 if (mInterrupter && mInterrupter->wasInterrupted()) { 173 thread::cancelGroupExecution(); 174 } 175 176 VelocityFieldIntegrator velField(*mVelGrid); 177 switch (mIntegrationOrder) { 178 case 1: 179 { 180 for (size_t n = range.begin(); n != range.end(); ++n) { 181 LocationType& X0 = (*mPoints)[n]; 182 // loop over number of time steps 183 for (unsigned int i = 0; i < mAdvIterations; ++i) { 184 velField.template rungeKutta<1>(mDt, X0); 185 } 186 } 187 } 188 break; 189 case 2: 190 { 191 for (size_t n = range.begin(); n != range.end(); ++n) { 192 LocationType& X0 = (*mPoints)[n]; 193 // loop over number of time steps 194 for (unsigned int i = 0; i < mAdvIterations; ++i) { 195 velField.template rungeKutta<2>(mDt, X0); 196 } 197 } 198 } 199 break; 200 case 3: 201 { 202 for (size_t n = range.begin(); n != range.end(); ++n) { 203 LocationType& X0 = (*mPoints)[n]; 204 // loop over number of time steps 205 for (unsigned int i = 0; i < mAdvIterations; ++i) { 206 velField.template rungeKutta<3>(mDt, X0); 207 } 208 } 209 } 210 break; 211 case 4: 212 { 213 for (size_t n = range.begin(); n != range.end(); ++n) { 214 LocationType& X0 = (*mPoints)[n]; 215 // loop over number of time steps 216 for (unsigned int i = 0; i < mAdvIterations; ++i) { 217 velField.template rungeKutta<4>(mDt, X0); 218 } 219 } 220 } 221 break; 222 } 223 } 224 225 private: 226 // the velocity field 227 const GridType* mVelGrid; 228 229 // vertex list of all the points 230 PointListT* mPoints; 231 232 // time integration parameters 233 float mDt; // time step 234 unsigned int mAdvIterations; // number of time steps 235 unsigned int mIntegrationOrder; 236 237 // operational parameters 238 bool mThreaded; 239 InterrupterType* mInterrupter; 240 241 };//end of PointAdvect class 242 243 244 template<typename GridT = Vec3fGrid, 245 typename PointListT = std::vector<typename GridT::ValueType>, 246 bool StaggeredVelocity = false, 247 typename CptGridType = GridT, 248 typename InterrupterType = util::NullInterrupter> 249 class ConstrainedPointAdvect 250 { 251 public: 252 using GridType = GridT; 253 using LocationType = typename PointListT::value_type; 254 using VelocityIntegratorType = VelocityIntegrator<GridT, StaggeredVelocity>; 255 using ClosestPointProjectorType = ClosestPointProjector<CptGridType>; 256 using PointListType = PointListT; 257 258 ConstrainedPointAdvect(const GridType& velGrid, 259 const GridType& cptGrid, int cptn, InterrupterType* interrupter = nullptr): 260 mVelGrid(&velGrid), 261 mCptGrid(&cptGrid), 262 mCptIter(cptn), 263 mInterrupter(interrupter) 264 { 265 } ConstrainedPointAdvect(const ConstrainedPointAdvect & other)266 ConstrainedPointAdvect(const ConstrainedPointAdvect& other): 267 mVelGrid(other.mVelGrid), 268 mCptGrid(other.mCptGrid), 269 mCptIter(other.mCptIter), 270 mPoints(other.mPoints), 271 mDt(other.mDt), 272 mAdvIterations(other.mAdvIterations), 273 mIntegrationOrder(other.mIntegrationOrder), 274 mThreaded(other.mThreaded), 275 mInterrupter(other.mInterrupter) 276 { 277 } ~ConstrainedPointAdvect()278 virtual ~ConstrainedPointAdvect(){} 279 setConstraintIterations(unsigned int cptIter)280 void setConstraintIterations(unsigned int cptIter) {mCptIter = cptIter;} setIntegrationOrder(unsigned int order)281 void setIntegrationOrder(unsigned int order) {mIntegrationOrder = order;} 282 setThreaded(bool threaded)283 void setThreaded(bool threaded) { mThreaded = threaded; } getThreaded()284 bool getThreaded() { return mThreaded; } 285 286 /// Constrained Advection a list of points over a time = dt * advIterations 287 void advect(PointListT& points, float dt, unsigned int advIterations = 1) 288 { 289 mPoints = &points; 290 mDt = dt; 291 292 if (mIntegrationOrder==0 && mCptIter == 0) { 293 return; // nothing to do! 294 } 295 (mIntegrationOrder>0) ? mAdvIterations = advIterations : mAdvIterations = 1; 296 297 if (mInterrupter) mInterrupter->start("Advecting points by OpenVDB velocity field: "); 298 const size_t N = mPoints->size(); 299 300 if (mThreaded) { 301 tbb::parallel_for(tbb::blocked_range<size_t>(0, N), *this); 302 } else { 303 (*this)(tbb::blocked_range<size_t>(0, N)); 304 } 305 if (mInterrupter) mInterrupter->end(); 306 } 307 308 309 /// Never call this method directly - it is use by TBB and has to be public! operator()310 void operator() (const tbb::blocked_range<size_t> &range) const 311 { 312 if (mInterrupter && mInterrupter->wasInterrupted()) { 313 thread::cancelGroupExecution(); 314 } 315 316 VelocityIntegratorType velField(*mVelGrid); 317 ClosestPointProjectorType cptField(*mCptGrid, mCptIter); 318 switch (mIntegrationOrder) { 319 case 0://pure CPT projection 320 { 321 for (size_t n = range.begin(); n != range.end(); ++n) { 322 LocationType& X0 = (*mPoints)[n]; 323 for (unsigned int i = 0; i < mAdvIterations; ++i) { 324 cptField.projectToConstraintSurface(X0); 325 } 326 } 327 } 328 break; 329 case 1://1'th order advection and CPT projection 330 { 331 for (size_t n = range.begin(); n != range.end(); ++n) { 332 LocationType& X0 = (*mPoints)[n]; 333 for (unsigned int i = 0; i < mAdvIterations; ++i) { 334 velField.template rungeKutta<1>(mDt, X0); 335 cptField.projectToConstraintSurface(X0); 336 } 337 } 338 } 339 break; 340 case 2://2'nd order advection and CPT projection 341 { 342 for (size_t n = range.begin(); n != range.end(); ++n) { 343 LocationType& X0 = (*mPoints)[n]; 344 for (unsigned int i = 0; i < mAdvIterations; ++i) { 345 velField.template rungeKutta<2>(mDt, X0); 346 cptField.projectToConstraintSurface(X0); 347 } 348 } 349 } 350 break; 351 352 case 3://3'rd order advection and CPT projection 353 { 354 for (size_t n = range.begin(); n != range.end(); ++n) { 355 LocationType& X0 = (*mPoints)[n]; 356 for (unsigned int i = 0; i < mAdvIterations; ++i) { 357 velField.template rungeKutta<3>(mDt, X0); 358 cptField.projectToConstraintSurface(X0); 359 } 360 } 361 } 362 break; 363 case 4://4'th order advection and CPT projection 364 { 365 for (size_t n = range.begin(); n != range.end(); ++n) { 366 LocationType& X0 = (*mPoints)[n]; 367 for (unsigned int i = 0; i < mAdvIterations; ++i) { 368 velField.template rungeKutta<4>(mDt, X0); 369 cptField.projectToConstraintSurface(X0); 370 } 371 } 372 } 373 break; 374 } 375 } 376 377 private: 378 const GridType* mVelGrid; // the velocity field 379 const GridType* mCptGrid; 380 int mCptIter; 381 PointListT* mPoints; // vertex list of all the points 382 383 // time integration parameters 384 float mDt; // time step 385 unsigned int mAdvIterations; // number of time steps 386 unsigned int mIntegrationOrder; // order of Runge-Kutta integration 387 // operational parameters 388 bool mThreaded; 389 InterrupterType* mInterrupter; 390 };// end of ConstrainedPointAdvect class 391 392 } // namespace tools 393 } // namespace OPENVDB_VERSION_NAME 394 } // namespace openvdb 395 396 #endif // OPENVDB_TOOLS_POINT_ADVECT_HAS_BEEN_INCLUDED 397