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.