PDA

View Full Version : Quake2 Lightmap Size / Coordinates



Tim
10-15-2000, 02:59 AM
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 = &amp;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(&amp;sHeader, sizeof(BSP_Header), 1, hFile);

// Is the file a BSP file ?
if (sHeader.szMagic[0] != 'I' &amp;#0124; &amp;#0124;
sHeader.szMagic[1] != 'B' &amp;#0124; &amp;#0124;
sHeader.szMagic[2] != 'S' &amp;#0124; &amp;#0124;
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(&amp;sHeader, &amp;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, &amp;sHeader, hFile);
SaveRead(m_pVertices, sizeof(m_pVertices[0]), m_sCount.iVertexCount, hFile);
// Load all edges
SeekToLump(L_EDGES, &amp;sHeader, hFile);
SaveRead(m_pEdges, sizeof(m_pEdges[0]), m_sCount.iEdgeCount, hFile);
// Load all faces
SeekToLump(L_FACES, &amp;sHeader, hFile);
SaveRead(m_pFaces, sizeof(m_pFaces[0]), m_sCount.iFaceCount, hFile);
// Load all face edges
SeekToLump(L_FACE_EDGE_TABLE, &amp;sHeader, hFile);
SaveRead(m_pFaceEdges, sizeof(m_pFaceEdges[0]), m_sCount.iFaceEdgeCount, hFile);
// Load all planes
SeekToLump(L_PLANES, &amp;sHeader, hFile);
SaveRead(m_pPlanes, sizeof(m_pPlanes[0]), m_sCount.iPlaneCount, hFile);
// Load all nodes
SeekToLump(L_NODES, &amp;sHeader, hFile);
SaveRead(m_pNodes, sizeof(m_pNodes[0]), m_sCount.iNodeCount, hFile);
// Load all leaves
SeekToLump(L_LEAVES, &amp;sHeader, hFile);
SaveRead(m_pLeaves, sizeof(m_pLeaves[0]), m_sCount.iLeafCount, hFile);
// Load all leaf faces
SeekToLump(L_LEAF_FACE_TABLE, &amp;sHeader, hFile);
SaveRead(m_pLeafFaces, sizeof(m_pLeafFaces[0]), m_sCount.iLeafFaceCount, hFile);
// Load all texture informations
SeekToLump(L_TEXTURE_INFORMATION, &amp;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, &amp;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, &amp;iLightmapWidth, &amp;iLightmapHeight,
&amp;m_pLightmaps[iCurFace].fMidFaceU, &amp;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
(&amp;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 = &amp;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 *&amp;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)] != '/' &amp;#0124; &amp;#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);
}