00001 /* 00002 * PROPRIETARY INFORMATION. This software is proprietary to 00003 * Side Effects Software Inc., and is not to be reproduced, 00004 * transmitted, or disclosed in any way without written permission. 00005 * 00006 * Produced by: 00007 * Jeff Lait 00008 * Side Effects 00009 * 477 Richmond Street West 00010 * Toronto, Ontario 00011 * Canada M5V 3E7 00012 * 416-504-9876 00013 * 00014 * NAME: Geometry library (C++) 00015 * 00016 * COMMENTS: Builds a Signed Distance Function from a GU_Detail. 00017 * This also supports implicit SDFs. These lack any 00018 * voxel representation. 00019 */ 00020 00021 #ifndef __GU_SDF_H__ 00022 #define __GU_SDF_H__ 00023 00024 #include "GU_API.h" 00025 #include <UT/UT_Vector3.h> 00026 #include <UT/UT_BoundingBox.h> 00027 #include <UT/UT_Plane.h> 00028 #include <UT/UT_VoxelArray.h> 00029 #include <UT/UT_PriorityQueue.h> 00030 #include <UT/UT_IntArray.h> 00031 #include <UT/UT_RefArray.h> 00032 00033 class GU_Detail; 00034 class GU_RayIntersect; 00035 class GEO_Point; 00036 class GEO_PrimPoly; 00037 class GEO_Primitive; 00038 class UT_DMatrix3; 00039 00040 /// 00041 /// gu_sdf_qelem 00042 /// This is used as an element in our queue. 00043 /// 00044 class GU_API gu_sdf_qelem 00045 { 00046 public: 00047 // Where we are in the queue. 00048 int qpos; 00049 // Where we are in the ref array. 00050 int refpos; 00051 // What our x/y/z cell position is. 00052 int x, y, z; 00053 // What our value is. 00054 fpreal dist; 00055 }; 00056 00057 /// 00058 /// gu_sdf_voxpos 00059 /// This is used to store a specific element of our voxel. 00060 /// 00061 class GU_API gu_sdf_voxpos 00062 { 00063 public: 00064 gu_sdf_voxpos() {} 00065 gu_sdf_voxpos(int px, int py, int pz) 00066 { 00067 x = px; y = py; z = pz; 00068 } 00069 int x, y, z; 00070 }; 00071 00072 /// 00073 /// gu_sdf_comp 00074 /// Comparator used by our queue. 00075 /// 00076 class GU_API gu_sdf_comp 00077 { 00078 public: 00079 int isLess(const gu_sdf_qelem &e1, const gu_sdf_qelem &e2) const 00080 { 00081 return SYSabs(e1.dist) > SYSabs(e2.dist); 00082 } 00083 int isLess(const gu_sdf_qelem *e1, const gu_sdf_qelem *e2) const 00084 { 00085 return SYSabs(e1->dist) > SYSabs(e2->dist); 00086 } 00087 }; 00088 00089 /// 00090 /// gu_sdf_queue 00091 /// This is a queue of voxels sorted by minimum distance. 00092 /// We want to subclass to be able to track where we are in the 00093 /// queue. This lets us update the queue when we change our elements 00094 /// distance. 00095 /// 00096 class GU_API gu_sdf_queue : 00097 public UT_PriorityQueue<gu_sdf_qelem *, gu_sdf_comp, true> 00098 { 00099 public: 00100 virtual void changedPosition(gu_sdf_qelem *e, unsigned int idx) const 00101 { 00102 e->qpos = idx; 00103 } 00104 }; 00105 00106 /// 00107 /// GU_SDFParms 00108 /// All the parameters to build an SDF can be set using this class 00109 /// 00110 class GU_API GU_SDFParms 00111 { 00112 public: 00113 GU_SDFParms(); 00114 virtual ~GU_SDFParms(); 00115 00116 enum sdfMode 00117 { 00118 RAY_INTERSECT, 00119 METAFIELD, 00120 MINIMUM, 00121 POINT_CLOUD, 00122 IMPLICIT_BOX, 00123 IMPLICIT_SPHERE, 00124 IMPLICIT_PLANE, 00125 VOLUME, 00126 VOLUMESAMPLE 00127 }; 00128 00129 /// These set the general method by which the SDF is computed. 00130 void setMode(sdfMode mode); 00131 sdfMode getMode() const; 00132 00133 /// This sets the axis used for planar SDFs. 00134 void setPlaneAxis(const UT_Vector3 &axis); 00135 const UT_Vector3 &getPlaneAxis() const; 00136 00137 /// Sets the divisions used by the SDF. The given values represent 00138 /// the number of cells that will be present in each direction. 00139 /// The SDF samples the center of the cells to determine their 00140 /// values. 00141 void setDivisions(int divx, int divy, int divz); 00142 void getDivisions(int &divx, int &divy, int &divz) const; 00143 00144 /// Determines if we should invert the sense of the SDFs field. 00145 void setInvert(bool invert); 00146 bool getInvert() const; 00147 00148 /// Sets the offset applied to the SDF's field. 00149 void setOffset(fpreal offset); 00150 fpreal getOffset() const; 00151 00152 /// Sets the tolerance to be used for ray intersection 00153 /// This is multiplied by the bounding box size of the sdf to 00154 /// determine the final tolerance. 00155 void setTol(fpreal tol); 00156 fpreal getTol() const; 00157 00158 /// Sets the volume used for building the sdf. 00159 /// This must match the resolution of the sdf's parameters. 00160 void setVolume(UT_VoxelArrayF *vol); 00161 UT_VoxelArrayF *getVolume() const; 00162 00163 /// Sets the bounding box which the SDF will be sampled in. 00164 /// Note that the bounding box should fully contain the geometry 00165 /// or settings like ForceBounds become meaningless. 00166 /// If this method is not called, the bounding box will be set 00167 /// to the bounding box of the GDP increased by the expandBounds 00168 /// function. 00169 void setBBox(const UT_BoundingBox &bbox); 00170 bool hasBBox() const; 00171 void getBBox(UT_BoundingBox &bbox) const; 00172 00173 /// LaserScan will consider only the most extreme intersections along 00174 /// each axis, allowing geometry with bad normals or holes to process. 00175 /// This is only used with ray-based methods. 00176 void setLaserScan(bool laserscan); 00177 bool getLaserScan() const; 00178 00179 /// FixSigns uses consensus methods to recover from holes. 00180 void setFixSigns(int fixsigns); 00181 int getFixSigns() const; 00182 00183 /// ForceBounds forces all of the outermost voxels to be 00184 /// marked as outside. This only applies when FixSigns is set. 00185 void setForceBounds(bool forcebounds); 00186 bool getForceBounds() const; 00187 00188 /// Sets the maximum distance to which the SDF will be evaluated. 00189 /// Theoritically, things outside this distance will be left 00190 /// at +/- infinitity. In practice, this is still not used. 00191 void setMaxDistance(fpreal dist); 00192 fpreal getMaxDistance() const; 00193 00194 /// Sets the number of neighbours inspected for doing MLS computation 00195 /// when constructing from a point cloud. 00196 void setNumNeighbour(int numneighbour); 00197 int getNumNeighbour() const; 00198 00199 void setSweepCount(int sweepcount) { mySweepCount = sweepcount;}; 00200 int getSweepCount() const { return mySweepCount; } 00201 00202 void setSweepThreshold(fpreal sweepthreshold) { mySweepThreshold = sweepthreshold; }; 00203 fpreal getSweepThreshold() const { return mySweepThreshold; } 00204 00205 void setIsoContour(fpreal iso) { myIsoContour = iso; } 00206 fpreal getIsoContour() const { return myIsoContour; } 00207 00208 private: 00209 sdfMode myMode; 00210 int myDivX, myDivY, myDivZ; 00211 00212 bool myInvert; 00213 00214 fpreal myTol; 00215 00216 UT_VoxelArrayF *myVolume; 00217 00218 bool myHasBBox; 00219 UT_BoundingBox myBBox; 00220 00221 UT_Vector3 myPlaneAxis; 00222 00223 bool myLaserScan; 00224 int myFixSigns; 00225 bool myForceBounds; 00226 00227 fpreal myOffset; 00228 fpreal myMaxDist; 00229 00230 int myNumNeighbour; 00231 00232 int mySweepCount; 00233 fpreal mySweepThreshold; 00234 00235 fpreal myIsoContour; 00236 }; 00237 00238 00239 /// 00240 /// GU_SDF 00241 /// This class builds a signed distance function from a given GU_Detail. 00242 /// Signed distance functions contain an approximate distance to the original 00243 /// surface in each voxel cell. If cell is inside the the geometry, it 00244 /// has a negative distance. Sidedness of geometry is determined by 00245 /// surface normals. The geometry should be relatively watertight. 00246 /// 00247 class GU_API GU_SDF 00248 { 00249 public: 00250 GU_SDF(); 00251 virtual ~GU_SDF(); 00252 00253 enum sdfImplicitType 00254 { 00255 SDF_EXPLICIT, // Not implicit. 00256 SDF_SPHERE, 00257 SDF_PLANE, 00258 SDF_BOX 00259 }; 00260 00261 /// Expands the given bounding box so it contains a two cell border 00262 /// on all sides. This helps condition the SDF stuff by ensuring 00263 /// the outer cells are all outside. 00264 static void expandBounds(UT_BoundingBox &bbox, 00265 int xres, int yres, int zres); 00266 00267 /// Build from the given gdp. 00268 void build(const GU_Detail *gdp, 00269 const GU_SDFParms &parms); 00270 00271 /// Initializes an empty sdf of a given resolution and from a given 00272 /// bounding box. 00273 void initEmpty(const UT_BoundingBox &bbox, 00274 int xres, int yres, int zres); 00275 00276 00277 UT_VoxelArrayF *getFunction() const { return myVoxels; } 00278 00279 /// Implicit SDFs have no array of voxels, instead defining themselves 00280 /// as a simple atomic shape. 00281 bool isImplicit() const 00282 { return myImplicitType != SDF_EXPLICIT; } 00283 00284 /// Returns the type of implicit surface, if any. 00285 sdfImplicitType getImplicitType() const 00286 { return myImplicitType; } 00287 00288 /// The normal definining the implicit function. This is only 00289 /// meaninful for half-plane implicit functions. 00290 const UT_Vector3 &getImplicitNormal() const 00291 { return myImplicitNormal; } 00292 00293 /// Returns the divisions that the spaces is voxelized into. 00294 /// This is meaningful even for implicit surfaces, as they have 00295 /// a prefered division set for purposes like advection. 00296 void getDivisions(int &xdiv, int &ydiv, int &zdiv) const; 00297 00298 /// Calculates the SDF field at the point. 00299 fpreal getDistance(const UT_Vector3 &pos) const; 00300 00301 // This calculates the distance to the SDF using a faster, but 00302 // less accurate method. The tol parameter is set to the 00303 // tolerance to which the distance was computed. 00304 // Basically, this will point sample the SDF, put the distance to 00305 // the point sample in the tol parameter, and return the point sample. 00306 // This involves 1/8th of the voxel lookup as a getDistance. 00307 fpreal getFastDistance(const UT_Vector3 &pos, fpreal &tol) const; 00308 00309 /// Returns the gradient of the SDF at the point. 00310 UT_Vector3 getGradient(const UT_Vector3 &pos) const; 00311 00312 /// Returns the closest point on the iso surface to the given 00313 /// point. 00314 /// The Courant-Friedreichs-Lewy condition governs how carefully 00315 /// we step to find the solution. 0.9 is near optimal, 0.5 is 00316 /// conservative. 00317 UT_Vector3 findClosest(const UT_Vector3 &pos, 00318 fpreal iso = 0.0, 00319 fpreal cfl_cond = 0.9) const; 00320 00321 /// Advects a point according to the SDF's gradient field. 00322 /// Advection continues until the point has moved the given distance. 00323 /// If normalize_gradient is false, it is moved until the given 00324 /// amount of *time* has passed. 00325 UT_Vector3 advect(const UT_Vector3 &pos, 00326 fpreal dist, 00327 fpreal cfl_cond = 0.9f, 00328 bool normalize_gradient = true) const; 00329 00330 /// Finds the smallest value in the SDF along the given line 00331 /// segment. The value must be smaller than the cutoff value. 00332 /// Returns false if no close value found. 00333 bool findSmallestOnEdge( 00334 fpreal &minvalue, 00335 UT_Vector3 &result, 00336 const UT_Vector3 &a, 00337 const UT_Vector3 &b, 00338 fpreal cutoff = FLT_MAX) const; 00339 00340 /// Finds the smallest value in the SDF inside the given triangle. 00341 /// The value must be smaller than the cutoff value. 00342 /// Returns false if no close value found. 00343 /// 00344 /// Note: this algorithm is far from perfect. It does only a very 00345 /// partial scan over the interior of the triangle, and it doesn't work 00346 /// well with implicit box SDFs. 00347 bool findSmallestOnTri( 00348 fpreal &minValue, 00349 UT_Vector2 &resultBary, 00350 const UT_Vector3 &p0, 00351 const UT_Vector3 &p1, 00352 const UT_Vector3 &p2, 00353 fpreal cutoff = FLT_MAX) const; 00354 00355 00356 /// Returns the point along a path where it first intersects the SDF. 00357 /// If the path doesn't intersect the SDF, this function returns false. 00358 /// If the start of the path is inside the SDF, the function returns 00359 /// true, and returns the starting point of the path as the intersection. 00360 bool findRayIntersection( 00361 UT_Vector3 &result, 00362 const UT_Vector3 &a, 00363 const UT_Vector3 &b, 00364 fpreal boundaryvalue = 0.0) const; 00365 00366 00367 const UT_Vector3 &getVoxelSize() const { return myVoxelSize; } 00368 fpreal getVoxelDiameter() const { return myVoxelDiameter; } 00369 const UT_Vector3 &getSize() const { return mySize; } 00370 const UT_Vector3 &getOrig() const { return myOrig; } 00371 00372 void setOrig(const UT_Vector3 &o); 00373 void setSize(const UT_Vector3 &s); 00374 00375 /// Computes the center of mass of the SDF. 00376 void computeCenterOfMass( 00377 UT_Vector3 ¢erofmass) const; 00378 00379 /// Computes the volume of the SDF. 00380 fpreal computeVolume() const; 00381 00382 /// Computes the inertial tensor. 00383 /// You provide the center of mass for it to use. 00384 /// Each sample point of the SDF is treated as a point mass. The 00385 /// mass is 0 for cell-points outside of the object, 1 for those 00386 /// fully inside, and suitably scaled for others. 00387 void computeInertialTensor(UT_DMatrix3 &tensor, 00388 const UT_Vector3 ¢erofmass) const; 00389 00390 /// Returns the amount of memory used by this object. 00391 int64 getMemoryUsage() const; 00392 00393 /// These invert the sense of the SDF. This means the getDistance() 00394 /// function will always return -getDistance(), effectively turning 00395 /// the object inside out. 00396 /// This does not affect the calculation of volumes, etc! 00397 /// (Technically, an inverted SDF has infinite volume, which isn't 00398 /// all that useful anyways) 00399 bool isInverted() const { return myInvert; } 00400 void setInverted(bool invert) { myInvert = invert; } 00401 00402 /// These offset the zero isosurface of the SDF. This means that 00403 /// getDistance() will always return getDistance() - offset, 00404 /// effectively inflating (if offset is positive) or deflating (if 00405 /// offset is negative) the object. 00406 /// 00407 /// Offsetting happens before inversion. 00408 fpreal getOffset() const { return myOffset; } 00409 void setOffset(fpreal offset) { myOffset = offset; } 00410 00411 /// Load and save this SDF. 00412 /// This occurs in binary format. 00413 void save(ostream &os) const; 00414 bool load(UT_IStream &is); 00415 00416 protected: 00417 /// This sends rays along all the major axes. The cell distances 00418 /// are set to the distance along the axes to surfaces. 00419 void sendRays(const GU_Detail *gdp, 00420 bool laserscan, 00421 bool usemetafield, 00422 fpreal tol); 00423 00424 /// This fixes the sign of the distance field by finding a 00425 /// consensus among neighbouring voxels. 00426 /// This requires myQueueIndices to be present. 00427 /// If forcebounds is set, it will make the boundary all positive 00428 /// in sign, making it easier to recover from holes. 00429 void fixSigns(bool forcebounds); 00430 00431 /// This finds consensus among the neighbours of this point 00432 /// It may then flip this points sign. If it does so, the 00433 /// point and it's neighbours are added to the list to 00434 /// be tested in the future. 00435 /// This uses myQueueIndices to track if a voxel has been added before. 00436 /// Returns 1 if a point flipped, 0 otherwise. 00437 int findConsensus(int x, int y, int z, 00438 int iteration, 00439 fpreal alpha, fpreal beta, 00440 UT_RefArray<gu_sdf_voxpos> &flippedlist); 00441 00442 /// Performs a fast sweep along the given axis. Any voxel pair 00443 /// that has a bad consensus will be raytested & flipped. 00444 /// The flip is then propagated. 00445 /// dir is +/-1 to represent a forward or back sweep. 00446 int fastSweepCorrect(GU_RayIntersect *isect, 00447 int axis, int dir, fpreal alpha); 00448 00449 /// Rasterizers... 00450 void rasterize(const GEO_Primitive *prim); 00451 void rasterizePoly(const GEO_PrimPoly *poly); 00452 00453 /// This takes coordinates in cell space. 00454 void rasterizeRawTri(UT_Vector3 p1, UT_Vector3 p2, UT_Vector3 p3); 00455 00456 /// This sets the given cell to a specific SDF value. This will be 00457 /// considered a finalized value, so it will be written to the 00458 /// output voxel array, any QElem will be deleted, and any neighbouring 00459 /// cells will get tentative values. 00460 /// Propagation to neighbouring cells only occurs if dist < maxdist, 00461 /// unless maxdist < 0 00462 void setCellDist(int x, int y, int z, fpreal dist, 00463 fpreal maxdist=-1.0); 00464 00465 /// Sets a tentative value to a given cell. 00466 void setCellTentative(int x, int y, int z, 00467 fpreal tentative); 00468 00469 /// Gets or creates the qelement at the given location. Returns 0 00470 /// if the element has already been processed. 00471 gu_sdf_qelem *getQElem(int x, int y, int z, 00472 bool create=true); 00473 00474 /// If you change the weight of the qelem, this will update the queue. 00475 void updateQElem(gu_sdf_qelem *qelem); 00476 00477 /// Calculates the distance to a cell by using the finalized 00478 /// distances to adjacent cells. 00479 /// The olddistance is used to determine sign. 00480 fpreal findMinDist(int x, int y, int z, 00481 fpreal olddist) const; 00482 00483 /// Propagate distances from all elements in the queue until maximum 00484 /// distance is reached. 00485 void propagateQueue(fpreal maxdist); 00486 00487 /// Builds a shell of distance values around the geometry 00488 /// based on minimum distance values. 00489 /// We want to fill in the cell values 00490 /// from (x,y,z) to (x+xw-1,y+yw-1,z+zw-1) 00491 /// inclusive. We only fill values that are within a band about 00492 /// the surface, leaving the rest for FMM. This is because min 00493 /// distance calls are expensive! 00494 void buildFromMinDist(GU_RayIntersect &isect, 00495 int x, int y, int z, 00496 int xw, int yw, int zw); 00497 00498 /// Builds a shell of distance values around the geometry 00499 /// from the point cloud data. 00500 /// If normals are present, they are used to calculate sidedness. 00501 /// Otherwise, the SDF becomes an unsigned distance-to-implicit surface. 00502 void buildFromPointCloud(const GU_Detail *gdp, 00503 const GU_SDFParms &parms); 00504 00505 /// Builds a shell of distance values around the given iso-contour 00506 /// of the volume. This allows one to then reinitialize an SDF function. 00507 void buildFromVolume(const UT_VoxelArrayF &src, fpreal isocontour); 00508 00509 /// Takes a list of GEO_Points and finds the best fititng 00510 /// plane which interpolates them. If noff is not -1, it 00511 /// will derive the normals from the point normals. 00512 UT_Plane findPlaneFromNeighbour( 00513 const UT_Vector3 &sample, 00514 const UT_PtrArray<GEO_Point *> &neighbour, 00515 int noff) const; 00516 00517 /// This is the actual explicit array of voxels. It is not 00518 /// present if the SDF array is implicit. It does not incorporate the 00519 /// offset or inversion. 00520 UT_VoxelArrayF *myVoxels; 00521 00522 /// What sort of implicit representation we are using. 00523 /// SDF_EXPLICIT if we have none. 00524 sdfImplicitType myImplicitType; 00525 00526 UT_Vector3 myImplicitNormal; 00527 00528 /// This flags whether we act as if our SDF had the opposite sign 00529 /// than it does. 00530 bool myInvert; 00531 /// This defines how far we should shift the zero isosurface of the SDF. 00532 fpreal myOffset; 00533 00534 /// Real world coordinates are transformed into the voxel array's 0-1 00535 /// space by subtracting myOrig and dividing by mySize. 00536 UT_Vector3 myOrig, mySize; 00537 00538 /// This stores the size of a voxel along each axis. 00539 UT_Vector3 myVoxelSize; 00540 00541 /// This caches the diameter of a voxel cell. Ie, myVoxelSize.length() 00542 fpreal myVoxelDiameter; 00543 00544 /// These are used in the building stage to rasterize to, and in the 00545 /// expansion stage as the priority queues. 00546 UT_VoxelArray<int> *myQueueIndices; 00547 UT_PtrArray<gu_sdf_qelem *> myQueueElements; 00548 UT_IntArray myQueueFreeList; 00549 gu_sdf_queue *myQueue; 00550 }; 00551 00552 #endif
1.5.9