00001
00002
00003 #include "frustum.h"
00004
00005
00006 Frustum::Frustum(const Frustum & f):mPlanes(6)
00007 {
00008 for (int i = 0; i < 6; ++i)
00009 mPlanes[i] = f.mPlanes[i];
00010 }
00011
00012 void Frustum::normalizePlane(TPlaneID planeID)
00013 {
00014 assertL(planeID >= 0 && planeID < (int)mPlanes.size() && "Bad frustum plane ID!");
00015 const GLVector3d &n = mPlanes[planeID].getNormal();
00016 double len = n.length();
00017 if (len)
00018 len = 1/len;
00019 mPlanes[planeID].setNormal(n * len);
00020 mPlanes[planeID].setD(mPlanes[planeID].getD() * len);
00021 }
00022
00023 void Frustum::normalizeAllPlanes(void)
00024 {
00025 normalizePlane(PL_LEFT);
00026 normalizePlane(PL_TOP);
00027 normalizePlane(PL_RIGHT);
00028 normalizePlane(PL_BOTTOM);
00029 normalizePlane(PL_FAR);
00030 normalizePlane(PL_NEAR);
00031 }
00032
00033 void Frustum::set(const GLMatrix4d &m, bool normalize)
00034 {
00035
00036 mPlanes[PL_LEFT].setNormal(
00037 GLVector3d(
00038 m[mi(4, 1)] + m[mi(1, 1)],
00039 m[mi(4, 2)] + m[mi(1, 2)],
00040 m[mi(4, 3)] + m[mi(1, 3)]
00041 ));
00042 mPlanes[PL_LEFT].setD(m[mi(4, 4)] + m[mi(1, 4)]);
00043
00044
00045 mPlanes[PL_RIGHT].setNormal(
00046 GLVector3d(
00047 m[mi(4, 1)] - m[mi(1, 1)],
00048 m[mi(4, 2)] - m[mi(1, 2)],
00049 m[mi(4, 3)] - m[mi(1, 3)]
00050 ));
00051 mPlanes[PL_RIGHT].setD(m[mi(4, 4)] - m[mi(1, 4)]);
00052
00053
00054 mPlanes[PL_TOP].setNormal(
00055 GLVector3d(
00056 m[mi(4, 1)] - m[mi(2, 1)],
00057 m[mi(4, 2)] - m[mi(2, 2)],
00058 m[mi(4, 3)] - m[mi(2, 3)]
00059 ));
00060 mPlanes[PL_TOP].setD(m[mi(4, 4)] - m[mi(2, 4)]);
00061
00062
00063 mPlanes[PL_BOTTOM].setNormal(
00064 GLVector3d(
00065 m[mi(4, 1)] + m[mi(2, 1)],
00066 m[mi(4, 2)] + m[mi(2, 2)],
00067 m[mi(4, 3)] + m[mi(2, 3)]
00068 ));
00069 mPlanes[PL_BOTTOM].setD(m[mi(4, 4)] + m[mi(2, 4)]);
00070
00071
00072 mPlanes[PL_NEAR].setNormal(
00073 GLVector3d(
00074 m[mi(4, 1)] + m[mi(3, 1)],
00075 m[mi(4, 2)] + m[mi(3, 2)],
00076 m[mi(4, 3)] + m[mi(3, 3)]
00077 ));
00078 mPlanes[PL_NEAR].setD(m[mi(4, 4)] + m[mi(3, 4)]);
00079
00080
00081 mPlanes[PL_FAR].setNormal(
00082 GLVector3d(
00083 m[mi(4, 1)] - m[mi(3, 1)],
00084 m[mi(4, 2)] - m[mi(3, 2)],
00085 m[mi(4, 3)] - m[mi(3, 3)]
00086 ));
00087 mPlanes[PL_FAR].setD(m[mi(4, 4)] - m[mi(3, 4)]);
00088
00089 if (normalize)
00090 normalizeAllPlanes();
00091 }
00092
00093 void Frustum::transform(const GLMatrix4d &m, bool normalize)
00094 {
00095 GLMatrix4d mt(m);
00096 mt.transpose();
00097
00098 for (TPlaneContainer::iterator it = mPlanes.begin(), ite = mPlanes.end(); it != ite; ++it){
00099 const GLVector3d &n = it->getNormal();
00100 GLVector4d v(n.x, n.y, n.z, it->getD()), q;
00101 q = mt * v;
00102 it->setNormal(GLVector3d(q.x, q.y, q.z));
00103 it->setD(q.w);
00104 }
00105 if (normalize)
00106 normalizeAllPlanes();
00107 }
00108
00109 Frustum::Halfspace Frustum::classifyPoint(const TPlane &plane, const GLVector3d &point, const double eps)
00110 {
00111 const double f(plane.getNormal().dot(point) + plane.getD());
00112
00113 if (f < eps)
00114 return NEGATIVE;
00115 else if (f > eps)
00116 return POSITIVE;
00117
00118 return ON_PLANE;
00119 }
00120
00121 bool Frustum::testPoint(const GLVector3d &p)
00122 {
00123 Halfspace h[6];
00124 h[0] = classifyPoint(getPlane(PL_LEFT), p);
00125 h[1] = classifyPoint(getPlane(PL_TOP), p);
00126 h[2] = classifyPoint(getPlane(PL_RIGHT), p);
00127 h[3] = classifyPoint(getPlane(PL_BOTTOM), p);
00128 h[4] = classifyPoint(getPlane(PL_FAR), p);
00129 h[5] = classifyPoint(getPlane(PL_NEAR), p);
00130
00131 bool inside = true;
00132 for (int i = 0; i < 6; ++i)
00133 inside &= (h[i] >= 0);
00134
00135 return inside;
00136 }
00137
00138 Frustum::ClipState Frustum::testSphere(const GLVector3d ¢er, const double radius) const
00139 {
00140 double dist;
00141 int intersect(0);
00142
00143
00144 for (TPlaneContainer::const_iterator it = mPlanes.begin(), ite = mPlanes.end(); it != ite; ++it) {
00145
00146 dist = it->distanceToPoint(center);
00147 if (dist < -radius)
00148 return Frustum::OUTSIDE;
00149 if (fabs(dist) < radius)
00150 ++intersect;
00151 }
00152 if (!intersect)
00153 return Frustum::INSIDE;
00154 return Frustum::INTERSECT;
00155 }
00156
00157 Frustum::ClipState Frustum::testBox(const GLVector3d ¢er, const GLVector3d &extent) const
00158 {
00159 const double eps = 0.0;
00160
00161 GLVector3d v[8];
00162
00163 v[0] = center + GLVector3d(extent.x, extent.y, extent.z);
00164 v[1] = center + GLVector3d(extent.x, extent.y, -extent.z);
00165 v[2] = center + GLVector3d(extent.x, -extent.y, extent.z);
00166 v[3] = center + GLVector3d(extent.x, -extent.y, -extent.z);
00167 v[4] = center + GLVector3d(-extent.x, extent.y, extent.z);
00168 v[5] = center + GLVector3d(-extent.x, extent.y, -extent.z);
00169 v[6] = center + GLVector3d(-extent.x, -extent.y, extent.z);
00170 v[7] = center + GLVector3d(-extent.x, -extent.y, -extent.z);
00171
00172
00173 bool all_inside = true;
00174 for (TPlaneContainer::const_iterator it = mPlanes.begin(), ite = mPlanes.end(); it != ite; ++it) {
00175 int outside_count = 0;
00176
00177
00178 for (int i = 0; i < 8; ++i) {
00179 Halfspace hs = classifyPoint(*it, v[i], eps);
00180 if (hs == NEGATIVE) {
00181 ++outside_count;
00182 all_inside = false;
00183 }
00184 }
00185
00186 if (outside_count == 8)
00187 return OUTSIDE;
00188 }
00189
00190 if (all_inside)
00191 return Frustum::INSIDE;
00192
00193 return Frustum::INTERSECT;
00194 }