1 // 2 // Copyright 2016 Pixar 3 // 4 // Licensed under the Apache License, Version 2.0 (the "Apache License") 5 // with the following modification; you may not use this file except in 6 // compliance with the Apache License and the following modification to it: 7 // Section 6. Trademarks. is deleted and replaced with: 8 // 9 // 6. Trademarks. This License does not grant permission to use the trade 10 // names, trademarks, service marks, or product names of the Licensor 11 // and its affiliates, except as required to comply with Section 4(c) of 12 // the License and to reproduce the content of the NOTICE file. 13 // 14 // You may obtain a copy of the Apache License at 15 // 16 // http://www.apache.org/licenses/LICENSE-2.0 17 // 18 // Unless required by applicable law or agreed to in writing, software 19 // distributed under the Apache License with the above modification is 20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 21 // KIND, either express or implied. See the Apache License for the specific 22 // language governing permissions and limitations under the Apache License. 23 // 24 #ifndef PXR_BASE_GF_BBOX3D_H 25 #define PXR_BASE_GF_BBOX3D_H 26 27 /// \file gf/bbox3d.h 28 /// \ingroup group_gf_BasicGeometry 29 30 #include "pxr/pxr.h" 31 #include "pxr/base/gf/matrix4d.h" 32 #include "pxr/base/gf/range3d.h" 33 #include "pxr/base/gf/api.h" 34 35 #include <iosfwd> 36 37 PXR_NAMESPACE_OPEN_SCOPE 38 39 /// \class GfBBox3d 40 /// \ingroup group_gf_BasicGeometry 41 /// Basic type: arbitrarily oriented 3D bounding box. 42 /// 43 /// This class represents a three-dimensional bounding box as an 44 /// axis-aligned box (\c GfRange3d) and a matrix (\c GfMatrix4d) to 45 /// transform it into the correct space. 46 /// 47 /// A \c GfBBox3d is more useful than using just \c GfRange3d instances 48 /// (which are always axis-aligned) for these reasons: 49 /// 50 /// \li When an axis-aligned bounding box is transformed several times, 51 /// each transformation can result in inordinate growth of the bounding 52 /// box. By storing the transformation separately, it can be applied once 53 /// at the end, resulting in a much better fit. For example, if the 54 /// bounding box at the leaf of a scene graph is transformed through 55 /// several levels of the graph hierarchy to the coordinate space at the 56 /// root, a \c GfBBox3d is generally much smaller than the \c GfRange3d 57 /// computed by transforming the box at each level. 58 /// 59 /// \li When two or more such bounding boxes are combined, having the 60 /// transformations stored separately means that there is a better 61 /// opportunity to choose a better coordinate space in which to combine 62 /// the boxes. 63 /// 64 /// \anchor bbox3d_zeroAreaFlag 65 /// <b> The Zero-area Primitives Flag </b> 66 /// 67 /// When bounding boxes are used in intersection test culling, it is 68 /// sometimes useful to extend them a little bit to allow 69 /// lower-dimensional objects with zero area, such as lines and points, 70 /// to be intersected. For example, consider a cube constructed of line 71 /// segments. The bounding box for this shape fits the cube exactly. If 72 /// an application wants to allow a near-miss of the silhouette edges of 73 /// the cube to be considered an intersection, it has to loosen the bbox 74 /// culling test a little bit. 75 /// 76 /// To distinguish when this loosening is necessary, each \c GfBBox3d 77 /// instance maintains a flag indicating whether any zero-area primitives 78 /// are contained within it. The application is responsible for setting 79 /// this flag correctly by calling \c SetHasZeroAreaPrimitives(). The 80 /// flag can be accessed during intersection tests by calling \c 81 /// HasZeroAreaPrimitives(). This flag is set by default in all 82 /// constructors to \c false. 83 /// 84 class GfBBox3d { 85 86 public: 87 88 /// The default constructor leaves the box empty, the transformation 89 /// matrix identity, and the \ref bbox3d_zeroAreaFlag "zero-area 90 /// primitives flag" \c false. GfBBox3d()91 GfBBox3d() { 92 _matrix.SetIdentity(); 93 _inverse.SetIdentity(); 94 _isDegenerate = false; 95 _hasZeroAreaPrimitives = false; 96 } 97 98 /// Copy constructor GfBBox3d(const GfBBox3d & rhs)99 GfBBox3d(const GfBBox3d& rhs) : 100 _box(rhs._box) { 101 _matrix = rhs._matrix; 102 _inverse = rhs._inverse; 103 _isDegenerate = rhs._isDegenerate; 104 _hasZeroAreaPrimitives = rhs._hasZeroAreaPrimitives; 105 } 106 107 /// This constructor takes a box and sets the matrix to identity. GfBBox3d(const GfRange3d & box)108 GfBBox3d(const GfRange3d &box) : 109 _box(box) { 110 _matrix.SetIdentity(); 111 _inverse.SetIdentity(); 112 _isDegenerate = false; 113 _hasZeroAreaPrimitives = false; 114 } 115 116 /// This constructor takes a box and a transformation matrix. GfBBox3d(const GfRange3d & box,const GfMatrix4d & matrix)117 GfBBox3d(const GfRange3d &box, const GfMatrix4d &matrix) { 118 Set(box, matrix); 119 _hasZeroAreaPrimitives = false; 120 } 121 122 /// Sets the axis-aligned box and transformation matrix. Set(const GfRange3d & box,const GfMatrix4d & matrix)123 void Set(const GfRange3d &box, const GfMatrix4d &matrix) { 124 _box = box; 125 _SetMatrices(matrix); 126 } 127 128 /// Sets the transformation matrix only. The axis-aligned box is not 129 /// modified. SetMatrix(const GfMatrix4d & matrix)130 void SetMatrix(const GfMatrix4d& matrix) { 131 _SetMatrices(matrix); 132 } 133 134 /// Sets the range of the axis-aligned box only. The transformation 135 /// matrix is not modified. SetRange(const GfRange3d & box)136 void SetRange(const GfRange3d& box) { 137 _box = box; 138 } 139 140 /// Returns the range of the axis-aligned untransformed box. GetRange()141 const GfRange3d & GetRange() const { 142 return _box; 143 } 144 145 /// Returns the range of the axis-aligned untransformed box. 146 /// This synonym of \c GetRange exists for compatibility purposes. GetBox()147 const GfRange3d & GetBox() const { 148 return GetRange(); 149 } 150 151 /// Returns the transformation matrix. GetMatrix()152 const GfMatrix4d & GetMatrix() const { 153 return _matrix; 154 } 155 156 /// Returns the inverse of the transformation matrix. This will be the 157 /// identity matrix if the transformation matrix is not invertible. GetInverseMatrix()158 const GfMatrix4d & GetInverseMatrix() const { 159 return _inverse; 160 } 161 162 /// Sets the \ref bbox3d_zeroAreaFlag "zero-area primitives flag" to the 163 /// given value. SetHasZeroAreaPrimitives(bool hasThem)164 void SetHasZeroAreaPrimitives(bool hasThem) { 165 _hasZeroAreaPrimitives = hasThem; 166 } 167 168 /// Returns the current state of the \ref bbox3d_zeroAreaFlag "zero-area 169 /// primitives flag". HasZeroAreaPrimitives()170 bool HasZeroAreaPrimitives() const { 171 return _hasZeroAreaPrimitives; 172 } 173 174 /// Returns the volume of the box (0 for an empty box). 175 GF_API 176 double GetVolume() const; 177 178 /// Transforms the bounding box by the given matrix, which is assumed to 179 /// be a global transformation to apply to the box. Therefore, this just 180 /// post-multiplies the box's matrix by \p matrix. Transform(const GfMatrix4d & matrix)181 void Transform(const GfMatrix4d &matrix) { 182 _SetMatrices(_matrix * matrix); 183 } 184 185 /// Returns the axis-aligned range (as a \c GfRange3d) that results from 186 /// applying the transformation matrix to the wxis-aligned box and 187 /// aligning the result. 188 GF_API 189 GfRange3d ComputeAlignedRange() const; 190 191 /// Returns the axis-aligned range (as a \c GfRange3d) that results from 192 /// applying the transformation matrix to the axis-aligned box and 193 /// aligning the result. This synonym for \c ComputeAlignedRange exists 194 /// for compatibility purposes. ComputeAlignedBox()195 GfRange3d ComputeAlignedBox() const { 196 return ComputeAlignedRange(); 197 } 198 199 /// Combines two bboxes, returning a new bbox that contains both. This 200 /// uses the coordinate space of one of the two original boxes as the 201 /// space of the result; it uses the one that produces whe smaller of the 202 /// two resulting boxes. 203 GF_API 204 static GfBBox3d Combine(const GfBBox3d &b1, const GfBBox3d &b2); 205 206 /// Returns the centroid of the bounding box. 207 /// The centroid is computed as the transformed centroid of the range. 208 GF_API 209 GfVec3d ComputeCentroid() const; 210 211 /// Hash. hash_value(const GfBBox3d & b)212 friend inline size_t hash_value(const GfBBox3d &b) { 213 size_t h = 0; 214 boost::hash_combine(h, b._box); 215 boost::hash_combine(h, b._matrix); 216 return h; 217 } 218 219 /// Component-wise equality test. The axis-aligned boxes and 220 /// transformation matrices match exactly for bboxes to be considered 221 /// equal. (To compare equality of the actual boxes, you can compute both 222 /// aligned boxes and test the results for equality.) 223 bool operator ==(const GfBBox3d &b) const { 224 return (_box == b._box && 225 _matrix == b._matrix); 226 } 227 228 /// Component-wise inequality test. The axis-aligned boxes and 229 /// transformation matrices match exactly for bboxes to be considered 230 /// equal. (To compare equality of the actual boxes, you can compute both 231 /// aligned boxes and test the results for equality.) 232 bool operator !=(const GfBBox3d &that) const { 233 return !(*this == that); 234 } 235 236 private: 237 /// The axis-aligned box. 238 GfRange3d _box; 239 /// Transformation matrix. 240 GfMatrix4d _matrix; 241 /// Inverse of the transformation matrix. 242 GfMatrix4d _inverse; 243 /// Flag indicating whether the matrix is degenerate. 244 bool _isDegenerate; 245 /// Flag indicating whether the bbox contains zero-area primitives. 246 bool _hasZeroAreaPrimitives; 247 248 /// Sets the transformation matrix and the inverse, checking for 249 /// degeneracies. 250 GF_API 251 void _SetMatrices(const GfMatrix4d &matrix); 252 253 /// This is used by \c Combine() when it is determined which coordinate 254 /// space to use to combine two boxes: \p b2 is transformed into the space 255 /// of \p b1 and the results are merged in that space. 256 static GfBBox3d _CombineInOrder(const GfBBox3d &b1, const GfBBox3d &b2); 257 }; 258 259 /// Output a GfBBox3d using the format [(range) matrix zeroArea] 260 /// 261 /// The zeroArea flag is true or false and indicates whether the 262 /// bbox has zero area primitives in it. 263 /// 264 /// \ingroup group_gf_DebuggingOutput 265 GF_API std::ostream& operator<<(std::ostream&, const GfBBox3d&); 266 267 PXR_NAMESPACE_CLOSE_SCOPE 268 269 #endif // PXR_BASE_GF_BBOX3D_H 270