OpenVDB  0.104.0
Frustum.h
Go to the documentation of this file.
1 
2 //
3 // Copyright (c) 2012 DreamWorks Animation LLC
4 //
5 // All rights reserved. This software is distributed under the
6 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )
7 //
8 // Redistributions of source code must retain the above copyright
9 // and license notice and the following restrictions and disclaimer.
10 //
11 // * Neither the name of DreamWorks Animation nor the names of
12 // its contributors may be used to endorse or promote products derived
13 // from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
18 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
19 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
20 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 // IN NO EVENT SHALL THE COPYRIGHT HOLDERS' AND CONTRIBUTORS' AGGREGATE
27 // LIABILITY FOR ALL CLAIMS REGARDLESS OF THEIR BASIS EXCEED US$250.00.
28 //
30 //
33 
34 #ifndef OPENVDB_MATH_FRUSTUM_HAS_BEEN_INCLUDED
35 #define OPENVDB_MATH_FRUSTUM_HAS_BEEN_INCLUDED
36 
37 #include <cassert>
38 #include <string>
39 #include <iostream>// for cout
40 #include <iosfwd>
41 #include <sstream>
42 #include <algorithm>//for min/max
43 #include "Maps.h"
44 #include "Math.h" // for isExactlyEqual()
45 #include "Vec3.h"
46 #include "Plane.h"
47 #include "Ray.h"
48 
49 
50 namespace openvdb {
52 namespace OPENVDB_VERSION_NAME {
53 namespace math {
54 
55 // This class assumes the normal of the front and back faces are
56 // equal but opposite! The four other faces to not share normals
57 // Front/back is in the z-direction
58 // Left/right is in the x-direction
59 // up/down is in the y-direction
60 
62 template<typename Real>
63 class Frustum
64 {
65 public:
66  typedef Vec3<Real> Point;
67  typedef Vec3<Real> Vector;
69 
70 private:
71  Plane<Real> mFace[6];// 6 faces of frustum
72 
73 public:
76 
79  {
80  mFace[0]=other.mFace[0];
81  mFace[1]=other.mFace[1];
82  mFace[2]=other.mFace[2];
83  mFace[3]=other.mFace[3];
84  mFace[4]=other.mFace[4];
85  mFace[5]=other.mFace[5];
86  }
87 
92 OPENVDB_DEPRECATED Frustum(const Transform& t) { this->init(t); }
93 
96 OPENVDB_DEPRECATED Frustum(const PlaneType &front, const PlaneType &back,
97  const PlaneType &left, const PlaneType &right,
98  const PlaneType &up, const PlaneType &down)
99  {
100  mFace[0]=front;
101  mFace[1]=back;
102  mFace[2]=left;
103  mFace[3]=right;
104  mFace[4]=up;
105  mFace[5]=down;
106  }
107 
117 OPENVDB_DEPRECATED Frustum(const Point &position, //origin = camera position
118  const Vector &direction,//z-axis = unit-vector from origin to center
119  const Vector &up, //x-axis (=center - upper edge of near plane)
120  const Vector &left,//y-axis = center - left edge of near plane
121  Real zNear, Real zFar)//two plane offsets along z-axis
122  {
123  initFromCamera(position, direction, up, left, zNear, zFar);
124  }
125 
128 
129  // an index-space bounding box defines the region transfromed into the frustum
130  Vector min = frustum.getBBox().min();
131  Vector max = frustum.getBBox().max();
132  // compute the four corners of the front face
133  Vector ll = frustum.applyMap(min);
134  Vector lr = frustum.applyMap(Vec3d(max.x(),min.y(),min.z()));
135  Vector ul = frustum.applyMap(Vec3d(min.x(),max.y(),min.z()));
136  Vector ur = frustum.applyMap(Vec3d(max.x(),max.y(),min.z()));
137  Vector center_near = 0.25*(ll + lr + ul + ur);
138  Vector up = 0.5 * (ul + ur ) - center_near;
139  Vector left = 0.5 * (ul + ll) - center_near;
140 
141 
142  // compute the corners of the far plane
143  ll = frustum.applyMap(Vec3d(min.x(), min.y(), max.z()));
144  lr = frustum.applyMap(Vec3d(max.x(), min.y(), max.z()));
145  ul = frustum.applyMap(Vec3d(min.x(), max.y(), max.z()));
146  ur = frustum.applyMap(Vec3d(max.x(), max.y(), max.z()));
147  Vector center_far = 0.25*(ll + lr + ul + ur);
148 
149  Vector direction = center_far - center_near;
150  Real depth = direction.length();
151  direction.normalize();
152  Real gamma = frustum.getTaper() - 1.;
153  Real zNear = 2.*left.length() / gamma;
154  Real zFar = zNear + depth;
155  Vector translation = frustum.getAffineMap()->applyMap(Vec3d(0,0,0));
156  Vector position = translation - zNear*direction;
157 
158  initFromCamera(position, direction, up, left, zNear, zFar);
159  }
160 
164  void rescaleLerp(const Point &unit_min, const Point &unit_max)
165  {
166  assert(this->isValid());
167  PlaneType tmp;
168  // rescaling in z (only positions are modified!)
169  tmp = mFace[0].lerpPoints(mFace[1], unit_min[2]);//front
170  mFace[1] = mFace[1].lerpPoints(mFace[0],1-unit_max[2]);//back
171  mFace[0] = tmp;
172  /*
173  mFace[1].flip();
174  tmp = mFace[0].lerp(mFace[1],unit_min[2]);//front
175  mFace[0].flip();
176  mFace[1].flip();
177  mFace[1] = mFace[0].lerp(mFace[1],unit_max[2]);//back
178  mFace[0]=tmp;//front
179  if (tmp0 != mFace[0]) std::cerr << "Error0: " << tmp0 << " != " << mFace[0] << std::endl;
180  if (tmp1 != mFace[1]) std::cerr << "Error1: " << tmp1 << " != " << mFace[1] << std::endl;
181  */
182  // rescaling in x
183  mFace[3].flip();
184  tmp = mFace[2].lerp(mFace[3],unit_min[0]);//left
185  mFace[2].flip();
186  mFace[3].flip();
187  mFace[3] = mFace[2].lerp(mFace[3],unit_max[0]);//right
188  mFace[2]=tmp;//left
189 
190  // rescalig in y
191  mFace[5].flip();
192  tmp = mFace[5].lerp(mFace[4],unit_max[1]);//up
193  mFace[4].flip();
194  mFace[5].flip();
195  mFace[5] = mFace[5].lerp(mFace[4],unit_min[1]);//down
196  mFace[4]=tmp;//up
197  }
198 
202  void rescaleSlerp(const Point &unit_min, const Point &unit_max)
203  {
204  assert(this->isValid());
205  PlaneType tmp;
206  // rescaling in z (only positions are modified!)
207  tmp = mFace[0].lerpPoints(mFace[1], unit_min[2]);//front
208  mFace[1] = mFace[1].lerpPoints(mFace[0],1-unit_max[2]);//back
209  mFace[0] = tmp;
210  /*
211  mFace[1].flip();
212  PlaneType tmp = mFace[0].slerp(mFace[1],unit_min[2]);//front
213  mFace[0].flip();
214  mFace[1].flip();
215  mFace[1] = mFace[0].slerp(mFace[1],unit_max[2]);//back
216  mFace[0] = tmp;//front
217  */
218  // rescaling in x
219  mFace[3].flip();
220  tmp = mFace[2].slerp(mFace[3],unit_min[0]);//left
221  mFace[2].flip();
222  mFace[3].flip();
223  mFace[3] = mFace[2].slerp(mFace[3],unit_max[0]);//right
224  mFace[2] = tmp;//left
225 
226  // rescalig in y
227  mFace[5].flip();
228  tmp = mFace[5].slerp(mFace[4],unit_max[1]);//up
229  mFace[4].flip();
230  mFace[5].flip();
231  mFace[5] = mFace[5].slerp(mFace[4],unit_min[1]);//down
232  mFace[4] = tmp;//up
233  }
234 
235  inline bool operator==(const Frustum &other) const
236  {
237  return mFace[0]==other.mFace[0] && mFace[1]==other.mFace[1]
238  && mFace[2]==other.mFace[2] && mFace[3]==other.mFace[3]
239  && mFace[4]==other.mFace[4] && mFace[5]==other.mFace[5];
240  }
241 
242  inline bool operator!=(const Frustum &other) const
243  {
244  return !(*this == other);
245  }
246 
249  inline bool isInside(const Point &pt) const
250  {
251  return mFace[0].isInside(pt) && mFace[1].isInside(pt)
252  && mFace[2].isInside(pt) && mFace[3].isInside(pt)
253  && mFace[4].isInside(pt) && mFace[5].isInside(pt);
254  }
255 
258  inline bool isOutside(const Point &pt) const
259  {
260  return mFace[0].isOutside(pt) || mFace[1].isOutside(pt)
261  || mFace[2].isOutside(pt) || mFace[3].isOutside(pt)
262  || mFace[4].isOutside(pt) || mFace[5].isOutside(pt);
263  }
264 
267  // NB THIS IS NOT A GOOD ENOUGH TEST!!!!!
268  inline bool isValid() const
269  {
270  return mFace[0].isValid() && mFace[1].isValid()
271  && mFace[2].isValid() && mFace[3].isValid()
272  && mFace[4].isValid() && mFace[5].isValid();
273  }
274 
276  inline PlaneType getFace(int n) const {
277  assert(n>=0 && n<6);
278  return mFace[n];
279  }
280  inline PlaneType getPlane(int n) const {return this->getFace(n);}
281 
283  inline void setFace(int n, const PlaneType &P) {
284  assert(n>=0 && n<6);
285  mFace[n]=P;
286  }
287  inline void setPlane(int n, const PlaneType &P) {this->setFace(n,P);}
288 
290  inline PlaneType& face(int n) {
291  assert(n>=0 && n<6);
292  return mFace[n];
293  }
294  inline PlaneType& plane(int n) {return face(n);}
295 
296  inline static std::string getFaceName(int i) {
297  switch(i) {
298  case 0 : return "front"; break;
299  case 1 : return "back "; break;
300  case 2 : return "left "; break;
301  case 3 : return "right"; break;
302  case 4 : return "upper"; break;
303  case 5 : return "lower"; break;
304  }
305  }
306 
324  int intersects(const Ray<Real> &ray, Real &t0, Real &t1) const
325  {
326  t0 = ray.getMinTime();//near
327  t1 = ray.getMaxTime();//far
328  for (int p=0; p<6; ++p) {// Test each plane of the frustum
329  const Real angl = mFace[p].getNorm().dot(ray.getDirection());
330  const Real dist = mFace[p].getNorm().dot(ray.getOrigin())-mFace[p].getDist();
331  if (isExactlyEqual(angl, 0.0)) {//ray is parallel to plane
332  if (dist > 0) return 0;//missed - ray origin is outside plane
333  } else {// ray not parallel to plane
334  const Real t = -dist / angl ;
335  if ( angl < 0 ) {// front face - t is a near point
336  if ( t > t1 ) return 0;// missed - never reached frustum
337  if ( t > t0 ) t0=t;// hit near face, update t0
338  } else {// back face - t is a far point - never reached frustum
339  if ( t < t0 ) return 0;//missed - never reached frustum
340  if ( t < t1 ) t1=t;// hit far face, update t1
341  }
342  }
343  }
344  // survived all tests so either it hit or it's completely inside
345  int hits=0;// origin inside and back face beyond tmax
346  if (t0>ray.getMinTime()) ++hits;// outside, hitting front face
347  if (t1<ray.getMaxTime()) ++hits;// inside, hitting back face
348  //fprintf(stderr,"t0=%f t1=%f hits=%i\n",t0,t1,hits);
349  return hits>0 ? hits : -1;
350  }
351 
354  bool intersects(const Ray<Real> &ray) const
355  {
356  Real t0, t1;
357  return this->intersects(ray,t0,t1)!=0;
358  }
359 
361  bool isInside(const Ray<Real> &ray) const
362  {
363  Real t0, t1;
364  return this->intersects(ray,t0,t1)==-1;
365  }
366 
367 private:
368 
373  void init(const Transform& transform)
374  {
375  const Point P[7]={transform.indexToWorld(Vector(0,0,0)),// 0 origin-near
376  transform.indexToWorld(Vector(1,0,0)),// 1 x-near
377  transform.indexToWorld(Vector(0,1,0)),// 2 y-near
378  transform.indexToWorld(Vector(1,1,0)),// 3 xy-near
379  transform.indexToWorld(Vector(0,0,1)),// 4 origin-far
380  transform.indexToWorld(Vector(1,0,1)),// 5 x-far
381  transform.indexToWorld(Vector(0,1,1))};//6 y-far
382  // are unit-vectors left or right handed?
383  mFace[0]=PlaneType(P[0],P[1],P[2]);//front (near) face, N = U_x x U_y
384  if (mFace[0].getNorm().dot(P[4]-P[0]) >= 0) {//Right-hand,i.e. LinearTransforms
385  mFace[0].flip(); //front (near) face
386  //mFace[1]=PlaneType(P[4],P[5],P[6]);//back (far)
387  mFace[2]=PlaneType(P[0],P[4],P[2]);//left
388  mFace[3]=PlaneType(P[1],P[3],P[5]);//right
389  mFace[4]=PlaneType(P[2],P[6],P[3]);//up
390  mFace[5]=PlaneType(P[0],P[1],P[4]);//down
391  } else {//Left-hand unit-vectors, i.e. FrustumTransForms
392  //mFace[1]=PlaneType(P[4],P[6],P[5]);//back (far)
393  mFace[2]=PlaneType(P[0],P[2],P[4]);//left
394  mFace[3]=PlaneType(P[1],P[5],P[3]);//right
395  mFace[4]=PlaneType(P[2],P[3],P[6]);//up
396  mFace[5]=PlaneType(P[0],P[4],P[1]);//down
397  }
398  mFace[1].set(P[4],-mFace[0].getNorm());//back (far) has opposit normal of front
399  }
400 
401  void initFromCamera(const Point &position, //origin = camera position
402  const Vector &direction,//z-axis = unit-vector from origin to center
403  const Vector &up, //x-axis (=center - upper edge of near plane)
404  const Vector &left,//y-axis = center - left edge of near plane
405  Real zNear, Real zFar)//two plane offsets along z-axis
406  {
407 
408  // Center of the near plane.
409  const Vector center = direction*zNear;
410 
411  // Get the four basis vectors along the frustum edges.
412  const Vector base[4]={center - up - left,// lower right base
413  center - up + left,// lower left base
414  center + up - left,// upper right base
415  center + up + left};//upper left base
416 
417  const Vector pos1 = position + base[1];// lower left vertex
418  const Vector pos2 = position + base[2];// upper right vertex
419 
420  mFace[0].set(pos2,-direction);//front face
421  mFace[1].set(position+direction*zFar,direction);//back face
422 
423  typedef typename PlaneType::Normalize NormalizedPlane;
424  mFace[2].set(pos1, base[3].cross(base[1]), NormalizedPlane());//left face
425  mFace[3].set(pos2, base[0].cross(base[2]), NormalizedPlane());//right face
426 
427  mFace[4].set(pos2, base[2].cross(base[3]), NormalizedPlane());//up face
428  mFace[5].set(pos1, base[1].cross(base[0]), NormalizedPlane());//down face
429  }
430 
431 
432 }; // class Frustum
433 
434 
435 template <typename Real>
436 inline std::ostream &operator<<(std::ostream &os, const Frustum<Real> &f) {
437  for (int i=0; i<6; ++i) os << "Frustum " << f.getFaceName(i)
438  << " face: " << f.getPlane(i) << std::endl;
439  return os;
440 }
441 
444 {
445 public:
446 
447 
448 
449  LegacyFrustum(std::istream& is) {
450  // Frist read in the old transform's base class.
451  // the "extents"
452  Coord tmpMin, tmpMax;
453  is.read(reinterpret_cast<char*>(&tmpMin), sizeof(Coord::ValueType) * 3);
454  is.read(reinterpret_cast<char*>(&tmpMax), sizeof(Coord::ValueType) * 3);
455 
456  // set the extents
457  mExtents = CoordBBox(tmpMin, tmpMax);
458 
459  // read the old-frustum class member data
460  //Mat4d tmpW2C;
461  Mat4d tmpW2C, tmpC2S, tmpS2C, tmpWorldToLocal;
462  Mat4d tmpS2U, tmpXYLocalToUnit, tmpZLocalToUnit;
463  Real tmpWindow[6];
464  Real tmpPadding;
465 
466  //Mat4d tmpXYUnitToLocal, tmpZUnitToLocal
467 
468  // read in each matrix.
469  is.read(reinterpret_cast<char*>(&tmpW2C),
471  is.read(reinterpret_cast<char*>(&mC2W),
472  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
473  is.read(reinterpret_cast<char*>(&tmpC2S),
474  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
475  is.read(reinterpret_cast<char*>(&tmpS2C),
476  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
477  is.read(reinterpret_cast<char*>(&tmpWorldToLocal),
478  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
479  is.read(reinterpret_cast<char*>(&mLocalToWorld),
480  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
481 
482  is.read(reinterpret_cast<char*>(&tmpWindow[0]), sizeof(Real));
483  is.read(reinterpret_cast<char*>(&tmpWindow[1]), sizeof(Real));
484  is.read(reinterpret_cast<char*>(&tmpWindow[2]), sizeof(Real));
485  is.read(reinterpret_cast<char*>(&tmpWindow[3]), sizeof(Real));
486  is.read(reinterpret_cast<char*>(&tmpWindow[4]), sizeof(Real));
487  is.read(reinterpret_cast<char*>(&tmpWindow[5]), sizeof(Real));
488 
489  is.read(reinterpret_cast<char*>(&tmpPadding), sizeof(Real));
490 
491  is.read(reinterpret_cast<char*>(&tmpS2U),
492  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
493  is.read(reinterpret_cast<char*>(&mXYUnitToLocal),
494  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
495  is.read(reinterpret_cast<char*>(&tmpXYLocalToUnit),
496  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
497  is.read(reinterpret_cast<char*>(&mZUnitToLocal),
498  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
499  is.read(reinterpret_cast<char*>(&tmpZLocalToUnit),
500  sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size);
501 
502 
503  mNearPlane = tmpWindow[4];
504  mFarPlane = tmpWindow[5];
505 
506  // Look up the world space corners of the
507  // frustum grid.
508  mFrNearOrigin = unitToLocalFrustum(Vec3R(0,0,0));
509  mFrFarOrigin = unitToLocalFrustum(Vec3R(0,0,1));
510 
511  Vec3d frNearXTip = unitToLocalFrustum(Vec3R(1,0,0));
512  Vec3d frNearYTip = unitToLocalFrustum(Vec3R(0,1,0));
513  mFrNearXBasis = frNearXTip - mFrNearOrigin;
514  mFrNearYBasis = frNearYTip - mFrNearOrigin;
515 
516  Vec3R frFarXTip = unitToLocalFrustum(Vec3R(1,0,1));
517  Vec3R frFarYTip = unitToLocalFrustum(Vec3R(0,1,1));
518  mFrFarXBasis = frFarXTip - mFrFarOrigin;
519  mFrFarYBasis = frFarYTip - mFrFarOrigin;
520 
521  }
522 
524 
525  const Mat4d& getCamXForm() const {return mC2W; }
526 
527  double getDepth() const {return (mFarPlane - mNearPlane); }
528  double getTaper() const {
529 
530  return getNearPlaneWidth() / getFarPlaneWidth();
531  }
532 
533  double getNearPlaneWidth() const {
534  double nearPlaneWidth = (unitToWorld(Vec3d(0,0,0)) - unitToWorld(Vec3d(1,0,0))).length();
535  return nearPlaneWidth;
536  }
537 
538  double getFarPlaneWidth() const {
539  double farPlaneWidth = (unitToWorld(Vec3d(0,0,1)) - unitToWorld(Vec3d(1,0,1))).length();
540  return farPlaneWidth;
541  }
542 
543  double getNearPlaneDist() const { return mNearPlane; }
544 
545  const CoordBBox& getBBox() const {return mExtents; }
546 
547  Vec3d unitToWorld(const Vec3d& in) const {return mLocalToWorld.transform( unitToLocal(in) ); }
548 
549 private:
550  LegacyFrustum(){};
551 
552  Vec3d unitToLocal(const Vec3d& U) const {
553 
554  // We first find the local space coordinates
555  // of the unit point projected onto the near
556  // and far planes of the frustum by using a
557  // linear combination of the planes basis vectors
558  Vec3d nearLS = ( U[0] * mFrNearXBasis ) + ( U[1] * mFrNearYBasis ) + mFrNearOrigin;
559  Vec3d farLS = ( U[0] * mFrFarXBasis ) + ( U[1] * mFrFarYBasis ) + mFrFarOrigin;
560 
561  // then we lerp the two ws points in frustum z space
562  return U[2] * farLS + ( 1.0 - U[2] ) * nearLS;
563  }
564 
565  Vec3d unitToLocalFrustum(const Vec3d& u) const {
566  Vec3d fzu = mZUnitToLocal.transformH(u);
567  Vec3d fu = u;
568  fu[2] = fzu.z();
569  return mXYUnitToLocal.transformH(fu);
570  }
571 
572 
573 private:
574 
575  Mat4d mC2W, mLocalToWorld, mXYUnitToLocal, mZUnitToLocal;
576  CoordBBox mExtents;
577  Vec3d mFrNearXBasis, mFrNearYBasis, mFrFarXBasis, mFrFarYBasis;
578  Vec3d mFrNearOrigin, mFrFarOrigin;
579  double mNearPlane, mFarPlane;
580 
581 };
582 
583 
584 } // namespace math
585 } // namespace OPENVDB_VERSION_NAME
586 } // namespace openvdb
587 
588 #endif // OPENVDB_MATH_FRUSTUM_HAS_BEEN_INCLUDED
589 
590 // Copyright (c) 2012 DreamWorks Animation LLC
591 // All rights reserved. This software is distributed under the
592 // Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ )