Animating Textures in DirectX 9

Started by
29 comments, last by busytree 17 years, 6 months ago
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
Advertisement
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.lastUpdate>kTimeBetweenUpdated){				g_Vertices.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.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.lastUpdate>kTimeBetweenUpdated){				g_Vertices.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.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.lastUpdate>kTimeBetweenUpdated){				g_Vertices.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.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.lastUpdate>kTimeBetweenUpdated){				g_Vertices.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.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.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.
For better quality timer, check out read this:
http://www.mvps.org/directx/articles/implementing_steady_motion.htm
Tadd- WarbleWare
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?
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

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.
interesting.
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.
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.
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 }    };

This topic is closed to new replies.

Advertisement