dynamicaabtreecull.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWPhys *
  23. * *
  24. * $Archive:: /Commando/Code/wwphys/dynamicaabtreecull.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Greg_h $*
  29. * *
  30. * $Modtime:: 3/06/02 11:24a $*
  31. * *
  32. * $Revision:: 22 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "dynamicaabtreecull.h"
  38. #include "pscene.h"
  39. #include "visrendercontext.h"
  40. #include "visoptimizationcontext.h"
  41. #include "visoptprogress.h"
  42. #include "visrasterizer.h"
  43. #include "boxrobj.h"
  44. #include "vector3i.h"
  45. #include "chunkio.h"
  46. #include "colmathfrustum.h"
  47. #include "colmathplane.h"
  48. #include "colmathaabox.h"
  49. #include "wwmemlog.h"
  50. const float DEFUALT_MAXOBJRADIUS = 7.5f;
  51. const float MIN_CELL_HEIGHT = 3.5f;
  52. /*
  53. ** Chunk ID's used by DynamicAABTreeClass
  54. */
  55. enum
  56. {
  57. DYNAMICAABTREE_CHUNK_AABTREE_CLASS_DATA = 622120600,
  58. DYNAMICAABTREE_CHUNK_VARIABLES,
  59. DYNAMICAABTREE_VARIABLE_BASEVISID = 0x00,
  60. DYNAMICAABTREE_VARIABLE_MAXOBJRADIUS,
  61. };
  62. /*
  63. ** DynamicAABTreeCullClass is a derived AABTree which assumes it contains PhysClasses
  64. ** these two functions encapsulate some typecasting which happens in a lot
  65. ** of places...
  66. */
  67. inline PhysClass * get_first_object(AABTreeNodeClass * node)
  68. {
  69. return (PhysClass *)(node->Object);
  70. }
  71. inline PhysClass * get_next_object(PhysClass * tile)
  72. {
  73. return (PhysClass *)(((AABTreeLinkClass *)tile->Get_Cull_Link())->NextObject);
  74. }
  75. /*
  76. ** DynamicAABTreeCullClass Implementation
  77. */
  78. DynamicAABTreeCullClass::DynamicAABTreeCullClass(PhysicsSceneClass * pscene) :
  79. PhysAABTreeCullClass(pscene),
  80. MaxObjRadius(DEFUALT_MAXOBJRADIUS),
  81. RenderBox(NULL),
  82. DebugIterator(this)
  83. {
  84. /*
  85. ** Modify the root node so that any object can be added into the tree
  86. */
  87. WWASSERT(RootNode != NULL);
  88. RootNode->Box.Extent.Set(FLT_MAX/4.0f,FLT_MAX/4.0f,FLT_MAX/4.0f);
  89. }
  90. DynamicAABTreeCullClass::~DynamicAABTreeCullClass(void)
  91. {
  92. REF_PTR_RELEASE(RenderBox);
  93. }
  94. void DynamicAABTreeCullClass::Add_Object(PhysClass * obj)
  95. {
  96. /*
  97. ** Find the appropriate node to insert this object into.
  98. */
  99. int node_index = find_insertion_node(obj);
  100. /*
  101. ** Add the object to that node
  102. */
  103. Add_Object_Internal(obj,node_index);
  104. }
  105. void DynamicAABTreeCullClass::Update_Culling(CullableClass * obj)
  106. {
  107. WWASSERT(obj);
  108. WWASSERT(obj->Get_Culling_System() == this);
  109. /*
  110. ** unlink it from the node it is currently in
  111. */
  112. AABTreeLinkClass * link = (AABTreeLinkClass *)obj->Get_Cull_Link();
  113. WWASSERT(link);
  114. AABTreeNodeClass * node = link->Node;
  115. WWASSERT(node);
  116. node->Remove_Object(obj);
  117. /*
  118. ** link it back into the tree
  119. */
  120. int node_index = find_insertion_node(obj);
  121. WWASSERT(node_index < NodeCount);
  122. IndexedNodes[node_index]->Add_Object(obj,false);
  123. }
  124. uint32 DynamicAABTreeCullClass::Get_Dynamic_Object_Vis_ID(const AABoxClass & obj_bounds,int * node_id)
  125. {
  126. bool is_big_obj = ( (obj_bounds.Extent.X > MaxObjRadius) ||
  127. (obj_bounds.Extent.Y > MaxObjRadius) ||
  128. (obj_bounds.Extent.Z > MaxObjRadius) );
  129. /*
  130. ** If the user supplied a node_id, we'll start searching from there
  131. */
  132. AABTreeNodeClass * start_node = RootNode;
  133. #if 0 // Disabling coherency because characters are getting stuck in local minima in the tree...
  134. if ((node_id != NULL) && (*node_id >= 0) && (*node_id < NodeCount)) {
  135. start_node = IndexedNodes[*node_id];
  136. }
  137. /*
  138. ** Step 1: Walk towards the root of the tree until we find a node that still
  139. ** contains this object (hopefully the starting node does!)
  140. */
  141. if (is_big_obj) {
  142. /*
  143. ** For "big" objects, we check the entire box against the node's box
  144. */
  145. while ( (CollisionMath::Overlap_Test(start_node->Box,obj_bounds) != CollisionMath::INSIDE) &&
  146. (start_node != RootNode) )
  147. {
  148. start_node = start_node->Parent;
  149. }
  150. } else {
  151. /*
  152. ** For "normal" objects, we check the center point against the "deflated" boxes. This
  153. ** is more correct for the way that VIS was generated.
  154. */
  155. while ( (deflated_box_contains_point(start_node->Box,obj_bounds.Center) == false) &&
  156. (start_node != RootNode) )
  157. {
  158. start_node = start_node->Parent;
  159. }
  160. }
  161. #endif
  162. /*
  163. ** Step 2: Now recurse down the tree to find the node this object should derive its visibility
  164. ** from
  165. */
  166. int node_index = 0;
  167. if (is_big_obj) {
  168. find_optimal_node(start_node,obj_bounds,node_index);
  169. } else {
  170. find_optimal_deflated_node(start_node,obj_bounds.Center,node_index);
  171. }
  172. /*
  173. ** Step 3: return the results to the user!
  174. */
  175. if (node_id != NULL) {
  176. *node_id = node_index;
  177. }
  178. return IndexedNodes[node_index]->UserData;
  179. }
  180. void DynamicAABTreeCullClass::Re_Partition
  181. (
  182. DynamicVectorClass<AABoxClass> * seed_boxes,
  183. const Vector3 & grid_min,
  184. const Vector3 & grid_max,
  185. const Vector3 & min_grid_cell_size,
  186. int max_grid_cell_count,
  187. float max_obj_radius
  188. )
  189. {
  190. /*
  191. ** Remember the max object radius
  192. */
  193. MaxObjRadius = max_obj_radius;
  194. /*
  195. ** Determine the grid parameters for the world. We want to get the grid cell size
  196. ** as close to min_grid_cell_size without exceeding max_grid_cell_count.
  197. */
  198. AABoxClass grid_bounds;
  199. grid_bounds.Init_Min_Max(grid_min,grid_max);
  200. Vector3 worldsize = grid_max - grid_min;
  201. Vector3i cellcount;
  202. cellcount.I = worldsize.X / min_grid_cell_size.X;
  203. cellcount.J = worldsize.Y / min_grid_cell_size.Y;
  204. cellcount.K = worldsize.Z / min_grid_cell_size.Z;
  205. int total_grid_count = cellcount.I * cellcount.J * cellcount.K;
  206. while (total_grid_count > max_grid_cell_count) {
  207. int i;
  208. Vector3 cellfraction;
  209. for (i=0; i<3; i++) {
  210. cellfraction[i] = (worldsize[i] / cellcount[i]) / min_grid_cell_size[i];
  211. }
  212. int smalldim = 0;
  213. float smallfraction = cellfraction[0];
  214. for (i=1; i<2; i++) {
  215. if (cellfraction[i] < smallfraction) {
  216. smalldim = i;
  217. smallfraction = cellfraction[i];
  218. }
  219. }
  220. cellcount[smalldim]--;
  221. total_grid_count = cellcount.I * cellcount.J * cellcount.K;
  222. }
  223. Vector3 cellsize;
  224. cellsize.X = worldsize.X / cellcount.I;
  225. cellsize.Y = worldsize.Y / cellcount.J;
  226. cellsize.Z = worldsize.Z / cellcount.K;
  227. /*
  228. ** Allocate the complete set of seed boxes. This will be *either* a voxelization
  229. ** of the level *or* a set of seed boxes passed in (which were presumably generated
  230. ** from a pathfind floodfill of the level).
  231. */
  232. SimpleDynVecClass<AABoxClass> boxes;
  233. Vector3 boxmin,boxmax;
  234. AABoxClass box;
  235. if ((seed_boxes != NULL) && (seed_boxes->Count() > 0)) {
  236. /*
  237. ** Add in the seed_boxes
  238. */
  239. for (int i=0; i<seed_boxes->Count(); i++) {
  240. /*
  241. ** Copy the input box, inflate it, add it to the array
  242. */
  243. box = (*seed_boxes)[i];
  244. float height = 2.0f * box.Extent.Z;
  245. if (height < MIN_CELL_HEIGHT) {
  246. box.Extent.Z += (MIN_CELL_HEIGHT - height) / 2.0f;
  247. box.Center.Z += (MIN_CELL_HEIGHT - height) / 2.0f;
  248. }
  249. box.Extent += Vector3(MaxObjRadius,MaxObjRadius,MaxObjRadius);
  250. boxes.Add(box);
  251. }
  252. }
  253. /*
  254. ** Add in the voxelization boxes
  255. */
  256. for (int k=0; k<cellcount.K; k++) {
  257. for (int j=0; j<cellcount.J; j++) {
  258. for (int i=0; i<cellcount.I; i++) {
  259. /*
  260. ** Compute the bounds of the grid cell
  261. */
  262. boxmin.X = grid_min.X + i * cellsize.X;
  263. boxmin.Y = grid_min.Y + j * cellsize.Y;
  264. boxmin.Z = grid_min.Z + k * cellsize.Z;
  265. boxmax = boxmin + cellsize;
  266. /*
  267. ** Inflate by MaxObjRadius
  268. */
  269. boxmin -= Vector3(MaxObjRadius,MaxObjRadius,MaxObjRadius);
  270. boxmax += Vector3(MaxObjRadius,MaxObjRadius,MaxObjRadius);
  271. /*
  272. ** Add to the array
  273. */
  274. box.Init_Min_Max(boxmin,boxmax);
  275. boxes.Add(box);
  276. }
  277. }
  278. }
  279. /*
  280. ** Call the base class re-partition function with our list of leaf boxes
  281. */
  282. PhysAABTreeCullClass::Re_Partition(grid_bounds,boxes);
  283. /*
  284. ** Mark the Visibility system dirty since our boxes all changed
  285. */
  286. Scene->Reset_Vis();
  287. /*
  288. ** Modify the root node so that any object can be added into the tree
  289. */
  290. RootNode->Box.Extent.Set(FLT_MAX/4.0f,FLT_MAX/4.0f,FLT_MAX/4.0f);
  291. }
  292. void DynamicAABTreeCullClass::Collect_Visible_Objects
  293. (
  294. const FrustumClass & frustum,
  295. VisTableClass * pvs,
  296. RefPhysListClass & visobjlist
  297. )
  298. {
  299. WWASSERT(RootNode != NULL);
  300. if (pvs != NULL) {
  301. /*
  302. ** Recursively collect objects directly into the specified list.
  303. */
  304. VisObjCollectContextClass collection_context(frustum,*pvs,visobjlist);
  305. if (Scene->Is_Vis_Inverted() || !Is_Hierarchical_Vis_Culling_Enabled()) {
  306. collect_visible_objects_no_hvis_recursive(RootNode,collection_context);
  307. } else {
  308. collect_visible_objects_recursive(RootNode,collection_context);
  309. }
  310. } else {
  311. /*
  312. ** Reset the internal collection, call the built-in frustum collection function
  313. */
  314. Reset_Collection();
  315. Collect_Objects(frustum);
  316. /*
  317. ** Loop over each collected object, adding it into the list
  318. */
  319. PhysClass * obj;
  320. for ( obj = Get_First_Collected_Object();
  321. obj != NULL;
  322. obj = Get_Next_Collected_Object(obj))
  323. {
  324. visobjlist.Add(obj);
  325. }
  326. }
  327. }
  328. void DynamicAABTreeCullClass::Render_Visible_Cells(RenderInfoClass & rinfo,VisTableClass * pvs,DisplayModeType mode)
  329. {
  330. AABTreeNodeClass * start = IndexedNodes[DebugIterator.Get_Current_Node_Index()];
  331. render_visible_cells_recursive(start,rinfo,pvs,mode);
  332. }
  333. void DynamicAABTreeCullClass::Assign_Vis_IDs(void)
  334. {
  335. /*
  336. ** Allocate a vis id for each node in the tree.
  337. */
  338. int nodecount = Partition_Node_Count();
  339. for (int i=0; i<nodecount; i++) {
  340. IndexedNodes[i]->UserData = Scene->Allocate_Vis_Object_ID();
  341. }
  342. }
  343. void DynamicAABTreeCullClass::Evaluate_Non_Occluder_Visibility(VisRenderContextClass & context)
  344. {
  345. WWASSERT(context.VisRasterizer != NULL);
  346. context.VisRasterizer->Set_Render_Mode(IDBufferClass::NON_OCCLUDER_MODE);
  347. evaluate_non_occluder_visibility_recursive(RootNode,context);
  348. }
  349. void DynamicAABTreeCullClass::Save_Static_Data(ChunkSaveClass & csave)
  350. {
  351. csave.Begin_Chunk(DYNAMICAABTREE_CHUNK_AABTREE_CLASS_DATA);
  352. TypedAABTreeCullSystemClass<PhysClass>::Save(csave);
  353. csave.End_Chunk();
  354. csave.Begin_Chunk(DYNAMICAABTREE_CHUNK_VARIABLES);
  355. WRITE_MICRO_CHUNK(csave,DYNAMICAABTREE_VARIABLE_MAXOBJRADIUS,MaxObjRadius);
  356. csave.End_Chunk();
  357. }
  358. void DynamicAABTreeCullClass::Load_Static_Data(ChunkLoadClass & cload)
  359. {
  360. WWMEMLOG(MEM_CULLINGDATA);
  361. while (cload.Open_Chunk()) {
  362. switch(cload.Cur_Chunk_ID()) {
  363. case DYNAMICAABTREE_CHUNK_AABTREE_CLASS_DATA:
  364. TypedAABTreeCullSystemClass<PhysClass>::Load(cload);
  365. break;
  366. case DYNAMICAABTREE_CHUNK_VARIABLES:
  367. while (cload.Open_Micro_Chunk()) {
  368. switch(cload.Cur_Micro_Chunk_ID()) {
  369. READ_MICRO_CHUNK(cload,DYNAMICAABTREE_VARIABLE_MAXOBJRADIUS,MaxObjRadius);
  370. }
  371. cload.Close_Micro_Chunk();
  372. }
  373. break;
  374. default:
  375. WWDEBUG_SAY(("Unhandled Chunk: 0x%X File: %s Line: %d\r\n",cload.Cur_Chunk_ID(),__FILE__,__LINE__));
  376. break;
  377. }
  378. cload.Close_Chunk();
  379. }
  380. }
  381. void DynamicAABTreeCullClass::evaluate_non_occluder_visibility_recursive
  382. (
  383. AABTreeNodeClass * node,
  384. VisRenderContextClass & context
  385. )
  386. {
  387. /*
  388. ** Try to avoid this processing if possible. If this node and all of its
  389. ** children are already considered potentially visible, return
  390. */
  391. if (subtree_is_visible(node,context)) {
  392. return;
  393. }
  394. /*
  395. ** now, determine whether this bounding box is visible. Note that
  396. ** we consider it visible if the view-point is inside the box, otherwise
  397. ** we have to render the box and scan for it.
  398. ** Note also that I use the "un-inflated" box for this.
  399. */
  400. AABoxClass nodebox = node->Box;
  401. nodebox.Extent -= Vector3(MaxObjRadius,MaxObjRadius,MaxObjRadius);
  402. bool box_is_visible = false;
  403. int vis_id = node->UserData;
  404. if ( (nodebox.Contains(context.Camera.Get_Position())) ||
  405. (context.VisTable.Get_Bit(vis_id) != 0) ||
  406. (context.Is_Vis_Quick_And_Dirty()))
  407. {
  408. box_is_visible = true;
  409. context.VisTable.Set_Bit(vis_id,true);
  410. } else {
  411. if (!context.Camera.Cull_Box(node->Box)) {
  412. context.Set_Vis_ID(vis_id); // use the node's vis-id
  413. context.VisRasterizer->Reset_Pixel_Counter();
  414. AABoxRenderObjClass * rbox = get_render_box();
  415. rbox->Set_Local_Center_Extent(nodebox.Center,nodebox.Extent);
  416. rbox->Special_Render(context); // render the bounding volume
  417. REF_PTR_RELEASE(rbox);
  418. if (context.VisRasterizer->Get_Pixel_Counter() > 0) {
  419. context.VisTable.Set_Bit(vis_id,true);
  420. box_is_visible = true;
  421. }
  422. }
  423. }
  424. /*
  425. ** Recursively test the child nodes.
  426. */
  427. if (box_is_visible) {
  428. if (node->Front != NULL) {
  429. evaluate_non_occluder_visibility_recursive(node->Front,context);
  430. }
  431. if (node->Back != NULL) {
  432. evaluate_non_occluder_visibility_recursive(node->Back,context);
  433. }
  434. }
  435. }
  436. bool DynamicAABTreeCullClass::subtree_is_visible
  437. (
  438. AABTreeNodeClass * node,
  439. VisRenderContextClass & context
  440. )
  441. {
  442. if (context.VisTable.Get_Bit(node->UserData) == false) return false;
  443. if (node->Front && !subtree_is_visible(node->Front,context)) return false;
  444. if (node->Back && !subtree_is_visible(node->Back,context)) return false;
  445. return true;
  446. }
  447. void DynamicAABTreeCullClass::set_tree_visibility
  448. (
  449. AABTreeNodeClass * node,
  450. VisRenderContextClass & context,
  451. bool onoff
  452. )
  453. {
  454. if (node == NULL) return;
  455. context.VisTable.Set_Bit(node->UserData,onoff);
  456. set_tree_visibility(node->Front,context,onoff);
  457. set_tree_visibility(node->Back,context,onoff);
  458. }
  459. AABoxRenderObjClass * DynamicAABTreeCullClass::get_render_box(void)
  460. {
  461. if (RenderBox == NULL) {
  462. RenderBox = NEW_REF(AABoxRenderObjClass,());
  463. }
  464. WWASSERT(RenderBox != NULL);
  465. RenderBox->Add_Ref();
  466. return RenderBox;
  467. }
  468. int DynamicAABTreeCullClass::find_insertion_node(CullableClass * obj)
  469. {
  470. int node_index = 0;
  471. const AABoxClass & cullbox = obj->Get_Cull_Box();
  472. if ( (cullbox.Extent.X > MaxObjRadius) ||
  473. (cullbox.Extent.Y > MaxObjRadius) ||
  474. (cullbox.Extent.Z > MaxObjRadius) )
  475. {
  476. find_optimal_node(RootNode,cullbox,node_index);
  477. } else {
  478. find_optimal_deflated_node(RootNode,cullbox.Center,node_index);
  479. }
  480. return node_index;
  481. }
  482. void DynamicAABTreeCullClass::find_optimal_node
  483. (
  484. AABTreeNodeClass * node,
  485. const AABoxClass & box,
  486. int & node_index
  487. )
  488. {
  489. /*
  490. ** We want to find the smallest node in the tree that contains
  491. ** the given bounding box.
  492. */
  493. int input_node_index = node_index;
  494. if (node->Front) {
  495. if (CollisionMath::Overlap_Test(node->Front->Box,box) == CollisionMath::INSIDE) {
  496. find_optimal_node(node->Front,box,node_index);
  497. }
  498. }
  499. if (node->Back) {
  500. if (CollisionMath::Overlap_Test(node->Back->Box,box) == CollisionMath::INSIDE) {
  501. find_optimal_node(node->Back,box,node_index);
  502. }
  503. }
  504. /*
  505. ** If we have no children or none of our children contained the object,
  506. ** then we check if we contain the object.
  507. */
  508. if (node_index == input_node_index) {
  509. if (node->Box.Extent.Quick_Length() < IndexedNodes[node_index]->Box.Extent.Quick_Length()) {
  510. node_index = node->Index;
  511. }
  512. }
  513. }
  514. void DynamicAABTreeCullClass::find_optimal_deflated_node
  515. (
  516. AABTreeNodeClass * node,
  517. const Vector3 & center,
  518. int & node_index
  519. )
  520. {
  521. /*
  522. ** We want to find the smallest "de-flated" leaf node that contains the
  523. ** given center point.
  524. */
  525. int input_node_index = node_index;
  526. if (node->Front) {
  527. if (deflated_box_contains_point(node->Front->Box,center)) {
  528. find_optimal_deflated_node(node->Front,center,node_index);
  529. }
  530. }
  531. if (node->Back) {
  532. if (deflated_box_contains_point(node->Back->Box,center)) {
  533. find_optimal_deflated_node(node->Back,center,node_index);
  534. }
  535. }
  536. /*
  537. ** If we have no children or none of our children contained the object,
  538. ** then we check if we contain the object.
  539. */
  540. if (node_index == input_node_index) {
  541. if (node->Box.Extent.Quick_Length() < IndexedNodes[node_index]->Box.Extent.Quick_Length()) {
  542. node_index = node->Index;
  543. }
  544. }
  545. }
  546. void DynamicAABTreeCullClass::collect_visible_objects_recursive
  547. (
  548. AABTreeNodeClass * node,
  549. VisObjCollectContextClass & context
  550. )
  551. {
  552. /*
  553. ** If this node is not visible, stop
  554. ** In Debug and Profile builds, we also recurse if vis was inverted (we can no-longer
  555. ** early-exit the entire sub-tree in this case)
  556. */
  557. if (context.PVS.Get_Bit(node->UserData) == 0) {
  558. NODE_REJECTED();
  559. return;
  560. }
  561. /*
  562. ** Cull the bounding volume of this node against the frustum.
  563. ** If it is culled, stop descending the tree.
  564. */
  565. CollisionMath::OverlapType overlap;
  566. overlap = CollisionMath::Overlap_Test(context.Frustum,node->Box,context.PlanesPassed);
  567. if (overlap == CollisionMath::OUTSIDE) {
  568. NODE_REJECTED();
  569. return;
  570. }
  571. // else if (overlap == CollisionMath::INSIDE) {
  572. // collect_visible_objects_recursive(node,context);
  573. // return;
  574. // }
  575. NODE_ACCEPTED();
  576. /*
  577. ** Test any objects in this node
  578. */
  579. if (node->Object) {
  580. if (context.PVS.Get_Bit(node->UserData) != 0) {
  581. PhysClass * obj = get_first_object(node);
  582. while (obj) {
  583. if (CollisionMath::Overlap_Test(context.Frustum,obj->Get_Cull_Box(),context.PlanesPassed) != CollisionMath::OUTSIDE) {
  584. context.VisObjList.Add(obj);
  585. }
  586. obj = get_next_object(obj);
  587. }
  588. }
  589. }
  590. /*
  591. ** Recurse into the children
  592. */
  593. if (node->Back) {
  594. collect_visible_objects_recursive(node->Back,context);
  595. }
  596. if (node->Front) {
  597. collect_visible_objects_recursive(node->Front,context);
  598. }
  599. }
  600. void DynamicAABTreeCullClass::collect_visible_objects_no_hvis_recursive
  601. (
  602. AABTreeNodeClass * node,
  603. VisObjCollectContextClass & context
  604. )
  605. {
  606. /*
  607. ** Cull the bounding volume of this node against the frustum.
  608. ** If it is culled, stop descending the tree.
  609. */
  610. CollisionMath::OverlapType overlap;
  611. overlap = CollisionMath::Overlap_Test(context.Frustum,node->Box,context.PlanesPassed);
  612. if (overlap == CollisionMath::OUTSIDE) {
  613. NODE_REJECTED();
  614. return;
  615. }
  616. // else if (overlap == CollisionMath::INSIDE) {
  617. // collect_visible_objects_recursive(node,context);
  618. // return;
  619. // }
  620. NODE_ACCEPTED();
  621. /*
  622. ** Test any objects in this node
  623. */
  624. if (node->Object) {
  625. if (context.PVS.Get_Bit(node->UserData) != 0) {
  626. PhysClass * obj = get_first_object(node);
  627. while (obj) {
  628. if (CollisionMath::Overlap_Test(context.Frustum,obj->Get_Cull_Box(),context.PlanesPassed) != CollisionMath::OUTSIDE) {
  629. context.VisObjList.Add(obj);
  630. }
  631. obj = get_next_object(obj);
  632. }
  633. }
  634. }
  635. /*
  636. ** Recurse into the children
  637. */
  638. if (node->Back) {
  639. collect_visible_objects_no_hvis_recursive(node->Back,context);
  640. }
  641. if (node->Front) {
  642. collect_visible_objects_no_hvis_recursive(node->Front,context);
  643. }
  644. }
  645. void DynamicAABTreeCullClass::render_visible_cells_recursive
  646. (
  647. AABTreeNodeClass * node,
  648. RenderInfoClass & rinfo,
  649. VisTableClass * pvs,
  650. DisplayModeType mode
  651. )
  652. {
  653. /*
  654. ** Cull the bounding volume of this node against the frustum.
  655. ** If it is culled or not visible, stop descending the tree.
  656. */
  657. int visid = node->UserData;
  658. if ( (CollisionMath::Overlap_Test(rinfo.Camera.Get_Frustum(),node->Box) == CollisionMath::OUTSIDE) ||
  659. (!Scene->Is_Vis_Inverted() && (pvs != NULL) && (pvs->Get_Bit(visid) == 0)) )
  660. {
  661. return;
  662. }
  663. /*
  664. ** If we're in a leaf or node with one child that is visible, draw a translucent box
  665. */
  666. int child_count = 0;
  667. if (node->Front != NULL) child_count++;
  668. if (node->Back != NULL) child_count++;
  669. if ((child_count <= 1) || (mode == DISPLAY_OCCUPIED)) {
  670. if ((pvs == NULL) || (pvs->Get_Bit(visid))) {
  671. if ((mode != DISPLAY_OCCUPIED) || (node->Object != NULL)) {
  672. // force boxes to get rendered (yuck)
  673. int oldmask = WW3D::Get_Collision_Box_Display_Mask();
  674. WW3D::Set_Collision_Box_Display_Mask(oldmask | 0x01);
  675. AABoxRenderObjClass * rbox = get_render_box();
  676. if (mode == DISPLAY_CENTERS) {
  677. rbox->Set_Local_Center_Extent(node->Box.Center,Vector3(0.3f,0.3f,0.3f));
  678. } else {
  679. rbox->Set_Local_Center_Extent(node->Box.Center,node->Box.Extent - Vector3(MaxObjRadius,MaxObjRadius,MaxObjRadius)); // set the box dimensions
  680. }
  681. float red,green,blue;
  682. if (child_count == 0) {
  683. green = 1.0f; //((visid % 13468) & 0xFF) / 255.0f;
  684. blue = green / 2.0f;
  685. red = green / 3.0f;
  686. } else if (child_count == 1) {
  687. blue = 1.0f; //((visid % 13468) & 0xFF) / 255.0f;
  688. red = blue / 2.0f;
  689. green = blue / 3.0f;
  690. } else {
  691. red = 1.0f; //((visid % 13468) & 0xFF) / 255.0f;
  692. blue = red / 2.0f;
  693. green = red / 3.0f;
  694. }
  695. rbox->Set_Color(Vector3(red,green,blue));
  696. rbox->Render(rinfo); // render the bounding volume
  697. REF_PTR_RELEASE(rbox);
  698. // restore the old box mask
  699. WW3D::Set_Collision_Box_Display_Mask(oldmask);
  700. }
  701. }
  702. }
  703. /*
  704. ** recurse into children
  705. */
  706. if (node->Front) {
  707. render_visible_cells_recursive(node->Front,rinfo,pvs,mode);
  708. }
  709. if (node->Back) {
  710. render_visible_cells_recursive(node->Back,rinfo,pvs,mode);
  711. }
  712. }
  713. void DynamicAABTreeCullClass::Prune_Redundant_Leaf_Nodes(VisOptimizationContextClass & context)
  714. {
  715. prune_redundant_leaf_nodes_recursive(RootNode,context);
  716. }
  717. void DynamicAABTreeCullClass::prune_redundant_leaf_nodes_recursive
  718. (
  719. AABTreeNodeClass * node,
  720. VisOptimizationContextClass & context
  721. )
  722. {
  723. /*
  724. ** Recurse down to the leaves first
  725. */
  726. if (node->Front) {
  727. prune_redundant_leaf_nodes_recursive(node->Front,context);
  728. }
  729. if (node->Back) {
  730. prune_redundant_leaf_nodes_recursive(node->Back,context);
  731. }
  732. /*
  733. ** Try to prune our children. They will only be removed if their
  734. ** vis matches ours and they are a leaf node.
  735. */
  736. prune_child(node,node->Front,context);
  737. prune_child(node,node->Back,context);
  738. context.Get_Progress_Object().Increment_Completed_Operations();
  739. }
  740. void DynamicAABTreeCullClass::prune_child
  741. (
  742. AABTreeNodeClass * parent,
  743. AABTreeNodeClass * child,
  744. VisOptimizationContextClass & context
  745. )
  746. {
  747. /*
  748. ** If the child node is NULL just return
  749. */
  750. if ((parent == NULL) || (child == NULL)) {
  751. return;
  752. }
  753. /*
  754. ** If the child is a leaf node and its VIS matches the parent vis, delete the
  755. ** child, fixup the vis tables and ID's, and re-insert the child's objects into the tree.
  756. */
  757. if ((child->Front == NULL) && (child->Back == NULL)) {
  758. int parent_object_id = parent->UserData;
  759. int child_object_id = child->UserData;
  760. float match_fraction = context.Compute_Object_Table_Match_Fraction(parent_object_id,child_object_id);
  761. WWDEBUG_SAY(("parent: %d, child: %d, match fraction: %f\n",parent_object_id,child_object_id,match_fraction));
  762. if (match_fraction >= context.Get_Dyn_Cell_Prune_Match_Fraction()) {
  763. /*
  764. ** unlink the node from its parent so that subsequent calls back into the tree will not see it.
  765. */
  766. if (parent->Front == child) {
  767. parent->Front = NULL;
  768. }
  769. if (parent->Back == child) {
  770. parent->Back = NULL;
  771. }
  772. /*
  773. ** re-index the tree to remove the final dangling pointer.
  774. */
  775. Re_Index_Nodes();
  776. /*
  777. ** re-insert all objects into the tree
  778. ** zero the bounding box of the node to remove any chance of the objects from being left
  779. ** in the child node!
  780. */
  781. child->Box.Extent.Set(0,0,0);
  782. PhysClass * obj = get_first_object(child);
  783. while (obj) {
  784. Update_Culling(obj);
  785. obj = get_first_object(child);
  786. }
  787. /*
  788. ** delete the node
  789. */
  790. delete child;
  791. /*
  792. ** Tell the optimizer to combine the tables. This is going to call back into the tree
  793. ** so make sure it is in a consistent state...
  794. */
  795. context.Combine_Object_Tables(parent_object_id,child_object_id);
  796. context.Get_Progress_Object().Increment_Dynamic_Cells_Removed();
  797. }
  798. }
  799. }
  800. void DynamicAABTreeCullClass::Merge_Vis_Object_IDs(uint32 id0,uint32 id1)
  801. {
  802. /*
  803. ** Each node has a vis object id.
  804. ** Whenever we encounter one of these with id1, set it to id0.
  805. ** Whenever we encounter one with id > id1, subtract one from it.
  806. */
  807. for (int i=0; i<NodeCount; i++) {
  808. AABTreeNodeClass * node = IndexedNodes[i];
  809. if (node->UserData == id1) {
  810. node->UserData = id0;
  811. } else if (node->UserData > id1) {
  812. node->UserData--;
  813. }
  814. }
  815. }