Sign in to follow this  

Animating Textures in DirectX 9

This topic is 4099 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 looked for a long time and could not find any tutorials that show how to make animated textures. I want to animate my character as it moves across the screen, but I do not want to use sprites, so is there any way to do it without using sprites.

Share this post


Link to post
Share on other sites
If you don't want to use sprites, then you are going to have to use textured Quads.

The big thing about animation is that you are going to have to track the time the texture is being displayed, then when it hits the length that you want it displayed, you switch to the next texture in the animation sequence.

A simple walk cycle can be done with 3 different textures. I would suggest using seperate textures at first to accomplish it. Then, you can save precious memory that textures eat up by using a single texture that serves as a sprite sheet (aka sprite atlas) and offset the UVs of your quad to accomodate the various images of the sequence.

If you need more help, just ask and I'll go further in depth if you can't find a solution.

Brandon

Share this post


Link to post
Share on other sites
Hmm... I read about texture quads. I am using vertex buffers and loading the texture onto it. I thought since I have coordinates of my textures TU and TV. I can just directly manipulate the coordinates and make animations, no luck.


///////////////////////////////////////////////////////////
struct CUSTOMVERTEX
{
float x, y, z; // The untransformed, 3D position for the vertex
float tu, tv;
};

CUSTOMVERTEX g_Vertices[] =
{

{-1.0f, 2.0f,-1.0f, 0.0f, 0.0f },
{ 1.0f, 2.0f,-1.0f, 0.25f, 0.0f },
{-1.0f,-1.0f,-1.0f, 0.0f, 1.0f },
{ 1.0f,-1.0f,-1.0f, 0.25f, 1.0f }

};
///////////////////////////////////////////////////////////



so I make CUSTOMVERTEX with world coordinates and texture coordinates. Then initialize and define g_Vertices.

Here is my problem


/////////////////////////////////////////////////////////////
if(KEYDOWN(buffer, DIK_LEFT)) {

D3DXMatrixTranslation(&matTranslate, position.x-= 0.09f, position.y, position.z);
g_Vertices[8].tu += .25f;
g_Vertices[18].tu += .25f;
if(1.0f == g_Vertices[8].tu && 1.0f == g_Vertices[18].tu){
g_Vertices[8].tu = .25f;
g_Vertices[18].tu = .25f;
}


}
/////////////////////////////////////////////////////////////


I am trying to invoke tu withen the g_Vertices just no luck. I tried various indexes and no change of texture. What am I missing? Time function?

Share this post


Link to post
Share on other sites
It appears that you are trying to modify the Vertex Buffer without being inbetween a Lock() and Unlock(). Make sure to lock the buffer anytime you want to adjust the vertex x,y,z or u,v.

Alternatively you can DrawPrimiteUP without needing to lock/unlock the vertex buffer but feeding in the pointer to the buffer, size and stride.

Brandon

Share this post


Link to post
Share on other sites



g_Vertices[1].tu += .25f;
g_Vertices[3].tu += .25f;
if(1.0f == g_Vertices[1].tu && 1.0f == g_Vertices[3].tu){
g_Vertices[1].tu = .25f;
g_Vertices[3].tu = .25f;
}




ok so I placed my KEYDOWN conditions between the lock and unlock functions. So when I press the left arrow, the texture shrinks and expands. :/ Which is cool and all. I want my texture just to change position not shrink. How do I modify that? My mind is blank for the moment.

Share this post


Link to post
Share on other sites
Heh, you got some UV animation going! Congrats, now you can make conveyor belts and really simple waves in water.

Your math is off for the UVs. As I stated first, work with DIFFERENT textures first just use SetTexture() when you press the keys, for now. After that we'll discuss sprite sheets.

Brandon

Share this post


Link to post
Share on other sites
As for setTexture() technique, I found that I need to use timer of some sort, maybe getTime() function? I found it more tedious than my initial methods. I understand the concept of sprite. I have a picture that has 4 different motions for the character. So my initial setting is this


CUSTOMVERTEX g_Vertices[] =
{

{-1.0f, 2.0f,-1.0f, 0.0f, 0.0f },
{ 1.0f, 2.0f,-1.0f, 0.25f, 0.0f },
{-1.0f,-1.0f,-1.0f, 0.0f, 1.0f },
{ 1.0f,-1.0f,-1.0f, 0.25f, 1.0f }

};




which means g_Vertices[1].tu = .25f and g_Vertices[3].tu = 0.25f. So I cropped one quarter of the image to fit as a texture. When I set these tu to 0.50f, it will have two characters motions, instead of one different character motion. I want one character to fit in the texture. So what's going on there?

Share this post


Link to post
Share on other sites
You need to move both texture co-ords across as below. This would give you the second quarter (across) of the base texture displayed on your quad.


CUSTOMVERTEX g_Vertices[] =
{

{-1.0f, 2.0f,-1.0f, 0.25f, 0.0f },
{ 1.0f, 2.0f,-1.0f, 0.50f, 0.0f },
{-1.0f,-1.0f,-1.0f, 0.25f, 1.0f },
{ 1.0f,-1.0f,-1.0f, 0.50f, 1.0f }

};

Share this post


Link to post
Share on other sites
OH wow, once I did this:

[source lang=cpp]
g_Vertices[0].tu += .25f;
g_Vertices[1].tu += .25f;
g_Vertices[2].tu += .25f;
g_Vertices[3].tu += .25f;
if(1.0f == g_Vertices[1].tu && 1.0f == g_Vertices[3].tu){
g_Vertices[0].tu = .0f;
g_Vertices[1].tu = .25f;
g_Vertices[2].tu = .0f;
g_Vertices[3].tu = .25f;
}



I have the animation of my character as if it was sprite. :) Thank you. my question is what's happening exactly with texture coordinates of tu on all four vertices. Then my next question is what is the effecient way implementing timer, so that animation is not too fast? Is there more efficient of doing this?

Share this post


Link to post
Share on other sites
The texture co-ordinates represent the position on the texture that the primitive draws its texture from, but as a proportion of the overall width or height, from (0.0,0.0) - middle of top left pixel to (1.0,1.0) - middle of bottom right pixel of the whole texture.

You can translate a pixel position across a texture into a texture co-ordinate like this:

int PixelX=Whatever;
int TextureWidth=OverallWidthOfTextureInPixels; // duh!
float TextureX=(float(PixelX)+0.5f)/(float(TextureWidth));

You perform the equivalent conversion to get the TextureY value.

Adding the 0.5 in there is to compensate for the fact that texture co-ordinates refer to the middle of a pixel whereas pixel co-ordinates refer to a whole pixel (or the top-left of a pixel, depending on how you want to look at it).

Based on the above conversion, the rest is the same as selecting rectangles to draw from an image a la GDI or DirectDraw.

In terms of your timer question, there are many ways to accomplish this. The approach I am currently using in my daft little 2D scroller (written with Direct3D BTW) is to measure the time elapsed since the last frame, since this can vary tremendously from computer to computer, or even frame to frame, and work out a float value that expresses this time in terms of 1.0 being a second, 0.5 is half a second and so on.

The animation can then be based on a delay value that, each frame, has the above time value subtracted from it. When the delay becomes <= 0, it is time to move to the next frame.

That is a bit of a simplification since the way I have implemented it also accounts for a frame taking longer than the total delay, but I would hope it is food for thought at least. You need to bear in mind that you cannot guarantee a consistent frame rate so any system you use for timing animations needs to take that into account.

HTH

Share this post


Link to post
Share on other sites
Yeehaw. I made my animation go not too fast using timeGetTime();

Here:


HRESULT hr;
hr = g_lpDIDevice->GetDeviceState(sizeof(buffer),(LPVOID)&buffer);
//object variables
D3DXMATRIX matObj, matScale, matRotate, matTranslate;
//Translate the object away from the origin

DWORD timeNow=timeGetTime();

g_pVB = createVertexBuffer(sizeof(g_Vertices) * sizeof(CUSTOMVERTEX), D3DFVF_CUSTOMVERTEX);

// Fill the vertex buffer.
VOID* pVertices;

hr = g_pVB->Lock( 0, sizeof(g_Vertices), (void**)&pVertices, 0 );
if FAILED (hr)
return E_FAIL;



if(KEYDOWN(buffer, DIK_LEFT)) {

D3DXMatrixTranslation(&matTranslate, position.x-= 0.09f, position.y, position.z);

for(int i =0; i<kNumofCharacters; i++){
if(timeNow-g_Vertices[i].lastUpdate>kTimeBetweenUpdated){
g_Vertices[i].tu += .25f;
//g_Vertices[1].tu += .25f;
//g_Vertices[2].tu += .25f;
//g_Vertices[3].tu += .25f;
if(0.75f == g_Vertices[1].tu && 0.75f == g_Vertices[3].tu){
g_Vertices[0].tu = .0f;
g_Vertices[1].tu = .25f;
g_Vertices[2].tu = .0f;
g_Vertices[3].tu = .25f;
}
g_Vertices[i].lastUpdate = timeNow;
}
}

}
if (KEYDOWN(buffer, DIK_UP)){
D3DXMatrixTranslation(&matTranslate, position.x, position.y+= .09f, position.z);

for(int i =0; i<kNumofCharacters; i++){
if(timeNow-g_Vertices[i].lastUpdate>kTimeBetweenUpdated){
g_Vertices[i].tu += .25f;
//g_Vertices[1].tu += .25f;
//g_Vertices[2].tu += .25f;
//g_Vertices[3].tu += .25f;
if(0.75f == g_Vertices[1].tu && 0.75f == g_Vertices[3].tu){
g_Vertices[0].tu = .0f;
g_Vertices[1].tu = .25f;
g_Vertices[2].tu = .0f;
g_Vertices[3].tu = .25f;
}
g_Vertices[i].lastUpdate = timeNow;
}
}
}
if (KEYDOWN(buffer, DIK_DOWN)) {
D3DXMatrixTranslation(&matTranslate, position.x, position.y-= .09f, position.z);

for(int i =0; i<kNumofCharacters; i++){
if(timeNow-g_Vertices[i].lastUpdate>kTimeBetweenUpdated){
g_Vertices[i].tu += .25f;
//g_Vertices[1].tu += .25f;
//g_Vertices[2].tu += .25f;
//g_Vertices[3].tu += .25f;
if(0.75f == g_Vertices[1].tu && 0.75f == g_Vertices[3].tu){
g_Vertices[0].tu = .0f;
g_Vertices[1].tu = .25f;
g_Vertices[2].tu = .0f;
g_Vertices[3].tu = .25f;
}
g_Vertices[i].lastUpdate = timeNow;
}
}
}
if (KEYDOWN(buffer, DIK_RIGHT)) {
D3DXMatrixTranslation(&matTranslate, position.x+= .09f, position.y, position.z);

for(int i =0; i<kNumofCharacters; i++){
if(timeNow-g_Vertices[i].lastUpdate>kTimeBetweenUpdated){
g_Vertices[i].tu += .25f;
//g_Vertices[1].tu += .25f;
//g_Vertices[2].tu += .25f;
//g_Vertices[3].tu += .25f;
if(0.75f == g_Vertices[1].tu && 0.75f == g_Vertices[3].tu){
g_Vertices[0].tu = .0f;
g_Vertices[1].tu = .25f;
g_Vertices[2].tu = .0f;
g_Vertices[3].tu = .25f;
}
g_Vertices[i].lastUpdate = timeNow;
}
}
}

// copy the vertices into the buffer
memcpy( pVertices, g_Vertices, sizeof(g_Vertices) );

// unlock the vertex buffer
g_pVB->Unlock();

return S_OK;

//D3DXMatrixMultiply(&matObj, &matTranslate, &matRotate);
}





Then I have this winMain. it supposed to initialize the values of lastUpdate.



for(int i = 0; i < kNumofCharacters; i++) {
g_Vertices[i].lastUpdate = timeGetTime();
}





The motion across the screen is pretty smooth. Now, I can really work on my core stuff of the game, AI, collisions, pathfindings, etc. I will try to make texture change as methods itself, to clean up the mess of the code in move() function.

Share this post


Link to post
Share on other sites
Thanks a lot, I wanted to know about better quality timer. I will also clean my code and comment it. Then post it here, if there someone who doesn't want to use sprite but animated textures.

My next step is collision detection. The thing is that it's in 3D world, and camera hovering above. So I can't do any traditional collision detection across the screen. I have to use the detection in virtual world, any small nudge to guide in correct direction of 3D path and collision detection?

Share this post


Link to post
Share on other sites
I suppose you aren't drawing to screen coordinates at this time but world coordinates. That may help you in the long run. here is a hint:


//Setup an orthographic perspective
D3DXMatrixOrthoLH (&matOrtho, (float) resWidth, (float) resHeight, 1.0f, 10.0f);
D3DXMatrixIdentity (&matIdentity);

d3dDevice->SetTransform(D3DTS_PROJECTION,&matOrtho);
d3dDevice->SetTransform(D3DTS_VIEW,&matIdentity);
d3dDevice->SetTransform(D3DTS_WORLD, &matIdentity);




Ortho will greatly help you in what you want to accomplish! Now you are working, in effect, in a 2D space. IntersectRect() your way to cheap collision detection!

Brandon

Share this post


Link to post
Share on other sites
On the subject of timers, I'd heard such terrible things about QPC failing on certain dual-core systems at the moment, and dubious fixes that some people seemed to think didn't work, I had a better look at timeGetTime().

I've personally found that if I call timeBeginPeriod(1); at the start of my game and timeEndPeriod(1);, I get really good performance out of timeGetTime(). It is more than accurate enough to run my game on a variety of different computer set-ups anyway. When I was testing and outputting the return result to a file, I found that it was accurate to 0.001 sort of precision and if I put a Sleep(1000) in the main loop, I was getting exactly 1.000000 out of it.

I don't know if there are problems with this approach but it works fine for me and avoids the current "issues" with QPC.

Share this post


Link to post
Share on other sites
so what is happening here?

Animated robot

I tried to make the my character background transperant. It seemed like I accomplished it, but once I placed a background or a floor, the character's back ground is black. What's going on? When I tried to add alpha1 channel to the picture so that everything around my Character is pink. In the game it would be white. Why is this happening?



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



This the settings of my rendingStates.


D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.BackBufferCount = 1;
d3dpp.BackBufferHeight = 480;
d3dpp.BackBufferWidth = 640;
d3dpp.hDeviceWindow = hwnd;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

hr = pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &pd3dDevice );



here is the initDirect3D.

So what is really happening here, I want my animated texture to have some transluscant.

Share this post


Link to post
Share on other sites
Two things occur to me.

1) How are you loading your texture? If you are not specifying an alpha either in the original image format, or automatically alpha-ing out a specific colour via something like D3DLoadTextureFromFileEx then you won't get any transparency.

2) Not 100% about this one but I think you may need to specify a pixel format that has an alpha component for your backbuffer. I use D3DFMT_A8R8B8G8 rather than D3DFMT_UNKNOWN. I think this only applies if you are running fullscreen but I'm not really sure about this point.

Share this post


Link to post
Share on other sites
Animated robot

I have another question Why is my background not square when vertices postion says it so?


CUSTOMVERTEX g_Background[] =
{

{-10.0f, 10.0f,-1.0f, 0.0f, 0.0f },
{ 10.0f, 10.0f,-1.0f, 1.0f, 0.0f },
{-10.0f,-10.0f,-1.0f, 0.0f, 1.0f },
{ 10.0f,-10.0f,-1.0f, 1.0f, 1.0f }

};


Share this post


Link to post
Share on other sites
Here's a copy-and-paste of my presentation parameters:


memset(&m_d3dPresent, 0, sizeof(m_d3dPresent));
m_d3dPresent.BackBufferWidth = m_nWidth;
m_d3dPresent.BackBufferHeight = m_nHeight;
m_d3dPresent.BackBufferFormat = (bWindowed) ? D3DFMT_UNKNOWN : D3DFMT_R5G6B5;
m_d3dPresent.BackBufferCount = 1;
m_d3dPresent.MultiSampleType = D3DMULTISAMPLE_NONE;
m_d3dPresent.MultiSampleQuality = 0;
m_d3dPresent.SwapEffect = D3DSWAPEFFECT_COPY;
m_d3dPresent.hDeviceWindow = m_hWnd;
m_d3dPresent.Windowed = bWindowed;
m_d3dPresent.EnableAutoDepthStencil = true;
m_d3dPresent.AutoDepthStencilFormat = D3DFMT_D16;
m_d3dPresent.Flags = D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
m_d3dPresent.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
m_d3dPresent.PresentationInterval = (bVSync) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;



If its windowed then I do indeed use D3DFMT_UNKNOWN.

If you do what EasilyConfused said with D3DLoadTextureFromFileEx() you should be good with the colorkey.


Oh yeah! I know that we aren't using sprites but this should fix your problem:

Call D3DXCreateSprite() to create a sprite, then call Begin(D3DXSPRITE_ALPHABLEND) on your sprite after BeginScene(), and end it prior to EndScene(), that may help you out. It fixed my problem that I had with it.


Brandon

Share this post


Link to post
Share on other sites
That is very interesting. I called the D3DXCreateSprite() and Begin(D3DXSPRITE_ALPHABLEND). Now my sprite has transparent background. Now is there a way to do this without involving D3DXSPRITE? Currently I am struggling with D3DXCreateTextureFromFileEx().


D3DXCreateTextureFromFileEx(pd3dDevice, "BOT.png", D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0, D3DFMT_FROM_FILE, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0xFF000000, NULL, NULL, &g_pTex );



Is there something horribly wrong what I did?

Share this post


Link to post
Share on other sites
Quote:
Original post by busytree
Animated robot

I have another question Why is my background not square when vertices postion says it so?

*** Source Snippet Removed ***


Because your monitors aspect ratio is not 1:1. More than likely your aspect ratio is 4:3 (1024x768, 1600x1200 etc), 5:4 (1280x1024), or 16:9 (720x540, 1920x1080 ect), which will distort the screen accordingly.

Share this post


Link to post
Share on other sites

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