Sign in to follow this  

Using D3DXMatrixTransformation2D in DX9

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

I'm having a little bit of trouble rotating my 2D sprite. I'm using textured quads and I'm trying to write my blit function. It works fine, until I'm going to add rotation, which looks like this:
// ... Fill the vertices

    // Check if we need to rotate the texture
    if (fRotate != 0)
    {
        D3DXVECTOR2 vCenter(m_ImgInfo.Width / 2.0f, m_ImgInfo.Height / 2.0f);
        D3DXVECTOR2 vScale(1.0f, 1.0f);
        D3DXVECTOR2 vRotCenter(m_ImgInfo.Width / 2.0f, m_ImgInfo.Height / 2.0f);
        D3DXMatrixTransformation2D(&matRotation, &vCenter, NULL, &vScale, &vRotCenter, fRotate, NULL);
        pRender->m_pDevice->SetTransform(D3DTS_WORLD, &matRotation);
    }

    // Unlock the vertexbuffer
    pRender->m_VertexBuffer->Unlock();

    // Draw the texture
    pRender->m_pDevice->SetTexture(0, this->m_lpTexture);
    return (pRender->m_pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2));

What's wrong with this code? Toolmaker

Share this post


Link to post
Share on other sites
Nothing's wrong with your code. When you say your 2D sprite, I assume you created your own sprite from a transformed quad. In other words, you're using XYZ_RHW right? Think about the name given to the vertices. It's "transformed vertices". Now that you think about it, it probably makes more sense. You can't use the World, View, or Projection transforms when drawing pre-transformed vertices (that's your job to transform them). You have to rotate the object yourself. If you just take each vertex and multiply it by your transformation matrix, then save the vertices, you should be set. It's a vector multiplied by a matrix. It should be easy to calculate with simple matrix multiplication or you could use a helper function.

Good luck,
Chris

Share this post


Link to post
Share on other sites
So instead of using SetTransform I need to multiply all the x and y values in the vertices?

In case you want to know how I fill my vertices:

TLVERTEX *vertices;
D3DXMATRIX matRotation;

pRender->m_VertexBuffer->Lock(0, 0, (void**)&vertices, NULL);

vertices[0].colour = VertexColor;
vertices[0].x = (float) pDest->left - 0.5f;
vertices[0].y = (float) pDest->top - 0.5f;
vertices[0].z = 0.0f;
vertices[0].rhw = 1.0f;
vertices[0].v = 0.0f;
vertices[0].u = 0.0f;

vertices[1].colour = VertexColor;
vertices[1].x = (float) pDest->right - 0.5f;
vertices[1].y = (float) pDest->top - 0.5f;
vertices[1].z = 0.0f;
vertices[1].rhw = 1.0f;
vertices[1].u = 1.0f;
vertices[1].v = 0.0f;

vertices[2].colour = VertexColor;
vertices[2].x = (float) pDest->right - 0.5f;
vertices[2].y = (float) pDest->bottom - 0.5f;
vertices[2].z = 0.0f;
vertices[2].rhw = 1.0f;
vertices[2].u = 1.0f;
vertices[2].v = 1.0f;

vertices[3].colour = VertexColor;
vertices[3].x = (float) pDest->left - 0.5f;
vertices[3].y = (float) pDest->bottom - 0.5f;
vertices[3].z = 0.0f;
vertices[3].rhw = 1.0f;
vertices[3].u = 0.0f;
vertices[3].v = 1.0f;


Share this post


Link to post
Share on other sites
Did you change the values of the vertices after the rotation was implemented as Chris said? Because its not going to work with the same values of pretransformed vertices.

Share this post


Link to post
Share on other sites
It would go something like this:


void TransformMe(D3DXMATRIX fpMatrix)
{
pMesh->LockVertexBuffer(0, (void**)&pData);
for (int x=0; x < NumVertices; x++)
{
pStruct = (DataStruct*)pData;
NewPosition.x = (*fpMatrix)._11*pStruct->Position.x +
(*fpMatrix)._21*pStruct->Position.y +
(*fpMatrix)._31*pStruct->Position.z +
(*fpMatrix)._41;
NewPosition.y = (*fpMatrix)._12*pStruct->Position.x +
(*fpMatrix)._22*pStruct->Position.y +
(*fpMatrix)._32*pStruct->Position.z +
(*fpMatrix)._42;
NewPosition.z = 0;
NewPosition.w = 1;
pStruct->Position = NewPosition;
pData += NumBytes;
}
pMesh->UnlockVertexBuffer();
}




pMesh is an ID3DXMesh interface, but you can just lock a vertex buffer. You can remove the NewPosition.z value or just set it to 0 (I removed it for you). NewPosition is a 4D float vector and represents your XYZ_RHW values. Position is also a 4D float vector in your FVF structure and represents the same thing. I modified the code a little, since this comes from what I do to 3D stuff, but you can further reduce it if you want. The way it is above, just be sure you use the D3DXMatrixTransformation2D function to get a proper transform passed in, which you are doing.

Chris

Share this post


Link to post
Share on other sites
I have no clue about the other code, I'm a pure n00b at this. Perhaps I should get a book that covers 2D game engine in D3D9 extensively. Are there any hints for that?

As for now, I've tried this code, which a friend gave me and worked for him, but not for me for a reason:

D3DXMATRIX matPosition, matRotation, matScaling, matTransform;

D3DXMatrixScaling(&matScaling, 1.0f, 1.0f, 1.0f );

D3DXMatrixTranslation( &matPosition, (float)(pDest->left + pDest->right) / 2.0f, (float)(pDest->top + pDest->bottom) / 2.0f, 0.0f );

//Set the Rotation Matrix
D3DXMatrixRotationZ( &matRotation, fRotation );

D3DXMatrixMultiply( &matTransform, &matScaling, &matPosition );
D3DXMatrixMultiply( &matTransform, &matRotation, &matTransform );

//D3DXMatrixMultiply( &matTransform, &matScaling, &matRotation );

pRender->m_pDevice->SetTransform( D3DTS_WORLD, &matTransform );

//pRender->m_pDevice->SetVertexShader(NULL);
//pRender->m_pDevice->SetStreamSource(0, pRender->m_VertexBuffer, 0, sizeof(TLVERTEX));

// Draw the texture
pRender->m_pDevice->SetTexture(0, this->m_lpTexture);
return (pRender->m_pDevice->DrawPrimitive(D3DPT_TRIANGLEFAN, 0, 2));



Ideas?

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
The thing to understand is that there are 2 types of vertices: Transformed and Untransformed. Well that's not entirely true, but any vertice will be in one of these two groups when it travels through the video card pipeline.

Untransformed vertices are a given when using the D3DFVF_XYZ type. The position vector (3 floats) in your FVF structure define the units in world space. The units are just floating point numbers, where their size is relative to the projection matrix. These units can be transformed by two other matrices (well they are transformed by the projection matrix obviously as I've stated): the View matrix and the World transform matrix. The view matrix and world transform matrix aren't special. You could set one to Identity (that is 1's in the diagonal of the matrix, 0 everywhere else), and use the other as if it was just both of them combined. For example:

View = Identity;
World = MyWorld*MyView; // I can't remember if this is the right order of multiplication, it doesn't matter, that's not my point.

For simplicity, the video card pipeline just takes Untransformed vertices and multiplies them through the 3 matrices, World, View, and Projection. Once multiplied through these matrices, the new vertex is considered to be in Screen Space. It is a vertex that was defined by some 3D point in world space and is now defined as an X,Y pixel in Screen Space.

Transformed vertices are a given when using the D3DFVF_XYZ_RHW type. The position vector (2 floats) in your FVF structure define the pixels from top/left corner of the screen. For simplicity's sake, the z value and w value (another 2 floats) after that are just set to 0 and 1 respectively. Don't get bogged down in what they do yet. These vertices, in their Transformed state, are considered to already be in Screen Space, therefore no projection, view, or world matrices in the video card pipeline are multiplied into the position vector.

The D3DXSprite interface uses transformed vertices, but it has a function SetTransform() that will just move the vertices around (scale, translate, and rotate them) manually, without using the graphics card pipeline.

Now for the reason for all of that. The code your friend gave you can not move a series of vertices created with D3DFVF_XYZ_RHW. The code will move a series of vertices created with D3DFVF_XYZ. Does this all make a little more sense, or did you know all of this already and I'm just assuming too much?

I can't recommend a book on 2D that I've read, because I haven't read any. There are some out there, probably in the Andre LaMothe Game Development series.

Good luck,
Chris

Share this post


Link to post
Share on other sites
Wow, that makes. Sense! Thanks for the long answer.

In other words, I just need to turn my vertex into XYZ form. I'll try that later today. And perhaps I'm going to switch back to the ID3DXSprite interface and try it a bit more.

Thanks!


++Supernat02->Rating;

Share this post


Link to post
Share on other sites
Yes, in short you can just change to XYZ. haha. I take it you didn't have much luck using D3DXSprite before. Here's a quick overview:


ID3DXSprite *pSprite;
LPD3DTEXTURE9 *pTexture;

// I don't have all the functions in front of me, so I'm going to code to the best of my ability, but you should get the idea.
D3DXCreateSprite(pDevice, &pSprite);

void Render(void)
{
D3DXMATRIX mTransform;
D3DXMatrixTranslation(&mTransform, 1.0f, 2.0f, 0.0f); // Move X,Y,Z to 1,2,0.

pSprite->Begin(D3DXSPRITE_SORT_TEXTURE); // Sort the textures
pSprite->SetTransform(&mTransform);
pSprite->Draw(pTexture, NULL, NULL, NULL, D3DCOLOR_XRGB(255,255,255));
pSprite->End();
}



Some notes:
D3DXSPRITE_SORT_TEXTURE means that the sprite will sort the textures that have been put into the Draw call. What happens is the Sprite interface is more than just a SINGLE sprite. It's a Sprite container. You can tell it to draw Some texture at Some position on the screen multiple times between the Begin and End calls. If figures out which ones use the same texture and sorts them so that it doesn't have to call "SetTexture(...)" for every call to Draw. When you call the End function, that's when the rendering actually takes place. In other words, that's when the data actually gets batched up and sent to the video card.

The three NULLS are source rect, Center, and Position. You don't want to set those to NULL all the time. You probably want to set Center to be the center of the screen (width/2, height/2, 0); and the Position to be in the top/left corner of the sprite from the center of the screen.

The color is the "tint" of the texture. Making it red will make the texture red for instance.

SetRenderState is ignored by the ID3DXSprite. You have to specify D3DXSPRITE_DONOTMODIFY_RENDERSTATE in the Begin function to prevent it from overwriting your render states. That may be a bit advanced for now, just keep it in mind.

Good luck,
Chris

Share this post


Link to post
Share on other sites

This topic is 4839 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.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this