Skip to main content
GameDev.net gamedev.net
🔒 Locked

D3D Tiling

Started by Zorbfish Jun 22, 2003 at 10:29 AM 15 replies 6k views
Original Post
Zorbfish
Zorbfish
First off I''ve been doing a bit of searching but have been unable to find a tut on square tile engines in d3d. I''ve found plenty of iso ones, which has given me at least an idea how to set up tiling. Here''s a few questions so far: 1. What''s the best primitive type to use for rendering my tile quads? I know that strips are the fastest, but from the examples I''ve seen no one uses them. And I can''t figure out how to set them up (because they have to all be interconnected). 2. Many of the examples use untransformed vertices, which I''m using, but few of them keep their vertices in cube space. Does it matter if the vertices are not in cube space originally? 3. I''m still confused how to setup a tile vertex buffer. Some use on quad and just transform it over and over, making multiple calls to drawprimitive. Others have huge buffers with every tile in them. How exactly should I approach this?
Zorbfish
Zorbfish
quote:
Original post by Zorbfish
2. Many of the examples use untransformed vertices, which I''m using, but few of them keep their vertices in cube space.


My mistake I meant model space.

Also to mention what I have managed to get working:

- Create my orthogonal projection
- Create one static vertex buffer with a quad for each type of tile (only difference being the texture coordinates).
- Load the external map into memory
- Loop through the map, translating and scaling each tile and specifying the vertex offset in the buffer as the tile index * 4

I''m still trying to get smooth scrolling to work. I tried just using the algorithm I used for ddraw but I can''t figure out how to figure in the correct positions of the tiles.

Zorbfish
Zorbfish
Has anyone done a tile engine in D3D that could provide some insight? Or all of you just going to keep viewing the thread and queitly slipping away?
szevvy
szevvy
This is what I'm using:


void _D3DRenderHandler::drawTexture(float XTL, float YTL, float XTR, float YTR,
float XBL, float YBL, float XBR, float YBR,
IDirect3DTexture8 *texture)
{
CVertex *vtxQuad;
g_pVertexBuffer->Lock(0, 0, (BYTE **)&vtxQuad, NULL);
vtxQuad[0].Create(XTL, YTL, 0, 0, 0);
vtxQuad[1].Create(XTR, YTR, 0, 1, 0);
vtxQuad[2].Create(XBL, YBL, 0, 0, 1);
vtxQuad[3].Create(XBR, YBR, 0, 1, 1);
g_pVertexBuffer->Unlock();
g_pDevice->SetStreamSource(0, g_pVertexBuffer, sizeof(CVertex));
g_pDevice->SetVertexShader(D3DFVF_CVertex);
g_pDevice->SetTexture(0, texture);
g_pDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
}

void _D3DRenderHandler::drawRect(int X1, int Y1, int X2, int Y2, IDirect3DTexture8 *texture)
{
drawTexture(X1, Y1, X2, Y1, X1, Y2, X2, Y2, texture);
};


also, my _D3DRenderHandler has defined:

IDirect3DVertexBuffer8 *g_pVertexBuffer;

you'll also need:


class CVertex
{
public:
// Its coordinates
float fX, fY, fZ, fRHW;

// Its texture coordinates
float fU, fV;

// A function to simplify initialization
void Create(float fp_fX,float fp_fY,float fp_fZ, float fp_fU, float fp_fV)
{
fX=fp_fX;
fY=fp_fY;
fZ=fp_fZ;
fU=fp_fU;
fV=fp_fV;
fRHW=1.0f;
}
};
// The CVertex FVF
const DWORD D3DFVF_CVertex=D3DFVF_XYZRHW | D3DFVF_TEX1;


Now, mine's isometric, but the principle's the same. I just go through from the minimum to maximum x and y, work out co-ords for that tile, and draw it.

(Sorry for the crap post, I'm doing this veeeery late at night, hopefully it'll give you something to work with till I fix it up tomowrrow.



[edited by - szevvy on June 25, 2003 7:24:49 AM]
Zorbfish
Zorbfish
Appreciate the posting, szvevvy. But does your code run fast? You''re doing a lock, texture set, and drawprimitive call every time you draw a quad which I think would be really slow for 3+ layers of tiles (what I''m shooting for).

I finally managed to get my tiles to smooth scroll. When I get it all done I plan on open sourcing the code and creating a tutorial set.
ebille
ebille
a much better approach is to create one large texture with all the tile graphics in it, then have the tile coordinates for each vertex reference it. This requires no texture change during the inner loop.
Zorbfish
Zorbfish
That''s what Im doing. I have one vertex buffer that in some way can be considered a tileset. It contains every tile in the tile texture. Right now Im trying this approach instead of indexed quads. So it makes accessing the right texture section quite easy. For example:

int tileindex = map[y][x];

device->DrawPrimitive(D3DPT_TRIANGLESTRIP, tileindex * 4, 2);

So I specify the quad''s offset by tileindex. Only problem is that I make many SetTransform and DrawPrimitive calls (one for each tile) which I read can be as bad as multiple lockings in one frame. I''m going to experiment with it this weekend when I have more free time.
Sil
Sil
quote:
Original post by ebille
a much better approach is to create one large texture with all the tile graphics in it, then have the tile coordinates for each vertex reference it. This requires no texture change during the inner loop.


This always seemed to me like the best idea. It is one of two ways to create a tile engine that I know of:

1. Create the geometry that is basically a grid of quads, create the texture that is placed on top of the geometry (which is what ebille suggested).

2. Store every possible textured quad in memory and render it when it is needed on screen (which is what Zorbfish is using).

The problem with the first one is your map is limited to the amount of texture information you can store in memory, but it renders things nicely and quickly. The problem with the second is that it is slow, but maps can be almost infinitely large because textures are repeated.

I like the first method because you can do anything graphically to the texture such as alphablending without any cost to the processor in game. A texture is a texture once it''s there in memory. But I''m finding myself more and more limited to using maps that are only 16x16 tiles, containing tiles that are 32x32 pixels, because the texture would then be 512x512, and I place 9 of them together to get what you see on screen plus what is drawn just offscreen to speed up scrolling/map loading times. It’s very difficult to set up and I don’t have it working perfectly, but the idea is to combine the two. I’d really like to find a tutorial on how Ultima Online’s tile system works if it is not just done by the second method using some assembly language to speed it up.
Unwise owl
Unwise owl
It isn''t illegal to switch textures a few times in your rendering loop. You can have many 32x32 tiles stored in a few 256x256 textures (as many as needed), and still save texture state changes; compared to each tile stored in a 32x32 texture. You could keep similar tiles in the same 256x256 texture, and the tiles would then most likely connect to each other physically in the game world. In turn that would make them appear in the same place in your rendering loop, and you''d only need to switch textures as necessary. Still it needs to be said that any tileset, twhich can be kept in a single texture should be done so.

You can also have different textures for each layer. Your fringe layer for example might require alpha blending, which your base layer does not. A simple solution would be to change texture and alpha blending as required for each layer only once (although it contradicts what I''ve said above).

All this makes me wish back to the simple days of Direct Draw...
Zorbfish
Zorbfish
quote:
Original post by Sil

1. Create the geometry that is basically a grid of quads, create the texture that is placed on top of the geometry (which is what ebille suggested).




Could you share a way to generate this geometry? I was originally going to implement it this way, but I could not figure out how to arrange my vertices. Also as you have to use an index buffer to speed up the process which again adds memory. Also I''m unsure whether a dynamic vertex buffer would be useful in this situation. The sdk, and many people on these boards, suggest that thousands of vertices are rendered as a minimum.

For example since I''m working in 640x480, I need to render 21x16 32x32 tiles which is totals at 4032 vertices (don''t know how many indices) and I think that''s not enough to be worthwhile.

If it was then I''d be able to see how to combine them:

1) Create an external tiledata file containing all texture offsets.
2) Load this and store it in an array.
3) Lock the dynamic buffer, update the currently viewed tiles with their correct texture offsets by map position.
4) Render all the layers in 3 calls

Sil
Sil
It takes a while to figure out, but in the end it isn't too hard setting up both an index buffer and vertex buffer for your quads.

First, you create the vertices, which for 21x16 tiles will be 22x17 vertices (the amount of pixel data in the tile doesn't matter at this point). You create a loop to put them into a grid shape, still retaining the single dimension of the array.

(quick example code in visual basic)


for i = 0 to 21
for j = 0 to 16
vtile(i * 17 + j).x = i '//because 0 to 16 is 17 numbers
vtile(i * 17 + j).y = j
next j
next i


This should fill a vertex array vtile(0 to 373) that can have up to 374 vertices.

Now that all the vertices are there, you have to build the triangle lists using an index array. Each quad will consist of 2 triangles, each with 3 sides, therefore: 374 * 2 * 3 = 2244 indices.


for i = 0 to 20 '//subtract 1 because we want to stop at the end vertices
for j = 0 to 15 '//ditto

itile(k + 0) = i * 17 + j + 0
itile(k + 1) = i * 17 + j + 1
itile(k + 2) = i * 17 + j + 18
itile(k + 3) = i * 17 + j + 0
itile(k + 4) = i * 17 + j + 18
itile(k + 5) = i * 17 + j + 17

k = k + 6
next j
next i


That may look complicated, but if you grab some graphing paper and map out the triangle lists (perhaps on a smaller grid), you will see each index points to the correct vertex. This fills an index array itile(0 to 2243).

[edited by - Sil on June 26, 2003 2:35:27 PM]
Zorbfish
Zorbfish
Actually I had done it out on graphing paper while in math class, I just couldn''t get the algorithm for generating it correct, thanks Sil.
creed1
creed1
I''ve been working on something similar to this, but I ran into a problem. I''m not exactly sure how to implement a single texture that holds all of my tiles.

I have the tileset being loaded as a single texture. All of my tiles are 16x16 and are arranged vetically in the tile set. Do I just need to set the tu and tv values of the vertex buffer according to the position of the tile that I want to use?

For example, how would I tell the vertex buffer to use the third tile in the tile set? The top, left coordinates of this tile would be 0, 32 in the tile set.

Thanks!

Sil
Sil
quote:
Original post by creed1
For example, how would I tell the vertex buffer to use the third tile in the tile set? The top, left coordinates of this tile would be 0, 32 in the tile set.



As you are probably already aware, you can simply draw a single tile/quad at a time, changing the tu,tv coordinates accordingly. However, if you want to render a vertex buffer filled with quads, you only get one texture to work with.

There are three ways that I know of to overcome this. The first way is good for rpg tiling, the second and third are good for large, complex landscape rendering.

1. Draw everything you need, a single tile/quad at a time, to an offscreen surface that becomes your texture for the entire map in the vertex buffer:

-load your texture set
-create a separate texture that will be the render target
-set this render target to be the destination of all rendering, preferably in 2D
-render your textures here as you would in directdraw, without a vertex buffer
-set the rendering back to normal, and use the render target as your texture for the map in the vertex buffer

2. Use single-pass or multi-pass rendering. I dislike this method because as far as I can tell, you can only blend the tiles based on the diffuse and specular alphas of the vertices. But, this is where you can get some realistic looking landscapes, so it’s not entirely bad.

3. Get every texture you will be using and make them 16x16 in their own separate bmp, tga or dds files. Load them in the program as an array, and also create a destination texture that is 16x16 where they will be headed in some form. Now you can loop through each texture, locking them, and moving their pixel data over to the destination texture based on how you want your map tiled. Yes, this means every tile for a 16x16 map gets a single pixel. If you use 64x64 textures, every tile gets 4 pixels, etc. While the textures are locked, you can do some blending to make it look a little bit decent, but it is not really worth it in the end.

If you need clarification on any of these methods, let me know. If you’ve heard of any other ways to do some tiling in direct3d, let me know as well.

And something you should look into: texture coordinates are not done using whole numbers as you seemed to have suggested, but ratios of the entire texture set. That means if you have a texture set that is 256x256 pixels and each texture inside is 16x16 pixels each, the third tile’s left,top coordinates would be:


tu = 0
tv = 2/16


...but it is not just that easy. You will have to specify in the other three/five vertices that make up the quad where the ends are:


tutv(0/16,2/16)-----tutv(1/16,2/16)
|
|
tutv(0/16,3/16)-----tutv(1/16,3/16)


[edited by - Sil on June 29, 2003 2:39:30 PM]
Zorbfish
Zorbfish
quote:
Original post by Sil
1. Draw everything you need, a single tile/quad at a time, to an offscreen surface that becomes your texture for the entire map in the vertex buffer:

-load your texture set
-create a separate texture that will be the render target
-set this render target to be the destination of all rendering, preferably in 2D
-render your textures here as you would in directdraw, without a vertex buffer
-set the rendering back to normal, and use the render target as your texture for the map in the vertex buffer



Also could be done by writing direct to your backbuffer just like ddraw. In both variations its rather slow.

Sil
Sil
Actually, if you create a static texture using a render target to be used with a static vertex buffer before you enter the rendering loop, it''s probably as fast as you can get. I even hear stories of dynamic vertex buffers having sufficient speeds, though I''ve never used them myself.

The biggest obstacle I''m trying to overcome at the moment is rendering large maps by breaking them down into sections, and then getting them to texture properly. I think this means having to create textures during the render loop, so I''m kind of at a lost. I read this tutorial on texture splatting, but I can''t visualize yet what I have to do.

Topic Locked

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

Sign in to reply to this topic.