Hello!
I've been having trouble with 3D space transformations in my 2D game in Directx 9. I have tried countless measures to resolve the issue, researched everywhere but nothing I attempt seems to resolve the issue so I decided to come here to receive direct help. Any help is greatly appreciated!
Here is a visual of what is happening in the game currently with the following code:
Here is what it is supposed to look like (im using DX9 Sprite to render this but I don't want to use it in further development which is why I want to use my own sprite rendering system with vertex/pixel shaders):
As you can see the square tiles are not being scaled/framed properly in the first image compared to the second. The tiles are being read from a spritesheet. The reason you see the trees and mouse sprites fine is because they are from single image spritesheet so no scaling is needed. I have tried to scale them properly, although it improves the sprite rendering... it still isn't showing properly because the large images become a little too small and the small images become a little too big. I have tried many different calculations to try and fix the scaling and position of the sprites in the spritesheet but have failed to resolve the issue. I have also tried running the game in fullscreen mode but nothing changes so I'm assuming the screen size of the game in window mode doesn't affect the scaling. What I do know is that the resolution set in DX9 does affect the scaling but I cannot derive a calculation method to handle that... I have searched everywhere, trying many methods but to no prevail.
EDIT: actually the trees aren't even scaled properly as you can see the difference between them when comparing both images. And since the trees are individual spritesheet images then it seems everything is out of proportion. Also to make things clear, the square tiles are showing the whole spritesheet scaled into a single sprite frame size (32x32 pixels) so you can see that its not positioning and scaling itself properly to the location of the spriteframe in the spritesheet.
Here is the code I use for the first image (I'll try to post the key code and not all of it):
//Here I set my sampler states for safe/efficiency purposes although I'm not sure if this will cause problems in rendering the sprites
g_pD3DDevice->SetRenderState(D3DRS_ALPHABLENDENABLE, true);
g_pD3DDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
g_pD3DDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
g_pD3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_pD3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
g_pD3DDevice->SetRenderState(D3DRS_ZENABLE, FALSE);
g_pD3DDevice->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);
g_pD3DDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);
g_pD3DDevice->SetSamplerState(0 ,D3DSAMP_MINFILTER, D3DTEXF_NONE);
g_pD3DDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_NONE);
g_pD3DDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE);
// Here are my vertex buffer definitions
typedef struct _CUSTOM2DVERTEX
{
D3DXVECTOR3 pos; // D3DFVF_XYZ (x,y,z)
//D3DCOLOR color;
D3DXVECTOR2 texCoord; // D3DFVF_TEX1 (u,v)
} CUSTOM2DVERTEX, *LPCUSTOM2DVERTEX;
#define D3DFVFCUSTOM2DVERTEX (D3DFVF_XYZ | D3DFVF_TEX2/* | D3DFVF_DIFFUSE*/)
LPDIRECT3DVERTEXBUFFER9 g_pVertexBuffer;
// Here is my vertex buffer creation code
if (FAILED(hr = g_pD3DDevice->CreateVertexBuffer(4*sizeof(CUSTOM2DVERTEX), usageProcessing | D3DUSAGE_DYNAMIC | D3DUSAGE_WRITEONLY, D3DFVFCUSTOM2DVERTEX, D3DPOOL_DEFAULT, &g_pVertexBuffer, NULL))) {
MessageBox(g_hWnd, "Failed to create Vertex buffer.", "ERROR!", MB_ICONEXCLAMATION | MB_OK);
SendMessage(g_hWnd, WM_DESTROY, NULL, NULL);
return hr;
}
// I setup my view port here on application startup since it doesn't change, g_wX and g_wY are the screen resolutions (we can assume 640x480) so the scale is based on the resolution size and not on 1.0x1.0
D3DXMATRIX matOrtho, matProj;
D3DXMatrixOrthoLH(&matOrtho, (float)g_wX, (float)g_wY, 0.0f, 1.0f);
D3DXMatrixOrthoOffCenterLH(&matOrtho, 0.0f, (float)g_wX, (float)g_wY, 0.0f, 0.0f, 1.0f);
g_pD3DDevice->SetTransform(D3DTS_PROJECTION, &matOrtho);
D3DXMatrixIdentity(&matProj);
g_pD3DDevice->SetTransform(D3DTS_VIEW, &matProj);
// as you can see I recycle my vertex buffer since I only set it once for efficiency so you can expect that I don't recreate it each render period
g_pD3DDevice->SetStreamSource(0, g_pVertexBuffer, 0, sizeof(CUSTOM2DVERTEX));
g_pD3DDevice->SetFVF(D3DFVFCUSTOM2DVERTEX);
// and finally here is the code that renders each sprite
// you can assume the scale.xyz values are all 1.0f
// the position.xyz values are all the position of the sprite frame relative to the screen; however, position.z will always be 0.0f in my code
D3DXMatrixScaling(&scaleMatrix, scale.x, scale.y, scale.z);
D3DXMatrixTranslation(&transMatrix, position.x, position.y, position.z);
D3DXMatrixMultiply(&matWorld, &scaleMatrix, &transMatrix);
DxDraw.g_pD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
// as you can see the scaling is at its default, so no scaling is done to "select" the sprite frame from a spritesheet to simplify the code as I tried to scale it before with no success
float minwidthFactor = 0.0f;
float minheightFactor = 0.0f;
float maxwidthFactor = 1.0f;
float maxheightFactor = 1.0f;
// the srcRect variable holds the RECT position data of the sprite frame relative to the spritesheet
CUSTOM2DVERTEX vertices[] = {
{ D3DXVECTOR3((float)srcRect.left-0.5f, (float)srcRect.top-0.5f, 0.0f), D3DXVECTOR2(minwidthFactor, minheightFactor) }, // left top
{ D3DXVECTOR3((float)srcRect.left-0.5f, (float)srcRect.bottom-0.5f, 0.0f), D3DXVECTOR2(minwidthFactor, maxheightFactor) }, // left bottom
{ D3DXVECTOR3((float)srcRect.right-0.5f, (float)srcRect.top-0.5f, 0.0f), D3DXVECTOR2(maxwidthFactor, minheightFactor) }, // right top
{ D3DXVECTOR3((float)srcRect.right-0.5f, (float)srcRect.bottom-0.5f, 0.0f), D3DXVECTOR2(maxwidthFactor, maxheightFactor) }, // right bottom
};
LPVOID lpVertices;
DxDraw.g_pVertexBuffer->Lock(0, sizeof(vertices), &lpVertices, D3DLOCK_DISCARD);
memcpy(lpVertices, vertices, sizeof(vertices));
DxDraw.g_pVertexBuffer->Unlock();
DxDraw.g_pD3DDevice->SetTexture(0, m_texture);
DxDraw.g_pD3DDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2);
On a side note, I want to create this sprite rendering system similar to how D3DXSPRITE works (as you can see in the second image example). The reason for this new sprite system is so I can use vertex/pixel shaders with maximum freedom for the next stage of development.
Thanks in advance for any provisions! Please feel free to ask any questions about my code incase I missed something or if it is confusing.