//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Header: $ // $NoKeywords: $ //=============================================================================// #ifndef FLOAT_BM_H #define FLOAT_BM_H #ifdef _WIN32 #pragma once #endif #include #include #include "tier0/dbg.h" struct PixRGBAF { float Red; float Green; float Blue; float Alpha; }; struct PixRGBA8 { unsigned char Red; unsigned char Green; unsigned char Blue; unsigned char Alpha; }; inline PixRGBAF PixRGBA8_to_F(PixRGBA8 const &x) { PixRGBAF f; f.Red = x.Red / float(255.0f); f.Green = x.Green / float(255.0f); f.Blue = x.Blue / float(255.0f); f.Alpha = x.Alpha / float(255.0f); return f; } inline PixRGBA8 PixRGBAF_to_8(PixRGBAF const &f) { PixRGBA8 x; x.Red = max(0, min(255.0, 255.0 * f.Red)); x.Green = max(0, min(255.0, 255.0 * f.Green)); x.Blue = max(0, min(255.0, 255.0 * f.Blue)); x.Alpha = max(0, min(255.0, 255.0 * f.Alpha)); return x; } #define SPFLAGS_MAXGRADIENT 1 // bit flag options for ComputeSelfShadowedBumpmapFromHeightInAlphaChannel: #define SSBUMP_OPTION_NONDIRECTIONAL 1 // generate ambient occlusion only #define SSBUMP_MOD2X_DETAIL_TEXTURE 2 // scale so that a flat unshadowed // value is 0.5, and bake rgb luminance // in. class FloatBitMap_t { public: int Width, Height; // bitmap dimensions float *RGBAData; // actual data FloatBitMap_t(void) // empty one { Width = Height = 0; RGBAData = 0; } FloatBitMap_t(int width, int height); // make one and allocate space FloatBitMap_t(char const *filename); // read one from a file (tga or pfm) FloatBitMap_t(FloatBitMap_t const *orig); // quantize one to 8 bits bool WriteTGAFile(char const *filename) const; bool LoadFromPFM( char const *filename); // load from floating point pixmap (.pfm) file bool WritePFM( char const *filename); // save to floating point pixmap (.pfm) file void InitializeWithRandomPixelsFromAnotherFloatBM( FloatBitMap_t const &other); inline float &Pixel(int x, int y, int comp) const { Assert((x >= 0) && (x < Width)); Assert((y >= 0) && (y < Height)); return RGBAData[4 * (x + Width * y) + comp]; } inline float &PixelWrapped(int x, int y, int comp) const { // like Pixel except wraps around to other side if (x < 0) x += Width; else if (x >= Width) x -= Width; if (y < 0) y += Height; else if (y >= Height) y -= Height; return RGBAData[4 * (x + Width * y) + comp]; } inline float &PixelClamped(int x, int y, int comp) const { // like Pixel except wraps around to other side x = clamp(x, 0, Width - 1); y = clamp(y, 0, Height - 1); return RGBAData[4 * (x + Width * y) + comp]; } inline float &Alpha(int x, int y) const { Assert((x >= 0) && (x < Width)); Assert((y >= 0) && (y < Height)); return RGBAData[3 + 4 * (x + Width * y)]; } // look up a pixel value with bilinear interpolation float InterpolatedPixel(float x, float y, int comp) const; inline PixRGBAF PixelRGBAF(int x, int y) const { Assert((x >= 0) && (x < Width)); Assert((y >= 0) && (y < Height)); PixRGBAF RetPix; int RGBoffset = 4 * (x + Width * y); RetPix.Red = RGBAData[RGBoffset + 0]; RetPix.Green = RGBAData[RGBoffset + 1]; RetPix.Blue = RGBAData[RGBoffset + 2]; RetPix.Alpha = RGBAData[RGBoffset + 3]; return RetPix; } inline void WritePixelRGBAF(int x, int y, PixRGBAF value) const { Assert((x >= 0) && (x < Width)); Assert((y >= 0) && (y < Height)); int RGBoffset = 4 * (x + Width * y); RGBAData[RGBoffset + 0] = value.Red; RGBAData[RGBoffset + 1] = value.Green; RGBAData[RGBoffset + 2] = value.Blue; RGBAData[RGBoffset + 3] = value.Alpha; } inline void WritePixel(int x, int y, int comp, float value) { Assert((x >= 0) && (x < Width)); Assert((y >= 0) && (y < Height)); RGBAData[4 * (x + Width * y) + comp] = value; } // paste, performing boundary matching. Alpha channel can be used to make // brush shape irregular void SmartPaste(FloatBitMap_t const &brush, int xofs, int yofs, uint32 flags); // force to be tileable using poisson formula void MakeTileable(void); void ReSize(int NewXSize, int NewYSize); // find the bounds of the area that has non-zero alpha. void GetAlphaBounds(int &minx, int &miny, int &maxx, int &maxy); // Solve the poisson equation for an image. The alpha channel of the image // controls which pixels are "modifiable", and can be used to set boundary // conditions. Alpha=0 means the pixel is locked. deltas are in the order // [(x,y)-(x,y-1),(x,y)-(x-1,y),(x,y)-(x+1,y),(x,y)-(x,y+1) void Poisson(FloatBitMap_t *deltas[4], int n_iters, uint32 flags // SPF_xxx ); FloatBitMap_t *QuarterSize(void) const; // get a new one downsampled FloatBitMap_t *QuarterSizeBlocky(void) const; // get a new one downsampled FloatBitMap_t *QuarterSizeWithGaussian( void) const; // downsample 2x using a gaussian void RaiseToPower(float pow); void ScaleGradients(void); void Logize(void); // pix=log(1+pix) void UnLogize(void); // pix=exp(pix)-1 // compress to 8 bits converts the hdr texture to an 8 bit texture, encoding // a scale factor in the alpha channel. upon return, the original pixel can // be (approximately) recovered by the formula rgb*alpha*overbright. this // function performs special numerical optimization on the texture to // minimize the error when using bilinear filtering to read the texture. void CompressTo8Bits(float overbright); // decompress a bitmap converted by CompressTo8Bits void Uncompress(float overbright); Vector AverageColor(void); // average rgb value of all pixels float BrightestColor(void); // highest vector magnitude void Clear( float r, float g, float b, float alpha); // set all pixels to speicifed values (0..1 nominal) void ScaleRGB(float scale_factor); // for all pixels, r,g,b*=scale_factor // given a bitmap with height stored in the alpha channel, generate vector // positions and normals void ComputeVertexPositionsAndNormals(float flHeightScale, Vector **ppPosOut, Vector **ppNormalOut) const; // generate a normal map with height stored in alpha. uses hl2 tangent // basis to support baked self shadowing. the bump scale maps the height of // a pixel relative to the edges of the pixel. This function may take a // while - many millions of rays may be traced. applications using this // method need to link w/ raytrace.lib FloatBitMap_t *ComputeSelfShadowedBumpmapFromHeightInAlphaChannel( float bump_scale, int nrays_to_trace_per_pixel = 100, uint32 nOptionFlags = 0 // SSBUMP_OPTION_XXX ) const; // generate a conventional normal map from a source with height stored in // alpha. FloatBitMap_t *ComputeBumpmapFromHeightInAlphaChannel( float bump_scale) const; // bilateral (edge preserving) smoothing filter. edge_threshold_value // defines the difference in values over which filtering will not occur. // Each channel is filtered independently. large radii will run slow, since // the bilateral filter is neither separable, nor is it a convolution that // can be done via fft. void TileableBilateralFilter(int radius_in_pixels, float edge_threshold_value); ~FloatBitMap_t(); void AllocateRGB(int w, int h) { if (RGBAData) delete[] RGBAData; RGBAData = new float[w * h * 4]; Width = w; Height = h; } }; // a FloatCubeMap_t holds the floating point bitmaps for 6 faces of a cube map class FloatCubeMap_t { public: FloatBitMap_t face_maps[6]; FloatCubeMap_t(int xfsize, int yfsize) { // make an empty one with face dimensions xfsize x yfsize for (int f = 0; f < 6; f++) face_maps[f].AllocateRGB(xfsize, yfsize); } // load basenamebk,pfm, basenamedn.pfm, basenameft.pfm, ... FloatCubeMap_t(char const *basename); // save basenamebk,pfm, basenamedn.pfm, basenameft.pfm, ... void WritePFMs(char const *basename); Vector AverageColor(void) { Vector ret(0, 0, 0); int nfaces = 0; for (int f = 0; f < 6; f++) if (face_maps[f].RGBAData) { nfaces++; ret += face_maps[f].AverageColor(); } if (nfaces) ret *= (1.0 / nfaces); return ret; } float BrightestColor(void) { float ret = 0.0; int nfaces = 0; for (int f = 0; f < 6; f++) if (face_maps[f].RGBAData) { nfaces++; ret = max(ret, face_maps[f].BrightestColor()); } return ret; } // resample a cubemap to one of possibly a lower resolution, using a given // phong exponent. dot-product weighting will be used for the filtering // operation. void Resample(FloatCubeMap_t &dest, float flPhongExponent); // returns the normalized direciton vector through a given pixel of a given // face Vector PixelDirection(int face, int x, int y); // returns the direction vector throught the center of a cubemap face Vector FaceNormal(int nFaceNumber); }; static inline float FLerp(float f1, float f2, float t) { return f1 + (f2 - f1) * t; } // Image Pyramid class. #define MAX_IMAGE_PYRAMID_LEVELS 16 // up to 64kx64k enum ImagePyramidMode_t { PYRAMID_MODE_GAUSSIAN, }; class FloatImagePyramid_t { public: int m_nLevels; FloatBitMap_t *m_pLevels[MAX_IMAGE_PYRAMID_LEVELS]; // level 0 is highest res FloatImagePyramid_t(void) { m_nLevels = 0; memset(m_pLevels, 0, sizeof(m_pLevels)); } // build one. clones data from src for level 0. FloatImagePyramid_t(FloatBitMap_t const &src, ImagePyramidMode_t mode); // read or write a Pixel from a given level. All coordinates are specified // in the same domain as the base level. float &Pixel(int x, int y, int component, int level) const; FloatBitMap_t *Level(int lvl) const { Assert(lvl < m_nLevels); Assert(lvl < ARRAYSIZE(m_pLevels)); return m_pLevels[lvl]; } // rebuild all levels above the specified level void ReconstructLowerResolutionLevels(int starting_level); ~FloatImagePyramid_t(void); void WriteTGAs( char const *basename) const; // outputs name_00.tga, name_01.tga,... }; #endif