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

游戏引擎

开发平台:

C++ Builder

  1. /** 
  2.  * @file linkability.cpp
  3.  * @author andrew@lindenlab.com
  4.  * @date 2007-04-23
  5.  * @brief Tests for the LLPrimLinkInfo template which computes the linkability of prims
  6.  *
  7.  * $LicenseInfo:firstyear=2007&license=viewergpl$
  8.  * 
  9.  * Copyright (c) 2007-2010, Linden Research, Inc.
  10.  * 
  11.  * Second Life Viewer Source Code
  12.  * The source code in this file ("Source Code") is provided by Linden Lab
  13.  * to you under the terms of the GNU General Public License, version 2.0
  14.  * ("GPL"), unless you have obtained a separate licensing agreement
  15.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  16.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18.  * 
  19.  * There are special exceptions to the terms and conditions of the GPL as
  20.  * it is applied to this Source Code. View the full text of the exception
  21.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  22.  * online at
  23.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24.  * 
  25.  * By copying, modifying or distributing this software, you acknowledge
  26.  * that you have read and understood your obligations described above,
  27.  * and agree to abide by those obligations.
  28.  * 
  29.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31.  * COMPLETENESS OR PERFORMANCE.
  32.  * $/LicenseInfo$
  33.  */
  34. #include "linden_common.h"
  35. #include "lltut.h"
  36. #include "llprimlinkinfo.h"
  37. #include "llrand.h"
  38. // helper function
  39. void randomize_sphere(LLSphere& sphere, F32 center_range, F32 radius_range)
  40. {
  41. F32 radius = ll_frand(2.f * radius_range) - radius_range;
  42. LLVector3 center;
  43. for (S32 i=0; i<3; ++i)
  44. {
  45. center.mV[i] = ll_frand(2.f * center_range) - center_range;
  46. }
  47. sphere.setRadius(radius);
  48. sphere.setCenter(center);
  49. }
  50. // helper function.  Same as above with a min and max radius.
  51. void randomize_sphere(LLSphere& sphere, F32 center_range, F32 minimum_radius, F32 maximum_radius)
  52. {
  53. F32 radius = ll_frand(maximum_radius - minimum_radius) + minimum_radius; 
  54. LLVector3 center;
  55. for (S32 i=0; i<3; ++i)
  56. {
  57. center.mV[i] = ll_frand(2.f * center_range) - center_range;
  58. }
  59. sphere.setRadius(radius);
  60. sphere.setCenter(center);
  61. }
  62. // helper function
  63. bool random_sort( const LLPrimLinkInfo< S32 >&, const LLPrimLinkInfo< S32 >& b)
  64. {
  65. return (ll_rand(64) < 32);
  66. }
  67. namespace tut
  68. {
  69. struct linkable_data
  70. {
  71. LLPrimLinkInfo<S32> info;
  72. };
  73. typedef test_group<linkable_data> linkable_test;
  74. typedef linkable_test::object linkable_object;
  75. tut::linkable_test wtf("prim linkability");
  76. template<> template<>
  77. void linkable_object::test<1>()
  78. {
  79. // Here we test the boundary of the LLPrimLinkInfo::canLink() method 
  80. // between semi-random middle-sized objects.
  81. S32 number_of_tests = 100;
  82. for (S32 test = 0; test < number_of_tests; ++test)
  83. {
  84. // compute the radii that would provide the above max link distance
  85. F32 first_radius = 0.f;
  86. F32 second_radius = 0.f;
  87. // compute a random center for the first sphere
  88. // compute some random max link distance
  89. F32 max_link_span = ll_frand(MAX_OBJECT_SPAN);
  90. if (max_link_span < OBJECT_SPAN_BONUS)
  91. {
  92. max_link_span += OBJECT_SPAN_BONUS;
  93. }
  94. LLVector3 first_center(
  95. ll_frand(2.f * max_link_span) - max_link_span, 
  96. ll_frand(2.f * max_link_span) - max_link_span, 
  97. ll_frand(2.f * max_link_span) - max_link_span);
  98. // put the second sphere at the right distance from the origin
  99. // such that it is within the max_link_distance of the first
  100. LLVector3 direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  101. direction.normalize();
  102. F32 half_milimeter = 0.0005f;
  103. LLVector3 second_center;
  104. // max_span = 3 * (first_radius + second_radius) + OBJECT_SPAN_BONUS
  105. // make sure they link at short distances
  106. {
  107. second_center = first_center + (OBJECT_SPAN_BONUS - half_milimeter) * direction;
  108. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  109. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  110. ensure("these nearby objects should link", first_info.canLink(second_info) );
  111. }
  112. // make sure they fail to link if we move them apart just a little bit
  113. {
  114. second_center = first_center + (OBJECT_SPAN_BONUS + half_milimeter) * direction;
  115. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  116. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  117. ensure("these nearby objects should NOT link", !first_info.canLink(second_info) );
  118. }
  119. // make sure the objects link or not at medium distances
  120. {
  121. first_radius = 0.3f * ll_frand(max_link_span - OBJECT_SPAN_BONUS);
  122. // This is the exact second radius that will link at exactly our random max_link_distance
  123. second_radius = ((max_link_span - OBJECT_SPAN_BONUS) / 3.f) - first_radius;
  124. second_center = first_center + (max_link_span - first_radius - second_radius - half_milimeter) * direction;
  125. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  126. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  127. ensure("these objects should link", first_info.canLink(second_info) );
  128. }
  129. // make sure they fail to link if we move them apart just a little bit
  130. {
  131. // move the second sphere such that it is a little too far from the first
  132. second_center += (2.f * half_milimeter) * direction;
  133. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  134. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  135. ensure("these objects should NOT link", !first_info.canLink(second_info) );
  136. }
  137. // make sure things don't link at far distances
  138. {
  139. second_center = first_center + (MAX_OBJECT_SPAN + 2.f * half_milimeter) * direction;
  140. second_radius = 0.3f * MAX_OBJECT_SPAN;
  141. LLPrimLinkInfo<S32> first_info(0, LLSphere(first_center, first_radius) );
  142. LLPrimLinkInfo<S32> second_info(1, LLSphere(second_center, second_radius) );
  143. ensure("these objects should NOT link", !first_info.canLink(second_info) );
  144. }
  145. }
  146. }
  147. template<> template<>
  148. void linkable_object::test<2>()
  149. {
  150. // Consider a row of eight spheres in a row, each 10m in diameter and centered
  151. // at 10m intervals:  01234567.
  152. F32 radius = 5.f;
  153. F32 spacing = 10.f;
  154. LLVector3 line_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  155. line_direction.normalize();
  156. LLVector3 first_center(ll_frand(2.f * spacing) -spacing, ll_frand(2.f * spacing) - spacing, ll_frand(2.f * spacing) - spacing);
  157. LLPrimLinkInfo<S32> infos[8];
  158. for (S32 index = 0; index < 8; ++index)
  159. {
  160. LLVector3 center = first_center + ((F32)(index) * spacing) * line_direction;
  161. infos[index].set(index, LLSphere(center, radius));
  162. }
  163. // Max span for 2 spheres of 5m radius is 3 * (5 + 5) + 1 = 31m
  164. // spheres 0&2 have a 30m span (from outside edge to outside edge) and should link
  165. {
  166. LLPrimLinkInfo<S32> root_info = infos[0];
  167. std::list< LLPrimLinkInfo<S32> > info_list;
  168. info_list.push_back(infos[2]);
  169. root_info.mergeLinkableSet(info_list);
  170. S32 prim_count = root_info.getPrimCount();
  171. ensure_equals("0&2 prim count should be 2", prim_count, 2);
  172. ensure_equals("0&2 unlinkable list should have length 0", (S32) info_list.size(), 0);
  173. }
  174. // spheres 0&3 have a 40 meter span and should NOT link outright
  175. {
  176. LLPrimLinkInfo<S32> root_info = infos[0];
  177. std::list< LLPrimLinkInfo<S32> > info_list;
  178. info_list.push_back(infos[3]);
  179. root_info.mergeLinkableSet(info_list);
  180. S32 prim_count = root_info.getPrimCount();
  181. ensure_equals("0&4 prim count should be 1", prim_count, 1);
  182. ensure_equals("0&4 unlinkable list should have length 1", (S32) info_list.size(), 1);
  183. }
  184. // spheres 0-4 should link no matter what order : 01234
  185. // Total span = 50m, 012 link with a r=15.5 giving max span of 3 * (15.5 + 5) + 1 = 62.5, but the cap is 54m
  186. {
  187. LLPrimLinkInfo<S32> root_info = infos[0];
  188. std::list< LLPrimLinkInfo<S32> > info_list;
  189. for (S32 index = 1; index < 5; ++index)
  190. {
  191. info_list.push_back(infos[index]);
  192. }
  193. root_info.mergeLinkableSet(info_list);
  194. S32 prim_count = root_info.getPrimCount();
  195. ensure_equals("01234 prim count should be 5", prim_count, 5);
  196. ensure_equals("01234 unlinkable list should have length 0", (S32) info_list.size(), 0);
  197. }
  198. // spheres 0-5 should link no matter what order : 04321
  199. {
  200. LLPrimLinkInfo<S32> root_info = infos[0];
  201. std::list< LLPrimLinkInfo<S32> > info_list;
  202. for (S32 index = 4; index > 0; --index)
  203. {
  204. info_list.push_back(infos[index]);
  205. }
  206. root_info.mergeLinkableSet(info_list);
  207. S32 prim_count = root_info.getPrimCount();
  208. ensure_equals("04321 prim count should be 5", prim_count, 5);
  209. ensure_equals("04321 unlinkable list should have length 0", (S32) info_list.size(), 0);
  210. }
  211. // spheres 0-4 should link no matter what order : 01423
  212. {
  213. LLPrimLinkInfo<S32> root_info = infos[0];
  214. std::list< LLPrimLinkInfo<S32> > info_list;
  215. info_list.push_back(infos[1]);
  216. info_list.push_back(infos[4]);
  217. info_list.push_back(infos[2]);
  218. info_list.push_back(infos[3]);
  219. root_info.mergeLinkableSet(info_list);
  220. S32 prim_count = root_info.getPrimCount();
  221. ensure_equals("01423 prim count should be 5", prim_count, 5);
  222. ensure_equals("01423 unlinkable list should have length 0", (S32) info_list.size(), 0);
  223. }
  224. // spheres 0-5 should NOT fully link, only 0-4 
  225. {
  226. LLPrimLinkInfo<S32> root_info = infos[0];
  227. std::list< LLPrimLinkInfo<S32> > info_list;
  228. for (S32 index = 1; index < 6; ++index)
  229. {
  230. info_list.push_back(infos[index]);
  231. }
  232. root_info.mergeLinkableSet(info_list);
  233. S32 prim_count = root_info.getPrimCount();
  234. ensure_equals("012345 prim count should be 5", prim_count, 5);
  235. ensure_equals("012345 unlinkable list should have length 1", (S32) info_list.size(), 1);
  236. std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
  237. if (info_itr != info_list.end())
  238. {
  239. // examine the contents of the unlinked info
  240. std::list<S32> unlinked_indecies;
  241. info_itr->getData(unlinked_indecies);
  242. // make sure there is only one index in the unlinked_info
  243. ensure_equals("012345 unlinkable index count should be 1", (S32) unlinked_indecies.size(), 1);
  244. // make sure its value is 6
  245. std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
  246. S32 unlinkable_index = *unlinked_index_itr;
  247. ensure_equals("012345 unlinkable index should be 5", (S32) unlinkable_index, 5);
  248. }
  249. }
  250. // spheres 0-7 should NOT fully link, only 0-5 
  251. {
  252. LLPrimLinkInfo<S32> root_info = infos[0];
  253. std::list< LLPrimLinkInfo<S32> > info_list;
  254. for (S32 index = 1; index < 8; ++index)
  255. {
  256. info_list.push_back(infos[index]);
  257. }
  258. root_info.mergeLinkableSet(info_list);
  259. S32 prim_count = root_info.getPrimCount();
  260. ensure_equals("01234567 prim count should be 5", prim_count, 5);
  261. // Should be 1 linkinfo on unlinkable that has 2 prims
  262. ensure_equals("01234567 unlinkable list should have length 1", (S32) info_list.size(), 1);
  263. std::list< LLPrimLinkInfo<S32> >::iterator info_itr = info_list.begin();
  264. if (info_itr != info_list.end())
  265. {
  266. // make sure there is only one index in the unlinked_info
  267. std::list<S32> unlinked_indecies;
  268. info_itr->getData(unlinked_indecies);
  269. ensure_equals("0123456 unlinkable index count should be 3", (S32) unlinked_indecies.size(), 3);
  270. // make sure its values are 6 and 7
  271. std::list<S32>::iterator unlinked_index_itr = unlinked_indecies.begin();
  272. S32 unlinkable_index = *unlinked_index_itr;
  273. ensure_equals("0123456 first unlinkable index should be 5", (S32) unlinkable_index, 5);
  274. ++unlinked_index_itr;
  275. unlinkable_index = *unlinked_index_itr;
  276. ensure_equals("0123456 second unlinkable index should be 6", (S32) unlinkable_index, 6);
  277. ++unlinked_index_itr;
  278. unlinkable_index = *unlinked_index_itr;
  279. ensure_equals("0123456 third unlinkable index should be 7", (S32) unlinkable_index, 7);
  280. }
  281. }
  282. }
  283. template<> template<>
  284. void linkable_object::test<3>()
  285. {
  286. // Here we test the link results between an LLPrimLinkInfo and a set of
  287. // randomized LLPrimLinkInfos where the expected results are known.
  288. S32 number_of_tests = 5;
  289. for (S32 test = 0; test < number_of_tests; ++test)
  290. {
  291. // the radii are known
  292. F32 first_radius = 1.f;
  293. F32 second_radius = 2.f;
  294. F32 third_radius = 3.f;
  295. // compute the distances
  296. F32 half_milimeter = 0.0005f;
  297. F32 max_first_second_span = 3.f * (first_radius + second_radius) + OBJECT_SPAN_BONUS;
  298. F32 linkable_distance = max_first_second_span - first_radius - second_radius - half_milimeter;
  299. F32 max_full_span = 3.f * (0.5f * max_first_second_span + third_radius) + OBJECT_SPAN_BONUS;
  300. F32 unlinkable_distance = max_full_span - 0.5f * linkable_distance - third_radius + half_milimeter;
  301. // compute some random directions
  302. LLVector3 first_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  303. first_direction.normalize();
  304. LLVector3 second_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  305. second_direction.normalize();
  306. LLVector3 third_direction(ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f, ll_frand(2.f) - 1.f);
  307. third_direction.normalize();
  308. // compute the centers
  309. LLVector3 first_center = ll_frand(10.f) * first_direction;
  310. LLVector3 second_center = first_center + ll_frand(linkable_distance) * second_direction;
  311. LLVector3 first_join_center = 0.5f * (first_center + second_center);
  312. LLVector3 third_center = first_join_center + unlinkable_distance * third_direction;
  313. // make sure the second info links and the third does not
  314. {
  315. // initialize the infos
  316. S32 index = 0;
  317. LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
  318. LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
  319. LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
  320. // put the second and third infos in a list
  321. std::list< LLPrimLinkInfo<S32> > info_list;
  322. info_list.push_back(second_info);
  323. info_list.push_back(third_info); 
  324. // merge the list with the first_info
  325. first_info.mergeLinkableSet(info_list);
  326. S32 prim_count = first_info.getPrimCount();
  327. ensure_equals("prim count should be 2", prim_count, 2);
  328. ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
  329. }
  330. // reverse the order and make sure we get the same results
  331. {
  332. // initialize the infos
  333. S32 index = 0;
  334. LLPrimLinkInfo<S32> first_info(index++, LLSphere(first_center, first_radius));
  335. LLPrimLinkInfo<S32> second_info(index++, LLSphere(second_center, second_radius));
  336. LLPrimLinkInfo<S32> third_info(index++, LLSphere(third_center, third_radius));
  337. // build the list in the reverse order
  338. std::list< LLPrimLinkInfo<S32> > info_list;
  339. info_list.push_back(third_info); 
  340. info_list.push_back(second_info);
  341. // merge the list with the first_info
  342. first_info.mergeLinkableSet(info_list);
  343. S32 prim_count = first_info.getPrimCount();
  344. ensure_equals("prim count should be 2", prim_count, 2);
  345. ensure_equals("unlinkable list should have length 1", (S32) info_list.size(), 1);
  346. }
  347. }
  348. }
  349. template<> template<>
  350. void linkable_object::test<4>()
  351. {
  352. // Here we test whether linkability is invarient under permutations
  353. // of link order.  To do this we generate a bunch of random spheres
  354. // and then try to link them in different ways.
  355. //
  356. // NOTE: the linkability will only be invarient if there is only one
  357. // linkable solution.  Multiple solutions will exist if the set of 
  358. // candidates are larger than the maximum linkable distance, or more 
  359. // numerous than a single linked object can contain.  This is easily 
  360. // understood by considering a very large set of link candidates, 
  361. // and first linking preferentially to the left until linking fails, 
  362. // then doing the same to the right -- the final solutions will differ.
  363. // Hence for this test we must generate candidate sets that lie within 
  364. // the linkability envelope of a single object.
  365. //
  366. // NOTE: a random set of objects will tend to either be totally linkable
  367. // or totally not.  That is, the random orientations that 
  368. F32 root_center_range = 0.f;
  369. F32 min_prim_radius = 0.1f;
  370. F32 max_prim_radius = 2.f;
  371. // Linkability is min(MAX_OBJECT_SPAN,3 *( R1 + R2 ) + BONUS)
  372. // 3 * (min_prim_radius + min_prim_radius) + OBJECT_SPAN_BONUS = 6 * min_prim_radius + OBJECT_SPAN_BONUS;
  373. // Use .45 instead of .5 to gaurantee objects are within the minimum span.
  374. F32 child_center_range = 0.45f * ( (6*min_prim_radius) + OBJECT_SPAN_BONUS );
  375. S32 number_of_tests = 100;
  376. S32 number_of_spheres = 10;
  377. S32 number_of_scrambles = 10;
  378. S32 number_of_random_bubble_sorts = 10;
  379. for (S32 test = 0; test < number_of_tests; ++test)
  380. {
  381. LLSphere sphere;
  382. S32 sphere_index = 0;
  383. // build the root piece
  384. randomize_sphere(sphere, root_center_range, min_prim_radius, max_prim_radius);
  385. info.set( sphere_index++, sphere );
  386. // build the unlinked pieces
  387. std::list< LLPrimLinkInfo<S32> > info_list;
  388. for (; sphere_index < number_of_spheres; ++sphere_index)
  389. {
  390. randomize_sphere(sphere, child_center_range, min_prim_radius, max_prim_radius);
  391. LLPrimLinkInfo<S32> child_info( sphere_index, sphere );
  392. info_list.push_back(child_info);
  393. }
  394. // declare the variables used to store the results
  395. std::list<S32> first_linked_list;
  396. {
  397. // the link attempt will modify our original info's, so we
  398. // have to make copies of the originals for testing
  399. LLPrimLinkInfo<S32> test_info( 0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
  400. std::list< LLPrimLinkInfo<S32> > test_list;
  401. test_list.assign(info_list.begin(), info_list.end());
  402. // try to link
  403. test_info.mergeLinkableSet(test_list);
  404. ensure("All prims should link, but did not.",test_list.empty());
  405. // store the results 
  406. test_info.getData(first_linked_list);
  407. first_linked_list.sort();
  408. }
  409. // try to link the spheres in various random orders
  410. for (S32 scramble = 0; scramble < number_of_scrambles; ++scramble)
  411. {
  412. LLPrimLinkInfo<S32> test_info(0, LLSphere(info.getCenter(), 0.5f * info.getDiameter()) );
  413. // scramble the order of the info_list
  414. std::list< LLPrimLinkInfo<S32> > test_list;
  415. test_list.assign(info_list.begin(), info_list.end());
  416. for (S32 i = 0; i < number_of_random_bubble_sorts; i++)
  417. {
  418. test_list.sort(random_sort);
  419. }
  420. // try to link
  421. test_info.mergeLinkableSet(test_list);
  422. // get the results 
  423. std::list<S32> linked_list;
  424. test_info.getData(linked_list);
  425. linked_list.sort();
  426. ensure_equals("linked set size should be order independent",linked_list.size(),first_linked_list.size());
  427. }
  428. }
  429. }
  430. }