void ScreenShot::capture( GuiCanvas *canvas ) { AssertFatal( mPending, "ScreenShot::capture() - The capture wasn't pending!" ); if ( mTiles == 1 ) { _singleCapture( canvas ); return; } char filename[256]; Point2I canvasSize = canvas->getPlatformWindow()->getVideoMode().resolution; // Calculate the real final size taking overlap in account Point2I overlapPixels( canvasSize.x * mPixelOverlap.x, canvasSize.y * mPixelOverlap.y ); // Calculate the overlap to be used by the frustum tiling, // so it properly expands the frustum to overlap the amount of pixels we want mFrustumOverlap.x = ((F32)canvasSize.x/(canvasSize.x - overlapPixels.x*2)) * ((F32)overlapPixels.x/(F32)canvasSize.x); mFrustumOverlap.y = ((F32)canvasSize.y/(canvasSize.y - overlapPixels.y*2)) * ((F32)overlapPixels.y/(F32)canvasSize.y); //overlapPixels.set(0,0); // Get a buffer to write a row of tiles into. GBitmap *outBuffer = new GBitmap( canvasSize.x * mTiles - overlapPixels.x * mTiles * 2 , canvasSize.y - overlapPixels.y ); // Open up the file on disk. dSprintf( filename, 256, "%s.%s", mFilename, "png" ); FileStream fs; if ( !fs.open( filename, Torque::FS::File::Write ) ) Con::errorf( "ScreenShot::capture() - Failed to open output file '%s'!", filename ); // Open a PNG stream for the final image DeferredPNGWriter pngWriter; pngWriter.begin(outBuffer->getFormat(), outBuffer->getWidth(), canvasSize.y * mTiles - overlapPixels.y * mTiles * 2, fs, 0); // Render each tile to generate a huge screenshot. for( U32 ty=0; ty < mTiles; ty++ ) { for( S32 tx=0; tx < mTiles; tx++ ) { // Set the current tile offset for tileFrustum(). mCurrTile.set( tx, mTiles - ty - 1 ); // Let the canvas render the scene. canvas->renderFrame( false ); // Now grab the current back buffer. GBitmap *gb = _captureBackBuffer(); // The current GFX device couldn't capture the backbuffer or it's unable of doing so. if (gb == NULL) return; // Copy the captured bitmap into its tile // within the output bitmap. const U32 inStride = gb->getWidth() * gb->getBytesPerPixel(); const U8 *inColor = gb->getBits() + inStride * overlapPixels.y; const U32 outStride = outBuffer->getWidth() * outBuffer->getBytesPerPixel(); const U32 inOverlapOffset = overlapPixels.x * gb->getBytesPerPixel(); const U32 inOverlapStride = overlapPixels.x * gb->getBytesPerPixel()*2; const U32 outOffset = (tx * (gb->getWidth() - overlapPixels.x*2 )) * gb->getBytesPerPixel(); U8 *outColor = outBuffer->getWritableBits() + outOffset; for( U32 row=0; row < gb->getHeight() - overlapPixels.y; row++ ) { dMemcpy( outColor, inColor + inOverlapOffset, inStride - inOverlapStride ); //Grandient blend the left overlap area of this tile over the previous tile left border if (tx && !(ty && row < overlapPixels.y)) { U8 *blendOverlapSrc = (U8*)inColor; U8 *blendOverlapDst = outColor - inOverlapOffset; for ( U32 px=0; px < overlapPixels.x; px++) { F32 blendFactor = (F32)px / (F32)overlapPixels.x; sBlendPixelRGB888(blendOverlapSrc, blendOverlapDst, blendFactor); blendOverlapSrc += gb->getBytesPerPixel(); blendOverlapDst += outBuffer->getBytesPerPixel(); } } //Gradient blend against the rows the excess overlap rows already in the buffer if (ty && row < overlapPixels.y) { F32 rowBlendFactor = (F32)row / (F32)overlapPixels.y; U8 *blendSrc = outColor + outStride * (outBuffer->getHeight() - overlapPixels.y); U8 *blendDst = outColor; for ( U32 px=0; px < gb->getWidth() - overlapPixels.x*2; px++) { sBlendPixelRGB888(blendSrc, blendDst, 1.0-rowBlendFactor); blendSrc += gb->getBytesPerPixel(); blendDst += outBuffer->getBytesPerPixel(); } } inColor += inStride; outColor += outStride; } delete gb; } // Write the captured tile row into the PNG stream pngWriter.append(outBuffer, outBuffer->getHeight()-overlapPixels.y); } //Close the PNG stream pngWriter.end(); // We captured... clear the flag. mPending = false; }
void TerrainFile::import( const GBitmap &heightMap, F32 heightScale, const Vector<U8> &layerMap, const Vector<String> &materials, bool flipYAxis ) { AssertFatal( heightMap.getWidth() == heightMap.getHeight(), "TerrainFile::import - Height map is not square!" ); AssertFatal( isPow2( heightMap.getWidth() ), "TerrainFile::import - Height map is not power of two!" ); const U32 newSize = heightMap.getWidth(); if ( newSize != mSize ) { mHeightMap.setSize( newSize * newSize ); mHeightMap.compact(); mSize = newSize; } // Convert the height map to heights. U16 *oBits = mHeightMap.address(); if ( heightMap.getFormat() == GFXFormatR5G6B5 ) { const F32 toFixedPoint = ( 1.0f / (F32)U16_MAX ) * floatToFixed( heightScale ); const U16 *iBits = (const U16*)heightMap.getBits(); if ( flipYAxis ) { for ( U32 i = 0; i < mSize * mSize; i++ ) { U16 height = convertBEndianToHost( *iBits ); *oBits = (U16)mCeil( (F32)height * toFixedPoint ); ++oBits; ++iBits; } } else { for(S32 y = mSize - 1; y >= 0; y--) { for(U32 x = 0; x < mSize; x++) { U16 height = convertBEndianToHost( *iBits ); mHeightMap[x + y * mSize] = (U16)mCeil( (F32)height * toFixedPoint ); ++iBits; } } } } else { const F32 toFixedPoint = ( 1.0f / (F32)U8_MAX ) * floatToFixed( heightScale ); const U8 *iBits = heightMap.getBits(); if ( flipYAxis ) { for ( U32 i = 0; i < mSize * mSize; i++ ) { *oBits = (U16)mCeil( ((F32)*iBits) * toFixedPoint ); ++oBits; iBits += heightMap.getBytesPerPixel(); } } else { for(S32 y = mSize - 1; y >= 0; y--) { for(U32 x = 0; x < mSize; x++) { mHeightMap[x + y * mSize] = (U16)mCeil( ((F32)*iBits) * toFixedPoint ); iBits += heightMap.getBytesPerPixel(); } } } } // Copy over the layer map. AssertFatal( layerMap.size() == mHeightMap.size(), "TerrainFile::import - Layer map is the wrong size!" ); mLayerMap = layerMap; mLayerMap.compact(); // Resolve the materials. _resolveMaterials( materials ); // Rebuild the collision grid map. _buildGridMap(); }