predlod.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  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 : G *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/predlod.cpp $*
  25. * *
  26. * $Author:: Jani_p $*
  27. * *
  28. * $Modtime:: 9/20/01 10:10a $*
  29. * *
  30. * $Revision:: 5 $*
  31. * *
  32. *-------------------------------------------------------------------------*
  33. * Functions: *
  34. * PredictiveLODOptimizerClass::Clear -- clear object list and total cost*
  35. * PredictiveLODOptimizerClass::Add_Object -- adds object to list, cost *
  36. * PredictiveLODOptimizerClass::Optimize_LODs -- does LOD optimization *
  37. * PredictiveLODOptimizerClass::Free -- releases all memory used. *
  38. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  39. #include "predlod.h"
  40. #include <memory.h>
  41. /* NOTE: The LODHeapNode and LODHeap classes are defined here for use in the
  42. * Optimize_LODs() member function. */
  43. // A node entry for a heap. It has no son/father pointers since it will be
  44. // used in an array implementation of a heap.
  45. /*
  46. ** NOTE: LODHeapNodes contain pointers to RenderObjClass, but these pointers
  47. ** are NOT tracked via refcounting. This is because the heaps are created and
  48. ** destroyed within one function, so all references created are necessary;
  49. ** and performing Add_Ref and Release_Ref every time a pointer is copied
  50. ** would hurt performance.
  51. */
  52. class LODHeapNode {
  53. public:
  54. LODHeapNode(void) { Item = NULL; }
  55. LODHeapNode (float key) { Item = NULL; Key = key; }
  56. LODHeapNode (RenderObjClass * item, float key) { Item = item; Key = key; }
  57. ~LODHeapNode(void) { }
  58. RenderObjClass * Get_Item(void) { return(Item); }
  59. float Get_Key(void) { return(Key); }
  60. void Set_Key(float key) { Key = key; }
  61. int operator < (const LODHeapNode& node) { return(Key < node.Key); }
  62. int operator <= (const LODHeapNode& node) { return(Key <= node.Key); }
  63. int operator > (const LODHeapNode& node) { return(Key > node.Key); }
  64. int operator >= (const LODHeapNode& node) { return(Key >= node.Key); }
  65. int operator == (const LODHeapNode& node) { return(Key == node.Key); }
  66. int operator != (const LODHeapNode& node) { return(Key != node.Key); }
  67. private:
  68. RenderObjClass *Item;
  69. float Key;
  70. };
  71. // A Heap implemented as a complete binary tree in an array.
  72. class LODHeap {
  73. public:
  74. // This constructor receives an array of HeapNodes and arranges it to
  75. // fulfil the heap condition. Note: the array will be used inside the
  76. // heap, so it should never be used or deleted outside. The resulting
  77. // heap is full - no nodes can be added until some are removed.
  78. LODHeap(int count, LODHeapNode *NodeArray) {
  79. Nodes = NodeArray;
  80. Num = Max = count;
  81. // Now build a heap from the array by working backwards, building
  82. // subheaps from the bottom up. (starting at the middle of the array
  83. // since the single-node subtrees at the leaves are already heaps)
  84. int index;
  85. for (index = Num/2; index >= 1; index--) Downheap(index);
  86. }
  87. ~LODHeap(void) {
  88. // delete []Nodes;
  89. }
  90. LODHeapNode *Top(void) {
  91. return &(Nodes[1]);
  92. }
  93. // This changes the key value of the entry at the top of the heap and
  94. // adjusts the heap accordingly.
  95. void Change_Key_Top(float new_key) {
  96. Nodes[1].Set_Key(new_key);
  97. Downheap(1);
  98. }
  99. // This searches for an entry which has a certain Item value, and
  100. // changes its key to a new one. The heap is then adjusted accordingly.
  101. void Change_Key(RenderObjClass *item, float new_key) {
  102. for (int i=1; i <= Num; i++) {
  103. if (Nodes[i].Get_Item() == item) {
  104. float old_key = Nodes[i].Get_Key();
  105. Nodes[i].Set_Key(new_key);
  106. // If the key has been decreased, adjust the node downwards.
  107. // Otherwise, adjust it upwards.
  108. if (new_key < old_key) Downheap(i);
  109. else Upheap(i);
  110. break;
  111. }
  112. }
  113. }
  114. private:
  115. LODHeap(void) {} // Just to ensure the default constructor is not used.
  116. // The node and key arrays have one extra entry because entry [0] is
  117. // reserved for various uses.
  118. LODHeapNode * Nodes; // The nodes
  119. int Max; // The maximum number of nodes
  120. int Num; // the current number of nodes
  121. // Two utility methods used by various other methods: both take a
  122. // single entry which violates the heap condition and moves it in the
  123. // heap until the heap condition is fulfilled.
  124. // Upheap takes an entry with a (possibly) overlarge key and moves it
  125. // up until the heap condition is satisfied. (this is a private
  126. // method, so no error checking is needed).
  127. // Note that Upheap puts a sentinel in Nodes[0].
  128. void Upheap(int index) {
  129. LODHeapNode node = Nodes[index];
  130. Nodes[0].Set_Key(FLT_MAX);
  131. while (Nodes[index/2] <= node) {
  132. Nodes[index] = Nodes[index/2];
  133. index = index/2;
  134. }
  135. Nodes[index] = node;
  136. }
  137. // Downheap takes an entry with a (possibly) oversmall key and moves it
  138. // down until the heap condition is satisfied. (this is a private
  139. // method, so no error checking is needed).
  140. void Downheap(int index) {
  141. LODHeapNode node = Nodes[index];
  142. while (index <= Num/2) {
  143. int child_index = index + index;
  144. if ((child_index < Num) && (Nodes[child_index] < Nodes[child_index+1])) child_index++;
  145. if (node >= Nodes[child_index]) break;
  146. Nodes[index] = Nodes[child_index];
  147. index = child_index;
  148. }
  149. Nodes[index] = node;
  150. }
  151. };
  152. // Static PredictiveLODOptimizerClass data members:
  153. RenderObjClass ** PredictiveLODOptimizerClass::ObjectArray = NULL;
  154. int PredictiveLODOptimizerClass::ArraySize = 0;
  155. int PredictiveLODOptimizerClass::NumObjects = 0;
  156. float PredictiveLODOptimizerClass::TotalCost = 0.0f;
  157. LODHeapNode * PredictiveLODOptimizerClass::VisibleObjArray1;
  158. LODHeapNode * PredictiveLODOptimizerClass::VisibleObjArray2;
  159. int PredictiveLODOptimizerClass::VisibleObjArraySize;
  160. /**************************************************************************
  161. * PredictiveLODOptimizerClass::Clear -- clear object list and total cost *
  162. * *
  163. * INPUT: *
  164. * *
  165. * OUTPUT: *
  166. * *
  167. * WARNINGS: *
  168. * *
  169. * HISTORY: *
  170. * 03/12/1999 NH : Created. *
  171. *========================================================================*/
  172. void PredictiveLODOptimizerClass::Clear(void)
  173. {
  174. if (ObjectArray) {
  175. // Release refs to all objects in the list:
  176. for (int i = 0; i < NumObjects; i++) {
  177. if (ObjectArray[i]) {
  178. ObjectArray[i]->Release_Ref();
  179. ObjectArray[i] = NULL;
  180. }
  181. }
  182. }
  183. TotalCost = 0.0f;
  184. NumObjects = 0;
  185. }
  186. /**************************************************************************
  187. * PredictiveLODOptimizerClass::Add_Object -- adds object to list, cost *
  188. * *
  189. * INPUT: *
  190. * *
  191. * OUTPUT: *
  192. * *
  193. * WARNINGS: *
  194. * *
  195. * HISTORY: *
  196. * 03/12/1999 NH : Created. *
  197. *========================================================================*/
  198. void PredictiveLODOptimizerClass::Add_Object(RenderObjClass *robj)
  199. {
  200. // If array present but too small, free it and copy it to new array.
  201. if (ObjectArray) {
  202. if (ArraySize <= NumObjects) {
  203. int new_array_size = NumObjects + 100;
  204. RenderObjClass **new_array = new RenderObjClass *[new_array_size];
  205. memcpy(new_array, ObjectArray, sizeof(RenderObjClass *) * NumObjects);
  206. delete [] ObjectArray;
  207. ObjectArray = new_array;
  208. ArraySize = new_array_size;
  209. }
  210. } else {
  211. // Create new object array.
  212. ObjectArray = new RenderObjClass *[100];
  213. }
  214. // Copy pointer and add ref
  215. ObjectArray[NumObjects] = robj;
  216. ObjectArray[NumObjects]->Add_Ref();
  217. NumObjects++;
  218. float cost = robj->Get_Cost();
  219. // Some sanity checking so one object doesn't mess up the entire scene
  220. WWASSERT (cost >= 0.0f);
  221. WWASSERT (cost < 1.0e6);
  222. TotalCost += cost;
  223. }
  224. /**************************************************************************
  225. * PredictiveLODOptimizerClass::Optimize_LODs -- does LOD optimization *
  226. * *
  227. * INPUT: float max_cost - the upper bound on the total scene Cost. *
  228. * *
  229. * OUTPUT: none. *
  230. * *
  231. * WARNINGS: *
  232. * *
  233. * HISTORY: *
  234. * 12/08/1997 NH : Created. *
  235. * 04/23/1997 NH : Ported to SR 1.3. *
  236. * 03/12/1999 NH : Moved to PredictiveLODOptimizerClass. *
  237. * *
  238. * COMMENTS: *
  239. * This function implements the algorithm outlined in "Adaptive *
  240. * Display Algorithm for Interactive Frame Rates During Visualization *
  241. * of Complex Virtual Environments", Thomas Funkhouser & Carlo Sequin, *
  242. * SIGGRAPH '93 Proceedings, pp. 247-253. *
  243. * Modifications have been made to support screensize clamping of LODs. *
  244. *========================================================================*/
  245. void PredictiveLODOptimizerClass::Optimize_LODs(float max_cost)
  246. {
  247. if (!ObjectArray || NumObjects == 0) return;
  248. AllocVisibleObjArrays(NumObjects);
  249. // Allocate and fill arrays. (one extra entry since the zeroth entry is not used).
  250. // LODHeapNode *visible_obj_array1 = new LODHeapNode[NumObjects + 1];
  251. // LODHeapNode *visible_obj_array2 = new LODHeapNode[NumObjects + 1];
  252. // Insert objects into arrays: (0th entry reserved for sentinel values)
  253. for (int i = 0; i < NumObjects; i++) {
  254. RenderObjClass *robj = ObjectArray[i];
  255. // We use minus Value for the first queue to make it ordered by minimum Value.
  256. VisibleObjArray1[i + 1] = LODHeapNode(robj, -(robj->Get_Value()));
  257. VisibleObjArray2[i + 1] = LODHeapNode(robj, robj->Get_Post_Increment_Value());
  258. }
  259. // Build priority queues:
  260. LODHeap min_current_value_queue(NumObjects, VisibleObjArray1);
  261. LODHeap max_post_increment_value_queue(NumObjects, VisibleObjArray2);
  262. // These memory areas now are pointed to within the heaps:
  263. // visible_obj_array1 = NULL;
  264. // visible_obj_array2 = NULL;
  265. // Main loop: iteratively increment/decrement tuples.
  266. bool done = false;
  267. RenderObjClass *max_data, *min_data;
  268. while (!done) {
  269. // Initialize max_data and min_data so comparison at end of loop uses correct values.
  270. max_data = NULL;
  271. min_data = NULL;
  272. // Increment incrementable tuple with maximum next value.
  273. if (TotalCost <= max_cost) {
  274. // If tuple with maximum next value is already at maximum lod, all
  275. // tuples are (since AT_MAX_LOD is smaller than any Value), so stop.
  276. if (max_post_increment_value_queue.Top()->Get_Key() == RenderObjClass::AT_MAX_LOD) {
  277. done = true;
  278. break;
  279. }
  280. // Get (incrementable) tuple with maximum next value.
  281. max_data = max_post_increment_value_queue.Top()->Get_Item();
  282. // Increment tuple (and update TotalCost accordingly).
  283. TotalCost -= max_data->Get_Cost();
  284. max_data->Increment_LOD();
  285. TotalCost += max_data->Get_Cost();
  286. // Update priority queues with incremented tuple.
  287. max_post_increment_value_queue.Change_Key_Top(max_data->Get_Post_Increment_Value());
  288. min_current_value_queue.Change_Key(max_data, -(max_data->Get_Value()));
  289. }
  290. // Decrement decerementable tuples with minimum current value.
  291. while (TotalCost > max_cost) {
  292. // If tuple with minimum current value is already at minimum lod, all
  293. // tuples are (since AT_MIN_LOD is smaller than any (negated) Value),
  294. // so stop.
  295. if (min_current_value_queue.Top()->Get_Key() == -RenderObjClass::AT_MIN_LOD) {
  296. done = true;
  297. break;
  298. }
  299. // Get (decrementable) tuple with minimum current value.
  300. min_data = min_current_value_queue.Top()->Get_Item();
  301. // Decrement tuple (and update TotalCost accordingly).
  302. TotalCost -= min_data->Get_Cost();
  303. min_data->Decrement_LOD();
  304. TotalCost += min_data->Get_Cost();
  305. // Update priority queues with incremented tuple.
  306. min_current_value_queue.Change_Key_Top(-(min_data->Get_Value()));
  307. max_post_increment_value_queue.Change_Key(min_data, min_data->Get_Post_Increment_Value());
  308. // Check termination criterion (same tuple incremented and decremented).
  309. if (max_data == min_data) {
  310. done = true;
  311. break;
  312. }
  313. }
  314. }
  315. // Clear optimizer:
  316. Clear();
  317. }
  318. /**************************************************************************
  319. * PredictiveLODOptimizerClass::Free -- releases all memory used. *
  320. * *
  321. * INPUT: *
  322. * *
  323. * OUTPUT: *
  324. * *
  325. * WARNINGS: *
  326. * *
  327. * HISTORY: *
  328. * 03/12/1999 NH : Created. *
  329. *========================================================================*/
  330. void PredictiveLODOptimizerClass::Free(void)
  331. {
  332. Clear();
  333. if (ObjectArray) {
  334. delete [] ObjectArray;
  335. ObjectArray = NULL;
  336. ArraySize = 0;
  337. }
  338. // Only the array number one has been allocated...
  339. if (VisibleObjArray1) delete[] VisibleObjArray1;
  340. VisibleObjArray1=NULL;
  341. VisibleObjArray2=NULL;
  342. VisibleObjArraySize = 0;
  343. }
  344. void PredictiveLODOptimizerClass::AllocVisibleObjArrays(int num_objects)
  345. {
  346. if (VisibleObjArraySize<num_objects) {
  347. VisibleObjArraySize=num_objects;
  348. if (VisibleObjArray1) delete[] VisibleObjArray1; // Only the first array is actually allocated
  349. VisibleObjArray1=new LODHeapNode[2*(num_objects + 1)];
  350. VisibleObjArray2=VisibleObjArray1+(num_objects + 1);
  351. }
  352. }