This section of the archives stores flipcode's complete Developer Toolbox collection, featuring a variety of mini-articles and source code contributions from our readers.

 

  Simple Quake3 BSP Loader
  Submitted by



I submit a simple Quake3 BSP loader. I made it after reading a Q3 Rendering Map article published on flipcode. It loads all the neccesary lumps of the BSP Quake3 format. I developed it with a minimal interface in order to integrate it easily in personal projects. Have a nice day :)
Nicolas Baudrey.

Currently browsing [SimpleQ3Loader.zip] (64,043 bytes) - [Q3Loader.cpp] - (24,801 bytes)

/**
 * Simple Q3 Map loader.
 * 
 * For comments or bug reports, contact : nicolas.baudrey@wanadoo.fr
 */

#include "Q3Loader.h"

#pragma pack(push, Q3LOADER_H) #include <cstdio> #pragma pack(pop, Q3LOADER_H)

/** * Check if the header of the map is valid. * * @param pMap The map to test. * * @return True if the map is valid, false otherwise. */ bool isValid(const TMapQ3& pMap) { // Check if the header is equal to ID Software Magic Number. if (strncmp(pMap.mHeader.mMagicNumber, cMagicNumber.c_str(), 4) != 0) { return false; }

// Check if the version number is equal to the Q3 map. if (pMap.mHeader.mVersion != cVersion) { return false; }

return true; }

/** * Read the header of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ bool readHeader(FILE* pFile, TMapQ3& pMap) { fread(&pMap.mHeader, 1, sizeof(THeader), pFile);

return isValid(pMap); }

/** * Read the texture lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readTexture(FILE* pFile, TMapQ3& pMap) { int lNbTextures = pMap.mHeader.mLumpes[cTextureLump].mLength / sizeof(TTexture);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cTextureLump].mOffset, SEEK_SET);

for (int lTextureCounter = 0; lTextureCounter < lNbTextures; ++lTextureCounter) { TTexture lTexture; fread(&lTexture, 1, sizeof(TTexture), pFile);

pMap.mTextures.push_back(lTexture); } }

/** * Read the entity lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readEntity(FILE* pFile, TMapQ3& pMap) { // Set the entity size. pMap.mEntity.mSize = pMap.mHeader.mLumpes[cEntityLump].mLength; // Allocate the entity buffer. pMap.mEntity.mBuffer = new char[pMap.mEntity.mSize];

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cEntityLump].mOffset, SEEK_SET); // Read the buffer. fread(pMap.mEntity.mBuffer, pMap.mEntity.mSize, sizeof(char), pFile); };

/** * Read the plane lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readPlane(FILE* pFile, TMapQ3& pMap) { int lNbPlanes = pMap.mHeader.mLumpes[cPlaneLump].mLength / sizeof(TPlane);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cPlaneLump].mOffset, SEEK_SET);

for (int lPlaneCounter = 0; lPlaneCounter < lNbPlanes; ++lPlaneCounter) { TPlane lPlane; fread(&lPlane, 1, sizeof(TPlane), pFile);

pMap.mPlanes.push_back(lPlane); } }

/** * Read the node lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readNode(FILE* pFile, TMapQ3& pMap) { int lNbNodes = pMap.mHeader.mLumpes[cNodeLump].mLength / sizeof(TNode);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cNodeLump].mOffset, SEEK_SET);

for (int lNodeCounter = 0; lNodeCounter < lNbNodes; ++lNodeCounter) { TNode lNode; fread(&lNode, 1, sizeof(TNode), pFile);

pMap.mNodes.push_back(lNode); } }

/** * Read the leaf lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readLeaf(FILE* pFile, TMapQ3& pMap) { int lNbLeaves = pMap.mHeader.mLumpes[cLeafLump].mLength / sizeof(TLeaf);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cLeafLump].mOffset, SEEK_SET);

for (int lLeafCounter = 0; lLeafCounter < lNbLeaves; ++lLeafCounter) { TLeaf lLeaf; fread(&lLeaf, 1, sizeof(TLeaf), pFile);

pMap.mLeaves.push_back(lLeaf); } }

/** * Read the leafface lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readLeafFace(FILE* pFile, TMapQ3& pMap) { int lNbLeafFaces = pMap.mHeader.mLumpes[cLeafFaceLump].mLength / sizeof(TLeafFace);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cLeafFaceLump].mOffset, SEEK_SET);

for (int lLeafFaceCounter = 0; lLeafFaceCounter < lNbLeafFaces; ++lLeafFaceCounter) { TLeafFace lLeafFace; fread(&lLeafFace, 1, sizeof(TLeafFace), pFile);

pMap.mLeafFaces.push_back(lLeafFace); } }

/** * Read the leafbrush lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readLeafBrush(FILE* pFile, TMapQ3& pMap) { int lNbLeafBrushes = pMap.mHeader.mLumpes[cLeafBrushLump].mLength / sizeof(TLeafBrush);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cLeafBrushLump].mOffset, SEEK_SET);

for (int lLeafBrusheCounter = 0; lLeafBrusheCounter < lNbLeafBrushes; ++lLeafBrusheCounter) { TLeafBrush lLeafBrush; fread(&lLeafBrush, 1, sizeof(TLeafBrush), pFile);

pMap.mLeafBrushes.push_back(lLeafBrush); } }

/** * Read the model lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readModel(FILE* pFile, TMapQ3& pMap) { int lNbModels = pMap.mHeader.mLumpes[cModelLump].mLength / sizeof(TModel);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cModelLump].mOffset, SEEK_SET);

for (int lModelCounter = 0; lModelCounter < lNbModels; ++lModelCounter) { TModel lModel; fread(&lModel, 1, sizeof(TModel), pFile);

pMap.mModels.push_back(lModel); } }

/** * Read the brush lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readBrush(FILE* pFile, TMapQ3& pMap) { int lNbBrushes = pMap.mHeader.mLumpes[cBrushLump].mLength / sizeof(TBrush);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cBrushLump].mOffset, SEEK_SET);

for (int lBrusheCounter = 0; lBrusheCounter < lNbBrushes; ++lBrusheCounter) { TBrush lBrush; fread(&lBrush, 1, sizeof(TBrush), pFile);

pMap.mBrushes.push_back(lBrush); } }

/** * Read the brush side lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readBrushSide(FILE* pFile, TMapQ3& pMap) { int lNbBrushSides = pMap.mHeader.mLumpes[cBrushSideLump].mLength / sizeof(TBrushSide);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cBrushSideLump].mOffset, SEEK_SET);

for (int lBrushSideCounter = 0; lBrushSideCounter < lNbBrushSides; ++lBrushSideCounter) { TBrushSide lBrushSide; fread(&lBrushSide, 1, sizeof(TBrushSide), pFile);

pMap.mBrushSides.push_back(lBrushSide); } }

/** * Read the vertex lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readVertex(FILE* pFile, TMapQ3& pMap) { int lNbVertices = pMap.mHeader.mLumpes[cVertexLump].mLength / sizeof(TVertex);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cVertexLump].mOffset, SEEK_SET);

for (int lVerticeCounter = 0; lVerticeCounter < lNbVertices; ++lVerticeCounter) { TVertex lVertex; fread(&lVertex, 1, sizeof(TVertex), pFile);

pMap.mVertices.push_back(lVertex); } }

/** * Read the meshvert lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readMeshVert(FILE* pFile, TMapQ3& pMap) { int lNbMeshVertices = pMap.mHeader.mLumpes[cMeshVertLump].mLength / sizeof(TMeshVert);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cMeshVertLump].mOffset, SEEK_SET);

for (int lVerticeCounter = 0; lVerticeCounter < lNbMeshVertices; ++lVerticeCounter) { TMeshVert lMeshVertex; fread(&lMeshVertex, 1, sizeof(TMeshVert), pFile);

pMap.mMeshVertices.push_back(lMeshVertex); } }

/** * Read the effect lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readEffect(FILE* pFile, TMapQ3& pMap) { int lNbEffects = pMap.mHeader.mLumpes[cEffectLump].mLength / sizeof(TEffect);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cEffectLump].mOffset, SEEK_SET);

for (int lEffectCounter = 0; lEffectCounter < lNbEffects; ++lEffectCounter) { TEffect lEffect; fread(&lEffect, 1, sizeof(TEffect), pFile);

pMap.mEffects.push_back(lEffect); } }

/** * Read the face lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readFace(FILE* pFile, TMapQ3& pMap) { int lNbFaces = pMap.mHeader.mLumpes[cFaceLump].mLength / sizeof(TFace);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cFaceLump].mOffset, SEEK_SET);

for (int lFaceCounter = 0; lFaceCounter < lNbFaces; ++lFaceCounter) { TFace lFace; fread(&lFace, 1, sizeof(TFace), pFile);

pMap.mFaces.push_back(lFace); } }

/** * Read the effect lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readLightMap(FILE* pFile, TMapQ3& pMap) { int lNbLightMaps = pMap.mHeader.mLumpes[cLightMapLump].mLength / sizeof(TLightMap);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cLightMapLump].mOffset, SEEK_SET);

for (int lLightMapCounter = 0; lLightMapCounter < lNbLightMaps; ++lLightMapCounter) { TLightMap lLightMap; fread(&lLightMap, 1, sizeof(TLightMap), pFile);

pMap.mLightMaps.push_back(lLightMap); } }

/** * Read the effect lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readLightVol(FILE* pFile, TMapQ3& pMap) { int lNbLightVols = pMap.mHeader.mLumpes[cLightVolLump].mLength / sizeof(TLightVol);

// Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cLightVolLump].mOffset, SEEK_SET);

for (int lLightVolCounter = 0; lLightVolCounter < lNbLightVols; ++lLightVolCounter) { TLightVol lLightVol; fread(&lLightVol, 1, sizeof(TLightVol), pFile);

pMap.mLightVols.push_back(lLightVol); } }

/** * Read the effect lump of the Q3 map. * * @param pFile The stream on the Q3 file data. * @param pMap The map structure to fill. */ void readVisData(FILE* pFile, TMapQ3& pMap) { // Go to the start of the chunk. fseek(pFile, pMap.mHeader.mLumpes[cVisDataLump].mOffset, SEEK_SET);

fread(&pMap.mVisData.mNbClusters, 1, sizeof(int), pFile); fread(&pMap.mVisData.mBytesPerCluster, 1, sizeof(int), pFile);

// Allocate the buffer. int lBufferSize = pMap.mVisData.mNbClusters * pMap.mVisData.mBytesPerCluster; pMap.mVisData.mBuffer = new unsigned char[lBufferSize];

fread(pMap.mVisData.mBuffer, lBufferSize, sizeof(unsigned char), pFile); }

/** * Dump all the Q3 map in a text file. * Must be used only for debug purpose. * * @param pFile The file to dump the informations. * @param pMap The Q3 map to dump in string. */ void debugInformations(const TMapQ3& pMap, FILE* pFile) { // Check if the given stream is valid. if (! pFile) { printf("debugInformations :: Invalid stream handle.\n"); return ; }

// Check if the map is valid. if (! isValid(pMap)) { printf("debugInformations :: Invalid Q3 map header.\n"); return; }

fprintf(pFile, "********* Header *********\n"); fprintf(pFile, "Magic Number : %s\n", pMap.mHeader.mMagicNumber); fprintf(pFile, "Version : %d\n", pMap.mHeader.mVersion); for (int lLumpCounter = 0; lLumpCounter < 17; ++lLumpCounter) { fprintf(pFile, "Lump %d\n", lLumpCounter); fprintf(pFile, "\tOffset : %d\n", pMap.mHeader.mLumpes[lLumpCounter].mOffset); fprintf(pFile, "\tLength : %d\n", pMap.mHeader.mLumpes[lLumpCounter].mLength); } fprintf(pFile, "\n"); fprintf(pFile, "********* Entity Lump *********\n"); fprintf(pFile, "Size : %d\n", pMap.mEntity.mSize); if (pMap.mEntity.mSize != 0) { fprintf(pFile, "Buffer : %s\n", pMap.mEntity.mBuffer); } fprintf(pFile, "\n");

fprintf(pFile, "********* Texture Lump *********\n"); for (int lTextureCounter = 0; lTextureCounter < pMap.mTextures.size(); ++lTextureCounter) { fprintf(pFile, "Texture %d\n", lTextureCounter); fprintf(pFile, "\tName : %s\n", pMap.mTextures[lTextureCounter].mName); fprintf(pFile, "\tFlags : %d\n", pMap.mTextures[lTextureCounter].mFlags); fprintf(pFile, "\tContents : %d\n", pMap.mTextures[lTextureCounter].mContents); } fprintf(pFile, "\n");

fprintf(pFile, "********* Plane Lump *********\n"); for (int lPlaneCounter = 0; lPlaneCounter < pMap.mPlanes.size(); ++lPlaneCounter) { fprintf(pFile, "Plane %d\n", lPlaneCounter); fprintf(pFile, "\tNormal : %f %f %f\n", pMap.mPlanes[lPlaneCounter].mNormal[0], pMap.mPlanes[lPlaneCounter].mNormal[1], pMap.mPlanes[lPlaneCounter].mNormal[2]); fprintf(pFile, "\tDistance : %f\n", pMap.mPlanes[lPlaneCounter].mDistance); } fprintf(pFile, "\n");

fprintf(pFile, "********* Node Lump *********\n"); for (int lNodeCounter = 0; lNodeCounter < pMap.mNodes.size(); ++lNodeCounter) { fprintf(pFile, "Node %d\n", lNodeCounter); fprintf(pFile, "\tPlane index : %d\n", pMap.mNodes[lNodeCounter].mPlane); fprintf(pFile, "\tChildren index : %d %d\n", pMap.mNodes[lNodeCounter].mChildren[0], pMap.mNodes[lNodeCounter].mChildren[1]); fprintf(pFile, "\tMin Bounding Box : %d %d %d\n", pMap.mNodes[lNodeCounter].mMins[0], pMap.mNodes[lNodeCounter].mMins[1], pMap.mNodes[lNodeCounter].mMins[2]); fprintf(pFile, "\tMax Bounding Box : %d %d %d\n", pMap.mNodes[lNodeCounter].mMaxs[0], pMap.mNodes[lNodeCounter].mMaxs[1], pMap.mNodes[lNodeCounter].mMaxs[2]); } fprintf(pFile, "\n");

fprintf(pFile, "********* Leaf Lump *********\n"); for (int lLeafCounter = 0; lLeafCounter < pMap.mLeaves.size(); ++lLeafCounter) { fprintf(pFile, "Leaf %d\n", lLeafCounter); fprintf(pFile, "\tCluster %d\n", pMap.mLeaves[lLeafCounter].mCluster); fprintf(pFile, "\tMin Bounding Box : %d %d %d\n", pMap.mLeaves[lLeafCounter].mMins[0], pMap.mLeaves[lLeafCounter].mMins[1], pMap.mLeaves[lLeafCounter].mMins[2]); fprintf(pFile, "\tMax Bounding Box : %d %d %d\n", pMap.mLeaves[lLeafCounter].mMaxs[0], pMap.mLeaves[lLeafCounter].mMaxs[1], pMap.mLeaves[lLeafCounter].mMaxs[2]); fprintf(pFile, "\tLeafFace %d\n", pMap.mLeaves[lLeafCounter].mLeafFace); fprintf(pFile, "\tNb LeafFace %d\n", pMap.mLeaves[lLeafCounter].mNbLeafFaces); fprintf(pFile, "\tLeafBrush %d\n", pMap.mLeaves[lLeafCounter].mLeafBrush); fprintf(pFile, "\tNb LeafBrushes %d\n", pMap.mLeaves[lLeafCounter].mNbLeafBrushes); } fprintf(pFile, "\n");

fprintf(pFile, "********* LeafFace Lump *********\n"); for (int lLeafFaceCounter = 0; lLeafFaceCounter < pMap.mLeafFaces.size(); ++lLeafFaceCounter) { fprintf(pFile, "LeafFace %d\n", lLeafFaceCounter); fprintf(pFile, "\tFaceIndex %d\n", pMap.mLeafFaces[lLeafFaceCounter].mFaceIndex); } fprintf(pFile, "\n");

fprintf(pFile, "********* LeafBrush Lump *********\n"); for (int lLeafBrushCounter = 0; lLeafBrushCounter < pMap.mLeafBrushes.size(); ++lLeafBrushCounter) { fprintf(pFile, "LeafBrush %d\n", lLeafBrushCounter); fprintf(pFile, "\tBrushIndex %d\n", pMap.mLeafBrushes[lLeafBrushCounter].mBrushIndex); } fprintf(pFile, "\n");

fprintf(pFile, "********* Model Lump *********\n"); for (int lModelCounter = 0; lModelCounter < pMap.mModels.size(); ++lModelCounter) { fprintf(pFile, "Model %d\n", lModelCounter); fprintf(pFile, "\tMin Bounding Box : %d %d %d\n", pMap.mModels[lModelCounter].mMins[0], pMap.mModels[lModelCounter].mMins[1], pMap.mModels[lModelCounter].mMins[2]); fprintf(pFile, "\tMax Bounding Box : %d %d %d\n", pMap.mModels[lModelCounter].mMaxs[0], pMap.mModels[lModelCounter].mMaxs[1], pMap.mModels[lModelCounter].mMaxs[2]); fprintf(pFile, "\tFace %d\n", pMap.mModels[lModelCounter].mFace); fprintf(pFile, "\tNbFaces %d\n", pMap.mModels[lModelCounter].mNbFaces); fprintf(pFile, "\tBrush %d\n", pMap.mModels[lModelCounter].mBrush); fprintf(pFile, "\tNbBrushes %d\n", pMap.mModels[lModelCounter].mNBrushes); } fprintf(pFile, "\n");

fprintf(pFile, "********* Brush Lump *********\n"); for (int lBrushCounter = 0; lBrushCounter < pMap.mBrushes.size(); ++lBrushCounter) { fprintf(pFile, "Brush %d\n", lModelCounter); fprintf(pFile, "\tBrushSide %d\n", pMap.mBrushes[lBrushCounter].mBrushSide); fprintf(pFile, "\tNbBrushSides %d\n", pMap.mBrushes[lBrushCounter].mNbBrushSides); fprintf(pFile, "\tTextureIndex %d\n", pMap.mBrushes[lBrushCounter].mTextureIndex); } fprintf(pFile, "\n");

fprintf(pFile, "********* BrushSide Lump *********\n"); for (int lBrushSideCounter = 0; lBrushSideCounter < pMap.mBrushSides.size(); ++lBrushSideCounter) { fprintf(pFile, "BrushSide %d\n", lBrushSideCounter); fprintf(pFile, "\tPlaneIndex %d\n", pMap.mBrushSides[lBrushSideCounter].mPlaneIndex); fprintf(pFile, "\tTextureIndex %d\n", pMap.mBrushSides[lBrushSideCounter].mTextureIndex); } fprintf(pFile, "\n");

fprintf(pFile, "********* Vertex Lump *********\n"); for (int lVertexCounter = 0; lVertexCounter < pMap.mVertices.size(); ++lVertexCounter) { fprintf(pFile, "Vertex %d\n", lVertexCounter); fprintf(pFile, "\tPosition : %f %f %f\n", pMap.mVertices[lVertexCounter].mPosition[0], pMap.mVertices[lVertexCounter].mPosition[1], pMap.mVertices[lVertexCounter].mPosition[2]); fprintf(pFile, "\tTexCoord0 : %f %f\n", pMap.mVertices[lVertexCounter].mTexCoord[0], pMap.mVertices[lVertexCounter].mTexCoord[1]); fprintf(pFile, "\tTexCoord0 : %f %f\n", pMap.mVertices[lVertexCounter].mTexCoord[2], pMap.mVertices[lVertexCounter].mTexCoord[3]); fprintf(pFile, "\tNormal : %f %f %f\n", pMap.mVertices[lVertexCounter].mNormal[0], pMap.mVertices[lVertexCounter].mNormal[1], pMap.mVertices[lVertexCounter].mNormal[2]); fprintf(pFile, "\tColor : %d %d %d %d\n", pMap.mVertices[lVertexCounter].mColor[0], pMap.mVertices[lVertexCounter].mColor[1], pMap.mVertices[lVertexCounter].mColor[2], pMap.mVertices[lVertexCounter].mColor[3]); } fprintf(pFile, "\n");

fprintf(pFile, "********* MeshVert Lump *********\n"); for (int lMeshVertCounter = 0; lMeshVertCounter < pMap.mMeshVertices.size(); ++lMeshVertCounter) { fprintf(pFile, "MeshVert %d\n", lMeshVertCounter); fprintf(pFile, "\tVertex Index : %d\n", pMap.mMeshVertices[lMeshVertCounter].mMeshVert); } fprintf(pFile, "\n");

fprintf(pFile, "********* Effect Lump *********\n"); for (int lEffectCounter = 0; lEffectCounter < pMap.mEffects.size(); ++lEffectCounter) { fprintf(pFile, "Effect %d\n", lEffectCounter); fprintf(pFile, "\tName : %s\n", pMap.mEffects[lEffectCounter].mName); fprintf(pFile, "\tBrush : %d\n", pMap.mEffects[lEffectCounter].mBrush); fprintf(pFile, "\tUnknown : %d\n", pMap.mEffects[lEffectCounter].mUnknown); } fprintf(pFile, "\n");

fprintf(pFile, "********* Face Lump *********\n"); for (int lFaceCounter = 0; lFaceCounter < pMap.mFaces.size(); ++lFaceCounter) { fprintf(pFile, "Face %d\n", lFaceCounter); fprintf(pFile, "\tTextureIndex : %d\n", pMap.mFaces[lFaceCounter].mTextureIndex); fprintf(pFile, "\tEffectIndex : %d\n", pMap.mFaces[lFaceCounter].mEffectIndex); fprintf(pFile, "\tType : %d\n", pMap.mFaces[lFaceCounter].mType); fprintf(pFile, "\tVertex : %d\n", pMap.mFaces[lFaceCounter].mVertex); fprintf(pFile, "\tNbVertices : %d\n", pMap.mFaces[lFaceCounter].mNbVertices); fprintf(pFile, "\tMeshVertex : %d\n", pMap.mFaces[lFaceCounter].mMeshVertex); fprintf(pFile, "\tNbMeshVertices : %d\n", pMap.mFaces[lFaceCounter].mNbMeshVertices); fprintf(pFile, "\tLightMapIndex : %d\n", pMap.mFaces[lFaceCounter].mLightmapIndex); fprintf(pFile, "\tLightMapCorner : %d %d\n", pMap.mFaces[lFaceCounter].mLightmapCorner[0], pMap.mFaces[lFaceCounter].mLightmapCorner[1]); fprintf(pFile, "\tLightmapSize : %d %d\n", pMap.mFaces[lFaceCounter].mLightmapSize[0], pMap.mFaces[lFaceCounter].mLightmapSize[1]); fprintf(pFile, "\tLightmapOrigin : %f %f %f\n", pMap.mFaces[lFaceCounter].mLightmapOrigin[0], pMap.mFaces[lFaceCounter].mLightmapOrigin[1], pMap.mFaces[lFaceCounter].mLightmapOrigin[2]); fprintf(pFile, "\tLightmapVecs S : %f %f %f\n", pMap.mFaces[lFaceCounter].mLightmapVecs[0][0], pMap.mFaces[lFaceCounter].mLightmapVecs[0][1], pMap.mFaces[lFaceCounter].mLightmapVecs[0][2]); fprintf(pFile, "\tLightmapVecs T : %f %f %f\n", pMap.mFaces[lFaceCounter].mLightmapVecs[1][0], pMap.mFaces[lFaceCounter].mLightmapVecs[1][1], pMap.mFaces[lFaceCounter].mLightmapVecs[1][2]); fprintf(pFile, "\tNormal : %f %f %f\n", pMap.mFaces[lFaceCounter].mNormal[0], pMap.mFaces[lFaceCounter].mNormal[1], pMap.mFaces[lFaceCounter].mNormal[2]); fprintf(pFile, "\tPatchSize : %d %d\n", pMap.mFaces[lFaceCounter].mPatchSize[0], pMap.mFaces[lFaceCounter].mPatchSize[1]); } fprintf(pFile, "\n");

fprintf(pFile, "********* LightMap Lump *********\n"); fprintf(pFile, "NbLightMaps %d\n", pMap.mLightMaps.size()); fprintf(pFile, "\n");

fprintf(pFile, "********* LightVol Lump *********\n"); for (int lLightVolCounter = 0; lLightVolCounter < pMap.mLightVols.size(); ++lLightVolCounter) { fprintf(pFile, "LightVol %d\n", lLightVolCounter); fprintf(pFile, "\tAmbient : %d %d %d\n", pMap.mLightVols[lLightVolCounter].mAmbient[0], pMap.mLightVols[lLightVolCounter].mAmbient[1], pMap.mLightVols[lLightVolCounter].mAmbient[2]); fprintf(pFile, "\tDirectional : %d %d %d\n", pMap.mLightVols[lLightVolCounter].mDirectional[0], pMap.mLightVols[lLightVolCounter].mDirectional[1], pMap.mLightVols[lLightVolCounter].mDirectional[2]); fprintf(pFile, "\tDir : %d %d\n", pMap.mLightVols[lLightVolCounter].mDir[0], pMap.mLightVols[lLightVolCounter].mDir[1]); } fprintf(pFile, "\n");

fprintf(pFile, "********* VisData Lump *********\n"); fprintf(pFile, "\tNbCluster %d\n", pMap.mVisData.mNbClusters); fprintf(pFile, "\tBytePerCluster %d\n", pMap.mVisData.mBytesPerCluster); fprintf(pFile, "\n"); }

/** * Read the map. * * @param pFilename The filename of the map to read. * * @return true if the loading successed, false otherwise. */ bool readMap(const std::string& pFilename, TMapQ3& pMap) { // Open the file. FILE* lFile = fopen(pFilename.c_str(), "r+b");

// Check if the file exists. if (! lFile) { printf("readMap :: Invalid stream handle.\n"); return false; }

// Read the header. if (readHeader(lFile, pMap) == false) { printf("readMap :: Invalid Q3 map header.\n"); return false; }

// Read the entity lump. readEntity(lFile, pMap);

// Read the texture lump. readTexture(lFile, pMap);

// Read the plane lump. readPlane(lFile, pMap);

// Read the node lump. readNode(lFile, pMap);

// Read the leaf lump. readLeaf(lFile, pMap);

// Read the leaf face lump. readLeafFace(lFile, pMap);

// Read the leaf brush lump. readLeafBrush(lFile, pMap);

// Read the model lump. readModel(lFile, pMap);

// Read the brush lump. readBrush(lFile, pMap);

// Read the brushside lump. readBrushSide(lFile, pMap);

// Read the vertex lump. readVertex(lFile, pMap);

// Read the meshvert lump. readMeshVert(lFile, pMap);

// Read the effect lump. readEffect(lFile, pMap);

// Read the face lump. readFace(lFile, pMap);

// Read the lightmap lump. readLightMap(lFile, pMap);

// Read the lightvol lump. readLightVol(lFile, pMap);

// read the visdata lump. readVisData(lFile, pMap); // Close the file. fclose(lFile);

return true; };

/** * Free all the datas of the map. *tLightmapVecs * @param pMap The Q3 map to free. */ void freeMap(TMapQ3& pMap) { if (pMap.mEntity.mBuffer) { delete [] pMap.mEntity.mBuffer; pMap.mEntity.mBuffer = NULL; }

if (pMap.mVisData.mBuffer) { delete [] pMap.mVisData.mBuffer; pMap.mVisData.mBuffer = NULL; }

pMap.mTextures.clear(); pMap.mPlanes.clear(); pMap.mNodes.clear(); pMap.mLeaves.clear(); pMap.mLeafFaces.clear(); pMap.mLeafBrushes.clear(); pMap.mModels.clear(); pMap.mBrushes.clear(); pMap.mBrushSides.clear(); pMap.mVertices.clear(); pMap.mMeshVertices.clear(); pMap.mEffects.clear(); pMap.mFaces.clear(); pMap.mLightMaps.clear(); pMap.mLightVols.clear(); }


Currently browsing [SimpleQ3Loader.zip] (64,043 bytes) - [Q3Loader.h] - (10,709 bytes)

/**
 * Simple Q3 Map loader.
 * 
 * For comments or bug reports, contact : nicolas.baudrey@wanadoo.fr
 */
#ifndef Q3LOADER_H
#define Q3LOADER_H

#pragma pack(push, Q3LOADER_H) #include <vector> #pragma pack(pop, Q3LOADER_H)

#pragma pack(push, Q3LOADER_H) #include <string> #pragma pack(pop, Q3LOADER_H)

/** * Description of a lump. * */ struct TLump { int mOffset; // Offset to start of lump, relative to beginning of file. int mLength; // Length of lump. Always a multiple of 4. };

/** * Header of the Q3 map. */ struct THeader { char mMagicNumber[4]; // Magic number. Always "IBSP". int mVersion; // Version number 0x2e for the BSP files distributed with Quake 3. TLump mLumpes[17]; // Lump directory, seventeen entries. };

/** * Entity of the Q3 map. * The entities lump stores game-related map information, including information about the map name, weapons, health, armor, triggers, spawn points, lights, and .md3 models to be placed in the map. */ struct TEntity { int mSize; // Size of the description. char* mBuffer; // Entity descriptions, stored as a string. };

/** * Texture of the Q3 map. * The textures lump stores information about surfaces and volumes, which are in turn associated with faces, brushes, and brushsides. */ struct TTexture { char mName[64]; // Texture name. int mFlags; // Surface flags. int mContents; // Content flags. };

/** * Plane of the Q3 map. * The planes lump stores a generic set of planes that are in turn referenced by nodes and brushsides. */ struct TPlane { float mNormal[3]; // Plane normal. float mDistance; // Distance from origin to plane along normal. };

/** * Node of the Q3 map. * The nodes lump stores all of the nodes in the map's BSP tree. */ struct TNode { int mPlane; // Plane index. int mChildren[2]; // Children indices. Negative numbers are leaf indices: -(leaf+1). int mMins[3]; // Integer bounding box min coord. int mMaxs[3]; // Integer bounding box max coord. };

/** * Leaf of the Q3 map. * The leafs lump stores the leaves of the map's BSP tree. */ struct TLeaf { int mCluster; // Visdata cluster index. int mArea; // Areaportal area. int mMins[3]; // Integer bounding box min coord. int mMaxs[3]; // Integer bounding box max coord. int mLeafFace; // First leafface for leaf. int mNbLeafFaces; // Number of leaffaces for leaf. int mLeafBrush; // First leafbrush for leaf. int mNbLeafBrushes; // Number of leafbrushes for leaf. };

/** * LeafFace of the Q3 map. * The leaffaces lump stores lists of face indices, with one list per leaf. */ struct TLeafFace { int mFaceIndex; // Face index. };

/** * Leaf Brush of the Q3 map. * The leafbrushes lump stores lists of brush indices, with one list per leaf. */ struct TLeafBrush { int mBrushIndex; // Brush index. };

/** * Model of the Q3 map. * The models lump describes rigid groups of world geometry. */ struct TModel { float mMins[3]; // Bounding box min coord. float mMaxs[3]; // Bounding box max coord. int mFace; // First face for model. int mNbFaces; // Number of faces for model. int mBrush; // First brush for model. int mNBrushes; // Number of brushes for model. };

/** * Brush of the Q3 map. * The brushes lump stores a set of brushes, which are in turn used for collision detection. */ struct TBrush { int mBrushSide; // First brushside for brush. int mNbBrushSides; // Number of brushsides for brush. int mTextureIndex; // Texture index. };

/** * BrushSide of the Q3 map. * The brushsides lump stores descriptions of brush bounding surfaces. */ struct TBrushSide { int mPlaneIndex; // Plane index. int mTextureIndex; // Texture index. };

/** * Vertex of the Q3 map. * The vertexes lump stores lists of vertices used to describe faces. */ struct TVertex { float mPosition[3]; // Vertex position. float mTexCoord[2][2]; // Vertex texture coordinates. 0 = Surface, 1 = Lightmap. float mNormal[3]; // Vertex normal. unsigned char mColor[4]; // Vertex color (RGBA). };

/** * MeshVert of the Q3 map. * The meshverts lump stores lists of vertex offsets, used to describe generalized triangle meshes. */ struct TMeshVert { int mMeshVert; // Vertex index offset, relative to first vertex of corresponding face. };

/** * Effect of the Q3 map. * The effects lump stores references to volumetric shaders (typically fog) which affect the rendering of a particular group of faces. */ struct TEffect { char mName[64]; // Effect shader. int mBrush; // Brush that generated this effect. int mUnknown; // Always 5, except in q3dm8, which has one effect with -1. };

/** * Face of the Q3 map. * The faces lump stores information used to render the surfaces of the map. */ struct TFace { int mTextureIndex; // Texture index. int mEffectIndex; // Index into lump 12 (Effects), or -1. int mType; // Face type. 1 = Polygon, 2 = Patch, 3 = Mesh, 4 = Billboard. int mVertex; // Index of first vertex. int mNbVertices; // Number of vertices. int mMeshVertex; // Index of first meshvert. int mNbMeshVertices; // Number of meshverts. int mLightmapIndex; // Lightmap index. int mLightmapCorner[2]; // Corner of this face's lightmap image in lightmap. int mLightmapSize[2]; // Size of this face's lightmap image in lightmap. float mLightmapOrigin[3]; // World space origin of lightmap. float mLightmapVecs[2][3];// World space lightmap s and t unit vectors. float mNormal[3]; // Surface normal. int mPatchSize[2]; // Patch dimensions. };

/** * Lightmap of the Q3 map. * The lightmaps lump stores the light map textures used make surface lighting look more realistic. */ struct TLightMap { unsigned char mMapData[128][128][3];// Lightmap color data. RGB. };

/** * Light volume of the Q3 map. * The lightvols lump stores a uniform grid of lighting information used to illuminate non-map objects. */ struct TLightVol { unsigned char mAmbient[3]; // Ambient color component. RGB. unsigned char mDirectional[3]; // Directional color component. RGB. unsigned char mDir[2]; // Direction to light. 0=phi, 1=theta. };

/** * The Visibility data of the Q3 map. * The visdata lump stores bit vectors that provide cluster-to-cluster visibility information. */ struct TVisData { int mNbClusters; // The number of clusters int mBytesPerCluster; // Bytes (8 bits) in the cluster's bitset unsigned char* mBuffer; // Array of bytes holding the cluster vis. };

/** * The Q3 map definition. * The Q3 map is defined by 17 lumps and a header section. */ struct TMapQ3 { THeader mHeader; // Header of the file. TEntity mEntity; // Array of the leaves. std::vector<TTexture> mTextures; // Array of the textures. std::vector<TPlane> mPlanes; // Array of the planes. std::vector<TNode> mNodes; // Array of the nodes. std::vector<TLeaf> mLeaves; // Array of the leaves. std::vector<TLeafFace> mLeafFaces; // Array of the leaf faces. std::vector<TLeafBrush> mLeafBrushes; // Array of the leaf brushes. std::vector<TModel> mModels; // Array of the models. std::vector<TBrush> mBrushes; // Array of the brushes. std::vector<TBrushSide> mBrushSides; // Array of the brush sides. std::vector<TVertex> mVertices; // Array of the vertices. std::vector<TMeshVert> mMeshVertices; // Array of the mesh vertices. std::vector<TEffect> mEffects; // Array of the effects. std::vector<TFace> mFaces; // Array of the faces. std::vector<TLightMap> mLightMaps; // Array of the light maps. std::vector<TLightVol> mLightVols; // Array of the light volumes. TVisData mVisData; // The visibility datas. };

/** * Constant for the ID Software Magic number. */ const std::string cMagicNumber = "IBSP"; /** * Constant for the Q3 Map version. */ const int cVersion = 0x2E; /** * Constant identifier for all the lumps. */ const int cEntityLump = 0x00; // Entities : Game-related object descriptions. const int cTextureLump = 0x01; // Textures : Surface descriptions. const int cPlaneLump = 0x02; // Planes : Planes used by map geometry. const int cNodeLump = 0x03; // Nodes : BSP tree nodes. const int cLeafLump = 0x04; // Leafs : BSP tree leaves. const int cLeafFaceLump = 0x05; // LeafFaces : Lists of face indices, one list per leaf. const int cLeafBrushLump = 0x06; // LeafBrushes Lists of brush indices, one list per leaf. const int cModelLump = 0x07; // Models Descriptions of rigid world geometry in map. const int cBrushLump = 0x08; // Brushes Convex polyhedra used to describe solid space. const int cBrushSideLump = 0x09; // Brushsides Brush surfaces. const int cVertexLump = 0x0A; // Vertexes Vertices used to describe faces. const int cMeshVertLump = 0x0B; // MeshVerts Lists of offsets, one list per mesh. const int cEffectLump = 0x0C; // Effects List of special map effects. const int cFaceLump = 0x0D; // Faces Surface geometry. const int cLightMapLump = 0x0E; // LightMaps Packed lightmap data. const int cLightVolLump = 0x0F; // LightVols Local illumination data. const int cVisDataLump = 0x10; // Visdata Cluster-cluster visibility data. /** * Dump all the Q3 map in a text file. * Must be used only for debug purpose. * * @param pFile The file to dump the informations. * @param pMap The Q3 map to dump in string. */ void debugInformations(const TMapQ3& pMap, FILE* pFile);

/** * Read the map. * * @param pFilename The filename of the map to read. * * @return true if the loading successed, false otherwise. */ bool readMap(const std::string& pFilename, TMapQ3& pMap);

/** * Free all the datas of the map. * * @param pMap The Q3 map to free. */ void freeMap(TMapQ3& pMap);

/** * Check if the header of the map is valid. * * @param pMap The map to test. * * @return True if the map is valid, false otherwise. */ bool isValid(const TMapQ3& pMap);

#endif

Currently browsing [SimpleQ3Loader.zip] (64,043 bytes) - [Q3Loader_Test.cpp] - (322 bytes)

#include "Q3Loader.h"

#pragma pack(push, MAIN) #include <cstdlib> #pragma pack(pop, MAIN)

int main(int pArgc, char** pArgv) { TMapQ3 lMap; readMap("final.bsp", lMap);

FILE* lFile = fopen("final_debug.txt", "w+"); debugInformations(lMap, lFile); fclose(lFile);

freeMap(lMap); return 0; };

The zip file viewer built into the Developer Toolbox made use of the zlib library, as well as the zlibdll source additions.

 

Copyright 1999-2008 (C) FLIPCODE.COM and/or the original content author(s). All rights reserved.
Please read our Terms, Conditions, and Privacy information.