Skip to main content
gamedev.net
🔒 Locked

Mapengine Dx7

Started by clabinsky 8 replies 900+ views
Original Post
clabinsky
clabinsky
Ok, so I have coded a class that seems far too big and dependent for my taste but since my brain is clogging up I need your advice. A great Stanford Professor taught me to consider all alternatives, so any suggestion is greatly appreciated, good or bad. Here is an explanation the design. 1. I have a base class that contains directx functions to manipulate surfaces CSurfBase. I have one class that inherits CSurfBase called CFrame. CFrame is basically a class holds an offscreen surface and the bitmap associated with it (to facilitate fast restores). Now, the CFrame class also contains an array of RECT, one rect for every sprite/tile on the surface. Since all my bitmaps contain homgene sprite/tile sizes it makes for fast blitting, never need to calculate the source RECT, instead only passing the index of the rect to be blitted. 2. My Map class (huh!) I programmed this in a first stage to see that everything worked and now I need to consider breaking the class apart. The CMapFx class is a singleton. It contains an array of CFrame objects holding all the Tilesets that I need. As I explained each tileset is a bitmap with equal sized tiles (63*63px). When I initiate the CMapFx I pass the number of tilesets I need and dynamically allocate the space needed. When I load a tileset I pass the number of Cols and Rows, Surface height and width, arrayindex of the CFrame, and a filename to load a bitmap from. It loads the bitmap and creates the RECT array. 3. The CMapFx also contains a map array: MAPLOCATION m_Map[MAX_X];[MAX_Y]; The type MAPLOCATION is a struct that can connect a linked list which means that each x,y in the map array can have a linked list - of map layers. When I blit the map, each layer is blitted. The CMapFx contains a number of member variables that are used to keep track of current hex, screenhexes etc. 4. And the most controversial I guess. The CMapFx object also creates a CFrame to hold a clean map surface, that is, this surface is bigger than the primary surface and all map manipulation is done to this surface. The surface size depends on the screen resolution to allow clipping in one operation. I blit the map to the backbuffer by using one pre calculated RECT that is the same size as the screen, so the map is automatically clipped. When I scroll the map, this surface is updated. I memcpy to copy the part of the surface that does not have to be blitted (scrolling right=left part of surface saved exept for the leftmost strip) and blit one edge. This works fine but it means locking operation. Please consider that this program is an updated version of a map editor I did in VB6. It must be able to hold 25 layers in each hex and the total mapsize is 300*300 hexes. If I would not be using memcpy I would have to blit 17*17*25 blits per screen in worst case. With memcpy I need only one lock/unlock and maximum 17*25 blits on one edge, though this is never the case. We currently use 512 63*63 tiles but this might change. I´d like to hear your comments on this and how you think I could/should break the CMapFx class into smaller parts. I have probably forgotten some things but then you are probable bored to death by now. Any comments highly appreciated. I am actually a Swede, living in paradise.
Why make it simple when you can make it sooo nice and complicated?
jesterlecodeur
jesterlecodeur
I can expose my idea (sorry i don''t understand all your post, it''s to late for my english uncoder).

I think using interface (w/ virtual function) is a good way to have independance.

  
IDisplay // This is an interface because you need independance no matter if is DX7 or software

{
int CreateTile( szName)=0; // Return an identifier for the tile

DrawTile( int whichTile, int x, int y)=0; // Draw a spécified tile

SetViewport( int x, int y)=0; // Spécifie the location of the player.

}

CMap
{
public:
SetTile( int TileId, int layer, int xHex, int yHex);
RenderTheMap(IDisplay* lpDisplay, int layertodraw)
{
for each x, y from 0 to 299
lpDisplay->DrawTile( TileId[layertoDraw][x][y], x*61, y*61);
}
private:
int TileId[25][300][300]; // Store the tile to use for each layer from each hex (if I have understood what you mean)

}


I think it will be my way. Unsee tile can easily cull by the engine, and your CMap don''t need to change when the implementation of IDiplay change.

Why English rules?? C pas très malin tout ça!
_______________
Jester, studient programmerThe Jester Home in French
clabinsky
clabinsky
Thanks for your reply jesterlecodeur.

You gave some good ideas there, I think. I think I know how to create the independence and actually without even having analyzed it good enough I think that the MapFx is independent of directX because it does what you say. I have pointers to the CFrame class that is dependent and all the blitting goes through this class. But I might be able to improve it even more because of the way you showed me your example.

By the way, I never loop through the whole map, that is 300 hexes, instead only what´s on the screen; but I think you new that anyway.

Now, how about the way I use an extra surface to manipulate. I have this offscreensurf that holds the current map. This surface is bigger than the backbuffer because I want to blit the whole screen in one go. Since it is bigger, the rect that is used to blit to tha backbuffer automatically clips the tiles correctly. In other words, I never have to adjust the size of a single tile-rect, which I guess saves speed. Whenever the map is scrolled or a new layer is painted on the map, the offscreen surface is adjusted and then the screen is blitted with the big screen-Rect.

Is there a performance hit when blitting a rect that is say, 1024*768 compared to blitting more smaller rects?

I memcpy the way to go when you want to move a big chunk of a surface new coordinates of the same surface?

Should I manipulate the backbuffer instead of having an extra surface?

Please let me know what you think.
Why make it simple when you can make it sooo nice and complicated?
jesterlecodeur
jesterlecodeur
quote:
Original post by clabinsky
1) Is there a performance hit when blitting a rect that is say, 1024*768 compared to blitting more smaller rects?

I memcpy the way to go when you want to move a big chunk of a surface new coordinates of the same surface?

Should I manipulate the backbuffer instead of having an extra surface?

Please let me know what you think.


1) No. I don''t think.

2) I don''t really understand, but just [g]delete[/g] memcpy for your vocable, use blit or some other DX7 API to copy rect.

3) Manipulate backbuffer, if I understood right your idea.

When you need to render (because on the user doing something or else), just send all your tiles to the render engine. The render engine will cull some tiles (most of them in fact) if they don''t fit to the actual player location.

Maybe you may have a problem handling a set of 300²x63²x4 = 1.4Go of texture (the case if all your texture are different [don''t load the same texture twice])? I think it will be the main problem.

Why English rules?? C pas très malin tout ça!
_______________
Jester, studient programmerThe Jester Home in French
clabinsky
clabinsky
Thanks a lot for taking your time!

quote:

1. No I don''t think.


Ok, that is good news if your assumption is right!
quote:

2) I don''t really understand, but just [g]delete[/g] memcpy for your vocable, use blit or some other DX7 API to copy rect.


The problem I have here is to explain why I do as I do, but let me try. I have assumed that memcpy is faster than Blt or BltFast in some instances and that the operations between Lock and Unlock should be optimised as much as possible. There is however a reason why I used memcpy instead of the Blt functions. First of all I tried to blit from and to the same surface but it did not work - now I have read that it should work so memcpy might be the wrong way to go. I will try this later today.

However, please explain what you meant with [g]delete[/g] unless you mean that I should simply delete that code and use Blt instead.

quote:

3) Manipulate backbuffer, if I understood right your idea.


Ok this is a very interesting point. The world map is 300*300 hexes and one screen in 1024*768 is app 17*17 hexes. Now, Sometimes during play other objects like windows, animations etc will be blitted to the backbuffer, thus covering the map. As soon as these objects are removed the backbuffer has to be updated to show the part of the map that was covered. I have not come up with a good way to blit only the part that was covered, instead, since I have a copy of the map on an off screen surface I just use this copy to update the backbuffer. I want the user to be able to scroll while a window partly covers the map. With a separate map surface this is easy, without it I can''t find a good solution.

Think about it, the map on screen has 17*17 hexes, each hex can hold a number of layers (usually<25). If I should blit each tile on this surface it would come out to max 17*17*25 at worst and that is unacceptable. If nothing would be covering the map I could blit part of the backbuffer to itself and only update the edge that needs to be updated because of the scrolling but if a window covers part of the map, using this method would scroll the window and the map! Now, if I had some sort of algorithm that kept track of which hexes are covered by another object I could find out which edge-hexes to updated because of the scrolling and which hexes to be updated because they were behind the window. All this seems soooo complicated compared to having a copy of the map on another surface, blit the copy and re-blit the window.

I don''t expect you to follow my resoning cause I find it kind of hard to explain. If you try, I would be thankful.

quote:

Maybe you may have a problem handling a set of 300²x63²x4 = 1.4Go of texture (the case if all your texture are different [don''t load the same texture twice])? I think it will be the main problem.


I use DirectDraw7 and load tilesets. Each tileset is a bitmap with a number of tiles on it and each tileset has an array with RECT''s attached to it so I can just specify which tile I want from the RECT array. Right now, there is only one tileset that is 1008*2016 px (!) and has 512 tiles. This object has a RECT array with 512 RECT''s, 1 for each tile.

So, I dont'' load tile by tile - is this something you''d recommend me to do? I know it takes up a whole lot of RAM but we estimate that 16MB graphics cars with 32 MB of RAM would be enough to run the game. By the time this is finished, that would be in most computers, I hope

Anyway, thanks again for helping out and hope to hear you comments again.



Why make it simple when you can make it sooo nice and complicated?
jesterlecodeur
jesterlecodeur
Using blit, give you one nice thing, it does not move the surfaces form the video card to the central mémory abd the reverse. That''s why I say to delete

Did you check the clippers fonctions ? For the windows.
One other good thing will be to give to the renderer for each tile a callback. When the tile need to be redraw the renderer call the callback. Do the same for your other graphics. But okay taking from an extra buffer can be good if you need to render 25 layers (I assume they have alpha chanel and then you need to render all layer), but try to avoid rendering all the layer would be a better idea than taking from a backbuffer.

For the big tileset, it can take 8Mo of RAM (in 32bit deepth), it is too much, try loading tileset near 512*512. This is to leave more chance to a tile to be in video memory.

In my post I assume than DirectDraw 7 rules are near DirectGraphics 8 ones.

Why English rules?? C pas très malin tout ça!
_______________
Jester, studient programmerThe Jester Home in French
clabinsky
clabinsky
I really thank you for your interest and...

you surprised me here. Use callback? I have no idea how to do this and why would that be of an advantage?

I know callback functions, I just didn´t know that could be an alternative here!!

However, I think I am going to take your advice: 1. using a clipper, and 2. manipulating the backbuffer. At least to check the difference.

Thing is that my fps really sucks right now and I think it is because of casting in a loop. But I´ll change that.



Why make it simple when you can make it sooo nice and complicated.
Why make it simple when you can make it sooo nice and complicated?
clabinsky
clabinsky
Ok, thanks.

You got my interest so I am going to look into it anyway.

Why make it simple when you can make it sooo nice and complicated?
Shannon Barber
Shannon Barber
You can probably get more specific information about efficent blitting in the graphics forum. I wouldn''t make the back buffer larger than the primary buffer, as this changes it from a page flipping operation into a blitting operation. Clip the map tiles when you blit them onto the back buffer.

In days of yore, they never would have dreamed of being able to have 30 or more (dynamic) layers of tiles. There''s a few discussions about ways of implementing such a huge combination of tiles - one was in this forum not too long ago.

Basically, you use data compression techniques - capitalize on the redundancy of the information in the map.

I also would make the tiles 64x64 pixels, as many cards can hardware accelerate blits that fall on power of 2 sizes.

New video cards have 32 or even 64 megs on RAM on them. But that doesn''t even matter that much. It probably won''t make a _huge_ difference to a tile based game as they generally don''t run in free-flowing simulated time - so the frame rate needs to be decent for smooth scrolling, but it doesn''t need to be anything close to the 100 that''s desirable in Quake.

Lastly, you can use the rtdsc op code to get CPU tick accurate timing information to profile your code and locate the exact chunks that are slow.
The trade-off between price and quality does not exist in Japan. Rather, the idea that high quality brings on cost reduction is widely accepted.-- Tajima & Matsubara

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.