1 /*************************************************************************
2  *                                                                       *
3  * Vega FEM Simulation Library Version 3.1                               *
4  *                                                                       *
5  * "distance field" library , Copyright (C) 2007 CMU, 2016 USC           *
6  * All rights reserved.                                                  *
7  *                                                                       *
8  * Code authors: Jernej Barbic, Hongyi Xu, Yijing Li                     *
9  * http://www.jernejbarbic.com/code                                      *
10  *                                                                       *
11  * Research: Jernej Barbic, Hongyi Xu, Doug L. James                     *
12  *                                                                       *
13  * Funding: National Science Foundation, Link Foundation,                *
14  *          Zumberge Research and Innovation Fund at USC                 *
15  *                                                                       *
16  * This library is free software; you can redistribute it and/or         *
17  * modify it under the terms of the BSD-style license that is            *
18  * included with this library in the file LICENSE.txt                    *
19  *                                                                       *
20  * This library is distributed in the hope that it will be useful,       *
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file     *
23  * LICENSE.TXT for more details.                                         *
24  *                                                                       *
25  *************************************************************************/
26 
27 /*
28   This code generates a distance filed, either signed or unsigned,
29   to the given triangle mesh.
30 
31   The distance field can be loaded/saved to a file.
32   You can also lookup the field (once computed or loaded) at arbitrary
33   locations inside the field, using trilinear interpolation.
34 
35   Input mesh need not have triangle faces (e.g., can have quads;
36   they will be triangulated).
37 
38   For signed field generation, the input mesh must be a closed manifold mesh.
39 
40   Input mesh must be given in the .obj format:
41   http://www.royriggs.com/obj.html
42 
43   By default, the bounding box will be a cube, obtained by fitting the
44   smallest cube to the geometry, and then expanded (scaled) from its
45   center by a factor of 1.5. You can provide your own bounding boxes.
46   However, note: (1) the provided bounding box must cover all the geometry,
47   and (2) bounding boxes that are not cubes were not (yet) tested.
48 
49   The bounding box will be divided into a "resolution" number of
50   cubes ("voxels") along each axis. The distance field will be computed at the
51   vertices of these voxels. So, if resolution is 256, the bounding box
52   will get divided into 256 x 256 x 256 voxels, and the distance field
53   will be computed on the resulting 257 x 257 x 257 grid of voxel vertices.
54   Note that when indexing voxels, the indices (i,j,k) will run from 0 to 255
55   inclusive, whereas when indexing voxel vertices (also called "grid vertices"),
56   they will run from 0 to 256 inclusive.
57 
58   Distance field data is stored at voxel vertices.
59   In memory, distance field value at voxel vertex (i,j,k) is stored
60   at location k * (resolutionX+1)*(resolutionY+1) + j * (resolutionX+1) + i .
61 
62   Internally, the code builds an octree on top of the triangle mesh.
63   There are two parameters that control this process (you can keep them
64   at default values, which worked well in practice for us) :
65   the max depth of the octree is "maxDepth", and
66   the max number of triangles intersecting an octree cell is "maxTriCount".
67   Note: once max depth level is reached, the maxTriCount bound is not imposed any more.
68 */
69 
70 #ifndef _DISTANCEFIELDBASE_H_
71 #define _DISTANCEFIELDBASE_H_
72 
73 #include "objMesh.h"
74 
75 class DistanceFieldBase
76 {
77 public:
78 
79   DistanceFieldBase();
~DistanceFieldBase()80   virtual ~DistanceFieldBase() {};
81 
82   DistanceFieldBase(int resolutionX, int resolutionY, int resolutionZ);
83 
84   // sets the bounding box within which the distance field will be computed
85   // set it before calling computeSigned/UnsignedField
86   void setBoundingBox(const Vec3d & bmin, const Vec3d & bmax);
87   void getBoundingBox(Vec3d & bmin, Vec3d & bmax);
88   // set a tight-fitting bounding box around the model, and expand it by the expansion ratio given
89   // note: if you don't set any bounding boxes, you get an automaticBoundingBox with 1.5 expansion ratio by default
90   void setAutomaticBoundingBox(bool allBoxSidesEqual=true, double expansionRatio=1.5);
91 
92   // compute bounding box with uniform cube voxels, of the specified resolution
93   // this routine uses the parameters set by the "setAutomaticBoundingBox"
94   // if allBoxSidesEqual=true, the voxels will not necessarily be cubes
95   void computeBoundingBox(ObjMesh * objMesh, int resolutionX, int resolutionY, int resolutionZ);
96 
97   // loads a previously computed distance field from a disk file
98   virtual int load(const std::string& filename) = 0; // returns 0 on success
99 
100   // saves the current distance field to a disk file (e.g. after computing it once, for later fast reloading)
101   virtual int save(const std::string& filename, bool doublePrecision) = 0;
102 
103   // exports the distance field to a file, in text format
104   virtual int saveToText(const std::string& filename) = 0;
105 
106   // return distance field value at grid vertex (i,j,k)
107   // each of i,j,k must be an integer from {0, ..., resolution{X,Y,Z}}
108   virtual float distance(int i, int j, int k) = 0;
109   // computes distance and gradient at arbitrary position
110   virtual float distance(Vec3d pos, int constrainToBox=0) = 0;
111   // alters the distance at a particular grid vertex (i,j,k)
112   virtual void setDistance(int i, int j, int k, float value) = 0;
113 
114   // gradient is computed with respect to trilinear interpolation
115   // note: gradient is discontinuous at the cell boundaries
116   virtual Vec3d gradient(const Vec3d& pos) = 0;
117 
118   // pack/unpack index
119   inline int packedVoxelIndex(int i, int j, int k); // returns a 32-bit unique voxel index
120   inline int packedVoxelIndex(const Vec3d & pos); // returns a 32-bit unique voxel index, for voxel constaining pos
121   inline void unpackVoxelIndex(int packedIndex, int & i, int & j, int & k);
122 
123   inline void voxelIndices(const Vec3d & pos, int * i, int * j, int * k); // returns indices of voxel containing pos
124 
125   inline bool validGridIndex(int i, int j, int k); // tells whether the grid index is valid: 0<=i<=resolutionX, 0<=j<=resolutionY, 0<=z<=resolutionZ
126   inline bool validVoxelIndex(int i, int j, int k);// tells whether the voxel index is valid: 0<=i<resolutionX, 0<=j<resolutionY, 0<=z<resolutionZ
127 
128   inline bool insideBox(const Vec3d & pos); // tells whether the pos is inside box or not
129 
130   // returns the world-coordinate position of the grid vertex with indices (i,j,k)
131   // must have: 0 <= i,j,k <= resolution{X,Y,Z}   (i,j,k of course not necessarily sorted)
132   inline Vec3d getGridPosition(int i, int j, int k);
133 
134   // /set/get distance field resolution
setResolution(int resolutionX,int resolutionY,int resolutionZ)135   inline void setResolution(int resolutionX, int resolutionY, int resolutionZ) { this->resolutionX = resolutionX; this->resolutionY = resolutionY; this->resolutionZ = resolutionZ; setGridParameters(); }
getResolutionX()136   inline int getResolutionX() const { return resolutionX; }
getResolutionY()137   inline int getResolutionY() const { return resolutionY; }
getResolutionZ()138   inline int getResolutionZ() const { return resolutionZ; }
139 
140   // get the diagonal of the bounding box
diagonal()141   inline double diagonal() const { return len(bmax_-bmin_);}
142 
143   // get the lower-left-front corner of bounding box
bmin()144   inline const Vec3d & bmin() const { return bmin_; }
145   // get the upper-right-back corner of bounding box
bmax()146   inline const Vec3d & bmax() const { return bmax_; }
147   // alternative interface to bmin, bmax functions above
bmin(float * bmin)148   inline void bmin(float * bmin) const { bmin[0] = (float) bmin_[0]; bmin[1] = (float) bmin_[1]; bmin[2] = (float) bmin_[2]; }
bmax(float * bmax)149   inline void bmax(float * bmax) const { bmax[0] = (float) bmax_[0]; bmax[1] = (float) bmax_[1]; bmax[2] = (float) bmax_[2]; }
bmin(double * bmin)150   inline void bmin(double * bmin) const { bmin[0] = bmin_[0]; bmin[1] = bmin_[1]; bmin[2] = bmin_[2]; }
bmax(double * bmax)151   inline void bmax(double * bmax) const { bmax[0] = bmax_[0]; bmax[1] = bmax_[1]; bmax[2] = bmax_[2]; }
152 
153   // returns the spatial dimensions of the voxels
154   // i.e., this is the spatial distance between consecutive grid vertices along the three dimensions
155   void getGridSpacing(double * gridX, double * gridY, double * gridZ);
156   void getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ);
157 
158   // conversion between absolute units to grid units
159   double absoluteUnitsToGridUnits(double absoluteUnits) const;
160   double gridUnitsToAbsoluteUnits(double gridUnits) const;
161 
162   virtual bool sanityCheck() = 0; // checks if distance for any two adjacent voxels is less than voxel grid spacing apart (which it must be by triangle inequality, for both signed and unsigned fields)
163 
164   virtual float maxValue() = 0;
165   virtual float minValue() = 0;
166 
167   virtual float maxAbsValue() = 0;
168   virtual float maxAbsValue(float threshold) = 0; // only abs values up to threshold
169   virtual float maxNonInftyAbsValue() = 0;
170 
171   //virtual void print() = 0;
172 
173   virtual void offsetDistanceField(double offset) = 0;
174 
175 protected:
176   // set side, gridX, girdY, gridZ, invGridX, invGridY, invGridZ
177   void setGridParameters();
178 
179   int resolutionX, resolutionY, resolutionZ;
180   Vec3d bmin_, bmax_;
181   Vec3d side;
182   double gridX, gridY, gridZ;           // spatial distance between consecutive grid vertices along the three dimensions
183   double invGridX, invGridY, invGridZ;  // inverse of gridX/Y/Z
184 
185   bool useAutomaticBox;
186   double expansionRatio;
187   bool allBoxSidesEqual;
188 
189   bool bboxComputed;
190   //float minBoundaryDistance;
191 };
192 
193 
getBoundingBox(Vec3d & bmin,Vec3d & bmax)194 inline void DistanceFieldBase::getBoundingBox(Vec3d & bmin, Vec3d & bmax)
195 {
196   bmin = bmin_;
197   bmax = bmax_;
198 }
199 
setBoundingBox(const Vec3d & bmin,const Vec3d & bmax)200 inline void DistanceFieldBase::setBoundingBox(const Vec3d& bmin, const Vec3d& bmax)
201 {
202   useAutomaticBox = false;
203   bmin_ = bmin;
204   bmax_ = bmax;
205   setGridParameters();
206 }
207 
setGridParameters()208 inline void DistanceFieldBase::setGridParameters()
209 {
210   side = bmax_ - bmin_;
211   gridX = side[0] / resolutionX;
212   gridY = side[1] / resolutionY;
213   gridZ = side[2] / resolutionZ;
214   invGridX = 1.0 / gridX;
215   invGridY = 1.0 / gridY;
216   invGridZ = 1.0 / gridZ;
217 }
218 
setAutomaticBoundingBox(bool allBoxSidesEqual,double expansionRatio)219 inline void DistanceFieldBase::setAutomaticBoundingBox(bool allBoxSidesEqual, double expansionRatio)
220 {
221   useAutomaticBox = true;
222   this->expansionRatio = expansionRatio;
223   this->allBoxSidesEqual = allBoxSidesEqual;
224 }
225 
getGridSpacing(double * gridX,double * gridY,double * gridZ)226 inline void DistanceFieldBase::getGridSpacing(double * gridX, double * gridY, double * gridZ)
227 {
228   *gridX = this->gridX;
229   *gridY = this->gridY;
230   *gridZ = this->gridZ;
231 }
232 
getInvGridSpacing(double * invGridX,double * invGridY,double * invGridZ)233 inline void DistanceFieldBase::getInvGridSpacing(double * invGridX, double * invGridY, double * invGridZ)
234 {
235   *invGridX = this->invGridX;
236   *invGridY = this->invGridY;
237   *invGridZ = this->invGridZ;
238 }
239 
getGridPosition(int i,int j,int k)240 inline Vec3d DistanceFieldBase::getGridPosition(int i, int j, int k)
241 {
242  return Vec3d (bmin_[0] + i * gridX, bmin_[1] + j * gridY, bmin_[2] + k * gridZ);
243 }
244 
245 
voxelIndices(const Vec3d & pos,int * i,int * j,int * k)246 inline void DistanceFieldBase::voxelIndices(const Vec3d& pos, int * i, int * j, int * k) // returns indices of voxel containing pos
247 {
248   *i = (int)((pos[0] - bmin_[0]) * invGridX);
249   *j = (int)((pos[1] - bmin_[1]) * invGridY);
250   *k = (int)((pos[2] - bmin_[2]) * invGridZ);
251 }
252 
253 
insideBox(const Vec3d & pos)254 inline bool DistanceFieldBase::insideBox(const Vec3d& pos)
255 {
256   int i = (int)((pos[0] - bmin_[0]) * invGridX);
257   int j = (int)((pos[1] - bmin_[1]) * invGridY);
258   int k = (int)((pos[2] - bmin_[2]) * invGridZ);
259 
260   return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
261 }
262 
validGridIndex(int i,int j,int k)263 inline bool DistanceFieldBase::validGridIndex(int i, int j, int k)
264 {
265   return ((i >= 0) && (i <= resolutionX) && (j >= 0) && (j <= resolutionY) && (k >= 0) && (k <= resolutionZ));
266 }
267 
validVoxelIndex(int i,int j,int k)268 inline bool DistanceFieldBase::validVoxelIndex(int i, int j, int k)
269 {
270   return ((i >= 0) && (i < resolutionX) && (j >= 0) && (j < resolutionY) && (k >= 0) && (k < resolutionZ));
271 }
272 
packedVoxelIndex(const Vec3d & pos)273 inline int DistanceFieldBase::packedVoxelIndex(const Vec3d& pos)
274 {
275   // get the indices
276   int i = (int)((pos[0] - bmin_[0]) * invGridX);
277   int j = (int)((pos[1] - bmin_[1]) * invGridY);
278   int k = (int)((pos[2] - bmin_[2]) * invGridZ);
279 
280   return (k * resolutionY + j ) * resolutionX + i;
281 }
282 
packedVoxelIndex(int i,int j,int k)283 inline int DistanceFieldBase::packedVoxelIndex(int i, int j, int k)
284 {
285   return (k * resolutionY + j ) * resolutionX + i;
286 }
287 
unpackVoxelIndex(int packedIndex,int & i,int & j,int & k)288 inline void DistanceFieldBase::unpackVoxelIndex(int packedIndex, int & i, int & j, int & k)
289 {
290   i = packedIndex % resolutionX;
291   packedIndex = packedIndex / resolutionX;
292   j = packedIndex % resolutionY;
293   k = packedIndex / resolutionY;
294 }
295 
absoluteUnitsToGridUnits(double absoluteUnits)296 inline double DistanceFieldBase::absoluteUnitsToGridUnits(double absoluteUnits) const
297 {
298   double invGridSize = std::min(std::min(invGridX, invGridY), invGridZ);
299   return absoluteUnits * invGridSize;
300 }
301 
gridUnitsToAbsoluteUnits(double gridUnits)302 inline double DistanceFieldBase::gridUnitsToAbsoluteUnits(double gridUnits) const
303 {
304   double gridSize = std::max(std::max(gridX, gridY), gridZ);
305   return gridUnits * gridSize;
306 }
307 
308 
309 #endif
310 
311