Quake3Bsp.cpp
上传用户:cxh8989
上传日期:2021-01-22
资源大小:2544k
文件大小:43k
源码类别:

射击游戏

开发平台:

Visual C++

  1. #include "Main.h"
  2. #include "Camera.h"
  3. #include "Quake3Bsp.h"
  4. #include "Frustum.h"
  5. // This is our maximum height that the user can climb over
  6. const float kMaxStepHeight = 10.0f;
  7. // We use the camera in our TryToStep() function so we extern the global camera
  8. extern CCamera g_Camera;
  9. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  10. // This is our global frustum class, which is used to cull BSP leafs
  11. extern CFrustum g_Frustum;
  12. // This will store how many faces are drawn and are seen by the camera
  13. extern int g_VisibleFaces;
  14. // This tells us if we want to render the lightmaps
  15. extern bool g_bLightmaps;
  16. // This holds the gamma value that was stored in the config file
  17. extern float g_Gamma;
  18. // This tells us if we want to render the textures
  19. extern bool g_bTextures;
  20. //////////////////////////// CQUAKE3BSP \\\\\\\\\\\\\*
  21. /////
  22. ///// This is our object's constructor to initial all it's data members
  23. /////
  24. //////////////////////////// CQUAKE3BSP \\\\\\\\\\\\\*
  25. CQuake3BSP::CQuake3BSP()
  26. {
  27. // Here we simply initialize our member variables to 0
  28. m_numOfVerts = 0;
  29. m_numOfFaces = 0;
  30. m_numOfIndices = 0;
  31. m_numOfTextures = 0;
  32. m_numOfLightmaps = 0;
  33. m_numOfNodes = 0;
  34. m_numOfLeafs = 0;
  35. m_numOfLeafFaces = 0;
  36. m_numOfPlanes = 0;
  37. m_numOfBrushes = 0;
  38. m_numOfBrushSides = 0;
  39. m_numOfLeafBrushes = 0;
  40. m_traceRatio = 0;
  41. m_traceType = 0;
  42. m_traceRadius = 0;
  43. bool m_bCollided = false;
  44. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  45. // Initialize our variables to start off
  46. bool m_bGrounded = false;
  47. bool m_bTryStep = false;
  48. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  49. // We need to initialize our Min and Max and Extent variables
  50. m_vTraceMins = CVector3(0, 0, 0);
  51. m_vTraceMaxs = CVector3(0, 0, 0);
  52. m_vExtents   = CVector3(0, 0, 0);
  53. // This will store the normal of the plane we collided with
  54. m_vCollisionNormal = CVector3(0, 0, 0);
  55. // Initialize all the dynamic BSP data pointers to NULL
  56. m_pVerts  = NULL;
  57. m_pFaces  = NULL;
  58. m_pIndices  = NULL;
  59. m_pNodes  = NULL;
  60. m_pLeafs  = NULL;
  61. m_pPlanes  = NULL;
  62. m_pLeafFaces  = NULL;
  63. memset(&m_clusters, 0, sizeof(tBSPVisData));
  64. // Here we initialize our dynamic arrays of data for the brush information of the BSP
  65. m_pBrushes       = NULL;
  66. m_pBrushSides  = NULL;
  67. m_pTextures      = NULL;
  68. m_pLeafBrushes  = NULL;
  69. }
  70. //////////////////////////// CHANGE GAMMA \\\\\\\\\\\\\*
  71. /////
  72. ///// This manually changes the gamma of an image
  73. /////
  74. //////////////////////////// CHANGE GAMMA \\\\\\\\\\\\\*
  75. void CQuake3BSP::ChangeGamma(byte *pImage, int size, float factor)
  76. {
  77. // Go through every pixel in the lightmap
  78. for(int i = 0; i < size / 3; i++, pImage += 3) 
  79. {
  80. float scale = 1.0f, temp = 0.0f;
  81. float r = 0, g = 0, b = 0;
  82. // extract the current RGB values
  83. r = (float)pImage[0];
  84. g = (float)pImage[1];
  85. b = (float)pImage[2];
  86. // Multiply the factor by the RGB values, while keeping it to a 255 ratio
  87. r = r * factor / 255.0f;
  88. g = g * factor / 255.0f;
  89. b = b * factor / 255.0f;
  90. // Check if the the values went past the highest value
  91. if(r > 1.0f && (temp = (1.0f/r)) < scale) scale=temp;
  92. if(g > 1.0f && (temp = (1.0f/g)) < scale) scale=temp;
  93. if(b > 1.0f && (temp = (1.0f/b)) < scale) scale=temp;
  94. // Get the scale for this pixel and multiply it by our pixel values
  95. scale*=255.0f;
  96. r*=scale; g*=scale; b*=scale;
  97. // Assign the new gamma'nized RGB values to our image
  98. pImage[0] = (byte)r;
  99. pImage[1] = (byte)g;
  100. pImage[2] = (byte)b;
  101. }
  102. }
  103. ////////////////////////////// CREATE LIGHTMAP TEXTURE \\\\\\\\\\\\\\*
  104. /////
  105. ///// This creates a texture map from the light map image bits
  106. /////
  107. ////////////////////////////// CREATE LIGHTMAP TEXTURE \\\\\\\\\\\\\\*
  108. void CQuake3BSP::CreateLightmapTexture(UINT &texture, byte *pImageBits, int width, int height)
  109. {
  110. // Generate a texture with the associative texture ID stored in the array
  111. glGenTextures(1, &texture);
  112. // This sets the alignment requirements for the start of each pixel row in memory.
  113. glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
  114. // Bind the texture to the texture arrays index and init the texture
  115. glBindTexture(GL_TEXTURE_2D, texture);
  116. // Change the lightmap gamma values by our desired gamma
  117. ChangeGamma(pImageBits, width*height*3, g_Gamma);
  118. // Build Mipmaps (builds different versions of the picture for distances - looks better)
  119. gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, pImageBits);
  120. //Assign the mip map levels
  121. glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  122. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
  123. glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  124. }
  125. //////////////////////////// FIND TEXTURE EXTENSION \\\\\\\\\\\\\*
  126. /////
  127. ///// This attaches the image extension to the texture name, if found
  128. /////
  129. //////////////////////////// FIND TEXTURE EXTENSION \\\\\\\\\\\\\*
  130. void CQuake3BSP::FindTextureExtension(char *strFileName)
  131. {
  132. char strJPGPath[MAX_PATH] = {0};
  133. char strTGAPath[MAX_PATH]    = {0}; 
  134. FILE *fp = NULL;
  135. // Get the current path we are in
  136. GetCurrentDirectory(MAX_PATH, strJPGPath);
  137. // Add on a '' and the file name to the end of the current path.
  138. // We create 2 seperate strings to test each image extension.
  139. strcat(strJPGPath, "\");
  140. strcat(strJPGPath, strFileName);
  141. strcpy(strTGAPath, strJPGPath);
  142. // Add the extensions on to the file name and path
  143. strcat(strJPGPath, ".jpg");
  144. strcat(strTGAPath, ".tga");
  145. // Check if there is a jpeg file with the texture name
  146. if((fp = fopen(strJPGPath, "rb")) != NULL)
  147. {
  148. // If so, then let's add ".jpg" onto the file name and return
  149. strcat(strFileName, ".jpg");
  150. return;
  151. }
  152. // Check if there is a targa file with the texture name
  153. if((fp = fopen(strTGAPath, "rb")) != NULL)
  154. {
  155. // If so, then let's add a ".tga" onto the file name and return
  156. strcat(strFileName, ".tga");
  157. return;
  158. }
  159. }
  160. //////////////////////////// LOAD BSP \\\\\\\\\\\\\*
  161. /////
  162. ///// This loads in all of the .bsp data for the level
  163. /////
  164. //////////////////////////// LOAD BSP \\\\\\\\\\\\\*
  165. bool CQuake3BSP::LoadBSP(const char *strFileName)
  166. {
  167. FILE *fp = NULL;
  168. int i = 0;
  169. // Check if the .bsp file could be opened
  170. if((fp = fopen(strFileName, "rb")) == NULL)
  171. {
  172. // Display an error message and quit if the file can't be found.
  173. MessageBox(g_hWnd, "Could not find BSP file!", "Error", MB_OK);
  174. return false;
  175. }
  176. // Initialize the header and lump structures
  177. tBSPHeader header = {0};
  178. tBSPLump lumps[kMaxLumps] = {0};
  179. // Read in the header and lump data
  180. fread(&header, 1, sizeof(tBSPHeader), fp);
  181. fread(&lumps, kMaxLumps, sizeof(tBSPLump), fp);
  182. // Now we know all the information about our file.  We can
  183. // then allocate the needed memory for our member variables.
  184. // Allocate the vertex memory
  185. m_numOfVerts = lumps[kVertices].length / sizeof(tBSPVertex);
  186. m_pVerts     = new tBSPVertex [m_numOfVerts];
  187. // Allocate the face memory
  188. m_numOfFaces = lumps[kFaces].length / sizeof(tBSPFace);
  189. m_pFaces     = new tBSPFace [m_numOfFaces];
  190. // Allocate the index memory
  191. m_numOfIndices = lumps[kIndices].length / sizeof(int);
  192. m_pIndices     = new int [m_numOfIndices];
  193. // Allocate memory to read in the texture information.
  194. m_numOfTextures = lumps[kTextures].length / sizeof(tBSPTexture);
  195. m_pTextures = new tBSPTexture [m_numOfTextures];
  196. // Allocate memory to read in the lightmap data.  
  197. m_numOfLightmaps = lumps[kLightmaps].length / sizeof(tBSPLightmap);
  198. tBSPLightmap *pLightmaps = new tBSPLightmap [m_numOfLightmaps ];
  199. // Seek to the position in the file that stores the vertex information
  200. fseek(fp, lumps[kVertices].offset, SEEK_SET);
  201. // Go through all of the vertices that need to be read and swap axis's
  202. for(i = 0; i < m_numOfVerts; i++)
  203. {
  204. // Read in the current vertex
  205. fread(&m_pVerts[i], 1, sizeof(tBSPVertex), fp);
  206. // Swap the y and z values, and negate the new z so Y is up.
  207. float temp = m_pVerts[i].vPosition.y;
  208. m_pVerts[i].vPosition.y = m_pVerts[i].vPosition.z;
  209. m_pVerts[i].vPosition.z = -temp;
  210. }
  211. // Seek to the position in the file that stores the index information
  212. fseek(fp, lumps[kIndices].offset, SEEK_SET);
  213. // Read in all the index information
  214. fread(m_pIndices, m_numOfIndices, sizeof(int), fp);
  215. // Seek to the position in the file that stores the face information
  216. fseek(fp, lumps[kFaces].offset, SEEK_SET);
  217. // Read in all the face information
  218. fread(m_pFaces, m_numOfFaces, sizeof(tBSPFace), fp);
  219. // Seek to the position in the file that stores the texture information
  220. fseek(fp, lumps[kTextures].offset, SEEK_SET);
  221. // Read in all the texture information
  222. fread(m_pTextures, m_numOfTextures, sizeof(tBSPTexture), fp);
  223. // Go through all of the textures
  224. for(i = 0; i < m_numOfTextures; i++)
  225. {
  226. // Find the extension if any and append it to the file name
  227. FindTextureExtension(m_pTextures[i].strName);
  228. // Create a texture from the image
  229. CreateTexture(m_textures[i], m_pTextures[i].strName);
  230. }
  231. // Seek to the position in the file that stores the lightmap information
  232. fseek(fp, lumps[kLightmaps].offset, SEEK_SET);
  233. // Go through all of the lightmaps and read them in
  234. for(i = 0; i < m_numOfLightmaps ; i++)
  235. {
  236. // Read in the RGB data for each lightmap
  237. fread(&pLightmaps[i], 1, sizeof(tBSPLightmap), fp);
  238. // Create a texture map for each lightmap that is read in.  The lightmaps
  239. // are always 128 by 128.
  240. CreateLightmapTexture(m_lightmaps[i], 
  241.  (unsigned char *)pLightmaps[i].imageBits, 128, 128);
  242. }
  243. // Delete the image bits because we are already done with them
  244. delete [] pLightmaps;
  245. // Store the number of nodes and allocate the memory to hold them
  246. m_numOfNodes = lumps[kNodes].length / sizeof(tBSPNode);
  247. m_pNodes     = new tBSPNode [m_numOfNodes];
  248. // Seek to the position in the file that hold the nodes and store them in m_pNodes
  249. fseek(fp, lumps[kNodes].offset, SEEK_SET);
  250. fread(m_pNodes, m_numOfNodes, sizeof(tBSPNode), fp);
  251. // Store the number of leafs and allocate the memory to hold them
  252. m_numOfLeafs = lumps[kLeafs].length / sizeof(tBSPLeaf);
  253. m_pLeafs     = new tBSPLeaf [m_numOfLeafs];
  254. // Seek to the position in the file that holds the leafs and store them in m_pLeafs
  255. fseek(fp, lumps[kLeafs].offset, SEEK_SET);
  256. fread(m_pLeafs, m_numOfLeafs, sizeof(tBSPLeaf), fp);
  257. // Now we need to go through and convert all the leaf bounding boxes
  258. // to the normal OpenGL Y up axis.
  259. for(i = 0; i < m_numOfLeafs; i++)
  260. {
  261. // Swap the min y and z values, then negate the new Z
  262. int temp = m_pLeafs[i].min.y;
  263. m_pLeafs[i].min.y = m_pLeafs[i].min.z;
  264. m_pLeafs[i].min.z = -temp;
  265. // Swap the max y and z values, then negate the new Z
  266. temp = m_pLeafs[i].max.y;
  267. m_pLeafs[i].max.y = m_pLeafs[i].max.z;
  268. m_pLeafs[i].max.z = -temp;
  269. }
  270. // Store the number of leaf faces and allocate the memory for them
  271. m_numOfLeafFaces = lumps[kLeafFaces].length / sizeof(int);
  272. m_pLeafFaces     = new int [m_numOfLeafFaces];
  273. // Seek to the leaf faces lump, then read it's data
  274. fseek(fp, lumps[kLeafFaces].offset, SEEK_SET);
  275. fread(m_pLeafFaces, m_numOfLeafFaces, sizeof(int), fp);
  276. // Store the number of planes, then allocate memory to hold them
  277. m_numOfPlanes = lumps[kPlanes].length / sizeof(tBSPPlane);
  278. m_pPlanes     = new tBSPPlane [m_numOfPlanes];
  279. // Seek to the planes lump in the file, then read them into m_pPlanes
  280. fseek(fp, lumps[kPlanes].offset, SEEK_SET);
  281. fread(m_pPlanes, m_numOfPlanes, sizeof(tBSPPlane), fp);
  282. // Go through every plane and convert it's normal to the Y-axis being up
  283. for(i = 0; i < m_numOfPlanes; i++)
  284. {
  285. float temp = m_pPlanes[i].vNormal.y;
  286. m_pPlanes[i].vNormal.y = m_pPlanes[i].vNormal.z;
  287. m_pPlanes[i].vNormal.z = -temp;
  288. }
  289. // Seek to the position in the file that holds the visibility lump
  290. fseek(fp, lumps[kVisData].offset, SEEK_SET);
  291. // Check if there is any visibility information first
  292. if(lumps[kVisData].length) 
  293. {
  294. // Read in the number of vectors and each vector's size
  295. fread(&(m_clusters.numOfClusters),  1, sizeof(int), fp);
  296. fread(&(m_clusters.bytesPerCluster), 1, sizeof(int), fp);
  297. // Allocate the memory for the cluster bitsets
  298. int size = m_clusters.numOfClusters * m_clusters.bytesPerCluster;
  299. m_clusters.pBitsets = new byte [size];
  300. // Read in the all the visibility bitsets for each cluster
  301. fread(m_clusters.pBitsets, 1, sizeof(byte) * size, fp);
  302. }
  303. // Otherwise, we don't have any visibility data (prevents a crash)
  304. else
  305. m_clusters.pBitsets = NULL;
  306. // Like we do for other data, we read get the size of brushes and allocate memory
  307. m_numOfBrushes = lumps[kBrushes].length / sizeof(int);
  308. m_pBrushes     = new tBSPBrush [m_numOfBrushes];
  309. // Here we read in the brush information from the BSP file
  310. fseek(fp, lumps[kBrushes].offset, SEEK_SET);
  311. fread(m_pBrushes, m_numOfBrushes, sizeof(tBSPBrush), fp);
  312. // Get the size of brush sides, then allocate memory for it
  313. m_numOfBrushSides = lumps[kBrushSides].length / sizeof(int);
  314. m_pBrushSides     = new tBSPBrushSide [m_numOfBrushSides];
  315. // Read in the brush sides data
  316. fseek(fp, lumps[kBrushSides].offset, SEEK_SET);
  317. fread(m_pBrushSides, m_numOfBrushSides, sizeof(tBSPBrushSide), fp);
  318. // Read in the number of leaf brushes and allocate memory for it
  319. m_numOfLeafBrushes = lumps[kLeafBrushes].length / sizeof(int);
  320. m_pLeafBrushes     = new int [m_numOfLeafBrushes];
  321. // Finally, read in the leaf brushes for traversing the bsp tree with brushes
  322. fseek(fp, lumps[kLeafBrushes].offset, SEEK_SET);
  323. fread(m_pLeafBrushes, m_numOfLeafBrushes, sizeof(int), fp);
  324. // Close the file
  325. fclose(fp);
  326. // Here we allocate enough bits to store all the faces for our bitset
  327. m_FacesDrawn.Resize(m_numOfFaces);
  328. // Return a success
  329. return true;
  330. }
  331. //////////////////////////// FIND LEAF \\\\\\\\\\\\\*
  332. /////
  333. ///// This returns the leaf our camera is in
  334. /////
  335. //////////////////////////// FIND LEAF \\\\\\\\\\\\\*
  336. int CQuake3BSP::FindLeaf(const CVector3 &vPos)
  337. {
  338. int i = 0;
  339. float distance = 0.0f;
  340. // Continue looping until we find a negative index
  341. while(i >= 0)
  342. {
  343. // Get the current node, then find the slitter plane from that
  344. // node's plane index.  Notice that we use a constant reference
  345. // to store the plane and node so we get some optimization.
  346. const tBSPNode&  node = m_pNodes[i];
  347. const tBSPPlane& plane = m_pPlanes[node.plane];
  348. // Use the Plane Equation (Ax + by + Cz + D = 0) to find if the
  349. // camera is in front of or behind the current splitter plane.
  350.         distance = plane.vNormal.x * vPos.x + 
  351. plane.vNormal.y * vPos.y + 
  352. plane.vNormal.z * vPos.z - plane.d;
  353. // If the camera is in front of the plane
  354.         if(distance >= 0)
  355. {
  356. // Assign the current node to the node in front of itself
  357.             i = node.front;
  358.         }
  359. // Else if the camera is behind the plane
  360.         else
  361. {
  362. // Assign the current node to the node behind itself
  363.             i = node.back;
  364.         }
  365.     }
  366. // Return the leaf index (same thing as saying:  return -(i + 1)).
  367.     return ~i;  // Binary operation
  368. }
  369. //////////////////////////// IS CLUSTER VISIBLE \\\\\\\\\\\\\*
  370. /////
  371. ///// This tells us if the "current" cluster can see the "test" cluster
  372. /////
  373. //////////////////////////// IS CLUSTER VISIBLE \\\\\\\\\\\\\*
  374. inline int CQuake3BSP::IsClusterVisible(int current, int test)
  375. {
  376. // Make sure we have valid memory and that the current cluster is > 0.
  377. // If we don't have any memory or a negative cluster, return a visibility (1).
  378. if(!m_clusters.pBitsets || current < 0) return 1;
  379. // Use binary math to get the 8 bit visibility set for the current cluster
  380. byte visSet = m_clusters.pBitsets[(current*m_clusters.bytesPerCluster) + (test / 8)];
  381. // Now that we have our vector (bitset), do some bit shifting to find if
  382. // the "test" cluster is visible from the "current" cluster, according to the bitset.
  383. int result = visSet & (1 << ((test) & 7));
  384. // Return the result ( either 1 (visible) or 0 (not visible) )
  385. return ( result );
  386. }
  387. /////////////////////////////////// DOT \\\\\\\\\\\\\\\\\\\*
  388. /////
  389. ///// This computers the dot product of 2 vectors
  390. /////
  391. /////////////////////////////////// DOT \\\\\\\\\\\\\\\\\\\*
  392. float Dot(CVector3 vVector1, CVector3 vVector2) 
  393. {
  394. //    (V1.x * V2.x        +        V1.y * V2.y        +        V1.z * V2.z)
  395. return ( (vVector1.x * vVector2.x) + (vVector1.y * vVector2.y) + (vVector1.z * vVector2.z) );
  396. }
  397. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  398. /////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\*
  399. /////
  400. ///// This checks a bunch of different heights to see if we can step up
  401. /////
  402. /////////////////////////////////// TRY TO STEP \\\\\\\\\\\\\\\\\\\*
  403. CVector3 CQuake3BSP::TryToStep(CVector3 vStart, CVector3 vEnd)
  404. {
  405. // In this function we loop until we either found a reasonable height
  406. // that we can step over, or find out that we can't step over anything.
  407. // We check 10 times, each time increasing the step size to check for
  408. // a collision.  If we don't collide, then we climb over the step.
  409. // Go through and check different heights to step up
  410. for(float height = 1.0f; height <= kMaxStepHeight; height++)
  411. {
  412. // Reset our variables for each loop interation
  413. m_bCollided = false;
  414. m_bTryStep = false;
  415. // Here we add the current height to our y position of a new start and end.
  416. // If these 2 new start and end positions are okay, we can step up.
  417. CVector3 vStepStart = CVector3(vStart.x, vStart.y + height, vStart.z);
  418. CVector3 vStepEnd   = CVector3(vEnd.x, vStart.y + height, vEnd.z);
  419. // Test to see if the new position we are trying to step collides or not
  420. CVector3 vStepPosition = Trace(vStepStart, vStepEnd);
  421. // If we didn't collide, we can step!
  422. if(!m_bCollided)
  423. {
  424. // Here we get the current view, then increase the y value by the current height.
  425. // This makes it so when we are walking up the stairs, our view follows our step
  426. // height and doesn't sag down as we walk up the stairs.
  427. CVector3 vNewView = g_Camera.View();
  428. g_Camera.SetView(CVector3(vNewView.x, vNewView.y + height, vNewView.z));
  429. // Return the current position since we stepped up somewhere
  430. return vStepPosition;
  431. }
  432. }
  433. // If we can't step, then we just return the original position of the collision
  434. return vStart;
  435. }
  436. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  437. /////////////////////////////////// TRACE \\\\\\\\\\\\\\\\\\\*
  438. /////
  439. ///// This takes a start and end position (general) to test against the BSP brushes
  440. /////
  441. /////////////////////////////////// TRACE \\\\\\\\\\\\\\\\\\\*
  442. CVector3 CQuake3BSP::Trace(CVector3 vStart, CVector3 vEnd)
  443. {
  444. // Initially we set our trace ratio to 1.0f, which means that we don't have
  445. // a collision or intersection point, so we can move freely.
  446. m_traceRatio = 1.0f;
  447. // We start out with the first node (0), setting our start and end ratio to 0 and 1.
  448. // We will recursively go through all of the nodes to see which brushes we should check.
  449. CheckNode(0, 0.0f, 1.0f, vStart, vEnd);
  450. // If the traceRatio is STILL 1.0f, then we never collided and just return our end position
  451. if(m_traceRatio == 1.0f)
  452. {
  453. return vEnd;
  454. }
  455. else // Else COLLISION!!!!
  456. {
  457. // Set our new position to a position that is right up to the brush we collided with
  458. CVector3 vNewPosition = vStart + ((vEnd - vStart) * m_traceRatio);
  459. // Get the distance from the end point to the new position we just got
  460. CVector3 vMove = vEnd - vNewPosition;
  461. // Get the distance we need to travel backwards to the new slide position.
  462. // This is the distance of course along the normal of the plane we collided with.
  463. float distance = Dot(vMove, m_vCollisionNormal);
  464. // Get the new end position that we will end up (the slide position).
  465. CVector3 vEndPosition = vEnd - m_vCollisionNormal*distance;
  466. // Since we got a new position for our sliding vector, we need to check
  467. // to make sure that new sliding position doesn't collide with anything else.
  468. vNewPosition = Trace(vNewPosition, vEndPosition);
  469. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  470. // 
  471. if(m_vCollisionNormal.y > 0.2f || m_bGrounded)
  472. m_bGrounded = true;
  473. else
  474. m_bGrounded = false;
  475. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  476. // Return the new position to be used by our camera (or player)
  477. return vNewPosition;
  478. }
  479. }
  480. /////////////////////////////////// TRACE RAY \\\\\\\\\\\\\\\\\\\*
  481. /////
  482. ///// This takes a start and end position (ray) to test against the BSP brushes
  483. /////
  484. /////////////////////////////////// TRACE RAY \\\\\\\\\\\\\\\\\\\*
  485. CVector3 CQuake3BSP::TraceRay(CVector3 vStart, CVector3 vEnd)
  486. {
  487. // We don't use this function, but we set it up to allow us to just check a
  488. // ray with the BSP tree brushes.  We do so by setting the trace type to TYPE_RAY.
  489. m_traceType = TYPE_RAY;
  490. // Run the normal Trace() function with our start and end 
  491. // position and return a new position
  492. return Trace(vStart, vEnd);
  493. }
  494. /////////////////////////////////// TRACE SPHERE \\\\\\\\\\\\\\\\\\\*
  495. /////
  496. ///// This tests a sphere around our movement vector against the BSP brushes for collision
  497. /////
  498. /////////////////////////////////// TRACE SPHERE \\\\\\\\\\\\\\\\\\\*
  499. CVector3 CQuake3BSP::TraceSphere(CVector3 vStart, CVector3 vEnd, float radius)
  500. {
  501. // Here we initialize the type of trace (SPHERE) and initialize other data
  502. m_traceType = TYPE_SPHERE;
  503. m_bCollided = false;
  504. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  505. // Here we initialize our variables for a new round of collision checks
  506. m_bTryStep = false;
  507. m_bGrounded = false;
  508. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  509. m_traceRadius = radius;
  510. // Get the new position that we will return to the camera or player
  511. CVector3 vNewPosition = Trace(vStart, vEnd);
  512. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  513. // Let's check to see if we collided with something and we should try to step up
  514. if(m_bCollided && m_bTryStep)
  515. {
  516. // Try and step up what we collided with
  517. vNewPosition = TryToStep(vNewPosition, vEnd);
  518. }
  519. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  520. // Return the new position to be changed for the camera or player
  521. return vNewPosition;
  522. }
  523. /////////////////////////////////// TRACE BOX \\\\\\\\\\\\\\\\\\\*
  524. /////
  525. ///// This takes a start and end position to test a AABB (box) against the BSP brushes
  526. /////
  527. /////////////////////////////////// TRACE BOX \\\\\\\\\\\\\\\\\\\*
  528. CVector3 CQuake3BSP::TraceBox(CVector3 vStart, CVector3 vEnd, CVector3 vMin, CVector3 vMax)
  529. {
  530. m_traceType = TYPE_BOX; // Set the trace type to a BOX
  531. m_vTraceMaxs = vMax; // Set the max value of our AABB
  532. m_vTraceMins = vMin; // Set the min value of our AABB
  533. m_bCollided = false; // Reset the collised flag
  534. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  535. // Here we initialize our variables for a new round of collision checks
  536. m_bTryStep = false;
  537. m_bGrounded = false;
  538. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  539. // Grab the extend of our box (the largest size for each x, y, z axis)
  540. m_vExtents = CVector3(-m_vTraceMins.x > m_vTraceMaxs.x ? -m_vTraceMins.x : m_vTraceMaxs.x,
  541.   -m_vTraceMins.y > m_vTraceMaxs.y ? -m_vTraceMins.y : m_vTraceMaxs.y,
  542.   -m_vTraceMins.z > m_vTraceMaxs.z ? -m_vTraceMins.z : m_vTraceMaxs.z);
  543. // Check if our movement collided with anything, then get back our new position
  544. CVector3 vNewPosition = Trace(vStart, vEnd);
  545. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  546. // Let's check to see if we collided with something and we should try to step up
  547. if(m_bCollided && m_bTryStep)
  548. {
  549. // Try and step up what we collided with
  550. vNewPosition = TryToStep(vNewPosition, vEnd);
  551. }
  552. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  553. // Return our new position
  554. return vNewPosition;
  555. }
  556. /////////////////////////////////// CHECK NODE \\\\\\\\\\\\\\\\\\\*
  557. /////
  558. ///// This traverses the BSP to find the brushes closest to our position
  559. /////
  560. /////////////////////////////////// CHECK NODE \\\\\\\\\\\\\\\\\\\*
  561. void CQuake3BSP::CheckNode(int nodeIndex, float startRatio, float endRatio, CVector3 vStart, CVector3 vEnd)
  562. {
  563. // Check if the next node is a leaf
  564. if(nodeIndex < 0)
  565. {
  566. // If this node in the BSP is a leaf, we need to negate and add 1 to offset
  567. // the real node index into the m_pLeafs[] array.  You could also do [~nodeIndex].
  568. tBSPLeaf *pLeaf = &m_pLeafs[-(nodeIndex + 1)];
  569. // We have a leaf, so let's go through all of the brushes for that leaf
  570. for(int i = 0; i < pLeaf->numOfLeafBrushes; i++)
  571. {
  572. // Get the current brush that we going to check
  573. tBSPBrush *pBrush = &m_pBrushes[m_pLeafBrushes[pLeaf->leafBrush + i]];
  574. // Check if we have brush sides and the current brush is solid and collidable
  575. if((pBrush->numOfBrushSides > 0) && (m_pTextures[pBrush->textureID].textureType & 1))
  576. {
  577. // Now we delve into the dark depths of the real calculations for collision.
  578. // We can now check the movement vector against our brush planes.
  579. CheckBrush(pBrush, vStart, vEnd);
  580. }
  581. }
  582. // Since we found the brushes, we can go back up and stop recursing at this level
  583. return;
  584. }
  585. // Grad the next node to work with and grab this node's plane data
  586. tBSPNode *pNode = &m_pNodes[nodeIndex];
  587. tBSPPlane *pPlane = &m_pPlanes[pNode->plane];
  588. // Here we use the plane equation to find out where our initial start position is
  589. // according the the node that we are checking.  We then grab the same info for the end pos.
  590. float startDistance = Dot(vStart, pPlane->vNormal) - pPlane->d;
  591. float endDistance = Dot(vEnd, pPlane->vNormal) - pPlane->d;
  592. float offset = 0.0f;
  593. // If we are doing sphere collision, include an offset for our collision tests below
  594. if(m_traceType == TYPE_SPHERE)
  595. offset = m_traceRadius;
  596. // Here we check to see if we are working with a BOX or not
  597. else if(m_traceType == TYPE_BOX)
  598. {
  599. // Get the distance our AABB is from the current splitter plane
  600. offset = (float)(fabs( m_vExtents.x * pPlane->vNormal.x ) +
  601.                          fabs( m_vExtents.y * pPlane->vNormal.y ) +
  602.                          fabs( m_vExtents.z * pPlane->vNormal.z ) );
  603. }
  604. // Here we check to see if the start and end point are both in front of the current node.
  605. // If so, we want to check all of the nodes in front of this current splitter plane.
  606. if(startDistance >= offset && endDistance >= offset)
  607. {
  608. // Traverse the BSP tree on all the nodes in front of this current splitter plane
  609. CheckNode(pNode->front, startDistance, endDistance, vStart, vEnd);
  610. }
  611. // If both points are behind the current splitter plane, traverse down the back nodes
  612. else if(startDistance < -offset && endDistance < -offset)
  613. {
  614. // Traverse the BSP tree on all the nodes in back of this current splitter plane
  615. CheckNode(pNode->back, startDistance, endDistance, vStart, vEnd);
  616. }
  617. else
  618. {
  619. // If we get here, then our ray needs to be split in half to check the nodes
  620. // on both sides of the current splitter plane.  Thus we create 2 ratios.
  621. float Ratio1 = 1.0f, Ratio2 = 0.0f, middleRatio = 0.0f;
  622. CVector3 vMiddle; // This stores the middle point for our split ray
  623. // Start of the side as the front side to check
  624. int side = pNode->front;
  625. // Here we check to see if the start point is in back of the plane (negative)
  626. if(startDistance < endDistance)
  627. {
  628. // Since the start position is in back, let's check the back nodes
  629. side = pNode->back;
  630. // Here we create 2 ratios that hold a distance from the start to the
  631. // extent closest to the start (take into account a sphere and epsilon).
  632. float inverseDistance = 1.0f / (startDistance - endDistance);
  633. Ratio1 = (startDistance - offset - kEpsilon) * inverseDistance;
  634. Ratio2 = (startDistance + offset + kEpsilon) * inverseDistance;
  635. }
  636. // Check if the starting point is greater than the end point (positive)
  637. else if(startDistance > endDistance)
  638. {
  639. // This means that we are going to recurse down the front nodes first.
  640. // We do the same thing as above and get 2 ratios for split ray.
  641. float inverseDistance = 1.0f / (startDistance - endDistance);
  642. Ratio1 = (startDistance + offset + kEpsilon) * inverseDistance;
  643. Ratio2 = (startDistance - offset - kEpsilon) * inverseDistance;
  644. }
  645. // Make sure that we have valid numbers and not some weird float problems.
  646. // This ensures that we have a value from 0 to 1 as a good ratio should be :)
  647. if (Ratio1 < 0.0f) Ratio1 = 0.0f;
  648.         else if (Ratio1 > 1.0f) Ratio1 = 1.0f;
  649.         if (Ratio2 < 0.0f) Ratio2 = 0.0f;
  650.         else if (Ratio2 > 1.0f) Ratio2 = 1.0f;
  651. // Just like we do in the Trace() function, we find the desired middle
  652. // point on the ray, but instead of a point we get a middleRatio percentage.
  653. middleRatio = startRatio + ((endRatio - startRatio) * Ratio1);
  654. vMiddle = vStart + ((vEnd - vStart) * Ratio1);
  655. // Now we recurse on the current side with only the first half of the ray
  656. CheckNode(side, startRatio, middleRatio, vStart, vMiddle);
  657. // Now we need to make a middle point and ratio for the other side of the node
  658. middleRatio = startRatio + ((endRatio - startRatio) * Ratio2);
  659. vMiddle = vStart + ((vEnd - vStart) * Ratio2);
  660. // Depending on which side should go last, traverse the bsp with the
  661. // other side of the split ray (movement vector).
  662. if(side == pNode->back)
  663. CheckNode(pNode->front, middleRatio, endRatio, vMiddle, vEnd);
  664. else
  665. CheckNode(pNode->back, middleRatio, endRatio, vMiddle, vEnd);
  666. }
  667. }
  668. /////////////////////////////////// CHECK BRUSH \\\\\\\\\\\\\\\\\\\*
  669. /////
  670. ///// This checks our movement vector against all the planes of the brush
  671. /////
  672. /////////////////////////////////// CHECK BRUSH \\\\\\\\\\\\\\\\\\\*
  673. void CQuake3BSP::CheckBrush(tBSPBrush *pBrush, CVector3 vStart, CVector3 vEnd)
  674. {
  675. float startRatio = -1.0f; // Like in BrushCollision.htm, start a ratio at -1
  676.     float endRatio = 1.0f; // Set the end ratio to 1
  677.     bool startsOut = false; // This tells us if we starting outside the brush
  678. // Go through all of the brush sides and check collision against each plane
  679. for(int i = 0; i < pBrush->numOfBrushSides; i++)
  680. {
  681. // Here we grab the current brush side and plane in this brush
  682. tBSPBrushSide *pBrushSide = &m_pBrushSides[pBrush->brushSide + i];
  683. tBSPPlane *pPlane = &m_pPlanes[pBrushSide->plane];
  684. // Let's store a variable for the offset (like for sphere collision)
  685. float offset = 0.0f;
  686. // If we are testing sphere collision we need to add the sphere radius
  687. if(m_traceType == TYPE_SPHERE)
  688. offset = m_traceRadius;
  689. // Test the start and end points against the current plane of the brush side.
  690. // Notice that we add an offset to the distance from the origin, which makes
  691. // our sphere collision work.
  692. float startDistance = Dot(vStart, pPlane->vNormal) - (pPlane->d + offset);
  693. float endDistance = Dot(vEnd, pPlane->vNormal) - (pPlane->d + offset);
  694. // Store the offset that we will check against the plane
  695. CVector3 vOffset = CVector3(0, 0, 0);
  696. // If we are using AABB collision
  697. if(m_traceType == TYPE_BOX)
  698. {
  699. // Grab the closest corner (x, y, or z value) that is closest to the plane
  700.             vOffset.x = (pPlane->vNormal.x < 0) ? m_vTraceMaxs.x : m_vTraceMins.x;
  701. vOffset.y = (pPlane->vNormal.y < 0) ? m_vTraceMaxs.y : m_vTraceMins.y;
  702. vOffset.z = (pPlane->vNormal.z < 0) ? m_vTraceMaxs.z : m_vTraceMins.z;
  703.             
  704. // Use the plane equation to grab the distance our start position is from the plane.
  705.             startDistance = Dot(vStart + vOffset, pPlane->vNormal) - pPlane->d;
  706. // Get the distance our end position is from this current brush plane
  707.             endDistance   = Dot(vEnd + vOffset, pPlane->vNormal) - pPlane->d;
  708.         }
  709. // Make sure we start outside of the brush's volume
  710. if(startDistance > 0) startsOut = true;
  711. // Stop checking since both the start and end position are in front of the plane
  712. if(startDistance > 0 && endDistance > 0)
  713. return;
  714. // Continue on to the next brush side if both points are behind or on the plane
  715. if(startDistance <= 0 && endDistance <= 0)
  716. continue;
  717. // If the distance of the start point is greater than the end point, we have a collision!
  718. if(startDistance > endDistance)
  719. {
  720. // This gets a ratio from our starting point to the approximate collision spot
  721. float Ratio1 = (startDistance - kEpsilon) / (startDistance - endDistance);
  722. // If this is the first time coming here, then this will always be true,
  723. if(Ratio1 > startRatio)
  724. {
  725. // Set the startRatio (currently the closest collision distance from start)
  726. startRatio = Ratio1;
  727. m_bCollided = true; // Let us know we collided!
  728. // Store the normal of plane that we collided with for sliding calculations
  729. m_vCollisionNormal = pPlane->vNormal;
  730. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  731. // This checks first tests if we actually moved along the x or z-axis,
  732. // meaning that we went in a direction somewhere.  The next check makes
  733. // sure that we don't always check to step every time we collide.  If
  734. // the normal of the plane has a Y value of 1, that means it's just the
  735. // flat ground and we don't need to check if we can step over it, it's flat!
  736. if((vStart.x != vEnd.x || vStart.z != vEnd.z) && pPlane->vNormal.y != 1)
  737. {
  738. // We can try and step over the wall we collided with
  739. m_bTryStep = true;
  740. }
  741. // Here we make sure that we don't slide slowly down walls when we
  742. // jump and collide into them.  We only want to say that we are on
  743. // the ground if we actually have stopped from falling.  A wall wouldn't
  744. // have a high y value for the normal, it would most likely be 0.
  745. if(m_vCollisionNormal.y >= 0.2f)
  746. m_bGrounded = true;
  747. /////// * /////////// * /////////// * NEW * /////// * /////////// * /////////// *
  748. }
  749. }
  750. else
  751. {
  752. // Get the ratio of the current brush side for the endRatio
  753. float Ratio = (startDistance + kEpsilon) / (startDistance - endDistance);
  754. // If the ratio is less than the current endRatio, assign a new endRatio.
  755. // This will usually always be true when starting out.
  756. if(Ratio < endRatio)
  757. endRatio = Ratio;
  758. }
  759. }
  760. // If we didn't start outside of the brush we don't want to count this collision - return;
  761. if(startsOut == false)
  762. {
  763. return;
  764. }
  765. // If our startRatio is less than the endRatio there was a collision!!!
  766. if(startRatio < endRatio)
  767. {
  768. // Make sure the startRatio moved from the start and check if the collision
  769. // ratio we just got is less than the current ratio stored in m_traceRatio.
  770. // We want the closest collision to our original starting position.
  771. if(startRatio > -1 && startRatio < m_traceRatio)
  772. {
  773. // If the startRatio is less than 0, just set it to 0
  774. if(startRatio < 0)
  775. startRatio = 0;
  776. // Store the new ratio in our member variable for later
  777. m_traceRatio = startRatio;
  778. }
  779. }
  780. }
  781. //////////////////////////// RENDER FACE \\\\\\\\\\\\\*
  782. /////
  783. ///// This renders a face, determined by the passed in index
  784. /////
  785. //////////////////////////// RENDER FACE \\\\\\\\\\\\\*
  786. void CQuake3BSP::RenderFace(int faceIndex)
  787. {
  788. // Here we grab the face from the index passed in
  789. tBSPFace *pFace = &m_pFaces[faceIndex];
  790. // Assign our array of face vertices for our vertex arrays and enable vertex arrays
  791. glVertexPointer(3, GL_FLOAT, sizeof(tBSPVertex), &(m_pVerts[pFace->startVertIndex].vPosition));
  792. glEnableClientState(GL_VERTEX_ARRAY);
  793. // If we want to render the textures
  794. if(g_bTextures)
  795. {
  796. // Set the current pass as the first texture (For multi-texturing)
  797. glActiveTextureARB(GL_TEXTURE0_ARB);
  798. // Give OpenGL the texture coordinates for the first texture, and enable that texture
  799. glClientActiveTextureARB(GL_TEXTURE0_ARB);
  800. glTexCoordPointer(2, GL_FLOAT, sizeof(tBSPVertex), 
  801.    &(m_pVerts[pFace->startVertIndex].vTextureCoord));
  802. // Set our vertex array client states for allowing texture coordinates
  803. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  804. // Turn on texture arrays for the first pass
  805. glClientActiveTextureARB(GL_TEXTURE0_ARB);
  806. // Turn on texture mapping and bind the face's texture map
  807. glEnable(GL_TEXTURE_2D);
  808. glBindTexture(GL_TEXTURE_2D,  m_textures[pFace->textureID]);
  809. }
  810. if(g_bLightmaps)
  811. {
  812. // Set the current pass as the second lightmap texture_
  813. glActiveTextureARB(GL_TEXTURE1_ARB);
  814. // Turn on texture arrays for the second lightmap pass
  815. glClientActiveTextureARB(GL_TEXTURE1_ARB);
  816. glEnableClientState(GL_TEXTURE_COORD_ARRAY);
  817. // Next, we need to specify the UV coordinates for our lightmaps.  This is done
  818. // by switching to the second texture and giving OpenGL our lightmap array.
  819. glClientActiveTextureARB(GL_TEXTURE1_ARB);
  820. glTexCoordPointer(2, GL_FLOAT, sizeof(tBSPVertex), 
  821.    &(m_pVerts[pFace->startVertIndex].vLightmapCoord));
  822. // Turn on texture mapping and bind the face's lightmap over the texture
  823. glEnable(GL_TEXTURE_2D);
  824. glBindTexture(GL_TEXTURE_2D,  m_lightmaps[pFace->lightmapID]);
  825. }
  826. // Render our current face to the screen with vertex arrays
  827. glDrawElements(GL_TRIANGLES, pFace->numOfIndices, GL_UNSIGNED_INT, &(m_pIndices[pFace->startIndex]) );
  828. }
  829. //////////////////////////// RENDER LEVEL \\\\\\\\\\\\\*
  830. /////
  831. ///// Goes through all of the faces and draws them if the type is FACE_POLYGON
  832. /////
  833. //////////////////////////// RENDER LEVEL \\\\\\\\\\\\\*
  834. void CQuake3BSP::RenderLevel(const CVector3 &vPos)
  835. {
  836. // Reset our bitset so all the slots are zero.
  837. m_FacesDrawn.ClearAll();
  838. // Grab the leaf index that our camera is in
  839. int leafIndex = FindLeaf(vPos);
  840. // Grab the cluster that is assigned to the leaf
  841. int cluster = m_pLeafs[leafIndex].cluster;
  842. // Initialize our counter variables (start at the last leaf and work down)
  843. int i = m_numOfLeafs;
  844. g_VisibleFaces = 0;
  845. // Go through all the leafs and check their visibility
  846. while(i--)
  847. {
  848. // Get the current leaf that is to be tested for visibility from our camera's leaf
  849. tBSPLeaf *pLeaf = &(m_pLeafs[i]);
  850. // If the current leaf can't be seen from our cluster, go to the next leaf
  851. if(!IsClusterVisible(cluster, pLeaf->cluster)) 
  852. continue;
  853. // If the current leaf is not in the camera's frustum, go to the next leaf
  854. if(!g_Frustum.BoxInFrustum((float)pLeaf->min.x, (float)pLeaf->min.y, (float)pLeaf->min.z,
  855.             (float)pLeaf->max.x, (float)pLeaf->max.y, (float)pLeaf->max.z))
  856. continue;
  857. // If we get here, the leaf we are testing must be visible in our camera's view.
  858. // Get the number of faces that this leaf is in charge of.
  859. int faceCount = pLeaf->numOfLeafFaces;
  860. // Loop through and render all of the faces in this leaf
  861. while(faceCount--)
  862. {
  863. // Grab the current face index from our leaf faces array
  864. int faceIndex = m_pLeafFaces[pLeaf->leafface + faceCount];
  865. // Before drawing this face, make sure it's a normal polygon
  866. if(m_pFaces[faceIndex].type != FACE_POLYGON) continue;
  867. // Since many faces are duplicated in other leafs, we need to
  868. // make sure this face already hasn't been drawn.
  869. if(!m_FacesDrawn.On(faceIndex)) 
  870. {
  871. // Increase the rendered face count to display for fun
  872. g_VisibleFaces++;
  873. // Set this face as drawn and render it
  874. m_FacesDrawn.Set(faceIndex);
  875. RenderFace(faceIndex);
  876. }
  877. }
  878. }
  879. }
  880. //////////////////////////// DESTROY \\\\\\\\\\\\\*
  881. /////
  882. ///// This cleans up our object and frees allocated memory
  883. /////
  884. //////////////////////////// DESTROY \\\\\\\\\\\\\*
  885. void CQuake3BSP::Destroy()
  886. {
  887. // If we still have valid memory for our vertices, free them
  888. if(m_pVerts) 
  889. {
  890. delete [] m_pVerts; m_pVerts = NULL;
  891. }
  892. // If we still have valid memory for our faces, free them
  893. if(m_pFaces)
  894. {
  895. delete [] m_pFaces; m_pFaces = NULL;
  896. }
  897. // If we still have valid memory for our indices, free them
  898. if(m_pIndices)
  899. {
  900. delete [] m_pIndices;
  901. m_pIndices = NULL;
  902. }
  903. // If we still have valid memory for our nodes, free them
  904. if(m_pNodes)
  905. {
  906. delete [] m_pNodes; m_pNodes = NULL;
  907. }
  908. // If we still have valid memory for our leafs, free them
  909. if(m_pLeafs)
  910. {
  911. delete [] m_pLeafs; m_pLeafs = NULL;
  912. }
  913. // If we still have valid memory for our leaf faces, free them
  914. if(m_pLeafFaces)
  915. {
  916. delete [] m_pLeafFaces; m_pLeafFaces = NULL;
  917. }
  918. // If we still have valid memory for our planes, free them
  919. if(m_pPlanes)
  920. {
  921. delete [] m_pPlanes; m_pPlanes = NULL;
  922. }
  923. // If we still have valid memory for our clusters, free them
  924. if(m_clusters.pBitsets)
  925. {
  926. delete [] m_clusters.pBitsets; m_clusters.pBitsets = NULL;
  927. }
  928. // If we still have valid memory for our brushes, free them
  929. if(m_pBrushes)
  930. {
  931. delete [] m_pBrushes; m_pBrushes = NULL;
  932. }
  933. // If we still have valid memory for our brush sides, free them
  934. if(m_pBrushSides)
  935. {
  936. delete [] m_pBrushSides; m_pBrushSides = NULL;
  937. }
  938. // If we still have valid memory for our leaf brushes, free them
  939. if(m_pLeafBrushes)
  940. {
  941. delete [] m_pLeafBrushes; m_pLeafBrushes = NULL;
  942. }
  943. // If we still have valid memory for our BSP texture info, free it
  944. if(m_pTextures)
  945. {
  946. delete [] m_pTextures; m_pTextures = NULL;
  947. }
  948. // Free all the textures
  949. glDeleteTextures(m_numOfTextures, m_textures);
  950. // Delete the lightmap textures
  951. glDeleteTextures(m_numOfLightmaps, m_lightmaps);
  952. }
  953. //////////////////////////// ~CQUAKE3BSP \\\\\\\\\\\\\*
  954. /////
  955. ///// This is our deconstructor that is called when the object is destroyed
  956. /////
  957. //////////////////////////// ~CQUAKE3BSP \\\\\\\\\\\\\*
  958. CQuake3BSP::~CQuake3BSP()
  959. {
  960. // Call our destroy function
  961. Destroy();
  962. }