Quake2 Lightmap Size / Coordinates

Hi !

I have SERIOUS trouble getting the size of Q2 lightmaps. I read trough three other Q2 engine sources, I can’t figure out what I’m doing wrong. Could you please look at my code an tell me ?

// BSP.cpp: Implementation of the class CBSP.
//
//////////////////////////////////////////////////////////////////////

#include “stdafx.h”
#include “BSP.h”

//////////////////////////////////////////////////////////////////////
// Construction / destruction
//////////////////////////////////////////////////////////////////////

CBSP::CBSP()
{
////////////////////////////////////////////////////////////////////////
// Initialize the member data
////////////////////////////////////////////////////////////////////////

m_pVertices = 0;
m_pEdges = 0;
m_pFaces = 0;
m_pFaceEdges = 0;
m_pPlanes = 0;
m_pNodes = 0;
m_pLeaves = 0;
m_pLeafFaces = 0;
m_pTexInfos = 0;
m_pTextures = 0;
m_pLightmaps = 0;

memset(&m_sCount, 0, sizeof(BSP_LumpCount));
}

CBSP::~CBSP()
{
////////////////////////////////////////////////////////////////////////
// Free the member data
////////////////////////////////////////////////////////////////////////

BSP_Texture *pCurTex = 0;
BSP_Texture *pNextTex = 0;

if (m_pVertices)
delete m_pVertices;
m_pVertices = 0;
if (m_pEdges)
delete m_pEdges;
m_pEdges = 0;
if (m_pFaces)
delete m_pFaces;
m_pFaces = 0;
if (m_pFaceEdges)
delete m_pFaceEdges;
m_pFaceEdges = 0;
if (m_pPlanes)
delete m_pPlanes;
m_pPlanes = 0;
if (m_pNodes)
delete m_pNodes;
m_pNodes = 0;
if (m_pLeaves)
delete m_pLeaves;
m_pLeaves = 0;
if (m_pLeafFaces)
delete m_pLeafFaces;
m_pLeafFaces = 0;
if (m_pTexInfos)
delete m_pTexInfos;
m_pTexInfos = 0;
if (m_pLightmaps)
delete m_pLightmaps;
m_pLightmaps = 0;

if (m_pTextures)
{
// Start cleaning up with the first node
pCurTex = m_pTextures;

  // Remove the linked list
  do
  {
  	assert(pCurTex);

  	// Save pointer to next texture
  	pNextTex = pCurTex->pNextTexture;

  	// Delete current texture
  	delete pCurTex;
  	pCurTex = 0;

  	// Process the next texture
  	pCurTex = pNextTex;

  } while (pCurTex); // Continue if this is not the tail node

  m_pTextures = 0;

}
}

void CBSP::RenderWithoutHSR()
{
////////////////////////////////////////////////////////////////////////
// Brute-force approach to render all geometry
////////////////////////////////////////////////////////////////////////

unsigned long int iCurFace, iCurFaceEdge;
BSP_Texture *pCurTexture = 0;
unsigned int iTexWidth;
int iVertex1, iVertex2, iTempVertex;
BSP_TexInfo *pTexInfo = 0;
float fU, fV;
static int iDisplayList = -1;
CTexture cMultitexturing;
char szLastTextureName[32] = { ‘\0’ };

assert(m_pEdges);
assert(m_pFaces);
assert(m_pVertices);
assert(m_pTextures);
assert(m_pLightmaps);
assert(m_pFaceEdges);

// Has the display list already been generated ?
if (iDisplayList != -1)
{
// Just call the list and exit
glCallList(iDisplayList);
return;
}

// This is the first time we render the scene. Create a display
// list and render the scene into the list while passing it to OpenGL.
iDisplayList = glGenLists(1);
glNewList(iDisplayList, GL_COMPILE);

// Save rendering state
glPushAttrib(GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT);

// Enable multitexturing
cMultitexturing.InitMultitexturing();

// Additive blending for the second TMU
cMultitexturing.ChoseActiveTMU(GL_TEXTURE1_ARB);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD);

// Use fullbright textures for the first TMU
cMultitexturing.ChoseActiveTMU(GL_TEXTURE0_ARB);
glDisable(GL_LIGHTING);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

// Loop trough all faces
for (iCurFace=0; iCurFace<m_sCount.iFaceCount; iCurFace++)
{
// Save ‘shortcut’ pointer to access the texture informations
pTexInfo = &m_pTexInfos[m_pFaces[iCurFace].iTexInfo];

  // Start with the first texture in the linked list
  pCurTexture = m_pTextures;

  // Find the current texture and bind it to the first TMU
  do
  {
  	assert(pCurTexture);

  	if (_stricmp(pTexInfo->szTextureName, pCurTexture->szTextureName) == 0)
  	{
  		// Texture found. Is a state change neccessary ?
  		if (_stricmp(szLastTextureName, pCurTexture->szTextureName) != 0)
  		{
  			// This texture differs from the last one. Make it the current one
  			cMultitexturing.ChoseActiveTMU(GL_TEXTURE0_ARB);
  			pCurTexture->cTexure.Bind();
  			// Save texture width for calculating the texture coordinates. You have to
  			// divide the planar texture coords by the width of the texture to get a
  			// correct mapping
  			iTexWidth = pCurTexture->cTexure.GetTexWidth();
  		}
  		break;
  	}

  	// Current texture is not the requested one, advance to the next
  	pCurTexture = pCurTexture->pNextTexture;

  } while (pCurTexture); // Stop when there are no more textures

  // Assign lightmap to the second TMU
  cMultitexturing.ChoseActiveTMU(GL_TEXTURE1_ARB);
  m_pLightmaps[iCurFace].cLightmap.Bind();
  
  // Begin drawing the face
  glBegin(GL_TRIANGLE_FAN);

  // Loop trough all face edges of the current face
  for (iCurFaceEdge=0; iCurFaceEdge<m_pFaces[iCurFace].iNumEdges; iCurFaceEdge++)
  {
  	// Don't read behind the face edge array
  	assert(m_pFaces[iCurFace].lFirstEdge + iCurFaceEdge < 
  		m_sCount.iFaceEdgeCount);

  	// Don't read behind the edge array. Drop a possible negative sign
  	// because this is only used to indicate the 'winding' of the edge
  	assert((unsigned long) abs(m_pFaceEdges[m_pFaces[iCurFace].lFirstEdge +
  		iCurFaceEdge]) < m_sCount.iEdgeCount);
  			
  	// Look up the two edge vertex indexes in the edge array
  	iVertex1 = m_pEdges[abs(m_pFaceEdges[m_pFaces[iCurFace].lFirstEdge +
  		iCurFaceEdge])].iVertex1;
  	iVertex2 = m_pEdges[abs(m_pFaceEdges[m_pFaces[iCurFace].lFirstEdge + 
  		iCurFaceEdge])].iVertex2;
  
  	// Reverse the vertex order
  	if (m_pFaceEdges[m_pFaces[iCurFace].lFirstEdge + iCurFaceEdge] > 0)
  	{
  		iTempVertex = iVertex1;
  		iVertex1 = iVertex2;
  		iVertex2 = iTempVertex;
  	}

  	// Texture coordinate for the first vertex
  	fU = (m_pVertices[iVertex1].x * pTexInfo->UAxis.x + m_pVertices[iVertex1].y *
  		pTexInfo->UAxis.y + m_pVertices[iVertex1].z * 
  		pTexInfo->UAxis.z + pTexInfo->UOffset) / iTexWidth;
  	fV = (m_pVertices[iVertex1].x * pTexInfo->VAxis.x + m_pVertices[iVertex1].y *
  		pTexInfo->VAxis.y + m_pVertices[iVertex1].z * 
  		pTexInfo->VAxis.z + pTexInfo->VOffset) / iTexWidth;

  	// Pass the coordinates for world and lightmap texture
  	cMultitexturing.MultiTexCoord2f(GL_TEXTURE0_ARB, fU, fV);
  	cMultitexturing.MultiTexCoord2f(GL_TEXTURE1_ARB, 
  		(float) (m_pLightmaps[iCurFace].cLightmap.GetTexWidth() / 2.0f) +
  		(fU - m_pLightmaps[iCurFace].fMidFaceU) / 16.0f,
  		(float) (m_pLightmaps[iCurFace].cLightmap.GetTexHeight() / 2.0f) +
  		(fV - m_pLightmaps[iCurFace].fMidFaceV) / 16.0f);
  	
  	// Pass the first vertex
  	glVertex3f(m_pVertices[iVertex1].x, m_pVertices[iVertex1].y, 
  		m_pVertices[iVertex1].z);

  	// Texture coordinate for the second vertex
  	fU = (m_pVertices[iVertex2].x * pTexInfo->UAxis.x + m_pVertices[iVertex2].y *
  		pTexInfo->UAxis.y + m_pVertices[iVertex2].z * 
  		pTexInfo->UAxis.z + pTexInfo->UOffset) / iTexWidth;
  	fV = (m_pVertices[iVertex2].x * pTexInfo->VAxis.x + m_pVertices[iVertex2].y *
  		pTexInfo->VAxis.y + m_pVertices[iVertex2].z * 
  		pTexInfo->VAxis.z + pTexInfo->VOffset) / iTexWidth;

  	// Pass the texture coordinates for world and lightmap texture
  	cMultitexturing.MultiTexCoord2f(GL_TEXTURE0_ARB, fU, fV);
  	cMultitexturing.MultiTexCoord2f(GL_TEXTURE1_ARB, 
  		(float) (m_pLightmaps[iCurFace].cLightmap.GetTexWidth() / 2.0f) + 
  		(fU - m_pLightmaps[iCurFace].fMidFaceU) / 16.0f,
  		(float) (m_pLightmaps[iCurFace].cLightmap.GetTexHeight() / 2.0f) + 
  		(fV - m_pLightmaps[iCurFace].fMidFaceV) / 16.0f);

  	// Pass the second vertex
  	glVertex3f(m_pVertices[iVertex2].x, m_pVertices[iVertex2].y, 
  		m_pVertices[iVertex2].z);
  }
  
  // Finished drawing the face
  glEnd();

}

// Restore previous rendering state and finish display list
glPopAttrib();
glEndList();
}

bool CBSP::LoadBSP(PSTR pszFileName, PSTR pszTexturePath, PSTR pszPalettePath)
{
////////////////////////////////////////////////////////////////////////
// Load a Q2 BSP file
////////////////////////////////////////////////////////////////////////

FILE *hFile = 0; // File handle for the BSP file
BSP_Header sHeader; // Header of the BSP file
CProgressWindow cLoadingStatus; // Progress window to show loading status

// Create the progress window. The progress bar is accessed trough a
// static member function. The window is automatically destroied when the
// object goes out of scope
cLoadingStatus.CreateProgressWindow(“Loading Quake 2 BSP File”);

// Open the file in binary mode
hFile = fopen(pszFileName, “rb”);

if (!hFile)
{
assert(hFile);
return false;
}

////////////////////////////////////////////////////////////////////////
// Read header and lump informations
////////////////////////////////////////////////////////////////////////

// Read header with lump informations
SaveRead(&sHeader, sizeof(BSP_Header), 1, hFile);

// Is the file a BSP file ?
if (sHeader.szMagic[0] != ‘I’ | |
sHeader.szMagic[1] != ‘B’ | |
sHeader.szMagic[2] != ‘S’ | |
sHeader.szMagic[3] != ‘P’)
{
// Wrong ‘magic’, no BSP file !
assert(sHeader.szMagic[0] == ‘I’);
fclose(hFile);
return false;
}

// Do we load the correct BSP file version ?
if (sHeader.lVersion != 38)
{
// Wrong BSP version, probably Q3 or a modified Q2 engine game
assert(sHeader.lVersion == 38);
fclose(hFile);
return false;
}

// Obtain the item count for the lumps
GetItemCount(&sHeader, &m_sCount);

////////////////////////////////////////////////////////////////////////
// Allocate memory
////////////////////////////////////////////////////////////////////////

m_pVertices = new Point3f[m_sCount.iVertexCount]; // Vertices
m_pEdges = new BSP_Edge[m_sCount.iEdgeCount]; // Edges
m_pFaces = new BSP_Face[m_sCount.iFaceCount]; // Faces
m_pFaceEdges = new long int[m_sCount.iFaceEdgeCount]; // Face edges
m_pPlanes = new BSP_Plane[m_sCount.iPlaneCount]; // Planes
m_pNodes = new BSP_Node[m_sCount.iNodeCount]; // Nodes
m_pLeaves = new BSP_Leaf[m_sCount.iLeafCount]; // Leaves
m_pLeafFaces = new unsigned short int[m_sCount.iLeafFaceCount]; // Leaf faces
m_pTexInfos = new BSP_TexInfo[m_sCount.iTexInfoCount]; // Texture informations
m_pLightmaps = new BSP_Lightmap[m_sCount.iFaceCount]; // Lightmaps

////////////////////////////////////////////////////////////////////////
// Load all needed lumb data
////////////////////////////////////////////////////////////////////////

// Load all vertices
SeekToLump(L_VERTICES, &sHeader, hFile);
SaveRead(m_pVertices, sizeof(m_pVertices[0]), m_sCount.iVertexCount, hFile);
// Load all edges
SeekToLump(L_EDGES, &sHeader, hFile);
SaveRead(m_pEdges, sizeof(m_pEdges[0]), m_sCount.iEdgeCount, hFile);
// Load all faces
SeekToLump(L_FACES, &sHeader, hFile);
SaveRead(m_pFaces, sizeof(m_pFaces[0]), m_sCount.iFaceCount, hFile);
// Load all face edges
SeekToLump(L_FACE_EDGE_TABLE, &sHeader, hFile);
SaveRead(m_pFaceEdges, sizeof(m_pFaceEdges[0]), m_sCount.iFaceEdgeCount, hFile);
// Load all planes
SeekToLump(L_PLANES, &sHeader, hFile);
SaveRead(m_pPlanes, sizeof(m_pPlanes[0]), m_sCount.iPlaneCount, hFile);
// Load all nodes
SeekToLump(L_NODES, &sHeader, hFile);
SaveRead(m_pNodes, sizeof(m_pNodes[0]), m_sCount.iNodeCount, hFile);
// Load all leaves
SeekToLump(L_LEAVES, &sHeader, hFile);
SaveRead(m_pLeaves, sizeof(m_pLeaves[0]), m_sCount.iLeafCount, hFile);
// Load all leaf faces
SeekToLump(L_LEAF_FACE_TABLE, &sHeader, hFile);
SaveRead(m_pLeafFaces, sizeof(m_pLeafFaces[0]), m_sCount.iLeafFaceCount, hFile);
// Load all texture informations
SeekToLump(L_TEXTURE_INFORMATION, &sHeader, hFile);
SaveRead(m_pTexInfos, sizeof(m_pTexInfos[0]), m_sCount.iTexInfoCount, hFile);

// Load the textures
if (!LoadTextures(m_pTextures, pszTexturePath, pszPalettePath))
return false;

// Load lightmaps
if (!LoadLightmaps(hFile, &sHeader))
return false;

////////////////////////////////////////////////////////////////////////
// Close file
////////////////////////////////////////////////////////////////////////

fclose(hFile);

return true;
}

bool CBSP::LoadLightmaps(FILE *hFile, BSP_Header *pHeader)
{
////////////////////////////////////////////////////////////////////////
// Load the lightmaps from the lightmap lump in the BSP file
////////////////////////////////////////////////////////////////////////

unsigned long int iCurFace;
bool bReturn;
unsigned char *pLightmapData = 0;
unsigned int iLightmapWidth, iLightmapHeight;

assert(m_pFaces);
assert(m_pTexInfos);
assert(hFile);
assert(pHeader);

CProgressWindow::SetTask(“Loading Lightmaps…”);

if (!hFile)
{
assert(hFile);
return false;
}

// Allocate space for the lightmaps
pLightmapData = new unsigned char [pHeader->Lump[L_LIGHTMAPS].iLength];

// Move the file pointer to the start of the lightmap lump
SeekToLump(L_LIGHTMAPS, pHeader, hFile);

// Load the whole lightmap lump out of the file
bReturn = SaveRead(pLightmapData, pHeader->Lump[L_LIGHTMAPS].iLength, 1, hFile);

if (!bReturn)
{
delete pLightmapData;
pLightmapData = 0;
return false;
}

// Build the lightmap for every face
for (iCurFace=0; iCurFace<m_sCount.iFaceCount; iCurFace++)
{
// Display status
CProgressWindow::SetProgress(iCurFace * 100 / m_sCount.iFaceCount);

  // Calculate the size of the lightmap. The average texture coordinate is
  // also returned and in the lightmap array
  GetLightmapSize(iCurFace, &iLightmapWidth, &iLightmapHeight,
  	&m_pLightmaps[iCurFace].fMidFaceU, &m_pLightmaps[iCurFace].fMidFaceV);

  // Create a texture from the data stored in pLightmapData. The lightmap's
  // offset in the file is the same as in the array
  bReturn = m_pLightmaps[iCurFace].cLightmap.LoadTextureArray
  	(&pLightmapData[m_pFaces[iCurFace].lLightmapOffset], iLightmapWidth, iLightmapHeight);
  assert(bReturn);

}

// Clean up
delete pLightmapData;
pLightmapData = 0;

return true;
}

void CBSP::GetLightmapSize(unsigned int iFace, unsigned int *pWidth,
unsigned int *pHeight, float *fMidFaceU,
float *fMidFaceV)
{
////////////////////////////////////////////////////////////////////////
// Calculate the size of a lightmap for a face. Also return the average
// texture coordinate for the lightmap’s face, it will be stored with
// the lightmap
////////////////////////////////////////////////////////////////////////

float fU, fV;
float fMinU = (float) +3.4E38, fMaxU = (float) -3.4E38;
float fMinV = (float) +3.4E38, fMaxV = (float) -3.4E38;
unsigned int iCurFaceEdge;
int iVertex1, iVertex2;
BSP_TexInfo *pTexInfo = 0;

assert(m_pTexInfos);
assert(m_pFaceEdges);
assert(m_pFaces);
assert(m_pVertices);
assert(pWidth);
assert(pHeight);
assert(fMidFaceU);
assert(fMidFaceV);

// Save ‘shortcut’ pointer to access the texture informations
pTexInfo = &m_pTexInfos[m_pFaces[iFace].iTexInfo];

// Loop trough all face edges of the face
for (iCurFaceEdge=0; iCurFaceEdge<m_pFaces[iFace].iNumEdges; iCurFaceEdge++)
{
// Look up the two edge vertex indexes in the edge array
iVertex1 = m_pEdges[abs(m_pFaceEdges[m_pFaces[iFace].lFirstEdge +
iCurFaceEdge])].iVertex1;
iVertex2 = m_pEdges[abs(m_pFaceEdges[m_pFaces[iFace].lFirstEdge +
iCurFaceEdge])].iVertex2;

  // Texture coordinate for the first vertex
  fU = (m_pVertices[iVertex1].x * pTexInfo->UAxis.x + m_pVertices[iVertex1].y *
  	pTexInfo->UAxis.y + m_pVertices[iVertex1].z * 
  	pTexInfo->UAxis.z + pTexInfo->UOffset);
  fV = (m_pVertices[iVertex1].x * pTexInfo->VAxis.x + m_pVertices[iVertex1].y *
  	pTexInfo->VAxis.y + m_pVertices[iVertex1].z * 
  	pTexInfo->VAxis.z + pTexInfo->VOffset);

  // Have we found a new smallest / biggest texture coordinate ?
  fMinU = __min(fMinU, fU);
  fMaxU = __max(fMaxU, fU);
  fMinV = __min(fMinV, fV);
  fMaxV = __max(fMaxV, fV);

  // Texture coordinate for the second vertex
  fU = (m_pVertices[iVertex2].x * pTexInfo->UAxis.x + m_pVertices[iVertex2].y *
  	pTexInfo->UAxis.y + m_pVertices[iVertex2].z * 
  	pTexInfo->UAxis.z + pTexInfo->UOffset);
  fV = (m_pVertices[iVertex2].x * pTexInfo->VAxis.x + m_pVertices[iVertex2].y *
  	pTexInfo->VAxis.y + m_pVertices[iVertex2].z * 
  	pTexInfo->VAxis.z + pTexInfo->VOffset);

  // Have we found a new smallest / biggest texture coordinate ?
  fMinU = __min(fMinU, fU);
  fMaxU = __max(fMaxU, fU);
  fMinV = __min(fMinV, fV);
  fMaxV = __max(fMaxV, fV);

}

// We know the min. and max. UV coordinates. Now calculate the lightmap size and
// store it in the passed pointers
*pWidth = (unsigned int) ceil(fMaxU / 16.0f) -
(unsigned int) floor(fMinU / 16.0f) + 1;
*pHeight = (unsigned int) ceil(fMaxV / 16.0f) -
(unsigned int) floor(fMinV / 16.0f) + 1;

// Calculate the average texture coordinate
*fMidFaceU = (fMinU + fMaxU) / 2.0f;
*fMidFaceV = (fMinV + fMaxV) / 2.0f;

// Quake 2 BSP don’t allow lightmaps larger than 16x16
if (*pWidth > 16)
*pWidth = 16;
if (*pHeight > 16)
*pHeight = 16;
}

bool CBSP::LoadTextures(BSP_Texture *&pHeadNode, PSTR pszTexturePath, PSTR pszPalettePath)
{
////////////////////////////////////////////////////////////////////////
// Load texture information and store the in the linked-list
////////////////////////////////////////////////////////////////////////

char szTexturePath[_MAX_PATH]; // Buffer to store the full path of a texture
unsigned int i;
bool bAdd = true;
BSP_Texture *pCurTexture = 0, *pTex = 0;

assert(m_pTexInfos);

CProgressWindow::SetTask(“Loading Textures…”);

// Allocate memory for the head node of the texture linked-list
pHeadNode = new BSP_Texture;
pHeadNode->pNextTexture = 0;

// Load all textures and add them to the list
for (i=0; i<m_sCount.iTexInfoCount; i++)
{
// Display status
CProgressWindow::SetProgress(i * 100 / m_sCount.iTexInfoCount);

  // Is the texture a new texture ?
  pTex = pHeadNode;
  bAdd = true;
  do
  {
  	assert(pTex);

  	// Is the current texture equal to the one we are about to add ?
  	if (_stricmp(pTex->szTextureName, m_pTexInfos[i].szTextureName) == 0)
  	{
  		// Texture already exists
  		bAdd = false;
  		break;
  	}

  	// Advance to the next texture
  	pTex = pTex->pNextTexture;

  } while (pTex);

  // Skip this entry if we already added the texture
  if (!bAdd)
  	continue;

  // First take the base path of all textures
  szTexturePath[0] = '\0';
  strcpy(szTexturePath, pszTexturePath);

  // Add slash to the texture path (if needed)
  if (szTexturePath[strlen(szTexturePath)] != '/' &#0124; &#0124;
  	szTexturePath[strlen(szTexturePath)] != '\\')
  {
  	strcat(szTexturePath, "/");
  }

  // Add the relative texture pathname
  strcat(szTexturePath, m_pTexInfos[i].szTextureName);

  // Add the .WAL extension
  strcat(szTexturePath, ".WAL");

  // Is this the first node ?
  if (i == 0)
  {
  	// Fill the first node

  	// Load the texture
  	if (!pHeadNode->cTexure.LoadWALTexture
  		(szTexturePath, pszPalettePath))
  		return false;

  	// Store the texture name in the linked-list
  	strcpy(pHeadNode->szTextureName, 
  		m_pTexInfos[i].szTextureName);

  	// Set current node pointer to the first node
  	pCurTexture = pHeadNode;
  }
  else
  {
  	// Add a node

  	assert(pCurTexture);

  	// Allocate memory for the node
  	pCurTexture->pNextTexture = new BSP_Texture;

  	// Save the new node in the current node pointer
  	pCurTexture = pCurTexture->pNextTexture;

  	// Load the texture
  	if (!pCurTexture->cTexure.LoadWALTexture
  		(szTexturePath, pszPalettePath))
  		return false;

  	// Store the texture name in the linked-list
  	strcpy(pCurTexture->szTextureName, 
  		m_pTexInfos[i].szTextureName);

  	// Set the next node pointer to zero
  	pCurTexture->pNextTexture = 0;
  }

}

return true;
}

bool CBSP::SaveRead(void *buffer, size_t size, size_t count, FILE *stream)
{
////////////////////////////////////////////////////////////////////////
// Performs a normal fread operations and makes sure that all items
// have been read
////////////////////////////////////////////////////////////////////////

size_t iReadCount;

// Perform the fread() call
iReadCount = fread(buffer, size, count, stream);

// Verify the number of succesfully read items
if (iReadCount == count)
{
return true;
}
else
{
// Insufficient amount of items read
assert(iReadCount == count);
return false;
}
}

void CBSP::SeekToLump(unsigned int iLumpIndex, BSP_Header *pHeader, FILE *hFile)
{
////////////////////////////////////////////////////////////////////////
// Place the file pointer at the start of a given lump
////////////////////////////////////////////////////////////////////////

int iSeekReturn;

assert(pHeader);
assert(hFile);

iSeekReturn = fseek(hFile, pHeader->Lump[iLumpIndex].iOffset, SEEK_SET);

assert(iSeekReturn == 0);
}

void CBSP::GetItemCount(BSP_Header *pHeader, BSP_LumpCount *pLumpCount)
{
////////////////////////////////////////////////////////////////////////
// Calculate the count of items in a lump
////////////////////////////////////////////////////////////////////////

// Initialize and verify data
assert(pHeader);
assert(pLumpCount);
memset(pLumpCount, 0, sizeof(BSP_LumpCount));

// Calculate the amount of vertices
pLumpCount->iVertexCount =
pHeader->Lump[L_VERTICES].iLength / sizeof(Point3f);
assert(pLumpCount->iVertexCount > 0);

// Calculate the amount of edges
pLumpCount->iEdgeCount =
pHeader->Lump[L_EDGES].iLength / (sizeof(BSP_Edge));
assert(pLumpCount->iEdgeCount > 0);

// Calculate the amount of faces
pLumpCount->iFaceCount =
pHeader->Lump[L_FACES].iLength / sizeof(BSP_Face);
assert(pLumpCount->iFaceCount > 0);

// Calculate the amount of face edges
pLumpCount->iFaceEdgeCount =
pHeader->Lump[L_FACE_EDGE_TABLE].iLength / sizeof(long int);
assert(pLumpCount->iFaceEdgeCount > 0);

// Calculate the amount of planes
pLumpCount->iPlaneCount =
pHeader->Lump[L_PLANES].iLength / sizeof(BSP_Plane);
assert(pLumpCount->iPlaneCount > 0);

// Calculate the amount of nodes
pLumpCount->iNodeCount =
pHeader->Lump[L_NODES].iLength / sizeof(BSP_Node);
assert(pLumpCount->iNodeCount > 0);

// Calculate the amount of leaves
pLumpCount->iLeafCount =
pHeader->Lump[L_LEAVES].iLength / sizeof(BSP_Leaf);
assert(pLumpCount->iLeafCount > 0);

// Calculate the amount of leaf faces
pLumpCount->iLeafFaceCount =
pHeader->Lump[L_LEAF_FACE_TABLE].iLength / sizeof(short int);
assert(pLumpCount->iLeafFaceCount > 0);

// Calculate the amount of texture informations
pLumpCount->iTexInfoCount =
pHeader->Lump[L_TEXTURE_INFORMATION].iLength / sizeof(BSP_TexInfo);
assert(pLumpCount->iTexInfoCount > 0);
}