vistable.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  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/vistable.cpp $*
  25. * *
  26. * Author:: Greg Hjelstrom *
  27. * *
  28. * $Modtime:: 6/11/01 6:06p $*
  29. * *
  30. * $Revision:: 24 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "vistable.h"
  36. #include "wwphysids.h"
  37. #include "pscene.h"
  38. #include "chunkio.h"
  39. #include "wwdebug.h"
  40. #include "lzo.h"
  41. #include "lzo1x.h"
  42. #include "phys.h"
  43. #include "wwmemlog.h"
  44. #include <windows.h>
  45. /*
  46. ** Chunk ID's used by a visibility table to save itself
  47. */
  48. enum {
  49. VISTABLE_CHUNK_BYTECOUNT = 0x00000001, // number of bytes in this pvs
  50. VISTABLE_CHUNK_BYTES, // pvs bytes, compressed with lzhl (OBSOLETE!)
  51. VISTABLE_CHUNK_LZOBYTES // pvs bytes, compressed with lzo
  52. };
  53. /**
  54. ** BitCounterClass
  55. ** This class is used to accelerate some of the bit-vector comparing and counting that
  56. ** goes on in VisTableClass.
  57. */
  58. class BitCounterClass
  59. {
  60. public:
  61. BitCounterClass(void);
  62. int Count_True_Bits(uint8 byte) { return TrueBits[byte]; }
  63. protected:
  64. uint8 TrueBits[256];
  65. };
  66. BitCounterClass::BitCounterClass(void)
  67. {
  68. /*
  69. ** Initialize the table of how many "on" bits there are
  70. ** in each number between 0 and 256
  71. */
  72. for (int i=0; i<256; i++) {
  73. TrueBits[i] = 0;
  74. for (int bit=0; bit<8; bit++) {
  75. if (i & (1<<bit)) {
  76. TrueBits[i]++;
  77. }
  78. }
  79. }
  80. }
  81. static BitCounterClass _TheBitCounter;
  82. /****************************************************************************************************
  83. **
  84. ** VisTableClass Implementation
  85. **
  86. ****************************************************************************************************/
  87. VisTableClass::VisTableClass(unsigned bitcount,int id) :
  88. BitCount(bitcount),
  89. Buffer(NULL),
  90. VisSectorID(id),
  91. Timestamp(0)
  92. {
  93. Alloc_Buffer(bitcount);
  94. }
  95. VisTableClass::VisTableClass(CompressedVisTableClass * ctable,int bitcount,int id) :
  96. BitCount(bitcount),
  97. Buffer(NULL),
  98. VisSectorID(id),
  99. Timestamp(0)
  100. {
  101. WWASSERT(ctable != NULL);
  102. Alloc_Buffer(bitcount);
  103. ctable->Decompress(Get_Bytes(),Get_Byte_Count());
  104. }
  105. VisTableClass::VisTableClass(const VisTableClass & that) :
  106. BitCount(0),
  107. Buffer(NULL),
  108. VisSectorID(0),
  109. Timestamp(0)
  110. {
  111. *this = that;
  112. }
  113. VisTableClass & VisTableClass::operator = (const VisTableClass & that)
  114. {
  115. Alloc_Buffer(that.BitCount);
  116. memcpy(Buffer,that.Buffer,Get_Byte_Count());
  117. BitCount = that.BitCount;
  118. VisSectorID = that.VisSectorID;
  119. Timestamp = that.Timestamp;
  120. return *this;
  121. }
  122. VisTableClass::~VisTableClass(void)
  123. {
  124. if (Buffer != NULL) {
  125. delete[] Buffer;
  126. Buffer = NULL;
  127. }
  128. BitCount = 0;
  129. }
  130. void VisTableClass::Alloc_Buffer(int bitcount)
  131. {
  132. WWMEMLOG(MEM_VIS);
  133. if (Buffer != NULL) {
  134. delete[] Buffer;
  135. Buffer = NULL;
  136. }
  137. BitCount = bitcount;
  138. int count = ((bitcount + 31) / 32);
  139. Buffer = new uint32[count];
  140. memset(Buffer,0,count * sizeof(uint32));
  141. }
  142. uint8 * VisTableClass::Get_Bytes(void) const
  143. {
  144. return (uint8*)Buffer;
  145. }
  146. int VisTableClass::Get_Byte_Count(void) const
  147. {
  148. /*
  149. ** In order to avoid "endian-ness" problems in the routines that
  150. ** loop over the bytes in the buffer, I'm returning the byte count
  151. ** up to the last longword. The extra bits should always be zero
  152. ** so they should not screw up anything.
  153. */
  154. return Get_Long_Count() * 4; //(BitCount + 7) / 8;
  155. }
  156. uint32 * VisTableClass::Get_Longs(void) const
  157. {
  158. return Buffer;
  159. }
  160. int VisTableClass::Get_Long_Count(void) const
  161. {
  162. return (BitCount + 31) / 32;
  163. }
  164. void VisTableClass::Reset_All(void)
  165. {
  166. if (Buffer != NULL) {
  167. memset(Buffer,0x00,Get_Byte_Count());
  168. }
  169. }
  170. void VisTableClass::Set_All(void)
  171. {
  172. /*
  173. ** Set the buffer to FF's
  174. */
  175. if (Buffer != NULL) {
  176. memset((uint8*)Buffer,0xFF,Get_Byte_Count());
  177. }
  178. /*
  179. ** Make sure the trailing bits are zero'd (just for sanity...)
  180. */
  181. for (int i=BitCount; i<32*Get_Long_Count(); i++) {
  182. Buffer[i>>5] &= ~(0x80000000u >> (i & 0x01F));
  183. }
  184. }
  185. void VisTableClass::Delete_Bit(int delete_index)
  186. {
  187. int i;
  188. /*
  189. ** first handle the long that the deleted bit is in
  190. */
  191. int first_long = delete_index >> 5;
  192. int start_bits = WWMath::Min((first_long + 1)<<5,BitCount-1);
  193. for (i=delete_index; i<start_bits; i++) {
  194. Set_Bit(i,(Get_Bit(i+1) != 0));
  195. }
  196. /*
  197. ** handle the reset of the buffer. There are two cases here, if the deleted
  198. ** bit was in the last long of the buffer, we simply have to clear the trailing
  199. ** bit. Otherwise we need to shift the rest of the longs in the buffer right
  200. ** by one bit and or in the MSB from the following long.
  201. */
  202. int long_count = Get_Long_Count();
  203. if (first_long == long_count-1) {
  204. Set_Bit(BitCount-1,false);
  205. } else {
  206. for (i=first_long+1; i<long_count-1; i++) {
  207. Buffer[i] = (Buffer[i]<<1) | ((Buffer[i+1] & 0x80000000) >> 31);
  208. }
  209. Buffer[long_count-1] = Buffer[long_count-1]<<1;
  210. }
  211. /*
  212. ** Finally, record that a bit was removed.
  213. */
  214. BitCount--;
  215. }
  216. void VisTableClass::Merge(const VisTableClass & that)
  217. {
  218. if (that.BitCount != BitCount) {
  219. return;
  220. }
  221. for (int i=0; i<Get_Long_Count(); i++) {
  222. Buffer[i] = Buffer[i] | that.Buffer[i];
  223. }
  224. }
  225. void VisTableClass::Invert(void)
  226. {
  227. for (int i=0; i<Get_Long_Count(); i++) {
  228. Buffer[i] = ~Buffer[i];
  229. }
  230. }
  231. bool VisTableClass::Is_Equal_To(const VisTableClass & that)
  232. {
  233. if (BitCount != that.BitCount) {
  234. return false;
  235. }
  236. if (memcmp((void*)Buffer,(void*)that.Buffer,Get_Byte_Count()) != 0) {
  237. return false;
  238. }
  239. return true;
  240. }
  241. int VisTableClass::Count_Differences(const VisTableClass & that)
  242. {
  243. if (BitCount != that.BitCount) {
  244. return BitCount;
  245. }
  246. int counter = 0;
  247. int byte_count = Get_Byte_Count();
  248. uint8 * my_bytes = Get_Bytes();
  249. uint8 * his_bytes = that.Get_Bytes();
  250. for (int i=0; i<byte_count; i++) {
  251. counter += _TheBitCounter.Count_True_Bits(my_bytes[i] ^ his_bytes[i]);
  252. }
  253. return counter;
  254. }
  255. int VisTableClass::Count_True_Bits(void)
  256. {
  257. int counter = 0;
  258. int byte_count = Get_Byte_Count();
  259. uint8 * my_bytes = Get_Bytes();
  260. for (int i=0; i<byte_count; i++) {
  261. counter += _TheBitCounter.Count_True_Bits(my_bytes[i]);
  262. }
  263. return counter;
  264. }
  265. float VisTableClass::Match_Fraction(const VisTableClass & that)
  266. {
  267. if (BitCount != that.BitCount) {
  268. return 0.0f;
  269. }
  270. /*
  271. ** or_counter will be the number of bits that were on in at least one of the vectors
  272. ** xor_counter will be the count of bits that were different (1 in one and zero in the other)
  273. */
  274. int xor_counter = 0;
  275. int or_counter = 0;
  276. int byte_count = Get_Byte_Count();
  277. uint8 * my_bytes = Get_Bytes();
  278. uint8 * his_bytes = that.Get_Bytes();
  279. for (int i=0; i<byte_count; i++) {
  280. xor_counter += _TheBitCounter.Count_True_Bits(my_bytes[i] ^ his_bytes[i]);
  281. or_counter += _TheBitCounter.Count_True_Bits(my_bytes[i] | his_bytes[i]);
  282. }
  283. /*
  284. ** match fraction is 1 - (different_bits / on_bits)
  285. */
  286. if (or_counter == 0) {
  287. return 1.0f;
  288. } else {
  289. return 1.0f - (float)xor_counter / (float)or_counter;
  290. }
  291. }
  292. /****************************************************************************************************
  293. **
  294. ** CompressedVisTableClass Implementation
  295. **
  296. ****************************************************************************************************/
  297. CompressedVisTableClass::CompressedVisTableClass(void) :
  298. BufferSize(0),
  299. Buffer(NULL)
  300. {
  301. }
  302. CompressedVisTableClass::CompressedVisTableClass(VisTableClass * bits) :
  303. BufferSize(0),
  304. Buffer(NULL)
  305. {
  306. WWMEMLOG(MEM_VIS);
  307. WWASSERT(bits != NULL);
  308. Compress(bits->Get_Bytes(),bits->Get_Byte_Count());
  309. }
  310. CompressedVisTableClass::CompressedVisTableClass(const CompressedVisTableClass &that) :
  311. BufferSize(0),
  312. Buffer(NULL)
  313. {
  314. (*this) = that;
  315. }
  316. CompressedVisTableClass::~CompressedVisTableClass(void)
  317. {
  318. if (Buffer != NULL) {
  319. delete[] Buffer;
  320. }
  321. }
  322. const CompressedVisTableClass &CompressedVisTableClass::operator= (const CompressedVisTableClass &that)
  323. {
  324. WWMEMLOG(MEM_VIS);
  325. if (Buffer != NULL) {
  326. delete [] Buffer;
  327. Buffer = NULL;
  328. }
  329. BufferSize = that.BufferSize;
  330. Buffer = new uint8[BufferSize];
  331. ::memcpy (Buffer, that.Buffer, sizeof(uint8)*BufferSize);
  332. return *this;
  333. }
  334. uint8 * CompressedVisTableClass::Get_Bytes(void)
  335. {
  336. return Buffer;
  337. }
  338. int CompressedVisTableClass::Get_Byte_Count(void) const
  339. {
  340. return BufferSize;
  341. }
  342. void CompressedVisTableClass::Load(ChunkLoadClass & cload)
  343. {
  344. WWMEMLOG(MEM_VIS);
  345. VisTableClass * old_table = NULL;
  346. if (Buffer != NULL) {
  347. old_table = NEW_REF(VisTableClass,(this,PhysicsSceneClass::Get_Instance()->Get_Vis_Table_Size(),0));
  348. delete[] Buffer;
  349. }
  350. cload.Open_Chunk();
  351. if (cload.Cur_Chunk_ID() != VISTABLE_CHUNK_BYTECOUNT) {
  352. cload.Close_Chunk();
  353. return;
  354. }
  355. cload.Read(&BufferSize,sizeof(BufferSize));
  356. cload.Close_Chunk();
  357. Buffer = new uint8[BufferSize];
  358. /*
  359. ** Load the compressed visibility bits. At one point in the past,
  360. ** we were using the lzhl compression scheme, if we encounter that chunk,
  361. ** just reset vis because that compressor had bugs and the data is probably
  362. ** invalid
  363. */
  364. bool load_error = false;
  365. while (cload.Open_Chunk()) {
  366. switch (cload.Cur_Chunk_ID()) {
  367. case VISTABLE_CHUNK_BYTES:
  368. cload.Read(Buffer,BufferSize);
  369. PhysicsSceneClass::Get_Instance()->Reset_Vis();
  370. load_error = true;
  371. break;
  372. case VISTABLE_CHUNK_LZOBYTES:
  373. WWASSERT(cload.Cur_Chunk_Length() == (uint32)BufferSize);
  374. cload.Read(Buffer,BufferSize);
  375. break;
  376. default:
  377. WWDEBUG_SAY(("Unhandled chunk ID: %d in vistable.cpp\r\n",cload.Cur_Chunk_ID()));
  378. load_error = true;
  379. break;
  380. }
  381. cload.Close_Chunk();
  382. }
  383. /*
  384. ** if we loaded a valid vis table and we had a previous valid table, merge
  385. ** the two together
  386. */
  387. if ((load_error == false) && (old_table != NULL)) {
  388. VisTableClass * new_table = NEW_REF(VisTableClass,(this,PhysicsSceneClass::Get_Instance()->Get_Vis_Table_Size(),0));
  389. new_table->Merge(*old_table);
  390. Compress(new_table->Get_Bytes(),new_table->Get_Byte_Count());
  391. REF_PTR_RELEASE(new_table);
  392. }
  393. REF_PTR_RELEASE(old_table);
  394. }
  395. void CompressedVisTableClass::Save(ChunkSaveClass & csave)
  396. {
  397. uint32 bytecount = BufferSize;
  398. csave.Begin_Chunk(VISTABLE_CHUNK_BYTECOUNT);
  399. csave.Write(&bytecount,sizeof(bytecount));
  400. csave.End_Chunk();
  401. csave.Begin_Chunk(VISTABLE_CHUNK_LZOBYTES);
  402. csave.Write(Buffer,bytecount);
  403. csave.End_Chunk();
  404. }
  405. void CompressedVisTableClass::Load (void* hfile)
  406. {
  407. WWMEMLOG(MEM_VIS);
  408. /*
  409. ** Free the buffer
  410. */
  411. if (Buffer != NULL) {
  412. delete [] Buffer;
  413. Buffer = NULL;
  414. BufferSize = 0L;
  415. }
  416. if ((HANDLE)hfile != INVALID_HANDLE_VALUE) {
  417. /*
  418. ** Read the buffer size
  419. */
  420. uint32 dwbytes_read = 0L;
  421. ::ReadFile ((HANDLE)hfile, &BufferSize, sizeof (BufferSize), &dwbytes_read, NULL);
  422. /*
  423. ** Read the buffer
  424. */
  425. Buffer = new uint8[BufferSize];
  426. ::ReadFile ((HANDLE)hfile, Buffer, sizeof (uint8) * BufferSize, &dwbytes_read, NULL);
  427. }
  428. return;
  429. }
  430. void CompressedVisTableClass::Save (void* hfile)
  431. {
  432. if ((HANDLE)hfile != INVALID_HANDLE_VALUE) {
  433. /*
  434. ** Write the buffer size
  435. */
  436. uint32 dwbytes_written = 0L;
  437. ::WriteFile ((HANDLE)hfile, &BufferSize, sizeof (BufferSize), &dwbytes_written, NULL);
  438. /*
  439. ** Write the buffer
  440. */
  441. ::WriteFile ((HANDLE)hfile, Buffer, sizeof (uint8) * BufferSize, &dwbytes_written, NULL);
  442. }
  443. return;
  444. }
  445. void CompressedVisTableClass::Compress(uint8 * src_buffer,int src_size)
  446. {
  447. WWMEMLOG(MEM_VIS);
  448. if (Buffer != NULL) {
  449. delete[] Buffer;
  450. Buffer = NULL;
  451. }
  452. uint8 * comp_buffer = new uint8[LZO_BUFFER_SIZE(src_size)];
  453. lzo_uint comp_size;
  454. int lzocode = LZOCompressor::Compress(src_buffer,src_size,comp_buffer,&comp_size);
  455. WWASSERT(lzocode == LZO_E_OK);
  456. BufferSize = comp_size;
  457. Buffer = new uint8[BufferSize];
  458. memcpy(Buffer,comp_buffer,BufferSize);
  459. #ifdef WWDEBUG
  460. lzo_uint decomp_size;
  461. LZOCompressor::Decompress(Buffer,BufferSize,comp_buffer,&decomp_size);
  462. WWASSERT(decomp_size == (lzo_uint)src_size);
  463. WWASSERT(src_size % 4 == 0);
  464. #endif
  465. delete[] comp_buffer;
  466. }
  467. void CompressedVisTableClass::Decompress(uint8 * decomp_buffer,int decomp_size)
  468. {
  469. WWMEMLOG(MEM_VIS);
  470. lzo_uint size;
  471. LZOCompressor::Decompress(Buffer,BufferSize,decomp_buffer, &size);
  472. WWASSERT((int)size == decomp_size);
  473. }
  474. #if 0
  475. void CompressedVisTableClass::Compare_Compression(void)
  476. {
  477. #ifdef WWDEBUG
  478. static int num_compressions = 0;
  479. static int total_size = 0;
  480. static int lcw_size = 0;
  481. static int lzo_size = 0;
  482. static int lcw_failures = 0;
  483. static int lzo_failures = 0;
  484. int test_size = tmp_size;
  485. uint8 * test_buf = tmp_buffer;
  486. num_compressions++;
  487. total_size += test_size;
  488. // Testing LCW
  489. uint8 * lcw_comp_buf = new uint8[test_size * 2];
  490. int lcw_comp_size = LCW_Comp(test_buf, lcw_comp_buf, test_size);
  491. uint8 * lcw_decomp_buf = new uint8[test_size * 2];
  492. int lcw_decomp_size = LCW_Uncomp(lcw_comp_buf, lcw_decomp_buf);
  493. lcw_size+=lcw_comp_size;
  494. if ((memcmp(test_buf,lcw_decomp_buf,test_size) != 0) || (lcw_decomp_size != test_size)) {
  495. lcw_failures++;
  496. }
  497. // Testing LZO
  498. uint8 * lzo_comp_buf = new uint8[test_size * 2]; //LZO_BUFFER_SIZE(test_size)];
  499. lzo_uint lzo_comp_size;
  500. LZOCompressor::Compress(test_buf,test_size,lzo_comp_buf,&lzo_comp_size);
  501. uint8 * lzo_decomp_buf = new uint8[test_size];
  502. lzo_uint lzo_decomp_size;
  503. LZOCompressor::Decompress(lzo_comp_buf,lzo_comp_size,lzo_decomp_buf,&lzo_decomp_size);
  504. lzo_size+=lzo_comp_size;
  505. if ((memcmp(test_buf,lzo_decomp_buf,test_size) != 0) || ((int)lzo_decomp_size != test_size)) {
  506. lzo_failures++;
  507. }
  508. delete[] lcw_comp_buf;
  509. delete[] lcw_decomp_buf;
  510. delete[] lzo_comp_buf;
  511. delete[] lzo_decomp_buf;
  512. #endif
  513. delete[] tmp_buffer;
  514. }
  515. #endif