| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | // Copyright Contributors to the OpenVDB Project | ||
| 2 | // SPDX-License-Identifier: MPL-2.0 | ||
| 3 | |||
| 4 | /// @file tools/VolumeToSpheres.h | ||
| 5 | /// | ||
| 6 | /// @brief Fill a closed level set or fog volume with adaptively-sized spheres. | ||
| 7 | |||
| 8 | #ifndef OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED | ||
| 9 | #define OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED | ||
| 10 | |||
| 11 | #include <openvdb/tree/LeafManager.h> | ||
| 12 | #include <openvdb/math/Math.h> | ||
| 13 | #include "Morphology.h" // for erodeActiveValues() | ||
| 14 | #include "PointScatter.h" | ||
| 15 | #include "LevelSetRebuild.h" | ||
| 16 | #include "LevelSetUtil.h" | ||
| 17 | #include "VolumeToMesh.h" | ||
| 18 | #include <openvdb/openvdb.h> | ||
| 19 | |||
| 20 | #include <tbb/blocked_range.h> | ||
| 21 | #include <tbb/parallel_for.h> | ||
| 22 | #include <tbb/parallel_reduce.h> | ||
| 23 | |||
| 24 | #include <algorithm> // for std::min(), std::max() | ||
| 25 | #include <cmath> // for std::sqrt() | ||
| 26 | #include <limits> // for std::numeric_limits | ||
| 27 | #include <memory> | ||
| 28 | #include <random> | ||
| 29 | #include <utility> // for std::pair | ||
| 30 | #include <vector> | ||
| 31 | |||
| 32 | |||
| 33 | namespace openvdb { | ||
| 34 | OPENVDB_USE_VERSION_NAMESPACE | ||
| 35 | namespace OPENVDB_VERSION_NAME { | ||
| 36 | namespace tools { | ||
| 37 | |||
| 38 | /// @brief Fill a closed level set or fog volume with adaptively-sized spheres. | ||
| 39 | /// | ||
| 40 | /// @param grid a scalar grid that defines the surface to be filled with spheres | ||
| 41 | /// @param spheres an output array of 4-tuples representing the fitted spheres<BR> | ||
| 42 | /// The first three components of each tuple specify the sphere center, | ||
| 43 | /// and the fourth specifies the radius. | ||
| 44 | /// The spheres are ordered by radius, from largest to smallest. | ||
| 45 | /// @param sphereCount lower and upper bounds on the number of spheres to be generated<BR> | ||
| 46 | /// The actual number will be somewhere within the bounds. | ||
| 47 | /// @param overlapping toggle to allow spheres to overlap/intersect | ||
| 48 | /// @param minRadius the smallest allowable sphere size, in voxel units<BR> | ||
| 49 | /// @param maxRadius the largest allowable sphere size, in voxel units | ||
| 50 | /// @param isovalue the voxel value that determines the surface of the volume<BR> | ||
| 51 | /// The default value of zero works for signed distance fields, | ||
| 52 | /// while fog volumes require a larger positive value | ||
| 53 | /// (0.5 is a good initial guess). | ||
| 54 | /// @param instanceCount the number of interior points to consider for the sphere placement<BR> | ||
| 55 | /// Increasing this count increases the chances of finding optimal | ||
| 56 | /// sphere sizes. | ||
| 57 | /// @param interrupter pointer to an object adhering to the util::NullInterrupter interface | ||
| 58 | /// | ||
| 59 | /// @note The minimum sphere count takes precedence over the minimum radius. | ||
| 60 | template<typename GridT, typename InterrupterT = util::NullInterrupter> | ||
| 61 | void | ||
| 62 | fillWithSpheres( | ||
| 63 | const GridT& grid, | ||
| 64 | std::vector<openvdb::Vec4s>& spheres, | ||
| 65 | const Vec2i& sphereCount = Vec2i(1, 50), | ||
| 66 | bool overlapping = false, | ||
| 67 | float minRadius = 1.0, | ||
| 68 | float maxRadius = std::numeric_limits<float>::max(), | ||
| 69 | float isovalue = 0.0, | ||
| 70 | int instanceCount = 10000, | ||
| 71 | InterrupterT* interrupter = nullptr); | ||
| 72 | |||
| 73 | |||
| 74 | //////////////////////////////////////// | ||
| 75 | |||
| 76 | |||
| 77 | /// @brief Accelerated closest surface point queries for narrow band level sets | ||
| 78 | /// @details Supports queries that originate at arbitrary world-space locations, | ||
| 79 | /// is not confined to the narrow band region of the input volume geometry. | ||
| 80 | template<typename GridT> | ||
| 81 | class ClosestSurfacePoint | ||
| 82 | { | ||
| 83 | public: | ||
| 84 | using Ptr = std::unique_ptr<ClosestSurfacePoint>; | ||
| 85 | using TreeT = typename GridT::TreeType; | ||
| 86 | using BoolTreeT = typename TreeT::template ValueConverter<bool>::Type; | ||
| 87 | using Index32TreeT = typename TreeT::template ValueConverter<Index32>::Type; | ||
| 88 | using Int16TreeT = typename TreeT::template ValueConverter<Int16>::Type; | ||
| 89 | |||
| 90 | /// @brief Extract surface points and construct a spatial acceleration structure. | ||
| 91 | /// | ||
| 92 | /// @return a null pointer if the initialization fails for any reason, | ||
| 93 | /// otherwise a unique pointer to a newly-allocated ClosestSurfacePoint object. | ||
| 94 | /// | ||
| 95 | /// @param grid a scalar level set or fog volume | ||
| 96 | /// @param isovalue the voxel value that determines the surface of the volume | ||
| 97 | /// The default value of zero works for signed distance fields, | ||
| 98 | /// while fog volumes require a larger positive value | ||
| 99 | /// (0.5 is a good initial guess). | ||
| 100 | /// @param interrupter pointer to an object adhering to the util::NullInterrupter interface. | ||
| 101 | template<typename InterrupterT = util::NullInterrupter> | ||
| 102 | static Ptr create(const GridT& grid, float isovalue = 0.0, | ||
| 103 | InterrupterT* interrupter = nullptr); | ||
| 104 | |||
| 105 | /// @brief Compute the distance from each input point to its closest surface point. | ||
| 106 | /// @param points input list of points in world space | ||
| 107 | /// @param distances output list of closest surface point distances | ||
| 108 | bool search(const std::vector<Vec3R>& points, std::vector<float>& distances); | ||
| 109 | |||
| 110 | /// @brief Overwrite each input point with its closest surface point. | ||
| 111 | /// @param points input/output list of points in world space | ||
| 112 | /// @param distances output list of closest surface point distances | ||
| 113 | bool searchAndReplace(std::vector<Vec3R>& points, std::vector<float>& distances); | ||
| 114 | |||
| 115 | /// @brief Tree accessor | ||
| 116 | ✗ | const Index32TreeT& indexTree() const { return *mIdxTreePt; } | |
| 117 | /// @brief Tree accessor | ||
| 118 | ✗ | const Int16TreeT& signTree() const { return *mSignTreePt; } | |
| 119 | |||
| 120 | private: | ||
| 121 | using Index32LeafT = typename Index32TreeT::LeafNodeType; | ||
| 122 | using IndexRange = std::pair<size_t, size_t>; | ||
| 123 | |||
| 124 | std::vector<Vec4R> mLeafBoundingSpheres, mNodeBoundingSpheres; | ||
| 125 | std::vector<IndexRange> mLeafRanges; | ||
| 126 | std::vector<const Index32LeafT*> mLeafNodes; | ||
| 127 | PointList mSurfacePointList; | ||
| 128 | size_t mPointListSize = 0, mMaxNodeLeafs = 0; | ||
| 129 | typename Index32TreeT::Ptr mIdxTreePt; | ||
| 130 | typename Int16TreeT::Ptr mSignTreePt; | ||
| 131 | |||
| 132 |
1/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
12 | ClosestSurfacePoint() = default; |
| 133 | template<typename InterrupterT = util::NullInterrupter> | ||
| 134 | bool initialize(const GridT&, float isovalue, InterrupterT*); | ||
| 135 | bool search(std::vector<Vec3R>&, std::vector<float>&, bool transformPoints); | ||
| 136 | }; | ||
| 137 | |||
| 138 | |||
| 139 | //////////////////////////////////////// | ||
| 140 | |||
| 141 | |||
| 142 | // Internal utility methods | ||
| 143 | |||
| 144 | /// @cond OPENVDB_DOCS_INTERNAL | ||
| 145 | |||
| 146 | namespace v2s_internal { | ||
| 147 | |||
| 148 | struct PointAccessor | ||
| 149 | { | ||
| 150 | PointAccessor(std::vector<Vec3R>& points) | ||
| 151 | 10 | : mPoints(points) | |
| 152 | { | ||
| 153 | } | ||
| 154 | |||
| 155 | void add(const Vec3R &pos) | ||
| 156 | { | ||
| 157 | 65000 | mPoints.push_back(pos); | |
| 158 | } | ||
| 159 | private: | ||
| 160 | std::vector<Vec3R>& mPoints; | ||
| 161 | }; | ||
| 162 | |||
| 163 | |||
| 164 | template<typename Index32LeafT> | ||
| 165 | class LeafOp | ||
| 166 | { | ||
| 167 | public: | ||
| 168 | |||
| 169 | LeafOp(std::vector<Vec4R>& leafBoundingSpheres, | ||
| 170 | const std::vector<const Index32LeafT*>& leafNodes, | ||
| 171 | const math::Transform& transform, | ||
| 172 | const PointList& surfacePointList); | ||
| 173 | |||
| 174 | void run(bool threaded = true); | ||
| 175 | |||
| 176 | |||
| 177 | void operator()(const tbb::blocked_range<size_t>&) const; | ||
| 178 | |||
| 179 | private: | ||
| 180 | std::vector<Vec4R>& mLeafBoundingSpheres; | ||
| 181 | const std::vector<const Index32LeafT*>& mLeafNodes; | ||
| 182 | const math::Transform& mTransform; | ||
| 183 | const PointList& mSurfacePointList; | ||
| 184 | }; | ||
| 185 | |||
| 186 | template<typename Index32LeafT> | ||
| 187 | 12 | LeafOp<Index32LeafT>::LeafOp( | |
| 188 | std::vector<Vec4R>& leafBoundingSpheres, | ||
| 189 | const std::vector<const Index32LeafT*>& leafNodes, | ||
| 190 | const math::Transform& transform, | ||
| 191 | const PointList& surfacePointList) | ||
| 192 | : mLeafBoundingSpheres(leafBoundingSpheres) | ||
| 193 | , mLeafNodes(leafNodes) | ||
| 194 | , mTransform(transform) | ||
| 195 | 12 | , mSurfacePointList(surfacePointList) | |
| 196 | { | ||
| 197 | 12 | } | |
| 198 | |||
| 199 | template<typename Index32LeafT> | ||
| 200 | void | ||
| 201 | 12 | LeafOp<Index32LeafT>::run(bool threaded) | |
| 202 | { | ||
| 203 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (threaded) { |
| 204 | 12 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mLeafNodes.size()), *this); | |
| 205 | } else { | ||
| 206 | ✗ | (*this)(tbb::blocked_range<size_t>(0, mLeafNodes.size())); | |
| 207 | } | ||
| 208 | 12 | } | |
| 209 | |||
| 210 | template<typename Index32LeafT> | ||
| 211 | void | ||
| 212 | 776 | LeafOp<Index32LeafT>::operator()(const tbb::blocked_range<size_t>& range) const | |
| 213 | { | ||
| 214 | typename Index32LeafT::ValueOnCIter iter; | ||
| 215 | Vec3s avg; | ||
| 216 | |||
| 217 |
2/2✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 776 times.
|
2130 | for (size_t n = range.begin(); n != range.end(); ++n) { |
| 218 | 1354 | avg[0] = 0.0; | |
| 219 | 1354 | avg[1] = 0.0; | |
| 220 | 1354 | avg[2] = 0.0; | |
| 221 | |||
| 222 | int count = 0; | ||
| 223 |
2/2✓ Branch 1 taken 86624 times.
✓ Branch 2 taken 1354 times.
|
89332 | for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { |
| 224 | 86624 | avg += mSurfacePointList[iter.getValue()]; | |
| 225 | 86624 | ++count; | |
| 226 | } | ||
| 227 |
2/2✓ Branch 0 taken 1346 times.
✓ Branch 1 taken 8 times.
|
1354 | if (count > 1) avg *= float(1.0 / double(count)); |
| 228 | |||
| 229 | float maxDist = 0.0; | ||
| 230 |
2/2✓ Branch 1 taken 86624 times.
✓ Branch 2 taken 1354 times.
|
89332 | for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { |
| 231 |
2/2✓ Branch 1 taken 3726 times.
✓ Branch 2 taken 82898 times.
|
86624 | float tmpDist = (mSurfacePointList[iter.getValue()] - avg).lengthSqr(); |
| 232 |
2/2✓ Branch 0 taken 3726 times.
✓ Branch 1 taken 82898 times.
|
86624 | if (tmpDist > maxDist) maxDist = tmpDist; |
| 233 | } | ||
| 234 | |||
| 235 | 1354 | Vec4R& sphere = mLeafBoundingSpheres[n]; | |
| 236 | 1354 | sphere[0] = avg[0]; | |
| 237 | 1354 | sphere[1] = avg[1]; | |
| 238 | 1354 | sphere[2] = avg[2]; | |
| 239 | 1354 | sphere[3] = maxDist * 2.0; // padded radius | |
| 240 | } | ||
| 241 | 776 | } | |
| 242 | |||
| 243 | |||
| 244 | class NodeOp | ||
| 245 | { | ||
| 246 | public: | ||
| 247 | using IndexRange = std::pair<size_t, size_t>; | ||
| 248 | |||
| 249 | NodeOp(std::vector<Vec4R>& nodeBoundingSpheres, | ||
| 250 | const std::vector<IndexRange>& leafRanges, | ||
| 251 | const std::vector<Vec4R>& leafBoundingSpheres); | ||
| 252 | |||
| 253 | inline void run(bool threaded = true); | ||
| 254 | |||
| 255 | inline void operator()(const tbb::blocked_range<size_t>&) const; | ||
| 256 | |||
| 257 | private: | ||
| 258 | std::vector<Vec4R>& mNodeBoundingSpheres; | ||
| 259 | const std::vector<IndexRange>& mLeafRanges; | ||
| 260 | const std::vector<Vec4R>& mLeafBoundingSpheres; | ||
| 261 | }; | ||
| 262 | |||
| 263 | inline | ||
| 264 | NodeOp::NodeOp(std::vector<Vec4R>& nodeBoundingSpheres, | ||
| 265 | const std::vector<IndexRange>& leafRanges, | ||
| 266 | 12 | const std::vector<Vec4R>& leafBoundingSpheres) | |
| 267 | : mNodeBoundingSpheres(nodeBoundingSpheres) | ||
| 268 | , mLeafRanges(leafRanges) | ||
| 269 | 12 | , mLeafBoundingSpheres(leafBoundingSpheres) | |
| 270 | { | ||
| 271 | } | ||
| 272 | |||
| 273 | inline void | ||
| 274 | 12 | NodeOp::run(bool threaded) | |
| 275 | { | ||
| 276 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (threaded) { |
| 277 | 12 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mLeafRanges.size()), *this); | |
| 278 | } else { | ||
| 279 | ✗ | (*this)(tbb::blocked_range<size_t>(0, mLeafRanges.size())); | |
| 280 | } | ||
| 281 | 12 | } | |
| 282 | |||
| 283 | inline void | ||
| 284 | 41 | NodeOp::operator()(const tbb::blocked_range<size_t>& range) const | |
| 285 | { | ||
| 286 | Vec3d avg, pos; | ||
| 287 | |||
| 288 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 41 times.
|
82 | for (size_t n = range.begin(); n != range.end(); ++n) { |
| 289 | |||
| 290 | 41 | avg[0] = 0.0; | |
| 291 | 41 | avg[1] = 0.0; | |
| 292 | 41 | avg[2] = 0.0; | |
| 293 | |||
| 294 | 41 | int count = int(mLeafRanges[n].second) - int(mLeafRanges[n].first); | |
| 295 | |||
| 296 |
2/2✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 41 times.
|
1395 | for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { |
| 297 | 1354 | avg[0] += mLeafBoundingSpheres[i][0]; | |
| 298 | 1354 | avg[1] += mLeafBoundingSpheres[i][1]; | |
| 299 | 1354 | avg[2] += mLeafBoundingSpheres[i][2]; | |
| 300 | } | ||
| 301 | |||
| 302 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 8 times.
|
41 | if (count > 1) avg *= float(1.0 / double(count)); |
| 303 | |||
| 304 | |||
| 305 | double maxDist = 0.0; | ||
| 306 | |||
| 307 |
2/2✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 41 times.
|
1395 | for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { |
| 308 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 1267 times.
|
1354 | pos[0] = mLeafBoundingSpheres[i][0]; |
| 309 | pos[1] = mLeafBoundingSpheres[i][1]; | ||
| 310 | pos[2] = mLeafBoundingSpheres[i][2]; | ||
| 311 | const auto radiusSqr = mLeafBoundingSpheres[i][3]; | ||
| 312 | |||
| 313 | 1354 | double tmpDist = (pos - avg).lengthSqr() + radiusSqr; | |
| 314 |
2/2✓ Branch 0 taken 87 times.
✓ Branch 1 taken 1267 times.
|
1354 | if (tmpDist > maxDist) maxDist = tmpDist; |
| 315 | } | ||
| 316 | |||
| 317 | 41 | Vec4R& sphere = mNodeBoundingSpheres[n]; | |
| 318 | |||
| 319 | 41 | sphere[0] = avg[0]; | |
| 320 | 41 | sphere[1] = avg[1]; | |
| 321 | 41 | sphere[2] = avg[2]; | |
| 322 | 41 | sphere[3] = maxDist * 2.0; // padded radius | |
| 323 | } | ||
| 324 | 41 | } | |
| 325 | |||
| 326 | |||
| 327 | //////////////////////////////////////// | ||
| 328 | |||
| 329 | |||
| 330 | template<typename Index32LeafT> | ||
| 331 | class ClosestPointDist | ||
| 332 | { | ||
| 333 | public: | ||
| 334 | using IndexRange = std::pair<size_t, size_t>; | ||
| 335 | |||
| 336 | ClosestPointDist( | ||
| 337 | std::vector<Vec3R>& instancePoints, | ||
| 338 | std::vector<float>& instanceDistances, | ||
| 339 | const PointList& surfacePointList, | ||
| 340 | const std::vector<const Index32LeafT*>& leafNodes, | ||
| 341 | const std::vector<IndexRange>& leafRanges, | ||
| 342 | const std::vector<Vec4R>& leafBoundingSpheres, | ||
| 343 | const std::vector<Vec4R>& nodeBoundingSpheres, | ||
| 344 | size_t maxNodeLeafs, | ||
| 345 | bool transformPoints = false); | ||
| 346 | |||
| 347 | |||
| 348 | void run(bool threaded = true); | ||
| 349 | |||
| 350 | |||
| 351 | void operator()(const tbb::blocked_range<size_t>&) const; | ||
| 352 | |||
| 353 | private: | ||
| 354 | |||
| 355 | void evalLeaf(size_t index, const Index32LeafT& leaf) const; | ||
| 356 | void evalNode(size_t pointIndex, size_t nodeIndex) const; | ||
| 357 | |||
| 358 | |||
| 359 | std::vector<Vec3R>& mInstancePoints; | ||
| 360 | std::vector<float>& mInstanceDistances; | ||
| 361 | |||
| 362 | const PointList& mSurfacePointList; | ||
| 363 | |||
| 364 | const std::vector<const Index32LeafT*>& mLeafNodes; | ||
| 365 | const std::vector<IndexRange>& mLeafRanges; | ||
| 366 | const std::vector<Vec4R>& mLeafBoundingSpheres; | ||
| 367 | const std::vector<Vec4R>& mNodeBoundingSpheres; | ||
| 368 | |||
| 369 | std::vector<float> mLeafDistances, mNodeDistances; | ||
| 370 | |||
| 371 | const bool mTransformPoints; | ||
| 372 | size_t mClosestPointIndex; | ||
| 373 | };// ClosestPointDist | ||
| 374 | |||
| 375 | |||
| 376 | template<typename Index32LeafT> | ||
| 377 | 14 | ClosestPointDist<Index32LeafT>::ClosestPointDist( | |
| 378 | std::vector<Vec3R>& instancePoints, | ||
| 379 | std::vector<float>& instanceDistances, | ||
| 380 | const PointList& surfacePointList, | ||
| 381 | const std::vector<const Index32LeafT*>& leafNodes, | ||
| 382 | const std::vector<IndexRange>& leafRanges, | ||
| 383 | const std::vector<Vec4R>& leafBoundingSpheres, | ||
| 384 | const std::vector<Vec4R>& nodeBoundingSpheres, | ||
| 385 | size_t maxNodeLeafs, | ||
| 386 | bool transformPoints) | ||
| 387 | : mInstancePoints(instancePoints) | ||
| 388 | , mInstanceDistances(instanceDistances) | ||
| 389 | , mSurfacePointList(surfacePointList) | ||
| 390 | , mLeafNodes(leafNodes) | ||
| 391 | , mLeafRanges(leafRanges) | ||
| 392 | , mLeafBoundingSpheres(leafBoundingSpheres) | ||
| 393 | , mNodeBoundingSpheres(nodeBoundingSpheres) | ||
| 394 | 28 | , mLeafDistances(maxNodeLeafs, 0.0) | |
| 395 | 28 | , mNodeDistances(leafRanges.size(), 0.0) | |
| 396 | , mTransformPoints(transformPoints) | ||
| 397 |
1/4✓ Branch 2 taken 14 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
14 | , mClosestPointIndex(0) |
| 398 | { | ||
| 399 | 14 | } | |
| 400 | |||
| 401 | |||
| 402 | template<typename Index32LeafT> | ||
| 403 | void | ||
| 404 | 14 | ClosestPointDist<Index32LeafT>::run(bool threaded) | |
| 405 | { | ||
| 406 |
1/2✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
|
14 | if (threaded) { |
| 407 | 14 | tbb::parallel_for(tbb::blocked_range<size_t>(0, mInstancePoints.size()), *this); | |
| 408 | } else { | ||
| 409 | ✗ | (*this)(tbb::blocked_range<size_t>(0, mInstancePoints.size())); | |
| 410 | } | ||
| 411 | 14 | } | |
| 412 | |||
| 413 | template<typename Index32LeafT> | ||
| 414 | void | ||
| 415 | 365473 | ClosestPointDist<Index32LeafT>::evalLeaf(size_t index, const Index32LeafT& leaf) const | |
| 416 | { | ||
| 417 | typename Index32LeafT::ValueOnCIter iter; | ||
| 418 | 365473 | const Vec3s center = mInstancePoints[index]; | |
| 419 | size_t& closestPointIndex = const_cast<size_t&>(mClosestPointIndex); | ||
| 420 | |||
| 421 |
2/2✓ Branch 0 taken 29458060 times.
✓ Branch 1 taken 365473 times.
|
30189006 | for (iter = leaf.cbeginValueOn(); iter; ++iter) { |
| 422 | |||
| 423 |
2/2✓ Branch 1 taken 1491138 times.
✓ Branch 2 taken 27966922 times.
|
29458060 | const Vec3s& point = mSurfacePointList[iter.getValue()]; |
| 424 | float tmpDist = (point - center).lengthSqr(); | ||
| 425 | |||
| 426 |
2/2✓ Branch 0 taken 1491138 times.
✓ Branch 1 taken 27966922 times.
|
29458060 | if (tmpDist < mInstanceDistances[index]) { |
| 427 | 1491138 | mInstanceDistances[index] = tmpDist; | |
| 428 | 1491138 | closestPointIndex = iter.getValue(); | |
| 429 | } | ||
| 430 | } | ||
| 431 | 365473 | } | |
| 432 | |||
| 433 | |||
| 434 | template<typename Index32LeafT> | ||
| 435 | void | ||
| 436 | 129462 | ClosestPointDist<Index32LeafT>::evalNode(size_t pointIndex, size_t nodeIndex) const | |
| 437 | { | ||
| 438 |
2/2✓ Branch 0 taken 124462 times.
✓ Branch 1 taken 5000 times.
|
129462 | if (nodeIndex >= mLeafRanges.size()) return; |
| 439 | |||
| 440 | 124462 | const Vec3R& pos = mInstancePoints[pointIndex]; | |
| 441 | 124462 | float minDist = mInstanceDistances[pointIndex]; | |
| 442 | size_t minDistIdx = 0; | ||
| 443 | Vec3R center; | ||
| 444 | bool updatedDist = false; | ||
| 445 | |||
| 446 | 4681982 | for (size_t i = mLeafRanges[nodeIndex].first, n = 0; | |
| 447 |
2/2✓ Branch 0 taken 4557520 times.
✓ Branch 1 taken 124462 times.
|
4681982 | i < mLeafRanges[nodeIndex].second; ++i, ++n) |
| 448 | { | ||
| 449 | float& distToLeaf = const_cast<float&>(mLeafDistances[n]); | ||
| 450 | |||
| 451 |
2/2✓ Branch 0 taken 4395390 times.
✓ Branch 1 taken 162130 times.
|
4557520 | center[0] = mLeafBoundingSpheres[i][0]; |
| 452 | center[1] = mLeafBoundingSpheres[i][1]; | ||
| 453 | center[2] = mLeafBoundingSpheres[i][2]; | ||
| 454 | const auto radiusSqr = mLeafBoundingSpheres[i][3]; | ||
| 455 | |||
| 456 |
2/2✓ Branch 0 taken 4395390 times.
✓ Branch 1 taken 162130 times.
|
4557520 | distToLeaf = float(std::max(0.0, (pos - center).lengthSqr() - radiusSqr)); |
| 457 | |||
| 458 |
2/2✓ Branch 0 taken 454835 times.
✓ Branch 1 taken 4102685 times.
|
4557520 | if (distToLeaf < minDist) { |
| 459 | minDist = distToLeaf; | ||
| 460 | minDistIdx = i; | ||
| 461 | updatedDist = true; | ||
| 462 | } | ||
| 463 | } | ||
| 464 | |||
| 465 |
2/2✓ Branch 0 taken 99817 times.
✓ Branch 1 taken 24645 times.
|
124462 | if (!updatedDist) return; |
| 466 | |||
| 467 | 99817 | evalLeaf(pointIndex, *mLeafNodes[minDistIdx]); | |
| 468 | |||
| 469 | 3866781 | for (size_t i = mLeafRanges[nodeIndex].first, n = 0; | |
| 470 |
2/2✓ Branch 0 taken 3766964 times.
✓ Branch 1 taken 99817 times.
|
3866781 | i < mLeafRanges[nodeIndex].second; ++i, ++n) |
| 471 | { | ||
| 472 |
4/4✓ Branch 0 taken 359240 times.
✓ Branch 1 taken 3407724 times.
✓ Branch 2 taken 265656 times.
✓ Branch 3 taken 93584 times.
|
3766964 | if (mLeafDistances[n] < mInstanceDistances[pointIndex] && i != minDistIdx) { |
| 473 | 265656 | evalLeaf(pointIndex, *mLeafNodes[i]); | |
| 474 | } | ||
| 475 | } | ||
| 476 | } | ||
| 477 | |||
| 478 | |||
| 479 | template<typename Index32LeafT> | ||
| 480 | void | ||
| 481 | 1369 | ClosestPointDist<Index32LeafT>::operator()(const tbb::blocked_range<size_t>& range) const | |
| 482 | { | ||
| 483 | Vec3R center; | ||
| 484 |
2/2✓ Branch 0 taken 65938 times.
✓ Branch 1 taken 1369 times.
|
67307 | for (size_t n = range.begin(); n != range.end(); ++n) { |
| 485 | |||
| 486 | 65938 | const Vec3R& pos = mInstancePoints[n]; | |
| 487 | 65938 | float minDist = mInstanceDistances[n]; | |
| 488 | size_t minDistIdx = 0; | ||
| 489 | |||
| 490 |
2/2✓ Branch 0 taken 231064 times.
✓ Branch 1 taken 65938 times.
|
297002 | for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { |
| 491 | float& distToNode = const_cast<float&>(mNodeDistances[i]); | ||
| 492 | |||
| 493 |
2/2✓ Branch 0 taken 118733 times.
✓ Branch 1 taken 112331 times.
|
231064 | center[0] = mNodeBoundingSpheres[i][0]; |
| 494 | center[1] = mNodeBoundingSpheres[i][1]; | ||
| 495 | center[2] = mNodeBoundingSpheres[i][2]; | ||
| 496 | const auto radiusSqr = mNodeBoundingSpheres[i][3]; | ||
| 497 | |||
| 498 |
2/2✓ Branch 0 taken 118733 times.
✓ Branch 1 taken 112331 times.
|
231064 | distToNode = float(std::max(0.0, (pos - center).lengthSqr() - radiusSqr)); |
| 499 | |||
| 500 |
2/2✓ Branch 0 taken 127797 times.
✓ Branch 1 taken 103267 times.
|
231064 | if (distToNode < minDist) { |
| 501 | minDist = distToNode; | ||
| 502 | minDistIdx = i; | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | 65938 | evalNode(n, minDistIdx); | |
| 507 | |||
| 508 |
2/2✓ Branch 0 taken 231064 times.
✓ Branch 1 taken 65938 times.
|
297002 | for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { |
| 509 |
4/4✓ Branch 0 taken 124462 times.
✓ Branch 1 taken 106602 times.
✓ Branch 2 taken 63524 times.
✓ Branch 3 taken 60938 times.
|
231064 | if (mNodeDistances[i] < mInstanceDistances[n] && i != minDistIdx) { |
| 510 | 63524 | evalNode(n, i); | |
| 511 | } | ||
| 512 | } | ||
| 513 | |||
| 514 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 65920 times.
|
65938 | mInstanceDistances[n] = std::sqrt(mInstanceDistances[n]); |
| 515 | |||
| 516 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 65920 times.
|
65938 | if (mTransformPoints) mInstancePoints[n] = mSurfacePointList[mClosestPointIndex]; |
| 517 | } | ||
| 518 | 1369 | } | |
| 519 | |||
| 520 | |||
| 521 | class UpdatePoints | ||
| 522 | { | ||
| 523 | public: | ||
| 524 | UpdatePoints( | ||
| 525 | const Vec4s& sphere, | ||
| 526 | const std::vector<Vec3R>& points, | ||
| 527 | std::vector<float>& distances, | ||
| 528 | std::vector<unsigned char>& mask, | ||
| 529 | bool overlapping); | ||
| 530 | |||
| 531 | 43 | float radius() const { return mRadius; } | |
| 532 | 43 | int index() const { return mIndex; } | |
| 533 | |||
| 534 | inline void run(bool threaded = true); | ||
| 535 | |||
| 536 | |||
| 537 | UpdatePoints(UpdatePoints&, tbb::split); | ||
| 538 | inline void operator()(const tbb::blocked_range<size_t>& range); | ||
| 539 | void join(const UpdatePoints& rhs) | ||
| 540 | { | ||
| 541 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 161 times.
|
162 | if (rhs.mRadius > mRadius) { |
| 542 | 1 | mRadius = rhs.mRadius; | |
| 543 | 1 | mIndex = rhs.mIndex; | |
| 544 | } | ||
| 545 | } | ||
| 546 | |||
| 547 | private: | ||
| 548 | const Vec4s& mSphere; | ||
| 549 | const std::vector<Vec3R>& mPoints; | ||
| 550 | std::vector<float>& mDistances; | ||
| 551 | std::vector<unsigned char>& mMask; | ||
| 552 | bool mOverlapping; | ||
| 553 | float mRadius; | ||
| 554 | int mIndex; | ||
| 555 | }; | ||
| 556 | |||
| 557 | inline | ||
| 558 | UpdatePoints::UpdatePoints( | ||
| 559 | const Vec4s& sphere, | ||
| 560 | const std::vector<Vec3R>& points, | ||
| 561 | std::vector<float>& distances, | ||
| 562 | std::vector<unsigned char>& mask, | ||
| 563 | 43 | bool overlapping) | |
| 564 | : mSphere(sphere) | ||
| 565 | , mPoints(points) | ||
| 566 | , mDistances(distances) | ||
| 567 | , mMask(mask) | ||
| 568 | , mOverlapping(overlapping) | ||
| 569 | , mRadius(0.0) | ||
| 570 | 43 | , mIndex(0) | |
| 571 | { | ||
| 572 | } | ||
| 573 | |||
| 574 | inline | ||
| 575 | 162 | UpdatePoints::UpdatePoints(UpdatePoints& rhs, tbb::split) | |
| 576 | 162 | : mSphere(rhs.mSphere) | |
| 577 | 162 | , mPoints(rhs.mPoints) | |
| 578 | 162 | , mDistances(rhs.mDistances) | |
| 579 | 162 | , mMask(rhs.mMask) | |
| 580 | 162 | , mOverlapping(rhs.mOverlapping) | |
| 581 | 162 | , mRadius(rhs.mRadius) | |
| 582 | 162 | , mIndex(rhs.mIndex) | |
| 583 | { | ||
| 584 | } | ||
| 585 | |||
| 586 | inline void | ||
| 587 | 43 | UpdatePoints::run(bool threaded) | |
| 588 | { | ||
| 589 |
1/2✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
|
43 | if (threaded) { |
| 590 | 43 | tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mPoints.size()), *this); | |
| 591 | } else { | ||
| 592 | ✗ | (*this)(tbb::blocked_range<size_t>(0, mPoints.size())); | |
| 593 | } | ||
| 594 | 43 | } | |
| 595 | |||
| 596 | inline void | ||
| 597 | 7064 | UpdatePoints::operator()(const tbb::blocked_range<size_t>& range) | |
| 598 | { | ||
| 599 | Vec3s pos; | ||
| 600 |
2/2✓ Branch 0 taken 242820 times.
✓ Branch 1 taken 7064 times.
|
249884 | for (size_t n = range.begin(); n != range.end(); ++n) { |
| 601 |
2/2✓ Branch 0 taken 172342 times.
✓ Branch 1 taken 70478 times.
|
242820 | if (mMask[n]) continue; |
| 602 | |||
| 603 |
2/2✓ Branch 0 taken 59569 times.
✓ Branch 1 taken 10909 times.
|
70478 | pos.x() = float(mPoints[n].x()) - mSphere[0]; |
| 604 |
2/2✓ Branch 0 taken 59569 times.
✓ Branch 1 taken 10909 times.
|
70478 | pos.y() = float(mPoints[n].y()) - mSphere[1]; |
| 605 |
2/2✓ Branch 0 taken 59569 times.
✓ Branch 1 taken 10909 times.
|
70478 | pos.z() = float(mPoints[n].z()) - mSphere[2]; |
| 606 | |||
| 607 | float dist = pos.length(); | ||
| 608 | |||
| 609 |
2/2✓ Branch 0 taken 59569 times.
✓ Branch 1 taken 10909 times.
|
70478 | if (dist < mSphere[3]) { |
| 610 | 59569 | mMask[n] = 1; | |
| 611 | 59569 | continue; | |
| 612 | } | ||
| 613 | |||
| 614 |
2/2✓ Branch 0 taken 5713 times.
✓ Branch 1 taken 5196 times.
|
10909 | if (!mOverlapping) { |
| 615 |
2/2✓ Branch 0 taken 1570 times.
✓ Branch 1 taken 4143 times.
|
7283 | mDistances[n] = std::min(mDistances[n], (dist - mSphere[3])); |
| 616 | } | ||
| 617 | |||
| 618 |
2/2✓ Branch 0 taken 287 times.
✓ Branch 1 taken 10622 times.
|
10909 | if (mDistances[n] > mRadius) { |
| 619 | 287 | mRadius = mDistances[n]; | |
| 620 | 287 | mIndex = int(n); | |
| 621 | } | ||
| 622 | } | ||
| 623 | 7064 | } | |
| 624 | |||
| 625 | |||
| 626 | } // namespace v2s_internal | ||
| 627 | |||
| 628 | /// @endcond | ||
| 629 | |||
| 630 | //////////////////////////////////////// | ||
| 631 | |||
| 632 | |||
| 633 | template<typename GridT, typename InterrupterT> | ||
| 634 | void | ||
| 635 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
|
20 | fillWithSpheres( |
| 636 | const GridT& grid, | ||
| 637 | std::vector<openvdb::Vec4s>& spheres, | ||
| 638 | const Vec2i& sphereCount, | ||
| 639 | bool overlapping, | ||
| 640 | float minRadius, | ||
| 641 | float maxRadius, | ||
| 642 | float isovalue, | ||
| 643 | int instanceCount, | ||
| 644 | InterrupterT* interrupter) | ||
| 645 | { | ||
| 646 | spheres.clear(); | ||
| 647 | |||
| 648 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | if (grid.empty()) return; |
| 649 | |||
| 650 | const int | ||
| 651 | minSphereCount = sphereCount[0], | ||
| 652 | 20 | maxSphereCount = sphereCount[1]; | |
| 653 |
2/4✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
|
20 | if ((minSphereCount > maxSphereCount) || (maxSphereCount < 1)) { |
| 654 | ✗ | OPENVDB_LOG_WARN("fillWithSpheres: minimum sphere count (" | |
| 655 | << minSphereCount << ") exceeds maximum count (" << maxSphereCount << ")"); | ||
| 656 | ✗ | return; | |
| 657 | } | ||
| 658 | 20 | spheres.reserve(maxSphereCount); | |
| 659 | |||
| 660 | 20 | auto gridPtr = grid.copy(); // shallow copy | |
| 661 | |||
| 662 |
3/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 3 times.
|
20 | if (gridPtr->getGridClass() == GRID_LEVEL_SET) { |
| 663 | // Clamp the isovalue to the level set's background value minus epsilon. | ||
| 664 | // (In a valid narrow-band level set, all voxels, including background voxels, | ||
| 665 | // have values less than or equal to the background value, so an isovalue | ||
| 666 | // greater than or equal to the background value would produce a mask with | ||
| 667 | // effectively infinite extent.) | ||
| 668 | 14 | isovalue = std::min(isovalue, | |
| 669 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 6 times.
|
16 | static_cast<float>(gridPtr->background() - math::Tolerance<float>::value())); |
| 670 |
3/4✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 1 times.
|
6 | } else if (gridPtr->getGridClass() == GRID_FOG_VOLUME) { |
| 671 | // Clamp the isovalue of a fog volume between epsilon and one, | ||
| 672 | // again to avoid a mask with infinite extent. (Recall that | ||
| 673 | // fog volume voxel values vary from zero outside to one inside.) | ||
| 674 |
1/2✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
|
8 | isovalue = math::Clamp(isovalue, math::Tolerance<float>::value(), 1.f); |
| 675 | } | ||
| 676 | |||
| 677 | // ClosestSurfacePoint is inaccurate for small grids. | ||
| 678 | // Resample the input grid if it is too small. | ||
| 679 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | auto numVoxels = gridPtr->activeVoxelCount(); |
| 680 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
20 | if (numVoxels < 10000) { |
| 681 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | const auto scale = 1.0 / math::Cbrt(2.0 * 10000.0 / double(numVoxels)); |
| 682 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | auto scaledXform = gridPtr->transform().copy(); |
| 683 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | scaledXform->preScale(scale); |
| 684 | |||
| 685 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | auto newGridPtr = levelSetRebuild(*gridPtr, isovalue, |
| 686 | LEVEL_SET_HALF_WIDTH, LEVEL_SET_HALF_WIDTH, scaledXform.get(), interrupter); | ||
| 687 | |||
| 688 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | const auto newNumVoxels = newGridPtr->activeVoxelCount(); |
| 689 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 1 times.
|
10 | if (newNumVoxels > numVoxels) { |
| 690 | OPENVDB_LOG_DEBUG_RUNTIME("fillWithSpheres: resampled input grid from " | ||
| 691 | << numVoxels << " voxel" << (numVoxels == 1 ? "" : "s") | ||
| 692 | << " to " << newNumVoxels << " voxel" << (newNumVoxels == 1 ? "" : "s")); | ||
| 693 | gridPtr = newGridPtr; | ||
| 694 | numVoxels = newNumVoxels; | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | const bool addNarrowBandPoints = (numVoxels < 10000); | ||
| 699 | 20 | int instances = std::max(instanceCount, maxSphereCount); | |
| 700 | |||
| 701 | using TreeT = typename GridT::TreeType; | ||
| 702 | using BoolTreeT = typename TreeT::template ValueConverter<bool>::Type; | ||
| 703 | using Int16TreeT = typename TreeT::template ValueConverter<Int16>::Type; | ||
| 704 | |||
| 705 | using RandGen = std::mersenne_twister_engine<uint32_t, 32, 351, 175, 19, | ||
| 706 | 0xccab8ee7, 11, 0xffffffff, 7, 0x31b6ab00, 15, 0xffe50000, 17, 1812433253>; // mt11213b | ||
| 707 | RandGen mtRand(/*seed=*/0); | ||
| 708 | |||
| 709 | const TreeT& tree = gridPtr->tree(); | ||
| 710 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | math::Transform transform = gridPtr->transform(); |
| 711 | |||
| 712 | std::vector<Vec3R> instancePoints; | ||
| 713 | { | ||
| 714 | // Compute a mask of the voxels enclosed by the isosurface. | ||
| 715 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | typename Grid<BoolTreeT>::Ptr interiorMaskPtr; |
| 716 |
3/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 7 times.
✓ Branch 4 taken 3 times.
|
20 | if (gridPtr->getGridClass() == GRID_LEVEL_SET) { |
| 717 |
1/2✓ Branch 1 taken 7 times.
✗ Branch 2 not taken.
|
14 | interiorMaskPtr = sdfInteriorMask(*gridPtr, isovalue); |
| 718 | } else { | ||
| 719 | // For non-level-set grids, the interior mask comprises the active voxels. | ||
| 720 |
1/2✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
|
6 | interiorMaskPtr = typename Grid<BoolTreeT>::Ptr(Grid<BoolTreeT>::create(false)); |
| 721 |
3/8✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 3 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 3 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
|
12 | interiorMaskPtr->setTransform(transform.copy()); |
| 722 | interiorMaskPtr->tree().topologyUnion(tree); | ||
| 723 | } | ||
| 724 | |||
| 725 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
20 | if (interrupter && interrupter->wasInterrupted()) return; |
| 726 | |||
| 727 | // If the interior mask is small and eroding it results in an empty grid, | ||
| 728 | // use the uneroded mask instead. (But if the minimum sphere count is zero, | ||
| 729 | // then eroding away the mask is acceptable.) | ||
| 730 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
20 | if (!addNarrowBandPoints || (minSphereCount <= 0)) { |
| 731 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | tools::erodeActiveValues(interiorMaskPtr->tree(), /*iterations=*/1, tools::NN_FACE, tools::IGNORE_TILES); |
| 732 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | tools::pruneInactive(interiorMaskPtr->tree()); |
| 733 | } else { | ||
| 734 | auto& maskTree = interiorMaskPtr->tree(); | ||
| 735 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | auto copyOfTree = StaticPtrCast<BoolTreeT>(maskTree.copy()); |
| 736 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | tools::erodeActiveValues(maskTree, /*iterations=*/1, tools::NN_FACE, tools::IGNORE_TILES); |
| 737 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
10 | tools::pruneInactive(maskTree); |
| 738 |
3/6✓ Branch 0 taken 1 times.
✓ Branch 1 taken 4 times.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
|
12 | if (maskTree.empty()) { interiorMaskPtr->setTree(copyOfTree); } |
| 739 | } | ||
| 740 | |||
| 741 | // Scatter candidate sphere centroids (instancePoints) | ||
| 742 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | instancePoints.reserve(instances); |
| 743 | v2s_internal::PointAccessor ptnAcc(instancePoints); | ||
| 744 | |||
| 745 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
20 | const auto scatterCount = Index64(addNarrowBandPoints ? (instances / 2) : instances); |
| 746 | |||
| 747 | UniformPointScatter<v2s_internal::PointAccessor, RandGen, InterrupterT> scatter( | ||
| 748 | ptnAcc, scatterCount, mtRand, 1.0, interrupter); | ||
| 749 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | scatter(*interiorMaskPtr); |
| 750 | } | ||
| 751 | |||
| 752 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
20 | if (interrupter && interrupter->wasInterrupted()) return; |
| 753 | |||
| 754 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
40 | auto csp = ClosestSurfacePoint<GridT>::create(*gridPtr, isovalue, interrupter); |
| 755 |
1/2✓ Branch 0 taken 10 times.
✗ Branch 1 not taken.
|
20 | if (!csp) return; |
| 756 | |||
| 757 | // Add extra instance points in the interior narrow band. | ||
| 758 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
20 | if (instancePoints.size() < size_t(instances)) { |
| 759 | const Int16TreeT& signTree = csp->signTree(); | ||
| 760 |
2/2✓ Branch 0 taken 136 times.
✓ Branch 1 taken 6 times.
|
284 | for (auto leafIt = signTree.cbeginLeaf(); leafIt; ++leafIt) { |
| 761 |
2/2✓ Branch 0 taken 8264 times.
✓ Branch 1 taken 136 times.
|
16800 | for (auto it = leafIt->cbeginValueOn(); it; ++it) { |
| 762 |
1/2✓ Branch 1 taken 8264 times.
✗ Branch 2 not taken.
|
16528 | const int flags = int(it.getValue()); |
| 763 |
2/2✓ Branch 0 taken 920 times.
✓ Branch 1 taken 7344 times.
|
16528 | if (!(volume_to_mesh_internal::EDGES & flags) |
| 764 | && (volume_to_mesh_internal::INSIDE & flags)) | ||
| 765 | { | ||
| 766 |
1/2✓ Branch 1 taken 920 times.
✗ Branch 2 not taken.
|
3680 | instancePoints.push_back(transform.indexToWorld(it.getCoord())); |
| 767 | } | ||
| 768 |
1/2✓ Branch 0 taken 8264 times.
✗ Branch 1 not taken.
|
16528 | if (instancePoints.size() == size_t(instances)) break; |
| 769 | } | ||
| 770 |
1/2✓ Branch 0 taken 136 times.
✗ Branch 1 not taken.
|
272 | if (instancePoints.size() == size_t(instances)) break; |
| 771 | } | ||
| 772 | } | ||
| 773 | |||
| 774 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 10 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
20 | if (interrupter && interrupter->wasInterrupted()) return; |
| 775 | |||
| 776 | // Assign a radius to each candidate sphere. The radius is the world-space | ||
| 777 | // distance from the sphere's center to the closest surface point. | ||
| 778 | std::vector<float> instanceRadius; | ||
| 779 |
2/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 10 times.
✗ Branch 4 not taken.
|
20 | if (!csp->search(instancePoints, instanceRadius)) return; |
| 780 | |||
| 781 | 20 | float largestRadius = 0.0; | |
| 782 | int largestRadiusIdx = 0; | ||
| 783 |
2/2✓ Branch 0 taken 65920 times.
✓ Branch 1 taken 10 times.
|
131860 | for (size_t n = 0, N = instancePoints.size(); n < N; ++n) { |
| 784 |
2/2✓ Branch 0 taken 292 times.
✓ Branch 1 taken 65628 times.
|
131840 | if (instanceRadius[n] > largestRadius) { |
| 785 | 584 | largestRadius = instanceRadius[n]; | |
| 786 | 584 | largestRadiusIdx = int(n); | |
| 787 | } | ||
| 788 | } | ||
| 789 | |||
| 790 |
1/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
20 | std::vector<unsigned char> instanceMask(instancePoints.size(), 0); |
| 791 | |||
| 792 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | minRadius = float(minRadius * transform.voxelSize()[0]); |
| 793 |
3/4✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
|
20 | maxRadius = float(maxRadius * transform.voxelSize()[0]); |
| 794 | |||
| 795 |
4/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 9 times.
✓ Branch 2 taken 52 times.
✓ Branch 3 taken 1 times.
|
108 | for (size_t s = 0, S = std::min(size_t(maxSphereCount), instancePoints.size()); s < S; ++s) { |
| 796 | |||
| 797 |
1/8✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
|
104 | if (interrupter && interrupter->wasInterrupted()) return; |
| 798 | |||
| 799 | 104 | largestRadius = std::min(maxRadius, largestRadius); | |
| 800 | |||
| 801 |
3/4✓ Branch 0 taken 9 times.
✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
|
104 | if ((int(s) >= minSphereCount) && (largestRadius < minRadius)) break; |
| 802 | |||
| 803 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
86 | const Vec4s sphere( |
| 804 | float(instancePoints[largestRadiusIdx].x()), | ||
| 805 | float(instancePoints[largestRadiusIdx].y()), | ||
| 806 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
86 | float(instancePoints[largestRadiusIdx].z()), |
| 807 | largestRadius); | ||
| 808 | |||
| 809 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
86 | spheres.push_back(sphere); |
| 810 | 86 | instanceMask[largestRadiusIdx] = 1; | |
| 811 | |||
| 812 | v2s_internal::UpdatePoints op( | ||
| 813 | sphere, instancePoints, instanceRadius, instanceMask, overlapping); | ||
| 814 |
1/2✓ Branch 1 taken 43 times.
✗ Branch 2 not taken.
|
86 | op.run(); |
| 815 | |||
| 816 | 86 | largestRadius = op.radius(); | |
| 817 | largestRadiusIdx = op.index(); | ||
| 818 | } | ||
| 819 | } // fillWithSpheres | ||
| 820 | |||
| 821 | |||
| 822 | //////////////////////////////////////// | ||
| 823 | |||
| 824 | |||
| 825 | template<typename GridT> | ||
| 826 | template<typename InterrupterT> | ||
| 827 | typename ClosestSurfacePoint<GridT>::Ptr | ||
| 828 | 12 | ClosestSurfacePoint<GridT>::create(const GridT& grid, float isovalue, InterrupterT* interrupter) | |
| 829 | { | ||
| 830 | 12 | auto csp = Ptr{new ClosestSurfacePoint}; | |
| 831 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 12 times.
|
12 | if (!csp->initialize(grid, isovalue, interrupter)) csp.reset(); |
| 832 | 12 | return csp; | |
| 833 | } | ||
| 834 | |||
| 835 | |||
| 836 | template<typename GridT> | ||
| 837 | template<typename InterrupterT> | ||
| 838 | bool | ||
| 839 | 12 | ClosestSurfacePoint<GridT>::initialize( | |
| 840 | const GridT& grid, float isovalue, InterrupterT* interrupter) | ||
| 841 | { | ||
| 842 | using Index32LeafManagerT = tree::LeafManager<Index32TreeT>; | ||
| 843 | using ValueT = typename GridT::ValueType; | ||
| 844 | |||
| 845 | const TreeT& tree = grid.tree(); | ||
| 846 | const math::Transform& transform = grid.transform(); | ||
| 847 | |||
| 848 | { // Extract surface point cloud | ||
| 849 | |||
| 850 | 24 | BoolTreeT mask(false); | |
| 851 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | volume_to_mesh_internal::identifySurfaceIntersectingVoxels(mask, tree, ValueT(isovalue)); |
| 852 | |||
| 853 |
2/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
|
12 | mSignTreePt.reset(new Int16TreeT(0)); |
| 854 |
3/6✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 12 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 12 times.
✗ Branch 9 not taken.
|
12 | mIdxTreePt.reset(new Index32TreeT(std::numeric_limits<Index32>::max())); |
| 855 | |||
| 856 | |||
| 857 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | volume_to_mesh_internal::computeAuxiliaryData( |
| 858 | *mSignTreePt, *mIdxTreePt, mask, tree, ValueT(isovalue)); | ||
| 859 | |||
| 860 |
1/6✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
|
12 | if (interrupter && interrupter->wasInterrupted()) return false; |
| 861 | |||
| 862 | // count unique points | ||
| 863 | |||
| 864 | using Int16LeafNodeType = typename Int16TreeT::LeafNodeType; | ||
| 865 | using Index32LeafNodeType = typename Index32TreeT::LeafNodeType; | ||
| 866 | |||
| 867 | std::vector<Int16LeafNodeType*> signFlagsLeafNodes; | ||
| 868 | mSignTreePt->getNodes(signFlagsLeafNodes); | ||
| 869 | |||
| 870 | const tbb::blocked_range<size_t> auxiliaryLeafNodeRange(0, signFlagsLeafNodes.size()); | ||
| 871 | |||
| 872 |
2/4✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 12 times.
✗ Branch 4 not taken.
|
12 | std::unique_ptr<Index32[]> leafNodeOffsets(new Index32[signFlagsLeafNodes.size()]); |
| 873 | |||
| 874 |
1/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
12 | tbb::parallel_for(auxiliaryLeafNodeRange, |
| 875 | volume_to_mesh_internal::LeafNodePointCount<Int16LeafNodeType::LOG2DIM> | ||
| 876 | (signFlagsLeafNodes, leafNodeOffsets)); | ||
| 877 | |||
| 878 | { | ||
| 879 | Index32 pointCount = 0; | ||
| 880 |
2/2✓ Branch 0 taken 1354 times.
✓ Branch 1 taken 12 times.
|
1366 | for (size_t n = 0, N = signFlagsLeafNodes.size(); n < N; ++n) { |
| 881 | 1354 | const Index32 tmp = leafNodeOffsets[n]; | |
| 882 | 1354 | leafNodeOffsets[n] = pointCount; | |
| 883 | 1354 | pointCount += tmp; | |
| 884 | } | ||
| 885 | |||
| 886 | 12 | mPointListSize = size_t(pointCount); | |
| 887 |
3/4✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 86624 times.
✓ Branch 4 taken 12 times.
|
86636 | mSurfacePointList.reset(new Vec3s[mPointListSize]); |
| 888 | } | ||
| 889 | |||
| 890 | |||
| 891 | std::vector<Index32LeafNodeType*> pointIndexLeafNodes; | ||
| 892 | mIdxTreePt->getNodes(pointIndexLeafNodes); | ||
| 893 | |||
| 894 |
4/8✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 12 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 10 times.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
|
24 | tbb::parallel_for(auxiliaryLeafNodeRange, volume_to_mesh_internal::ComputePoints<TreeT>( |
| 895 | mSurfacePointList.get(), tree, pointIndexLeafNodes, | ||
| 896 | signFlagsLeafNodes, leafNodeOffsets, transform, ValueT(isovalue))); | ||
| 897 | } | ||
| 898 | |||
| 899 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
12 | if (interrupter && interrupter->wasInterrupted()) return false; |
| 900 | |||
| 901 | 24 | Index32LeafManagerT idxLeafs(*mIdxTreePt); | |
| 902 | |||
| 903 | using Index32RootNodeT = typename Index32TreeT::RootNodeType; | ||
| 904 | using Index32NodeChainT = typename Index32RootNodeT::NodeChainType; | ||
| 905 | static_assert(Index32NodeChainT::Size > 1, | ||
| 906 | "expected tree depth greater than one"); | ||
| 907 | using Index32InternalNodeT = typename Index32NodeChainT::template Get<1>; | ||
| 908 | |||
| 909 | typename Index32TreeT::NodeCIter nIt = mIdxTreePt->cbeginNode(); | ||
| 910 | nIt.setMinDepth(Index32TreeT::NodeCIter::LEAF_DEPTH - 1); | ||
| 911 | 12 | nIt.setMaxDepth(Index32TreeT::NodeCIter::LEAF_DEPTH - 1); | |
| 912 | |||
| 913 | std::vector<const Index32InternalNodeT*> internalNodes; | ||
| 914 | |||
| 915 | 12 | const Index32InternalNodeT* node = nullptr; | |
| 916 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 12 times.
|
53 | for (; nIt; ++nIt) { |
| 917 | nIt.getNode(node); | ||
| 918 |
2/4✓ Branch 0 taken 41 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 41 times.
✗ Branch 4 not taken.
|
41 | if (node) internalNodes.push_back(node); |
| 919 | } | ||
| 920 | |||
| 921 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::vector<IndexRange>().swap(mLeafRanges); |
| 922 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | mLeafRanges.resize(internalNodes.size()); |
| 923 | |||
| 924 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::vector<const Index32LeafT*>().swap(mLeafNodes); |
| 925 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | mLeafNodes.reserve(idxLeafs.leafCount()); |
| 926 | |||
| 927 | typename Index32InternalNodeT::ChildOnCIter leafIt; | ||
| 928 | 12 | mMaxNodeLeafs = 0; | |
| 929 |
2/2✓ Branch 0 taken 41 times.
✓ Branch 1 taken 12 times.
|
53 | for (size_t n = 0, N = internalNodes.size(); n < N; ++n) { |
| 930 | |||
| 931 | 41 | mLeafRanges[n].first = mLeafNodes.size(); | |
| 932 | |||
| 933 | 41 | size_t leafCount = 0; | |
| 934 |
2/2✓ Branch 1 taken 1354 times.
✓ Branch 2 taken 41 times.
|
1436 | for (leafIt = internalNodes[n]->cbeginChildOn(); leafIt; ++leafIt) { |
| 935 |
1/4✓ Branch 1 taken 1354 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
|
1354 | mLeafNodes.push_back(&(*leafIt)); |
| 936 | 1354 | ++leafCount; | |
| 937 | } | ||
| 938 | |||
| 939 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 35 times.
|
47 | mMaxNodeLeafs = std::max(leafCount, mMaxNodeLeafs); |
| 940 | |||
| 941 | 41 | mLeafRanges[n].second = mLeafNodes.size(); | |
| 942 | } | ||
| 943 | |||
| 944 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::vector<Vec4R>().swap(mLeafBoundingSpheres); |
| 945 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | mLeafBoundingSpheres.resize(mLeafNodes.size()); |
| 946 | |||
| 947 | 12 | v2s_internal::LeafOp<Index32LeafT> leafBS( | |
| 948 | mLeafBoundingSpheres, mLeafNodes, transform, mSurfacePointList); | ||
| 949 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | leafBS.run(); |
| 950 | |||
| 951 | |||
| 952 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 12 times.
|
12 | std::vector<Vec4R>().swap(mNodeBoundingSpheres); |
| 953 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | mNodeBoundingSpheres.resize(internalNodes.size()); |
| 954 | |||
| 955 | v2s_internal::NodeOp nodeBS(mNodeBoundingSpheres, mLeafRanges, mLeafBoundingSpheres); | ||
| 956 |
1/2✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
|
12 | nodeBS.run(); |
| 957 | return true; | ||
| 958 | } // ClosestSurfacePoint::initialize | ||
| 959 | |||
| 960 | |||
| 961 | template<typename GridT> | ||
| 962 | bool | ||
| 963 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
|
28 | ClosestSurfacePoint<GridT>::search(std::vector<Vec3R>& points, |
| 964 | std::vector<float>& distances, bool transformPoints) | ||
| 965 | { | ||
| 966 | distances.clear(); | ||
| 967 | 28 | distances.resize(points.size(), std::numeric_limits<float>::infinity()); | |
| 968 | |||
| 969 | 28 | v2s_internal::ClosestPointDist<Index32LeafT> cpd(points, distances, mSurfacePointList, | |
| 970 | 28 | mLeafNodes, mLeafRanges, mLeafBoundingSpheres, mNodeBoundingSpheres, | |
| 971 | mMaxNodeLeafs, transformPoints); | ||
| 972 | |||
| 973 |
1/2✓ Branch 1 taken 14 times.
✗ Branch 2 not taken.
|
28 | cpd.run(); |
| 974 | |||
| 975 | 28 | return true; | |
| 976 | } | ||
| 977 | |||
| 978 | |||
| 979 | template<typename GridT> | ||
| 980 | bool | ||
| 981 | 20 | ClosestSurfacePoint<GridT>::search(const std::vector<Vec3R>& points, std::vector<float>& distances) | |
| 982 | { | ||
| 983 | 20 | return search(const_cast<std::vector<Vec3R>& >(points), distances, false); | |
| 984 | } | ||
| 985 | |||
| 986 | |||
| 987 | template<typename GridT> | ||
| 988 | bool | ||
| 989 | 4 | ClosestSurfacePoint<GridT>::searchAndReplace(std::vector<Vec3R>& points, | |
| 990 | std::vector<float>& distances) | ||
| 991 | { | ||
| 992 | 4 | return search(points, distances, true); | |
| 993 | } | ||
| 994 | |||
| 995 | |||
| 996 | //////////////////////////////////////// | ||
| 997 | |||
| 998 | |||
| 999 | // Explicit Template Instantiation | ||
| 1000 | |||
| 1001 | #ifdef OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 1002 | |||
| 1003 | #ifdef OPENVDB_INSTANTIATE_VOLUMETOSPHERES | ||
| 1004 | #include <openvdb/util/ExplicitInstantiation.h> | ||
| 1005 | #endif | ||
| 1006 | |||
| 1007 | OPENVDB_INSTANTIATE_CLASS ClosestSurfacePoint<FloatGrid>; | ||
| 1008 | OPENVDB_INSTANTIATE_CLASS ClosestSurfacePoint<DoubleGrid>; | ||
| 1009 | |||
| 1010 | #define _FUNCTION(TreeT) \ | ||
| 1011 | void fillWithSpheres(const Grid<TreeT>&, std::vector<openvdb::Vec4s>&, const Vec2i&, \ | ||
| 1012 | bool, float, float, float, int, util::NullInterrupter*) | ||
| 1013 | OPENVDB_REAL_TREE_INSTANTIATE(_FUNCTION) | ||
| 1014 | #undef _FUNCTION | ||
| 1015 | |||
| 1016 | #endif // OPENVDB_USE_EXPLICIT_INSTANTIATION | ||
| 1017 | |||
| 1018 | |||
| 1019 | } // namespace tools | ||
| 1020 | } // namespace OPENVDB_VERSION_NAME | ||
| 1021 | } // namespace openvdb | ||
| 1022 | |||
| 1023 | #endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED | ||
| 1024 |