00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "boundingSphere.h"
00020 #include "boundingHexahedron.h"
00021 #include "boundingLine.h"
00022 #include "config_mathutil.h"
00023 #include "dcast.h"
00024
00025 #include <math.h>
00026 #include <algorithm>
00027
00028 TypeHandle BoundingSphere::_type_handle;
00029
00030 BoundingVolume *BoundingSphere::
00031 make_copy() const {
00032 return new BoundingSphere(*this);
00033 }
00034
00035 LPoint3f BoundingSphere::
00036 get_min() const {
00037 nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00038 nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00039 return LPoint3f(_center[0] - _radius,
00040 _center[1] - _radius,
00041 _center[2] - _radius);
00042 }
00043
00044 LPoint3f BoundingSphere::
00045 get_max() const {
00046 nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00047 nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00048 return LPoint3f(_center[0] + _radius,
00049 _center[1] + _radius,
00050 _center[2] + _radius);
00051 }
00052
00053 LPoint3f BoundingSphere::
00054 get_approx_center() const {
00055 nassertr(!is_empty(), LPoint3f(0.0f, 0.0f, 0.0f));
00056 nassertr(!is_infinite(), LPoint3f(0.0f, 0.0f, 0.0f));
00057 return get_center();
00058 }
00059
00060 void BoundingSphere::
00061 xform(const LMatrix4f &mat) {
00062 nassertv(!mat.is_nan());
00063
00064 if (!is_empty() && !is_infinite()) {
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078 float xd,yd,zd,scale;
00079
00080 #define ROW_DOTTED(mat,ROWNUM) \
00081 (mat._m.m._##ROWNUM##0*mat._m.m._##ROWNUM##0 + \
00082 mat._m.m._##ROWNUM##1*mat._m.m._##ROWNUM##1 + \
00083 mat._m.m._##ROWNUM##2*mat._m.m._##ROWNUM##2)
00084
00085 xd = ROW_DOTTED(mat,0);
00086 yd = ROW_DOTTED(mat,1);
00087 zd = ROW_DOTTED(mat,2);
00088
00089 scale = max(xd,yd);
00090 scale = max(scale,zd);
00091 scale = sqrtf(scale);
00092
00093
00094 _radius *= scale;
00095
00096
00097 _center = _center * mat;
00098 }
00099 }
00100
00101 void BoundingSphere::
00102 output(ostream &out) const {
00103 if (is_empty()) {
00104 out << "bsphere, empty";
00105 } else if (is_infinite()) {
00106 out << "bsphere, infinite";
00107 } else {
00108 out << "bsphere, c (" << _center << "), r " << _radius;
00109 }
00110 }
00111
00112 bool BoundingSphere::
00113 extend_other(BoundingVolume *other) const {
00114 return other->extend_by_sphere(this);
00115 }
00116
00117 bool BoundingSphere::
00118 around_other(BoundingVolume *other,
00119 const BoundingVolume **first,
00120 const BoundingVolume **last) const {
00121 return other->around_spheres(first, last);
00122 }
00123
00124 int BoundingSphere::
00125 contains_other(const BoundingVolume *other) const {
00126 return other->contains_sphere(this);
00127 }
00128
00129
00130 bool BoundingSphere::
00131 extend_by_point(const LPoint3f &point) {
00132 nassertr(!point.is_nan(), false);
00133
00134 if (is_empty()) {
00135 _center = point;
00136 _radius = 0.0f;
00137 _flags = 0;
00138 } else if (!is_infinite()) {
00139 LVector3f v = point - _center;
00140 float dist2 = dot(v, v);
00141 if (dist2 > _radius * _radius) {
00142 _radius = sqrtf(dist2);
00143 }
00144 }
00145 return true;
00146 }
00147
00148 bool BoundingSphere::
00149 extend_by_sphere(const BoundingSphere *sphere) {
00150 nassertr(!sphere->is_empty() && !sphere->is_infinite(), false);
00151 nassertr(!is_infinite(), false);
00152
00153 if (is_empty()) {
00154 _center = sphere->_center;
00155 _radius = sphere->_radius;
00156 _flags = 0;
00157 } else {
00158 float dist = length(sphere->_center - _center);
00159
00160 _radius = max(_radius, dist + sphere->_radius);
00161 }
00162 return true;
00163 }
00164
00165 bool BoundingSphere::
00166 extend_by_hexahedron(const BoundingHexahedron *hexahedron) {
00167 return extend_by_finite(hexahedron);
00168 }
00169
00170 bool BoundingSphere::
00171 extend_by_finite(const FiniteBoundingVolume *volume) {
00172 nassertr(!volume->is_empty(), false);
00173
00174 LVector3f min1 = volume->get_min();
00175 LVector3f max1 = volume->get_max();
00176
00177 if (is_empty()) {
00178 _center = (min1 + max1) * 0.5f;
00179 _radius = length(LVector3f(max1 - _center));
00180 _flags = 0;
00181 } else {
00182 LVector3f v = max1 - _center;
00183 float dist2 = dot(v, v);
00184
00185 if (dist2 > _radius * _radius) {
00186 _radius = sqrtf(dist2);
00187 }
00188 }
00189
00190 return true;
00191 }
00192
00193 bool BoundingSphere::
00194 around_points(const LPoint3f *first, const LPoint3f *last) {
00195 nassertr(first != last, false);
00196
00197
00198
00199 const LPoint3f *p = first;
00200
00201 #ifndef NDEBUG
00202
00203 int skipped_nan = 0;
00204 while (p != last && (*p).is_nan()) {
00205 ++p;
00206 ++skipped_nan;
00207 }
00208 if (p == last) {
00209 mathutil_cat.warning()
00210 << "BoundingSphere around NaN\n";
00211 return false;
00212 }
00213 #endif
00214
00215 LPoint3f min_box = *p;
00216 LPoint3f max_box = *p;
00217 ++p;
00218
00219 #ifndef NDEBUG
00220
00221 while (p != last && (*p).is_nan()) {
00222 ++p;
00223 ++skipped_nan;
00224 }
00225 #endif
00226
00227 if (p == last) {
00228
00229
00230
00231 _center = min_box;
00232 _radius = 0.0f;
00233
00234 } else {
00235
00236 while (p != last) {
00237 #ifndef NDEBUG
00238
00239 if ((*p).is_nan()) {
00240 ++skipped_nan;
00241 } else
00242 #endif
00243 {
00244 min_box.set(min(min_box[0], (*p)[0]),
00245 min(min_box[1], (*p)[1]),
00246 min(min_box[2], (*p)[2]));
00247 max_box.set(max(max_box[0], (*p)[0]),
00248 max(max_box[1], (*p)[1]),
00249 max(max_box[2], (*p)[2]));
00250 }
00251 ++p;
00252 }
00253
00254
00255 _center = (min_box + max_box) * 0.5f;
00256
00257
00258 float max_dist2 = 0.0f;
00259 for (p = first; p != last; ++p) {
00260 LVector3f v = (*p) - _center;
00261 float dist2 = dot(v, v);
00262 max_dist2 = max(max_dist2, dist2);
00263 }
00264
00265 _radius = sqrtf(max_dist2);
00266 }
00267
00268 #ifndef NDEBUG
00269 if (skipped_nan != 0) {
00270 mathutil_cat.warning()
00271 << "BoundingSphere ignored " << skipped_nan << " NaN points of "
00272 << (last - first) << " total.\n";
00273 }
00274 #endif
00275
00276 _flags = 0;
00277
00278 return true;
00279 }
00280
00281 bool BoundingSphere::
00282 around_spheres(const BoundingVolume **first,
00283 const BoundingVolume **last) {
00284 return around_finite(first, last);
00285 }
00286
00287 bool BoundingSphere::
00288 around_hexahedrons(const BoundingVolume **first,
00289 const BoundingVolume **last) {
00290 return around_finite(first, last);
00291 }
00292
00293 bool BoundingSphere::
00294 around_finite(const BoundingVolume **first,
00295 const BoundingVolume **last) {
00296 nassertr(first != last, false);
00297
00298
00299
00300
00301
00302
00303
00304 const BoundingVolume **p = first;
00305 nassertr(!(*p)->is_empty() && !(*p)->is_infinite(), false);
00306 nassertr((*p)->is_of_type(FiniteBoundingVolume::get_class_type()), false);
00307 const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
00308 LPoint3f min_box = vol->get_min();
00309 LPoint3f max_box = vol->get_max();
00310
00311 bool any_unknown = false;
00312
00313 for (++p; p != last; ++p) {
00314 nassertr(!(*p)->is_infinite(), false);
00315 if (!(*p)->is_empty() &&
00316 (*p)->is_of_type(FiniteBoundingVolume::get_class_type())) {
00317 const FiniteBoundingVolume *vol = DCAST(FiniteBoundingVolume, *p);
00318 LPoint3f min1 = vol->get_min();
00319 LPoint3f max1 = vol->get_max();
00320 min_box.set(min(min_box[0], min1[0]),
00321 min(min_box[1], min1[1]),
00322 min(min_box[2], min1[2]));
00323 max_box.set(max(max_box[0], max1[0]),
00324 max(max_box[1], max1[1]),
00325 max(max_box[2], max1[2]));
00326
00327 if (!(*p)->is_of_type(BoundingSphere::get_class_type())) {
00328 any_unknown = true;
00329 }
00330 }
00331 }
00332
00333
00334 _center = (min_box + max_box) * 0.5f;
00335
00336 if (any_unknown) {
00337
00338
00339
00340 _radius = length(max_box - _center);
00341
00342 } else {
00343
00344
00345 _radius = 0.0f;
00346 for (p = first; p != last; ++p) {
00347 if (!(*p)->is_empty()) {
00348 if ((*p)->is_of_type(BoundingSphere::get_class_type())) {
00349 const BoundingSphere *sphere = DCAST(BoundingSphere, *p);
00350 float dist = length(sphere->_center - _center);
00351 _radius = max(_radius, dist + sphere->_radius);
00352 } else {
00353
00354 mathutil_cat.error()
00355 << "Unexpected type in BoundingSphere::around_finite()\n";
00356 nassertr(false, false);
00357 }
00358 }
00359 }
00360 }
00361
00362 _flags = 0;
00363 return true;
00364 }
00365
00366 int BoundingSphere::
00367 contains_point(const LPoint3f &point) const {
00368 nassertr(!point.is_nan(), IF_no_intersection);
00369
00370 if (is_empty()) {
00371 return IF_no_intersection;
00372
00373 } else if (is_infinite()) {
00374 return IF_possible | IF_some | IF_all;
00375
00376 } else {
00377 LVector3f v = point - _center;
00378 float dist2 = dot(v, v);
00379 return (dist2 <= _radius * _radius) ?
00380 IF_possible | IF_some | IF_all : IF_no_intersection;
00381 }
00382 }
00383
00384 int BoundingSphere::
00385 contains_lineseg(const LPoint3f &a, const LPoint3f &b) const {
00386 nassertr(!a.is_nan() && !b.is_nan(), IF_no_intersection);
00387
00388 if (a == b) {
00389 return contains_point(a);
00390 }
00391 if (is_empty()) {
00392 return IF_no_intersection;
00393
00394 } else if (is_infinite()) {
00395 return IF_possible | IF_some | IF_all;
00396
00397 } else {
00398 LPoint3f from = a;
00399 LVector3f delta = b - a;
00400 float t1, t2;
00401
00402
00403
00404 float A = dot(delta, delta);
00405
00406 nassertr(A != 0.0f, 0);
00407
00408 LVector3f fc = from - _center;
00409 float B = 2.0f * dot(delta, fc);
00410 float C = dot(fc, fc) - _radius * _radius;
00411
00412 float radical = B*B - 4.0f*A*C;
00413
00414 if (IS_NEARLY_ZERO(radical)) {
00415
00416 t1 = t2 = -B / (2.0f*A);
00417 return (t1 >= 0.0f && t1 <= 1.0f) ?
00418 IF_possible | IF_some : IF_no_intersection;
00419 }
00420
00421 if (radical < 0.0f) {
00422
00423 return IF_no_intersection;
00424 }
00425
00426 float reciprocal_2A = 1.0f/(2.0f*A);
00427 float sqrt_radical = sqrtf(radical);
00428
00429 t1 = ( -B - sqrt_radical ) * reciprocal_2A;
00430 t2 = ( -B + sqrt_radical ) * reciprocal_2A;
00431
00432 if (t1 >= 0.0f && t2 <= 1.0f) {
00433 return IF_possible | IF_some | IF_all;
00434 } else if (t1 <= 1.0f && t2 >= 0.0f) {
00435 return IF_possible | IF_some;
00436 } else {
00437 return IF_no_intersection;
00438 }
00439 }
00440 }
00441
00442 int BoundingSphere::
00443 contains_sphere(const BoundingSphere *sphere) const {
00444 nassertr(!is_empty() && !is_infinite(), 0);
00445 nassertr(!sphere->is_empty() && !sphere->is_infinite(), 0);
00446
00447 LVector3f v = sphere->_center - _center;
00448 float dist2 = dot(v, v);
00449
00450 if (_radius >= sphere->_radius &&
00451 dist2 <= (_radius - sphere->_radius) * (_radius - sphere->_radius)) {
00452
00453 return IF_possible | IF_some | IF_all;
00454
00455 } else if (dist2 > (_radius + sphere->_radius) * (_radius + sphere->_radius)) {
00456
00457 return IF_no_intersection;
00458
00459 } else {
00460
00461 return IF_possible | IF_some;
00462 }
00463 }
00464
00465 int BoundingSphere::
00466 contains_hexahedron(const BoundingHexahedron *hexahedron) const {
00467 return hexahedron->contains_sphere(this) & ~IF_all;
00468 }
00469
00470 int BoundingSphere::
00471 contains_line(const BoundingLine *line) const {
00472 return line->contains_sphere(this) & ~IF_all;
00473 }