vxl.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. /*
  2. ** Command & Conquer Generals(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. /* $Header: /Commando/Code/Tools/max2w3d/vxl.cpp 4 10/28/97 6:08p Greg_h $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Commando / G Math Library *
  24. * *
  25. * $Archive:: /Commando/Code/Tools/max2w3d/vxl.cpp $*
  26. * *
  27. * $Author:: Greg_h $*
  28. * *
  29. * $Modtime:: 10/14/97 3:07p $*
  30. * *
  31. * $Revision:: 4 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  36. #include "vxl.h"
  37. #include "errclass.h"
  38. /*
  39. This module will voxelize one or more meshes. It is only used to compute
  40. some things like Moment of Inertia and Center of Mass for the object.
  41. Much of the code which was doing the shading and lighting computations
  42. has been stripped out.
  43. */
  44. static void compute_dimensions(
  45. INodeListClass & meshlist,
  46. const Matrix3 & parenttm,
  47. TimeValue curtime,
  48. Point3 * min,
  49. Point3 * max
  50. );
  51. #define VIS_UNKNOWN 0
  52. #define VIS_SOLID 1
  53. #define VIS_VISIBLE 2
  54. /************************************************************************
  55. * VoxelClass Constructor
  56. *
  57. * Voxelize a list of meshes.
  58. *
  59. * INPUTS:
  60. *
  61. * OUTPUS:
  62. *
  63. ************************************************************************/
  64. VoxelClass::VoxelClass
  65. (
  66. INodeListClass & meshlist,
  67. int resolution,
  68. Matrix3 parenttm,
  69. TimeValue time,
  70. Progress_Meter_Class & meter
  71. )
  72. {
  73. Resolution = resolution;
  74. ParentTM = parenttm;
  75. CurTime = time;
  76. XDim = resolution + 1;
  77. YDim = resolution + 1;
  78. ZDim = resolution + 1;
  79. // assert that none of the dimensions
  80. // were too big. (if this happened, VoxelData is going
  81. // to be a *HUMONGOUS* amount of memory...)
  82. assert(XDim < 256);
  83. assert(YDim < 256);
  84. assert(ZDim < 256);
  85. // Allocate visibility flags array
  86. VisData = new uint8[XDim * YDim * ZDim];
  87. if (VisData == NULL) {
  88. throw ErrorClass("out of memory!");
  89. }
  90. memset(VisData,0,XDim*YDim*ZDim);
  91. /*
  92. ** compute the two corners of the bounding box of
  93. ** these meshes. Note that these coordinates are
  94. ** be specified in the space defined by ParentTM.
  95. */
  96. Point3 min;
  97. Point3 max;
  98. compute_dimensions(meshlist,ParentTM,CurTime,&min,&max);
  99. /*
  100. ** The voxelizer uses three values: offset, size,
  101. ** and scale:
  102. **
  103. ** offset - the position of the "most negative" corner
  104. ** size - dimensions of the box
  105. ** scale - scale factor to apply to vertices after they've
  106. ** been translated by -offset
  107. **
  108. */
  109. Size = max - min;
  110. Offset = min;
  111. Scale.x = Resolution / Size.x;
  112. Scale.y = Resolution / Size.y;
  113. Scale.z = Resolution / Size.z;
  114. /*
  115. ** Dimensions of a single voxel block
  116. */
  117. BlockXDim = Size.x / Resolution;
  118. BlockYDim = Size.y / Resolution;
  119. BlockZDim = Size.z / Resolution;
  120. /*
  121. ** Voxelize the meshes!
  122. */
  123. Quantize_Meshes
  124. (
  125. meshlist,
  126. meter
  127. );
  128. }
  129. /************************************************************************
  130. * VoxelClass Destructor
  131. *
  132. * De-Allocates memory used by the voxel object
  133. *
  134. * INPUTS:
  135. * none
  136. *
  137. * OUTPUS:
  138. * none
  139. *
  140. ************************************************************************/
  141. VoxelClass::~VoxelClass()
  142. {
  143. if (VisData != NULL) delete[] VisData;
  144. }
  145. /************************************************************************
  146. * VoxelClass::Quantize_Meshes
  147. *
  148. * Generataes voxel data from the list of meshes passed in.
  149. *
  150. * INPUTS:
  151. * meshlist - list of meshes to "voxelize"
  152. * meter - progress meter object
  153. *
  154. * OUTPUS:
  155. * none
  156. *
  157. ************************************************************************/
  158. void VoxelClass::Quantize_Meshes
  159. (
  160. INodeListClass & meshlist,
  161. Progress_Meter_Class & meter
  162. )
  163. {
  164. /*
  165. ** Progress meter updating
  166. */
  167. meter.Finish_In_Steps(2);
  168. Progress_Meter_Class slabmeter(meter,meter.Increment);
  169. slabmeter.Finish_In_Steps(ZDim);
  170. /*
  171. ** Generate the Voxel Layer for each slice of the model
  172. */
  173. float min_z = Offset.z;
  174. float max_z = Offset.z + Size.z;
  175. float sliceh = Size.z / (float)ZDim;
  176. for (int slicecount = 0; slicecount < ZDim; slicecount++ )
  177. {
  178. float slicez = min_z + (max_z - min_z) * ((float)slicecount/(float)(ZDim-1));
  179. VoxelLayerClass * vlayer = new VoxelLayerClass
  180. (
  181. meshlist,
  182. CurTime,
  183. ParentTM,
  184. Offset,
  185. Scale,
  186. slicez,
  187. sliceh,
  188. XDim,
  189. YDim
  190. );
  191. Set_Layer(*vlayer,slicecount);
  192. slabmeter.Add_Increment();
  193. if (slabmeter.Cancelled()) throw ErrorClass("Export Cancelled");
  194. delete vlayer;
  195. }
  196. meter.Add_Increment();
  197. // 3D visibility calculations
  198. Progress_Meter_Class vismeter(meter,meter.Increment);
  199. Compute_Visiblity(vismeter);
  200. meter.Add_Increment();
  201. // Compute the voxel bounding box
  202. Compute_Bounding_Box(Size,Offset);
  203. }
  204. /************************************************************************
  205. * VoxelClass::Set_Layer
  206. *
  207. * Sets a layer of the Voxel data according to contents of the passed
  208. * bitmap.
  209. *
  210. * INPUTS:
  211. * bitmap - bitmap to use as the source data
  212. * z - z co-ordinate of the layer to set
  213. *
  214. * OUTPUS:
  215. * none
  216. *
  217. ************************************************************************/
  218. void VoxelClass::Set_Layer
  219. (
  220. VoxelLayerClass & vlayer,
  221. uint32 z
  222. )
  223. {
  224. // bitmap must have same x&y dimensions as the voxel space
  225. if (vlayer.Get_Width() != (unsigned)XDim) return;
  226. if (vlayer.Get_Height() != (unsigned)YDim) return;
  227. // Copy the solid voxels into our voxel cube
  228. for (unsigned j=0; j<vlayer.Get_Height(); j++) {
  229. for (unsigned i=0; i<vlayer.Get_Width(); i++) {
  230. if (!vlayer.Is_Solid(i,j)) {
  231. raw_set_vis(i,j,z,VIS_UNKNOWN);
  232. } else {
  233. raw_set_vis(i,j,z,VIS_SOLID);
  234. }
  235. }
  236. }
  237. }
  238. /************************************************************************
  239. * VoxelClass::Compute_Bounding_Box
  240. *
  241. * Given the information which the MeshObjList gives us regarding the
  242. * size and offset of the group of meshes we are processing, compute the
  243. * scaled co-ordinates of the eight corners of the bounding box.
  244. *
  245. * INPUTS:
  246. * size - width, length, and height of the bounding box
  247. * offset - offset to the first point
  248. * scale - scale factors for each axis.
  249. *
  250. * OUTPUS:
  251. * none
  252. *
  253. ************************************************************************/
  254. void VoxelClass::Compute_Bounding_Box
  255. (
  256. Point3 size,
  257. Point3 offset
  258. )
  259. {
  260. int i;
  261. // First we just set the bounding box values in the
  262. // original scale. The corners must be specified in the
  263. // order expected.
  264. // +x +y -z
  265. BoxCorner[0].x = offset.x + size.x;
  266. BoxCorner[0].y = offset.y + size.y;
  267. BoxCorner[0].z = offset.z;
  268. // +x -y -z
  269. BoxCorner[1].x = offset.x + size.x;
  270. BoxCorner[1].y = offset.y;
  271. BoxCorner[1].z = offset.z;
  272. // -x -y -z
  273. BoxCorner[2].x = offset.x;
  274. BoxCorner[2].y = offset.y;
  275. BoxCorner[2].z = offset.z;
  276. // -x +y -z
  277. BoxCorner[3].x = offset.x;
  278. BoxCorner[3].y = offset.y + size.y;
  279. BoxCorner[3].z = offset.z;
  280. // +x +y +z
  281. BoxCorner[4].x = offset.x + size.x;
  282. BoxCorner[4].y = offset.y + size.y;
  283. BoxCorner[4].z = offset.z + size.z;
  284. // +x -y +z
  285. BoxCorner[5].x = offset.x + size.x;
  286. BoxCorner[5].y = offset.y;
  287. BoxCorner[5].z = offset.z + size.z;
  288. // -x -y +z
  289. BoxCorner[6].x = offset.x;
  290. BoxCorner[6].y = offset.y;
  291. BoxCorner[6].z = offset.z + size.z;
  292. // -x +y +z
  293. BoxCorner[7].x = offset.x;
  294. BoxCorner[7].y = offset.y + size.y;
  295. BoxCorner[7].z = offset.z + size.z;
  296. // Now, scale all of them into the voxel co-ordinate system
  297. for (i=0; i<8; i++) {
  298. BoxCorner[i].x *= Scale.x;
  299. BoxCorner[i].y *= Scale.y;
  300. BoxCorner[i].z *= Scale.z;
  301. }
  302. }
  303. /************************************************************************
  304. * VoxelClass::Compute_Visibility
  305. *
  306. * Detects the outer shell of the voxel object.
  307. * "Tunnels" are drilled into the voxel space from all directions, all
  308. * voxels that are encountered before hitting the outer shell are marked
  309. * as VIS_VISIBLE. After doing all of the tunnels, all voxels which are
  310. * not now marked as VIS_VISIBLE are marked as VIS_SOLID. This way we
  311. * can absorb all internal geometry into one big blob. Next we will
  312. * suck out the internals of that blob :-)
  313. *
  314. * INPUTS:
  315. * none
  316. *
  317. * OUTPUS:
  318. * none
  319. *
  320. ************************************************************************/
  321. void VoxelClass::Compute_Visiblity
  322. (
  323. Progress_Meter_Class & meter
  324. )
  325. {
  326. int x,y,z;
  327. meter.Finish_In_Steps(ZDim + XDim + ZDim);
  328. /////////////////////////////////////////////////////////////
  329. // First, I'm going to loop through the layers (X-Y planes)
  330. // For each layer, I'll tunnel in the x and y direction
  331. // from each point along its edge. This is identical to
  332. // the way James does his 2d visibility
  333. /////////////////////////////////////////////////////////////
  334. for (z = 0; z < (int)ZDim; z++) {
  335. // Tunneling in the X direction
  336. for (y = 0; y < (int)YDim; y++) {
  337. for (x = 0; x < (int)XDim; x++) {
  338. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  339. raw_set_vis(x,y,z,VIS_VISIBLE);
  340. }
  341. for (x = (int)XDim-1; x >= 0; x--) {
  342. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  343. raw_set_vis(x,y,z,VIS_VISIBLE);
  344. }
  345. }
  346. // Tunneling in the Y direction
  347. for (x = 0; x < (int)XDim; x++) {
  348. for (y = 0; y < (int)YDim; y++) {
  349. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  350. raw_set_vis(x,y,z,VIS_VISIBLE);
  351. }
  352. for (y = (int)YDim-1; y >= 0; y--) {
  353. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  354. raw_set_vis(x,y,z,VIS_VISIBLE);
  355. }
  356. }
  357. meter.Add_Increment();
  358. if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
  359. } // done with the X-Y layers
  360. /////////////////////////////////////////////////////////////
  361. // Now I'm going to tunnel up and down through the object.
  362. // To do this, I will loop across the width of the object
  363. // (the X direction) and at each step tunnel through the
  364. // Y-Z plane from all points along the top and bottom.
  365. /////////////////////////////////////////////////////////////
  366. for (x = 0; x < (int)XDim; x++) {
  367. // Tunneling in the Z direction
  368. for (y = 0; y < (int)YDim; y++) {
  369. for (z = 0; z < (int)ZDim; z++) {
  370. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  371. raw_set_vis(x,y,z,VIS_VISIBLE);
  372. }
  373. for (z = (int)ZDim-1; z >= 0; z--) {
  374. if (raw_read_vis(x,y,z) == VIS_SOLID) break;
  375. raw_set_vis(x,y,z,VIS_VISIBLE);
  376. }
  377. }
  378. meter.Add_Increment();
  379. if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
  380. } // done with the X-Z layers
  381. ///////////////////////////////////////////////////////////
  382. // Now, we search for all of the VIS_UNKNOWN voxels and
  383. // set them to VIS_SOLID and we are done voxelizing
  384. ///////////////////////////////////////////////////////////
  385. for (z = 0; z < (int)ZDim; z++) {
  386. for (y = 0; y < (int)YDim; y++) {
  387. for (x = 0; x < (int)XDim; x++) {
  388. int vis = raw_read_vis(x,y,z);
  389. if (vis == VIS_UNKNOWN) {
  390. raw_set_vis(x,y,z,VIS_SOLID);
  391. }
  392. }
  393. }
  394. meter.Add_Increment();
  395. if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
  396. }
  397. }
  398. /************************************************************************
  399. * VoxelClass::raw_read_vis
  400. *
  401. * safe read of the visiblity data at i,j,k
  402. *
  403. * INPUTS:
  404. * i,j,k - integer indices of the visiblity data to read
  405. *
  406. * OUTPUS:
  407. * none
  408. *
  409. ************************************************************************/
  410. uint8 VoxelClass::raw_read_vis
  411. (
  412. int i,
  413. int j,
  414. int k
  415. )
  416. {
  417. if (i<0) return 0;
  418. if (j<0) return 0;
  419. if (k<0) return 0;
  420. if (i>=(int)XDim) return 0;
  421. if (j>=(int)YDim) return 0;
  422. if (k>=(int)ZDim) return 0;
  423. return VisData[i + j*XDim + k*XDim*YDim];
  424. }
  425. /************************************************************************
  426. * VoxelClass::raw_set_vis
  427. *
  428. * safe set of the visibility data at i,j,k
  429. *
  430. * INPUTS:
  431. * i,j,k - integer indices of the visibility data to set
  432. * val - value to set.
  433. *
  434. * OUTPUS:
  435. * none
  436. *
  437. ************************************************************************/
  438. void VoxelClass::raw_set_vis(
  439. int i,
  440. int j,
  441. int k,
  442. uint8 val
  443. )
  444. {
  445. if (i<0) return;
  446. if (j<0) return;
  447. if (k<0) return;
  448. if (i>=(int)XDim) return;
  449. if (j>=(int)YDim) return;
  450. if (k>=(int)ZDim) return;
  451. VisData[i + j*XDim + k*XDim*YDim] = val;
  452. return;
  453. }
  454. void compute_dimensions
  455. (
  456. INodeListClass & meshlist,
  457. const Matrix3 & parenttm,
  458. TimeValue curtime,
  459. Point3 * set_min,
  460. Point3 * set_max
  461. )
  462. {
  463. // Find the minimum and maximum extents in the X, Y, and Z directions.
  464. // Also find the total surface area.
  465. Point3 min;
  466. Point3 max;
  467. float surface_area = 0.0;
  468. BOOL first = TRUE;
  469. for ( unsigned i = 0; i < meshlist.Num_Nodes() ; ++ i )
  470. {
  471. // Get the relavent data from the INode
  472. INode * n = meshlist[i];
  473. Object * obj = n->EvalWorldState(curtime).obj;
  474. TriObject * tri = (TriObject *)obj->ConvertToType(curtime, triObjectClassID);
  475. Mesh * mesh = &(tri->mesh);
  476. Matrix3 tm = n->GetObjTMAfterWSM(curtime);
  477. // Compute a matrix which takes vertices of this mesh into the
  478. // specified parent space.
  479. Matrix3 delta = tm * Inverse(parenttm);
  480. unsigned verts = mesh->getNumVerts();
  481. unsigned faces = mesh->getNumFaces();
  482. for ( unsigned vert_index = 0; vert_index < verts; ++ vert_index )
  483. {
  484. Point3 p = delta * mesh->verts [vert_index];
  485. if ( first )
  486. {
  487. first = FALSE;
  488. min = max = p;
  489. }
  490. else
  491. {
  492. if ( p.x < min.x ) min.x = p.x;
  493. if ( p.y < min.y ) min.y = p.y;
  494. if ( p.z < min.z ) min.z = p.z;
  495. if ( p.x > max.x ) max.x = p.x;
  496. if ( p.y > max.y ) max.y = p.y;
  497. if ( p.z > max.z ) max.z = p.z;
  498. }
  499. }
  500. for ( unsigned face_index = 0; face_index < faces; ++ face_index )
  501. {
  502. Face face = mesh->faces [ face_index ];
  503. Point3 a = mesh->verts [ face.v[0] ];
  504. Point3 b = mesh->verts [ face.v[1] ];
  505. Point3 c = mesh->verts [ face.v[2] ];
  506. double area = 0.5 * Length ( CrossProd ( b - a, c - a ) );
  507. surface_area += (float) area;
  508. }
  509. }
  510. // In the odd case that there are no vertices....
  511. if ( first )
  512. {
  513. min = max = Point3 (0,0,0);
  514. }
  515. *set_min = min;
  516. *set_max = max;
  517. }
  518. uint8 VoxelClass::Is_Solid(int i,int j,int k)
  519. {
  520. return (raw_read_vis(i,j,k) == VIS_SOLID);
  521. }
  522. void VoxelClass::Compute_Physical_Properties(double Volume[1],double CM[3],double I[9])
  523. {
  524. int i,j,k;
  525. // volume of a single voxel block:
  526. double bvol = BlockXDim * BlockYDim * BlockZDim;
  527. // volume of object
  528. double volume = 0.0;
  529. Point3 cm(0.0,0.0,0.0);
  530. int numblocks = 0;
  531. ////////////////////////////////////////////////////////////////////////
  532. // compute the volume and the center of mass
  533. ////////////////////////////////////////////////////////////////////////
  534. for (k=0; k < ZDim; k++) {
  535. for (j=0; j < YDim; j++) {
  536. for (i=0; i < XDim; i++) {
  537. if (Is_Solid(i,j,k)) {
  538. // Add this block's volume to the total
  539. volume += bvol;
  540. // Add this block's position to the CM computation
  541. cm += Voxel_Position(i,j,k);
  542. numblocks++;
  543. }
  544. }
  545. }
  546. }
  547. cm.x = cm.x / (double)numblocks;
  548. cm.y = cm.y / (double)numblocks;
  549. cm.z = cm.z / (double)numblocks;
  550. CM[0] = cm.x;
  551. CM[1] = cm.y;
  552. CM[2] = cm.z;
  553. Volume[0] = volume;
  554. ////////////////////////////////////////////////////////////////////////
  555. // compute the inertia tensor assuming constant density and factoring
  556. // density out:
  557. //
  558. //
  559. // ( ( ( 2 2
  560. // | | | y + z -(xy) -(xz)
  561. // | | |
  562. // | | | 2 2
  563. // I= den*| | | -(xy) x + z -(yz) dx dy dz
  564. // | | |
  565. // | | | 2 2
  566. // | | | -(xz) -(yz) x + y
  567. // ) ) )
  568. //
  569. ////////////////////////////////////////////////////////////////////////
  570. for (i=0; i < 9; i++) {
  571. I[i] = 0.0;
  572. }
  573. for (k=0; k < ZDim; k++) {
  574. for (j=0; j < YDim; j++) {
  575. for (i=0; i < XDim; i++) {
  576. if (Is_Solid(i,j,k)) {
  577. // position of block, relative to the CM
  578. Point3 pos = Voxel_Position(i,j,k) - cm;
  579. // moments of inertia
  580. double y2z2 = pos.y * pos.y + pos.z * pos.z;
  581. double x2z2 = pos.x * pos.x + pos.z * pos.z;
  582. double x2y2 = pos.x * pos.x + pos.y * pos.y;
  583. // products of inertia
  584. double xy = pos.x * pos.y;
  585. double xz = pos.x * pos.z;
  586. double yz = pos.y * pos.z;
  587. // add to the running total!
  588. I[0] += y2z2 * bvol;
  589. I[1] += -xy * bvol;
  590. I[2] += -xz * bvol;
  591. I[3] += -xy * bvol;
  592. I[4] += x2z2 * bvol;
  593. I[5] += -yz * bvol;
  594. I[6] += -xz * bvol;
  595. I[7] += -yz * bvol;
  596. I[8] += x2y2 * bvol;
  597. }
  598. }
  599. }
  600. }
  601. }
  602. Point3 VoxelClass::Voxel_Position(int i,int j,int k)
  603. {
  604. // returns the coordinates of the center of block(i,j,k)
  605. return Point3(
  606. Offset.x + i * BlockXDim + BlockXDim / 2.0,
  607. Offset.y + j * BlockYDim + BlockYDim / 2.0,
  608. Offset.z + k * BlockZDim + BlockZDim / 2.0
  609. );
  610. }