Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
aabtreecull.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(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/***********************************************************************************************
20 *** 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 ***
21 ***********************************************************************************************
22 * *
23 * Project Name : WWMath *
24 * *
25 * $Archive:: /Commando/Code/wwmath/aabtreecull.cpp $*
26 * *
27 * Author:: Greg Hjelstrom *
28 * *
29 * $Modtime:: 8/26/01 2:18p $*
30 * *
31 * $Revision:: 25 $*
32 * *
33 *---------------------------------------------------------------------------------------------*
34 * Functions: *
35 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
36
37
38#include "aabtreecull.h"
39#include "chunkio.h"
40#include "iostruct.h"
41#include <string.h>
42#include "sphere.h"
43#include "colmath.h"
44#include "colmathinlines.h"
45
46
47
48/*
49** Declare the pools
50*/
53
54
55/*
56** Current version of the file format
57*/
59
60
61/*
62** Chunk Id's used by the aabtree code to save itself into a file
63*/
64enum
65{
66 AABTREE_CHUNK_VERSION = 0x00000001, // version wrapper, contains 32bit version #
67 AABTREE_CHUNK_AABNODE = 0x00000101, // generic aab-node wrapper
68 AABTREE_CHUNK_AABNODE_INFO, // OBSOLETE! generic aab-node definition (IOAABNodeStruct)
69 AABTREE_CHUNK_AABNODE_CONTENTS, // wrapper around contents of the node
70 AABTREE_CHUNK_AABNODE_VARIABLES, // wrapper around variables for a node
71
72 AABTREE_CHUNK_NODE_INDEX = 0x00000200, // wrapper around the node index for an object
73
76};
77
78
79/*
80** IOAABNodeStruct
81** Data structure for the contents of a node in the AAB-Tree
82*/
83#define AABNODE_ATTRIBUTE_FRONT_CHILD 0x00000001
84#define AABNODE_ATTRIBUTE_BACK_CHILD 0x00000002
85
92
93
94/*************************************************************************
95**
96** Utility functions for walking the object list in an AABTree Node
97**
98*************************************************************************/
99static inline CullableClass * get_first_object(AABTreeNodeClass * node)
100{
101 return node->Object;
102}
103
104static inline CullableClass * get_next_object(CullableClass * obj)
105{
106 return ((AABTreeLinkClass *)obj->Get_Cull_Link())->NextObject;
107}
108
109
110/*************************************************************************
111**
112** AABTreeCullSystemClass Implementation
113**
114*************************************************************************/
123
125{
126 // Delete all links and release-ref all cullables:
127 int nidx;
128 for (nidx = 0; nidx < NodeCount; nidx++) {
130 }
131
132 // Delete node tree (deleting the root recursively deletes all nodes)
133 delete RootNode;
134
135 // Delete indexed node pointer array
136 if (IndexedNodes) {
137 delete[] IndexedNodes;
139 }
140}
141
142
144{
146 (
147 (obj->Get_Culling_System() == NULL),
148 "AABTreeCullSystemClass::Add -- Object is already in another culling system!\n"
149 );
150
151 AABTreeLinkClass * new_link = new AABTreeLinkClass(this);
152 obj->Set_Cull_Link(new_link);
153
154 if (node_index == -1) {
156 } else {
157 WWASSERT(node_index < NodeCount);
158 IndexedNodes[node_index]->Add_Object(obj,false);
159 ObjectCount++;
160 }
161
162 obj->Add_Ref();
163}
164
165
167{
168 WWASSERT(obj);
169 WWASSERT(obj->Get_Culling_System() == this);
170
172 WWASSERT(link);
173
174 AABTreeNodeClass * node = link->Node;
175 WWASSERT(node);
176
177 node->Remove_Object(obj);
179 delete link;
180 obj->Set_Cull_Link(NULL);
181
182 ObjectCount--;
183 WWASSERT(ObjectCount >= 0);
184 obj->Release_Ref();
185}
186
188{
189 WWASSERT(obj);
190 WWASSERT(obj->Get_Culling_System() == this);
191
192 // unlink it from the node it is currently in
194 WWASSERT(link);
195 AABTreeNodeClass * node = link->Node;
196 WWASSERT(node);
197 node->Remove_Object(obj);
198 // decrement the object counter, the node can't
199 // decrement it for us...
200 ObjectCount--;
201
202 // drop it into the tree again
204}
205
210
215
220
225
230
235
237{
238 int get_max_depth = 0;
240 return get_max_depth;
241}
242
244{
245 return ObjectCount;
246}
247
249{
250 int curcount = 1;
251 if (node->Front) {
252 curcount += Partition_Node_Count_Recursive(node->Front);
253 }
254 if (node->Back) {
255 curcount += Partition_Node_Count_Recursive(node->Back);
256 }
257 return curcount;
258}
259
260void AABTreeCullSystemClass::Partition_Tree_Depth_Recursive(AABTreeNodeClass * node,int cur_depth,int & max_depth) const
261{
262 cur_depth++;
263 if (cur_depth > max_depth) {
264 max_depth = cur_depth;
265 }
266 if (node->Front) {
267 Partition_Tree_Depth_Recursive(node->Front,cur_depth,max_depth);
268 }
269 if (node->Back) {
270 Partition_Tree_Depth_Recursive(node->Back,cur_depth,max_depth);
271 }
272}
273
275{
276 // order the children in terms of size
277 AABTreeNodeClass * big_child = node->Front;
278 AABTreeNodeClass * small_child = node->Back;
279
280 if (big_child && small_child && (big_child->Compute_Volume() < small_child->Compute_Volume())) {
281 AABTreeNodeClass * tmp = big_child;
282 big_child = small_child;
283 small_child = tmp;
284 }
285
286 // Can we fit in the smaller child?
287 if (small_child && small_child->Box.Contains(obj->Get_Cull_Box())) {
288 Add_Object_Recursive(small_child,obj);
289 return;
290 }
291
292 // Can we fit in the bigger child?
293 if (big_child && big_child->Box.Contains(obj->Get_Cull_Box())) {
294 Add_Object_Recursive(big_child,obj);
295 return;
296 }
297
298 // Ok, we have to fit in this node.
299 node->Add_Object(obj);
300 ObjectCount++;
301}
302
304{
305 WWASSERT(node);
306 WWASSERT(obj);
307
309 (
310 (obj->Get_Culling_System() == NULL),
311 "AABTreeCullSystemClass::Add_Loaded_Object -- Object is already in another culling system!\n"
312 );
313
314 AABTreeLinkClass * new_link = new AABTreeLinkClass(this);
315 obj->Set_Cull_Link(new_link);
316
317 node->Add_Object(obj);
318 ObjectCount++;
319 obj->Add_Ref();
320}
321
323{
324 /*
325 ** transfer all objects to a temporary node
326 */
327 AABTreeNodeClass * dummy_node = new AABTreeNodeClass;
328 RootNode->Transfer_Objects(dummy_node);
329
330 /*
331 ** delete the old tree and make the dummy node the new root
332 */
333 delete RootNode;
334 RootNode = dummy_node;
335
336 /*
337 ** partition the objects
338 */
339 RootNode->Partition();
340
341 /*
342 ** re-index the nodes
343 */
345
346 /*
347 ** reset the statistics
348 */
350
351}
352
354{
355 /*
356 ** transfer all objects to a temporary node
357 */
358 AABTreeNodeClass * dummy_node = new AABTreeNodeClass;
359 RootNode->Transfer_Objects(dummy_node);
360
361 /*
362 ** delete the old tree
363 */
364 delete RootNode;
365
366 /*
367 ** allocate a new root node and tell it to partition the given array of boxes
368 */
370 RootNode->Partition(bounds,boxes);
371
372 /*
373 ** re-index the nodes
374 */
376
377 /*
378 ** reset statistics
379 */
381
382 /*
383 ** re-insert all objects and delete the temporary node
384 */
385 dummy_node->Box.Extent.Set(0,0,0);
386 CullableClass * obj = get_first_object(dummy_node);
387 while (obj != NULL) {
388 Update_Culling(obj);
389 obj = get_first_object(dummy_node);
390 }
391
392 delete dummy_node;
393
394 /*
395 ** Modify the root node so that any object can be added into the tree
396 */
397 RootNode->Box.Extent.Set(FLT_MAX,FLT_MAX,FLT_MAX);
398}
399
404
406{
408 return RootNode->Box;
409}
410
412{
413 if ((node_id >= 0) && (node_id < NodeCount)) {
414 *set_bounds = IndexedNodes[node_id]->Box;
415 } else {
416 *set_bounds = IndexedNodes[0]->Box;
417 }
418}
419
421{
422 Stats.NodeCount = NodeCount;
423 Stats.NodesAccepted = 0;
424 Stats.NodesTriviallyAccepted = 0;
425 Stats.NodesRejected = 0;
426}
427
432
434{
435 /*
436 ** Collect any objects in this node
437 */
438 if (node->Object) {
439 CullableClass * obj = get_first_object(node);
440 while (obj) {
442 obj = get_next_object(obj);
443 }
444 }
445
446 /*
447 ** Statistics
448 */
450
451 /*
452 ** Descend into the children
453 */
454 if (node->Back) {
456 }
457 if (node->Front) {
459 }
460}
461
462
464{
465 /*
466 ** Is the point inside this volume?
467 */
468 if (node->Box.Contains(point) == false) {
470 return;
471 }
472
474
475 /*
476 ** Collect any objects in this node
477 */
478 if (node->Object) {
479 CullableClass * obj = get_first_object(node);
480 while (obj) {
481 if (obj->Get_Cull_Box().Contains(point)) {
483 }
484 obj = get_next_object(obj);
485 }
486 }
487
488 /*
489 ** Descend into the children
490 */
491 if (node->Back) {
492 Collect_Objects_Recursive(node->Back,point);
493 }
494 if (node->Front) {
495 Collect_Objects_Recursive(node->Front,point);
496 }
497}
498
500{
501 /*
502 ** Cull the given box against the bounding volume of this node
503 ** If it is culled, stop descending the tree. If the current node is
504 ** completely contained inside the given box, jump into the collection function
505 ** that doesn't do any more volume checking...
506 */
508 if (overlap == CollisionMath::OUTSIDE) {
510 return;
511 } else if (overlap == CollisionMath::INSIDE) {
513 return;
514 }
515
517
518 /*
519 ** Test any objects in this node
520 */
521 if (node->Object) {
522 CullableClass * obj = get_first_object(node);
523 while (obj) {
526 }
527 obj = get_next_object(obj);
528 }
529 }
530
531 /*
532 ** Recurse into any children
533 */
534 if (node->Back) {
536 }
537 if (node->Front) {
539 }
540}
541
542
544{
545 /*
546 ** Cull the given box against the bounding volume of this node
547 ** If it is culled, stop descending the tree. If the current node is
548 ** completely contained inside the given box, jump into the collection function
549 ** that doesn't do any more volume checking...
550 */
552 if (overlap == CollisionMath::OUTSIDE) {
554 return;
555 } else if (overlap == CollisionMath::INSIDE) {
557 return;
558 }
559
561
562 /*
563 ** Test any objects in this node
564 */
565 if (node->Object) {
566 CullableClass * obj = get_first_object(node);
567 while (obj) {
570 }
571 obj = get_next_object(obj);
572 }
573 }
574
575 /*
576 ** Recurse into any children
577 */
578 if (node->Back) {
580 }
581 if (node->Front) {
583 }
584}
585
587(
588 AABTreeNodeClass * node,
589 const FrustumClass & frustum,
590 int planes_passed
591)
592{
593 /*
594 ** Cull the bounding volume of this node against the frustum.
595 ** If it is culled, stop descending the tree.
596 */
597 CollisionMath::OverlapType overlap = CollisionMath::Overlap_Test(frustum,node->Box,planes_passed);
598 if (overlap == CollisionMath::OUTSIDE) {
600 return;
601 } else if (overlap == CollisionMath::INSIDE) {
603 return;
604 }
605
607
608 /*
609 ** Test any objects in this node
610 */
611 if (node->Object) {
612 CullableClass * obj = get_first_object(node);
613 while (obj) {
616 }
617 obj = get_next_object(obj);
618 }
619 }
620
621 /*
622 ** Recurse into any children
623 */
624 if (node->Back) {
625 Collect_Objects_Recursive(node->Back,frustum,planes_passed);
626 }
627 if (node->Front) {
628 Collect_Objects_Recursive(node->Front,frustum,planes_passed);
629 }
630}
631
632
634{
635 /*
636 ** Is the point inside this volume?
637 */
640 return;
641 }
643
644 /*
645 ** Collect any objects in this node
646 */
647 if (node->Object) {
648 CullableClass * obj = get_first_object(node);
649 while (obj) {
652 }
653 obj = get_next_object(obj);
654 }
655 }
656
657 /*
658 ** Descend into the children
659 */
660 if (node->Back) {
661 Collect_Objects_Recursive(node->Back,sphere);
662 }
663 if (node->Front) {
664 Collect_Objects_Recursive(node->Front,sphere);
665 }
666}
667
669{
670 MinMaxAABoxClass minmaxbox(node->Box);
671
672 /*
673 ** Update child boxes first and ensure that we bound them
674 */
675 if (node->Front) {
677 minmaxbox.Add_Box(node->Front->Box);
678 }
679 if (node->Back) {
681 minmaxbox.Add_Box(node->Back->Box);
682 }
683
684 /*
685 ** Make sure we bound our contained objects
686 */
687 if (node->Object) {
688 CullableClass * obj = get_first_object(node);
689 while (obj) {
690 minmaxbox.Add_Box(obj->Get_Cull_Box());
691 obj = get_next_object(obj);
692 }
693 }
694
695 node->Box.Init_Min_Max(minmaxbox.MinCorner,minmaxbox.MaxCorner);
696}
697
698
700{
701 WWASSERT_PRINT(Object_Count() == 0, "Remove all objects from AAB-Culling system before loading!\n");
702
703 delete RootNode;
705
706 // The first chunk should be a version chunk
707 cload.Open_Chunk();
708 if (cload.Cur_Chunk_ID() != AABTREE_CHUNK_VERSION) {
709 WWDEBUG_SAY(("Attempting to read an obsolete AAB-Tree!"));
710 cload.Close_Chunk();
711 return;
712 }
713
714 // read in the version and verify that it is the current version
715 uint32 version;
716 cload.Read(&version,sizeof(version));
717 if (version != AABTREE_CURRENT_VERSION) {
718 WWDEBUG_SAY(("Attempting to read an obsolete AAB-Tree!"));
719 cload.Close_Chunk();
720 return;
721 }
722 cload.Close_Chunk();
723
724 // read in the tree
725 Load_Nodes(RootNode,cload);
726
727 // re-index all nodes
729
730 // reset the statistics
732}
733
735{
736 // Open the node description
737 cload.Open_Chunk();
739
740 // Load the node description.
741 // Older files will contain a chunk named AABTREE_CHUNK_AABNODE_INFO while newer
742 // files will contain AABTREE_CHUNK_AABNODE_VARIABLES which contains the IOAABNodeStruct
743 // in a micro-chunk.
744 IOAABNodeStruct node_desc;
745 memset(&node_desc,0,sizeof(IOAABNodeStruct));
746
747 cload.Open_Chunk();
749
750 // Loading the legacy format...
752 cload.Read(&node_desc,sizeof(node_desc));
753
754 } else if (cload.Cur_Chunk_ID() == AABTREE_CHUNK_AABNODE_VARIABLES) {
755
756 // Loading the new format, contains micro chunks...
757 while (cload.Open_Micro_Chunk()) {
758 switch(cload.Cur_Micro_Chunk_ID()) {
761 }
762 cload.Close_Micro_Chunk();
763 }
764 }
765 cload.Close_Chunk();
766
767 // Initialize the node bounds.
768 node->Box.Center.X = node_desc.Center.X;
769 node->Box.Center.Y = node_desc.Center.Y;
770 node->Box.Center.Z = node_desc.Center.Z;
771
772 node->Box.Extent.X = node_desc.Extent.X;
773 node->Box.Extent.Y = node_desc.Extent.Y;
774 node->Box.Extent.Z = node_desc.Extent.Z;
775
776 // Load the contents of the node.
777 cload.Open_Chunk();
779 Load_Node_Contents(node,cload);
780 cload.Close_Chunk();
781
782 // Close the node description
783 cload.Close_Chunk();
784
785 // if we are supposed to have a front child, load it
787 WWASSERT(node->Front == NULL);
788 node->Front = new AABTreeNodeClass();
789 node->Front->Parent = node;
790 Load_Nodes(node->Front,cload);
791 }
792
793 // if we have a back child, load it
795 WWASSERT(node->Back == NULL);
796 node->Back = new AABTreeNodeClass();
797 node->Back->Parent = node;
798 Load_Nodes(node->Back,cload);
799 }
800}
801
803{
806 csave.Write(&version,sizeof(uint32));
807 csave.End_Chunk();
808
809 Save_Nodes(RootNode,csave);
810}
811
813{
814 WWASSERT(node);
816
818 IOAABNodeStruct node_desc;
819 memset(&node_desc,0,sizeof(node_desc));
820
821 node_desc.Center.X = node->Box.Center.X;
822 node_desc.Center.Y = node->Box.Center.Y;
823 node_desc.Center.Z = node->Box.Center.Z;
824
825 node_desc.Extent.X = node->Box.Extent.X;
826 node_desc.Extent.Y = node->Box.Extent.Y;
827 node_desc.Extent.Z = node->Box.Extent.Z;
828
829 if (node->Front) {
831 }
832
833 if (node->Back) {
835 }
836
839 csave.End_Chunk();
840
842 Save_Node_Contents(node,csave);
843 csave.End_Chunk();
844
845 csave.End_Chunk();
846
847 if (node->Front) {
848 Save_Nodes(node->Front,csave);
849 }
850
851 if (node->Back) {
852 Save_Nodes(node->Back,csave);
853 }
854}
855
857{
858 uint32 index;
859 cload.Open_Chunk();
861 cload.Read(&index,sizeof(index));
862 cload.Close_Chunk();
863
864 Add_Object_Internal(obj,index);
865}
866
868{
869 WWASSERT(obj);
870 WWASSERT(obj->Get_Culling_System() == this);
871
873 WWASSERT(link);
874
875 AABTreeNodeClass * node = link->Node;
876 WWASSERT(node);
877
878 uint32 index = node->Index;
880 csave.Write(&index,sizeof(index));
881 csave.End_Chunk();
882}
883
884
899
900
902{
903 node->Index = counter;
904 IndexedNodes[counter] = node;
905 counter++;
906
907 if (node->Front) {
909 }
910 if (node->Back) {
912 }
913}
914
915
916
917/*************************************************************************
918**
919** AABTreeNodeClass Implementation
920**
921*************************************************************************/
923 Index(0),
924 Box(Vector3(0,0,0),Vector3(0,0,0)),
925 Parent(NULL),
926 Front(NULL),
927 Back(NULL),
928 Object(NULL),
929 UserData(0)
930{
931}
932
934{
935 // objects should be removed before deleting the partition tree
936 WWASSERT(Object == NULL);
937
938 // delete our children
939 if (Front) {
940 delete Front;
941 Front = NULL;
942 }
943 if (Back) {
944 delete Back;
945 Back = NULL;
946 }
947}
948
950{
951 /*
952 ** make the children update their boxes first
953 */
954 if (Front) {
955 Front->Compute_Bounding_Box();
956 }
957
958 if (Back) {
959 Back->Compute_Bounding_Box();
960 }
961
963}
964
966{
967 /*
968 ** Now make sure we bound our children
969 */
970 MinMaxAABoxClass box(Vector3(FLT_MAX,FLT_MAX,FLT_MAX),Vector3(-FLT_MAX,-FLT_MAX,-FLT_MAX));
971
972 if (Front) {
973 box.Add_Box(Front->Box);
974 }
975
976 if (Back) {
977 box.Add_Box(Back->Box);
978 }
979
980 /*
981 ** bound the objects in this node
982 */
983 CullableClass * obj = Object;
984 while (obj) {
985 box.Add_Box(obj->Get_Cull_Box());
987 obj = link->NextObject;
988 }
989
990 Box.Init(box);
991}
992
994{
995 return Box.Volume();
996}
997
998void AABTreeNodeClass::Add_Object(CullableClass * obj,bool update_bounds)
999{
1001 WWASSERT(link);
1002
1003 link->Node = this;
1004 link->NextObject = Object;
1005 Object = obj;
1006
1007 if (update_bounds) {
1008 // if this is the only object and we have no children, just copy
1009 // the object's bounding box, otherwise, add it to what we have
1010 if ((Object_Count() == 1) && (Front == NULL) && (Back == NULL)) {
1011 Box = obj->Get_Cull_Box();
1012 } else {
1013 Box.Add_Box(obj->Get_Cull_Box());
1014 }
1015 }
1016}
1017
1019{
1020 WWASSERT(obj);
1021
1022 // find the given object in our linked list
1023 CullableClass * prevobj = NULL;
1024 CullableClass * curobj = Object;
1025
1026 while (curobj) {
1027
1028 AABTreeLinkClass * link = (AABTreeLinkClass *)curobj->Get_Cull_Link();
1029
1030 if (curobj == obj) {
1031 // found the object, unlink it.
1032 if (prevobj) {
1033 AABTreeLinkClass * prevlink = (AABTreeLinkClass *)prevobj->Get_Cull_Link();
1034 prevlink->NextObject = link->NextObject;
1035 } else {
1036 Object = link->NextObject;
1037 }
1038
1039 link->NextObject = NULL;
1040 link->Node = NULL;
1041 return;
1042 }
1043
1044 // move to the next object
1045 prevobj = curobj;
1046 curobj = link->NextObject;
1047 }
1048}
1049
1051{
1052 // unlink all of our objects, relinking them to the dummy_node
1053 while (Object) {
1054 CullableClass * obj = Object;
1055 Remove_Object(obj);
1056 dummy_node->Add_Object(obj);
1057 }
1058
1059 // do the same with our children
1060 if (Front) {
1061 Front->Transfer_Objects(dummy_node);
1062 }
1063
1064 if (Back) {
1065 Back->Transfer_Objects(dummy_node);
1066 }
1067}
1068
1070{
1071 CullableClass * obj = Object;
1072 int count = 0;
1073
1074 while (obj) {
1075 count++;
1076 obj = ((AABTreeLinkClass *)obj->Get_Cull_Link())->NextObject;
1077 }
1078
1079 return count;
1080}
1081
1083{
1084 int count = 0;
1085 CullableClass * obj = Object;
1086 WWASSERT(obj != NULL);
1087
1088 while (obj && (count != index)) {
1089 count++;
1090 obj = ((AABTreeLinkClass *)obj->Get_Cull_Link())->NextObject;
1091 }
1092 WWASSERT(count == index);
1093 return obj;
1094}
1095
1096
1097/******************************************************************************************
1098**
1099** Partitioning code which partitions the objects in the tree and passes them into
1100** the new children
1101**
1102******************************************************************************************/
1103
1105{
1106 /*
1107 ** if we're down to only 2 objects, we're done
1108 */
1109 int obj_count = Object_Count();
1110 if (obj_count <= 2) return;
1111
1112 /*
1113 ** Create an array of the bounding boxes of our objects
1114 */
1115 SimpleDynVecClass<AABoxClass> boxes(obj_count);
1116 CullableClass * obj = Object;
1117 while (obj != NULL) {
1118 boxes.Add(obj->Get_Cull_Box());
1119 obj = get_next_object(obj);
1120 }
1121
1122 /*
1123 ** Select and assign the splitting plane
1124 ** De-allocate the array of boxes to conserve memory
1125 */
1127 Select_Splitting_Plane(&sc,boxes);
1128 boxes.Resize(0);
1129
1130 /*
1131 ** If there was no good split, just leave all of
1132 ** the objects in this node
1133 */
1134 if (sc.Cost == FLT_MAX) {
1135 return;
1136 }
1137
1138 /*
1139 ** Split the tiles
1140 */
1141 AABTreeNodeClass * front = new AABTreeNodeClass;
1143 Split_Objects(sc,front,back);
1144
1145 /*
1146 ** Build a front tree if necessary.
1147 */
1148 if (front->Object_Count() > 0) {
1149 Front = front;
1150 Front->Parent = this;
1151 Front->Partition();
1152 } else {
1153 delete front;
1154 front = NULL;
1155 }
1156
1157 /*
1158 ** Build a back tree if necessary.
1159 */
1160 if (back->Object_Count() > 0) {
1161 Back = back;
1162 Back->Parent = this;
1163 Back->Partition();
1164 } else {
1165 delete back;
1166 back = NULL;
1167 }
1168}
1169
1170
1171
1173{
1174 // This function assumes that this node is a leaf
1175 WWASSERT(Front == NULL);
1176 WWASSERT(Back == NULL);
1178
1179 int fcount = 0;
1180 int bcount = 0;
1181
1182 // unlink all of our objects, relinking them to the appropriate node
1183 while (Object) {
1184
1185 // pull the object out of this node
1186 CullableClass * obj = Object;
1188
1189 // decide which node to add the object to,
1190 // NOTE: we have to use the same convention as Compute_Score!
1191 const AABoxClass & box = obj->Get_Cull_Box();
1192
1194
1195 front->Add_Object(obj);
1196 fcount++;
1197
1198 } else {
1199
1200 back->Add_Object(obj);
1201 bcount++;
1202
1203 }
1204 }
1205
1206 // copy the bounding boxes
1207 front->Box = sc.FrontBox;
1209 back->Box = sc.BackBox;
1211
1212 // when we are all done, the counts should match.
1213 WWASSERT(fcount == sc.FrontCount);
1214 WWASSERT(bcount == sc.BackCount);
1215}
1216
1217
1218
1219
1220/******************************************************************************************
1221**
1222** Partitioning code which generates the tree based on a set of input boxes
1223**
1224******************************************************************************************/
1226{
1227 Box = bounds;
1228
1229 /*
1230 ** if we're down to only 1 box, we're done
1231 */
1232 if (boxes.Count() <= 1) return;
1233
1234 /*
1235 ** Select and assign the splitting plane
1236 */
1238 Select_Splitting_Plane(&sc,boxes);
1239
1240 /*
1241 ** If there was no good split, just leave all of
1242 ** the objects in this node
1243 */
1244 if (sc.Cost == FLT_MAX) {
1245 return;
1246 }
1247
1248 /*
1249 ** Split the boxes
1250 ** Deallocate the input box array to conserve RAM
1251 */
1254 Split_Boxes(sc,boxes,frontboxes,backboxes);
1255 boxes.Delete_All();
1256
1257 /*
1258 ** Build a front tree if necessary.
1259 */
1260 if (frontboxes.Count() > 0) {
1261 Front = new AABTreeNodeClass;
1262 Front->Parent = this;
1263 Front->Partition(sc.FrontBox,frontboxes);
1264 } else {
1265 Front = NULL;
1266 }
1267
1268 /*
1269 ** Build a back tree if necessary.
1270 */
1271 if (backboxes.Count() > 0) {
1272 Back = new AABTreeNodeClass;
1273 Back->Parent = this;
1274 Back->Partition(sc.BackBox,backboxes);
1275 } else {
1276 Back = NULL;
1277 }
1278}
1279
1281(
1284 SimpleDynVecClass<AABoxClass> & frontboxes,
1286)
1287{
1288 WWASSERT(boxes.Count() == sc.FrontCount + sc.BackCount);
1289
1290 // copy each box in the input array into the appropriate output array
1291 for (int i=0; i<boxes.Count(); i++) {
1292
1293 const AABoxClass & box = boxes[i];
1294
1296
1297 frontboxes.Add(box);
1298
1299 } else {
1300
1301 backboxes.Add(box);
1302
1303 }
1304 }
1305
1306 // when we are all done, the counts should match.
1307 WWASSERT(frontboxes.Count() == sc.FrontCount);
1308 WWASSERT(backboxes.Count() == sc.BackCount);
1309}
1310
1311
1312
1313/******************************************************************************************
1314**
1315** Partitioning code which searches for the best split for a given set of boxes. This
1316** code is re-used by both types of partitioning (partitioning contained objects or
1317** partitioning based on a set of "seed" boxes.)
1318**
1319******************************************************************************************/
1320
1322(
1323 SplitChoiceStruct * sc,
1325)
1326{
1327 const int NUM_TRYS = 300;
1328
1329 /*
1330 ** Try putting axis-aligned planes through some random vertices
1331 */
1332 int objcount = boxes.Count();
1333 int trys = 0;
1334 for (trys = 0; trys < MIN(NUM_TRYS,objcount); trys++) {
1335
1336 int obj_index;
1338
1339 /*
1340 ** Select a random object
1341 */
1342 obj_index = rand() % objcount;
1343 const AABoxClass & box = boxes[obj_index];
1344
1345 /*
1346 ** Select a random plane which co-incides with one of the faces
1347 ** of the object's bounding box
1348 */
1349 switch(rand() % 6) {
1350 case 0: test.Plane.Set(AAPlaneClass::XNORMAL,box.Center.X + box.Extent.X); break;
1351 case 1: test.Plane.Set(AAPlaneClass::XNORMAL,box.Center.X - box.Extent.X); break;
1352 case 2: test.Plane.Set(AAPlaneClass::YNORMAL,box.Center.Y + box.Extent.Y); break;
1353 case 3: test.Plane.Set(AAPlaneClass::YNORMAL,box.Center.Y - box.Extent.Y); break;
1354 case 4: test.Plane.Set(AAPlaneClass::ZNORMAL,box.Center.Z + box.Extent.Z); break;
1355 case 5: test.Plane.Set(AAPlaneClass::ZNORMAL,box.Center.Z - box.Extent.Z); break;
1356 };
1357
1358 /*
1359 ** Get the score for this plane
1360 */
1361 Compute_Score(&test,boxes);
1362 if (test.Cost < sc->Cost) {
1363 *sc = test;
1364 }
1365
1366 }
1367
1368 /*
1369 ** Still haven't found a valid splitting plane, uh-oh.
1370 */
1371 if ((trys >= MIN(NUM_TRYS,objcount)) && (sc->Cost == FLT_MAX)) {
1373 return;
1374 }
1375}
1376
1378(
1381)
1382{
1383 /*
1384 ** Try putting axis-aligned planes along each face of each box
1385 */
1386 int objcount = boxes.Count();
1387 for (int obj_index = 0; obj_index < objcount; obj_index++) {
1388
1389 AAPlaneClass plane;
1390 const AABoxClass & box = boxes[obj_index];
1391
1392 /*
1393 ** Try each face of this box
1394 */
1395 for (int plane_index = 0; plane_index < 6; plane_index++) {
1397 switch(plane_index % 6) {
1398 case 0: test.Plane.Set(AAPlaneClass::XNORMAL,box.Center.X + box.Extent.X); break;
1399 case 1: test.Plane.Set(AAPlaneClass::XNORMAL,box.Center.X - box.Center.Y); break;
1400 case 2: test.Plane.Set(AAPlaneClass::YNORMAL,box.Center.Y + box.Center.Y); break;
1401 case 3: test.Plane.Set(AAPlaneClass::YNORMAL,box.Center.Y - box.Center.Y); break;
1402 case 4: test.Plane.Set(AAPlaneClass::ZNORMAL,box.Center.Z + box.Center.Z); break;
1403 case 5: test.Plane.Set(AAPlaneClass::ZNORMAL,box.Center.Z - box.Center.Z); break;
1404 };
1405
1406 test.FrontBox.Init_Empty();
1407 test.BackBox.Init_Empty();
1408
1409 /*
1410 ** Get the score for this plane
1411 */
1412 Compute_Score(&test,boxes);
1413 if (test.Cost < sc->Cost) {
1414 *sc = test;
1415 }
1416 }
1417 }
1418
1419 /*
1420 ** Notify user that we couldn't split this node
1421 */
1422#ifdef WWDEBUG
1423 if (sc->Cost == FLT_MAX) {
1424 WWDEBUG_SAY(("Unable to split node! objcount = %d. (%.2f,%.2f,%.2f)\r\n",objcount,Box.Center.X, Box.Center.Y, Box.Center.Z));
1425 }
1426#endif
1427}
1428
1429
1431(
1434)
1435{
1436 /*
1437 ** Suitability of a plane as a partition plane is based on the following factors:
1438 ** - How many "tiles" are on each side of the plane,
1439 */
1440 for (int i=0; i<boxes.Count(); i++) {
1441
1442 const AABoxClass & box = boxes[i];
1443
1445
1446 sc->FrontCount++;
1447 sc->FrontBox.Add_Box(box);
1448
1449 } else {
1450
1451 sc->BackCount++;
1452 sc->BackBox.Add_Box(box);
1453
1454 }
1455 }
1456
1457 /*
1458 ** Compute the cost.
1459 */
1460 float back_cost = sc->BackBox.Volume() * sc->BackCount;
1461 float front_cost = sc->FrontBox.Volume() * sc->FrontCount;
1462
1463 sc->Cost = front_cost + back_cost;
1464
1465 if ((sc->FrontCount == 0) || (sc->BackCount == 0)) {
1466 sc->Cost = FLT_MAX;
1467 }
1468}
1469
1470
1471
1472/**************************************************************************************
1473
1474 AABTreeIterator Implemenation
1475
1476**************************************************************************************/
1477
1478
1480 Tree(tree),
1481 CurNodeIndex(0)
1482{
1483 WWASSERT(Tree != NULL);
1484}
1485
1487{
1488 CurNodeIndex = 0;
1489}
1490
1492{
1493 validate();
1494 if (CurNodeIndex != 0) {
1495 CurNodeIndex = Tree->IndexedNodes[CurNodeIndex]->Parent->Index;
1496 return true;
1497 } else {
1498 return false;
1499 }
1500}
1501
1503{
1504 validate();
1505 if (CurNodeIndex != 0) {
1506
1507 /*
1508 ** find which child of our parent we are
1509 */
1510 AABTreeNodeClass * parent = Tree->IndexedNodes[CurNodeIndex]->Parent;
1511 AABTreeNodeClass * parent_front = parent->Front;
1512 AABTreeNodeClass * parent_back = parent->Back;
1513
1514 /*
1515 ** if our parent doesn't have two children, we don't have a sibling
1516 */
1517 if ((parent_front == NULL) || (parent_back == NULL)) {
1518 return false;
1519 }
1520
1521 /*
1522 ** if we our our parent's front child, go to its back child
1523 */
1524 if ((int)parent_front->Index == CurNodeIndex) {
1525 CurNodeIndex = parent_back->Index;
1526 return true;
1527 }
1528
1529 /*
1530 ** if we our our parent's back child, go to its front child
1531 */
1532 if ((int)parent_back->Index == (int)CurNodeIndex) {
1533 CurNodeIndex = parent_front->Index;
1534 return true;
1535 }
1536 }
1537 return false;
1538}
1539
1541{
1542 validate();
1543 return (Tree->IndexedNodes[CurNodeIndex]->Front != NULL);
1544}
1545
1547{
1548 validate();
1549 if (Has_Front_Child()) {
1550 CurNodeIndex = Tree->IndexedNodes[CurNodeIndex]->Front->Index;
1551 return true;
1552 }
1553 return false;
1554}
1555
1557{
1558 validate();
1559 return (Tree->IndexedNodes[CurNodeIndex]->Back != NULL);
1560}
1561
1563{
1564 if (Has_Back_Child()) {
1565 CurNodeIndex = Tree->IndexedNodes[CurNodeIndex]->Back->Index;
1566 return true;
1567 }
1568 return false;
1569}
1570
1572{
1573 return CurNodeIndex;
1574}
1575
1577{
1578 Tree->Get_Node_Bounds(CurNodeIndex,set_box);
1579}
1580
1581void AABTreeIterator::validate(void)
1582{
1583 if ((CurNodeIndex < 0) || (CurNodeIndex >= Tree->NodeCount)) {
1584 CurNodeIndex = 0;
1585 }
1586}
1587
1588/*
1589
1590 Can we make a more compact AABTree?
1591
1592 Here is the existing data in each Node:
1593
1594 uint32 Index; // Index of this node
1595 AABoxClass Box; // Bounding box of the node
1596 AABTreeNodeClass * Parent; // parent of this node
1597 AABTreeNodeClass * Front; // front node
1598 AABTreeNodeClass * Back; // back node
1599 CullableClass * Object; // objects in this node
1600 uint32 UserData; // 32bit field for the user, initialized to 0
1601
1602 Total Size: 48 bytes
1603
1604 Here is a possible replacement:
1605
1606 uint16 Index;
1607 short x,y,z,w,l,h; // Need origin and scale factors for the boxes stored in tree
1608 uint16 ParentIndex;
1609 uint16 FrontIndex;
1610 uint16 BackIndex;
1611 CullableClass * Object;
1612 uint32 UserData;
1613
1614 Total Size: 28 bytes
1615
1616*/
#define NULL
Definition BaseType.h:92
#define WWASSERT
#define MIN(a, b)
Definition always.h:189
unsigned long uint32
Definition bittype.h:46
#define WRITE_MICRO_CHUNK(csave, id, var)
Definition chunkio.h:293
#define READ_MICRO_CHUNK(cload, id, var)
Definition chunkio.h:334
#define WWMATH_EPSILON
Definition wwmath.h:54
const uint32 AABTREE_CURRENT_VERSION
@ AABTREE_CHUNK_AABNODE_VARIABLES
@ AABTREE_VARIABLE_USERDATA
@ AABTREE_CHUNK_AABNODE
@ AABTREE_VARIABLE_NODESTRUCT
@ AABTREE_CHUNK_AABNODE_INFO
@ AABTREE_CHUNK_NODE_INDEX
@ AABTREE_CHUNK_VERSION
@ AABTREE_CHUNK_AABNODE_CONTENTS
#define AABNODE_ATTRIBUTE_FRONT_CHILD
#define AABNODE_ATTRIBUTE_BACK_CHILD
int Partition_Tree_Depth(void) const
void Update_Bounding_Boxes_Recursive(AABTreeNodeClass *node)
void Update_Bounding_Boxes(void)
void Partition_Tree_Depth_Recursive(AABTreeNodeClass *node, int cur_depth, int &max_depth) const
virtual void Load_Node_Contents(AABTreeNodeClass *, ChunkLoadClass &)
void Load_Object_Linkage(ChunkLoadClass &cload, CullableClass *obj)
void Add_Object_Internal(CullableClass *obj, int node_index=-1)
void Remove_Object_Internal(CullableClass *obj)
const AABoxClass & Get_Bounding_Box(void)
void Re_Index_Nodes_Recursive(AABTreeNodeClass *node, int &counter)
AABTreeNodeClass ** IndexedNodes
void Save_Object_Linkage(ChunkSaveClass &csave, CullableClass *obj)
void Add_Loaded_Object(AABTreeNodeClass *node, CullableClass *obj)
virtual void Update_Culling(CullableClass *obj)
int Object_Count(void) const
virtual ~AABTreeCullSystemClass(void)
int Partition_Node_Count_Recursive(AABTreeNodeClass *node) const
void Collect_Objects_Recursive(AABTreeNodeClass *node)
const StatsStruct & Get_Statistics(void)
virtual void Save_Node_Contents(AABTreeNodeClass *, ChunkSaveClass &)
int Partition_Node_Count(void) const
void NODE_TRIVIALLY_ACCEPTED(void)
void Get_Node_Bounds(int node_id, AABoxClass *set_bounds)
virtual void Collect_Objects(const Vector3 &point)
virtual void Load(ChunkLoadClass &cload)
void Add_Object_Recursive(AABTreeNodeClass *node, CullableClass *obj)
void Save_Nodes(AABTreeNodeClass *node, ChunkSaveClass &csave)
AABTreeNodeClass * RootNode
virtual void Save(ChunkSaveClass &csave)
void Load_Nodes(AABTreeNodeClass *node, ChunkLoadClass &cload)
bool Enter_Back_Child(void)
void Get_Current_Box(AABoxClass *set_box)
bool Has_Front_Child(void)
bool Has_Back_Child(void)
AABTreeIterator(AABTreeCullSystemClass *tree)
bool Enter_Sibling(void)
bool Enter_Front_Child(void)
int Get_Current_Node_Index(void)
bool Enter_Parent(void)
void Add_Object(CullableClass *obj, bool update_bounds=true)
void Transfer_Objects(AABTreeNodeClass *dummy_node)
void Compute_Score(SplitChoiceStruct *sc, SimpleDynVecClass< AABoxClass > &boxes)
AABTreeNodeClass * Parent
AABTreeNodeClass * Back
void Split_Boxes(const SplitChoiceStruct &sc, SimpleDynVecClass< AABoxClass > &boxes, SimpleDynVecClass< AABoxClass > &frontboxes, SimpleDynVecClass< AABoxClass > &backboxes)
CullableClass * Peek_Object(int index)
float Compute_Volume(void)
AABTreeNodeClass * Front
void Select_Splitting_Plane_Brute_Force(SplitChoiceStruct *sc, SimpleDynVecClass< AABoxClass > &boxes)
void Select_Splitting_Plane(SplitChoiceStruct *sc, SimpleDynVecClass< AABoxClass > &boxes)
void Split_Objects(const SplitChoiceStruct &sc, AABTreeNodeClass *front, AABTreeNodeClass *back)
void Remove_Object(CullableClass *obj)
CullableClass * Object
void Compute_Bounding_Box(void)
void Compute_Local_Bounding_Box(void)
Vector3 Center
Definition aabox.h:123
void Init_Min_Max(const Vector3 &min, const Vector3 &max)
Definition aabox.h:339
Vector3 Extent
Definition aabox.h:124
WWINLINE bool Contains(const Vector3 &point) const
Definition aabox.h:504
bool Close_Micro_Chunk()
Definition chunkio.cpp:585
bool Close_Chunk()
Definition chunkio.cpp:448
uint32 Cur_Chunk_ID()
Definition chunkio.cpp:484
uint32 Cur_Micro_Chunk_ID()
Definition chunkio.cpp:622
uint32 Read(void *buf, uint32 nbytes)
Definition chunkio.cpp:692
bool Open_Chunk()
Definition chunkio.cpp:412
bool Open_Micro_Chunk()
Definition chunkio.cpp:557
uint32 Write(const void *buf, uint32 nbytes)
Definition chunkio.cpp:264
bool Begin_Chunk(uint32 id)
Definition chunkio.cpp:108
bool End_Chunk()
Definition chunkio.cpp:148
static OverlapType Overlap_Test(const AAPlaneClass &plane, const Vector3 &point)
friend class CullableClass
Definition cullsys.h:197
void Add_To_Collection(CullableClass *obj)
Definition cullsys.cpp:145
WWINLINE CullLinkClass * Get_Cull_Link(void) const
Definition cullsys.h:104
WWINLINE const AABoxClass & Get_Cull_Box(void) const
Definition cullsys.h:94
WWINLINE void Set_Cull_Link(CullLinkClass *c)
Definition cullsys.h:103
CullSystemClass * Get_Culling_System(void) const
Definition cullsys.cpp:86
WWINLINE float Volume(void) const
Definition aabox.h:162
void Add_Box(const MinMaxAABoxClass &box)
Definition aabox.h:586
Vector3 MinCorner
Definition aabox.h:164
Vector3 MaxCorner
Definition aabox.h:165
WWINLINE void Release_Ref(void) const
Definition refcount.h:146
void Add_Ref(void) const
Definition refcount.cpp:171
void Delete_All(bool allow_shrink=true)
Definition simplevec.h:547
int Count(void) const
Definition simplevec.h:263
bool Add(T const &object, int new_size_hint=0)
Definition simplevec.h:371
virtual bool Resize(int newsize)
Definition simplevec.h:348
float X
Definition vector3.h:90
float Z
Definition vector3.h:92
float Y
Definition vector3.h:91
WWINLINE void Set(float x, float y, float z)
Definition vector3.h:103
#define DEFINE_AUTO_POOL(T, BLOCKSIZE)
Definition mempool.h:160
int counter
Definition patch.cpp:410
IOVector3Struct Center
IOVector3Struct Extent
int test
Definition test6.cpp:32
#define WWASSERT_PRINT(expr, string)
Definition wwdebug.h:135
#define WWDEBUG_SAY(x)
Definition wwdebug.h:114