I'm trying to recreate a Return To Castle Wolfenstein renderer using Direct3D 11, but I can't seem to be able to render the lightmaps as they should be. I have not found much information on the internet on how Quake3/RTCW lightmaps are actually created from the BSP files. Basically, the result is as below:
And this is how the result should be:
With the info taken from Q3 BSP file format, I extracted the lightmap x and y origin inside the lightmap data taken from the lightmap index lmindex, in the BSP file from lm_start[0] and lm_start1 and the width and height from lm_size[0] and lm_size1. The problem seems to be in finding the lightmap texture coordinates, which I assumed were the same as the face textures. After each lightmap extracted, I assigned the lightmap index to the face index. A negative index indicates that no lightmap is associated to the face.
Here is the code I used to create lightmaps from the file:
bool XWorldMap::CreateLightmaps(ID3D11Device* pd3d, ID3D11DeviceContext*
pd3dDeviceContext)
{
m_lightMaps.reserve(m_numFaces);
for (int i = 0; i < m_numFaces; ++i)
{
ID3D11ShaderResourceView* pTexture = nullptr;
int x = m_q3Faces[i].lmstart[0];
int y = m_q3Faces[i].lmstart[1];
int lightMapWidth = m_q3Faces[i].lmsize[0];
int lightMapHeight = m_q3Faces[i].lmsize[1];
int lightMapIndex = m_q3Faces[i].lmindex;
m_q3Faces[i].lmindex = (lightMapIndex < 0) ? -1 : i;
if (lightMapIndex < 0 || lightMapWidth == 0 || lightMapHeight == 0)
{
m_lightMaps.push_back(nullptr);
continue;
}
std::vector<char> lightMapData;
lightMapData.reserve(4*lightMapHeight*lightMapWidth);
for (int j = y; j < y + lightMapHeight; ++j)
{
for (int k = x; k < x + lightMapWidth; ++k)
{
lightMapData.push_back(m_q3LightMaps[lightMapIndex].map[j][k][0]);
lightMapData.push_back(m_q3LightMaps[lightMapIndex].map[j][k][1]);
lightMapData.push_back(m_q3LightMaps[lightMapIndex].map[j][k][2]);
lightMapData.push_back(255);
}
}
if (!CreateTexture(pd3d, pd3dDeviceContext, lightMapData, lightMapHeight, lightMapWidth, &pTexture))
return false;
m_lightMaps.push_back(pTexture);
}
return true;
}
This is the code to create the lightmap textures:
bool CreateTexture(ID3D11Device* pd3d, ID3D11DeviceContext*
pd3dDeviceContext, std::vector<char> imgData, unsigned int height, unsigned
int width, ID3D11ShaderResourceView ** pTexture)
{
HRESULT hr;
ID3D11Texture2D *pTex2D = nullptr;
D3D11_TEXTURE2D_DESC texDesc;
memset(&texDesc, 0, sizeof(D3D11_TEXTURE2D_DESC));
texDesc.Width = width;
texDesc.Height = height;
texDesc.MipLevels = texDesc.ArraySize = 1;
texDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_DYNAMIC;
texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
texDesc.MiscFlags = 0;
hr = pd3d->CreateTexture2D(&texDesc, nullptr, &pTex2D);
if (FAILED(hr))
return false;
D3D11_MAPPED_SUBRESOURCE mappedTex;
pd3dDeviceContext->Map(pTex2D, 0, D3D11_MAP_WRITE_DISCARD, 0,
&mappedTex);
UCHAR* pTexels = (UCHAR*)mappedTex.pData;
int j = 0;
for (UINT row = 0; row < texDesc.Height; row++)
{
UINT rowStart = row * mappedTex.RowPitch;
for (UINT col = 0; col < texDesc.Width; col++)
{
UINT colStart = col * 4;
pTexels[rowStart + colStart + 0] = imgData[j++];
pTexels[rowStart + colStart + 1] = imgData[j++];
pTexels[rowStart + colStart + 2] = imgData[j++];
pTexels[rowStart + colStart + 3] = imgData[j++];
}
}
pd3dDeviceContext->Unmap(pTex2D, D3D11CalcSubresource(0,0,1));
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
memset(&srvDesc, 0, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC));
srvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MipLevels = 1;
hr = pd3d->CreateShaderResourceView(pTex2D, &srvDesc, pTexture);
if (FAILED(hr))
return false;
return true;
}