« GDC 2005: Will Wright unveils "Spore" | Main | BASS Sound Library (Crashing is Bad) »

May 28, 2005

Using ID3DXSprite

I'm currently working on a 2d game that mostly wants to draw unrotated rectangular images with one-bit (cookie cutter) alpha. Occasionally, though, the design requires that some of these images rotate on the screen (within the plane of the screen, nothing too fancy... yet).
One route that I've taken down this path previously is to use OpenGL with an orthographic projection, but for reasons outside my control, OpenGL isn't an option for this project.

Back in earlier versions of DirectX, you might use DirectDraw for 2D games. But these days, DirectDraw and D3D are integrated, so I'm using Direct3D.

Since I've got the fullon 3D pipe available to me, I could certainly draw textured quads to the screen, similar to what I did with "Cars With Guns", but this seems like overkill when most of what I want to do is a simple copy of the image information, unmodified, from the source texture to the screen. In particular, I want the graphics to show up pixel-accurate on the screen without antialiasing or filtering.

In my research, I found CodeSampler.com, which is a terrific source for examples. At first, I was planning to use DirectX 9.0c, and the ID3DX sample got me running, giving me a simple interface for drawing sprites to the screen, with or without rotation, with scaling and other fancy stuff should I make that decision.

One thing that happens in just about any software project is changing requirements. I discovered after getting a working version of the game up that the game had to work on stock Windows XP machines, which meant dropping back to DX8.1.

Fortunately, DX8.1 has support for ID3DXSprite. Unfortunately, the interface is just about completely different. The MSDN documentation for each version, 8.1 and 9.0, is of no help.

    /* during device creation */
    D3DXCreateSprite( g_pd3dDevice, &g_pSpriteContext );


    /* during render */
    g_pSpriteContext->Begin();

    D3DCOLOR c = D3DCOLOR_COLORVALUE(1.0f,
                                     1.0f,
                                     1.0f,
                                     1.0f);
    
    float px=(pSpr->right+pSpr->left)/2.0f;
    float py=(pSpr->top+pSpr->bottom)/2.0f;
        
    float ox=pSpr->right-px;
    float oy=pSpr->bottom-py;
        
    D3DXVECTOR2 scaling(1.0, 1.0);
    D3DXVECTOR2 rotCenter(ox,oy);
			  
    D3DXVECTOR2 translate(pSpr->posX-ox,
                          pSpr->posY-oy);
    
    g_pSpriteContext->Draw(pSpr->textureHandle,
                          &pSpr->srcRect,
                          &scaling,
                          &rotCenter,
                          pSpr->angle,
                          &translate,
                          c);
The code above is a rough sketch of how I use the code. I begin by creating a "sprite context object" - my own term, as calling it a "sprite object" is misleading. I create my sprite object just after I have acquired the device.

Later on, when I'm rendering my scene, first I call Begin() on my context object, at which point, I'm ready to draw the individual sprites.

First, I create a color for the sprite. In this case, I want the original colors to be used, so I use a solid white color.

Next, I set px, py to the center of the sprite within the texture. It's important to understand that the intended use of ID3DXSprite is to draw rectangular sections of source art to the screen, so several of the arguments to Draw() are relative to the upper left corner of the texture.

Second, I set ox, oy to be the "half size" of the sprite (or 'offset', if you prefer). This is the distance from upper left corner of the sprite to the center.

I create a scaling vector, but I don't currently make use of it - so I leave it at a neutral value, (1.0, 1.0).

Next, rotCenter gets the half size offset value, specifying the point around which the rotation should be centered. For my purposes, the center of the bounding box of the sprite is a good point to center the rotation about, but for odd-sized objects (like a fryingpan) you might want to set the rotation center to be somewhere closer to one of the corners.

The last part of the setup is to create the vector describing the location where the sprite should be drawn on the screen. The peculiar bit is that the sprite is positioned by its upper left hand corner, which makes little sense when you start rotating the sprites. So I subtract off the half size offset from the point on the screen where I want the center to be drawn.

Finally, I call Draw(). The arguments are the texture handle, the sprite bounding box (in texture pixels), our scaling vector, the rotation center, the angle (in radions), the screen position coordinates, and finally the color.

Phew.


the MSDN documentation of ID3DXSprite is authoritative, but very difficult to get useful information out of.

Posted by tsmaster at May 28, 2005 08:46 PM

Comments

Post a comment




Remember Me?