// "filename" param is something like "screenshots/shot0000.tga" // note that if the last extension is ".jpg", then it'll save a JPG, else TGA // void R_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { #ifndef _XBOX byte *buffer; int i, c, temp; qboolean bSaveAsJPG = !strnicmp(&fileName[strlen(fileName)-4],".jpg",4); if (bSaveAsJPG) { // JPG saver expects to be fed RGBA data, though it presumably ignores 'A'... // buffer = (unsigned char *) Z_Malloc(glConfig.vidWidth*glConfig.vidHeight*4, TAG_TEMP_WORKSPACE, qfalse); qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); // gamma correct if ( tr.overbrightBits>0 && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); } SaveJPG(fileName, 95, width, height, buffer); } else { // TGA... // buffer = (unsigned char *) Z_Malloc(glConfig.vidWidth*glConfig.vidHeight*3 + 18, TAG_TEMP_WORKSPACE, qfalse); memset (buffer, 0, 18); 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 qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); // 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; } // gamma correct if ( tr.overbrightBits>0 && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 ); } FS_WriteFile( fileName, buffer, c ); } Z_Free( buffer ); #endif }
/* ================== GL_ScreenShot_f ================== */ void GL_ScreenShot_f (void) { byte *buffer; #ifdef WIN32 DWORD tID; #endif buffer = malloc(vid.width*vid.height*3); qglReadPixels (0, 0, vid.width, vid.height, GL_RGB, GL_UNSIGNED_BYTE, buffer ); #ifdef WIN32 if (!strcmp (ri.Cmd_Argv(1), "jpg")) { GL_ScreenShot_JPG (buffer); } else { #ifdef USE_THREADS //_beginthreadex (NULL, 0, (unsigned int (__stdcall *)(void *))png_write_thread, (void *)buffer, 0, &tID); CreateThread (NULL, 0, (LPTHREAD_START_ROUTINE)png_write_thread, (LPVOID)buffer, 0, &tID); ri.Con_Printf (PRINT_ALL, "Taking PNG screenshot...\n"); #else png_write_thread (buffer); #endif } #else GL_ScreenShot_JPG (buffer); #endif }
/* ======================================================================================================================================= RB_TestFlare ======================================================================================================================================= */ void RB_TestFlare(flare_t *f) { float depth; qboolean visible; float fade; float screenZ; FBO_t *oldFbo; backEnd.pc.c_flareTests++; // doing a readpixels is as good as doing a glFinish(), so // don't bother with another sync glState.finishCalled = qfalse; // if we're doing multisample rendering, read from the correct FBO oldFbo = glState.currentFBO; if (tr.msaaResolveFbo) { FBO_Bind(tr.msaaResolveFbo); } // read back the z buffer contents qglReadPixels(f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth); // if we're doing multisample rendering, switch to the old FBO if (tr.msaaResolveFbo) { FBO_Bind(oldFbo); } screenZ = backEnd.viewParms.projectionMatrix[14] / ((2*depth - 1) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10]); visible = (qboolean)(f->flags & 1); if (-f->eyeZ - -screenZ > 24) visible = qfalse; if (visible) { if (!f->visible) { f->visible = qtrue; f->fadeTime = backEnd.refdef.time - 1; } fade = ((backEnd.refdef.time - f->fadeTime) /1000.0f) * r_flareFade->value; } else { if (f->visible) { f->visible = qfalse; f->fadeTime = backEnd.refdef.time - 1; } fade = 1.0f - ((backEnd.refdef.time - f->fadeTime) / 1000.0f) * r_flareFade->value; } if (fade < 0) { fade = 0; } if (fade > 1) { fade = 1; } f->drawIntensity = fade; }
void R_LevelShot( void ) { #ifndef _XBOX char checkname[MAX_OSPATH]; byte *buffer; byte *source; byte *src, *dst; int x, y; int r, g, b; float xScale, yScale; int xx, yy; sprintf( checkname, "levelshots/%s.tga", tr.worldDir + strlen("maps/") ); source = (byte*) Z_Malloc( glConfig.vidWidth * glConfig.vidHeight * 3, TAG_TEMP_WORKSPACE, qfalse ); buffer = (byte*) Z_Malloc( LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18, TAG_TEMP_WORKSPACE, qfalse ); memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = LEVELSHOTSIZE & 255; buffer[13] = LEVELSHOTSIZE >> 8; buffer[14] = LEVELSHOTSIZE & 255; buffer[15] = LEVELSHOTSIZE >> 8; buffer[16] = 24; // pixel size qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); // resample from source xScale = glConfig.vidWidth / (4.0*LEVELSHOTSIZE); yScale = glConfig.vidHeight / (3.0*LEVELSHOTSIZE); for ( y = 0 ; y < LEVELSHOTSIZE ; y++ ) { for ( x = 0 ; x < LEVELSHOTSIZE ; x++ ) { r = g = b = 0; for ( yy = 0 ; yy < 3 ; yy++ ) { for ( xx = 0 ; xx < 4 ; xx++ ) { src = source + 3 * ( glConfig.vidWidth * (int)( (y*3+yy)*yScale ) + (int)( (x*4+xx)*xScale ) ); r += src[0]; g += src[1]; b += src[2]; } } dst = buffer + 18 + 3 * ( y * LEVELSHOTSIZE + x ); dst[0] = b / 12; dst[1] = g / 12; dst[2] = r / 12; } } // gamma correct if ( glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, LEVELSHOTSIZE * LEVELSHOTSIZE * 3 ); } FS_WriteFile( checkname, buffer, LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18 ); Z_Free( buffer ); Z_Free( source ); VID_Printf( PRINT_ALL, "Wrote %s\n", checkname ); #endif }
/* ================== RB_ReadPixels Reads an image but takes care of alignment issues for reading RGB images. Reads a minimum offset for where the RGB data starts in the image from integer stored at pointer offset. When the function has returned the actual offset was written back to address offset. This address will always have an alignment of packAlign to ensure efficient copying. Stores the length of padding after a line of pixels to address padlen Return value must be freed with ri.Hunk_FreeTempMemory() ================== */ byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) { byte *buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); bufstart = buffer; padwidth = linelen; int p2width = 1, p2height = 1; int xx, yy, aa; while (p2width < glConfig.vidWidth) p2width *= 2; while (p2height < glConfig.vidHeight) p2height *= 2; byte *source = (byte *) ri.Hunk_AllocateTempMemory(p2width * p2height * 4); qglReadPixels(0, 0, p2width, p2height, GL_RGBA, GL_UNSIGNED_BYTE, source); for (yy = y; yy < height; yy++) for (xx = x; xx < width; xx++) for (aa = 0; aa < 3; aa++) buffer[yy * width * 3 + xx * 3 + aa] = source[(yy + y) * p2width * 4 + (xx + x) * 4 + aa]; ri.Hunk_FreeTempMemory(source); *offset = bufstart - buffer; *padlen = padwidth - linelen; return buffer; }
static void R_LevelShot( void ) { #ifndef _XBOX char checkname[MAX_OSPATH]; byte *buffer; byte *source; byte *src, *dst; int x, y; int r, g, b; float xScale, yScale; int xx, yy; sprintf( checkname, "levelshots/%s.tga", tr.world->baseName ); source = (unsigned char *)Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight * 3 ); buffer = (unsigned char *)Hunk_AllocateTempMemory( LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18); Com_Memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = LEVELSHOTSIZE & 255; buffer[13] = LEVELSHOTSIZE >> 8; buffer[14] = LEVELSHOTSIZE & 255; buffer[15] = LEVELSHOTSIZE >> 8; buffer[16] = 24; // pixel size qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); // resample from source xScale = glConfig.vidWidth / (4.0*LEVELSHOTSIZE); yScale = glConfig.vidHeight / (3.0*LEVELSHOTSIZE); for ( y = 0 ; y < LEVELSHOTSIZE ; y++ ) { for ( x = 0 ; x < LEVELSHOTSIZE ; x++ ) { r = g = b = 0; for ( yy = 0 ; yy < 3 ; yy++ ) { for ( xx = 0 ; xx < 4 ; xx++ ) { src = source + 3 * ( glConfig.vidWidth * (int)( (y*3+yy)*yScale ) + (int)( (x*4+xx)*xScale ) ); r += src[0]; g += src[1]; b += src[2]; } } dst = buffer + 18 + 3 * ( y * LEVELSHOTSIZE + x ); dst[0] = b / 12; dst[1] = g / 12; dst[2] = r / 12; } } // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, LEVELSHOTSIZE * LEVELSHOTSIZE * 3 ); } FS_WriteFile( checkname, buffer, LEVELSHOTSIZE * LEVELSHOTSIZE*3 + 18 ); Hunk_FreeTempMemory( buffer ); Hunk_FreeTempMemory( source ); Com_Printf ("Wrote %s\n", checkname ); #endif }
/* ================== R_TakeScreenshot ================== */ void R_TakeScreenshot( int x, int y, int width, int height, char *fileName ) { byte *buffer; int i, c, temp; buffer = (unsigned char *)Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*3+18); Com_Memset (buffer, 0, 18); 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 qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer+18 ); // 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; } // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, glConfig.vidWidth * glConfig.vidHeight * 3 ); } FS_WriteFile( fileName, buffer, c ); Hunk_FreeTempMemory( buffer ); }
/* ============== CaptureRenderToFile ============== */ void idRenderSystemLocal::CaptureRenderToFile( const char *fileName, bool fixAlpha ) { if ( !glConfig.isInitialized ) { return; } renderCrop_t *rc = &renderCrops[currentRenderCrop]; guiModel->EmitFullScreen(); guiModel->Clear(); R_IssueRenderCommands(); qglReadBuffer( GL_BACK ); // include extra space for OpenGL padding to word boundaries int c = ( rc->width + 3 ) * rc->height; byte *data = (byte *)R_StaticAlloc( c * 3 ); qglReadPixels( rc->x, rc->y, rc->width, rc->height, GL_RGB, GL_UNSIGNED_BYTE, data ); byte *data2 = (byte *)R_StaticAlloc( c * 4 ); for ( int i = 0 ; i < c ; i++ ) { data2[ i * 4 ] = data[ i * 3 ]; data2[ i * 4 + 1 ] = data[ i * 3 + 1 ]; data2[ i * 4 + 2 ] = data[ i * 3 + 2 ]; data2[ i * 4 + 3 ] = 0xff; } R_WriteTGA( fileName, data2, rc->width, rc->height, true ); R_StaticFree( data ); R_StaticFree( data2 ); }
/* ============= RB_ExportCubemaps ============= */ const void *RB_ExportCubemaps(const void *data) { const exportCubemapsCommand_t *cmd = data; // finish any 2D drawing if needed if (tess.numIndexes) RB_EndSurface(); if (!glRefConfig.framebufferObject || !tr.world || tr.numCubemaps == 0) { // do nothing ri.Printf(PRINT_ALL, "Nothing to export!\n"); return (const void *)(cmd + 1); } if (cmd) { FBO_t *oldFbo = glState.currentFBO; int sideSize = r_cubemapSize->integer * r_cubemapSize->integer * 4; byte *cubemapPixels = ri.Malloc(sideSize * 6); int i, j; FBO_Bind(tr.renderCubeFbo); for (i = 0; i < tr.numCubemaps; i++) { char filename[MAX_QPATH]; cubemap_t *cubemap = &tr.cubemaps[i]; byte *p = cubemapPixels; for (j = 0; j < 6; j++) { FBO_AttachImage(tr.renderCubeFbo, cubemap->image, GL_COLOR_ATTACHMENT0_EXT, j); qglReadPixels(0, 0, r_cubemapSize->integer, r_cubemapSize->integer, GL_RGBA, GL_UNSIGNED_BYTE, p); p += sideSize; } if (cubemap->name[0]) { COM_StripExtension(cubemap->name, filename, MAX_QPATH); Q_strcat(filename, MAX_QPATH, ".dds"); } else { Com_sprintf(filename, MAX_QPATH, "cubemaps/%s/%03d.dds", tr.world->baseName, i); } R_SaveDDS(filename, cubemapPixels, r_cubemapSize->integer, r_cubemapSize->integer, 6); ri.Printf(PRINT_ALL, "Saved cubemap %d as %s\n", i, filename); } FBO_Bind(oldFbo); ri.Free(cubemapPixels); } return (const void *)(cmd + 1); }
int R_MME_MultiPassNext (qboolean useMain) { mmeBlurControl_t *control; shotData_t *shotData; passData_t *passData; byte* outAlloc; __m64 *outAlign; //int i; if (useMain) { control = &passDataMain.control; shotData = &shotDataMain; passData = &passDataMain; } else { control = &passDataLeft.control; shotData = &shotDataLeft; passData = &passDataLeft; } if ( !control->totalFrames ) return 0; if (shotData->allocFailed) { return 0; } outAlloc = ri.Hunk_AllocateTempMemory( shotData->pixelCount * 3 + 16); outAlign = (__m64 *)((((int)(outAlloc)) + 15) & ~15); // don't call GLimp_EndFrame() //qglReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_BGR, GL_UNSIGNED_BYTE, outAlign); qglReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, outAlign); // testing #if 0 for (i = 0; i < glConfig.vidWidth * glConfig.vidHeight * 3; i++) { ((byte *)outAlign)[i] += i; } #endif R_MME_BlurAccumAdd( &passData->dof, outAlign ); ri.Hunk_FreeTempMemory( outAlloc ); if ( ++(control->totalIndex) < control->totalFrames ) { int nextIndex = control->totalIndex; if ( ++(nextIndex) >= control->totalFrames ) { // pass } return 1; } control->totalIndex = 0; R_MME_BlurAccumShift( &passData->dof ); return 0; }
/* ==================== R_LevelShot levelshots are specialized 128*128 thumbnails for the menu system, sampled down from full screen distorted images ==================== */ void R_LevelShot( void ) { char checkname[MAX_OSPATH]; byte *buffer; byte *source; byte *src, *dst; int x, y; int r, g, b; float xScale, yScale; int xx, yy; sprintf( checkname, "levelshots/%s.tga", tr.world->baseName ); source = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight * 3 ); buffer = ri.Hunk_AllocateTempMemory( 128 * 128*3 + 18); Com_Memset (buffer, 0, 18); buffer[2] = 2; // uncompressed type buffer[12] = 128; buffer[14] = 128; buffer[16] = 24; // pixel size qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); // resample from source xScale = glConfig.vidWidth / 512.0f; yScale = glConfig.vidHeight / 384.0f; for ( y = 0 ; y < 128 ; y++ ) { for ( x = 0 ; x < 128 ; x++ ) { r = g = b = 0; for ( yy = 0 ; yy < 3 ; yy++ ) { for ( xx = 0 ; xx < 4 ; xx++ ) { src = source + 3 * ( glConfig.vidWidth * (int)( (y*3+yy)*yScale ) + (int)( (x*4+xx)*xScale ) ); r += src[0]; g += src[1]; b += src[2]; } } dst = buffer + 18 + 3 * ( y * 128 + x ); dst[0] = b / 12; dst[1] = g / 12; dst[2] = r / 12; } } // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer + 18, 128 * 128 * 3 ); } ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 ); ri.Hunk_FreeTempMemory( buffer ); ri.Hunk_FreeTempMemory( source ); ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); }
void R_MME_GetMultiShot (byte * target, int width, int height, int glMode) { if ( !passDataMain.control.totalFrames ) { //R_MME_GetShot( target, shotData.main.type ); qglReadPixels(0, 0, width, height, glMode, GL_UNSIGNED_BYTE, target); } else { //Com_Memcpy( target, passDataMain.dof.accum, mainData.pixelCount * 3 ); Com_Memcpy( target, passDataMain.dof.accum, shotDataMain.pixelCount * 3 ); } }
/* ================== RB_TestFlare ================== */ static void RB_TestFlare( flare_t *f ) { float depth; qboolean visible; float fade; float screenZ; if (f->fadeTime == -666) { RB_TestFlareFast(f); return; } backEnd.pc.c_flareTests++; // doing a readpixels is as good as doing a glFinish(), so // don't bother with another sync glState.finishCalled = qfalse; // read back the z buffer contents qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); screenZ = backEnd.viewParms.projectionMatrix[14] / ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); visible = ( -f->eyeZ - -screenZ ) < 24; if ( visible ) { if ( !f->visible ) { f->visible = qtrue; f->fadeTime = backEnd.refdef.time - 1; } { fade = ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; } } else { if ( f->visible ) { f->visible = qfalse; f->fadeTime = backEnd.refdef.time - 1; } fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; } if ( fade < 0 ) { fade = 0; } if ( fade > 1 ) { fade = 1; } f->drawIntensity = fade; }
/** * @brief RB_SwapBuffers * @param[in] data * @return */ const void *RB_SwapBuffers(const void *data) { const swapBuffersCommand_t *cmd; // finish any 2D drawing if needed if (tess.numIndexes) { RB_EndSurface(); } // texture swapping test if (r_showImages->integer) { RB_ShowImages(); } RB_GammaScreen(); cmd = ( const swapBuffersCommand_t * ) data; // we measure overdraw by reading back the stencil buffer and // counting up the number of increments that have happened if (r_measureOverdraw->integer) { int i; long sum = 0; unsigned char *stencilReadback; stencilReadback = ri.Hunk_AllocateTempMemory(glConfig.vidWidth * glConfig.vidHeight); qglReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback); for (i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++) { sum += stencilReadback[i]; } backEnd.pc.c_overDraw += sum; ri.Hunk_FreeTempMemory(stencilReadback); } if (!glState.finishCalled) { qglFinish(); } Ren_LogComment("***************** RB_SwapBuffers *****************\n\n\n"); ri.GLimp_SwapFrame(); backEnd.projection2D = qfalse; return ( const void * ) (cmd + 1); }
/* =================== RB_ShowIntensity Debugging tool to see how much dynamic range a scene is using. The greatest of the rgb values at each pixel will be used, with the resulting color shading from red at 0 to green at 128 to blue at 255 =================== */ static void RB_ShowIntensity() { byte *colorReadback; int i, j, c; if ( !r_showIntensity.GetBool() ) { return; } colorReadback = (byte *)R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight() * 4, TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA, GL_UNSIGNED_BYTE, colorReadback ); c = renderSystem->GetWidth() * renderSystem->GetHeight() * 4; for ( i = 0; i < c; i+=4 ) { j = colorReadback[i]; if ( colorReadback[i+1] > j ) { j = colorReadback[i+1]; } if ( colorReadback[i+2] > j ) { j = colorReadback[i+2]; } if ( j < 128 ) { colorReadback[i+0] = 2*(128-j); colorReadback[i+1] = 2*j; colorReadback[i+2] = 0; } else { colorReadback[i+0] = 0; colorReadback[i+1] = 2*(255-j); colorReadback[i+2] = 2*(j-128); } } // draw it back to the screen qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); GL_State( GLS_DEPTHFUNC_ALWAYS ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0, 0 ); qglPopMatrix(); GL_Color( 1, 1, 1 ); globalImages->BindNull(); qglMatrixMode( GL_MODELVIEW ); qglDrawPixels( renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA , GL_UNSIGNED_BYTE, colorReadback ); R_StaticFree( colorReadback ); }
/* ================== R_TakeScreenshot ================== */ void R_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) { byte *buffer; buffer = (unsigned char *)Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); } FS_WriteFile( fileName, buffer, 1 ); // create path SaveJPG( fileName, 95, glConfig.vidWidth, glConfig.vidHeight, buffer); Hunk_FreeTempMemory( buffer ); }
/* ================== RB_TakeScreenshotJPEG ================== */ void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) { byte *buffer; buffer = (byte *)ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); // ***GREGS_VC9_PORT_MOD*** -- needed typecast qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); // gamma correct if ( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); } ri.FS_WriteFile( fileName, buffer, 1 ); // create path SaveJPG( fileName, 95, glConfig.vidWidth, glConfig.vidHeight, buffer); ri.Hunk_FreeTempMemory( buffer ); }
// levelshots are specialized 128*128 thumbnails for the menu system, sampled // down from full screen distorted images static void R_LevelShot() { char checkname[ MAX_OSPATH ]; String::Sprintf( checkname, MAX_OSPATH, "levelshots/%s.tga", tr.world->baseName ); byte* source = new byte[ glConfig.vidWidth * glConfig.vidHeight * 3 ]; byte* buffer = new byte[ 128 * 128 * 3 ]; qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_RGB, GL_UNSIGNED_BYTE, source ); // resample from source float xScale = glConfig.vidWidth / 512.0f; float yScale = glConfig.vidHeight / 384.0f; for ( int y = 0; y < 128; y++ ) { for ( int x = 0; x < 128; x++ ) { int r = 0; int g = 0; int b = 0; for ( int yy = 0; yy < 3; yy++ ) { for ( int xx = 0; xx < 4; xx++ ) { byte* src = source + 3 * ( glConfig.vidWidth * ( int )( ( y * 3 + yy ) * yScale ) + ( int )( ( x * 4 + xx ) * xScale ) ); r += src[ 0 ]; g += src[ 1 ]; b += src[ 2 ]; } } byte* dst = buffer + 3 * ( y * 128 + x ); dst[ 0 ] = r / 12; dst[ 1 ] = g / 12; dst[ 2 ] = b / 12; } } // gamma correct if ( tr.overbrightBits > 0 && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, 128 * 128 * 3 ); } R_SaveTGA( checkname, buffer, 128, 128, false ); delete[] buffer; delete[] source; common->Printf( "Wrote %s\n", checkname ); }
/* ================== RB_TakeScreenshotJPEG ================== */ void RB_TakeScreenshotJPEG( int x, int y, int width, int height, char *fileName ) { byte *buffer; int quality = r_jpegQuality -> value; buffer = ri.Hunk_AllocateTempMemory(glConfig.vidWidth*glConfig.vidHeight*4); qglReadPixels( x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer ); // gamma correct if ( glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, glConfig.vidWidth * glConfig.vidHeight * 4 ); } ri.FS_WriteFile( fileName, buffer, 1 ); // create path SaveJPG( fileName, quality, glConfig.vidWidth, glConfig.vidHeight, buffer); ri.Hunk_FreeTempMemory( buffer ); }
/* =============== idRenderSystemLocal::ProjectMouseToWorldCoord http://nehe.gamedev.net/article/using_gluunproject/16013/ =============== */ void idRenderSystemLocal::ProjectMouseToWorldCoord( idVec2 mouseXY, idVec3 &worldPos ) { GLint viewport[4]; // Where The Viewport Values Will Be Stored GLfloat modelview[16]; // Where The 16 Doubles Of The Modelview Matrix Are To Be Stored GLfloat projection[16]; GLfloat winX, winY, winZ; qglGetIntegerv(GL_VIEWPORT, viewport); // Retrieves The Viewport Values (X, Y, Width, height). qglGetFloatv(GL_MODELVIEW_MATRIX, modelview); // Retrieve The Modelview Matrix qglGetFloatv(GL_PROJECTION_MATRIX, projection); // Retrieve The Modelview Matrix winX = (float)mouseXY.x; winY = (float)viewport[3] - (float)mouseXY.y; qglReadPixels( mouseXY.x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ ); myUnProjectf( winX, winY, winZ, modelview, projection, viewport, &worldPos.x); }
static void RB_TestFlare( flare_t* f ) { backEnd.pc.c_flareTests++; // Original Quake 3 code. #if 0 // doing a readpixels is as good as doing a glFinish(), so // don't bother with another sync glState.finishCalled = false; // read back the z buffer contents float depth; qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); float screenZ = backEnd.viewParms.projectionMatrix[ 14 ] / ( ( 2 * depth - 1 ) * backEnd.viewParms.projectionMatrix[ 11 ] - backEnd.viewParms.projectionMatrix[ 10 ] ); bool visible = ( -f->eyeZ - -screenZ ) < 24; #endif bool visible = f->flags & 1; float fade; if ( visible ) { if ( !f->visible ) { f->visible = true; f->fadeTime = backEnd.refdef.time - 1; } fade = ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; } else { if ( f->visible ) { f->visible = false; f->fadeTime = backEnd.refdef.time - 1; } fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; } if ( fade < 0 ) { fade = 0; } if ( fade > 1 ) { fade = 1; } f->drawIntensity = fade; }
/* =================== RB_CountStencilBuffer Print an overdraw count based on stencil index values =================== */ static void RB_CountStencilBuffer() { int count; int i; byte *stencilReadback; stencilReadback = (byte *)R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight(), TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); count = 0; for ( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { count += stencilReadback[i]; } R_StaticFree( stencilReadback ); // print some stats (not supposed to do from back end in SMP...) common->Printf( "overdraw: %5.1f\n", (float)count/(renderSystem->GetWidth() * renderSystem->GetHeight()) ); }
static void RB_TakeScreenshot( int x, int y, int width, int height, const char* fileName, bool IsJpeg ) { byte* buffer = new byte[ width * height * 3 ]; qglReadPixels( x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, buffer ); // gamma correct if ( tr.overbrightBits > 0 && glConfig.deviceSupportsGamma ) { R_GammaCorrect( buffer, width * height * 3 ); } if ( IsJpeg ) { FS_WriteFile( fileName, buffer, 1 ); // create path R_SaveJPG( fileName, 95, width, height, buffer ); } else { R_SaveTGA( fileName, buffer, width, height, false ); } delete[] buffer; }
byte* RB_ReadPixels(int x, int y, int width, int height, size_t* offset, int* padlen) { byte* buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width * 3; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); bufstart = PADP((intptr_t) buffer + *offset, packAlign); qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); *offset = bufstart - buffer; *padlen = padwidth - linelen; return buffer; }
byte *RB_ReadZBuffer(int x, int y, int width, int height, int *padlen) { byte *buffer, *bufstart; int padwidth, linelen; GLint packAlign; qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); linelen = width; padwidth = PAD(linelen, packAlign); // Allocate a few more bytes so that we can choose an alignment we like buffer = ri.Hunk_AllocateTempMemory(padwidth * height + packAlign - 1); bufstart = PADP(( intptr_t ) buffer, packAlign); qglDepthRange(0.0f, 1.0f); qglReadPixels(x, y, width, height, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, bufstart); *padlen = padwidth - linelen; return buffer; }
/* =================== RB_ShowDepthBuffer Draw the depth buffer as colors =================== */ static void RB_ShowDepthBuffer() { void *depthReadback; if ( !r_showDepth.GetBool() ) { return; } qglPushMatrix(); qglLoadIdentity(); qglMatrixMode( GL_PROJECTION ); qglPushMatrix(); qglLoadIdentity(); qglOrtho( 0, 1, 0, 1, -1, 1 ); qglRasterPos2f( 0, 0 ); qglPopMatrix(); qglMatrixMode( GL_MODELVIEW ); qglPopMatrix(); GL_State( GLS_DEPTHFUNC_ALWAYS ); GL_Color( 1, 1, 1 ); globalImages->BindNull(); depthReadback = R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight()*4, TAG_RENDER_TOOLS ); memset( depthReadback, 0, renderSystem->GetWidth() * renderSystem->GetHeight()*4 ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_DEPTH_COMPONENT , GL_FLOAT, depthReadback ); #if 0 for ( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { ((byte *)depthReadback)[i*4] = ((byte *)depthReadback)[i*4+1] = ((byte *)depthReadback)[i*4+2] = 255 * ((float *)depthReadback)[i]; ((byte *)depthReadback)[i*4+3] = 1; } #endif qglDrawPixels( renderSystem->GetWidth(), renderSystem->GetHeight(), GL_RGBA , GL_UNSIGNED_BYTE, depthReadback ); R_StaticFree( depthReadback ); }
/* ============== idRenderSystemLocal::CaptureRenderToFile ============== */ void idRenderSystemLocal::CaptureRenderToFile( const char *fileName, bool fixAlpha ) { if ( !R_IsInitialized() ) { return; } idScreenRect & rc = renderCrops[currentRenderCrop]; guiModel->EmitFullScreen(); guiModel->Clear(); RenderCommandBuffers( frameData->cmdHead ); if ( !vr->useFBO ) // koz { glReadBuffer( GL_BACK ); } // include extra space for OpenGL padding to word boundaries int c = ( rc.GetWidth() + 3 ) * rc.GetHeight(); byte *data = (byte *)R_StaticAlloc( c * 3 ); qglReadPixels( rc.x1, rc.y1, rc.GetWidth(), rc.GetHeight(), GL_RGB, GL_UNSIGNED_BYTE, data ); byte *data2 = (byte *)R_StaticAlloc( c * 4 ); for ( int i = 0 ; i < c ; i++ ) { data2[ i * 4 ] = data[ i * 3 ]; data2[ i * 4 + 1 ] = data[ i * 3 + 1 ]; data2[ i * 4 + 2 ] = data[ i * 3 + 2 ]; data2[ i * 4 + 3 ] = 0xff; } R_WriteTGA( fileName, data2, rc.GetWidth(), rc.GetHeight(), true ); R_StaticFree( data ); R_StaticFree( data2 ); }
/* ================== RB_TakeVideoFrameCmd ================== */ const void *RB_TakeVideoFrameCmd( const void *data ) { const videoFrameCommand_t *cmd; int frameSize; int i; cmd = (const videoFrameCommand_t *)data; qglReadPixels( 0, 0, cmd->width, cmd->height, GL_RGBA, GL_UNSIGNED_BYTE, cmd->captureBuffer ); // gamma correct if( ( tr.overbrightBits > 0 ) && glConfig.deviceSupportsGamma ) R_GammaCorrect( cmd->captureBuffer, cmd->width * cmd->height * 4 ); if( cmd->motionJpeg ) { frameSize = SaveJPGToBuffer( cmd->encodeBuffer, 95, cmd->width, cmd->height, cmd->captureBuffer ); } else { frameSize = cmd->width * cmd->height * 4; // Vertically flip the image for( i = 0; i < cmd->height; i++ ) { Com_Memcpy( &cmd->encodeBuffer[ i * ( cmd->width * 4 ) ], &cmd->captureBuffer[ ( cmd->height - i - 1 ) * ( cmd->width * 4 ) ], cmd->width * 4 ); } } ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize ); return (const void *)(cmd + 1); }
/* ================== RB_TakeVideoFrameCmd ================== */ const void *RB_TakeVideoFrameCmd( const void *data ) { const videoFrameCommand_t *cmd; int frameSize; int i; cmd = (const videoFrameCommand_t *)data; qglReadPixels( 0, 0, cmd->width, cmd->height, GL_RGBA, GL_UNSIGNED_BYTE, cmd->captureBuffer ); // gamma correct if( glConfig.deviceSupportsGamma ) R_GammaCorrect( cmd->captureBuffer, cmd->width * cmd->height * 4 ); if( cmd->motionJpeg ) { frameSize = SaveJPGToBuffer( cmd->encodeBuffer, 90, cmd->width, cmd->height, cmd->captureBuffer ); ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize ); } else { frameSize = cmd->width * cmd->height; for( i = 0; i < frameSize; i++) // Pack to 24bpp and swap R and B { cmd->encodeBuffer[ i*3 ] = cmd->captureBuffer[ i*4 + 2 ]; cmd->encodeBuffer[ i*3 + 1 ] = cmd->captureBuffer[ i*4 + 1 ]; cmd->encodeBuffer[ i*3 + 2 ] = cmd->captureBuffer[ i*4 ]; } ri.CL_WriteAVIVideoFrame( cmd->encodeBuffer, frameSize * 3 ); } return (const void *)(cmd + 1); }
/* =================== RB_ScanStencilBuffer Debugging tool to see what values are in the stencil buffer =================== */ void RB_ScanStencilBuffer() { int counts[256]; int i; byte *stencilReadback; memset( counts, 0, sizeof( counts ) ); stencilReadback = (byte *)R_StaticAlloc( renderSystem->GetWidth() * renderSystem->GetHeight(), TAG_RENDER_TOOLS ); qglReadPixels( 0, 0, renderSystem->GetWidth(), renderSystem->GetHeight(), GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); for ( i = 0; i < renderSystem->GetWidth() * renderSystem->GetHeight(); i++ ) { counts[ stencilReadback[i] ]++; } R_StaticFree( stencilReadback ); // print some stats (not supposed to do from back end in SMP...) common->Printf( "stencil values:\n" ); for ( i = 0; i < 255; i++ ) { if ( counts[i] ) { common->Printf( "%i: %i\n", i, counts[i] ); } } }