/* =================== 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; }
/* =================== R_ParsePastImageProgram =================== */ const char* R_ParsePastImageProgram( idLexer& src ) { parseBuffer[0] = 0; R_ParseImageProgram_r( src, NULL, NULL, NULL, NULL, NULL ); return parseBuffer; }
/* =================== 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 vertscale[4]; MatchAndAppendToken( src, "(" ); R_ParseImageProgram_r( src, pic, width, height, timestamps, depth ); for( int i = 0; i < 4; i++ ) { MatchAndAppendToken( src, "," ); src.ReadToken( &token ); AppendToken( token ); vertscale[i] = token.GetFloatValue(); } // process it if( pic ) { R_ImageScale( *pic, *width, *height, vertscale ); } 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; }