Is Resolving a RenderTarget to a GBitmap possible?
by John Kanalakis · in Torque 3D Professional · 05/09/2010 (12:13 pm) · 7 replies
Essentially, I'm trying to get the in-game screen capture function working on Mac OS. The path I'm exploring is grabbing the active render target, resolving to a bitmap, then writing that bitmap to a file.
I started by creating a profile for a PNG-friendly screen capture texture...
Next, I modified GuiCanvas::renderFrame() in guiCanvas.cpp to look for the screen capture signal and then grab the active render target and dump it to an image file. Around line 1650, I added the following..
Everything compiles and links, but at run time, the following error is produced when it's time to take the screen capture.
"Engine/source/gfx/gfxTextureManager.cpp(744):Fatal"
"Anonymous texture didn't get the format it wanted."
I feel like I'm close and that there's something wrong with the profile definition at the beginning. Has anyone tried resolving a render target to a texture and then saved it out to a file?
Thank you,
John K.
EDIT:
Some additional comments... Changing the texture profile to the following results in a slightly different error message...
The error message reads, "Engine/source/gfx/gfxTextureObject.h(179) : Fatal"
"GFXTextureObject::getBitmap - Cannot access bitmap for a 'CaptureTextureProfile2' texture."
I started by creating a profile for a PNG-friendly screen capture texture...
GFX_ImplementTextureProfile( CaptureTextureProfile, GFXTextureProfile::DiffuseMap, GFXTextureProfile::Static, GFXTextureProfile::DXT1 );
Next, I modified GuiCanvas::renderFrame() in guiCanvas.cpp to look for the screen capture signal and then grab the active render target and dump it to an image file. Around line 1650, I added the following..
if( gScreenShot != NULL && gScreenShot->mPending )
{
//JK gScreenShot->captureStandard();
const Point2I &targetSize = renderTarget->getSize();
GFXFormat targetFormat = renderTarget->getFormat();
GFXTexHandle textureObject;
textureObject.set( targetSize.x, targetSize.y, targetFormat, &CaptureTextureProfile, "textureObject" );
renderTarget->resolveTo(textureObject);
GBitmap* bitmapRender = textureObject->getBitmap();
FileStream stream;
stream.open( gScreenShot->mFilename, Torque::FS::File::Write );
bitmapRender->writeBitmap( "png", stream );
stream.close();
delete bitmapRender;
gScreenShot->mPending = false;
}
PROFILE_END(); Everything compiles and links, but at run time, the following error is produced when it's time to take the screen capture.
"Engine/source/gfx/gfxTextureManager.cpp(744):Fatal"
"Anonymous texture didn't get the format it wanted."
I feel like I'm close and that there's something wrong with the profile definition at the beginning. Has anyone tried resolving a render target to a texture and then saved it out to a file?
Thank you,
John K.
EDIT:
Some additional comments... Changing the texture profile to the following results in a slightly different error message...
GFX_ImplementTextureProfile( CaptureTextureProfile2, GFXTextureProfile::DiffuseMap, GFXTextureProfile::PreserveSize | GFXTextureProfile::RenderTarget | GFXTextureProfile::Pooled, GFXTextureProfile::None );
The error message reads, "Engine/source/gfx/gfxTextureObject.h(179) : Fatal"
"GFXTextureObject::getBitmap - Cannot access bitmap for a 'CaptureTextureProfile2' texture."
About the author
John Kanalakis is the owner of EnvyGames, an independent game development studio in Silicon Valley that produces games and tools for Xbox 360, Windows, and the Web.
#2
The error now reads, "Engine/source/gfx/gfxTextureManager.cpp(774) : Fatal"
"Anonymous texture didn't get the format it wanted."
I also tried setting the profile compression to None, but it has no effect.
John K.
05/10/2010 (4:26 pm)
Thank you, Manoel, it looks like it's getting closer, but still produces an error. I made the following change:GFX_ImplementTextureProfile( CaptureTextureProfile, GFXTextureProfile::DiffuseMap, GFXTextureProfile::SystemMemory, GFXTextureProfile::DXT1 );
The error now reads, "Engine/source/gfx/gfxTextureManager.cpp(774) : Fatal"
"Anonymous texture didn't get the format it wanted."
I also tried setting the profile compression to None, but it has no effect.
John K.
#3
05/11/2010 (6:07 am)
John, I remembered that I had to solve something like this a while ago. Let me know if this code helps.// this is using a rendertarget that has its separate bin
// but I don't see why it shouldn't work with other render targets
MatTextureTarget *texTarget = MatTextureTarget::findTargetByName( "portrait" );
if (!texTarget)
return;
GFXTextureObject *texObject = texTarget->getTargetTexture(0);
if (!texObject)
return;
// this is probably the more interesting part
GBitmap bmp( texObject->getWidth(), texObject->getHeight(), false, GFXFormatR8G8B8A8 );
// you might not need the fill - I had to render over
// a transparent bg
bmp.fill(ColorI(0,0,0,0));
texObject->copyToBmp( &bmp );
// finally save the file where "filename" is a parameter
// for the current function
FileStream stream;
stream.open( filename, Torque::FS::File::Write );
if ( stream.getStatus() == Stream::Ok )
bmp.writeBitmap("png", stream);
#4
It gets a little closer, but now the error generated indicates the following:
"Profiler::hashPop - didn't get expected ProfilerRoot!"
At least it's something new for me to look into. Thank you again for your advice.
John K.
05/11/2010 (3:54 pm)
Very interesting, thank you Konrad. I'll need to look into this approach of registering specific render targets more closely, as you're doing in line 3. The variation of your example that I tried (using a GFXTextureObject*) looks like this...GFXTextureObject* texObject;
renderTarget->resolveTo(texObject);
if (!texObject)
return;
// this is probably the more interesting part
GBitmap bmp( texObject->getWidth(), texObject->getHeight(), false, GFXFormatR8G8B8A8 );
// you might not need the fill - I had to render over a transparent bg
bmp.fill(ColorI(0,0,0,0));
texObject->copyToBmp( &bmp );
// finally save the file where "filename" is a parameter for the current function
FileStream stream;
stream.open( gScreenShot->mFilename, Torque::FS::File::Write );
if ( stream.getStatus() == Stream::Ok )
bmp.writeBitmap("png", stream);It requires changing the texObject definition from GFXTarget* renderTarget into a GFXGLTextureTarget*It gets a little closer, but now the error generated indicates the following:
"Profiler::hashPop - didn't get expected ProfilerRoot!"
At least it's something new for me to look into. Thank you again for your advice.
John K.
#5
Here's the texture profile...
Here's the capture code...
The console output that reports the dimensions returns this...
****** renderTargetSize=1000x550, hTexture.Size=1024x1024
The images that are created are also stretched to 1024x1024.
I'm just a little puzzled since I'm creating the GFXTexHandle with the dimensions of the render target, which is 1000x50 (the size of the entire renderable window). It seems that the resolveTo() method is changing the GFXTexHandle to 1024x1024 for some reason.
Anyone have any ideas?
John K.
05/16/2010 (9:11 pm)
Here's an update. Thanks to Konrad's pointer, I'm pretty close. The screen capture is saved to disk, but there is a strange behavior that I'm still trying to shake. The GBitmap is always 1024x1024. Even though I set an explicitly different size, it's always 1024x1024.Here's the texture profile...
GFX_ImplementTextureProfile( CaptureTextureProfile, GFXTextureProfile::DiffuseMap, GFXTextureProfile::SystemMemory, GFXTextureProfile::None );
Here's the capture code...
if( gScreenShot != NULL && gScreenShot->mPending )
{
//JK gScreenShot->captureStandard();
GFXTarget* renderTarget = mPlatformWindow->getGFXTarget();
const Point2I& renderTargetSize = renderTarget->getSize();
GFXFormat renderTargetFormat = renderTarget->getFormat();
GFXTexHandle* hTexture = new GFXTexHandle( renderTargetSize.x,
renderTargetSize.y,
renderTargetFormat,
&CaptureTextureProfile,
"textureObject");
renderTarget->resolveTo( *hTexture );
Con::printf("****** renderTargetSize=%dx%d, hTexture.Size=%dx%d", renderTargetSize.x, renderTargetSize.y, hTexture->getWidth(), hTexture->getHeight() );
if (!hTexture)
return;
GBitmap bitmap( hTexture->getWidth(), hTexture->getHeight(), false, GFXFormatR8G8B8A8 );
bitmap.fill(ColorI(0,0,0,0));
hTexture->copyToBmp( &bitmap );
FileStream stream;
stream.open( gScreenShot->mFilename, Torque::FS::File::Write );
if ( stream.getStatus() == Stream::Ok )
bitmap.writeBitmap("png", stream);
gScreenShot->mPending = false;
} The console output that reports the dimensions returns this...
****** renderTargetSize=1000x550, hTexture.Size=1024x1024
The images that are created are also stretched to 1024x1024.
I'm just a little puzzled since I'm creating the GFXTexHandle with the dimensions of the render target, which is 1000x50 (the size of the entire renderable window). It seems that the resolveTo() method is changing the GFXTexHandle to 1024x1024 for some reason.
Anyone have any ideas?
John K.
#6
So 1000 becomes 1024 and 550 becomes 1024.
After that, does your image fills all the texture surface, or only the 1000x550 one.
Perhaps you have to copy only the region you need, or you need to resize your texture when you copy it in your bitmap?
Nicolas Buquet
www.buquet-net.com/cv/
05/17/2010 (3:00 am)
I think it is because textures' dimension need to be a power of 2.So 1000 becomes 1024 and 550 becomes 1024.
After that, does your image fills all the texture surface, or only the 1000x550 one.
Perhaps you have to copy only the region you need, or you need to resize your texture when you copy it in your bitmap?
Nicolas Buquet
www.buquet-net.com/cv/
#7
An added bonus would be that you would have a firm control over what would be elected onto that texture target. Also, in this case you would not have to worry about the stretching or the pow 2 dimensions afaik.
05/17/2010 (3:56 am)
Perhaps a bloated solution in your case, but if you made a named texture target - just like renderGlowManager does - then you could get the texture object like I suggested. You'd only have to render everything into it - so addElement would add everything.An added bonus would be that you would have a firm control over what would be elected onto that texture target. Also, in this case you would not have to worry about the stretching or the pow 2 dimensions afaik.
Associate Manoel Neto
Default Studio Name