🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Mini-map generation

Published May 17, 2006
Advertisement
Before today I haven't really had a decent mini map in the game. I'd been using D3DXFillTexture to generate a basic 256x256, 4 color mini map (green for grass, dark gray for buildings, light gray for roads, blue for water) and it looked kinda crappy.

Now I've coded up a method to render the entire city to a 512x512 texture, and then I use that texture upsampled for the minimap. It looks like it'll work out quite well. From my previous test sessions with my publisher I've noticed that the lack of a decent mini map was a problem...hopefully that's a thing of the past.

Instead of having the author of each map distribute a .bmp image with each .map city file, I've decided to generate the city mini maps at the start of the game. The most obvious solution would be to use a RenderToTexture function to render the map to a texture...but there are a lot of cards out there that don't support that functionality, or multiple render targets.

So what I decided to do....is at the loading of the particular map, I render the entire map onto the center of the screen and then grab that part of the screen and then load it into a texture file (much like taking a screenshot). Using D3DDevice->GetFrontBufferData(). This seems like it will work on any system. If it turns out that this doesn't work on some systems...I suppose I could just embed each city's mini-map into the .map file format I use. Though it would increase the map size by about 1MB, and make downloading maps from a server less efficient.

Hopefully this method will work out. There is one issue though, because I'm using the front buffer as my render target the user can see the map being displayed...I need to find a work around using multiple swap chains, or something like that. Until then this produces the results I'm going for, and will work on pretty much any system out there...if anybody has some ideas about how to generate these mini maps without them being displayed...I'd like to hear it :-)

Here's the code to do what I'm talking about...I'm really not worried about efficiency since this only gets executed once.

I know a few of these functions are pretty slow, and I'm using the hard disk as a intermediary instead of just going from the copied front buffer surface -> a renderable texture...this is mostly for debugging purposes.

void CCity::GenerateCityMap(LPDIRECT3DTEXTURE9 pMapTexture){ //Temporary variables D3DVIEWPORT9 oldViewport; D3DXMATRIX OldView, OldProj, OldWorld, TempMatrix; int OldTimeOfDay[2] = { GameWorld.m_WorldTime.tm_hour, GameWorld.m_WorldTime.tm_min }; int MapOffset[2] = { (ValidResolutions[GameEngine.GetCurrentResolution()][0]/2)-256, (ValidResolutions[GameEngine.GetCurrentResolution()][1]/2)-256 }; // Save old device info D3DDEVICE->GetViewport(&oldViewport); D3DDEVICE->GetTransform(D3DTS_VIEW, &OldView); D3DDEVICE->GetTransform(D3DTS_PROJECTION, &OldProj); D3DDEVICE->GetTransform(D3DTS_WORLD, &OldWorld); // Setup city map viewport D3DVIEWPORT9 CityMapViewport; CityMapViewport.X = MapOffset[0]; CityMapViewport.Y = MapOffset[1]; CityMapViewport.Width = 512; CityMapViewport.Height = 512; CityMapViewport.MinZ = 0.0f; CityMapViewport.MaxZ = 1.0f; D3DDEVICE->SetViewport(&CityMapViewport); //Make the entire city visible for(int i = 0; i < MAP_SIZE; i++)  for(int j = 0; j < MAP_SIZE; j++)  {   m_VisibilityTable[j] = true;  } //end of for loop   //Set the lighting conditions //Always render the map at Noon. GameWorld.m_WorldTime.tm_hour = 12; GameWorld.m_WorldTime.tm_min = 0; GameEngine.SetWeatherCondition();  //Generate all of the render groups using the new visibility data. GameEngine.UpdateRenderGroups(true);   //Set view/world/projection matrices for the city map render D3DDEVICE->SetTransform(D3DTS_VIEW, D3DXMatrixLookAtLH(&TempMatrix, &D3DXVECTOR3(-10.5,100,-10.5), &D3DXVECTOR3(-10.5,0.1,-10.001), &D3DXVECTOR3(0,1,0))); D3DDEVICE->SetTransform(D3DTS_PROJECTION, D3DXMatrixOrthoLH(&TempMatrix, (MAP_SIZE*TILE_SCALE), (MAP_SIZE*TILE_SCALE), 0.01, 1000.0)); D3DDEVICE->SetTransform(D3DTS_WORLD, D3DXMatrixIdentity(&TempMatrix)); D3DDEVICE->BeginScene(); {  //Update shadow map transforms  GameEngine.UpdateShadowTransform();   if(GameVariableValues[38] >= 2)  {   //Global shadow map   GameEngine.GenerateShadowMapTexture(0);   //Generate local shadow map every frame   GameEngine.GenerateShadowMapTexture(1);  } //end of if  //Clear the area of the screen the map will be rendered to  D3DDEVICE->Clear(0, NULL, D3DCLEAR_ZBUFFER|D3DCLEAR_TARGET, 0x00000000, 1.0f, 0L);  //Render city  GameEngine.RenderScene(false, false);  } D3DDEVICE->EndScene();  //Show the map so it can be captured with a screenshot D3DDEVICE->Present(NULL, NULL, NULL, NULL);// Sleep(2000); //Restore old device info D3DDEVICE->SetViewport(&oldViewport); D3DDEVICE->SetTransform(D3DTS_VIEW, &OldView); D3DDEVICE->SetTransform(D3DTS_PROJECTION, &OldProj); D3DDEVICE->SetTransform(D3DTS_WORLD, &OldWorld); GameWorld.m_WorldTime.tm_hour = OldTimeOfDay[0]; GameWorld.m_WorldTime.tm_min = OldTimeOfDay[1];  //Create scratch surface to receive the front buffer copy LPDIRECT3DSURFACE9 pFrontBufferCopy; if(GameEngine.GetWindowed() == true)  D3DDEVICE->CreateOffscreenPlainSurface(GameEngine.Direct3D.GetDesktopResolution().x, GameEngine.Direct3D.GetDesktopResolution().y, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pFrontBufferCopy, NULL); else  D3DDEVICE->CreateOffscreenPlainSurface(ValidResolutions[GameEngine.GetCurrentResolution()][0], ValidResolutions[GameEngine.GetCurrentResolution()][1], D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &pFrontBufferCopy, NULL); //Get back buffer surface D3DDEVICE->GetFrontBufferData(0, pFrontBufferCopy); //Specify city map rect, needs to be modified slightly if running in windowed mode. //Only grab the part of the front buffer that contains the 512x512 map! RECT CityMapRect;  if(GameEngine.GetWindowed() == false) {  CityMapRect.top = MapOffset[1];   CityMapRect.left = MapOffset[0];  CityMapRect.right = CityMapRect.left+512;   CityMapRect.bottom = CityMapRect.top+512; } //end of if else {  CityMapRect.top = MapOffset[1]+24;   CityMapRect.left = MapOffset[0]+3;  CityMapRect.right = CityMapRect.left+512;   CityMapRect.bottom = CityMapRect.top+512; } //end of else //Save city map image to hard drive //Only save the 512x512 area that contains the map, the region is specified in CityMapRect D3DXSaveSurfaceToFile("CityMap.bmp", D3DXIFF_BMP, pFrontBufferCopy, NULL, &CityMapRect); //Load city map texture from hard drive. SAFE_RELEASE(pMapTexture); D3DXCreateTextureFromFile(D3DDEVICE, "CityMap.bmp", &pMapTexture);	  //Clean up SAFE_RELEASE(pFrontBufferCopy); DeleteFile("CityMap.bmp");} //end of GenerateCityMap function


But yea...if anybody can follow what I'm doing there, and knows of a widely supported way of doing this other than multiple render targets, lemme know. The only real issue is getting the map to render without displaying it to the user.

I threw together a quick test map...here's how it looks in the map editor.


Aaaand here's what the mini map looks like...

I think that it came out pretty clean. That's why I like this method so much...it'll generate these on any system that can run the game, regardless of hardware.

Alright back to work...I've fixed many bugs today, but I've still got a lot of work to do, and I'm going to have to create a lot more artwork for the city...I think there isn't enough variety.

Ugh...so much to do, I was hoping to have the game done tommorow...but that's not going to happen. Oh well, my only concern is getting the game up to MY standards, and I'm going to keep working until that happens.

- Dan


*****************************************************
Update....I've scratched the idea of generating the minimaps at load time, and I'll just save them as a .bmp file. The simpler things are the less that can go wrong. This is a much more sound approach. Also now I can guarantee that everyone will see the same mini map, and I can bake shadows into them and do some other cool effects, that not all computers would support.

Yea...this is def. the way to go. Though players will have to download at least 1.5MB worth of data with each custom map.
*****************************************************
Next Entry Screenshots!
0 likes 6 comments

Comments

Gaheris
Great, (mini)maps are very important. I remember playing GTA 2 (which lacked any kind of ingame map) and getting frustrated because I got lost all the time.
May 17, 2006 05:17 AM
superpig
Why are you using BMP? If you use a compressed image format it should cut down the amount of extra data considerably.
May 17, 2006 05:28 AM
Wyzfen
I'd use a PNG for the map - heaps smaller.

Also, what cards dont support render to texture ? What are you aiming for as minimum specs ?
My engine relies on render to texture - every frame is rendered to a texture ! You've got me worried that perhaps thats a bad idea after all :p

Wyzfen
May 17, 2006 06:34 AM
dgreen02
superpig - well the D3DX SAVE functions only support .bmp and .dds, and .dib. On the other hand the LOAD functions support all kinds of good formats like .jpg, .png, etc. Those fucntions are the easiest way to load/save DIRECT3DTEXTUREs as far as I know.

Gaheris...I agree with you mini maps are important, the game already looks a lot better with a small rotating version of the game world in the corner of the screen!

Wyzfen - Well for instance one of my test computers has a NVIDIA Geforce FX 5500 that only supports 1 render target, and render to texture calls fail (shadow maps for instance). Also most older Intel integrated video cards will not support that I'm pretty sure, and since I'm going for a commercial release I don't want to block out that whole part of the market.

Thanks for the replies guys! I'll try to write up my own compressions algorithm, maybe zlib the .map file after I include the .bmp into it.

- Dan
May 17, 2006 10:11 AM
Wyzfen
Quote:
Wyzfen - Well for instance one of my test computers has a NVIDIA Geforce FX 5500 that only supports 1 render target, and render to texture calls fail (shadow maps for instance). Also most older Intel integrated video cards will not support that I'm pretty sure, and since I'm going for a commercial release I don't want to block out that whole part of the market.


Hmm. I'll have to look into that. I'm developing on a GeForce 4 Ti 4200 Go - which can only have 1 render-target, but thats all i use for shadows / the main view (keep swapping as necessary).

Perhaps i dont understand the issue ? I'll have to get around to releasing something to test i guess :p

oh - and i didnt know that save didnt support pngs - that sucks - there are some nice compression libraries around tho - If you can automatically pack your maps, it'll make it heaps easier for users to distribute (mmm. in-game distribution ? ) :)
Wyzfen
May 18, 2006 04:47 AM
superpig
Even using DDS would be an immense improvement over BMP, because you'd be able to encode the image with DXT compression. (You should be doing this for most of your textures anyway).
May 19, 2006 06:27 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement