Sign in to follow this  
zdlr

Rendering Q3 BSP

Recommended Posts

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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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 ;)

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i], 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[i].name);
strcat(normalmapname, "_n");

// Find the texture path/extension
FindTextureExtension(m_Textures[i].name);
FindTextureExtension(normalmapname);

// Load the texture based off of what we found.
//if(FAILED(D3DXCreateTextureFromFile( m_pDevice, m_pTextures[i].name,
// &m_pTextureMaps[i] )))
m_Materials[i] = engine->CreateMaterial();

if (!m_Materials[i]->SetTextureLayer(0, IbtMaterial::LAYER_BASE, m_Textures[i].name))
{
//m_pApp->Log("Texture not Found: %s", m_pTextures[i].name);
// If the texture can't be loaded, simply make it NULL.
//m_pTextureMaps[i] = NULL;
btErrorLog::GetPtr()->Printf("Could not create material %s!!", m_Textures[i].name);
BT_SAFE_RELEASE(m_Materials[i]);
if (m_Materials[i] != NULL)
btErrorLog::GetPtr()->Printf("Material #%d is not NULL!!", i);
}
else
btErrorLog::GetPtr()->Printf("Loaded texture: %s", m_Textures[i].name);

FILE *test = NULL;
if ((test = fopen(normalmapname, "rb")) != NULL)
{
btErrorLog::GetPtr()->Printf("Adding bump map to %s", m_Textures[i].name);

fclose(test);
if (!m_Materials[i]->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[i].x = m_pVerts[i].position[0];
pVertices[i].y = m_pVerts[i].position[2];
pVertices[i].z = m_pVerts[i].position[1];

pVertices[i].nx = m_pVerts[i].normal[0];
pVertices[i].ny = m_pVerts[i].normal[2];
pVertices[i].nz = m_pVerts[i].normal[1];

pVertices[i].color = D3DCOLOR_RGBA(m_pVerts[i].color[0],m_pVerts[i].color[1],
m_pVerts[i].color[2],m_pVerts[i].color[3]);

pVertices[i].tu = m_pVerts[i].texcoord[0];
pVertices[i].tv = m_pVerts[i].texcoord[1];

pVertices[i].lu = m_pVerts[i].lightmap[0];
pVertices[i].lv = m_pVerts[i].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[i] = (SHORT) m_pIndices[i];
}

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;
}


Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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[i].type == 1 || m_Faces[i].type == 3))
{
//if(m_pRenderState[rShowTextureMap])
{
//If the texture is different from the one previously use, swap.
if(m_Faces[i].texture != lastTexture)
{
lastTexture = m_Faces[i].texture;

// A negative texture index indicates no texture.
if(m_Faces[i].texture > -1)
{
if (m_Materials[m_Faces[i].texture] != NULL)
{
btTexture *tex = (btTexture*)m_Materials[m_Faces[i].texture]->GetTextureLayer(0);
m_pDevice->SetTexture(0, tex->GetTexture());
}
}
else
{
m_pDevice->SetTexture(0, NULL);
}
}
}

m_pDevice->DrawIndexedPrimitive( D3DPT_TRIANGLELIST , m_Faces[i].vertex, 0, m_Faces[i].n_verts,
m_Faces[i].meshvert, (m_Faces[i].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;
}


Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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[i].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 stuff
D3DCAPS9 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 stuff

renderer->VSelectIndexBuffer(m_IndexBuffer);

for(eInt i=0; i < m_numFaces; i++)
{
cBspQuake3Face face = m_pFaces[i];

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]

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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

Sign in to follow this