| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /* $Header: /Commando/Code/Tools/max2w3d/vxl.cpp 4 10/28/97 6:08p Greg_h $ */
- /***********************************************************************************************
- *** Confidential - Westwood Studios ***
- ***********************************************************************************************
- * *
- * Project Name : Commando / G Math Library *
- * *
- * $Archive:: /Commando/Code/Tools/max2w3d/vxl.cpp $*
- * *
- * $Author:: Greg_h $*
- * *
- * $Modtime:: 10/14/97 3:07p $*
- * *
- * $Revision:: 4 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "vxl.h"
- #include "errclass.h"
- /*
- This module will voxelize one or more meshes. It is only used to compute
- some things like Moment of Inertia and Center of Mass for the object.
- Much of the code which was doing the shading and lighting computations
- has been stripped out.
- */
- static void compute_dimensions(
- INodeListClass & meshlist,
- const Matrix3 & parenttm,
- TimeValue curtime,
- Point3 * min,
- Point3 * max
- );
- #define VIS_UNKNOWN 0
- #define VIS_SOLID 1
- #define VIS_VISIBLE 2
- /************************************************************************
- * VoxelClass Constructor
- *
- * Voxelize a list of meshes.
- *
- * INPUTS:
- *
- * OUTPUS:
- *
- ************************************************************************/
- VoxelClass::VoxelClass
- (
- INodeListClass & meshlist,
- int resolution,
- Matrix3 parenttm,
- TimeValue time,
- Progress_Meter_Class & meter
- )
- {
- Resolution = resolution;
- ParentTM = parenttm;
- CurTime = time;
- XDim = resolution + 1;
- YDim = resolution + 1;
- ZDim = resolution + 1;
-
- // assert that none of the dimensions
- // were too big. (if this happened, VoxelData is going
- // to be a *HUMONGOUS* amount of memory...)
- assert(XDim < 256);
- assert(YDim < 256);
- assert(ZDim < 256);
-
- // Allocate visibility flags array
- VisData = new uint8[XDim * YDim * ZDim];
- if (VisData == NULL) {
- throw ErrorClass("out of memory!");
- }
- memset(VisData,0,XDim*YDim*ZDim);
- /*
- ** compute the two corners of the bounding box of
- ** these meshes. Note that these coordinates are
- ** be specified in the space defined by ParentTM.
- */
- Point3 min;
- Point3 max;
- compute_dimensions(meshlist,ParentTM,CurTime,&min,&max);
- /*
- ** The voxelizer uses three values: offset, size,
- ** and scale:
- **
- ** offset - the position of the "most negative" corner
- ** size - dimensions of the box
- ** scale - scale factor to apply to vertices after they've
- ** been translated by -offset
- **
- */
- Size = max - min;
- Offset = min;
- Scale.x = Resolution / Size.x;
- Scale.y = Resolution / Size.y;
- Scale.z = Resolution / Size.z;
- /*
- ** Dimensions of a single voxel block
- */
- BlockXDim = Size.x / Resolution;
- BlockYDim = Size.y / Resolution;
- BlockZDim = Size.z / Resolution;
-
- /*
- ** Voxelize the meshes!
- */
- Quantize_Meshes
- (
- meshlist,
- meter
- );
- }
- /************************************************************************
- * VoxelClass Destructor
- *
- * De-Allocates memory used by the voxel object
- *
- * INPUTS:
- * none
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- VoxelClass::~VoxelClass()
- {
- if (VisData != NULL) delete[] VisData;
- }
- /************************************************************************
- * VoxelClass::Quantize_Meshes
- *
- * Generataes voxel data from the list of meshes passed in.
- *
- * INPUTS:
- * meshlist - list of meshes to "voxelize"
- * meter - progress meter object
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- void VoxelClass::Quantize_Meshes
- (
- INodeListClass & meshlist,
- Progress_Meter_Class & meter
- )
- {
- /*
- ** Progress meter updating
- */
- meter.Finish_In_Steps(2);
- Progress_Meter_Class slabmeter(meter,meter.Increment);
- slabmeter.Finish_In_Steps(ZDim);
-
- /*
- ** Generate the Voxel Layer for each slice of the model
- */
- float min_z = Offset.z;
- float max_z = Offset.z + Size.z;
- float sliceh = Size.z / (float)ZDim;
- for (int slicecount = 0; slicecount < ZDim; slicecount++ )
- {
- float slicez = min_z + (max_z - min_z) * ((float)slicecount/(float)(ZDim-1));
- VoxelLayerClass * vlayer = new VoxelLayerClass
- (
- meshlist,
- CurTime,
- ParentTM,
- Offset,
- Scale,
- slicez,
- sliceh,
- XDim,
- YDim
- );
- Set_Layer(*vlayer,slicecount);
- slabmeter.Add_Increment();
- if (slabmeter.Cancelled()) throw ErrorClass("Export Cancelled");
- delete vlayer;
- }
- meter.Add_Increment();
- // 3D visibility calculations
- Progress_Meter_Class vismeter(meter,meter.Increment);
- Compute_Visiblity(vismeter);
- meter.Add_Increment();
- // Compute the voxel bounding box
- Compute_Bounding_Box(Size,Offset);
- }
- /************************************************************************
- * VoxelClass::Set_Layer
- *
- * Sets a layer of the Voxel data according to contents of the passed
- * bitmap.
- *
- * INPUTS:
- * bitmap - bitmap to use as the source data
- * z - z co-ordinate of the layer to set
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- void VoxelClass::Set_Layer
- (
- VoxelLayerClass & vlayer,
- uint32 z
- )
- {
- // bitmap must have same x&y dimensions as the voxel space
- if (vlayer.Get_Width() != (unsigned)XDim) return;
- if (vlayer.Get_Height() != (unsigned)YDim) return;
- // Copy the solid voxels into our voxel cube
- for (unsigned j=0; j<vlayer.Get_Height(); j++) {
- for (unsigned i=0; i<vlayer.Get_Width(); i++) {
- if (!vlayer.Is_Solid(i,j)) {
- raw_set_vis(i,j,z,VIS_UNKNOWN);
- } else {
- raw_set_vis(i,j,z,VIS_SOLID);
- }
- }
- }
- }
- /************************************************************************
- * VoxelClass::Compute_Bounding_Box
- *
- * Given the information which the MeshObjList gives us regarding the
- * size and offset of the group of meshes we are processing, compute the
- * scaled co-ordinates of the eight corners of the bounding box.
- *
- * INPUTS:
- * size - width, length, and height of the bounding box
- * offset - offset to the first point
- * scale - scale factors for each axis.
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- void VoxelClass::Compute_Bounding_Box
- (
- Point3 size,
- Point3 offset
- )
- {
- int i;
- // First we just set the bounding box values in the
- // original scale. The corners must be specified in the
- // order expected.
- // +x +y -z
- BoxCorner[0].x = offset.x + size.x;
- BoxCorner[0].y = offset.y + size.y;
- BoxCorner[0].z = offset.z;
-
- // +x -y -z
- BoxCorner[1].x = offset.x + size.x;
- BoxCorner[1].y = offset.y;
- BoxCorner[1].z = offset.z;
- // -x -y -z
- BoxCorner[2].x = offset.x;
- BoxCorner[2].y = offset.y;
- BoxCorner[2].z = offset.z;
-
- // -x +y -z
- BoxCorner[3].x = offset.x;
- BoxCorner[3].y = offset.y + size.y;
- BoxCorner[3].z = offset.z;
-
- // +x +y +z
- BoxCorner[4].x = offset.x + size.x;
- BoxCorner[4].y = offset.y + size.y;
- BoxCorner[4].z = offset.z + size.z;
-
- // +x -y +z
- BoxCorner[5].x = offset.x + size.x;
- BoxCorner[5].y = offset.y;
- BoxCorner[5].z = offset.z + size.z;
- // -x -y +z
- BoxCorner[6].x = offset.x;
- BoxCorner[6].y = offset.y;
- BoxCorner[6].z = offset.z + size.z;
-
- // -x +y +z
- BoxCorner[7].x = offset.x;
- BoxCorner[7].y = offset.y + size.y;
- BoxCorner[7].z = offset.z + size.z;
-
- // Now, scale all of them into the voxel co-ordinate system
- for (i=0; i<8; i++) {
- BoxCorner[i].x *= Scale.x;
- BoxCorner[i].y *= Scale.y;
- BoxCorner[i].z *= Scale.z;
- }
- }
- /************************************************************************
- * VoxelClass::Compute_Visibility
- *
- * Detects the outer shell of the voxel object.
- * "Tunnels" are drilled into the voxel space from all directions, all
- * voxels that are encountered before hitting the outer shell are marked
- * as VIS_VISIBLE. After doing all of the tunnels, all voxels which are
- * not now marked as VIS_VISIBLE are marked as VIS_SOLID. This way we
- * can absorb all internal geometry into one big blob. Next we will
- * suck out the internals of that blob :-)
- *
- * INPUTS:
- * none
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- void VoxelClass::Compute_Visiblity
- (
- Progress_Meter_Class & meter
- )
- {
- int x,y,z;
- meter.Finish_In_Steps(ZDim + XDim + ZDim);
-
- /////////////////////////////////////////////////////////////
- // First, I'm going to loop through the layers (X-Y planes)
- // For each layer, I'll tunnel in the x and y direction
- // from each point along its edge. This is identical to
- // the way James does his 2d visibility
- /////////////////////////////////////////////////////////////
- for (z = 0; z < (int)ZDim; z++) {
- // Tunneling in the X direction
- for (y = 0; y < (int)YDim; y++) {
- for (x = 0; x < (int)XDim; x++) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- for (x = (int)XDim-1; x >= 0; x--) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- }
- // Tunneling in the Y direction
- for (x = 0; x < (int)XDim; x++) {
- for (y = 0; y < (int)YDim; y++) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- for (y = (int)YDim-1; y >= 0; y--) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- }
- meter.Add_Increment();
- if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
- } // done with the X-Y layers
-
- /////////////////////////////////////////////////////////////
- // Now I'm going to tunnel up and down through the object.
- // To do this, I will loop across the width of the object
- // (the X direction) and at each step tunnel through the
- // Y-Z plane from all points along the top and bottom.
- /////////////////////////////////////////////////////////////
- for (x = 0; x < (int)XDim; x++) {
- // Tunneling in the Z direction
- for (y = 0; y < (int)YDim; y++) {
- for (z = 0; z < (int)ZDim; z++) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- for (z = (int)ZDim-1; z >= 0; z--) {
- if (raw_read_vis(x,y,z) == VIS_SOLID) break;
- raw_set_vis(x,y,z,VIS_VISIBLE);
- }
- }
- meter.Add_Increment();
- if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
- } // done with the X-Z layers
- ///////////////////////////////////////////////////////////
- // Now, we search for all of the VIS_UNKNOWN voxels and
- // set them to VIS_SOLID and we are done voxelizing
- ///////////////////////////////////////////////////////////
- for (z = 0; z < (int)ZDim; z++) {
- for (y = 0; y < (int)YDim; y++) {
- for (x = 0; x < (int)XDim; x++) {
- int vis = raw_read_vis(x,y,z);
- if (vis == VIS_UNKNOWN) {
- raw_set_vis(x,y,z,VIS_SOLID);
- }
- }
- }
- meter.Add_Increment();
- if (meter.Cancelled()) throw ErrorClass("Export Cancelled");
- }
- }
- /************************************************************************
- * VoxelClass::raw_read_vis
- *
- * safe read of the visiblity data at i,j,k
- *
- * INPUTS:
- * i,j,k - integer indices of the visiblity data to read
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- uint8 VoxelClass::raw_read_vis
- (
- int i,
- int j,
- int k
- )
- {
- if (i<0) return 0;
- if (j<0) return 0;
- if (k<0) return 0;
- if (i>=(int)XDim) return 0;
- if (j>=(int)YDim) return 0;
- if (k>=(int)ZDim) return 0;
- return VisData[i + j*XDim + k*XDim*YDim];
- }
- /************************************************************************
- * VoxelClass::raw_set_vis
- *
- * safe set of the visibility data at i,j,k
- *
- * INPUTS:
- * i,j,k - integer indices of the visibility data to set
- * val - value to set.
- *
- * OUTPUS:
- * none
- *
- ************************************************************************/
- void VoxelClass::raw_set_vis(
- int i,
- int j,
- int k,
- uint8 val
- )
- {
- if (i<0) return;
- if (j<0) return;
- if (k<0) return;
- if (i>=(int)XDim) return;
- if (j>=(int)YDim) return;
- if (k>=(int)ZDim) return;
- VisData[i + j*XDim + k*XDim*YDim] = val;
- return;
- }
- void compute_dimensions
- (
- INodeListClass & meshlist,
- const Matrix3 & parenttm,
- TimeValue curtime,
- Point3 * set_min,
- Point3 * set_max
- )
- {
- // Find the minimum and maximum extents in the X, Y, and Z directions.
- // Also find the total surface area.
- Point3 min;
- Point3 max;
- float surface_area = 0.0;
- BOOL first = TRUE;
- for ( unsigned i = 0; i < meshlist.Num_Nodes() ; ++ i )
- {
-
- // Get the relavent data from the INode
- INode * n = meshlist[i];
- Object * obj = n->EvalWorldState(curtime).obj;
- TriObject * tri = (TriObject *)obj->ConvertToType(curtime, triObjectClassID);
- Mesh * mesh = &(tri->mesh);
- Matrix3 tm = n->GetObjTMAfterWSM(curtime);
-
- // Compute a matrix which takes vertices of this mesh into the
- // specified parent space.
- Matrix3 delta = tm * Inverse(parenttm);
-
- unsigned verts = mesh->getNumVerts();
- unsigned faces = mesh->getNumFaces();
- for ( unsigned vert_index = 0; vert_index < verts; ++ vert_index )
- {
- Point3 p = delta * mesh->verts [vert_index];
- if ( first )
- {
- first = FALSE;
- min = max = p;
- }
- else
- {
- if ( p.x < min.x ) min.x = p.x;
- if ( p.y < min.y ) min.y = p.y;
- if ( p.z < min.z ) min.z = p.z;
- if ( p.x > max.x ) max.x = p.x;
- if ( p.y > max.y ) max.y = p.y;
- if ( p.z > max.z ) max.z = p.z;
- }
- }
- for ( unsigned face_index = 0; face_index < faces; ++ face_index )
- {
- Face face = mesh->faces [ face_index ];
- Point3 a = mesh->verts [ face.v[0] ];
- Point3 b = mesh->verts [ face.v[1] ];
- Point3 c = mesh->verts [ face.v[2] ];
- double area = 0.5 * Length ( CrossProd ( b - a, c - a ) );
- surface_area += (float) area;
- }
- }
- // In the odd case that there are no vertices....
- if ( first )
- {
- min = max = Point3 (0,0,0);
- }
- *set_min = min;
- *set_max = max;
- }
- uint8 VoxelClass::Is_Solid(int i,int j,int k)
- {
- return (raw_read_vis(i,j,k) == VIS_SOLID);
- }
- void VoxelClass::Compute_Physical_Properties(double Volume[1],double CM[3],double I[9])
- {
- int i,j,k;
-
- // volume of a single voxel block:
- double bvol = BlockXDim * BlockYDim * BlockZDim;
-
- // volume of object
- double volume = 0.0;
- Point3 cm(0.0,0.0,0.0);
- int numblocks = 0;
- ////////////////////////////////////////////////////////////////////////
- // compute the volume and the center of mass
- ////////////////////////////////////////////////////////////////////////
- for (k=0; k < ZDim; k++) {
- for (j=0; j < YDim; j++) {
- for (i=0; i < XDim; i++) {
- if (Is_Solid(i,j,k)) {
- // Add this block's volume to the total
- volume += bvol;
-
- // Add this block's position to the CM computation
- cm += Voxel_Position(i,j,k);
- numblocks++;
- }
- }
- }
- }
- cm.x = cm.x / (double)numblocks;
- cm.y = cm.y / (double)numblocks;
- cm.z = cm.z / (double)numblocks;
- CM[0] = cm.x;
- CM[1] = cm.y;
- CM[2] = cm.z;
- Volume[0] = volume;
- ////////////////////////////////////////////////////////////////////////
- // compute the inertia tensor assuming constant density and factoring
- // density out:
- //
- //
- // ( ( ( 2 2
- // | | | y + z -(xy) -(xz)
- // | | |
- // | | | 2 2
- // I= den*| | | -(xy) x + z -(yz) dx dy dz
- // | | |
- // | | | 2 2
- // | | | -(xz) -(yz) x + y
- // ) ) )
- //
- ////////////////////////////////////////////////////////////////////////
- for (i=0; i < 9; i++) {
- I[i] = 0.0;
- }
- for (k=0; k < ZDim; k++) {
- for (j=0; j < YDim; j++) {
- for (i=0; i < XDim; i++) {
- if (Is_Solid(i,j,k)) {
-
- // position of block, relative to the CM
- Point3 pos = Voxel_Position(i,j,k) - cm;
- // moments of inertia
- double y2z2 = pos.y * pos.y + pos.z * pos.z;
- double x2z2 = pos.x * pos.x + pos.z * pos.z;
- double x2y2 = pos.x * pos.x + pos.y * pos.y;
- // products of inertia
- double xy = pos.x * pos.y;
- double xz = pos.x * pos.z;
- double yz = pos.y * pos.z;
-
- // add to the running total!
- I[0] += y2z2 * bvol;
- I[1] += -xy * bvol;
- I[2] += -xz * bvol;
- I[3] += -xy * bvol;
- I[4] += x2z2 * bvol;
- I[5] += -yz * bvol;
- I[6] += -xz * bvol;
- I[7] += -yz * bvol;
- I[8] += x2y2 * bvol;
- }
- }
- }
- }
- }
- Point3 VoxelClass::Voxel_Position(int i,int j,int k)
- {
- // returns the coordinates of the center of block(i,j,k)
- return Point3(
- Offset.x + i * BlockXDim + BlockXDim / 2.0,
- Offset.y + j * BlockYDim + BlockYDim / 2.0,
- Offset.z + k * BlockZDim + BlockZDim / 2.0
- );
- }
|