diff --git a/neo/framework/common_frame.cpp b/neo/framework/common_frame.cpp index a23e83d9..bae66cc6 100644 --- a/neo/framework/common_frame.cpp +++ b/neo/framework/common_frame.cpp @@ -31,7 +31,7 @@ If you have questions concerning this license or the applicable additional terms #pragma hdrstop #include "Common_local.h" -#include "../renderer/Image.h" +#include "../renderer/Image.h" // now I did it! #include "../renderer/ImageOpts.h" // RB begin @@ -530,14 +530,15 @@ void idCommonLocal::Frame() // save the screenshot and audio from the last draw if needed if( aviCaptureMode ) { - idStr name = va( "demos/%s/%s_%05i.tga", aviDemoShortName.c_str(), aviDemoShortName.c_str(), aviDemoFrameCount++ ); - renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL ); + idStr name; + name.Format( "demos/%s/%s_%05i", aviDemoShortName.c_str(), aviDemoShortName.c_str(), aviDemoFrameCount++ ); + renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL, TGA ); // remove any printed lines at the top before taking the screenshot console->ClearNotifyLines(); // this will call Draw, possibly multiple times if com_aviDemoSamples is > 1 - renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL ); + renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL, TGA ); } //-------------------------------------------- diff --git a/neo/renderer/Image.h b/neo/renderer/Image.h index 6a90561f..368c678f 100644 --- a/neo/renderer/Image.h +++ b/neo/renderer/Image.h @@ -69,6 +69,13 @@ typedef enum CF_2D_ARRAY // not a cube map but not a single 2d texture either } cubeFiles_t; +enum imageFileType_t +{ + TGA, + PNG, + JPG +}; + #include "ImageOpts.h" #include "BinaryImage.h" @@ -390,6 +397,7 @@ void R_BlendOverTexture( byte* data, int pixelCount, const byte blend[4] ); void R_HorizontalFlip( byte* data, int width, int height ); void R_VerticalFlip( byte* data, int width, int height ); void R_RotatePic( byte* data, int width ); +void R_ApplyCubeMapTransforms( int i, byte* data, int size ); /* ==================================================================== diff --git a/neo/renderer/Image_process.cpp b/neo/renderer/Image_process.cpp index cee19af1..a10d760b 100644 --- a/neo/renderer/Image_process.cpp +++ b/neo/renderer/Image_process.cpp @@ -538,6 +538,7 @@ void R_RotatePic( byte* data, int width ) { for( j = 0 ; j < width ; j++ ) { + // apparently rotates the picture and then it flips the picture horitzontally *( temp + i * width + j ) = *( ( int* )data + j * width + i ); } } @@ -547,3 +548,25 @@ void R_RotatePic( byte* data, int width ) R_StaticFree( temp ); } +// transforms in both ways, the images from a cube map, +// in both the Env map and the Skybox map systems. +void R_ApplyCubeMapTransforms( int iter, byte* data, int size ) +{ + if( ( iter == 1 ) || ( iter == 2 ) ) + { + R_VerticalFlip( data, size, size ); + } + if( ( iter == 0 ) || ( iter == 1 ) || ( iter == 4 ) || ( iter == 5 ) ) + { + R_RotatePic( data, size ); // apparently not only rotates but also flips horitzontally + } + if( iter == 1 ) + { + R_VerticalFlip( data, size, size ); + } + else if( iter == 3 ) // that's so just for having less lines + { + R_HorizontalFlip( data, size, size ); + } +} + diff --git a/neo/renderer/RenderSystem.h b/neo/renderer/RenderSystem.h index a9e757d7..c21088fd 100644 --- a/neo/renderer/RenderSystem.h +++ b/neo/renderer/RenderSystem.h @@ -332,7 +332,7 @@ public: // This will perform swapbuffers, so it is NOT an approppriate way to // generate image files that happen during gameplay, as for savegame // markers. Use WriteRender() instead. - virtual void TakeScreenshot( int width, int height, const char* fileName, int samples, struct renderView_s* ref ) = 0; + virtual void TakeScreenshot( int width, int height, const char* fileName, int samples, struct renderView_s* ref, int exten ) = 0; // the render output can be cropped down to a subset of the real screen, as // for save-game reviews and split-screen multiplayer. Users of the renderer diff --git a/neo/renderer/RenderSystem_init.cpp b/neo/renderer/RenderSystem_init.cpp index aad44109..2678ef38 100644 --- a/neo/renderer/RenderSystem_init.cpp +++ b/neo/renderer/RenderSystem_init.cpp @@ -237,6 +237,10 @@ idCVar r_shadowMapPolygonOffset( "r_shadowMapPolygonOffset", "3000", CVAR_RENDER idCVar r_shadowMapOccluderFacing( "r_shadowMapOccluderFacing", "2", CVAR_RENDERER | CVAR_INTEGER, "0 = front faces, 1 = back faces, 2 = twosided" ); // RB end +const char* fileExten[3] = { "tga", "png", "jpg" }; +const char* envDirection[6] = { "_nx", "_py", "_ny", "_pz", "_nz", "_px" }; +const char* skyDirection[6] = { "_forward", "_back", "_left", "_right", "_up", "_down" }; + /* ======================== @@ -1223,20 +1227,39 @@ If ref == NULL, common->UpdateScreen will be used ================== */ // RB: changed .tga to .png -void idRenderSystemLocal::TakeScreenshot( int width, int height, const char* fileName, int blends, renderView_t* ref ) +void idRenderSystemLocal::TakeScreenshot( int width, int height, const char* fileName, int blends, renderView_t* ref, int exten ) { byte* buffer; - int i, j; + int i, j, c, temp; + idStr finalFileName; + + finalFileName.Format( "%s.%s", fileName, fileExten[exten] ); takingScreenshot = true; - int pix = width * height; + int pix = width * height; + const int bufferSize = pix * 3 + 18; - buffer = ( byte* )R_StaticAlloc( pix * 3 ); + if( exten == PNG ) + { + buffer = ( byte* )R_StaticAlloc( pix * 3 ); + } + else if( exten == TGA ) + { + buffer = ( byte* )R_StaticAlloc( bufferSize ); + memset( buffer, 0, bufferSize ); + } if( blends <= 1 ) { - R_ReadTiledPixels( width, height, buffer, ref ); + if( exten == PNG ) + { + R_ReadTiledPixels( width, height, buffer, ref ); + } + else if( exten == TGA ) + { + R_ReadTiledPixels( width, height, buffer + 18, ref ); + } } else { @@ -1248,25 +1271,70 @@ void idRenderSystemLocal::TakeScreenshot( int width, int height, const char* fil for( i = 0 ; i < blends ; i++ ) { - R_ReadTiledPixels( width, height, buffer, ref ); + if( exten == PNG ) + { + R_ReadTiledPixels( width, height, buffer, ref ); + } + else if( exten == TGA ) + { + R_ReadTiledPixels( width, height, buffer + 18, ref ); + } for( j = 0 ; j < pix * 3 ; j++ ) { - shortBuffer[j] += buffer[j]; + if( exten == PNG ) + { + shortBuffer[j] += buffer[j]; + } + else if( exten == TGA ) + { + shortBuffer[j] += buffer[18 + j]; + } } } // divide back to bytes for( i = 0 ; i < pix * 3 ; i++ ) { - buffer[i] = shortBuffer[i] / blends; + if( exten == PNG ) + { + buffer[i] = shortBuffer[i] / blends; + } + else if( exten == TGA ) + { + buffer[18 + i] = shortBuffer[i] / blends; + } } R_StaticFree( shortBuffer ); r_jitter.SetBool( false ); } - - R_WritePNG( fileName, buffer, 3, width, height, false ); + if( exten == PNG ) + { + R_WritePNG( finalFileName, buffer, 3, width, height, false ); + } + else + { + // fill in the header (this is vertically flipped, which qglReadPixels emits) + buffer[2] = 2; // uncompressed type + buffer[12] = width & 255; + buffer[13] = width >> 8; + buffer[14] = height & 255; + buffer[15] = height >> 8; + buffer[16] = 24; // pixel size + + // swap rgb to bgr + c = 18 + width * height * 3; + + for( i = 18 ; i < c ; i += 3 ) + { + temp = buffer[i]; + buffer[i] = buffer[i + 2]; + buffer[i + 2] = temp; + } + + fileSystem->WriteFile( finalFileName, buffer, c ); + } R_StaticFree( buffer ); @@ -1317,7 +1385,7 @@ void R_ScreenshotFilename( int& lastNumber, const char* base, idStr& fileName ) time( &aclock ); struct tm* t = localtime( &aclock ); - sprintf( fileName, "%s%s-%04d%02d%02d-%02d%02d%02d-%03d.png", base, "rbdoom-3-bfg", + sprintf( fileName, "%s%s-%04d%02d%02d-%02d%02d%02d-%03d", base, "rbdoom-3-bfg", 1900 + t->tm_year, 1 + t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, lastNumber ); #endif // RB end @@ -1397,7 +1465,7 @@ void R_ScreenShot_f( const idCmdArgs& args ) // put the console away console->Close(); - tr.TakeScreenshot( width, height, checkname, blends, NULL ); + tr.TakeScreenshot( width, height, checkname, blends, NULL, PNG ); common->Printf( "Wrote %s\n", checkname.c_str() ); } @@ -1444,6 +1512,137 @@ void R_StencilShot() fileSystem->WriteFile( "screenshots/stencilShot.tga", buffer.Ptr(), c, "fs_savepath" ); } +/* +================== +R_EnvShot_f + +envshot + +Saves out env/_ft.tga, etc +================== +*/ +void R_EnvShot_f( const idCmdArgs& args ) +{ + idStr fullname; + const char* baseName; + int i; + idMat3 axis[7], oldAxis; + renderView_t ref; + viewDef_t primary; + int blends; + const char* extension; + int size; + int res_w, res_h, old_fov_x, old_fov_y; + + res_w = renderSystem->GetWidth(); + res_h = renderSystem->GetHeight(); + + if( args.Argc() != 2 && args.Argc() != 3 && args.Argc() != 4 ) + { + common->Printf( "USAGE: envshot [size] [blends]\n" ); + return; + } + baseName = args.Argv( 1 ); + + blends = 1; + if( args.Argc() == 4 ) + { + size = atoi( args.Argv( 2 ) ); + blends = atoi( args.Argv( 3 ) ); + } + else if( args.Argc() == 3 ) + { + size = atoi( args.Argv( 2 ) ); + blends = 1; + } + else + { + size = 256; + blends = 1; + } + + if( !tr.primaryView ) + { + common->Printf( "No primary view.\n" ); + return; + } + + primary = *tr.primaryView; + + memset( &axis, 0, sizeof( axis ) ); + axis[0][0][0] = 1; // this one gets ignored as it always come out wrong. + axis[0][1][2] = 1; // and so we repeat this axis as the last one. + axis[0][2][1] = 1; + + axis[1][0][0] = -1; + axis[1][1][2] = -1; + axis[1][2][1] = 1; + + axis[2][0][1] = 1; + axis[2][1][0] = -1; + axis[2][2][2] = -1; + + axis[3][0][1] = -1; + axis[3][1][0] = -1; + axis[3][2][2] = 1; + + axis[4][0][2] = 1; + axis[4][1][0] = -1; + axis[4][2][1] = 1; + + axis[5][0][2] = -1; + axis[5][1][0] = 1; + axis[5][2][1] = 1; + + axis[6][0][0] = 1; // this is the repetition of the first axis + axis[6][1][2] = 1; + axis[6][2][1] = 1; + + // let's get the game window to a "size" resolution + if( ( res_w != size ) || ( res_h != size ) ) + { + cvarSystem->SetCVarInteger( "r_windowWidth", size ); + cvarSystem->SetCVarInteger( "r_windowHeight", size ); + R_SetNewMode( false ); // the same as "vid_restart" + } // FIXME that's a hack!! + + for( i = 0 ; i < 7 ; i++ ) + { + + ref = primary.renderView; + + if( i == 0 ) + { + // so we return to that axis and fov after the fact. + oldAxis = ref.viewaxis; + old_fov_x = ref.fov_x; + old_fov_y = ref.fov_y; + //this is part of the hack + extension = "_wrong"; + } + else + { + // this keeps being part of the hack + extension = envDirection[ i - 1 ]; //yes, indeed, this is totally part of the hack! + } + + ref.fov_x = ref.fov_y = 90; + ref.viewaxis = axis[i]; + fullname.Format( "env/%s%s", baseName, extension ); + + tr.TakeScreenshot( size, size, fullname, blends, &ref, TGA ); + } + + // restore the original resolution, axis and fov + ref.viewaxis = oldAxis; + ref.fov_x = old_fov_x; + ref.fov_y = old_fov_y; + cvarSystem->SetCVarInteger( "r_windowWidth", res_w ); + cvarSystem->SetCVarInteger( "r_windowHeight", res_h ); + R_SetNewMode( false ); // the same as "vid_restart" + + common->Printf( "Wrote a env set with the name %s\n", baseName ); +} //============================================================================ @@ -1536,9 +1735,6 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) renderView_t ref; viewDef_t primary; int downSample; - const char* extensions[6] = { "_px.tga", "_nx.tga", "_py.tga", "_ny.tga", - "_pz.tga", "_nz.tga" - }; int outSize; byte* buffers[6]; int width = 0, height = 0; @@ -1588,7 +1784,7 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) // read all of the images for( i = 0 ; i < 6 ; i++ ) { - sprintf( fullname, "env/%s%s", baseName, extensions[i] ); + fullname.Format( "env/%s%s.%s", baseName, envDirection[i], fileExten[TGA] ); common->Printf( "loading %s\n", fullname.c_str() ); const bool captureToImage = false; common->UpdateScreen( captureToImage ); @@ -1663,11 +1859,11 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) if( map == 0 ) { - sprintf( fullname, "env/%s_amb%s", baseName, extensions[i] ); + fullname.Format( "env/%s_amb%s.%s", baseName, envDirection[i], fileExten[TGA] ); } else { - sprintf( fullname, "env/%s_spec%s", baseName, extensions[i] ); + fullname.Format( "env/%s_spec%s.%s", baseName, envDirection[i], fileExten[TGA] ); } common->Printf( "writing %s\n", fullname.c_str() ); const bool captureToImage = false; @@ -1685,6 +1881,115 @@ void R_MakeAmbientMap_f( const idCmdArgs& args ) } } +void R_TransformCubemap( const char* orgDirection[6], const char* orgDir, const char* destDirection[6], const char* destDir, const char* baseName ) +{ + idStr fullname; + int i; + bool errorInOriginalImages = false; + int outSize; + byte* buffers[6]; + int width = 0, height = 0; + + for( i = 0 ; i < 6 ; i++ ) + { + // read every image images + fullname.Format( "%s/%s%s.%s", orgDir, baseName, orgDirection[i], fileExten [TGA] ); + common->Printf( "loading %s\n", fullname.c_str() ); + const bool captureToImage = false; + common->UpdateScreen( captureToImage ); + R_LoadImage( fullname, &buffers[i], &width, &height, NULL, true ); + + //check if the buffer is troublesome + if( !buffers[i] ) + { + common->Printf( "failed.\n" ); + errorInOriginalImages = true; + } + else if( width != height ) + { + common->Printf( "wrong size pal!\n\n\nget your shit together and set the size according to your images!\n\n\ninept programmers are inept!\n" ); + errorInOriginalImages = true; // yeah, but don't just choke on a joke! + } + else + { + errorInOriginalImages = false; + } + + if( errorInOriginalImages ) + { + errorInOriginalImages = false; + for( i-- ; i >= 0 ; i-- ) + { + Mem_Free( buffers[i] ); // clean up every buffer from this stage down + } + + return; + } + + // apply rotations and flips + R_ApplyCubeMapTransforms( i, buffers[i], width ); + + //save the images with the appropiate skybox naming convention + fullname.Format( "%s/%s/%s%s.%s", destDir, baseName, baseName, destDirection[i], fileExten [TGA] ); + common->Printf( "writing %s\n", fullname.c_str() ); + common->UpdateScreen( false ); + R_WriteTGA( fullname, buffers[i], width, width ); + } + + for( i = 0 ; i < 6 ; i++ ) + { + if( buffers[i] ) + { + Mem_Free( buffers[i] ); + } + } +} + +/* +================== +R_TransformEnvToSkybox_f + +R_TransformEnvToSkybox_f + +transforms env textures (of the type px, py, pz, nx, ny, nz) +to skybox textures ( forward, back, left, right, up, down) +================== +*/ +void R_TransformEnvToSkybox_f( const idCmdArgs& args ) +{ + + if( args.Argc() != 2 ) + { + common->Printf( "USAGE: envToSky \n" ); + return; + } + + R_TransformCubemap( envDirection, "env", skyDirection, "skybox", args.Argv( 1 ) ); +} + +/* +================== +R_TransformSkyboxToEnv_f + +R_TransformSkyboxToEnv_f + +transforms skybox textures ( forward, back, left, right, up, down) +to env textures (of the type px, py, pz, nx, ny, nz) +================== +*/ + +void R_TransformSkyboxToEnv_f( const idCmdArgs& args ) +{ + + if( args.Argc() != 2 ) + { + common->Printf( "USAGE: skyToEnv \n" ); + return; + } + + R_TransformCubemap( skyDirection, "skybox", envDirection, "env", args.Argv( 1 ) ); +} + //============================================================================ @@ -2039,7 +2344,10 @@ void R_InitCommands() cmdSystem->AddCommand( "listGuis", R_ListGuis_f, CMD_FL_RENDERER, "lists guis" ); cmdSystem->AddCommand( "touchGui", R_TouchGui_f, CMD_FL_RENDERER, "touches a gui" ); cmdSystem->AddCommand( "screenshot", R_ScreenShot_f, CMD_FL_RENDERER, "takes a screenshot" ); + cmdSystem->AddCommand( "envshot", R_EnvShot_f, CMD_FL_RENDERER, "takes an environment shot" ); cmdSystem->AddCommand( "makeAmbientMap", R_MakeAmbientMap_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "makes an ambient map" ); + cmdSystem->AddCommand( "envToSky", R_TransformEnvToSkybox_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "transforms environment textures to sky box textures" ); + cmdSystem->AddCommand( "skyToEnv", R_TransformSkyboxToEnv_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "transforms sky box textures to environment textures" ); cmdSystem->AddCommand( "gfxInfo", GfxInfo_f, CMD_FL_RENDERER, "show graphics info" ); cmdSystem->AddCommand( "modulateLights", R_ModulateLights_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "modifies shader parms on all lights" ); cmdSystem->AddCommand( "testImage", R_TestImage_f, CMD_FL_RENDERER | CMD_FL_CHEAT, "displays the given image centered on screen", idCmdSystem::ArgCompletion_ImageName ); diff --git a/neo/renderer/tr_local.h b/neo/renderer/tr_local.h index 8944973c..26c0d89d 100644 --- a/neo/renderer/tr_local.h +++ b/neo/renderer/tr_local.h @@ -814,7 +814,7 @@ public: virtual const emptyCommand_t* SwapCommandBuffers_FinishCommandBuffers(); virtual void RenderCommandBuffers( const emptyCommand_t* commandBuffers ); - virtual void TakeScreenshot( int width, int height, const char* fileName, int downSample, renderView_t* ref ); + virtual void TakeScreenshot( int width, int height, const char* fileName, int downSample, renderView_t* ref, int exten ); virtual void CropRenderSize( int width, int height ); virtual void CaptureRenderToImage( const char* imageName, bool clearColorAfterCopy = false ); virtual void CaptureRenderToFile( const char* fileName, bool fixAlpha );