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