Textured Quads

Started by
15 comments, last by Programmer16 18 years, 9 months ago
I've been reviewing the most efficient method of using D3D to do 2D stuff. On terms of blittering bitmaps, (surface to surface copying) I found that textured quads are the best and fastest method of blittering (Direct3D version 9). The thing is, even though I read the tutorial named "2D in Direct3D using Textured Quads" by Eamonn Doherty, I still don't understand this. I have a load of confusion and would like some help... So here we go: 1) My first question lies in the content in the custom vertex structure. Is it really required to have a color value AND a texture coordinate? I have no idea what the texture coordinate is for, nor do I have any idea why the vertices would have a color. They are just coordinate specifications for the location of the bitmap, I don't want lines to be drawn or colored pixels to be shown... just the bitmap pixels!! Can I leave these out? 2) What is RHW for? I've been through many websites and I have also asked a few math professors at my college and I have learned nothing about its usefulness. I just don't get it, that's all I can say. 3) When you are filling in the vertex buffer, how does it know how large to make the buffer? For example, ::Lock() returns a BYTE pointer to the beginning of the vertex buffer, yet it doesn't tell you how large the buffer is. How do I know if I'm going to create a buffer overflow? 4) The set of custom vertices I create are the DESTINATION of the bitmap to be blittered? I have no idea where these verticies will go. I'm assuming they will be placed on the backbuffer so that it will display when flipped. 5) IDirect3DDevice9::SetTexture() asks for the texture to which my sprite sheet is located. This texture contains a loaded bitmap with several sprites bunched together. How do I tell it ONLY to blitter a specific area of the texture, rather than the whole thing? 6) IDirect3DDevice9::DrawPrimitive() is also confusing in terms of its second and third parameters. Could someone emphasize the purpose of these parameters? I have reviewed MSDN yet I still remain clueless. As you can see, I am uselessly stuck on the concept of textured quads. I am actually getting quite frustrated because this shouldn't be so hard for me, yet it is anyway. Below I'm going to paste the code I have used to test out my blitter. The code assumes a valid device and texture, which I do have. My code fails at ::DrawPrimitive(). custom vertex structure:

struct BitmapVertex
{
    float x;
    float y;
    float z;
    float rhw;
    D3DCOLOR Color;
    float tu;
    float tv;
};
Blitter function:


// Vertex Buffer, Texture, and Device are all located externally

int Blitter(int dest_x, int dest_y, RECT* source)
{
    //--Variables---------------------------------------------------
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    BitmapVertex*               Quad                            = 0;
    //--------------------------------------------------------------

    if(m_VertexBuffer->Lock(0, 0, (void**)&Quad, 0) != D3D_OK)
        return 0;

    Quad[0].x       = (float)dest_x;
    Quad[0].y       = (float)dest_y;
    Quad[0].rhw     = 1.0;
    Quad[0].tu      = 0.0;
    Quad[0].tv      = 0.0;

    Quad[1].x       = (float)dest_x + (source->right - source->left);
    Quad[1].y       = (float)dest_y;
    Quad[1].rhw     = 1.0;
    Quad[1].tu      = 1.0;
    Quad[1].tv      = 0.0;

    Quad[2].x       = (float)dest_x + (source->right - source->left);
    Quad[2].y       = (float)dest_y + (source->bottom - source->top);
    Quad[2].rhw     = 1.0;
    Quad[2].tu      = 1.0;
    Quad[2].tv      = 1.0;

    Quad[3].x       = (float)dest_x;
    Quad[3].y       = (float)dest_y + (source->bottom - source->top);
    Quad[3].rhw     = 1.0;
    Quad[3].tu      = 0.0;
    Quad[3].tv      = 1.0;

    m_VertexBuffer->Unlock();

    if(m_Device->SetTexture(0, m_Texture) != D3D_OK)
        return 0;

    if(m_Device->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2) != D3D_OK)
        return 0;

    return 1;
}
Advertisement
No expert myself, might be wrong... the standard caveats.

1) Yes. <br><br>The texture coordinate are 'pegs' to map the texture to the verteces. You can change the orientation of the bitmap by changing the texture coordinates. They're also used to render &#111;nly a portion of a larger image. Since texture switches are very, very slow a common trick is to put multiple images &#111;n a single texture and then use the texture coordinates to specify what image to use.<br><br>The color value is a mask [for my setup anyways]. If you render a bitmap to a polygon with &#111;nly blue set, then &#111;nly the blue parts of the bitmap will be rendered. If you render it with 'black' nothing will show up. This might &#111;nly be with diffuse set. Not sure.<br><br>2) Dunno. Always have it set to 1. I knew vaguely at &#111;ne time…<br><br>3) Looking back at my code, you set the size in CreateVertexBuffer; first parameter. And msdn concurs.<br><br>4) Yes, for all purposes. <br><br>5) See above, the texture coordinates. I've not done this myself yet.<br><br>6) The second parameter is an index.<br>it is essentially <tt>vertexes[parameter_2]</tt> in your vertex_buffer.<br><br>the third parameter is the number of primitives [triangles, lines, points…] to make the vertexes into.<br><br>If you specify <tt>drawprimitive(…,0,1)</tt> it will start at index 0, and draw &#111;ne primitive. If you're using triangle lists for example, it will make vertex 0,1, and 2 into a triangle.<br><br>
Hi, I'm not too sure why the code fails at DrawPrimitive, but all I can see is, you haven't set any Z values for the quaternion... and also, did you specify the FVF format when you created the vertex buffer? your custom FVF should be: D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1. Also... before you call the DrawPrimitive function, did u call the SetStreamSource and SetFVF functions?

perhaps you can post here the errors you are getting....

hope I've helped...

-fuchiefck
-fuchiefck----------------------------------------------------------"Inside the world that you as a programmer or developer create, you are God" - Zerbst & Duvel
Quote:Original post by Telastyn
No expert myself, might be wrong... the standard caveats.

1) Yes. <br><br>The texture coordinate are 'pegs' to map the texture to the verteces. You can change the orientation of the bitmap by changing the texture coordinates. They're also used to render &#111;nly a portion of a larger image. Since texture switches are very, very slow a common trick is to put multiple images &#111;n a single texture and then use the texture coordinates to specify what image to use.<br><br>The color value is a mask [for my setup anyways]. If you render a bitmap to a polygon with &#111;nly blue set, then &#111;nly the blue parts of the bitmap will be rendered. If you render it with 'black' nothing will show up. This might &#111;nly be with diffuse set. Not sure.<br><br>2) Dunno. Always have it set to 1. I knew vaguely at &#111;ne time…<br><br>3) Looking back at my code, you set the size in CreateVertexBuffer; first parameter. And msdn concurs.<br><br>4) Yes, for all purposes. <br><br>5) See above, the texture coordinates. I've not done this myself yet.<br><br>6) The second parameter is an index.<br>it is essentially <tt>vertexes[parameter_2]</tt> in your vertex_buffer.<br><br>the third parameter is the number of primitives [triangles, lines, points…] to make the vertexes into.<br><br>If you specify <tt>drawprimitive(…,0,1)</tt> it will start at index 0, and draw &#111;ne primitive. If you're using triangle lists for example, it will make vertex 0,1, and 2 into a triangle.<!–QUOTE–></td></tr></table></BLOCKQUOTE><!–/QUOTE–><!–ENDQUOTE–><br><br>Cool so how do I use the texture coordinates? The tutorial says specifically that u and v may &#111;nly be between 0 and 1. What are the mathematics behind the texture coordinates? Could you explain, in detail, how they work? I need to know how to use them.<br><br>Thanks!
Quote:Original post by fuchiefck
Hi, I'm not too sure why the code fails at DrawPrimitive, but all I can see is, you haven't set any Z values for the quaternion... and also, did you specify the FVF format when you created the vertex buffer? your custom FVF should be: D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1. Also... before you call the DrawPrimitive function, did u call the SetStreamSource and SetFVF functions?

perhaps you can post here the errors you are getting....

hope I've helped...

-fuchiefck


Yes, I apologize. You just mentioned a lot of code that I left out. I'll provide more examples of code I have below:

// My DWORDDWORD m_VertexFormat = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1;// Initialize the VertexBuffer, etcif(m_Device->CreateVertexBuffer(sizeof(BitmapVertex) * 4, 0,    m_VertexFormat, D3DPOOL_DEFAULT, &m_VertexBuffer, 0) != D3D_OK){    return 0;}


This is all the code I have involved with Textured Quads.

The other things you speak of I obviously haven't done, because I have no idea what they do / mean. I also have no idea how to set that stuff up.

Mind telling me what else I need to do?

Also, I receive no "errors", I force my application to terminate when one of the functions fail, this is how I debug. All I can tell you is that DrawPrimitive does *not* return D3D_OK.

Thank you greatly!
This might be completely wrong, as I've never used them really, and it's been maybe 9 months since I've done much DX work...

The UV coordinates are percentages. If you have 4 images on a sheet for example, the UV coords to place the lower right image onto a TQ would be [clockwise from UpLeft](.5,.5),(1,.5),(.5,1),(1,1). It's trying to map the entire sheet to the polygon, but the UV coords keep it confined to the image you want.

[edit below]:

As for the setup stuff....

This is code I have for 'render image' in my old system:
rtn=guiroot->d3ddev->SetStreamSource(0,vb,0,sizeof(d3d_vertex));rtn=guiroot->d3ddev->SetFVF(vb_format);rtn=guiroot->d3ddev->SetTexture(0,tptr);

(DrawPrimitive is the next line).
vb is the vertex buffer.
vb_format is the vertex format.
tptr is ... IDirect3DTexture9 *tptr;

That is most likely mucho overkill. I don't remember what the 0 parameters are, I've never changed them.

As for the one time initialization:
d3dif=Direct3DCreate9(D3D_SDK_VERSION);if (!d3dif){        //die or something;}bzero(&d3dprops,sizeof(d3dprops));d3dprops.Windowed=TRUE;d3dprops.SwapEffect = D3DSWAPEFFECT_DISCARD;d3dprops.BackBufferFormat = D3DFMT_UNKNOWN;//d3dprops.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE;d3dif->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, winobj, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3dprops, &d3ddev);d3ddev->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);d3ddev->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);d3ddev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);d3ddev->SetTextureStageState(0,D3DTSS_ALPHAOP, D3DTOP_MODULATE);


This sort of stuff sets up alpha blending and other translucency. I don't know exactly how or why, but it works; and I remember what a pain it was to find just the right mix.... Hope that helps.
I'm no expert at this but in your code what I think you should be doing is first assign all the vertices... so:

    Quad[0].x       = (float)dest_x;    Quad[0].y       = (float)dest_y;    Quad[0].rhw     = 1.0;    Quad[0].tu      = 0.0;    Quad[0].tv      = 0.0;    Quad[1].x       = (float)dest_x + (source->right - source->left);    Quad[1].y       = (float)dest_y;    Quad[1].rhw     = 1.0;    Quad[1].tu      = 1.0;    Quad[1].tv      = 0.0;    Quad[2].x       = (float)dest_x + (source->right - source->left);    Quad[2].y       = (float)dest_y + (source->bottom - source->top);    Quad[2].rhw     = 1.0;    Quad[2].tu      = 1.0;    Quad[2].tv      = 1.0;    Quad[3].x       = (float)dest_x;    Quad[3].y       = (float)dest_y + (source->bottom - source->top);    Quad[3].rhw     = 1.0;    Quad[3].tu      = 0.0;    Quad[3].tv      = 1.0;


THEN you lock the vertex buffer

VOID* myVertices;m_VertexBuffer->Lock(0, sizeof(Quad), (void**)&Quad, 0);memcpy( myVertices, Quad, sizeof(Quad) ); //copy into myVerticesm_VertexBuffer->Unlock();


As for the texture coordinates take a look at the sdk documentation its explained very well in there...

This is the code I'm using to draw a textured rectangle in my tetris clone
if that helps...
void TexRectangle(IDirect3DDevice9* dev,IDirect3DTexture9* texture,float x,float y,float sx,float sy){	Vertex v[4];	v[0].point[0] = x;	v[0].point[1] = y;	v[0].point[2] = 0.0f;	v[0].u = 0.0f; v[0].v = 0.0f;	v[1].point[0] = x+sx;	v[1].point[1] = y;	v[1].point[2] = 0.0f;	v[1].u = 1.0f; v[1].v = 0.0f;	v[2].point[0] = x;	v[2].point[1]= y+sy;	v[2].point[2] = 0.0f;	v[2].u = 0.0f;v[2].v = 1.0f;	v[3].point[0] = x+sx;	v[3].point[1] = y+sy;	v[3].point[2] = 0.0f;	v[3].u = 1.0f;	v[3].v = 1.0f;	dev->SetFVF(TEXVERTEXFORMAT);	dev->SetTexture(0,texture);	dev->DrawPrimitiveUP(D3DPT_TRIANGLESTRIP,2,(void*)v,sizeof(Vertex));}


[Edited by - setaglib on June 29, 2005 12:50:12 AM]
Let me set up a scenario.

Say I have a bitmap 512x512, and it has 32x32 sprites in it, for a total of 256 sprites.

Now lets say I want to get a sprite located in the middle somewhere, how would I set up the texture coordinates to blitter only this little 32x32 square?

To my understanding, texture coordinates are a percentage which determines how much of the *WHOLE* texture to blitter. So that means a sprite of mine could be located at 128,128,160,160 (RECT format), I would need to blitter only this small square.

I have read MSDN on texture coordinates, yet it still does not discuss how to achieve the above. Any help?


I'm honestly starting to conclude that Textured Quads suck for basic bitmap blittering for 2D space. It is far too complicated. I liked ID3DXSprite, it was very simple. Too bad the Sprite interface distorts the bitmaps when you blitter them :(

I'm not quite as lost as I was, I understand everything except for texture coordinates.

By the way, I added SetFVF() before my SetTexture() function, and DrawPrimitive() still fails :(


128/512 == .25
160/512 == .3125
Hey, I *finally* got it working, and I see bitmaps on my screen. I really thank everyone for taking me this far, but there are still just a few more questions I have, if you don't mind.

1) What exactly do all of the SetRenderState() functions do? I read MSDN on a few of the constants you can set, particularly the Alphablend state, for SRCBLEND, etc. The alpha blending modes are very confusing, I don't understand. Here is some code:

    m_Device->SetRenderState(D3DRS_LIGHTING, FALSE);    m_Device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);    m_Device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);    m_Device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);    m_Device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);


2) My bitmaps seem to be a little bit off on measurements. First thing I notice is that my bitmaps are blittered in a "blurry" state, or anti-aliased. For example, pixels in the bitmap are blurred, I don't know why this is happening. They shouldn't be filtered like that.

3) the RIGHT and BOTTOM edges of my bitmaps seem to be a few pixels short. It seems like these pixels aren't even being copied. Why is this so?

I'm sure most of this has to do with the way I set up my vertex structure. If you could look at my math and possibly lead me in the right direction, I would be much appreciative. Below you will find an updated version of my blitter function.

Take note that this is a method inside an interface of mine. Most of the declarations are hidden from you, yet most of this should be very evident. This function returns 0 on failure, or 1 for success:

int CBitmap::Blitter(int dest_x, int dest_y, RECT* source){    //--Variables---------------------------------------------------    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~    BitmapVertex*               Quad                            = 0;    //--------------------------------------------------------------    if(!m_Display->IsInitialized())        return 0;    if(!m_Loaded)        return 0;    if(m_VertexBuffer->Lock(0, 0, (void**)&Quad, 0) != D3D_OK)        return 0;    Quad[0].x       = (float)dest_x;    Quad[0].y       = (float)dest_y;    Quad[0].z       = 0.0;    Quad[0].rhw     = 1.0;    Quad[0].Color   = 0xffffffff;    Quad[0].tu      = (float)source->left / m_TexInfo.Width;    Quad[0].tv      = (float)source->top / m_TexInfo.Height;    Quad[1].x       = (float)dest_x + (source->right - source->left);    Quad[1].y       = (float)dest_y;    Quad[1].z       = 0.0;    Quad[1].rhw     = 1.0;    Quad[1].Color   = 0xffffffff;    Quad[1].tu      = (float)(source->left + (source->right - source->left)) / m_TexInfo.Width;    Quad[1].tv      = (float)source->top / m_TexInfo.Height;    Quad[2].x       = (float)dest_x + (source->right - source->left);    Quad[2].y       = (float)dest_y + (source->bottom - source->top);    Quad[2].z       = 0.0;    Quad[2].rhw     = 1.0;    Quad[2].Color   = 0xffffffff;    Quad[2].tu      = (float)(source->left + (source->right - source->left)) / m_TexInfo.Width;    Quad[2].tv      = (float)(source->top + (source->bottom - source->top)) / m_TexInfo.Height;    Quad[3].x       = (float)dest_x;    Quad[3].y       = (float)dest_y + (source->bottom - source->top);    Quad[3].z       = 0.0;    Quad[3].rhw     = 1.0;    Quad[3].Color   = 0xffffffff;    Quad[3].tu      = (float)source->left / m_TexInfo.Width;    Quad[3].tv      = (float)(source->top + (source->bottom - source->top)) / m_TexInfo.Height;    m_VertexBuffer->Unlock();    if(m_Display->GetDevice()->SetStreamSource(0, m_VertexBuffer, 0, sizeof(BitmapVertex)) != D3D_OK)        return 0;    if(m_Display->GetDevice()->SetFVF(m_VertexFormat) != D3D_OK)        return 0;    if(m_Display->GetDevice()->SetTexture(0, m_Texture) != D3D_OK)        return 0;    if(m_Display->GetDevice()->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2) != D3D_OK)        return 0;    return 1;}

This topic is closed to new replies.

Advertisement