/* ============== 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 ); }
/* ================ idRenderWorldLocal::FreeWorld ================ */ void idRenderWorldLocal::FreeWorld() { int i; // this will free all the lightDefs and entityDefs FreeDefs(); // free all the portals and check light/model references for ( i = 0 ; i < numPortalAreas ; i++ ) { portalArea_t *area; portal_t *portal, *nextPortal; area = &portalAreas[i]; for ( portal = area->portals ; portal ; portal = nextPortal ) { nextPortal = portal->next; delete portal->w; R_StaticFree( portal ); } // there shouldn't be any remaining lightRefs or entityRefs if ( area->lightRefs.areaNext != &area->lightRefs ) { common->Error( "FreeWorld: unexpected remaining lightRefs" ); } if ( area->entityRefs.areaNext != &area->entityRefs ) { common->Error( "FreeWorld: unexpected remaining entityRefs" ); } } if ( portalAreas ) { R_StaticFree( portalAreas ); portalAreas = NULL; numPortalAreas = 0; R_StaticFree( areaScreenRect ); areaScreenRect = NULL; } if ( doublePortals ) { R_StaticFree( doublePortals ); doublePortals = NULL; numInterAreaPortals = 0; } if ( areaNodes ) { R_StaticFree( areaNodes ); areaNodes = NULL; } // free all the inline idRenderModels for ( i = 0 ; i < localModels.Num() ; i++ ) { renderModelManager->RemoveModel( localModels[i] ); delete localModels[i]; } localModels.Clear(); areaReferenceAllocator.Shutdown(); interactionAllocator.Shutdown(); areaNumRefAllocator.Shutdown(); mapName = "<FREED>"; }
/* ================ R_FreeInteractionCullInfo ================ */ void R_FreeInteractionCullInfo( srfCullInfo_t &cullInfo ) { if ( cullInfo.facing != NULL ) { R_StaticFree( cullInfo.facing ); cullInfo.facing = NULL; } if ( cullInfo.cullBits != NULL ) { if ( cullInfo.cullBits != LIGHT_CULL_ALL_FRONT ) { R_StaticFree( cullInfo.cullBits ); } cullInfo.cullBits = NULL; } }
/* ================= R_HeightmapToNormalMap it is not possible to convert a heightmap into a normal map properly without knowing the texture coordinate stretching. We can assume constant and equal ST vectors for walls, but not for characters. ================= */ static void R_HeightmapToNormalMap( byte *data, int width, int height, float scale ) { int i, j; byte *depth; scale = scale / 256; // copy and convert to grey scale j = width * height; depth = ( byte * ) R_StaticAlloc( j ); for( i = 0; i < j; i++ ) { depth[i] = ( data[i * 4] + data[i * 4 + 1] + data[i * 4 + 2] ) / 3; } idVec3 dir, dir2; for( i = 0; i < height; i++ ) { for( j = 0; j < width; j++ ) { int d1, d2, d3, d4; int a1, a2, a3, a4; // FIXME: look at five points? // look at three points to estimate the gradient a1 = d1 = depth[( i * width + j )]; a2 = d2 = depth[( i * width + ( ( j + 1 ) & ( width - 1 ) ) )]; a3 = d3 = depth[( ( ( i + 1 ) & ( height - 1 ) ) * width + j )]; a4 = d4 = depth[( ( ( i + 1 ) & ( height - 1 ) ) * width + ( ( j + 1 ) & ( width - 1 ) ) )]; d2 -= d1; d3 -= d1; dir[0] = -d2 * scale; dir[1] = -d3 * scale; dir[2] = 1; dir.NormalizeFast(); a1 -= a3; a4 -= a3; dir2[0] = -a4 * scale; dir2[1] = a1 * scale; dir2[2] = 1; dir2.NormalizeFast(); dir += dir2; dir.NormalizeFast(); a1 = ( i * width + j ) * 4; data[a1 + 0] = ( byte )( dir[0] * 127 + 128 ); data[a1 + 1] = ( byte )( dir[1] * 127 + 128 ); data[a1 + 2] = ( byte )( dir[2] * 127 + 128 ); data[a1 + 3] = 255; } } R_StaticFree( depth ); }
/* =================== R_ImageAdd =================== */ static void R_ImageAdd( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) { int i, j; int c; byte *newMap; // resample pic2 to the same size as pic1 if ( width2 != width1 || height2 != height1 ) { newMap = R_Dropsample( data2, width2, height2, width1, height1 ); data2 = newMap; } else { newMap = NULL; } c = width1 * height1 * 4; for ( i = 0 ; i < c ; i++ ) { j = data1[i] + data2[i]; if ( j > 255 ) { j = 255; } data1[i] = j; } if ( newMap ) { R_StaticFree( newMap ); } }
/* ================= idRenderWorldLocal::FreeDefs dump all the interactions ================= */ void idRenderWorldLocal::FreeDefs() { int i; generateAllInteractionsCalled = false; if ( interactionTable ) { R_StaticFree( interactionTable ); interactionTable = NULL; } // free all lightDefs for ( i = 0 ; i < lightDefs.Num() ; i++ ) { idRenderLightLocal *light; light = lightDefs[i]; if ( light && light->world == this ) { FreeLightDef( i ); lightDefs[i] = NULL; } } // free all entityDefs for ( i = 0 ; i < entityDefs.Num() ; i++ ) { idRenderEntityLocal *mod; mod = entityDefs[i]; if ( mod && mod->world == this ) { FreeEntityDef( i ); entityDefs[i] = NULL; } } }
/* ==================== GenerateMegaPreview Make a 2k x 2k preview image for a mega texture that can be used in modeling programs ==================== */ void idMegaTexture::GenerateMegaPreview( const char *fileName ) { idFile *fileHandle = fileSystem->OpenFileRead( fileName ); if ( !fileHandle ) { common->Printf( "idMegaTexture: failed to open %s\n", fileName ); return; } idStr outName = fileName; outName.StripFileExtension(); outName += "_preview.tga"; common->Printf( "Creating %s.\n", outName.c_str() ); megaTextureHeader_t header; fileHandle->Read( &header, sizeof( header ) ); if ( header.tileSize < 64 || header.tilesWide < 1 || header.tilesHigh < 1 ) { common->Printf( "idMegaTexture: bad header on %s\n", fileName ); return; } int tileSize = header.tileSize; int width = header.tilesWide; int height = header.tilesHigh; int tileOffset = 1; int tileBytes = tileSize * tileSize * 4; // find the level that fits while ( width * tileSize > 2048 || height * tileSize > 2048 ) { tileOffset += width * height; width >>= 1; if ( width < 1 ) { width = 1; } height >>= 1; if ( height < 1 ) { height = 1; } } byte *pic = (byte *)R_StaticAlloc( width * height * tileBytes ); byte *oldBlock = (byte *)_alloca( tileBytes ); for ( int y = 0 ; y < height ; y++ ) { for ( int x = 0 ; x < width ; x++ ) { int tileNum = tileOffset + y * width + x; fileHandle->Seek( tileNum * tileBytes, FS_SEEK_SET ); fileHandle->Read( oldBlock, tileBytes ); for ( int yy = 0 ; yy < tileSize ; yy++ ) { memcpy( pic + ( ( y * tileSize + yy ) * width * tileSize + x * tileSize ) * 4, oldBlock + yy * tileSize * 4, tileSize * 4 ); } } } R_WriteTGA( outName.c_str(), pic, width * tileSize, height * tileSize, false ); R_StaticFree( pic ); delete fileHandle; }
/* =============== idInteraction::FreeSurfaces Frees the surfaces, but leaves the interaction linked in, so it will be regenerated automatically =============== */ void idInteraction::FreeSurfaces( void ) { if ( this->surfaces ) { for ( int i = 0 ; i < this->numSurfaces ; i++ ) { surfaceInteraction_t *sint = &this->surfaces[i]; if ( sint->lightTris ) { if ( sint->lightTris != LIGHT_TRIS_DEFERRED ) { R_FreeStaticTriSurf( sint->lightTris ); } sint->lightTris = NULL; } if ( sint->shadowTris ) { // if it doesn't have an entityDef, it is part of a prelight // model, not a generated interaction if ( this->entityDef ) { R_FreeStaticTriSurf( sint->shadowTris ); sint->shadowTris = NULL; } } R_FreeInteractionCullInfo( sint->cullInfo ); } R_StaticFree( this->surfaces ); this->surfaces = NULL; } this->numSurfaces = -1; }
/* =================== R_AddNormalMaps =================== */ static void R_AddNormalMaps( byte *data1, int width1, int height1, byte *data2, int width2, int height2 ) { int i, j; byte *newMap; // resample pic2 to the same size as pic1 if( width2 != width1 || height2 != height1 ) { newMap = R_Dropsample( data2, width2, height2, width1, height1 ); data2 = newMap; } else { newMap = NULL; } // add the normal change from the second and renormalize for( i = 0; i < height1; i++ ) { for( j = 0; j < width1; j++ ) { byte *d1, *d2; idVec3 n; float len; d1 = data1 + ( i * width1 + j ) * 4; d2 = data2 + ( i * width1 + j ) * 4; n[0] = ( d1[0] - 128 ) / 127.0; n[1] = ( d1[1] - 128 ) / 127.0; n[2] = ( d1[2] - 128 ) / 127.0; // There are some normal maps that blend to 0,0,0 at the edges // this screws up compression, so we try to correct that here by instead fading it to 0,0,1 len = n.LengthFast(); if( len < 1.0f ) { n[2] = idMath::Sqrt( 1.0 - ( n[0] * n[0] ) - ( n[1] * n[1] ) ); } n[0] += ( d2[0] - 128 ) / 127.0; n[1] += ( d2[1] - 128 ) / 127.0; n.Normalize(); d1[0] = ( byte )( n[0] * 127 + 128 ); d1[1] = ( byte )( n[1] * 127 + 128 ); d1[2] = ( byte )( n[2] * 127 + 128 ); d1[3] = 255; } } if( newMap ) { R_StaticFree( newMap ); } }
/* ================ R_SmoothNormalMap ================ */ static void R_SmoothNormalMap( byte* data, int width, int height ) { byte* orig; int i, j, k, l; idVec3 normal; byte* out; static float factors[3][3] = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 1, 1 } }; orig = ( byte* )R_StaticAlloc( width * height * 4, TAG_IMAGE ); memcpy( orig, data, width * height * 4 ); for( i = 0 ; i < width ; i++ ) { for( j = 0 ; j < height ; j++ ) { normal = vec3_origin; for( k = -1 ; k < 2 ; k++ ) { for( l = -1 ; l < 2 ; l++ ) { byte* in; in = orig + ( ( ( j + l ) & ( height - 1 ) ) * width + ( ( i + k ) & ( width - 1 ) ) ) * 4; // ignore 000 and -1 -1 -1 if( in[0] == 0 && in[1] == 0 && in[2] == 0 ) { continue; } if( in[0] == 128 && in[1] == 128 && in[2] == 128 ) { continue; } normal[0] += factors[k + 1][l + 1] * ( in[0] - 128 ); normal[1] += factors[k + 1][l + 1] * ( in[1] - 128 ); normal[2] += factors[k + 1][l + 1] * ( in[2] - 128 ); } } normal.Normalize(); out = data + ( j * width + i ) * 4; out[0] = ( byte )( 128 + 127 * normal[0] ); out[1] = ( byte )( 128 + 127 * normal[1] ); out[2] = ( byte )( 128 + 127 * normal[2] ); } } R_StaticFree( orig ); }
/* ============== 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 ); }
/* =============== idInteraction::FreeSurfaces Frees the surfaces, but leaves the interaction linked in, so it will be regenerated automatically =============== */ void idInteraction::FreeSurfaces() { // anything regenerated is no longer an optimized static version this->staticInteraction = false; if ( this->surfaces != NULL ) { for ( int i = 0; i < this->numSurfaces; i++ ) { surfaceInteraction_t &srf = this->surfaces[i]; Mem_Free( srf.shadowIndexes ); srf.shadowIndexes = NULL; } R_StaticFree( this->surfaces ); this->surfaces = NULL; } this->numSurfaces = -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 ); }
/* ================= idRenderWorldLocal::FreeDefs dump all the interactions ================= */ void idRenderWorldLocal::FreeDefs() { generateAllInteractionsCalled = false; if( interactionTable ) { R_StaticFree( interactionTable ); interactionTable = NULL; } // free all lightDefs for( int i = 0; i < lightDefs.Num(); i++ ) { idRenderLightLocal* light = lightDefs[i]; if( light != NULL && light->world == this ) { FreeLightDef( i ); lightDefs[i] = NULL; } } // free all entityDefs for( int i = 0; i < entityDefs.Num(); i++ ) { idRenderEntityLocal* mod = entityDefs[i]; if( mod != NULL && mod->world == this ) { FreeEntityDef( i ); entityDefs[i] = NULL; } } // Reset decals and overlays for( int i = 0; i < decals.Num(); i++ ) { decals[i].entityHandle = -1; decals[i].lastStartTime = 0; } for( int i = 0; i < overlays.Num(); i++ ) { overlays[i].entityHandle = -1; overlays[i].lastStartTime = 0; } }
/* =================== 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()) ); }
/* =================== 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 ); }
/* =================== 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] ); } } }
/* =================== R_ParseImageProgram_r If pic is NULL, the timestamps will be filled in, but no image will be generated If both pic and timestamps are NULL, it will just advance past it, which can be used to parse an image program from a text stream. =================== */ static bool R_ParseImageProgram_r( idLexer &src, byte **pic, int *width, int *height, ID_TIME_T *timestamps, textureDepth_t *depth ) { idToken token; float scale; ID_TIME_T timestamp; src.ReadToken( &token ); AppendToken( token ); if ( !token.Icmp( "heightmap" ) ) { MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale = token.GetFloatValue(); // process it if ( pic ) { R_HeightmapToNormalMap( *pic, *width, *height, scale ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "addnormals" ) ) { byte *pic2; int width2, height2; MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) { if ( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if ( pic ) { R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "smoothnormals" ) ) { MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } if ( pic ) { R_SmoothNormalMap( *pic, *width, *height ); if ( depth ) { *depth = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "add" ) ) { byte *pic2; int width2, height2; MatchAndAppendToken( src, "(" ); if ( !R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ) ) { return false; } MatchAndAppendToken( src, "," ); if ( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, depth ) ) { if ( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if ( pic ) { R_ImageAdd( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "scale" ) ) { float scale[4]; int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); for ( i = 0 ; i < 4 ; i++ ) { MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale[i] = token.GetFloatValue(); } // process it if ( pic ) { R_ImageScale( *pic, *width, *height, scale ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "invertAlpha" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // process it if ( pic ) { R_InvertAlpha( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "invertColor" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // process it if ( pic ) { R_InvertColor( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "makeIntensity" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // copy red to green, blue, and alpha if ( pic ) { int c; c = *width * *height * 4; for ( i = 0 ; i < c ; i+=4 ) { (*pic)[i+1] = (*pic)[i+2] = (*pic)[i+3] = (*pic)[i]; } } MatchAndAppendToken( src, ")" ); return true; } if ( !token.Icmp( "makeAlpha" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); // average RGB into alpha, then set RGB to white if ( pic ) { int c; c = *width * *height * 4; for ( i = 0 ; i < c ; i+=4 ) { (*pic)[i+3] = ( (*pic)[i+0] + (*pic)[i+1] + (*pic)[i+2] ) / 3; (*pic)[i+0] = (*pic)[i+1] = (*pic)[i+2] = 255; } } MatchAndAppendToken( src, ")" ); return true; } // if we are just parsing instead of loading or checking, // don't do the R_LoadImage if ( !timestamps && !pic ) { return true; } // load it as an image R_LoadImage( token.c_str(), pic, width, height, ×tamp, true ); if ( timestamp == -1 ) { return false; } // add this to the timestamp if ( timestamps ) { if ( timestamp > *timestamps ) { *timestamps = timestamp; } } return true; }
/* ============== RenderBump_f ============== */ void RenderBump_f( const idCmdArgs &args ) { idRenderModel *lowPoly; idStr source; int i, j; const char *cmdLine; int numRenderBumps; renderBump_t *renderBumps, *rb = NULL; renderBump_t opt; int startTime, endTime; // update the screen as we print common->SetRefreshOnPrint( true ); // there should be a single parameter, the filename for a game loadable low-poly model if ( args.Argc() != 2 ) { common->Error( "Usage: renderbump <lowPolyModel>" ); } common->Printf( "----- Renderbump %s -----\n", args.Argv( 1 ) ); startTime = Sys_Milliseconds(); // get the lowPoly model source = args.Argv( 1 ); lowPoly = renderModelManager->CheckModel( source ); if ( !lowPoly ) { common->Error( "Can't load model %s", source.c_str() ); } renderBumps = (renderBump_t *)R_StaticAlloc( lowPoly->NumSurfaces() * sizeof( *renderBumps ) ); numRenderBumps = 0; for ( i = 0 ; i < lowPoly->NumSurfaces() ; i++ ) { const modelSurface_t *ms = lowPoly->Surface( i ); // default options memset( &opt, 0, sizeof( opt ) ); opt.width = 512; opt.height = 512; opt.antiAlias = 1; opt.outline = 8; opt.traceFrac = 0.05f; // parse the renderbump parameters for this surface cmdLine = ms->shader->GetRenderBump(); common->Printf( "surface %i, shader %s\nrenderBump = %s ", i, ms->shader->GetName(), cmdLine ); if ( !ms->geometry ) { common->Printf( "(no geometry)\n" ); continue; } idCmdArgs localArgs; localArgs.TokenizeString( cmdLine, false ); if ( localArgs.Argc() < 2 ) { common->Printf( "(no action)\n" ); continue; } common->Printf( "(rendering)\n" ); for ( j = 0 ; j < localArgs.Argc() - 2; j++ ) { const char *s; s = localArgs.Argv( j ); if ( s[0] == '-' ) { j++; s = localArgs.Argv( j ); if ( s[0] == '\0' ) { continue; } } if ( !idStr::Icmp( s, "size" ) ) { if ( j + 2 >= localArgs.Argc() ) { j = localArgs.Argc(); break; } opt.width = atoi( localArgs.Argv( j + 1 ) ); opt.height = atoi( localArgs.Argv( j + 2 ) ); j += 2; } else if ( !idStr::Icmp( s, "trace" ) ) { opt.traceFrac = atof( localArgs.Argv( j + 1 ) ); j += 1; } else if ( !idStr::Icmp( s, "globalMap" ) ) { opt.saveGlobalMap = true; } else if ( !idStr::Icmp( s, "colorMap" ) ) { opt.saveColorMap = true; } else if ( !idStr::Icmp( s, "outline" ) ) { opt.outline = atoi( localArgs.Argv( j + 1 ) ); j += 1; } else if ( !idStr::Icmp( s, "aa" ) ) { opt.antiAlias = atoi( localArgs.Argv( j + 1 ) ); j += 1; } else { common->Printf( "WARNING: Unknown option \"%s\"\n", s ); break; } } if ( j != ( localArgs.Argc() - 2 ) ) { common->Error( "usage: renderBump [-size width height] [-aa <1-2>] [globalMap] [colorMap] [-trace <0.01 - 1.0>] normalMapImageFile highPolyAseFile" ); } idStr::Copynz( opt.outputName, localArgs.Argv( j ), sizeof( opt.outputName ) ); idStr::Copynz( opt.highName, localArgs.Argv( j + 1 ), sizeof( opt.highName ) ); // adjust size for anti-aliasing opt.width <<= opt.antiAlias; opt.height <<= opt.antiAlias; // see if we already have a renderbump going for another surface that this should use for ( j = 0 ; j < numRenderBumps ; j++ ) { rb = &renderBumps[j]; if ( idStr::Icmp( rb->outputName, opt.outputName ) ) { continue; } // all the other parameters must match, or it is an error if ( idStr::Icmp( rb->highName, opt.highName) || rb->width != opt.width || rb->height != opt.height || rb->antiAlias != opt.antiAlias || rb->traceFrac != opt.traceFrac ) { common->Error( "mismatched renderbump parameters on image %s", rb->outputName ); continue; } // saveGlobalMap will be a sticky option rb->saveGlobalMap = rb->saveGlobalMap | opt.saveGlobalMap; break; } // create a new renderbump if needed if ( j == numRenderBumps ) { numRenderBumps++; rb = &renderBumps[j]; *rb = opt; InitRenderBump( rb ); } // render the triangles for this surface RenderBumpTriangles( ms->geometry, rb ); } // // anti-alias and write out all renderbumps that we have completed // for ( i = 0 ; i < numRenderBumps ; i++ ) { WriteRenderBump( &renderBumps[i], opt.outline << opt.antiAlias ); } R_StaticFree( renderBumps ); endTime = Sys_Milliseconds(); common->Printf( "%5.2f seconds for renderBump\n", ( endTime - startTime ) / 1000.0 ); common->Printf( "---------- RenderBump Completed ----------\n" ); // stop updating the screen as we print common->SetRefreshOnPrint( false ); }
NSBitmapImageRep::~NSBitmapImageRep() { R_StaticFree( bmap ); bmap = NULL; }
/* =================== R_ParseImageProgram_r If pic is NULL, the timestamps will be filled in, but no image will be generated If both pic and timestamps are NULL, it will just advance past it, which can be used to parse an image program from a text stream. =================== */ static bool R_ParseImageProgram_r( idLexer& src, byte** pic, int* width, int* height, ID_TIME_T* timestamps, textureUsage_t* usage ) { idToken token; float scale; ID_TIME_T timestamp; src.ReadToken( &token ); // Since all interaction shaders now assume YCoCG diffuse textures. We replace all entries for the intrinsic // _black texture to the black texture on disk. Doing this will cause a YCoCG compliant texture to be generated. // Without a YCoCG compliant black texture we will get color artifacts for any interaction // material that specifies the _black texture. if( token == "_black" ) { token = "textures/black"; } // also check for _white if( token == "_white" ) { token = "guis/assets/white"; } AppendToken( token ); if( !token.Icmp( "heightmap" ) ) { MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale = token.GetFloatValue(); // process it if( pic ) { R_HeightmapToNormalMap( *pic, *width, *height, scale ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "addnormals" ) ) { byte* pic2 = NULL; int width2, height2; MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); if( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) { if( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if( pic ) { R_AddNormalMaps( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "smoothnormals" ) ) { MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } if( pic ) { R_SmoothNormalMap( *pic, *width, *height ); if( usage ) { *usage = TD_BUMP; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "add" ) ) { byte* pic2 = NULL; int width2, height2; MatchAndAppendToken( src, "(" ); if( !R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ) ) { return false; } MatchAndAppendToken( src, "," ); if( !R_ParseImageProgram_r( src, pic ? &pic2 : NULL, &width2, &height2, timestamps, usage ) ) { if( pic ) { R_StaticFree( *pic ); *pic = NULL; } return false; } // process it if( pic ) { R_ImageAdd( *pic, *width, *height, pic2, width2, height2 ); R_StaticFree( pic2 ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "scale" ) ) { float scale[4]; int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); for( i = 0 ; i < 4 ; i++ ) { MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); scale[i] = token.GetFloatValue(); } // process it if( pic ) { R_ImageScale( *pic, *width, *height, scale ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "invertAlpha" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // process it if( pic ) { R_InvertAlpha( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "invertColor" ) ) { MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // process it if( pic ) { R_InvertColor( *pic, *width, *height ); } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "makeIntensity" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // copy red to green, blue, and alpha if( pic ) { int c; c = *width * *height * 4; for( i = 0 ; i < c ; i += 4 ) { ( *pic )[i + 1] = ( *pic )[i + 2] = ( *pic )[i + 3] = ( *pic )[i]; } } MatchAndAppendToken( src, ")" ); return true; } if( !token.Icmp( "makeAlpha" ) ) { int i; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, usage ); // average RGB into alpha, then set RGB to white if( pic ) { int c; c = *width * *height * 4; for( i = 0 ; i < c ; i += 4 ) { ( *pic )[i + 3] = ( ( *pic )[i + 0] + ( *pic )[i + 1] + ( *pic )[i + 2] ) / 3; ( *pic )[i + 0] = ( *pic )[i + 1] = ( *pic )[i + 2] = 255; } } MatchAndAppendToken( src, ")" ); return true; } // if we are just parsing instead of loading or checking, // don't do the R_LoadImage if( !timestamps && !pic ) { return true; } // load it as an image R_LoadImage( token.c_str(), pic, width, height, ×tamp, true ); if( timestamp == -1 ) { return false; } // add this to the timestamp if( timestamps ) { if( timestamp > *timestamps ) { *timestamps = timestamp; } } return true; }