Archived

This topic is now archived and is closed to further replies.

Optimizing 2D Iso Tile Maps In DirectX 8

This topic is 5897 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have a 2D Isometric Tiling engine being done with DirectX8, each tile being used with a ID3DXSprite Interface (or sharing it). It works great and all, but needs some optimization. I have already clipped out massive overdraw, so it only renders exactly the number of tiles & walls it needs to. In a 10x10x10 (LengthXWidthXHeight) Tilemap, it gets approx 225+ FPS on my geforce2 1000mhz pc. Please tell me some ideas as to where I can possibly speed it up. Data structs // A 2D image, drawn in screen space typedef class Image { public: // holds the image data LPD3DXSPRITE ImageData; // the actual image LPDIRECT3DTEXTURE8 Texture; // for various ways of drawing RECT Rect; D3DXVECTOR2 Scaling; D3DXVECTOR2 RotationCenter; D3DXVECTOR2 Translation; float RotationAngle; D3DCOLOR Color; // render long lines, but its faster inline void Render() { ImageData->Draw(Texture, NULL, &Scaling, &RotationCenter, RotationAngle, &Translation, Color); }; inline void RenderRect() { ImageData->Draw(Texture, &Rect, &Scaling, &RotationCenter, RotationAngle, &Translation, Color); }; inline void RenderThis(RECT* rect, D3DXVECTOR2* scale, D3DXVECTOR2* cntr, float ang, D3DXVECTOR2* loc, D3DCOLOR col) { ImageData->Draw(Texture, rect, scale, cntr, ang, loc, col); }; // creates/destroys the image bool Create(LPDIRECT3DDEVICE8 device, char* imgfilename); void Init(); void Destroy(); // change params of image inline void ChangeRect(int l, int r, int t, int b) { Rect.left = l; Rect.right = r; Rect.top = t; Rect.bottom = b; }; inline void Scale(float x, float y) { Scaling.x *= x; Scaling.y *= y; RotationCenter.x *= x; RotationCenter.y *= y; }; inline void SetCenter(float x, float y) { RotationCenter.x = x; RotationCenter.y = y; }; inline void SetPosition(float x, float y) { Translation.x = x; Translation.y = y; }; inline void Translate(float x, float y) { Translation.x += x; Translation.y += y; }; inline void SetAngle(float theta) { RotationAngle = theta; }; inline void Rotate(float theta) { RotationAngle += theta; }; inline void SetColor(D3DCOLOR color) { Color = color; }; // constructors Image(LPDIRECT3DDEVICE8 device, char* imgfilename); Image(); ~Image(); } Image, *Image_ptr; // holds location data & colors for tiles typedef struct TILE { D3DXVECTOR2 TileLocation; D3DXVECTOR2 Wall1Location; D3DXVECTOR2 Wall2Location; D3DCOLOR Color; } TILE, *TILE_PTR; typedef class TileSet { public: int NumTiles; int NumLWalls; int NumRWalls; Image** TileImg; // image of tile Image** WallImg1; // left side wall Image** WallImg2; // right side wall inline void RenderTile(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { TileImg[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; inline void RenderLWall(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { WallImg1[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; inline void RenderRWall(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { WallImg2[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; void Destroy(); void Create(int p_numtiles, int p_numlwalls, int p_numrwalls, Image** p_tileimg, Image** p_wallimg1, Image** p_wallimg2); TileSet(); } TileSet, *TileSet_ptr; // if no tile is present, the usage is NO_TILE (-1) #define NO_TILE -1 #define NO_DRAW -2 // tile there, but dont draw it typedef class Map { private: int MapLength; int MapWidth; int MapHeight; int TileLength; // length of tiles *in pixels* int TileWidth; // width of tiles *in pixels* int TileHeight; // height of 1 tile *in pixels* TileSet TileImg; // the tiles to use TILE* Tiles; // locations int *TileUsage; // what tile set to use for each block (if -1, none is drawn) int *LWallUsage; // what left wall to draw int *RWallUsage; // what right wall to draw int *zOrder; // order to draw public: void Compile(); void Render(); int Load(char* filename); int Save(char* filename); void Destroy(); // constructors Map(); void Create(int p_maplength, int p_mapwidth, int p_mapheight, int p_tilelength, int p_tilewidth, int p_tileheight); void SetTSet(int p_numtiles, int p_numlwalls, int p_numrwalls, Image** p_tileimg, Image** p_wallimg1, Image** p_wallimg2); ~Map(); } Map, *Map_ptr; Map::Map() { MapLength = 0; MapWidth = 0; MapHeight = 0; TileLength = 0; TileWidth = 0; TileHeight = 0; TileUsage = NULL; LWallUsage = NULL; RWallUsage = NULL; Tiles = NULL; zOrder = NULL; } void Map::Create(int p_maplength, int p_mapwidth, int p_mapheight, int p_tilelength, int p_tilewidth, int p_tileheight) { // create a map MapLength = p_maplength; MapWidth = p_mapwidth; MapHeight = p_mapheight; TileLength = p_tilelength; TileWidth = p_tilewidth; TileHeight = p_tileheight; TileUsage = new int[MapLength*MapWidth*MapHeight]; LWallUsage = new int[MapLength*MapWidth*MapHeight]; RWallUsage = new int[MapLength*MapWidth*MapHeight]; Tiles = new TILE[MapLength*MapWidth*MapHeight]; zOrder = new int[MapLength*MapWidth*MapHeight]; memset((void*)zOrder, 0, sizeof(int)*MapLength*MapWidth*MapHeight); // jus set some random tiles for (int i = 0; i < MapLength*MapWidth*MapHeight; i++) { if (i < MapLength*MapWidth) TileUsage = 0; else if (TileUsage[i-(MapLength*MapWidth)] != NO_TILE) TileUsage[i] = rand()%2-1; else TileUsage[i] = NO_TILE; if (TileUsage[i] >= 0) LWallUsage[i] = 0; else LWallUsage[i] = NO_TILE; if (TileUsage[i] >= 0) RWallUsage[i] = 0; else RWallUsage[i] = NO_TILE; } } Map::~Map() { } void Map::Destroy() { if (Tiles) { delete Tiles; } if (zOrder) { delete zOrder; } if (TileUsage) { delete TileUsage; TileUsage = NULL; } if (LWallUsage) { delete LWallUsage; LWallUsage = NULL; } if (RWallUsage) { delete RWallUsage; RWallUsage = NULL; } TileImg.Destroy(); } int facecount = 0; // set all the screen space coords & zOrder void Map::Compile() { int order = 0; // render the map on screen // start on top screen row, at the lowest tiles, then draw up for (int height = 0; height < MapHeight; height++) { // index of starting block int startblock = (MapLength * MapWidth * (height+1)) - 1; int currblock = startblock; int screenrows = MapLength + MapWidth - 1; // calculate on-screen space location D3DXVECTOR2 screenplace; screenplace.x = 320-TileLength/2; for (int i = 0; i < screenrows; i++) { // determine number of blocks on this screen row int blocksonrow = (i + 1); if ((i+1) > (screenrows/2)) blocksonrow = screenrows - i; if (blocksonrow > MapLength) blocksonrow = MapLength; if (blocksonrow > MapWidth) blocksonrow = MapWidth; int firstblock; // draw the blocks for (int b = 0; b < blocksonrow; b++) { screenplace.y = (TileHeight*MapHeight)+(i * (TileWidth/2)) - (height * TileHeight); screenplace.y = screenplace.y; Tiles[currblock].TileLocation = screenplace; D3DXVECTOR2 sc2 = screenplace; sc2.y += TileHeight; sc2.x += TileLength / 2; Tiles[currblock].Wall2Location = sc2; sc2.x -= TileLength / 2; Tiles[currblock].Wall1Location = sc2; zOrder[order] = currblock; order++; screenplace.x += TileLength; if (b == 0) { firstblock = currblock; } if (b != (blocksonrow - 1)) { currblock += (MapWidth-1); } else if (b == (blocksonrow - 1)) { if ((i+1) < (MapLength)) { currblock = firstblock - MapWidth; } else currblock = firstblock - 1; } } screenplace.x -= (TileLength*.5)+TileLength*blocksonrow; if ((i+1) > (MapLength-1)) { screenplace.x += TileLength; } } } // end of for for (int c = 0; c < MapLength*MapWidth*MapHeight; c++) { if (TileUsage[c] >= 0) facecount++; if (LWallUsage[c] >= 0) facecount++; if (RWallUsage[c] >= 0) facecount++; } // remove drawing of unnescessary walls for (int index = 0; index < MapLength*MapWidth*MapHeight; index++) { // rwmove right wall drawings if ((index - 1) >= 0 && index % MapWidth != 0) { if (TileUsage[index - 1] >= 0 || TileUsage[index - 1] == NO_DRAW) { RWallUsage[index] = NO_DRAW; } } // remove left wall drawings if (index - MapWidth >= 0 && TileUsage[index] != NO_TILE) { int i = index; while (i >= MapLength*MapWidth) { i -= MapLength*MapWidth; } if (i >= MapWidth && TileUsage[index - MapWidth] != NO_TILE) { LWallUsage[index] = NO_DRAW; } } // remove unessasry walls (if there is no tile there) if (TileUsage[index] == NO_TILE) { LWallUsage[index] = NO_TILE; RWallUsage[index] = NO_TILE; } // remove extra tile overdraw by location for (int index2 = index + 1; index2 < MapLength*MapWidth*MapHeight; index2++) { if (Tiles[index].TileLocation == Tiles[index2].TileLocation && TileUsage[index2] != NO_TILE) { TileUsage[index] = NO_DRAW; } } } for (index = 0; index < MapLength*MapWidth*MapHeight; index++) { if ((index + MapLength*MapWidth) < MapLength*MapWidth*MapHeight) { if ((TileUsage[index + MapLength*MapWidth] >= 0 || TileUsage[index + MapLength*MapWidth] == NO_DRAW) && (LWallUsage[index + MapLength*MapWidth] >= 0 || LWallUsage[index + MapLength*MapWidth] == NO_DRAW) && (RWallUsage[index + MapLength*MapWidth] >= 0 || RWallUsage[index + MapLength*MapWidth] == NO_DRAW)) { TileUsage[index] = NO_DRAW; } } } } float c = 0; void Map::Render() { // prepare the textures for (int t = 0; t < TileImg.NumTiles; t++) { TileImg.TileImg[t]->ImageData->Begin(); } for (t = 0; t < TileImg.NumLWalls; t++) { TileImg.WallImg1[t]->ImageData->Begin(); } for (t = 0; t < TileImg.NumRWalls; t++) { TileImg.WallImg2[t]->ImageData->Begin(); } int times = MapLength*MapWidth*MapHeight; int tiledraw = 0; c+=.01; for (int order = 0; order < times; order++) { if (TileUsage[zOrder[order]] >= 0) { if (View.IsIn(Tiles[zOrder[order]].TileLocation, D3DXVECTOR2(TileLength, TileWidth))) { //if (KEYDOWN(VK_RETURN)) TileImg.RenderTile(TileUsage[zOrder[order]], Tiles[zOrder[order]].TileLocation, D3DCOLOR_ARGB(255,255,255,255)); char tm[6]; itoa(zOrder[order], tm, 10); //font->DrawText(Tiles[zOrder[order]].TileLocation.x+10, Tiles[zOrder[order]].TileLocation.y+8, D3DCOLOR_ARGB(192, 255, 255, 0), tm); tiledraw++; } } if (LWallUsage[zOrder[order]] >= 0) { if (View.IsIn(Tiles[zOrder[order]].Wall1Location, D3DXVECTOR2(TileLength, TileWidth))) { TileImg.RenderLWall(0, Tiles[zOrder[order]].Wall1Location, D3DCOLOR_ARGB(255,255,255,255)); tiledraw++; } } if (RWallUsage[zOrder[order]] >= 0) { if (View.IsIn(Tiles[zOrder[order]].Wall2Location, D3DXVECTOR2(TileLength, TileWidth))) { //if (KEYDOWN(VK_SPACE)) TileImg.RenderRWall(0, Tiles[zOrder[order]].Wall2Location, D3DCOLOR_ARGB(255,255,255,255)); tiledraw++; } } } char tm[30]; /*itoa(tiledraw, tm, 10); strcat(tm, " sprites rendered"); font->DrawText(1, 40, D3DCOLOR_ARGB(255,255,255,255), tm); itoa(facecount, tm, 10); font->DrawText(1, 80, D3DCOLOR_ARGB(255,255,255,255), tm);*/ // end rendering for (t = 0; t < TileImg.NumTiles; t++) { TileImg.TileImg[t]->ImageData->End(); } for (t = 0; t < TileImg.NumLWalls; t++) { TileImg.WallImg1[t]->ImageData->End(); } for (t = 0; t < TileImg.NumRWalls; t++) { TileImg.WallImg2[t]->ImageData->End(); } } void Map::SetTSet(int p_numtiles, int p_numlwalls, int p_numrwalls, Image** p_tileimg, Image** p_wallimg1, Image** p_wallimg2) { TileImg.Create(p_numtiles, p_numlwalls, p_numrwalls, p_tileimg, p_wallimg1, p_wallimg2); } /* typedef class TileSet { int NumTiles; Image** TileImg; // image of tile Image** WallImg1; // left side wall Image** WallImg2; // right side wall public: inline void RenderTile(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { TileImg[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; inline void RenderLWall(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { WallImg1[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; inline void RenderRWall(int p_index, D3DXVECTOR2 p_loc, D3DCOLOR p_color) { WallImg2[p_index]->RenderThis(NULL, NULL, NULL, 0, &p_loc, p_color); }; Destroy(); TileSet(int p_numtiles, Image** p_tileimg, Image** p_wallimg); TileSet(); } TileSet, *TileSet_ptr */ void TileSet::Create(int p_numtiles, int p_numlwalls, int p_numrwalls, Image** p_tileimg, Image** p_wallimg1, Image** p_wallimg2) { NumTiles = p_numtiles; NumLWalls = p_numlwalls; NumRWalls = p_numrwalls; // alloc mem TileImg = new Image*[p_numtiles]; WallImg1 = new Image*[p_numlwalls]; WallImg2 = new Image*[p_numrwalls]; // set the tile sets images for (int i = 0; i < p_numtiles; i++) { TileImg[i] = p_tileimg[i]; } for (i = 0; i < p_numlwalls; i++) { WallImg1[i] = p_wallimg1[i]; } for (i = 0; i < p_numrwalls; i++) { WallImg2[i] = p_wallimg2[i]; } } TileSet::TileSet() { NumTiles = 0; NumLWalls = 0; NumRWalls = 0; TileImg = NULL; WallImg1 = NULL; WallImg2 = NULL; } void TileSet::Destroy() { for (int i = 0; i < NumTiles; i++) { TileImg[i] = NULL; } for (i = 0; i < NumLWalls; i++) { WallImg1[i] = NULL; } for (i = 0; i < NumRWalls; i++) { WallImg1[i] = NULL; } delete WallImg2; delete WallImg1; delete TileImg; } If you actaully managed to look through this thank you verymuch. -Vulcan Edited by - Vulcan on October 23, 2001 2:44:36 PM

Share this post


Link to post
Share on other sites
G''day!

That''s way too much code to reasonably expect people to look at. The first question that springs to my mind is, why are you worries about optimizing so much if you''re getting 225 FPS? If it''s because you want to support really old hardware and slow computers, than fine. Otherwise you''re wasting your time on an area that doesn''t need help.

Now assuming you are targetting old hardware (which isn''t a bad idea) the first thing you should do is get a crappy PC to test on. Crappy PCs are easy to find, all you need is a friend to run it on his/her PC and see what you get.

Assuming you still want to speed things up, ditch ID3DXSprite. Create a VertexBuffer to hold all of your tiles. Put all of your tiles into a single texture(or group of textures depending on how many you''re using, don''t make texture pages larger than 256x256 to support old hardware).

Then when you need to rebuild your display, lock your VB and change the tu & tv coordinates to match the individual tiles, unlock and draw. You''re almost guaranteed to double your frame rate.


Stay Casual,

Ken
Drunken Hyena

Share this post


Link to post
Share on other sites