Skip to main content
GameDev.net gamedev.net
🔒 Locked

Rendering Q3 BSP

Started by zdlr Oct 6, 2006 at 7:35 AM 15 replies 2.8k views
Original Post
zdlr
zdlr
I'm writing a renderer for Q3 BSPs. Sadly, I'm off to a bad start. Loading in the BSP is fine, all my sanity checks work ok. When I come to render it using D3D9, I just get a black screen. The clear colour is magenta so I suppose it's positive that *something* is being drawn. I've tried to narrow down all the candidates that I can and here's what I've got. 1) The fvf could be wrong. I've specified D3DFVF_POSITION | D3DFVF_NORMAL | D3DFVF_DIFFUSE | D3DFVF_TEX2. That's also the order I've declared the vertex, which is where it matters: struct cBspQuake3Vertex { float position[3]; // x, y, z float normal[3]; // x, y, z unsigned byte diffuse[4]; // r, g, b, a float texture[2][2]; // texU, texV, lightMapU, lightMapV }; 2) Lighting is off (because the color is stored in the vertex). 3) Z Buffer is enabled and is cleared each frame. 4) I've set the world, view and projection matrices. The view is set at the player spawn for Q3DM1, the map I'm using. 5) I've converted Quake's coords to DX's LH system (I just swap y and z in position, right?). 6) Cull mode is set to none. 7) I've dumped the whole vertex lump into a vertex buffer and I'm using the meshVerts lump as an index buffer. This is suspect to me - why does Q3 use ints for this when D3DFMT_INDEX32 isn't recommended? Is this where I'm going wrong? Ok, I realise there's a good 1,000,000 things that could be causing this, can anyone think of the most obvious that I might have missed. Cheers, ZdlR
webjeff
webjeff
Quote:
5) I've converted Quake's coords to DX's LH system (I just swap y and z in position, right?).


Are you talking about converting RH to LH? If so, you only need to invert z and be careful on the winding. I dont know about the quake bsp format, but max exports z up, so in that case I do need to swap y and z, but its for a different reason.

Also, the only other thing I can think of is maybe your camera is too close also, or something with scaling.

But check on that handedness issue.

Jeff.
bakery2k1
bakery2k1
Quote:
Original post by zdlr
When I come to render it using D3D9, I just get a black screen. The clear colour is magenta so I suppose it's positive that *something* is being drawn.


Are you currently implementing any of the culling methods (BSP/PVS)? If so, try disabling those to see if they are causing your problem.

Try moving your camera "back" a long way to see if the general shape of the level is being drawn.

Quote:

2) Lighting is off (because the color is stored in the vertex).


Are you sure your vertex colors are correct (not black)?

Quote:

5) I've converted Quake's coords to DX's LH system (I just swap y and z in position, right?).


I think so. At least for OpenGL (RH system), you swap y and z, then invert z. So, a simple swap looks correct for LH.

webjeff: Quake maps use the Z coordinate for up, so swapping Y and Z is necessary.
zdlr
zdlr
I believe Q3 is like Max in that x/y is the player's general movement plane and z is the jumping plane. As it is an Id game, and they use GL, it could be that I also need to switch the handedness from RH to LH?

The view's near is 1.0f and far is 10000.0f (which seemed sensible given the values I saw in some of the BSP leaves).

As for winding, surely cos culling is off that shouldn't matter for now?

I won't be able to fiddle with it until tonight but you can be sure it's all I'm going to be doing this weekend!

ZdlR
zdlr
zdlr
Good idea re moving the camera back a long way, I'll try that and see if I'm not just way too close to something. The vertex colours are taken straight from the vertex lump in the file so I'm guessing they're correct! I'll scope a few of them out with the debugger.

I'm not culling anything as yet, throwing the entire tree at the gfx card. I know this isn't 'proper' but, as you said, for debugging purposes it's sufficient for now.

ZdlR
Enrico
Enrico
Quote:
Original post by zdlr
2) Lighting is off (because the color is stored in the vertex).

The colors look weird most of the time.

Quote:
5) I've converted Quake's coords to DX's LH system (I just swap y and z in position, right?).

Don't forget to convert the texture coordinates and the culling order.

Quote:
7) I've dumped the whole vertex lump into a vertex buffer and I'm using the meshVerts lump as an index buffer. This is suspect to me - why does Q3 use ints for this when D3DFMT_INDEX32 isn't recommended? Is this where I'm going wrong?

This sounds wrong. Try to render only with the vertex buffer with points instead of triangles. There you can check if that works. When that works, you can start creating triangles. To do that, you need the "MeshVertices"-lump and the "Surfaces"-lump. The first step is to create an IndexBuffer for every face (wasteful). Try this code:
void CreateIndexBuffer(std::vector &faces, std::vector &indices){    std::vector::iterator face = faces.begin();    for(; face != faces.end(); face++)    {        if(1 == face->type || 3 == face->type)        {            // create an Index Buffer for every face            spg::IIndexBuffer *ib = (spg::IIndexBuffer*) m_pRenderDevice->CreateObject(spg::RDO_INDEXBUFFER);                           // create indices array            spg::Array renderIndices;                        renderIndices.Reserve(face->numOfFaceIndices);                        for(size_t i=0; i < (size_t)face->numOfFaceIndices; i++)                        {                                const spg::Index32 index = face->vertexIndex + indices[face->faceVertexIndex + i];                                renderIndices.PushBack(index);                        }                        // create IndexBuffer from indices array and save the Index Buffer            ib->Append(renderIndices);            IndexBuffers.add(ib);        }    }}

This is the code from some articles I have written about Loading and Rendering Quake3 Files (maps and models). They are currently unpublished due to problems with the server. I can send copies via e-mail, just drop me a PM ;)
--
zdlr
zdlr
Yeah, thanks Enrico, you've certainly hit on something there. I figured it was a little *too* easy just to throw the meshVerts lump at an index buffer. I've PM'd you.

ZdlR
PlayerX
PlayerX
Quake 3's indices are relative to the face's vertices, not the vertex list as a whole. Q3 loads a separate vertex buffer pointer for every face, hence why the indices are relative to just those face's vertices.
paradoxnj
paradoxnj
Here is my loader code:

btBoolean btWorld::CreateFromFile(const char *filename){	FILE *fp = NULL;		btErrorLog::GetPtr()->Printf("Loading level:  %s", filename);	// Check if the .bsp file could be opened	if((fp = fopen(filename, "rb")) == NULL)	{ 		//m_pApp->Log("BSP File could not be opened: %s", strFileName);		btErrorLog::GetPtr()->Printf("Could not open map %s!!", filename);		return BT_FALSE;	}	// Initialize the header and lump structures	BSP_HEADER header;	// Read in the header	// This is a really cool chunk of info that	// Tells us where to find all the good stuff	// in the file. Kind of like a "Table of Contents"	fread(&header, 1, sizeof(BSP_HEADER), fp);	m_EntityString = new char[header.entries[lEntities].length / sizeof(char)];	//allocate the space needed for our data structures 	m_NumVerts = header.entries[lVertcies].length / sizeof(BSP_VERTEX);	m_pVerts = new BSP_VERTEX[m_NumVerts];	m_NumFaces = header.entries[lFaces].length / sizeof(BSP_FACE);	//m_Faces = new BSP_FACE[m_NumFaces];	m_Faces.resize(m_NumFaces);	m_NumIndices = header.entries[lIndices].length / sizeof(BSP_INDEX);	m_pIndices = new BSP_INDEX[m_NumIndices];	m_NumTextures = header.entries[lTextures].length / sizeof(BSP_TEXTURE);	m_Textures = new BSP_TEXTURE [m_NumTextures];	m_Materials.resize(m_NumTextures);	for (size_t k = 0; k < m_NumTextures; k++)		m_Materials[k] = NULL;	// Now this part is kinda messy. In reality, I should be setting this	// up so that we never have to seek backwards in the file, BUUUUUTTT...	// since this isn't a commercial project, and we're not too concerned	// about load times we're just gonna let it slide for now.		// Seek to the position of the entities	fseek(fp, header.entries[lEntities].offset, SEEK_SET);	// Read in the entity string	fread(m_EntityString, header.entries[lEntities].length, sizeof(char), fp);	// Log them to file//	btErrorLog::GetPtr()->Printf("%s\n", m_EntityString);	// Seek to the position in the file that stores the vertex information	fseek(fp, header.entries[lVertcies].offset, SEEK_SET);		// Read in the vertex information 	fread(m_pVerts, m_NumVerts, sizeof(BSP_VERTEX), fp);	// Seek to the position in the file that stores the index information	fseek(fp, header.entries[lIndices].offset, SEEK_SET);		// Read in the index information 	fread(m_pIndices, m_NumIndices, sizeof(BSP_INDEX), fp);	// Seek to the position in the file that stores the face information	fseek(fp, header.entries[lFaces].offset, SEEK_SET);	// Read in all the face information	for (u32 i = 0; i < m_NumFaces; i++)		fread(&m_Faces, 1, sizeof(BSP_FACE), fp);	// Seek to the position in the file that stores the texture information	fseek(fp, header.entries[lTextures].offset, SEEK_SET);	// Read in all the texture information	fread(m_Textures, m_NumTextures, sizeof(BSP_TEXTURE), fp);	// Close the file	fclose(fp);	IbtEngine *engine = btEngine_GetSingleton();	// First, we're going to go through all of the textures	// All we're given is file path (not even an extension!)	// So we're going to first locate the file, and then load	// it into a DirectX texture.	for(u32 i = 0; i < m_NumTextures; i++)	{		char								normalmapname[64];		ZeroMemory(normalmapname, 64);		strcpy(normalmapname, m_Textures.name);		strcat(normalmapname, "_n");		// Find the texture path/extension		FindTextureExtension(m_Textures.name);		FindTextureExtension(normalmapname);		// Load the texture based off of what we found.		//if(FAILED(D3DXCreateTextureFromFile( m_pDevice, m_pTextures.name,        //                           &m_pTextureMaps )))		m_Materials = engine->CreateMaterial();		if (!m_Materials->SetTextureLayer(0, IbtMaterial::LAYER_BASE, m_Textures.name))		{			//m_pApp->Log("Texture not Found: %s", m_pTextures.name);			// If the texture can't be loaded, simply make it NULL.			//m_pTextureMaps = NULL;			btErrorLog::GetPtr()->Printf("Could not create material %s!!", m_Textures.name);			BT_SAFE_RELEASE(m_Materials);			if (m_Materials != NULL)				btErrorLog::GetPtr()->Printf("Material #%d is not NULL!!", i);		}		else			btErrorLog::GetPtr()->Printf("Loaded texture:  %s", m_Textures.name);		FILE	*test = NULL;		if ((test = fopen(normalmapname, "rb")) != NULL)		{			btErrorLog::GetPtr()->Printf("Adding bump map to %s", m_Textures.name);			fclose(test);			if (!m_Materials->SetTextureLayer(1, IbtMaterial::LAYER_BUMPMAP, normalmapname))				continue;		}	}	HRESULT									hres;	// Occlusion Culling stuff	hres = m_pDevice->CreateQuery(D3DQUERYTYPE_OCCLUSION, &m_pOccluder);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not create occluder!!");		return BT_FALSE;	}	D3DDISPLAYMODE					mode;	hres = m_pDevice->GetDisplayMode(0, &mode);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not get display mode!!");		return BT_FALSE;	}	hres = D3DXCreateTexture(m_pDevice, 320, 240, 1, D3DUSAGE_RENDERTARGET, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, &m_pOccluderTexture);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not create occluder texture!!");		return BT_FALSE;	}	D3DSURFACE_DESC					desc;	m_pOccluderTexture->GetSurfaceLevel(0, &m_pOccluderSurface);	m_pOccluderSurface->GetDesc(&desc);	hres = D3DXCreateRenderToSurface(m_pDevice, desc.Width, desc.Height, desc.Format, TRUE, D3DFMT_D16, &m_pOccluderRender);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not create occluder!!");		return BT_FALSE;	}	// Create Vertex Buffer. I shouldn't need to explain this.	// The only thing to notice is that we're creating exactly	// enough space for all of our Vertices, yet we're using a	// different struct than what we read the verts into.	// (We read them into a BSP_VERTEX, and are creating space	// for a BSP_CUSTOM_VERTEX)	m_pDevice->CreateVertexBuffer( m_NumVerts * sizeof(BSP_CUSTOM_VERTEX), 0,								   BSP_CUSTOM_VERTEX_FVF, D3DPOOL_DEFAULT, &m_pVB, NULL );	BSP_CUSTOM_VERTEX *pVertices = NULL;	// Again, no explanation needed, right? Typical DirectX stuff.	m_pVB->Lock(0, m_NumVerts * sizeof(BSP_CUSTOM_VERTEX), (VOID**) &pVertices, 0);		// Now, at this point it would be simply LOVELY if we could 	// simply do this:	//	// memcpy(pVertices, m_pVerts, numVerts * sizeof(BSP_VERTEX));	//	// However, no such luck here, since there's a slight 	// Quirk in how DirectX processes it's vertices. You see,	// The information inside the vertex had to be in a 	// particular order for DirectX to draw it correctly. In	// this case that order is: 	//	// Position->Normal->Color->Texture1->Texture2	//	// DirectX is happy with this, and all is right in the world.	// However, the BSP file says differently! It's data comes to	// you in THIS order:	//	// Position->Texture1->Texture2->Normal->Color	//	// Feed this to DirectX and it will puke random red triangles 	// all over your screen. (Trust me, I've tried!)	// SO, what this means is that instead of a nice and easy memcopy	// we get to loop through each vert like so:	for(i = 0; i < m_NumVerts; i++)	{		pVertices.x = m_pVerts.position[0];		pVertices.y = m_pVerts.position[2];		pVertices.z = m_pVerts.position[1];		pVertices.nx = m_pVerts.normal[0];		pVertices.ny = m_pVerts.normal[2];		pVertices.nz = m_pVerts.normal[1];						pVertices.color = D3DCOLOR_RGBA(m_pVerts.color[0],m_pVerts.color[1],										   m_pVerts.color[2],m_pVerts.color[3]);		pVertices.tu = m_pVerts.texcoord[0];		pVertices.tv = m_pVerts.texcoord[1];		pVertices.lu = m_pVerts.lightmap[0];		pVertices.lv = m_pVerts.lightmap[1];	}	m_pVB->Unlock();		// Create Index Buffer	// NOTICE: This has been altered from past tutorials since we had several	// complaints that it wasn't working on older cards. Now it will check	// the caps for the Index Buffer Type and create the proper buffer accordingly.	D3DCAPS9 DevCaps;	((btEngine*)engine)->GetCaps(&DevCaps);	if(DevCaps.MaxVertexIndex > 0x0000FFFF)	{		m_pDevice->CreateIndexBuffer( m_NumIndices * sizeof(BSP_INDEX), 0,									D3DFMT_INDEX32, D3DPOOL_DEFAULT, &m_pIB, NULL );				BSP_INDEX *pIndices = NULL;					m_pIB->Lock(0, m_NumIndices * sizeof(BSP_INDEX), (VOID**) &pIndices, 0);				// Ah, now HERE we have the luxury of a memcpy, because, well		// a number is a number. And there was much rejoicing!		memcpy(pIndices, m_pIndices, m_NumIndices * sizeof(BSP_INDEX));		m_pIB->Unlock();	}	else	{		//m_pApp->Log("Hardware does not support 32 Bit Indcies. Forcing to 16 Bit");				m_pDevice->CreateIndexBuffer( m_NumIndices * sizeof(BSP_INDEX), 0,									D3DFMT_INDEX16, D3DPOOL_DEFAULT, &m_pIB, NULL );				SHORT *pIndices = NULL;					m_pIB->Lock(0, m_NumIndices * sizeof(SHORT), (VOID**) &pIndices, 0);				for(i = 0; i < m_NumIndices; i++)		{			pIndices = (SHORT) m_pIndices;		}		m_pIB->Unlock();	}	// Sort faces by texture	std::sort(m_Faces.begin(), m_Faces.end(), FaceSortFunctor());	// Create vertex declaration	hres = m_pDevice->CreateVertexDeclaration(g_VDecl, &m_pVDecl);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not create vertex declaration!!");		return BT_FALSE;	}		btErrorLog::GetPtr()->Printf("Num Faces:  %d", m_NumFaces);	btErrorLog::GetPtr()->Printf("Num Vertices:  %d", m_NumVerts);	btErrorLog::GetPtr()->Printf("Level %s loaded successfully...", filename);	//And we're done building the level! Was that too hard?	return BT_TRUE;}
zdlr
zdlr
Cheers for that, paradox, much appreciated.

Quote:

// Now, at this point it would be simply LOVELY if we could
// simply do this:
//
// memcpy(pVertices, m_pVerts, numVerts * sizeof(BSP_VERTEX));


Bah. I didn't even think about it being in a diff order in the bsp file! How've I missed that? Looks like I've a long weekend ahead of me.

In other news, throwing the vertex buffer at the gfx card as a point list was still fruitful, thanks to the position being in the right order in the vertex format. I can actually make out the starting courtyard of Q3DM1. Progress!

Thanks again,
ZdlR
paradoxnj
paradoxnj
Here's my rendering code also. I am going to experiment with running the levels through NvTriStrip and see if I can speed it up a bit.

btBoolean btWorld::Render(IbtCamera *Camera){	D3DXMATRIX						scale;	HRESULT							hres;	D3DMATERIAL9					material;	ZeroMemory(&material, sizeof(D3DMATERIAL9));	material.Ambient.a = 1.0f;	material.Ambient.r = 1.0f;	material.Ambient.g = 1.0f;	material.Ambient.b = 1.0f;	material.Diffuse.a = 1.0f;	material.Diffuse.r = 1.0f;	material.Diffuse.g = 1.0f;	material.Diffuse.b = 1.0f;	//D3DXMatrixScaling(&scale, 2.0f, 2.0f, 2.0f);	//m_pDevice->SetTransform(D3DTS_WORLD, &scale);	// All of this info is really rather redundant now, but	// once we get into lightmapping it will help, so let's	// just leave it be, mmkay?	m_pDevice->SetTextureStageState(0,D3DTSS_TEXCOORDINDEX,0);	m_pDevice->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_DISABLE);	m_pDevice->SetTextureStageState(0, D3DTSS_COLOROP, D3DTOP_ADD);	m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG1, D3DTA_TEXTURE);	m_pDevice->SetTextureStageState(0, D3DTSS_COLORARG2, D3DTA_DIFFUSE);		m_pDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_ANISOTROPIC);	m_pDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);	m_pDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_ANISOTROPIC);	// Tell DirectX what Vertex Format we're using.	m_pDevice->SetFVF(BSP_CUSTOM_VERTEX_FVF);		// Set the Vertex Buffer we're streaming from.	m_pDevice->SetStreamSource(0, m_pVB, 0, sizeof(BSP_CUSTOM_VERTEX));		// And the Indices we're using.	m_pDevice->SetIndices(m_pIB);		// I like to Null out both of these before rendering, just 	// incase we still have a texture set from a previous model  	m_pDevice->SetTexture(0, NULL);	m_pDevice->SetTexture(1, NULL);		// Calc the world/view/projection matrix	D3DXMATRIX					wvp;	D3DXMATRIX					view, world, proj;	((btCamera*)Camera)->CalcViewMatrix(&view);		D3DXMatrixIdentity(&world);	//m_pDevice->GetTransform(D3DTS_PROJECTION, &proj);	((btEngine*)btEngine_GetSingleton())->GetProjectionMatrix(&proj);	wvp = world * view * proj;	D3DXMatrixTranspose(&wvp, &wvp);	// Pass it to the vertex shader	hres = m_pDevice->SetVertexShaderConstantF(0, (float*)wvp, 0);	if (FAILED(hres))	{		btErrorLog::GetPtr()->Printf("Could not set world/view/projection matrix constant!!");		return BT_FALSE;	}	//Set default material	m_pDevice->SetMaterial(&material);	// Finally, we Draw! I'm checking here to see if the texture for 	// each face has changed so that we only call SetTexture() when needed.	int lastTexture = -1;		for(u32 i = 0; i < m_NumFaces; i++)	{		if((m_Faces.type == 1 || m_Faces.type == 3))		{				//if(m_pRenderState[rShowTextureMap])				{					//If the texture is different from the one previously use, swap.					if(m_Faces.texture != lastTexture)					{						lastTexture = m_Faces.texture;												// A negative texture index indicates no texture.						if(m_Faces.texture > -1)						{							if (m_Materials[m_Faces.texture] != NULL)							{								btTexture *tex = (btTexture*)m_Materials[m_Faces.texture]->GetTextureLayer(0);								m_pDevice->SetTexture(0,  tex->GetTexture());							}						}						else						{							m_pDevice->SetTexture(0,  NULL);						}					}				}				m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST , m_Faces.vertex, 0, m_Faces.n_verts,												m_Faces.meshvert, (m_Faces.n_meshverts/3));		}	}	// Draw all objects	for (size_t j = 0; j < m_Objects.size(); j++)	{		if (!m_Objects[j]->Render(Camera, 0.0f))			return BT_FALSE;	}	return BT_TRUE;}

zdlr
zdlr
Ok, so I'm edging ever closer, I think.

I tried creating index buffers for each bsp face but this gave me some funky polygon soup. I'm not sure what caused that but I've since reverted to a single index buffer, akin to paradoxnj's renderer.

When I bypass the index buffer and render as points I can see the entire level as expected and everything looks as it should, colours included. However, when I cycle through the faces and try to render using the index buffer like paradoxnj does, using the "Scenario 4" style from MSDN, I just get a black screen again! I'm not sure what's going on here, but it's likely that something is wrong with my index buffer.

ZdlR
paradoxnj
paradoxnj
You should be using one index buffer for the whole set of indices and one vertex buffer for all the vertices. Post your render code and your loading code and let's see if we can figure this out.
zdlr
zdlr
I'm using a single vertex buffer and a single index buffer, yes. It works fine when I render as points, it's surely the index buffer that's the problem.

I've tried to chop this down as far as is humanly possible to the bare essentials.


Loading:
eVoid cBspQuake3::LoadHeader(eIFStream &stream)		{			m_pHeader = new cBspQuake3Header;			stream.read(reinterpret_cast<eThinChar*>(m_pHeader), sizeof(cBspQuake3Header));		}		eVoid cBspQuake3::LoadEntities(eIFStream &stream)		{			m_pEntities = new cBspQuake3Entity;			eInt entitySize = m_pHeader->directoryEntries[ENTITY].length;			m_pEntities->entities = new eThinChar[entitySize];			eInt entityLumpOffset = m_pHeader->directoryEntries[ENTITY].offset;						stream.seekg(entityLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pEntities->entities), entitySize);		}		eVoid cBspQuake3::LoadTextures(eIFStream &stream)		{			eInt totalTextures = m_pHeader->directoryEntries[TEXTURE].length / sizeof(cBspQuake3Texture);			m_pTextures = new cBspQuake3Texture[totalTextures];			eInt textureLumpOffset = m_pHeader->directoryEntries[TEXTURE].offset;			stream.seekg(textureLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pTextures), totalTextures * sizeof(cBspQuake3Texture));			for(eInt i=0; i<totalTextures; i++)			{				GLOBAL_RENDERER->VLoadTexture(i, m_pTextures.name);			}		}		eVoid cBspQuake3::LoadPlanes(eIFStream &stream)		{			cBspQuake3DirectoryEntry planeEntry = m_pHeader->directoryEntries[PLANE];			eInt totalPlanes = planeEntry.length / sizeof(cBspQuake3Plane);			m_pPlanes = new cBspQuake3Plane[totalPlanes];			eInt planeLumpOffset = planeEntry.offset;			stream.seekg(planeLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pPlanes), totalPlanes * sizeof(cBspQuake3Plane));		}		eVoid cBspQuake3::LoadNodes(eIFStream &stream)		{			cBspQuake3DirectoryEntry nodeEntry = m_pHeader->directoryEntries[NODE];			eInt totalNodes = nodeEntry.length / sizeof(cBspQuake3Node);			m_pNodes = new cBspQuake3Node[totalNodes];			eInt nodeLumpOffset = nodeEntry.offset;			stream.seekg(nodeLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pNodes), totalNodes * sizeof(cBspQuake3Node));		}		eVoid cBspQuake3::LoadLeafs(eIFStream &stream)		{			cBspQuake3DirectoryEntry leafEntry = m_pHeader->directoryEntries[LEAF];			eInt totalLeafs = leafEntry.length / sizeof(cBspQuake3Leaf);			m_pLeafs = new cBspQuake3Leaf[totalLeafs];			eInt leafLumpOffset = leafEntry.offset;			stream.seekg(leafLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pLeafs), totalLeafs * sizeof(cBspQuake3Leaf));		}		eVoid cBspQuake3::LoadLeafFaces(eIFStream &stream)		{			cBspQuake3DirectoryEntry leafFaceEntry = m_pHeader->directoryEntries[LEAFFACE];			eInt totalLeafFaces = leafFaceEntry.length / sizeof(cBspQuake3LeafFace);			m_pLeafFaces = new cBspQuake3LeafFace[totalLeafFaces];			eInt leafFaceLumpOffset = leafFaceEntry.offset;						stream.seekg(leafFaceLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pLeafFaces), totalLeafFaces * sizeof(cBspQuake3LeafFace));		}				eVoid cBspQuake3::LoadLeafBrushes(eIFStream &stream)		{			cBspQuake3DirectoryEntry leafBrushEntry = m_pHeader->directoryEntries[LEAFBRUSH];			eInt totalLeafBrushes = leafBrushEntry.length / sizeof(cBspQuake3LeafBrush);			m_pLeafBrushes = new cBspQuake3LeafBrush[totalLeafBrushes];			eInt leafBrushLumpOffset = leafBrushEntry.offset;			stream.seekg(leafBrushLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pLeafBrushes), totalLeafBrushes * sizeof(cBspQuake3LeafBrush));		}		eVoid cBspQuake3::LoadModels(eIFStream &stream)		{			cBspQuake3DirectoryEntry modelEntry = m_pHeader->directoryEntries[MODEL];			eInt totalModels = modelEntry.length / sizeof(cBspQuake3Model);			m_pModels = new cBspQuake3Model[totalModels];			eInt modelLumpOffset = modelEntry.offset;						stream.seekg(modelLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pModels), totalModels * sizeof(cBspQuake3Model));		}		eVoid cBspQuake3::LoadBrushes(eIFStream &stream)		{			cBspQuake3DirectoryEntry brushEntry = m_pHeader->directoryEntries[BRUSH];			eInt totalBrushes = brushEntry.length / sizeof(cBspQuake3Brush);			m_pBrushes = new cBspQuake3Brush[totalBrushes];			eInt brushLumpOffset = brushEntry.offset;			stream.seekg(brushLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pBrushes), totalBrushes * sizeof(cBspQuake3Brush));		}		eVoid cBspQuake3::LoadBrushSides(eIFStream &stream)		{			cBspQuake3DirectoryEntry brushSideEntry = m_pHeader->directoryEntries[BRUSHSIDE];			eInt totalBrushSides = brushSideEntry.length / sizeof(cBspQuake3BrushSide);			m_pBrushSides = new cBspQuake3BrushSide[totalBrushSides];			eInt brushSidesLumpOffset = brushSideEntry.offset;			stream.seekg(brushSidesLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pBrushSides), totalBrushSides * sizeof(cBspQuake3BrushSide));		}				eVoid cBspQuake3::LoadVertexes(eIFStream &stream)		{			cBspQuake3DirectoryEntry vertexEntry = m_pHeader->directoryEntries[VERTEX];			eInt totalVertexes = m_numVertexes = vertexEntry.length / sizeof(cBspQuake3Vertex);			m_pVertexes = new cBspQuake3Vertex[totalVertexes];			eInt vertexLumpOffset = vertexEntry.offset;			stream.seekg(vertexLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pVertexes), totalVertexes * sizeof(cBspQuake3Vertex));			VertexBspToDx(totalVertexes);						GLOBAL_RENDERER->VCreateVertexBuffer(totalVertexes, QUAKE3_FVF, (eVoid*)m_pVertexesDX, sizeof(cBspQuake3VertexDX));		}		eVoid cBspQuake3::LoadMeshVertexes(eIFStream &stream)		{			cBspQuake3DirectoryEntry meshVertexEntry = m_pHeader->directoryEntries[MESHVERTEX];			eInt totalMeshVertexes = m_numMeshVertexes = meshVertexEntry.length / sizeof(cBspQuake3Vertex);			m_pMeshVertexes = new cBspQuake3MeshVertex[totalMeshVertexes];			eInt meshVertexLumpOffset = meshVertexEntry.offset;			stream.seekg(meshVertexLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pMeshVertexes), totalMeshVertexes * sizeof(cBspQuake3MeshVertex));		}		eVoid cBspQuake3::LoadEffects(eIFStream &stream)		{			cBspQuake3DirectoryEntry effectEntry = m_pHeader->directoryEntries[EFFECT];			eInt totalEffects = effectEntry.length / sizeof(cBspQuake3Effect);			m_pEffects = new cBspQuake3Effect[totalEffects];			eInt effectLumpOffset = effectEntry.offset;			stream.seekg(effectLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pEffects), totalEffects * sizeof(cBspQuake3Effect));		}		eVoid cBspQuake3::LoadFaces(eIFStream &stream)		{			cBspQuake3DirectoryEntry faceEntry = m_pHeader->directoryEntries[FACE];			eInt totalFaces = m_numFaces = faceEntry.length / sizeof(cBspQuake3Face);			m_pFaces = new cBspQuake3Face[totalFaces];			eInt faceLumpOffset = faceEntry.offset;			stream.seekg(faceLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pFaces), totalFaces * sizeof(cBspQuake3Face));		}		eVoid cBspQuake3::LoadLightMaps(eIFStream &stream)		{			cBspQuake3DirectoryEntry lightMapEntry = m_pHeader->directoryEntries[LIGHTMAP];			eInt totalLightMaps = lightMapEntry.length / sizeof(cBspQuake3LightMap);			m_pLightMaps = new cBspQuake3LightMap[totalLightMaps];			eInt lightMapLumpOffset = lightMapEntry.offset;			stream.seekg(lightMapLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pLightMaps), totalLightMaps * sizeof(cBspQuake3LightMap));		}		eVoid cBspQuake3::LoadLightVols(eIFStream &stream)		{			cBspQuake3DirectoryEntry lightVolEntry = m_pHeader->directoryEntries[LIGHTVOL];			eInt totalLightVols = lightVolEntry.length / sizeof(cBspQuake3LightVol);			m_pLightVols = new cBspQuake3LightVol[totalLightVols];			eInt lightVolLumpOffset = lightVolEntry.offset;			stream.seekg(lightVolLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(m_pLightVols), totalLightVols * sizeof(cBspQuake3LightVol));		}		eVoid cBspQuake3::LoadVisData(eIFStream &stream)		{			cBspQuake3DirectoryEntry visDataEntry = m_pHeader->directoryEntries[VISDATA];			eInt visDataLumpOffset = visDataEntry.offset;			m_pVisData = new cBspQuake3VisData;			stream.seekg(visDataLumpOffset);			stream.read(reinterpret_cast<eThinChar*>(&(m_pVisData->totalVectors)), sizeof(eInt));			stream.read(reinterpret_cast<eThinChar*>(&(m_pVisData->vectorSize)), sizeof(eInt));			eInt totalVectorSize = m_pVisData->totalVectors * m_pVisData->vectorSize;			m_pVisData->vectors = new eUByte[ totalVectorSize ];						stream.read(reinterpret_cast<eThinChar*>(m_pVisData->vectors), totalVectorSize * sizeof(eUByte));		}


Rendering:
// init stuffD3DCAPS9 devCaps;				HRESULT wereDeviceCapsReceived = m_pDevice->GetDeviceCaps(&devCaps);												eBool isIndexBuffer32Supported = (devCaps.MaxVertexIndex > 0x0000FFFF);								D3DMATERIAL9 material;				ZeroMemory(&material, sizeof(D3DMATERIAL9));				material.Ambient.a = material.Diffuse.a = 1.0f;				material.Ambient.r = material.Diffuse.r = 1.0f;				material.Ambient.g = material.Diffuse.g = 1.0f;				material.Ambient.b = material.Diffuse.b = 1.0f;				HRESULT wasColorVertexEnabled = m_pDevice->SetRenderState(D3DRS_COLORVERTEX, true);								HRESULT wasZBufferEnabled = m_pDevice->SetRenderState( D3DRS_ZENABLE, true );								HRESULT wasMaterialSet = m_pDevice->SetMaterial( &material );								HRESULT wasLightingEnabled = m_pDevice->SetRenderState(D3DRS_LIGHTING, false);								HRESULT wasCullModeSet = m_pDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);								HRESULT wasLightSet = m_pDevice->SetRenderState( D3DRS_AMBIENT, D3DCOLOR_XRGB(255,255,255) );								D3DXMATRIX world;				D3DXMatrixIdentity( &world );								HRESULT wasWorldTransformSet = m_pDevice->SetTransform( D3DTS_WORLD, &world );								D3DXMATRIX view;				D3DXMatrixIdentity( &view );				D3DXVECTOR3 dxEye = cVector3D3DAdapter(eye);				D3DXVECTOR3 at = D3DXVECTOR3(0.0f, 0.0f, 0.0f); //dxEye + D3DXVECTOR3(0.0f, 0.0f, 1.0f);				D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);				D3DXMatrixLookAtLH( &view, &dxEye, &at, &up );								HRESULT wasViewTransformSet = m_pDevice->SetTransform( D3DTS_VIEW, &view );								D3DXMATRIX projection;				D3DXMatrixIdentity( &projection);				D3DXMatrixPerspectiveFovLH( &projection, D3DX_PI / 4, (eFloat(800)/600), 1.0f, 10000.0f );				HRESULT wasProjectionTransformSet = m_pDevice->SetTransform( D3DTS_PROJECTION, &projection );								HRESULT wasNullTextureSelected = m_pDevice->SetTexture(0, NULL);				///////// SNIP //////////////render stuffrenderer->VSelectIndexBuffer(m_IndexBuffer);						for(eInt i=0; i < m_numFaces; i++)			{				cBspQuake3Face face = m_pFaces;				if (face.type != 1 && face.type != 3)				{					continue;				}				//renderer->VSelectTexture(face.textureIndex);				renderer->VDrawTriangleList(					face.vertexIndex,					face.totalVertexes,					face.meshVertIndex,					face.totalMeshVerts / 3); // primitive count			}


I apologise profusely for the long post but I guess it's necessary. I've cut out a lot of error checking of HRESULTs, and there's a lot more engine implementation that I could paste.

ZdlR

[Edited by - zdlr on October 7, 2006 5:56:05 PM]
zdlr
zdlr
I'm an idiot. I've spotted it.

I'd like to draw your attention to this method:

eVoid cBspQuake3::LoadMeshVertexes(eIFStream &stream){	cBspQuake3DirectoryEntry meshVertexEntry = m_pHeader->directoryEntries[MESHVERTEX];	eInt totalMeshVertexes = m_numMeshVertexes = meshVertexEntry.length / sizeof(cBspQuake3Vertex);	m_pMeshVertexes = new cBspQuake3MeshVertex[totalMeshVertexes];	eInt meshVertexLumpOffset = meshVertexEntry.offset;	stream.seekg(meshVertexLumpOffset);	stream.read(reinterpret_cast<eThinChar*>(m_pMeshVertexes), totalMeshVertexes * sizeof(cBspQuake3MeshVertex));}


Yeah, it's pretty obvious really, but code blindness totally got the better of me. It should be sizeof(cBspQuake3MeshVertex). Somehow, none of my sanity checks caught that so I may have to improve them.

It still doesn't look 100% but it's much better. Thank you all for your help!

ZdlR
Enrico
Enrico
Hi,

sorry for the late reply.

The system I present in the articles uses exactly one vertex buffer for the complete level and several Index Buffers. How many index buffers are used, depends on the rendering mode (no material scripts, no lightmaps, only material scripts, only lightmaps or both together). The code presented above is the heart piece of all these rendering modes. I will try to put the articles online as soon as possible. I still have some problems with my website :-(


Greetings, Enrico
--

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.