Jump to content
  • Advertisement
Sign in to follow this  
jikbar

DirectX BSP loader- index problem

This topic is 5034 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi everybody! I'm working on a Quake3 BSP loader for my FPS game, Assassin. The loader is based on the tutorials on gametutorials.com (only a DirectX version, obviously). The (very few) bsp loaders for directx that i've seen haven't given me much info and seem quite inefficient. My problem is that, after switching to using mesh indices for compatibility, only the first face of my test level (a cubic room) gets drawn and the more complex levels dont get drawn at all! I've gone through the entire loading process and rendering process tons of times just to see that all the data is there and that evrything is as it should be. I really have no clue whats going wrong and im anxious to start working on other parts of the game (by that i mean debugging other things!) Heres the code for CLevelSceneNode.h (the bsp defines are taken directly from the tuts on gametutorials):
[source lng="c++"]
// LevelSceneNode.h: interface for the CLevelSceneNode class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_LEVELSCENENODE_H__3022E221_54D5_11D9_B0E9_CA3E1BC80863__INCLUDED_)
#define AFX_LEVELSCENENODE_H__3022E221_54D5_11D9_B0E9_CA3E1BC80863__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "SceneNode.h"
#include "Camera.h"
#include "LightmapBuffer.h"
#include "BspStructs.h"
#include "Material.h"
#include "Connector.h"

//Handles the loading and displaying of a Quake 3 style BSP
// level. Calling Load() with a valid file name will load all
// the data, create a vertex buffer, load textures and lightmaps,
// create a BSP tree, position the camera and load entities.

class CLevelSceneNode : public CSceneNode  
{
public:
	char* bspEntities;
	uint GammaPixel(int gamma, uchar r, uchar g, uchar b);
	CCamera* cam;
	void Render();
	void CreateEffect(CMaterial& outMat, int effect, int texture, int lightmap);
	bool Load(char* sFile);
	CLevelSceneNode();
	virtual ~CLevelSceneNode();

protected:
	int* bspIndices;
	uint nIndices;
	array<IDirect3DTexture8*> textures;
	array<IDirect3DTexture8*> lightmaps;
	CLightmapBuffer vb;
	void CreateLevel();
	void CreateTexture(tBSPTexture& t);
	void CreateLightmap(tBSPLightmap& lightmap);
	tBSPVertex* bspVerts;
	tBSPLightmap* bspLightmaps;
	uint nLightmaps;
	tBSPTexture* bspTextures;
	uint nTextures;
	uint nFaces;
	tBSPFace* bspFaces;
	uint nVerts;
	tBSPLump bspLumps[kMaxLumps];
	tBSPHeader bspHeader;
	ifstream file;
};

#endif // !defined(AFX_LEVELSCENENODE_H__3022E221_54D5_11D9_B0E9_CA3E1BC80863__INCLUDED_)

Heres the code for CLevelSceneNode.cpp:
// LevelSceneNode.cpp: implementation of the CLevelSceneNode class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "LevelSceneNode.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CLevelSceneNode::CLevelSceneNode()
{
	bspFaces = NULL;
	bspVerts = NULL;
	cam = NULL;
	bspIndices = NULL;
}

CLevelSceneNode::~CLevelSceneNode()
{
	//delete everything that wasnt deleted after Load() (anything useful to Render())
	if(bspFaces)
		delete [] bspFaces;
}

bool CLevelSceneNode::Load(char *sFile)
{
	//get the camera
	cam = &link->GetCam();
	//open the file and call functions to read data
	file.open(sFile);
	if(file.fail())
		return false;
	
	uint i = 0;
	/////////////////Read header data
	//read header
	file.read((char*)&bspHeader, sizeof(tBSPHeader));
	//test header to make sure it's a valid BSP file
	if(bspHeader.strID[0] != 'I' && bspHeader.strID[1] != 'B' &&
		bspHeader.strID[2] != 'S' && bspHeader.strID[3] != 'P')
	{
		file.close();
		return false;
	}
	//make sure its a Quake3 BSP file
	if(bspHeader.version != 0x2e)
	{
		file.close();
		return false;
	}
	//read lumps
	file.read((char*)bspLumps, sizeof(tBSPLump) * kMaxLumps);
	/////////////////Read Polygon data
	//read vertices
	nVerts = bspLumps[kVertices].length / sizeof(tBSPVertex);
	bspVerts = new tBSPVertex[nVerts];
	file.seekg(bspLumps[kVertices].offset);
	file.read((char*)&bspVerts[0], sizeof(tBSPVertex)*nVerts);
	for(i = 0; i < nVerts; ++i)
	{
		//swap Y and Z so Y is up
		Swap(bspVerts.vPosition.y, bspVerts.vPosition.z);
		//flip U coordinate
		bspVerts.vTextureCoord.x *= -1;
	}
	//read faces
	nFaces = bspLumps[kFaces].length / sizeof(tBSPFace);
	bspFaces = new tBSPFace[nFaces];
	file.seekg(bspLumps[kFaces].offset);
	file.read((char*)bspFaces, sizeof(tBSPFace) * nFaces);
	//read texture info
	file.seekg(bspLumps[kTextures].offset);
	nTextures = bspLumps[kTextures].length / sizeof(tBSPTexture);
	bspTextures = new tBSPTexture[nTextures];
	ZeroMemory(bspTextures, sizeof(tBSPTexture) * nTextures);
	file.read((char*)bspTextures, sizeof(tBSPTexture) * nTextures);
	for(i = 0; i < nTextures; ++i)
	{
		CreateTexture(bspTextures);
	}
	//read lightmaps
	nLightmaps = bspLumps[kLightmaps].length / sizeof(tBSPLightmap);
	bspLightmaps = new tBSPLightmap[nLightmaps];
	file.seekg(bspLumps[kLightmaps].offset);
	file.read((char*)bspLightmaps, sizeof(tBSPLightmap) * nLightmaps);
	for(i = 0; i < nLightmaps; ++i)
	{
		CreateLightmap(bspLightmaps);
	}
	//read index data
	file.seekg(bspLumps[kMeshVerts].offset);
	(int)file.tellg();
	nIndices = bspLumps[kMeshVerts].length / sizeof(int);
	bspIndices = new int[nIndices];
	file.read((char*)bspIndices, sizeof(int) * nIndices);
	//we now have enough data to construct an unoptimized level
	//////////////////Read BSP tree data
	
	//////////////////Read entity data
	uint nEntitySize = bspLumps[kEntities].length / sizeof(char);
	bspEntities = new char[nEntitySize + 1];
	strset(bspEntities, 0);
	file.read(bspEntities, nEntitySize);
	
	//////////////////Generate the level
	CreateLevel();
	
	//less confusing to unload all data in one spot!
	delete [] bspTextures;
	delete [] bspLightmaps;
	delete [] bspVerts;
	delete [] bspIndices;
	
	file.close();
	return true;
}

void CLevelSceneNode::CreateTexture(tBSPTexture &t)
{
	//create strings for the possible texture files
	char bmpPath[256] = {0};
	char tgaPath[256] = {0};
	char jpgPath[256] = {0};
	strcpy(bmpPath, t.strName);
	strcpy(tgaPath, t.strName);
	strcpy(jpgPath, t.strName);
	strcat(bmpPath, ".bmp");
	strcat(tgaPath, ".tga");
	strcat(jpgPath, ".jpg");
	IDirect3DTexture8* tex = NULL;
	//check if these exist and check read permission at the same time
	// if the load fails check the other formats
	if(access(bmpPath, 4) > -1)
	{
		link->LoadTexture(bmpPath, (IDirect3DBaseTexture8**)&tex);
	}
	if(access(jpgPath, 4) > -1)
	{
		link->LoadTexture(jpgPath, (IDirect3DBaseTexture8**)&tex);
	}
	if(access(tgaPath, 4) > -1)
	{
		link->LoadTexture(tgaPath, (IDirect3DBaseTexture8**)&tex);
	}
	//if all the loads failed,a default texture is loaded
	if(tex == NULL)
	{
		tex = (IDirect3DTexture8*)link->GetTexture("black");
	}
	textures.push_back(tex);
}

void CLevelSceneNode::CreateLightmap(tBSPLightmap &lightmap)
{
	//create a D3D texture and fill it with lightmap data
	IDirect3DTexture8* tex;
	HRESULT hr = D3DXCreateTexture(link->dev, 128, 128, 0, 0,
		D3DFMT_X8R8G8B8, D3DPOOL_MANAGED, &tex);
	if(FAILED(hr))
	{
		//add a NULL* to the array so that other indices wont be invalid
		lightmaps.push_back(tex);
		return;
	}
	//lock the texture
	D3DLOCKED_RECT data;
	tex->LockRect(0, &data, NULL, 0);
	// and fill it with the lightmap data
	int pitch = data.Pitch / 4;
	for(int x = 0; x < 128; ++x)
	{
		for(int y = 0; y < 128; ++y)
		{

			static_cast<uint*>(data.pBits)[x + y*pitch] = GammaPixel(20,
				lightmap.imageBits[y][x][0], lightmap.imageBits[y][x][1],
				lightmap.imageBits[y][x][2]);
		}
	}
	//unlock the texture to stream it back to video memory
	tex->UnlockRect(0);
	//create mipmap chain (use the best filter combo possible)
	D3DXFilterTexture(tex, NULL, 0, D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER);
	//add to the lightmap array
	lightmaps.push_back(tex);
}

void CLevelSceneNode::CreateLevel()
{
	//convert the Quake3 level data into data that can be used
	uint i = 0;
	//create a vertex buffer
	for(i=0; i<nVerts; i++)
	{
		//create a lightmap vert
		LightmapVertex l;
		tBSPVertex& v = bspVerts;
		l.x = v.vPosition.x;
		l.y = v.vPosition.y;
		l.z = v.vPosition.z;
		l.colour = v.color;
		l.u1 = v.vTextureCoord.x;
		l.v1 = v.vTextureCoord.y;
		l.u2 = v.vLightmapCoord.x;
		l.v2 = v.vLightmapCoord.y;
		vb.AddVertex(&l);	//CLightmapBuffer will make a copy of the vert before adding it
	}
	//create an index buffer
	for(i=0; i<(uint)nIndices; i++)
	{
		vb.AddIndex(bspIndices);
	}
	
	vb.SetLink(link);
	vb.CreateBuffer();
}

void CLevelSceneNode::CreateEffect(CMaterial &mat, int effect, int tex, int lmap)
{
	//loads a Material based on a texture, lightmap and shader effect
	//TODO: add support for Q3 shaders
	if((uint)tex > textures.size() - 1)
		return;
	if((uint)lmap > lightmaps.size())
		return;
	mat.SetLink(link);
	mat.SetTexture(0, textures[tex]);
	mat.SetTexture(1, lightmaps[lmap]);
	//basic 2X lightmap material
	mat.AddCall(MATCALL_SETSTAGE, 0, 0);
	mat.AddCall(MATCALL_SETSTAGE, 1, 1);
	mat.AddCall(MATCALL_SETSTATE, 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1);
	mat.AddCall(MATCALL_SETSTATE, 0, D3DTSS_COLORARG1, D3DTA_TEXTURE);/*
	mat.AddCall(MATCALL_SETSTATE, 1, D3DTSS_COLOROP, D3DTOP_MODULATE);
	mat.AddCall(MATCALL_SETSTATE, 1, D3DTSS_COLORARG1, D3DTA_CURRENT);
	mat.AddCall(MATCALL_SETSTATE, 1, D3DTSS_COLORARG2, D3DTA_TEXTURE);*/
}

void CLevelSceneNode::Render()
{
	//for now, just render all the faces
	// later, this will walk the BSP tree and check the PVS
	// to find which faces to render
	HRESULT hr = D3D_OK;
	link->matStack->Push();
	D3DXMatrixIdentity(&rel);
	link->matStack->MultMatrix(&rel);
	//make sure the level was loaded!
	link->dev->SetTransform(D3DTS_WORLD, &rel);
	vb.SetStream();
	vb.SetShader();
	link->dev->SetRenderState(D3DRS_LIGHTING, FALSE);
	link->dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	link->dev->SetTextureStageState(0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
	link->dev->SetTextureStageState(0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	link->dev->SetTextureStageState(0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
	link->dev->SetTextureStageState(1, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
	link->dev->SetTextureStageState(1, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	link->dev->SetTextureStageState(1, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
	for(uint i = 0; i < nFaces; ++i)
	{
		tBSPFace& f = bspFaces;
		CMaterial m;
		CreateEffect(m, f.effect, f.textureID, f.lightmapID);
		m.Render();
		switch(f.type)
		{
		case 1:	//polygon
			/*hr = link->dev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,0 , nVerts, f.meshVertIndex,
				f.numMeshVerts / 3);*/
			link->dev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, f.numOfVerts, f.meshVertIndex,
				f.numMeshVerts / 3);
			break;
		case 3:	//mesh
			link->dev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, f.startVertIndex , f.numOfVerts, f.meshVertIndex,
				f.numMeshVerts / 3);
			break;
		}
	}
	
	uint nMax = children.size();
	for(i = 0; i < nMax; ++i)
	{
		children->Render();
	}
	link->matStack->Pop();
}

uint CLevelSceneNode::GammaPixel(int gamma, uchar rVal, uchar gVal, uchar bVal)
{
	//this function is used to remove contrast in lightmaps
	float scale = 1;
	float temp = 0;
	float r = rVal * gamma / 255.0f;
	float g = gVal * gamma / 255.0f;
	float b = bVal * gamma / 255.0f;
	if(r > 1 && (temp = 1.0f/r) < scale)
		scale = temp;
	if(g > 1 && (temp = 1.0f/g) < scale)
		scale = temp;
	if(b > 1 && (temp = 1.0f/b) < scale)
		scale = temp;
	scale *= 255;
	rVal = (uchar)(r*scale);
	gVal = (uchar)(g*scale);
	bVal = (uchar)(b*scale);
	return D3DCOLOR_XRGB(rVal,gVal,bVal);
}

I haven't started work on the BSP treee loader/walker since I want to make sure that it can actually draw levels before trying to draw them quickly. The classes that are undefined should be pretty self-descriptive. If anyone out there is looking for the (much needed, I think) DirectX BSP loader code, feel free to borrow any part of it. Thanks for ANY help (even if it doesnt work)! PS. im sorry if this is the wrong forum to post this in, I'm a newbie and dont quite know my way around these forums yet.

Share this post


Link to post
Share on other sites
Advertisement
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!