/* ** Command & Conquer Generals Zero Hour(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 . */ /* $Header: /Commando/Code/Tools/max2w3d/vxllayer.cpp 4 10/28/97 6:08p Greg_h $ */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : Commando / G * * * * File Name : VXLLAYER.CPP * * * * Programmer : Greg Hjelstrom * * * * Start Date : 06/10/97 * * * * Last Update : June 10, 1997 [GH] * * * *---------------------------------------------------------------------------------------------* * Functions: * * VoxelLayerClass::VoxelLayerClass -- Constructor for VoxelLayerClass * * VoxelLayerClass::Intersect_Triangle -- Intersect a triangle with the slab * * VoxelLayerClass::Draw_Line -- Draw a line of voxels into the slab * * VoxelLayerClass::Scan_Triangle -- Clip and scan-convert a triangle into the slab * * clip_tri_to_slab -- Clips a triangle against a voxel slab * * clip_poly -- clip a polygon against a single 3D plane * * output -- Emit a vertex into a polygons vertex list * * inside -- Test whether a point is in the front half-space of a plane * * intersect -- compute intersection between a line and a plane * * clear_scan_table -- clears the static scanline table * * fixup_scan_table -- ensure all spans are left->right in order * * scan_edge -- Scan convert an edge * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "vxllayer.h" #include "plane.h" /*************************************************************************************** ** local types ***************************************************************************************/ struct vertexstruct { Point3 Pos; Point3 Bary; }; struct scanstruct { vertexstruct P[2]; }; /*************************************************************************************** ** static data ***************************************************************************************/ static scanstruct _scantab[256]; const int LEFT = 0; const int RIGHT = 1; const float EMPTY_SPAN = -10000.0f; /*************************************************************************************** ** local functions ***************************************************************************************/ static void clip_tri_to_slab( Point3 p0, Point3 p1, Point3 p2, float z0, float z1, vertexstruct * outverts, int * setnum); static void clip_poly( vertexstruct * inverts, int innum, vertexstruct * outverts, int * outnum, const PlaneClass & clipplane); static void output( const vertexstruct & outvert, vertexstruct * poly, int * numverts); static int inside( const vertexstruct & p, const PlaneClass & plane); static vertexstruct intersect( const vertexstruct & p0, const vertexstruct & p1, const PlaneClass & plane); static void clear_scan_table(void); static void fixup_scan_table( int y0, int y1); static void scan_edge( const vertexstruct & p0, const vertexstruct & p1); /*********************************************************************************************** * VoxelLayerClass::VoxelLayerClass -- Constructor for VoxelLayerClass * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ VoxelLayerClass::VoxelLayerClass ( INodeListClass & object_list, TimeValue time, Matrix3 parenttm, Point3 offset, Point3 scale, float slicez, float sliceh, int bmwidth, int bmheight ) { unsigned i; SliceZ = slicez; SliceH = sliceh; SliceZ0 = slicez - sliceh / 2; SliceZ1 = slicez + sliceh / 2; bitmap_width = bmwidth; bitmap_height = bmheight; // Initialize everything with zero memset ( &(Solid[0][0]), 0, sizeof(Solid)); // Go through the list of objects and intersect them with the plane. for ( i = 0; i < object_list.Num_Nodes(); i++ ) { // Get relavent data from MAX INode * inode = object_list[i]; Object * obj = inode->EvalWorldState(time).obj; TriObject * tri = (TriObject *)obj->ConvertToType(time, triObjectClassID); Mesh * mesh = &(tri->mesh); Matrix3 objtm = inode->GetObjectTM(time); // Compute a delta matrix which puts vertices into the parent space Matrix3 delta = objtm * Inverse(parenttm); // Loop through each face, intersecting it with the slice. unsigned faces = mesh->getNumFaces(); for ( unsigned face_index = 0; face_index < faces; ++ face_index ) { Face & face = mesh->faces [ face_index ]; // transform the vertices into the parent space Point3 a = mesh->verts [ face.v[0] ] * delta; Point3 b = mesh->verts [ face.v[1] ] * delta; Point3 c = mesh->verts [ face.v[2] ] * delta; // shift the vertices to the origin a.x -= offset.x; a.y -= offset.y; b.x -= offset.x; b.y -= offset.y; c.x -= offset.x; c.y -= offset.y; // scale the vertices into the voxel grid a.x *= scale.x; a.y *= scale.y; b.x *= scale.x; b.y *= scale.y; c.x *= scale.x; c.y *= scale.y; // Intersect_Triangle ( a, b, c, SliceZ ); Scan_Triangle( a, b, c ); } } } /*********************************************************************************************** * VoxelLayerClass::Intersect_Triangle -- Intersect a triangle with the slab * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ void VoxelLayerClass::Intersect_Triangle ( Point3 a, Point3 b, Point3 c, float z ) { double start_x, start_y, end_x, end_y; // If the triangle is wholly above or below the intersection plane, // it does not intersect with the plane. if ( a.z < z && b.z < z && c.z < z ) return; if ( a.z > z && b.z > z && c.z > z ) return; // Find the upward intersection moving counterclockwise. This will be // the start of the edge. if ( a.z < z && b.z >= z ) { start_x = a.x + (b.x - a.x) * (z - a.z) / (b.z - a.z); start_y = a.y + (b.y - a.y) * (z - a.z) / (b.z - a.z); } else if ( b.z < z && c.z >= z ) { start_x = b.x + (c.x - b.x) * (z - b.z) / (c.z - b.z); start_y = b.y + (c.y - b.y) * (z - b.z) / (c.z - b.z); } else if ( c.z < z && a.z >= z ) { start_x = c.x + (a.x - c.x) * (z - c.z) / (a.z - c.z); start_y = c.y + (a.y - c.y) * (z - c.z) / (a.z - c.z); } else { return; } // Find the downward intersection moving counterclockwise. This is the end // of the edge. if ( a.z >= z && b.z < z ) { end_x = a.x + (b.x - a.x) * (z - a.z) / (b.z - a.z); end_y = a.y + (b.y - a.y) * (z - a.z) / (b.z - a.z); } else if ( b.z >= z && c.z < z ) { end_x = b.x + (c.x - b.x) * (z - b.z) / (c.z - b.z); end_y = b.y + (c.y - b.y) * (z - b.z) / (c.z - b.z); } else if ( c.z >= z && a.z < z ) { end_x = c.x + (a.x - c.x) * (z - c.z) / (a.z - c.z); end_y = c.y + (a.y - c.y) * (z - c.z) / (a.z - c.z); } else { return; } // Draw the edge into the bitmap. Draw_Line(start_x, start_y, end_x, end_y); } /*********************************************************************************************** * VoxelLayerClass::Draw_Line -- Draw a line of voxels into the slab * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ void VoxelLayerClass::Draw_Line ( double x0, double y0, double x1, double y1 ) { // Fill in the squares containing the line's endpoints. Add_Solid((int)x0, (int)y0); Add_Solid((int)x1, (int)y1); // Fill in the squares between the endpoints. double delta_x = fabs (x1 - x0); double delta_y = fabs (y1 - y0); if ( delta_x > delta_y ) { // This is an X-major line. if ( x0 > x1 ) { double temp = x0; x0 = x1; x1 = temp; temp = y0; y0 = y1; y1 = temp; } double step_y = (y1 - y0) / delta_x; double y = y0 + step_y * (floor (x0 + 1) - x0); for ( int x = (int) x0; x < (int) x1; ++ x ) { if ( (int) y >= 0 && (int) y < bitmap_height ) { Add_Solid(x, (int)y); Add_Solid(x + 1, (int)y); } y += step_y; } } else { // This is a Y-major line. if ( y0 > y1 ) { double temp = x0; x0 = x1; x1 = temp; temp = y0; y0 = y1; y1 = temp; } double step_x = (x1 - x0) / delta_y; double x = x0 + step_x * (floor (y0 + 1) - y0); for ( int y = (int) y0; y < (int) y1; ++ y ) { if ( (int) x >= 0 && (int) x < 256 ) { Add_Solid((int)x, y); Add_Solid((int)x, y+1); } } } } /*********************************************************************************************** * VoxelLayerClass::Scan_Triangle -- Clip and scan-convert a triangle into the slab * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ void VoxelLayerClass::Scan_Triangle ( Point3 p0, Point3 p1, Point3 p2 ) { int i; // check if the entire triangle is above or below the slab: if (p0.z < SliceZ0 && p1.z < SliceZ0 && p2.z < SliceZ1) return; if (p0.z > SliceZ1 && p1.z > SliceZ1 && p2.z > SliceZ1) return; // clip the triangle to the slab vertexstruct polyvert[8]; int numverts; clip_tri_to_slab(p0,p1,p2,SliceZ0,SliceZ1,polyvert,&numverts); if (numverts == 0) return; // clear the scanline table, get y-extents of polygon clear_scan_table(); float miny = polyvert[0].Pos.y; float maxy = polyvert[0].Pos.y; for (i=1; i maxy) maxy = polyvert[i].Pos.y; } // scanconvert the triangle int start = numverts - 1; for (i=0; i= 0.0f) { return 1; } else { return 0; } } /*********************************************************************************************** * intersect -- compute intersection between a line and a plane * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ static vertexstruct intersect ( const vertexstruct & p0, const vertexstruct & p1, const PlaneClass & plane ) { float t; Point3 delta = p1.Pos - p0.Pos; float num = -( plane.N[0] * p0.Pos.x + plane.N[1] * p0.Pos.y + plane.N[2] * p0.Pos.z + plane.D ); float den = plane.N[0] * delta.x + plane.N[1] * delta.y + plane.N[2] * delta.z; if (den != 0.0f) { t = num / den; } else { t = 0.0f; } vertexstruct i; i.Pos = (1.0f - t) * p0.Pos + t*p1.Pos; i.Bary = (1.0f - t) * p0.Bary + t*p1.Bary; return i; } /*********************************************************************************************** * clear_scan_table -- clears the static scanline table * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ static void clear_scan_table(void) { memset(_scantab,0,sizeof(_scantab)); for (int i=0; i<256; i++) { _scantab[i].P[0].Pos.x = EMPTY_SPAN; _scantab[i].P[1].Pos.x = EMPTY_SPAN; } } /*********************************************************************************************** * fixup_scan_table -- ensure all spans are left->right in order * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ static void fixup_scan_table(int y0,int y1) { int i; assert(y1 >= y0); // Ensure the left -> right convention is followed. for (i=y0; i<=y1; i++) { if (_scantab[i].P[LEFT].Pos.x > _scantab[i].P[RIGHT].Pos.x) { vertexstruct tmp = _scantab[i].P[LEFT]; _scantab[i].P[LEFT] = _scantab[i].P[RIGHT]; _scantab[i].P[RIGHT] = tmp; } } // Ensure that we leave no gaps. for (i=y0; i _scantab[i].P[RIGHT].Pos.x) { _scantab[i+1].P[LEFT].Pos.x = _scantab[i].P[RIGHT].Pos.x; } } } /*********************************************************************************************** * scan_edge -- Scan convert an edge * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/10/1997 GH : Created. * *=============================================================================================*/ static void scan_edge ( const vertexstruct & p0, const vertexstruct & p1 ) { // is this a perfectly horizontal edge: if (floor(p0.Pos.y) == floor(p1.Pos.y)) { int si = (int)floor(p0.Pos.y); const vertexstruct *left, *right; if (p0.Pos.x < p1.Pos.x) { left = &p0; right = &p1; } else { left = &p1; right = &p0; } // does this scanline already have a span in it? if (_scantab[si].P[0].Pos.x != EMPTY_SPAN) { // yes, expand this scanline's span to include this span if (left->Pos.x < _scantab[si].P[LEFT].Pos.x) { _scantab[si].P[LEFT] = *left; } if (right->Pos.x > _scantab[si].P[RIGHT].Pos.x) { _scantab[si].P[RIGHT] = *right; } } else { // no, set this scanline with the span for this edge _scantab[si].P[LEFT] = *left; _scantab[si].P[RIGHT] = *right; } return; } // is this a left or right edge: int side; const vertexstruct *top, *bot; if (p0.Pos.y < p1.Pos.y) { side = RIGHT; top = &p0; bot = &p1; } else { side = LEFT; top = &p1; bot = &p0; } // scan the edge into _scantab for (double y = floor(top->Pos.y); y <= floor(bot->Pos.y); y += 1.0f) { // parametric position on the scanline: double t = (y - floor(top->Pos.y)) / (floor(bot->Pos.y) - floor(top->Pos.y)); // position: _scantab[(int)y].P[side].Pos = (1.0f - (float)t)*top->Pos + (float)t*bot->Pos; // barycentric coords: _scantab[(int)y].P[side].Bary = (1.0f - (float)t)*top->Bary + (float)t*bot->Bary; } }