Jump to content
  • Advertisement
Sign in to follow this  
  • entries
  • comments
  • views


Sign in to follow this  
Evil Steve


Yay! Textures work!

It's currently less than efficient, I'm drawing each face seperately, which means I have silly numbers of draw calls, but I can batch by texture later on to make that much better.

It's a bit of a pain to do, since I need to patch up vertex indices, but it works. And textures were surprisingly easy (Thank god for the Unofficial Quake Specs telling you how to use them).

So, screenshot time:
(Click to enlarge)

And the code that makes it work - Which isn't really that interesting, but might be useful if someone else writing a Quake BSP renderer stumbles across this:

// BSP vertices are just points, we need point + texture coordinate.
// Since all vertices of a face share the same texture, we can cache
// vertices on a per-face basis. So, create a lookup table that will
// look up BSP vertexindices to "real" indices (-1 means "not
// yet initialised").
s32* pVertexLookup = new s32[nVerts];
ELog::Get().DebugLog(L"MapContextBSP::Load: Out of memory!\n");
return false;
std::vector vVerts;

u16* pLock = (u16*)m_pIB->Lock(0, nEdges*2);
delete[] pVertexLookup;
ELog::Get().DebugLog(L"MapContextBSP::Load: Failed to lock index buffer\n");
return false;

size_t nIndex = 0;
for(u32 nLeaf=1; nLeaf<75; ++nLeaf)
const BSPLeaf& leaf = pLeafs[nLeaf];
for(u32 nFace=0; nFace {
const BSPFace& face = pFaces[pFaceLookup[leaf.lface_id + nFace]];
const BSPTextureInfo& texinfo = pTexInfos[face.texinfo_id];

Face outFace;
outFace.nTexture = texinfo.miptex;
outFace.nStartIndex = nIndex;
outFace.nNumTris = 0;

u16 nRoot=0;
u16 nLast=0;
std::fill(pVertexLookup, pVertexLookup+nVerts, -1);
for(u32 nEdge=0; nEdge {
// Find indices for this edge
s32 nEdgeLookup = pEdgeLookup[face.ledge_id + nEdge];
u16 v0, v1;
if(nEdgeLookup > 0)
v0 = pEdges[nEdgeLookup].v0;
v1 = pEdges[nEdgeLookup].v1;
v0 = pEdges[-nEdgeLookup].v1;
v1 = pEdges[-nEdgeLookup].v0;

// Make up a triangle
if(nEdge == 0)
nRoot = v0;
Assert(nLast == v0, "Face edges do not form a line loop!");

// Patch up vertices
u16 nIndices[3] = {nRoot, nLast, v1};
for(int j=0; j<3; ++j)
u16 nIndex = nIndices[j];
if(pVertexLookup[nIndex] == -1)
Vertex vert;
vert.vPos = EVector3(pVertices[nIndex].pos.x,
vert.nColour = 0xffffffff;
vert.tu = (pVertices[nIndex].pos.DotProduct(texinfo.vecS) + texinfo.distS) /
vert.tv = (pVertices[nIndex].pos.DotProduct(texinfo.vecT) + texinfo.distT) /

pVertexLookup[nIndex] = (u32)vVerts.size();
Assert(vVerts.size() < 65536, "More than 64k vertices! Start panicing!");

*pLock++ = (u16)pVertexLookup[nIndex];
nIndex += 3;
nLast = v1;


delete[] pVertexLookup;

There's nothing much to say about that code really. The loop over the leafs is a magic number again (75 just happens to be the number of leafs in that chunk of the map). m_vTextures is a vector of structs containing a texture pointer (In the order specified in the BSP file), and that code fills the index buffer, and fills a vector of faces and vertices. I'll have to change the code so that the number of indices is worked out rather than using a magic number (Which you can't see in that code).

Rendering is pretty simple too:

bool PMapContextBSP::Render(PGraphics& gfx)
m_pShader->SetMatrix("mWorldViewProj", m_pOwner->GetTransform() * gfx.GetViewProj());

gfx.SetStreamSource(m_pVB, 0);

for(size_t i=0; i {
const Face& face = m_vFaces;
gfx.SetTexture(0, m_vTextures[face.nTexture].pTexture);
gfx.DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, m_header.lumps[BSPHeader::LUMP_Vertices].nSize / sizeof(BSPVert),
(UINT)face.nStartIndex, (UINT)face.nNumTris);

return true;

Again, not very efficient. It transforms all the vertices (And that's the wrong number, I just noticed...), and uses worst case parameters for the DIP() call. There's no sorting by texture or anything (yet) either. Still, it works, and that's the main thing.

Anyhoo, back to work I think...
Sign in to follow this  


Recommended Comments

There are no comments to display.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • 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!