Rendering Q3 BSP

Started by
15 comments, last by Enrico 17 years, 6 months ago
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;}

Anthony Rufrano
RealityFactory 2 Programmer
Advertisement
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
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.
Anthony Rufrano
RealityFactory 2 Programmer
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]
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
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
--
I have uploaded the articles with sample programs somewhere else: Loading and Rendering Quake3 Maps and Using Quake3's Lightmaps
--

This topic is closed to new replies.

Advertisement