Sign in to follow this  
  • entries
    557
  • comments
    1237
  • views
    421523

Untitled

Sign in to follow this  
Evil Steve

105 views

Well, randomly inspired by This post by Scet today (Well, This image, anyway), I decided to try making a Quake BSP renderer. I don't currently have any way to get remotely complicated maps into my engine, so a basic BSP renderer should be a good start.
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
Start map, start pos
(Click to enlarge)


Quake I start map, top-down view
Start map, top down
(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.
Sign in to follow this  


6 Comments


Recommended Comments

While the Unofficial Quake Specs does get the layout of the structures right, there where a few changes in data types between then and the final version(29).

This is bspfile.h from the Quake Source, which is a much better reference.


/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

*/



// upper design bounds

#define MAX_MAP_HULLS 4

#define MAX_MAP_MODELS 256
#define MAX_MAP_BRUSHES 4096
#define MAX_MAP_ENTITIES 1024
#define MAX_MAP_ENTSTRING 65536

#define MAX_MAP_PLANES 32767
#define MAX_MAP_NODES 32767 // because negative shorts are contents
#define MAX_MAP_CLIPNODES 32767 //
#define MAX_MAP_LEAFS 8192
#define MAX_MAP_VERTS 65535
#define MAX_MAP_FACES 65535
#define MAX_MAP_MARKSURFACES 65535
#define MAX_MAP_TEXINFO 4096
#define MAX_MAP_EDGES 256000
#define MAX_MAP_SURFEDGES 512000
#define MAX_MAP_TEXTURES 512
#define MAX_MAP_MIPTEX 0x200000
#define MAX_MAP_LIGHTING 0x100000
#define MAX_MAP_VISIBILITY 0x100000

#define MAX_MAP_PORTALS 65536

// key / value pair sizes

#define MAX_KEY 32
#define MAX_VALUE 1024

//=============================================================================


#define BSPVERSION 29
#define TOOLVERSION 2

typedef struct
{
int fileofs, filelen;
} lump_t;

#define LUMP_ENTITIES 0
#define LUMP_PLANES 1
#define LUMP_TEXTURES 2
#define LUMP_VERTEXES 3
#define LUMP_VISIBILITY 4
#define LUMP_NODES 5
#define LUMP_TEXINFO 6
#define LUMP_FACES 7
#define LUMP_LIGHTING 8
#define LUMP_CLIPNODES 9
#define LUMP_LEAFS 10
#define LUMP_MARKSURFACES 11
#define LUMP_EDGES 12
#define LUMP_SURFEDGES 13
#define LUMP_MODELS 14

#define HEADER_LUMPS 15

typedef struct
{
float mins[3], maxs[3];
float origin[3];
int headnode[MAX_MAP_HULLS];
int visleafs; // not including the solid leaf 0
int firstface, numfaces;
} dmodel_t;

typedef struct
{
int version;
lump_t lumps[HEADER_LUMPS];
} dheader_t;

typedef struct
{
int nummiptex;
int dataofs[4]; // [nummiptex]
} dmiptexlump_t;

#define MIPLEVELS 4
typedef struct miptex_s
{
char name[16];
unsigned width, height;
unsigned offsets[MIPLEVELS]; // four mip maps stored
} miptex_t;


typedef struct
{
float point[3];
} dvertex_t;


// 0-2 are axial planes
#define PLANE_X 0
#define PLANE_Y 1
#define PLANE_Z 2

// 3-5 are non-axial planes snapped to the nearest
#define PLANE_ANYX 3
#define<

Share this comment


Link to comment
Quote:
Original post by Scet
While the Unofficial Quake Specs does get the layout of the structures right, there where a few changes in data types between then and the final version(29).

This is bspfile.h from the Quake Source, which is a much better reference.

*** Source Snippet Removed ***
Ah cool, thanks. I have the source code kicking about on my machine somewhere; I was just going to carry on blindly till something goes wrong, which probably isn't very clever [smile]

Do you know of an example change in data type? Do you mean an entire struct changing, or something changing from e.g. int to unsigned int? And does that change the size of the parent struct?

Share this comment


Link to comment
Quote:
Original post by Evil Steve
Do you know of an example change in data type? Do you mean an entire struct changing, or something changing from e.g. int to unsigned int? And does that change the size of the parent struct?


I don't really remember which ones change. The change is in the type, short to int, unsigned to signed, etc.

Sometimes the size of the struct changes, but since you're probably doing the whole "sizeof( lump ) / sizeof( struct )" the map will load fine. However, later during rendering you'd run into an index into an array that's out of bounds because the struct size was off.

Share this comment


Link to comment
Quote:
Original post by Scet
Quote:
Original post by Evil Steve
Do you know of an example change in data type? Do you mean an entire struct changing, or something changing from e.g. int to unsigned int? And does that change the size of the parent struct?


I don't really remember which ones change. The change is in the type, short to int, unsigned to signed, etc.

Sometimes the size of the struct changes, but since you're probably doing the whole "sizeof( lump ) / sizeof( struct )" the map will load fine. However, later during rendering you'd run into an index into an array that's out of bounds because the struct size was off.
Ah, ok. I'll definitely use the source code equivalents then.

Cheers [smile]

Share this comment


Link to comment

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