PDGL_API void pdglReadPixels( GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels) { if(pglReadPixels) { pglReadPixels(x, y, width, height, format, type, pixels); return; } pglReadPixels=pdglGetProcAddress("glReadPixels"); pglReadPixels(x, y, width, height, format, type, pixels); }
qboolean VID_ScreenShot( const char *filename, int shot_type ) { rgbdata_t *r_shot; uint flags = IMAGE_FLIP_Y; int width = 0, height = 0; qboolean result; r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); r_shot->width = (glState.width + 3) & ~3; r_shot->height = (glState.height + 3) & ~3; r_shot->flags = IMAGE_HAS_COLOR | IMAGE_HAS_ALPHA; r_shot->type = PF_RGBA_32; r_shot->size = r_shot->width * r_shot->height * PFDesc[r_shot->type].bpp; r_shot->palette = NULL; r_shot->buffer = Mem_Alloc( r_temppool, r_shot->size ); // get screen frame pglPixelStorei(GL_PACK_ALIGNMENT, 1); // PANDORA, just in case pglReadPixels( 0, 0, r_shot->width, r_shot->height, GL_RGBA, GL_UNSIGNED_BYTE, r_shot->buffer ); switch( shot_type ) { case VID_SCREENSHOT: if( !gl_overview->integer ) VID_ImageAdjustGamma( r_shot->buffer, r_shot->width, r_shot->height ); // scrshot gamma break; case VID_SNAPSHOT: if( !gl_overview->integer ) VID_ImageAdjustGamma( r_shot->buffer, r_shot->width, r_shot->height ); // scrshot gamma FS_AllowDirectPaths( true ); break; case VID_LEVELSHOT: flags |= IMAGE_RESAMPLE; if( glState.wideScreen ) { height = 480; width = 800; } else { height = 480; width = 640; } break; case VID_MINISHOT: flags |= IMAGE_RESAMPLE; height = 200; width = 320; break; case VID_MAPSHOT: V_WriteOverviewScript(); // store overview script too flags |= IMAGE_RESAMPLE|IMAGE_QUANTIZE; // GoldSrc request overviews in 8-bit format height = 768; width = 1024; break; } Image_Process( &r_shot, width, height, 0.0f, flags, NULL ); // write image result = FS_SaveImage( filename, r_shot ); host.write_to_clipboard = false; // disable write to clipboard FS_AllowDirectPaths( false ); // always reset after store screenshot FS_FreeImage( r_shot ); return result; }
/* ================= VID_CubemapShot ================= */ qboolean VID_CubemapShot( const char *base, uint size, const float *vieworg, qboolean skyshot ) { rgbdata_t *r_shot, *r_side; byte *temp = NULL; byte *buffer = NULL; string basename; int i = 1, flags, result; if( !RI.drawWorld || !cl.worldmodel ) return false; // make sure the specified size is valid while( i < size ) i<<=1; if( i != size ) return false; if( size > glState.width || size > glState.height ) return false; // setup refdef RI.params |= RP_ENVVIEW; // do not render non-bmodel entities // alloc space temp = Mem_Alloc( r_temppool, size * size * 3 ); buffer = Mem_Alloc( r_temppool, size * size * 3 * 6 ); r_shot = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); r_side = Mem_Alloc( r_temppool, sizeof( rgbdata_t )); // use client vieworg if( !vieworg ) vieworg = cl.refdef.vieworg; for( i = 0; i < 6; i++ ) { // go into 3d mode R_Set2DMode( false ); if( skyshot ) { R_DrawCubemapView( vieworg, r_skyBoxInfo[i].angles, size ); flags = r_skyBoxInfo[i].flags; } else { R_DrawCubemapView( vieworg, r_envMapInfo[i].angles, size ); flags = r_envMapInfo[i].flags; } pglReadPixels( 0, 0, size, size, GL_RGB, GL_UNSIGNED_BYTE, temp ); r_side->flags = IMAGE_HAS_COLOR; r_side->width = r_side->height = size; r_side->type = PF_RGB_24; r_side->size = r_side->width * r_side->height * 3; r_side->buffer = temp; if( flags ) Image_Process( &r_side, 0, 0, 0.0f, flags, NULL ); Q_memcpy( buffer + (size * size * 3 * i), r_side->buffer, size * size * 3 ); } RI.params &= ~RP_ENVVIEW; r_shot->flags = IMAGE_HAS_COLOR; r_shot->flags |= (skyshot) ? IMAGE_SKYBOX : IMAGE_CUBEMAP; r_shot->width = size; r_shot->height = size; r_shot->type = PF_RGB_24; r_shot->size = r_shot->width * r_shot->height * 3 * 6; r_shot->palette = NULL; r_shot->buffer = buffer; // make sure what we have right extension Q_strncpy( basename, base, MAX_STRING ); FS_StripExtension( basename ); FS_DefaultExtension( basename, ".tga" ); // write image as 6 sides result = FS_SaveImage( basename, r_shot ); FS_FreeImage( r_shot ); FS_FreeImage( r_side ); return result; }
// read depth buffer and update visibility flag of depth points static void UpdateDepthPointsVisibility( const CDrawPort *pdp, const INDEX iMirrorLevel, DepthInfo *pdi, const INDEX ctCount) { const GfxAPIType eAPI = _pGfx->gl_eCurrentAPI; ASSERT(GfxValidApi(eAPI)); ASSERT( pdp!=NULL && ctCount>0); const CRaster *pra = pdp->dp_Raster; // OpenGL if( eAPI==GAT_OGL) { _sfStats.StartTimer(CStatForm::STI_GFXAPI); FLOAT fPointOoK; // for each stored point for( INDEX idi=0; idi<ctCount; idi++) { DepthInfo &di = pdi[idi]; // skip if not in required mirror level or was already checked in this iteration if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue; const PIX pixJ = pra->ra_Height-1 - di.di_pixJ; // OpenGL has Y-inversed buffer! pglReadPixels( di.di_pixI, pixJ, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &fPointOoK); OGL_CHECKERROR; // it is visible if there is nothing nearer in z-buffer already di.di_bVisible = (di.di_fOoK<fPointOoK); } // done _sfStats.StopTimer(CStatForm::STI_GFXAPI); return; } // Direct3D #ifdef SE1_D3D if( eAPI==GAT_D3D) { _sfStats.StartTimer(CStatForm::STI_GFXAPI); // ok, this will get really complicated ... // We'll have to do it thru back buffer because darn DX8 won't let us have values from z-buffer; // Anyway, we'll lock backbuffer, read color from the lens location and try to write little triangle there // with slightly modified color. Then we'll readout that color and see if triangle passes z-test. Voila! :) // P.S. To avoid lock-modify-lock, we need to batch all the locks in one. Uhhhh ... :( COLOR col; INDEX idi; SLONG slColSize; HRESULT hr; D3DLOCKED_RECT rectLocked; D3DSURFACE_DESC surfDesc; LPDIRECT3DSURFACE8 pBackBuffer; // fetch back buffer (different for full screen and windowed mode) const BOOL bFullScreen = _pGfx->gl_ulFlags & GLF_FULLSCREEN; if( bFullScreen) { hr = _pGfx->gl_pd3dDevice->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer); } else { hr = pra->ra_pvpViewPort->vp_pSwapChain->GetBackBuffer( 0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer); } // what, cannot get a back buffer? if( hr!=D3D_OK) { // to hell with it all _sfStats.StopTimer(CStatForm::STI_GFXAPI); return; } // keep format of back-buffer pBackBuffer->GetDesc(&surfDesc); const D3DFORMAT d3dfBack = surfDesc.Format; // prepare array that'll back-buffer colors from depth point locations _acolDelayed.Push(ctCount); // store all colors for( idi=0; idi<ctCount; idi++) { DepthInfo &di = pdi[idi]; // skip if not in required mirror level or was already checked in this iteration if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest) continue; // fetch pixel _acolDelayed[idi] = 0; const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 }; hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY); if( hr!=D3D_OK) continue; // skip if lock didn't make it // read, convert and store original color _acolDelayed[idi] = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE; pBackBuffer->UnlockRect(); } // prepare to draw little triangles there with slightly adjusted colors _sfStats.StopTimer(CStatForm::STI_GFXAPI); gfxEnableDepthTest(); gfxDisableDepthWrite(); gfxDisableBlend(); gfxDisableAlphaTest(); gfxDisableTexture(); _sfStats.StartTimer(CStatForm::STI_GFXAPI); // prepare array and shader _avtxDelayed.Push(ctCount*3); d3dSetVertexShader(D3DFVF_CTVERTEX); // draw one trianle around each depth point INDEX ctVertex = 0; for( idi=0; idi<ctCount; idi++) { DepthInfo &di = pdi[idi]; col = _acolDelayed[idi]; // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue; const ULONG d3dCol = rgba2argb(col^0x20103000); const PIX pixI = di.di_pixI - pdp->dp_MinI; // convert raster loc to drawport loc const PIX pixJ = di.di_pixJ - pdp->dp_MinJ; // batch it and advance to next triangle CTVERTEX &vtx0 = _avtxDelayed[ctVertex++]; CTVERTEX &vtx1 = _avtxDelayed[ctVertex++]; CTVERTEX &vtx2 = _avtxDelayed[ctVertex++]; vtx0.fX=pixI; vtx0.fY=pixJ-2; vtx0.fZ=di.di_fOoK; vtx0.ulColor=d3dCol; vtx0.fU=vtx0.fV=0; vtx1.fX=pixI-2; vtx1.fY=pixJ+2; vtx1.fZ=di.di_fOoK; vtx1.ulColor=d3dCol; vtx1.fU=vtx0.fV=0; vtx2.fX=pixI+2; vtx2.fY=pixJ; vtx2.fZ=di.di_fOoK; vtx2.ulColor=d3dCol; vtx2.fU=vtx0.fV=0; } // draw a bunch hr = _pGfx->gl_pd3dDevice->DrawPrimitiveUP( D3DPT_TRIANGLELIST, ctVertex/3, &_avtxDelayed[0], sizeof(CTVERTEX)); D3D_CHECKERROR(hr); // readout colors again and compare to old ones for( idi=0; idi<ctCount; idi++) { DepthInfo &di = pdi[idi]; col = _acolDelayed[idi]; // skip if not in required mirror level or was already checked in this iteration, or wasn't fetched at all if( iMirrorLevel!=di.di_iMirrorLevel || _iCheckIteration!=di.di_iSwapLastRequest || col==0) continue; // fetch pixel const RECT rectToLock = { di.di_pixI, di.di_pixJ, di.di_pixI+1, di.di_pixJ+1 }; hr = pBackBuffer->LockRect( &rectLocked, &rectToLock, D3DLOCK_READONLY); if( hr!=D3D_OK) continue; // skip if lock didn't make it // read new color const COLOR colNew = UnpackColor_D3D( (UBYTE*)rectLocked.pBits, d3dfBack, slColSize) | CT_OPAQUE; pBackBuffer->UnlockRect(); // if we managed to write adjusted color, point is visible! di.di_bVisible = (col!=colNew); } // phew, done! :) D3DRELEASE( pBackBuffer, TRUE); _acolDelayed.PopAll(); _avtxDelayed.PopAll(); _sfStats.StopTimer(CStatForm::STI_GFXAPI); return; } #endif // SE1_D3D }