Archived

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

D3D 9.0, Alpha Blending and 2D Tiles

This topic is 5004 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

OK folks, I''m stumped. I''m dipping my toes into the world of 3D (specifically Direct3D 9.0) because I want to support alpha blending in version 2.0 of the Scrolling Game Development Kit, and it seems like the best way to get hadrware alpha blending is by using the 3D hardware. I''m exploring technologies that I will use for the new version and Direct3D has just blown me away. I could probably figure this out if I spend a couple weeks familiarizing myself thoroughly with Direct3D, but I don''t think I need to spend that kind of time when my usage of the technology will be so limited. So I have 2 major questions I hope you can help me out with (each have many sub-questions). I looked through the articles & resources section and couldn''t find anything current, and a quick search of this forum didn''t give me the answers I was looking for. (I''m using Managed DirectX 9.0) 1. What''s the most efficient way to draw a screen full of tiles from a map that can be millions of tiles in size, and scroll it pixel by pixel. In the 2D world, DirectDraw could do this very effectively because I created an off-screen surface with all my tiles on it and I would simply copy rectangles (with BltFast) from the off screen surface to the visible surface according to the current scroll position of the map. Taking into account the scroll position of the map was easy because I just looked at a different part of the map array and offset my source and target positions for the copy accordingly. (The source position is offset according to the tile value and possibly clipping. The target is offset by the position within the visible window of the tile currently being drawn.) In Direct3D, however, it looks like I have a few limitations and I maybe haven''t seen the best option yet. I noticed that textures are similar to DirectDraw''s off-screen surfaces and am wondering if I should be using those as my "tilesets". I see they have to be a power of 2 pixels in size, and I think I could deal with that, but so many sub-questions: 1A) Does a texture have to be square? 1B) Is there a limit to a single texture size other than available video memory? (I want to be able to do 256 tiles and allow each tile to be up to 128x128 pixels.) 1C) Is it inefficient to be specifying u and v coordinates with floating point numbers -- should I pre-calculate an array of floating point numbers to pass to Direct3D so I don''t have to do the math each time to find the tile on the texture? 1D) I don''t think I should be specifying the texture coordinates at each vertex in a large rectangular array of vertecies to draw the map, and every frame, go through each vertex and update the texture coordinates it references based on what tile will need to be drawn at that position. I would like to draw the tiles one by one as I did with DirectDraw and have it be as fast as it was with DirectDraw. Is this possible? (My experiment is using a triangle strip -- is there another better way to draw tiles?) 1E) If I have to make a separate texture for each tile and draw it, it looks like I have to call SetTexture each time I draw a tile. Is there a better way? 1F) It''s conceivable that I could jsut set up thw whole map ahead of time and set all the vertecies'' textures when the map is initialized and just set the camera/transform or whatever to look at the right part of the map. I don''t think this is a good idea, though, because I only want the engine to be concerned with the piece of the map that is visible. With the DirectDraw method, the code knew how to jump right to the rectangle within the 2D array of tiles where the visible window was scrolled to. It wouldn''t even consider tiles outside this window. That meant you could have maps with millions of tiles that would perform as well as a map that was only 1-screen big. 2) I managed to load a PNG file into a texture and verify that its alpha was working, but only partly. 2A) The trianglestrip on which the texture was drawn seemed to appear solid white behind the transparent areas of the texture. Can''t I make it truly transparent so you see whatever was really there before the draw operation happened? 2B) The semi-transparent (translucent) areas of the texture did not seem to be taken into account. When I drew the PNG image directly on the window, I could verify that .NET in fact loaded the semi-transparent info from the PNG, but when I uset that same image as a texture, the semi-transparent areas appeared solid against the triangle-strip''s white background, rather than 50% white. "All you need to do to learn circular logic is learn circular logic"

Share this post


Link to post
Share on other sites
1A) as far as i know, no. however it must be a power of two in both directions. if the image file is not a power of two, it will be stretched, and depending on the loading filter type, it might look pretty bad.

1B) with DX9 compatible cards, no. older cards (really old) didn't use to let you use textures bigger than the screen res, but with any modern DX9-compatible card, you will have no problem with 256x256 textures. you will be limited to the number of textures by memory, of course.

1C) i take it you're making a bunch of quads and texturing each quad with a portion of a "source" texture that contains all the tiles in one image. it would probably be a good idea to keep track of the texture coords of each tile in the source image, and keep track of the tile number of the tiles on the map. that way you can do a simple lookup in the tile coord table when you need to set the UV coords of a map tile.

1D) you can draw tiles one by one if you draw each as a separate quad (or as the same quad but at a different position and with a different texture).

1E) nope. it's probably a better idea to use one big source texture in the first place, so you only have to make one SetTexture for the entire map.

1F) it's not a very good idea to do that, because if you had something like a 5000x5000 map, that would be 50 million polys (2 polys for each tile). my suggestion woul be to draw them tile by tile with the method i suggested in 1D - use one quad, set the coords, position it, draw it, repeat. that way you could just draw the onscreen tiles.

2) this whole problem sounds like you're not setting the rendering engine up right. try using the following in your setup code:

gpD3DDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);
gpD3DDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);

where gpD3DDevice is your pointer to your device. this should make the alpha work.

also be sure when you're loading the texture that you load it with the correct format. it needs to be a format like D3DFMT_A8R8G8B8.

hope i've helped

[edit] hmm, i was thinking about it, and perhaps you could use the ID3DXSprite interface for this. it lets you draw portions of a texture to the screen with no 3d math or anything involved.

this is an example of how it could be used..


LPD3DXSPRITE sprite;
D3DXCreateSprite(gpD3DDevice,&sprite);


// in the main loop



gpD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0);
sprite->Begin();


// for each tile on the screen

// perform a lookup to see which tile to use, so we know what UV coords to use


tile=map[x][y];
RECT r;
r.left=tilesetcoords[tile].u1;
r.right=tilesetcoords[tile].u2;
r.top=tilesetcoords[tile].v1;
r.bottom=tilesetcoords[tile].v2;
sprite->Draw(texTileset,&r,NULL,NULL,0,&D3DXVECTOR2(screenx,screeny),0xFFFFFFFF);


// done drawing the map


sprite->End();


this is really just pseudocode, but you get the idea.

[edited by - Drakex on April 3, 2004 5:06:39 PM]

[edited by - Drakex on April 3, 2004 5:07:32 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by Drakex
1A) as far as i know, no. however it must be a power of two in both directions. if the image file is not a power of two, it will be stretched, and depending on the loading filter type, it might look pretty bad.



Yes, but if you know the dimensions of the texture you can correct this by squishing it in the opposite direction when rendering it.

quote:

1B) with DX9 compatible cards, no. older cards (really old) didn''t use to let you use textures bigger than the screen res, but with any modern DX9-compatible card, you will have no problem with 256x256 textures. you will be limited to the number of textures by memory, of course.



Not quite. It''s all video-card dependent. You have to check the device caps in order to see what texture size the video cards can handle. (use GetDeviceCaps or something)
Old Voodoo cards can only take 256x256 while most cards these days can handle up to 4096x4096.

quote:

[edit] hmm, i was thinking about it, and perhaps you could use the ID3DXSprite interface for this. it lets you draw portions of a texture to the screen with no 3d math or anything involved.

this is an example of how it could be used..


LPD3DXSPRITE sprite;
D3DXCreateSprite(gpD3DDevice,&sprite);


// in the main loop



gpD3DDevice->Clear(0,NULL,D3DCLEAR_TARGET,0,1.0f,0);
sprite->Begin();


// for each tile on the screen

// perform a lookup to see which tile to use, so we know what UV coords to use


tile=map[x][y];
RECT r;
r.left=tilesetcoords[tile].u1;
r.right=tilesetcoords[tile].u2;
r.top=tilesetcoords[tile].v1;
r.bottom=tilesetcoords[tile].v2;
sprite->Draw(texTileset,&r,NULL,NULL,0,&D3DXVECTOR2(screenx,screeny),0xFFFFFFFF);


// done drawing the map


sprite->End();


this is really just pseudocode, but you get the idea.



he''s absolutely right. ID3DXSprite kicks major butt. It''s also blazingly fast if you get the 9.0b Summer Update version of DirectX.

Go with ID3DXSprite.

Share this post


Link to post
Share on other sites
Thanks for the input on number 1. I was looking through the billboard example in the DirectX 9.0 SDK and came to a similar conclusion. But I do like this sprite idea. But what everybody seems to be missing is the "Managed" in "Managed DirectX 9.0". All the documents and posts I've looked at are referring to C++ type code and what appears to be un-managed classes / interfaces from DirectX 9.0. I can't find any Sprite class among the managed DirectX 9.0 framework. (I'm using C#)

Also, I still can't figure out why I'm not getting any alpha on this. Does it work for anyone else? (If it does, maybe I just have a bad PNG file.)


PresentParameters pp = new PresentParameters();
Bitmap img = (Bitmap)Bitmap.FromFile(@"C:\Documents and Settings\Ben\Desktop\Tex1.png");

pp.DeviceWindow = this;
pp.Windowed = true;
pp.SwapEffect = SwapEffect.Discard;

Device dev = new Device(Manager.Adapters.Default.Adapter, DeviceType.Hardware, this,
CreateFlags.HardwareVertexProcessing, pp);

Texture tex1 = Texture.FromBitmap(dev, img, 0, Pool.Managed);
dev.SetTexture(0, tex1);

dev.VertexFormat = CustomVertex.TransformedColoredTextured.Format;

VertexBuffer vb = new VertexBuffer(typeof(CustomVertex.TransformedColoredTextured),
4, dev, 0, CustomVertex.PositionColoredTextured.Format, Pool.Default);

// I've tried TransformedColored with a transparent color;

// I've tried TransformedTextured with no colors, just textures

// (got white background);

// I've tried PositionedColoredTextured -- then nothing appeared at all

CustomVertex.TransformedColoredTextured[] arv =
(CustomVertex.TransformedColoredTextured[]) vb.Lock(0, 0);

arv[0] = new CustomVertex.TransformedColoredTextured(0,0, .5f, 1, Color.Red.ToArgb(),0,0);
arv[1] = new CustomVertex.TransformedColoredTextured(100,0,.5f, 1, Color.Green.ToArgb(), 2f,0);
arv[2] = new CustomVertex.TransformedColoredTextured(0,100,.5f, 1, Color.Violet.ToArgb(), 0,2f);
arv[3] = new CustomVertex.TransformedColoredTextured(100,100,.5f, 1, Color.Transparent.ToArgb(), 2f,2f);

vb.Unlock();

dev.RenderState.Lighting = false;
dev.RenderState.AlphaSourceBlend = Blend.SourceAlpha;
dev.RenderState.AlphaDestinationBlend = Blend.InvSourceAlpha;
dev.RenderState.AlphaBlendEnable = true;

dev.Clear(ClearFlags.Target, Color.Black, 1.0f, 0);
dev.BeginScene();
dev.SetStreamSource(0, vb, 0);
dev.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
// Squish the image and draw it again over the top of itself to make

// it more obvious that there's no transparency / translucency.

arv = (CustomVertex.TransformedColoredTextured[]) vb.Lock(0, 0);
arv[0].X = arv[0].X + 50;
arv[2].X = arv[2].X + 50;
vb.Unlock();
dev.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 2);
dev.EndScene();
dev.Present();
dev.Dispose();


"All you need to do to learn circular logic is learn circular logic"

[edited by - BlueMonk on April 4, 2004 9:41:19 AM]

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
You have to add Microsoft.DirectX.Direct3DX to your references to get the Sprite class.

Share this post


Link to post
Share on other sites
Thank you so much! Everything works now! I got a texture to draw to the screen with all the tranlucency I expected. And adding the new reference to my project allowed me to also see where this "TextureLoader" I kept seeing reference to was coming from. It''s all downhill from here

"All you need to do to learn circular logic is learn circular logic"

Share this post


Link to post
Share on other sites
I see that I can even apply arbitrary transformations to the sprites if I want to rotate and flip them in real time, which will be quite nice. So I don't think I need to use anything except sprites. However, if anyone can tell me what's wrong with the way I was drawing textured triangle strips (as far as getting the alpha to work) I would be interested to know. I must have forgotten to set some kind of alpha flag on the triangle strip or something.

"All you need to do to learn circular logic is learn circular logic"

[edited by - BlueMonk on April 5, 2004 7:30:59 AM]

Share this post


Link to post
Share on other sites