llsphere.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:11k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file llsphere.cpp
  3.  * @author Andrew Meadows
  4.  * @brief Simple line class that can compute nearest approach between two lines
  5.  *
  6.  * $LicenseInfo:firstyear=2007&license=viewergpl$
  7.  * 
  8.  * Copyright (c) 2007-2010, Linden Research, Inc.
  9.  * 
  10.  * Second Life Viewer Source Code
  11.  * The source code in this file ("Source Code") is provided by Linden Lab
  12.  * to you under the terms of the GNU General Public License, version 2.0
  13.  * ("GPL"), unless you have obtained a separate licensing agreement
  14.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  15.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17.  * 
  18.  * There are special exceptions to the terms and conditions of the GPL as
  19.  * it is applied to this Source Code. View the full text of the exception
  20.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  21.  * online at
  22.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23.  * 
  24.  * By copying, modifying or distributing this software, you acknowledge
  25.  * that you have read and understood your obligations described above,
  26.  * and agree to abide by those obligations.
  27.  * 
  28.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30.  * COMPLETENESS OR PERFORMANCE.
  31.  * $/LicenseInfo$
  32.  */
  33. #include "linden_common.h"
  34. #include "llsphere.h"
  35. LLSphere::LLSphere()
  36. : mCenter(0.f, 0.f, 0.f),
  37. mRadius(0.f)
  38. { }
  39. LLSphere::LLSphere( const LLVector3& center, F32 radius)
  40. {
  41. set(center, radius);
  42. }
  43. void LLSphere::set( const LLVector3& center, F32 radius )
  44. {
  45. mCenter = center;
  46. setRadius(radius);
  47. }
  48. void LLSphere::setCenter( const LLVector3& center)
  49. {
  50. mCenter = center;
  51. }
  52. void LLSphere::setRadius( F32 radius)
  53. {
  54. if (radius < 0.f)
  55. {
  56. radius = -radius;
  57. }
  58. mRadius = radius;
  59. }
  60. const LLVector3& LLSphere::getCenter() const
  61. {
  62. return mCenter;
  63. }
  64. F32 LLSphere::getRadius() const
  65. {
  66. return mRadius;
  67. }
  68. // returns 'TRUE' if this sphere completely contains other_sphere
  69. BOOL LLSphere::contains(const LLSphere& other_sphere) const
  70. {
  71. F32 separation = (mCenter - other_sphere.mCenter).length();
  72. return (mRadius >= separation + other_sphere.mRadius) ? TRUE : FALSE;
  73. }
  74. // returns 'TRUE' if this sphere completely contains other_sphere
  75. BOOL LLSphere::overlaps(const LLSphere& other_sphere) const
  76. {
  77. F32 separation = (mCenter - other_sphere.mCenter).length();
  78. return (separation <= mRadius + other_sphere.mRadius) ? TRUE : FALSE;
  79. }
  80. // returns overlap
  81. // negative overlap is closest approach
  82. F32 LLSphere::getOverlap(const LLSphere& other_sphere) const
  83. {
  84. // separation is distance from other_sphere's edge and this center
  85. return (mCenter - other_sphere.mCenter).length() - mRadius - other_sphere.mRadius;
  86. }
  87. bool LLSphere::operator==(const LLSphere& rhs) const
  88. {
  89. // TODO? -- use approximate equality for centers?
  90. return (mRadius == rhs.mRadius
  91. && mCenter == rhs.mCenter);
  92. }
  93. std::ostream& operator<<( std::ostream& output_stream, const LLSphere& sphere)
  94. {
  95. output_stream << "{center=" << sphere.mCenter << "," << "radius=" << sphere.mRadius << "}";
  96. return output_stream;
  97. }
  98. // static 
  99. // removes any spheres that are contained in others
  100. void LLSphere::collapse(std::vector<LLSphere>& sphere_list)
  101. {
  102. std::vector<LLSphere>::iterator first_itr = sphere_list.begin();
  103. while (first_itr != sphere_list.end())
  104. {
  105. bool delete_from_front = false;
  106. std::vector<LLSphere>::iterator second_itr = first_itr;
  107. ++second_itr;
  108. while (second_itr != sphere_list.end())
  109. {
  110. if (second_itr->contains(*first_itr))
  111. {
  112. delete_from_front = true;
  113. break;
  114. }
  115. else if (first_itr->contains(*second_itr))
  116. {
  117. sphere_list.erase(second_itr++);
  118. }
  119. else
  120. {
  121. ++second_itr;
  122. }
  123. }
  124. if (delete_from_front)
  125. {
  126. sphere_list.erase(first_itr++);
  127. }
  128. else
  129. {
  130. ++first_itr;
  131. }
  132. }
  133. }
  134. // static
  135. // returns the bounding sphere that contains both spheres
  136. LLSphere LLSphere::getBoundingSphere(const LLSphere& first_sphere, const LLSphere& second_sphere)
  137. {
  138. LLVector3 direction = second_sphere.mCenter - first_sphere.mCenter;
  139. // HACK -- it is possible to get enough floating point error in the 
  140. // other getBoundingSphere() method that we have to add some slop
  141. // at the end.  Unfortunately, this breaks the link-order invarience
  142. // for the linkability tests... unless we also apply the same slop
  143. // here.
  144. F32 half_milimeter = 0.0005f;
  145. F32 distance = direction.length();
  146. if (0.f == distance)
  147. {
  148. direction.setVec(1.f, 0.f, 0.f);
  149. }
  150. else
  151. {
  152. direction.normVec();
  153. }
  154. // the 'edge' is measured from the first_sphere's center
  155. F32 max_edge = 0.f;
  156. F32 min_edge = 0.f;
  157. max_edge = llmax(max_edge + first_sphere.getRadius(), max_edge + distance + second_sphere.getRadius() + half_milimeter);
  158. min_edge = llmin(min_edge - first_sphere.getRadius(), min_edge + distance - second_sphere.getRadius() - half_milimeter);
  159. F32 radius = 0.5f * (max_edge - min_edge);
  160. LLVector3 center = first_sphere.mCenter + (0.5f * (max_edge + min_edge)) * direction;
  161. return LLSphere(center, radius);
  162. }
  163. // static
  164. // returns the bounding sphere that contains an arbitrary set of spheres
  165. LLSphere LLSphere::getBoundingSphere(const std::vector<LLSphere>& sphere_list)
  166. {
  167. // this algorithm can get relatively inaccurate when the sphere 
  168. // collection is 'small' (contained within a bounding sphere of about 
  169. // 2 meters or less)
  170. // TODO -- improve the accuracy for small collections of spheres
  171. LLSphere bounding_sphere( LLVector3(0.f, 0.f, 0.f), 0.f );
  172. S32 sphere_count = sphere_list.size();
  173. if (1 == sphere_count)
  174. {
  175. // trivial case -- single sphere
  176. std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
  177. bounding_sphere = *sphere_itr;
  178. }
  179. else if (2 == sphere_count)
  180. {
  181. // trivial case -- two spheres
  182. std::vector<LLSphere>::const_iterator first_sphere = sphere_list.begin();
  183. std::vector<LLSphere>::const_iterator second_sphere = first_sphere;
  184. ++second_sphere;
  185. bounding_sphere = LLSphere::getBoundingSphere(*first_sphere, *second_sphere);
  186. }
  187. else if (sphere_count > 0)
  188. {
  189. // non-trivial case -- we will approximate the solution
  190. //
  191. // NOTE -- there is a fancy/fast way to do this for large 
  192. // numbers of arbirary N-dimensional spheres -- you can look it
  193. // up on the net.  We're dealing with 3D spheres at collection
  194. // sizes of 256 spheres or smaller, so we just use this
  195. // brute force method.
  196. // TODO -- perhaps would be worthwile to test for the solution where
  197. // the largest spanning radius just happens to work.  That is, where
  198. // there are really two spheres that determine the bounding sphere,
  199. // and all others are contained therein.
  200. // compute the AABB
  201. std::vector<LLSphere>::const_iterator first_itr = sphere_list.begin();
  202. LLVector3 max_corner = first_itr->getCenter() + first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
  203. LLVector3 min_corner = first_itr->getCenter() - first_itr->getRadius() * LLVector3(1.f, 1.f, 1.f);
  204. {
  205. std::vector<LLSphere>::const_iterator sphere_itr = sphere_list.begin();
  206. for (++sphere_itr; sphere_itr != sphere_list.end(); ++sphere_itr)
  207. {
  208. LLVector3 center = sphere_itr->getCenter();
  209. F32 radius = sphere_itr->getRadius();
  210. for (S32 i=0; i<3; ++i)
  211. {
  212. if (center.mV[i] + radius > max_corner.mV[i])
  213. {
  214. max_corner.mV[i] = center.mV[i] + radius;
  215. }
  216. if (center.mV[i] - radius < min_corner.mV[i])
  217. {
  218. min_corner.mV[i] = center.mV[i] - radius;
  219. }
  220. }
  221. }
  222. }
  223. // get the starting center and radius from the AABB
  224. LLVector3 diagonal = max_corner - min_corner;
  225. F32 bounding_radius = 0.5f * diagonal.length();
  226. LLVector3 bounding_center = 0.5f * (max_corner + min_corner);
  227. // compute the starting step-size
  228. F32 minimum_radius = 0.5f * llmin(diagonal.mV[VX], llmin(diagonal.mV[VY], diagonal.mV[VZ]));
  229. F32 step_length = bounding_radius - minimum_radius;
  230. S32 step_count = 0;
  231. S32 max_step_count = 12;
  232. F32 half_milimeter = 0.0005f;
  233. // wander the center around in search of tighter solutions
  234. S32 last_dx = 2; // 2 is out of bounds --> no match
  235. S32 last_dy = 2;
  236. S32 last_dz = 2;
  237. while (step_length > half_milimeter
  238. && step_count < max_step_count)
  239. {
  240. // the algorithm for testing the maximum radius could be expensive enough
  241. // that it makes sense to NOT duplicate testing when possible, so we keep
  242. // track of where we last tested, and only test the new points
  243. S32 best_dx = 0;
  244. S32 best_dy = 0;
  245. S32 best_dz = 0;
  246. // sample near the center of the box
  247. bool found_better_center = false;
  248. for (S32 dx = -1; dx < 2; ++dx)
  249. {
  250. for (S32 dy = -1; dy < 2; ++dy)
  251. {
  252. for (S32 dz = -1; dz < 2; ++dz)
  253. {
  254. if (dx == 0 && dy == 0 && dz == 0)
  255. {
  256. continue;
  257. }
  258. // count the number of indecies that match the last_*'s
  259. S32 match_count = 0;
  260. if (last_dx == dx) ++match_count;
  261. if (last_dy == dy) ++match_count;
  262. if (last_dz == dz) ++match_count;
  263. if (match_count == 2)
  264. {
  265. // we've already tested this point
  266. continue;
  267. }
  268. LLVector3 center = bounding_center;
  269. center.mV[VX] += (F32) dx * step_length;
  270. center.mV[VY] += (F32) dy * step_length;
  271. center.mV[VZ] += (F32) dz * step_length;
  272. // compute the radius of the bounding sphere
  273. F32 max_radius = 0.f;
  274. std::vector<LLSphere>::const_iterator sphere_itr;
  275. for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
  276. {
  277. F32 radius = (sphere_itr->getCenter() - center).length() + sphere_itr->getRadius();
  278. if (radius > max_radius)
  279. {
  280. max_radius = radius;
  281. }
  282. }
  283. if (max_radius < bounding_radius)
  284. {
  285. best_dx = dx;
  286. best_dy = dy;
  287. best_dz = dz;
  288. bounding_center = center;
  289. bounding_radius = max_radius;
  290. found_better_center = true;
  291. }
  292. }
  293. }
  294. }
  295. if (found_better_center)
  296. {
  297. // remember where we came from so we can avoid retesting
  298. last_dx = -best_dx;
  299. last_dy = -best_dy;
  300. last_dz = -best_dz;
  301. }
  302. else
  303. {
  304. // reduce the step size
  305. step_length *= 0.5f;
  306. //++step_count;
  307. // reset the last_*'s
  308. last_dx = 2; // 2 is out of bounds --> no match
  309. last_dy = 2;
  310. last_dz = 2;
  311. }
  312. }
  313. // HACK -- it is possible to get enough floating point error for the
  314. // bounding sphere to too small on the order of 10e-6, but we only need
  315. // it to be accurate to within about half a millimeter
  316. bounding_radius += half_milimeter;
  317. // this algorithm can get relatively inaccurate when the sphere 
  318. // collection is 'small' (contained within a bounding sphere of about 
  319. // 2 meters or less)
  320. // TODO -- fix this
  321. /* debug code
  322. {
  323. std::vector<LLSphere>::const_iterator sphere_itr;
  324. for (sphere_itr = sphere_list.begin(); sphere_itr != sphere_list.end(); ++sphere_itr)
  325. {
  326. F32 radius = (sphere_itr->getCenter() - bounding_center).length() + sphere_itr->getRadius();
  327. if (radius + 0.1f > bounding_radius)
  328. {
  329. std::cout << " rad = " << radius << "  bounding - rad = " << (bounding_radius - radius) << std::endl;
  330. }
  331. }
  332. std::cout << "n" << std::endl;
  333. }
  334. */ 
  335. bounding_sphere.set(bounding_center, bounding_radius);
  336. }
  337. return bounding_sphere;
  338. }