| |
| |
|
|
HowTo 2: Record an ingame movie (*.avi file) -- Section: Proxy-Dll --
Now that we can intercept every IDirect3DDevice9::Present event, how about saving every frame to a file, thus creating an ingame video ? Well, let see how this could work.
The video file creation routines of this HowTo are based on a fine article by James Dougherty, written for gamedev.net
(http://www.gamedev.net/reference/articles/article2063.asp). I do use some parts of his code, too.
Again, I utilize the Flatspace Demo for testing, focusing on DX9.
How would video creation work ? First, we'd need a keyboard hook to switch recording ingame on and off. Then, we'd intercept every call to IDirect3DDevice9::Present , take a screen snapshot and process it and save it to an avi-stream. Basically, we should have an ingame video then.
- Add a global keyboard hook to your project (using SetWindowsHookEx and a callback routine).
- Add a new class (like "CAviSystem") to your project (where all the new processing takes place).
- Call bitmap/avi processing routines from within myIDirect3DDevice9::Present, if needed.
For the demo, I chose the "INSERT" key to toggle ingame recording on and off. The newly created *.avi file will always be saved to C:\myVideoX.avi (where "X" is an increasing number, in case several recordings are done after a game was started). There is a beep when recording toggles.
To see what I got from Flatspace with this setup, see the downloads page.
Of course, you might need some more tweaking to enhance this up to a "fully qualifying ingame video tool".
The source to this HowTo is available at the downloads page, too.
[UPDATE] Additional info, brought up by Peter Kuhn (thanks a lot, Peter!):
Taken screenshots can sometimes miss some elements of the scene (preferably, HUD or Sprites). This is a result of delayed caching within the video driver. The elements not visible are simply not yet drawn while intercepting Present - although one would expect that at first glance.
Peter proposes to add a "wait loop" inside the proxy'd ::Present call. This could look like this:
HRESULT myIDirect3DDevice9::Present(CONST RECT* pSourceRect,CONST RECT* pDestRect,HWND hDestWindowOverride,CONST RGNDATA* pDirtyRegion)
{
// call original routine
HRESULT hres = m_pIDirect3DDevice9->Present( pSourceRect, pDestRect, hDestWindowOverride, pDirtyRegion);
// Flush the engine content
IDirect3DQuery9* mQuery = NULL;
this->CreateQuery(D3DQUERYTYPE_EVENT, &mQuery);
mQuery->Issue(D3DISSUE_END);
while(mQuery->GetData(NULL, 0, D3DGETDATA_FLUSH) == S_FALSE); // wait loop here
mQuery->Release();
// do anything you want here (e.g. capturing using the GDI), the frame is now rendered completely
return (hres);
}
|
Of course, this will have some impact on fps (minor, though).
|
|
|
|
|
| |