/* ** Command & Conquer Renegade(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 . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : WWPhys * * * * $Archive:: /Commando/Code/wwphys/bpt.cpp $* * * * Author:: Greg_h * * * * $Modtime:: 9/09/99 11:29a $* * * * $Revision:: 15 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * BptClass::BptClass -- constructor * * BptClass::~BptClass -- destructor * * BptClass::Free -- releases all assets in use * * BptClass::Build -- build a bpt from the given mesh (generates a new mesh) * * BptClass::Load -- Initialize this Bpt object from contents of a W3D file * * BptClass::Save -- Save this Bpt object into a W3D file * * BptClass::Render -- submit this object for rendering * * BptClass::Cast_Ray -- Intersect a ray with this Bpt Object * * BptClass::Cast_AABox -- Intersect a swept axis-aligned box. * * BptClass::Cast_OBBox -- Intersect a swept oriented box with this bpt object * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #if 0 // OBSOLETE!!! #ifdef PORT140 #include "bpt.h" #include "vector.h" #include "uarray.h" #include "mesh.h" #include "material.h" #include "chunkio.h" #include "vector3.h" #include "tri.h" #include "wwdebug.h" #include "meshbuild.h" #include "camera.h" #include "sr.hpp" #define BPT_FRONT 0x01 #define BPT_BACK 0x02 #define BPT_ON 0x04 #define BPT_BOTH 0x08 #define BPT_EPSILON 0.0001f #define BPT_COINCIDENCE_EPSILON 0.000001f class BptVertexClass; class BptPolyClass; class BptNodeClass; class BptBuilderClass; class BptImpNodeClass; class BptImpClass; class BptImpBuilderClass; /* ** NormalHasherClass, used by the code which builds the ** table of unique Normal vectors. */ class NormalHasherClass : public HashCalculatorClass { public: virtual bool Items_Match(const Vector3 & a, const Vector3 & b) { // looking for an exact match for normals... return ((a.X == b.X) && (a.Y == b.Y) && (a.Z == b.Z)); } virtual void Compute_Hash(const Vector3 & item) { HashVal = (int)(item.X*12345.6f + item.Y*1714.38484f + item.Z*27561.3f)&1023; } virtual int Num_Hash_Bits(void) { return 10; // 10bit hash value. } virtual int Num_Hash_Values(void) { return 1; // only one hash value for normals, require *exact* match } virtual int Get_Hash_Value(int index) { return HashVal; } private: int HashVal; }; /* ** DistanceHasherClass, used by the code which builds the ** table of unique distance values. */ class DistanceHasherClass : public HashCalculatorClass { virtual bool Items_Match(const float & a, const float & b) { // looking for an exact match for distances return (a == b); } virtual void Compute_Hash(const float & item) { HashVal = (int)(item)&1023; } virtual int Num_Hash_Bits(void) { return 12; // 12bit hash value. } virtual int Num_Hash_Values(void) { return 1; // only one hash value for normals, require *exact* match } virtual int Get_Hash_Value(int index) { return HashVal; } private: int HashVal; }; /* ** BPT Notes ** ** - Probably need to build the initial tree in a big straightforward recursive datastructure ** with no indirection or anything. Then, when the whole thing is being built correctly, ** process the tree down so that everything that can be shared (e.g. plane equations) are ** shared. ** ** - Hide most of the implementation details even from the bpt header file by using a ** BptImpClass. This class will contain the final "optimized" bpt information. ** ** - Maybe need a BptBuilder class that builds the initial "straightforward" tree structure ** and then compresses it into a BptImp. ** ** - Extend PlaneClass to know about special case planes such as x-y, y-z, x-z. Optimize ** the culling and clipping functions to take advantage of them. ** ** - Separate the normal from the distance in the plane equation? Many many planes will ** probably share the same normal but different distances. This probably should be a ** very late optimization. */ /* ** BptVertexClass ** This is the vertex representation used in building the BPT */ class BptVertexClass { public: BptVertexClass(void); BptVertexClass(const BptVertexClass & that); BptVertexClass & operator = (const BptVertexClass & that); int Which_Side(const PlaneClass & plane) const; Vector3 Position; Vector3 Normal; Vector3 Color; Vector3 TexCoord; static BptVertexClass Lerp(const BptVertexClass & v0,const BptVertexClass & v1,double lerp); static BptVertexClass Intersect_Plane(const BptVertexClass & v0,const BptVertexClass & v1,const PlaneClass & plane); }; /* ** BptPolyClass ** This is the polygon representation used in building the BPT */ class BptPolyClass { public: enum { BPT_POLY_MAX_VERTS = 16 }; BptPolyClass(void); BptPolyClass(const BptPolyClass & that); BptPolyClass(const BptVertexClass * points, int num); BptPolyClass & operator = (const BptPolyClass & that); const BptVertexClass & operator[] (int i) const { return Verts[i]; } BptVertexClass & operator[] (int i) { return Verts[i]; } void Compute_Plane(void); int Which_Side(const PlaneClass & plane) const; void Split(const PlaneClass & plane,BptPolyClass & front,BptPolyClass & back) const; bool Is_Degenerate(void); bool Salvage_Degenerate(void); int MaterialId; int NumVerts; BptVertexClass Verts[BPT_POLY_MAX_VERTS]; PlaneClass Plane; }; /* ** BptNodeClass ** This is the node representation used in building the BPT */ class BptNodeClass { public: BptNodeClass(void) : NumPolys(0),Polys(NULL),Front(NULL),Back(NULL),ArrayIndex(-1) { } ~BptNodeClass(void) { if (Front) delete Front; if (Back) delete Back; if (Polys) delete[] Polys; } void Build(int numpolys,BptPolyClass * polys); int Num_Polys(void) const; int Num_Tris(void) const; int Num_Nodes(void) const; int Max_Depth(void) const; void Submit_Polys(MeshBuilderClass & builder) const; void Compute_Bounding_Box(void); int Assign_Array_Indices(int index); void Submit_Nodes(BptImpBuilderClass & impbuilder) const; private: PlaneClass Plane; // splitting plane uint32 NumPolys; // num polys on plane BptPolyClass * Polys; // array of polys BptNodeClass * Front; // pointer to front tree BptNodeClass * Back; // pointer to back tree int ArrayIndex; // used in building the tree into array form. Vector3 Min; // bounding box for this tree. Vector3 Max; /* ** SplitChoiceStruct. Whenever a plane is tested to see if it is a suitable splitting ** plane, we'll cache all of the values it computes so we do not duplicate the effort elsewhere. */ struct SplitChoiceStruct { SplitChoiceStruct(void) : Score(0.0f),FrontCount(0),BackCount(0),OnCount(0),SplitCount(0),Plane(0,0,0,0) {} float Score; // 0.0 is worst, 1.0 is "perfect" int FrontCount; // number of polys in front of the plane int BackCount; // number of polys behind the plane int OnCount; // number of polys on the plane int SplitCount; // number of polys split by the plane PlaneClass Plane; // plane equation }; /* ** SplitArrayStruct. This struct simply stores the result of a split operation. The ** arrays for the front, back and "on" polygons will be allocated and filled in. */ struct SplitArraysStruct { SplitArraysStruct(void) : FrontCount(0),BackCount(0),OnCount(0),FrontPolys(NULL),BackPolys(NULL),OnPolys(NULL) {} int FrontCount; int BackCount; int OnCount; BptPolyClass * FrontPolys; BptPolyClass * BackPolys; BptPolyClass * OnPolys; }; SplitChoiceStruct Compute_Plane_Score(int numpolys,BptPolyClass * polys,const PlaneClass & plane); SplitChoiceStruct Select_Splitting_Plane(int numpolys,BptPolyClass * polys); void Split_Polys(const int num_polys, const BptPolyClass * polys, const SplitChoiceStruct & sc, SplitArraysStruct * arrays); void Max_Depth_Internal(int cur_depth, int & max_depth) const; friend class BptImpBuilderClass; }; /* ** BptBuilderClass ** This class builds the initial straightforward binary tree representation of ** the mesh given to it. It then "optimizes" the tree into a BptImpClass to ** save ram. */ class BptBuilderClass { public: BptBuilderClass(void); ~BptBuilderClass(void); BptImpClass * Build(MeshClass * mesh); BptImpClass * Build(int numpolys,BptPolyClass * polyarray,MaterialInfoClass * matinfo); private: void unwind_mesh(MeshClass * mesh,int & set_numpolys,BptPolyClass * & set_polyarray); BptImpClass * build_imp(void); BptNodeClass * Root; MaterialInfoClass * MatInfo; int InputPolyCount; int OutputPolyCount; int InputTriCount; int OutputTriCount; int NodeCount; int MaxDepth; }; /* ** BptImpNodeClass ** This is the node representation in the final BPT implementation. ** It allows for sharing of plane equations, indirection to the polygons, etc */ class BptImpNodeClass { public: float MinX,MinY,MinZ; float MaxX,MaxY,MaxZ; BptImpNodeClass * Front; BptImpNodeClass * Back; uint16 FirstPoly; uint16 PolyCount; uint16 NormalIndex; uint16 DistanceIndex; bool Is_Visible(const CameraClass & camera); bool Cast_AABox_To_Polys(PhysAABoxCollisionTestClass & coltest); }; /* ** BptImpClass ** This class contains the actual run-time binary partition tree information ** and the code for building a bpt out of an arbitrary list of polygons. */ class BptImpClass { public: ~BptImpClass(void); void Free(void); void Build_Apt(const CameraClass & camera); bool Cast_Ray(PhysRayCollisionTestClass & raytest) const; bool Cast_AABox(PhysAABoxCollisionTestClass & boxtest) const; bool Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest) const; private: // These functions are used by the BptImpBuilder to construct // the BptImp... BptImpClass(void); void Set_Mesh(MeshClass * mesh); void Set_Node_Array(int numnodes, BptImpNodeClass * nodes); void Set_Normal_Array(const UniqueArrayClass & normals); void Set_Distance_Array(const UniqueArrayClass & distances); void Set_Poly_Index_Array(int numremaps,int * polyremaps); void Begin_Apt(void); void Add_Polys_To_Apt(BptImpNodeClass * node); void End_Apt(void); void Build_Apt_Recursive(BptImpNodeClass * node,const CameraClass & cam); bool Cast_Ray_Recursive(BptImpNodeClass * node,PhysRayCollisionTestClass & boxtest) const; bool Cast_AABox_Recursive(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const; bool Cast_OBBox_Recursive(BptImpNodeClass * node,PhysOBBoxCollisionTestClass & boxtest) const; bool Cast_Ray_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const { assert(0); return false; } bool Cast_AABox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const; bool Cast_OBBox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const { assert(0); return false; } void Verify_Node_Bounding_Volume(BptImpNodeClass * node,const Vector3 & min,const Vector3 & max) const; // Polygon Mesh in w3d-friendly form MeshClass * Mesh; // Binary partition nodes int NodeCount; BptImpNodeClass * Nodes; // Node Polygon remap arrays int PolyIndexCount; int * PolyIndices; // Plane equation normals, these are stored separate from the distances // because many many of the planes will share the same normal but not dist. int NormalCount; Vector3 * Normals; // Plane equation distances, again, stored separately from the normals int DistanceCount; float * Distances; // Active Poly Table support. These variables are used internally by // the culling code. The ActivePolyTable pointer is set to point into // the surrender mesh's apt. int ActivePolyCount; SRDWORD * ActivePolyTable; friend class BptClass; friend class BptImpBuilderClass; }; /* ** BptImpBuilderClass ** This guy takes a completed bpt tree (given as a pointer to the root node) ** and churns through the thing, generating a BptImpClass. */ class BptImpBuilderClass { public: BptImpBuilderClass(void); ~BptImpBuilderClass(void); void Free(void); BptImpClass * Build_Bpt_Imp(BptNodeClass * root,MaterialInfoClass * matinfo); void Add_Bpt_Node(const BptNodeClass * node); private: void Remap_Triangle_Indices(void); MeshBuilderClass MeshBuilder; // accumulate the polygons NormalHasherClass NormalHasher; // computes hash values for the normals DistanceHasherClass DistanceHasher; // computes hash values for the distances UniqueArrayClass * UniqueNormals; // accumulates the unique normals UniqueArrayClass * UniqueDistances; // accumulates the unique distances int NodeCount; // number of nodes in the tree BptImpNodeClass * Nodes; // array of all of the nodes int TriCount; // number of triangles in the tree int CurTriIndex; // current remap index int * TriIndexArray; // array of triangle remap indices }; /*********************************************************************************************** * BptClass::BptClass -- constructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ BptClass::BptClass(void) : BptImp(NULL) { } /*********************************************************************************************** * BptClass::~BptClass -- destructor * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ BptClass::~BptClass(void) { Free(); } /*********************************************************************************************** * BptClass::Free -- releases all assets in use * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ void BptClass::Free(void) { if (BptImp) { delete BptImp; BptImp = NULL; } } /*********************************************************************************************** * BptClass::Build -- build a bpt from the given mesh (generates a new mesh) * * * * This function will process the given mesh into a binary partition tree. It has to generate * * a new mesh due to the fact that some polygons may be split in the partioning process. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ void BptClass::Build(MeshClass * mesh) { assert(mesh); /* ** First, release everything that we have! */ Free(); /* ** Create a new BptImp object using the given mesh */ BptBuilderClass builder; BptImp = builder.Build(mesh); } /*********************************************************************************************** * BptClass::Load -- Initialize this Bpt object from contents of a W3D file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ int BptClass::Load(ChunkLoadClass & cload) { assert(0); return 0; } /*********************************************************************************************** * BptClass::Save -- Save this Bpt object into a W3D file * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ int BptClass::Save(ChunkSaveClass & csave) { assert(0); return 0; } /*********************************************************************************************** * BptClass::Render -- submit this object for rendering * * * * This function sets the active polygon table for the mesh based on the camera frustum. The * * bpt is used to cull out portions of the level which are not inside the frustum. Then the * * mesh is added to the given surrender scene. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * * 5/29/98 GTH : Created. * *=============================================================================================*/ void BptClass::Render( srScene * scene, const CameraClass &camera ) { /* ** Do our APT stuff */ BptImp->Build_Apt(camera); /* ** Then let Mesh render itself */ BptImp->Mesh->Render(scene,camera); } void BptClass::Update_Cached_Bounding_Volumes(void) const { CachedBoundingSphere = BptImp->Mesh->Get_Bounding_Sphere(); CachedBoundingBox = BptImp->Mesh->Get_Bounding_Box(); Validate_Cached_Bounding_Volumes(); } #if 0 MeshClass * BptClass::Get_Mesh(void) { if (BptImp) { if (BptImp->Mesh) BptImp->Mesh->Add_Ref(); return BptImp->Mesh; } else { return NULL; } } #endif /*********************************************************************************************** * BptClass::Cast_Ray -- Intersect a ray with this Bpt Object * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ bool BptClass::Cast_Ray(PhysRayCollisionTestClass & raytest) const { assert(BptImp); return BptImp->Cast_Ray(raytest); } /*********************************************************************************************** * BptClass::Cast_AABox -- Intersect a swept axis-aligned box. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ bool BptClass::Cast_AABox(PhysAABoxCollisionTestClass & aaboxtest) const { assert(BptImp); return BptImp->Cast_AABox(aaboxtest); } /*********************************************************************************************** * BptClass::Cast_OBBox -- Intersect a swept oriented box with this bpt object * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 5/6/98 GTH : Created. * *=============================================================================================*/ bool BptClass::Cast_OBBox(PhysOBBoxCollisionTestClass & obboxtest) const { assert(BptImp); return BptImp->Cast_OBBox(obboxtest); } /************************************************************** BptVertexClass Implementation **************************************************************/ BptVertexClass::BptVertexClass(void) : Position(0,0,0), Normal(0,0,0), Color(0,0,0), TexCoord(0,0,0) { } BptVertexClass::BptVertexClass(const BptVertexClass & that) { Position = that.Position; Normal = that.Normal; Color = that.Color; TexCoord = that.TexCoord; } BptVertexClass & BptVertexClass::operator = (const BptVertexClass & that) { if (this != &that) { Position = that.Position; Normal = that.Normal; Color = that.Color; TexCoord = that.TexCoord; } return *this; } BptVertexClass BptVertexClass::Lerp(const BptVertexClass & v0,const BptVertexClass & v1,double lerp) { assert(lerp >= -BPT_EPSILON); assert(lerp <= 1.0 + BPT_EPSILON); // interpolate position BptVertexClass res; res.Position = ::Lerp(v0.Position,v1.Position,lerp); // interpolate normal, renormalize res.Normal = ::Lerp(v0.Normal,v1.Normal,lerp); res.Normal.Normalize(); // interpolate color res.Color = ::Lerp(v0.Color,v1.Color,lerp); // interpolate texture coordinates res.TexCoord = ::Lerp(v0.TexCoord,v1.TexCoord,lerp); return res; } int BptVertexClass::Which_Side(const PlaneClass & plane) const { float d = Vector3::Dot_Product(plane.N,Position); d -= plane.D; if (d > BPT_EPSILON) { return BPT_FRONT; } if (d < -BPT_EPSILON) { return BPT_BACK; } return BPT_ON; } BptVertexClass BptVertexClass::Intersect_Plane ( const BptVertexClass & p0, const BptVertexClass & p1, const PlaneClass & plane ) { double alpha = 0.0f; plane.Compute_Intersection(p0.Position,p1.Position,&alpha); return BptVertexClass::Lerp(p0,p1,alpha); } /************************************************************** BptPolyClass Implementation **************************************************************/ BptPolyClass::BptPolyClass(void) : NumVerts(0) { } BptPolyClass::BptPolyClass(const BptPolyClass & that) { NumVerts = that.NumVerts; for (int i=0;iRelease_Ref(); } BptImpClass * BptBuilderClass::Build(MeshClass * mesh) { /* ** Here we go, unwind the mesh into BptVerts and BptPolys */ int numpolys; BptPolyClass * polys; unwind_mesh(mesh,numpolys,polys); assert(numpolys > 0); /* ** Jump into the generalized build method */ MaterialInfoClass * matinfo = mesh->Get_Material_Info(); BptImpClass * result = Build(numpolys,polys,matinfo); matinfo->Release_Ref(); /* ** Build function deletes the poly array... */ polys = NULL; return result; } BptImpClass * BptBuilderClass::Build(int numpolys,BptPolyClass * polys,MaterialInfoClass * matinfo) { /* ** Create a copy of all of the materials */ MatInfo = (MaterialInfoClass *)matinfo->Clone(); /* ** Store the input counts */ InputPolyCount = numpolys; InputTriCount = 0; for (int i=0; iBuild(numpolys,polys); /* ** Compile some statistics about the tree */ OutputPolyCount = Root->Num_Polys(); OutputTriCount = Root->Num_Tris(); NodeCount = Root->Num_Nodes(); MaxDepth = Root->Max_Depth(); WWDEBUG_SAY(("Binary Tree Generated!\r\n")); WWDEBUG_SAY(("Node Count: %d\r\n",NodeCount)); WWDEBUG_SAY(("Max Depth: %d\r\n",MaxDepth)); WWDEBUG_SAY(("Input Poly Count: %d\r\n",InputPolyCount)); WWDEBUG_SAY(("Output Poly Count: %d\r\n",OutputPolyCount)); WWDEBUG_SAY(("Input Tri Count: %d\r\n",InputTriCount)); WWDEBUG_SAY(("Output Tri Count: %d\r\n",OutputTriCount)); WWDEBUG_SAY(("Out/In Poly Ratio: %f\r\n",(float)OutputPolyCount / (float)InputPolyCount)); WWDEBUG_SAY(("Out/In Tri Ratio: %f\r\n",(float)OutputTriCount / (float)InputTriCount)); WWDEBUG_SAY(("\r\n")); /* ** Build the optimized BptImp and return it. */ return build_imp(); } void BptBuilderClass::unwind_mesh(MeshClass * mesh,int & set_numpolys,BptPolyClass * & set_polyarray) { srMeshModel * srmesh = mesh->Get_Surrender_Mesh(); // get pointers to the interesting stuff in the mesh srVector3 * loc = srmesh->getVertexLoc(); srVector3 * norm = srmesh->getVertexNormal(); srVector3 * uv = srmesh->getVertexUVW(srMaterial::DIFFUSE_COLOR,false); srVector3 * dcg = srmesh->getVertexDCG(false); srVector3i * polyvert = srmesh->getPolyVertex(); srMaterial ** polymtl = srmesh->getPolyMaterial(false); // allocate a bunch of BptPolygons set_numpolys = srmesh->getPolygonCount(); set_polyarray = new BptPolyClass[set_numpolys]; int src_idx = 0; int dst_idx = 0; for (src_idx = 0; src_idxGet_Mat_Info_Index(); } else { set_polyarray[dst_idx].MaterialId = 0; } } set_polyarray[dst_idx].Compute_Plane(); // if the triangle is not degenerate, accept it; increment the counter if (!set_polyarray[dst_idx].Is_Degenerate()) { dst_idx++; } } // set the number of polys to the actual number kept set_numpolys = dst_idx; assert(set_numpolys <= srmesh->getPolygonCount()); WWDEBUG_SAY(("Unwinding Input Mesh\r\n")); WWDEBUG_SAY(("Input poly count: %d\r\n",srmesh->getPolygonCount())); WWDEBUG_SAY(("Count after removing degenerates: %d\r\n",set_numpolys)); WWDEBUG_SAY(("\r\n")); } BptImpClass * BptBuilderClass::build_imp(void) { BptImpBuilderClass impbuilder; BptImpClass * imp = impbuilder.Build_Bpt_Imp(Root,MatInfo); return imp; } /************************************************************** BptNodeClass Implementation **************************************************************/ int BptNodeClass::Num_Polys(void) const { int count = NumPolys; if (Front) count += Front->Num_Polys(); if (Back) count += Back->Num_Polys(); return count; } int BptNodeClass::Num_Tris(void) const { int count = 0; for (unsigned int i=0; iNum_Tris(); if (Back) count += Back->Num_Tris(); return count; } int BptNodeClass::Num_Nodes(void) const { int count = 1; if (Front) count += Front->Num_Nodes(); if (Back) count += Back->Num_Nodes(); return count; } int BptNodeClass::Max_Depth(void) const { int max_depth = 0; Max_Depth_Internal(0,max_depth); return max_depth; } void BptNodeClass::Max_Depth_Internal(int cur_depth,int & max_depth) const { cur_depth++; if (cur_depth > max_depth) { max_depth = cur_depth; } if (Front) { Front->Max_Depth_Internal(cur_depth,max_depth); } if (Back) { Back->Max_Depth_Internal(cur_depth,max_depth); } } void BptNodeClass::Build(int numpolys,BptPolyClass * polys) { /* ** Select and assign the splitting plane */ SplitChoiceStruct sc = Select_Splitting_Plane(numpolys,polys); Plane = sc.Plane; /* ** Split the polygons */ SplitArraysStruct arrays; Split_Polys(numpolys,polys,sc,&arrays); /* ** Free the memory in use by polys! */ delete[] polys; /* ** Store the "on" polys in this node */ NumPolys = arrays.OnCount; Polys = arrays.OnPolys; arrays.OnPolys = NULL; /* ** Build a front tree if necessary. Remember that the Build function ** deletes the poly array. */ if (arrays.FrontCount) { assert(arrays.FrontPolys != NULL); Front = new BptNodeClass; Front->Build(arrays.FrontCount,arrays.FrontPolys); arrays.FrontPolys = NULL; } /* ** Build a back tree if necessary. Remember that the build function ** deletes the poly array. */ if (arrays.BackCount) { assert(arrays.BackPolys != NULL); Back = new BptNodeClass; Back->Build(arrays.BackCount,arrays.BackPolys); arrays.BackPolys = NULL; } } BptNodeClass::SplitChoiceStruct BptNodeClass::Compute_Plane_Score(int numpolys,BptPolyClass * polys,const PlaneClass & plane) { /* ** Suitability of a plane as a partition plane is based on the following factors: ** - How many polygons will be split ** - How many polygons end up on each side of the tree ** - How many polygons end up on the plane (do NOT want many on the plane) ** - How evenly the volume of space is divided. ** The following weights will determine how much influence each factor ** has on the final score. */ const float POLY_SPLIT_WEIGHT = 5.0f; const float POLY_COUNT_BALANCE_WEIGHT = 1.0f; const float POLYS_ON_PLANE_WEIGHT = 1.0f; const float TOTAL_WEIGHT = POLY_SPLIT_WEIGHT + POLY_COUNT_BALANCE_WEIGHT + POLYS_ON_PLANE_WEIGHT; const int MAX_POLYS_ON_PLANE = 10; const int MAX_SPLITS = 10; /* ** Count up the cases */ SplitChoiceStruct sc; sc.Plane = plane; for (int i=0; i MAX_POLYS_ON_PLANE) { sc.Score *= 0.4f; } return sc; } BptNodeClass::SplitChoiceStruct BptNodeClass::Select_Splitting_Plane(int numpolys,BptPolyClass * polys) { doitagain: const int NUM_VERTEX_TRYS = 30; const int NUM_POLY_TRYS = 30; SplitChoiceStruct best_plane_stats; SplitChoiceStruct considered_plane_stats; PlaneClass plane; /* ** Try putting axis-aligned planes through some random vertices */ for (int vertex_trys = 0; vertex_trys < min(NUM_VERTEX_TRYS,3*numpolys); vertex_trys++) { int poly_index = rand() % numpolys; int vert_index = rand() % polys[poly_index].NumVerts;; Vector3 pos = polys[poly_index].Verts[vert_index].Position; /* ** Try the x-axis */ plane.Set(Vector3(1,0,0),pos); considered_plane_stats = Compute_Plane_Score(numpolys,polys,plane); if (considered_plane_stats.Score > best_plane_stats.Score) { best_plane_stats = considered_plane_stats; } /* ** Try the y-axis */ plane.Set(Vector3(0,1,0),pos); considered_plane_stats = Compute_Plane_Score(numpolys,polys,plane); if (considered_plane_stats.Score > best_plane_stats.Score) { best_plane_stats = considered_plane_stats; } } /* ** Now try "autopartition" planes which come from the input polygons */ for (int poly_trys=0; poly_trys best_plane_stats.Score) { best_plane_stats = considered_plane_stats; } } if (best_plane_stats.Score <= 0.0f) { _asm int 0x03; goto doitagain; } //assert(best_plane_stats.Score > 0.0f); return best_plane_stats; } void BptNodeClass::Split_Polys ( const int num_polys, const BptPolyClass * polys, const SplitChoiceStruct & sc, SplitArraysStruct * arrays ) { // Note that this routine allocates three arrays of polygons, an array of polys in // front of the plane, an array of polys behind the plane, and an array of polys on the plane. // The caller is then responsible for keeping track of the memory this routine allocates. if (sc.FrontCount + sc.SplitCount > 0) { arrays->FrontPolys = new BptPolyClass[sc.FrontCount + sc.SplitCount]; } if (sc.BackCount + sc.SplitCount > 0) { arrays->BackPolys = new BptPolyClass[sc.BackCount + sc.SplitCount]; } if (sc.OnCount > 0) { arrays->OnPolys = new BptPolyClass[sc.OnCount]; } arrays->FrontCount = 0; arrays->BackCount = 0; arrays->OnCount = 0; for (int i=0; iFrontPolys[arrays->FrontCount++] = polys[i]; break; case BPT_BACK: arrays->BackPolys[arrays->BackCount++] = polys[i]; break; case BPT_ON: arrays->OnPolys[arrays->OnCount++] = polys[i]; break; case BPT_BOTH: polys[i].Split(sc.Plane,arrays->FrontPolys[arrays->FrontCount],arrays->BackPolys[arrays->BackCount]); if (!arrays->FrontPolys[arrays->FrontCount].Is_Degenerate()) { arrays->FrontCount++; } if (!arrays->BackPolys[arrays->BackCount].Is_Degenerate()) { arrays->BackCount++; } break; } } // when we are all done, the counts should match. assert(arrays->FrontCount <= sc.FrontCount + sc.SplitCount); assert(arrays->BackCount <= sc.BackCount + sc.SplitCount); assert(arrays->OnCount == sc.OnCount); /* ** Check if we threw away all of the polys ** (this can only happen if all of them were degenerate...) */ if ((arrays->OnCount == 0) && (arrays->OnPolys != NULL)) { delete[] arrays->OnPolys; arrays->OnPolys = NULL; } if ((arrays->BackCount == 0) && (arrays->BackPolys != NULL)) { delete[] arrays->BackPolys; arrays->BackPolys = NULL; } if ((arrays->FrontCount == 0) && (arrays->FrontPolys != NULL)) { delete[] arrays->FrontPolys; arrays->FrontPolys = NULL; } } void BptNodeClass::Submit_Polys(MeshBuilderClass & builder) const { /* ** Submit the back tree */ if (Back) { Back->Submit_Polys(builder); } /* ** Triangulate each poly and submit it to the mesh builder */ MeshBuilderClass::FaceClass face; for (unsigned int fi=0; fiNumVerts; ti++) { face.MaterialIdx = poly->MaterialId; face.SmGroup = 1; //TODO: get the correct smooth group??? face.Index = 0; //TODO: do we have an actual index? face.Attributes = 0; face.Verts[0].Position = poly->Verts[0].Position; face.Verts[0].TexCoord = poly->Verts[0].TexCoord; face.Verts[0].Color = poly->Verts[0].Color; face.Verts[1].Position = poly->Verts[ti-1].Position; face.Verts[1].TexCoord = poly->Verts[ti-1].TexCoord; face.Verts[1].Color = poly->Verts[ti-1].Color; face.Verts[2].Position = poly->Verts[ti].Position; face.Verts[2].TexCoord = poly->Verts[ti].TexCoord; face.Verts[2].Color = poly->Verts[ti].Color; builder.Add_Face(face); } } /* ** Submit the front tree */ if (Front) { Front->Submit_Polys(builder); } } void BptNodeClass::Compute_Bounding_Box(void) { /* ** make the children update their boxes first */ if (Front) { Front->Compute_Bounding_Box(); } if (Back) { Back->Compute_Bounding_Box(); } Min.Set(10000.0f,10000.0f,10000.0f); Max.Set(-10000.0f,-10000.0f,-10000.0f); /* ** Bound the polys in this node */ for (unsigned int poly_index = 0; poly_index < NumPolys; poly_index++) { for (int vert_index = 0; vert_index < Polys[poly_index].NumVerts; vert_index++) { Vector3 & point = Polys[poly_index].Verts[vert_index].Position; if (point.X - BPT_EPSILON < Min.X) Min.X = point.X - BPT_EPSILON; if (point.X + BPT_EPSILON > Max.X) Max.X = point.X + BPT_EPSILON; if (point.Y - BPT_EPSILON < Min.Y) Min.Y = point.Y - BPT_EPSILON; if (point.Y + BPT_EPSILON > Max.Y) Max.Y = point.Y + BPT_EPSILON; if (point.Z - BPT_EPSILON < Min.Z) Min.Z = point.Z - BPT_EPSILON; if (point.Z + BPT_EPSILON > Max.Z) Max.Z = point.Z + BPT_EPSILON; } } /* ** bound the polys in the front child node */ if (Front) { if (Front->Min.X < Min.X) Min.X = Front->Min.X; if (Front->Max.X > Max.X) Max.X = Front->Max.X; if (Front->Min.Y < Min.Y) Min.Y = Front->Min.Y; if (Front->Max.Y > Max.Y) Max.Y = Front->Max.Y; if (Front->Min.Z < Min.Z) Min.Z = Front->Min.Z; if (Front->Max.Z > Max.Z) Max.Z = Front->Max.Z; } /* ** bound the polys in the back child node */ if (Back) { if (Back->Min.X < Min.X) Min.X = Back->Min.X; if (Back->Max.X > Max.X) Max.X = Back->Max.X; if (Back->Min.Y < Min.Y) Min.Y = Back->Min.Y; if (Back->Max.Y > Max.Y) Max.Y = Back->Max.Y; if (Back->Min.Z < Min.Z) Min.Z = Back->Min.Z; if (Back->Max.Z > Max.Z) Max.Z = Back->Max.Z; } assert(Min.X != 10000.0f); assert(Min.Y != 10000.0f); assert(Min.Z != 10000.0f); assert(Max.X != -10000.0f); assert(Max.Y != -10000.0f); assert(Max.Z != -10000.0f); } int BptNodeClass::Assign_Array_Indices(int index) { /* ** This function is used to assign a sequential index to ** each node in the tree. The BptImp stores its nodes in ** an array so this index is used to determine which slot ** in the array to put each node into. */ ArrayIndex = index; index++; if (Front) { index = Front->Assign_Array_Indices(index); } if (Back) { index = Back->Assign_Array_Indices(index); } return index; } void BptNodeClass::Submit_Nodes(BptImpBuilderClass & imp_builder) const { imp_builder.Add_Bpt_Node(this); if (Front) { Front->Submit_Nodes(imp_builder); } if (Back) { Back->Submit_Nodes(imp_builder); } } /************************************************************** BptImpClass Implementation **************************************************************/ inline bool BptImpNodeClass::Is_Visible(const CameraClass & camera) { return !camera.Cull_Box(Vector3(MinX,MinY,MinZ),Vector3(MaxX,MaxY,MaxZ)); } /************************************************************** BptImpClass Implementation **************************************************************/ BptImpClass::BptImpClass(void) : Mesh(NULL), NodeCount(0), Nodes(NULL), PolyIndexCount(0), PolyIndices(NULL), NormalCount(0), Normals(NULL), DistanceCount(0), Distances(NULL), ActivePolyCount(0), ActivePolyTable(NULL) { } BptImpClass::~BptImpClass(void) { Free(); } void BptImpClass::Free(void) { if (Mesh!=NULL) { Mesh->Release_Ref(); Mesh = NULL; } NodeCount = 0; if (Nodes != NULL) { delete[] Nodes; Nodes = NULL; } PolyIndexCount = 0; if (PolyIndices != NULL) { delete[] PolyIndices; PolyIndices = NULL; } NormalCount = 0; if (Normals != NULL) { delete[] Normals; Normals = NULL; } DistanceCount = 0; if (Distances != NULL) { delete[] Distances; Distances = NULL; } } void BptImpClass::Set_Mesh(MeshClass * mesh) { if (Mesh) Mesh->Release_Ref(); Mesh = mesh; if (Mesh) Mesh->Add_Ref(); } void BptImpClass::Set_Node_Array(int numnodes, BptImpNodeClass * nodes) { if (Nodes) delete[] Nodes; Nodes = nodes; NodeCount = numnodes; } void BptImpClass::Set_Normal_Array(const UniqueArrayClass & normals) { if (normals.Count() != NormalCount) { if (Normals) delete[] Normals; Normals = new Vector3[normals.Count()]; } NormalCount = normals.Count(); for (int i=0; i & distances) { if (distances.Count() != DistanceCount) { if (Distances) delete[] Distances; Distances = new float[distances.Count()]; } DistanceCount = distances.Count(); for (int i=0; iGet_Surrender_Mesh()->getActivePolygonTable(); } void BptImpClass::Add_Polys_To_Apt(BptImpNodeClass * node) { assert(node != NULL); #if 0 memcpy(&(ActivePolyTable[ActivePolyCount]),&(PolyIndices[node->FirstPoly]),node->PolyCount); ActivePolyCount += node->PolyCount; #else for (int poly_index=0; poly_indexPolyCount; poly_index++) { ActivePolyTable[ActivePolyCount++] = PolyIndices[node->FirstPoly + poly_index]; } #endif } void BptImpClass::End_Apt(void) { Mesh->Get_Surrender_Mesh()->setActivePolygonCount(ActivePolyCount); // WWDEBUG_SAY(("Active Polys: %d\r\n",ActivePolyCount)); } void BptImpClass::Build_Apt_Recursive(BptImpNodeClass * node,const CameraClass & cam) { /* ** Check the camera's frustum against this node's ** bounding volume */ if (!node->Is_Visible(cam)) { Verify_Node_Bounding_Volume(node,Vector3(node->MinX,node->MinY,node->MinZ),Vector3(node->MaxX,node->MaxY,node->MaxZ)); return; } /* ** Ok part of this tree may be visible, compute ** which side of the splitting plane the camera is on */ float dist = Vector3::Dot_Product(Normals[node->NormalIndex],cam.Get_Position()); if (dist > Distances[node->DistanceIndex]) { /* ** FRONT: ** We are in front of the plane so proceed in the following order: ** Visit the 'Back' node ** Add our polys to the APT ** Visit the 'Front' node */ if (node->Back) Build_Apt_Recursive(node->Back,cam); Add_Polys_To_Apt(node); if (node->Front) Build_Apt_Recursive(node->Front,cam); } else { /* ** BACK: ** We are in back of the plane so proceed in the following order: ** Visit the 'Front' node ** Add our polys to the APT ** Visit the 'Back' node */ if (node->Front) Build_Apt_Recursive(node->Front,cam); Add_Polys_To_Apt(node); if (node->Back) Build_Apt_Recursive(node->Back,cam); } } bool BptImpClass::Cast_Ray(PhysRayCollisionTestClass & raytest) const { return Cast_Ray_Recursive(&(Nodes[0]),raytest); } bool BptImpClass::Cast_AABox(PhysAABoxCollisionTestClass & boxtest) const { /* ** Test the sweeping AABox against the BPT recursively! */ WWDEBUG_PROFILE_START("BPT::Cast_AABox"); bool res = Cast_AABox_Recursive(&(Nodes[0]),boxtest); WWDEBUG_PROFILE_STOP("BPT::Cast_AABox"); return res; } bool BptImpClass::Cast_OBBox(PhysOBBoxCollisionTestClass & boxtest) const { return Cast_OBBox_Recursive(&(Nodes[0]),boxtest); } bool BptImpClass::Cast_Ray_Recursive ( BptImpNodeClass * node, PhysRayCollisionTestClass & raytest ) const { return false; } bool BptImpClass::Cast_AABox_Recursive ( BptImpNodeClass * node, PhysAABoxCollisionTestClass & boxtest ) const { /* ** First, check the bounding box of the move against the bounding box ** of this tree, if they don't intersect, bail out */ if ((boxtest.SweepMin.X > node->MaxX) || (boxtest.SweepMax.X < node->MinX)) { return false; } if ((boxtest.SweepMin.Y > node->MaxY) || (boxtest.SweepMax.Y < node->MinY)) { return false; } if ((boxtest.SweepMin.Z > node->MaxZ) || (boxtest.SweepMax.Z < node->MinZ)) { return false; } if (node->Front) { Cast_AABox_Recursive(node->Front,boxtest); } Cast_AABox_To_Polys(node,boxtest); if (node->Back) { Cast_AABox_Recursive(node->Back,boxtest); } return false; } bool BptImpClass::Cast_OBBox_Recursive ( BptImpNodeClass * node, PhysOBBoxCollisionTestClass & boxtest ) const { return false; } bool BptImpClass::Cast_AABox_To_Polys(BptImpNodeClass * node,PhysAABoxCollisionTestClass & boxtest) const { /* ** Simply loop through the polys in this node, checking each for collision */ TriClass tri; srMeshModel * srmesh = Mesh->Get_Surrender_Mesh(); srVector3 * loc = srmesh->getVertexLoc(); srVector4 * norms = srmesh->getPolyEq(); srVector3i * polyverts = srmesh->getPolyVertex(); int polyhit = -1; for (int poly_counter=0; poly_counterPolyCount; poly_counter++) { int poly_index = PolyIndices[node->FirstPoly + poly_counter]; tri.V[0] = As_Vector3(&(loc[ polyverts[poly_index][0] ])); tri.V[1] = As_Vector3(&(loc[ polyverts[poly_index][1] ])); tri.V[2] = As_Vector3(&(loc[ polyverts[poly_index][2] ])); tri.N = (Vector3 *)&(norms[poly_index]); if ( boxtest.Box.Cast_To_Tri(boxtest.Move,tri,boxtest.Result) ) {; polyhit = poly_index; } if (boxtest.Result->StartBad) { return true; } } if (polyhit != -1) { return true; } return false; } void BptImpClass::Verify_Node_Bounding_Volume(BptImpNodeClass * node,const Vector3 & min,const Vector3 & max) const { srMeshModel * srmesh = Mesh->Get_Surrender_Mesh(); srVector3 * loc = srmesh->getVertexLoc(); srVector3i * polyverts = srmesh->getPolyVertex(); Vector3 * v; for (int remap_index = node->FirstPoly; remap_index < node->FirstPoly + node->PolyCount; remap_index++) { int poly_index = PolyIndices[remap_index]; v = As_Vector3(&loc[ polyverts[poly_index][0] ]); assert(v->X >= min.X); assert(v->Y >= min.Y); assert(v->Z >= min.Z); assert(v->X <= max.X); assert(v->Y <= max.Y); assert(v->Z <= max.Z); v = As_Vector3(&loc[ polyverts[poly_index][1] ]); assert(v->X >= min.X); assert(v->Y >= min.Y); assert(v->Z >= min.Z); assert(v->X <= max.X); assert(v->Y <= max.Y); assert(v->Z <= max.Z); v = As_Vector3(&loc[ polyverts[poly_index][2] ]); assert(v->X >= min.X); assert(v->Y >= min.Y); assert(v->Z >= min.Z); assert(v->X <= max.X); assert(v->Y <= max.Y); assert(v->Z <= max.Z); } if (node->Back) { Verify_Node_Bounding_Volume(node->Back,min,max); } if (node->Front) { Verify_Node_Bounding_Volume(node->Front,min,max); } } /************************************************************** BptImpBuilderClass Implementation **************************************************************/ BptImpBuilderClass::BptImpBuilderClass(void) : MeshBuilder(true,10000,5000), // TODO: should move this to Build_Bpt_Imp so it is dynamically initialzed UniqueNormals(NULL), UniqueDistances(NULL), NodeCount(0), Nodes(NULL), TriCount(0), CurTriIndex(0), TriIndexArray(NULL) { } BptImpBuilderClass::~BptImpBuilderClass(void) { Free(); } void BptImpBuilderClass::Free(void) { if (UniqueNormals != NULL) { delete UniqueNormals; UniqueNormals = NULL; } if (UniqueDistances != NULL) { delete UniqueDistances; UniqueDistances = NULL; } NodeCount = 0; if (Nodes != NULL) { delete[] Nodes; Nodes = NULL; } TriCount = 0; CurTriIndex = 0; if (TriIndexArray != NULL) { delete[] TriIndexArray; TriIndexArray = NULL; } } BptImpClass * BptImpBuilderClass::Build_Bpt_Imp(BptNodeClass * root,MaterialInfoClass * matinfo) { Free(); WWDEBUG_SAY(("Building the Run-Time Bpt\r\n")); TriCount = root->Num_Tris(); /* ** Allocate the UniqueNormals and UniqueDistances objects */ int size = TriCount; int growth = TriCount / 3; if (growth < 5) growth = 5; UniqueNormals = new UniqueArrayClass(size,growth,&NormalHasher); UniqueDistances = new UniqueArrayClass(size,growth,&DistanceHasher); /* ** Set up the mesh builder */ MeshBuilder.Reset(true,TriCount,growth); MeshBuilder.Disable_All_Vertex_Channels(); MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_NORMAL); // MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_COLOR); MeshBuilder.Enable_Vertex_Channel(MeshBuilderClass::VERTEX_CHANNEL_TEXCOORD); /* ** Allocate the array of triangle indices, set ** CurTriIndex to point to the top. */ TriIndexArray = new int[TriCount]; CurTriIndex = 0; /* ** Allocate the array of nodes */ NodeCount = root->Num_Nodes(); Nodes = new BptImpNodeClass[NodeCount]; /* ** Compute the hierarchical bounding boxes */ WWDEBUG_SAY(("Computing Hierarchical Bounding Volumes.\r\n")); root->Compute_Bounding_Box(); WWDEBUG_SAY(("Hierarchical Bounding Volumes Done.\r\n")); /* ** Have the tree "unwind" itself into our arrays */ root->Assign_Array_Indices(0); root->Submit_Nodes(*this); /* ** Process the mesh */ MeshBuilder.Build_Mesh(true); /* ** Remap all of the polygon indices (account for the ** mesh builder re-ording polygons) */ Remap_Triangle_Indices(); /* ** Create the BptImpClass */ MeshClass * mesh = new MeshClass(); mesh->Init(MeshBuilder,matinfo,"BspMesh",""); BptImpClass * bptimp = new BptImpClass(); bptimp->Set_Mesh(mesh); bptimp->Set_Node_Array(NodeCount,Nodes); bptimp->Set_Poly_Index_Array(TriCount,TriIndexArray); bptimp->Set_Normal_Array(*UniqueNormals); bptimp->Set_Distance_Array(*UniqueDistances); // the "imp" assumed ownership of this stuff mesh->Release_Ref(); mesh = NULL; Nodes = NULL; TriIndexArray = NULL; Free(); WWDEBUG_SAY(("Run-Time Bpt Done\r\n")); WWDEBUG_SAY(("\r\n")); return bptimp; } void BptImpBuilderClass::Add_Bpt_Node(const BptNodeClass * node) { BptImpNodeClass newnode; /* ** initialize the front pointer */ if (node->Front) { assert(node->Front->ArrayIndex >= 0); assert(node->Front->ArrayIndex < NodeCount); newnode.Front = &(Nodes[node->Front->ArrayIndex]); } else { newnode.Front = NULL; //BptImpNodeClass::NULL_NODE_INDEX; } /* ** initialize the back pointer */ if (node->Back) { assert(node->Back->ArrayIndex >= 0); assert(node->Back->ArrayIndex < NodeCount); newnode.Back = &(Nodes[node->Back->ArrayIndex]); } else { newnode.Back = NULL; //BptImpNodeClass::NULL_NODE_INDEX; } /* ** Triangulate each poly and submit it to the mesh builder ** install the Triangle Remap Indices as we go */ newnode.FirstPoly = CurTriIndex; newnode.PolyCount = 0; MeshBuilderClass::FaceClass face; for (unsigned int fi=0; fiNumPolys; fi++) { BptPolyClass * poly = &(node->Polys[fi]); for (int ti = 2;ti < poly->NumVerts; ti++) { face.MaterialIdx = poly->MaterialId; face.SmGroup = 1; //TODO: get the correct smooth group??? face.Index = 0; //TODO: do we have an actual index? face.Attributes = 0; face.Verts[0].Position = poly->Verts[0].Position; face.Verts[0].TexCoord = poly->Verts[0].TexCoord; face.Verts[0].Color = poly->Verts[0].Color; face.Verts[1].Position = poly->Verts[ti-1].Position; face.Verts[1].TexCoord = poly->Verts[ti-1].TexCoord; face.Verts[1].Color = poly->Verts[ti-1].Color; face.Verts[2].Position = poly->Verts[ti].Position; face.Verts[2].TexCoord = poly->Verts[ti].TexCoord; face.Verts[2].Color = poly->Verts[ti].Color; TriIndexArray[CurTriIndex] = MeshBuilder.Add_Face(face); CurTriIndex++; newnode.PolyCount++; /* ** just verifying that all verts are inside the bounding box */ assert(face.Verts[0].Position.X >= node->Min.X); assert(face.Verts[0].Position.Y >= node->Min.Y); assert(face.Verts[0].Position.Z >= node->Min.Z); assert(face.Verts[1].Position.X >= node->Min.X); assert(face.Verts[1].Position.Y >= node->Min.Y); assert(face.Verts[1].Position.Z >= node->Min.Z); assert(face.Verts[2].Position.X >= node->Min.X); assert(face.Verts[2].Position.Y >= node->Min.Y); assert(face.Verts[2].Position.Z >= node->Min.Z); assert(face.Verts[0].Position.X <= node->Max.X); assert(face.Verts[0].Position.Y <= node->Max.Y); assert(face.Verts[0].Position.Z <= node->Max.Z); assert(face.Verts[1].Position.X <= node->Max.X); assert(face.Verts[1].Position.Y <= node->Max.Y); assert(face.Verts[1].Position.Z <= node->Max.Z); assert(face.Verts[2].Position.X <= node->Max.X); assert(face.Verts[2].Position.Y <= node->Max.Y); assert(face.Verts[2].Position.Z <= node->Max.Z); } } /* ** Bounding box */ newnode.MinX = node->Min.X; newnode.MinY = node->Min.Y; newnode.MinZ = node->Min.Z; newnode.MaxX = node->Max.X; newnode.MaxY = node->Max.Y; newnode.MaxZ = node->Max.Z; assert(newnode.MinX <= newnode.MaxX); assert(newnode.MinY <= newnode.MaxY); assert(newnode.MinZ <= newnode.MaxZ); /* ** Plane Equation */ assert(UniqueNormals); assert(UniqueDistances); newnode.NormalIndex = UniqueNormals->Add(node->Plane.N); newnode.DistanceIndex = UniqueDistances->Add(node->Plane.D); /* ** install the node! */ Nodes[node->ArrayIndex] = newnode; } void BptImpBuilderClass::Remap_Triangle_Indices(void) { int pi; int * index_remap = new int[MeshBuilder.Get_Face_Count()]; assert(MeshBuilder.Get_Face_Count() == TriCount); for (pi=0; pi < MeshBuilder.Get_Face_Count(); pi++) { const MeshBuilderClass::FaceClass & face = MeshBuilder.Get_Face(pi); assert(face.AddIndex >= 0); assert(face.AddIndex < TriCount); index_remap[ face.AddIndex ] = pi; } for (pi=0; pi < TriCount; pi++) { TriIndexArray[pi] = index_remap[TriIndexArray[pi]]; } delete[] index_remap; } #endif // PORT140 #endif // OBSOLETE!