I've been meaning to tinker with the Quake I format models for a while now, so this is a reasonable start.
I got a wee bit done today; not an awful lot - most of the time was spent tidying up engine code, and adding a proper camera class so I can wander about the levels without having to have weird key combinations. So now I can aim with the mouse and use up/down to move forwards and back, left/right to strafe left and right, and numpad 1 and numpad0 to move up/down.
I can load a BSP map and render it in wireframe in one massive chunk withot any problems at all. The BSP file contains a list of all the vertices (Well, vertex positions), and a list of index pairs that make up the edge. So it's just a case of using those to draw a large indexed linelist.
So, screenshots...
Quake I start map, at the spawnpoint, facing to the left slightly
(Click to enlarge)
Quake I start map, top-down view
(Click to enlarge)
Next up will be making it use triangles obviously, I'm not sure how that'll go. Something that worries me slightly is that the Unofficial Quake Specs say that the BSP file version should be 23, but the files I have (Not sure what version of Quake, but it's the full one) are version 29. So there's bound to be some annoying differences that I'll run into.
And for anyone that cares at all, here's the relevant code:
bool PMapContextBSP::Render(PGraphics& gfx){ m_pShader->SetMatrix("mWorldViewProj", m_pOwner->GetTransform() * gfx.GetViewProj()); gfx.SetIndices(m_pIB); gfx.SetVertexDecl(m_pVertexDecl); gfx.SetVertexShader(m_pShader); gfx.SetStreamSource(m_pVB, 0); gfx.SetTexture(0, NULL); gfx.DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, m_header.vertices.nSize / sizeof(BSPVert), 0, m_header.edges.nSize / sizeof(BSPEdge)); return true;}//============================================================================bool PMapContextBSP::Load(PGraphics& gfx, EResource::SP pResource){ // Validate file size and version const u8* pData = (const u8*)pResource->GetData(); const BSPHeader* pHeader = (const BSPHeader*)pData; if(pResource->GetLength() < sizeof(BSPHeader)) { ELog::Get().DebugLog(L"Map file is too short to include a header\n"); return false; } else if(pHeader->nVersion != 0x1d) { ELog::Get().DebugLog(L"Invalid version in map file\n"); return false; } if(s_bDebugSpew) { ELog::Get().DebugFormat(L"Loading '%s':\n", m_pOwner->GetID().ToString().c_str()); } // Vertices u32 nVerts = pHeader->vertices.nSize / sizeof(BSPVert); if(pHeader->vertices.nOffset >= pResource->GetLength() || pHeader->vertices.nOffset+nVerts*sizeof(BSPVert) >= pResource->GetLength()) { ELog::Get().DebugLog(L"Invalid vertex directory in map file\n"); return false; } const BSPVert* pVertices = (const BSPVert*)(pData + pHeader->vertices.nOffset); // Edges u32 nEdges = pHeader->edges.nSize / sizeof(BSPEdge); if(pHeader->edges.nOffset >= pResource->GetLength() || pHeader->edges.nOffset+nVerts*sizeof(BSPEdge) >= pResource->GetLength()) { ELog::Get().DebugLog(L"Invalid edge directory in map file\n"); return false; } const BSPEdge* pEdges = (const BSPEdge*)(pData + pHeader->edges.nOffset); // Allocate VB m_pVB = new PVertexBuffer(gfx); HRESULT hResult = m_pVB->Create(nVerts, sizeof(Vertex)); if(FAILED(hResult)) { m_pVB = 0; ELog::Get().DebugLog(L"Failed to create vertex buffer\n"); return false; } // Allocate IB m_pIB = new PIndexBuffer(gfx); hResult = m_pIB->Create(nEdges*2); if(FAILED(hResult)) { m_pIB = 0; m_pVB = 0; ELog::Get().DebugLog(L"Failed to create index buffer\n"); return false; } // Lock IB { u16* pLock = (u16*)m_pIB->Lock(0, nEdges*2); if(!pLock) { m_pIB = 0; m_pVB = 0; ELog::Get().DebugLog(L"Failed to lock index buffer\n"); return false; } for(u32 i=0; i { *pLock++ = pEdges.v0; *pLock++ = pEdges.v1; } m_pIB->Unlock(); } // Init vertex decl m_pVertexDecl = new PVertexDecl; m_pVertexDecl->AppendPosition().AppendDiffuse(); if(!m_pVertexDecl->GetDecl()) // Ensure decl is created { m_pVertexDecl = 0; m_pVB = 0; m_pIB = 0; ELog::Get().DebugLog(L"Failed to create vertex declaration\n"); return false; } // Init shader std::wstring strShader = L"Basic.vsh"; m_pShader = gfx.GetShader(strShader.c_str(), PVertexShader::VS_2_0); if(!m_pShader) { m_pVertexDecl = 0; m_pVB = 0; m_pIB = 0; ELog::Get().DebugLog(L"Failed to load vertex shader\n"); return false; } // Fill VB { Vertex* pLock = (Vertex*)m_pVB->Lock(0, nVerts); for(u32 i=0; i { pLock->vPos = EVector3(pVertices.pos.x, pVertices.pos.z, pVertices.pos.y); pLock->nColour = 0xffff0000; pLock->tu = 0.0f; pLock->tv = 0.0f; ++pLock; } m_pVB->Unlock(); } // Save header and return m_header = *pHeader; return true;}
Anyway, that's enough for now, I're orf.