morezoom
morezoom

Reputation: 101

DirectX 9 - drawing a 2D sprite in its exact dimensions

I'm trying to build a simple 2D game using DirectX9, and I want to be able to use sprite dimensions and coordinates with no scaling applied.

The book that I'm following ("Introduction to 3D Game Programming with DirectX 9.0c" by Frank Luna) shows a trick using Direct3D's sprite functions to render graphics in 2D, but the book code still sets up a camera using D3DXMatrixLookAtLH and D3DXMatrixPerspectiveFovLH, and the sprite images get scaled in perspective. How do I set up the view and projection to where sprites are rendered in original dimensions and X-Y coordinates can be addressed as an actual pixel location within the window?

UPDATE

Although this might not be the ideal solution, I did come up with a workaround. I realized if I set the projection matrix with 90-degree field-of-view and the near plane at z=0, then all I have to do is to look at the origin (0, 0, 0) with the D3DXMatrixLookAtRH and step back by half of the screen width (the height of an Isosceles Right Triangle is half of the base).

So for my client area being 400 x 400, the following settings worked for me:

// get client rect
RECT R;
GetClientRect(hWnd, &R);
float width  = (float)R.right;
float height = (float)R.bottom;

// step back by 400/2=200 and look at the origin
D3DXMATRIX V;
D3DXVECTOR3 pos(0.0f, 0.0f, (-width*0.5f) / (width/height)); // see "UPDATE 2" below
D3DXVECTOR3 up(0.0f, 1.0f, 0.0f);
D3DXVECTOR3 target(0.0f, 0.0f, 0.0f);
D3DXMatrixLookAtLH(&V, &pos, &target, &up);
d3dDevice->SetTransform(D3DTS_VIEW, &V);

// PI x 0.5 -> 90 degrees, set the near plane to z=0
D3DXMATRIX P;
D3DXMatrixPerspectiveFovLH(&P, D3DX_PI * 0.5f, width/height, 0.0f, 5000.0f);
d3dDevice->SetTransform(D3DTS_PROJECTION, &P);

Turning off all the texturing filters (or setting to D3DTEXF_POINT) seems to get the best pixel-accurate feel.

Another important thing to note was that CreateWindowEx() with requested 400 x 400 size returned a client area of something like 387 x 362, so I had to check with GetClientRect(), calculate the difference and readjust the window size using SetWindowPos() after initial creation.

The screenshot below shows the result of taking the steps mentioned above. The original bitmap (right) is rendered with no scaling/stretching applied in the app (left)... finally!

enter image description here

UPDATE 2

I didn't test the above method for when the aspect ratio isn't 1:1. I adjusted the code - the amount you step back for your camera position should be ... window_width * 0.5 / aspect_ratio (or width/height).

Upvotes: 0

Views: 1840

Answers (1)

Chuck Walbourn
Chuck Walbourn

Reputation: 41137

DirectX Tool Kit SpriteBatch class is designed to do exactly what you describe. When drawing with Direct3D, screen coordinates are (-1,-1) to (1,1) with (-1,-1) in the upper-right corner.

This sets up the matrix that will let you specify in screen-coordinates with (0,0) in the upper-right.

// Compute the matrix.
float xScale = (mViewPort.Width  > 0) ? 2.0f / mViewPort.Width  : 0.0f;
float yScale = (mViewPort.Height > 0) ? 2.0f / mViewPort.Height : 0.0f;

switch( rotation )
{
case DXGI_MODE_ROTATION_ROTATE90:
    return XMMATRIX
    (
         0,       -yScale,  0,  0,
         -xScale, 0,        0,  0,
         0,       0,        1,  0,
         1,       1,        0,  1
    );

case DXGI_MODE_ROTATION_ROTATE270:
    return XMMATRIX
    (
         0,       yScale,   0,  0,
         xScale,  0,        0,  0,
         0,       0,        1,  0,
        -1,      -1,        0,  1
    );

case DXGI_MODE_ROTATION_ROTATE180:
    return XMMATRIX
    (
        -xScale,  0,       0,  0,
         0,       yScale,  0,  0,
         0,       0,       1,  0,
         1,      -1,       0,  1
    );

default:
    return XMMATRIX
    (
         xScale,  0,       0,  0,
         0,      -yScale,  0,  0,
         0,       0,       1,  0,
        -1,       1,       0,  1
    );
}

In Direct3D 9 the pixel centers were defined a little differently than Direct3D 10/11/12 so the typical solution in the legacy API was to add a 0.5,0.5 half-center offset to all the positions. You don't need to do this with Direct3D 10/11/12.

Upvotes: 1

Related Questions