/*********************************************************************************************** * * Copyright © DreamWorks Interactive. 1996 * * Contents: * Classes for fast bumpmapping. * * Notes: * * CBumpAnglePair describes a normal in terms of two polar coordinates. By definition * 'theta' describes the angle of the normal from the y-axis along the x/y plane, and * 'phi' describes the angle of the normal to that plane. * * Phi may be stored in two locations, PhiB and PhiL, in the CBumpAnglePair class; PhiB * represents the phi angle of made by the bumpmap, and PhiL represents the angle made * by the light. Only one representation of theta is required as the theta in the lookup * table represents the difference in the light's theta and the bumpmap's table. * * Bumpmapping requires the dot product between the light normal and the normal to a pixel * on the bumpmap's surface. The fast bumpmap replaces normals described as vectors (i.e., * using 'x,' 'y,' and 'z' components) with normals described as angles. Note that normals * (by definition) have a unit length of one, so angles are just as accurate but require * less data (i.e., 'theta' and 'phi' values. This turns the 3D problem into a 2D problem. * * Theta can be any value between 0 and 2 x Pi. Phi can be any value from -Pi/2 to Pi/2, * however Phi for the bumpmap is clamped to the range 0 to Pi/2 for cache efficiency; * it is felt that most "real-life" bumpmaps won't have surface normals with a negative z * component. * * First a table is set up to return the arccos (i.e., dot product) value represented as * an intensity. The index into the table is given in the format phi light - theta - * phi bump. For more information refer to the 'FastBumpMath' and 'FastBumpTable' modules. * This table need be set up only once during program initialization. * * Secondly bumpmaps are loaded. Bumpmaps consist of combining a heightfield with * curvatures represented by vertex normals, and adding texture. * * Heightfields are converted to normals by selecting two points adjacent to a given pixel * and using these three points to define a plane. The x and y values are the x and y * positions of the pixels on the bitmap, while the z value is value (e.g., grayscale * intensity) of the pixels. The z value is scaled by some value called 'f_bumpiness' in * the function 'AddHeightField.' * * Curvature is then added by using a modified rasterizing routine to interpolate normals * across triangles on the bumpmap, and transforming bumpmap normals using these * interpolated normals. To allow for graceful handling of a situation where overlapping * triangles are trying to add curvature, a flag has been added to ensure curvature can * be applied only once at a given pixel. * * The resulting normal is then stored as two angles, theta and phi, in the bumpmap along * with the colour of the texture. * * Finally, bumpmaps are rendered through a template parameter in the 'LineLinearTex.hpp' * module. To render the bumpmap, the directional lighting normal for the polygon is * calculated and transformed to texture space. The lighting normal is then broken up into * its theta and phi values. The phi of the light is then combined with the address of the * conversion table to generate a pointer into the table. This pointer is a constant value * for the entire polygon. * * The theta of the light is then subtracted from the theta of the bumpmap. The resulting * theta value is combined with the phi of the bumpmap, and used as an index into the * section of the table described by the previously generated pointer. The value returned * from the table is an intensity, which is combined with the pixel's colour (and, * optionally, the fogging value at that point), and used as the clut index. * * Lights other than directional lights can also be implemented by using a secondary table * of only 32 bytes in size. * * To do: * Replace the code to test if curvature should be applied to a triangle in 'ApplyCurves' * with an appropriate function in the mesh. * Work directly with heightfield normals and curvature normals when building the bumpmap, * instead of converting heightfield normals to angles and back to normals when combining * the basic bumpmap with curvature. Alternatively, the angles generated by the * heightfield could be converted quickly to the 'CAngle' format and the rotations applied * directly. * Interpolate to generate values for the edges. * Delete the 'DrawBumpTest' function once the loader integrates bumpmapping. * Make pbumpCreateUniqueBumpap create a single texture with gaps, rather than a new texture * for each triangle. This allows triangles to share a single surface again. * * Optional to do: * * Add filters for preprocessing bumpmaps. * Use curve fitting for height-field to bumpmap conversion. * *********************************************************************************************** * * $Log:: /JP2_PC/Source/Lib/Renderer/Primitives/FastBump.hpp $ * * 46 98.08.26 6:56p Mmouni * Added declaration for FastBumpCleanup(). * * 45 8/25/98 2:34p Rvande * removed redundant class scope * * 44 98.07.08 12:11p Mmouni * Moved CPixelFormatBumpMap here from the .cpp file. * * 43 98.05.21 4:39p Mmouni * Change iCURVE_BIT to 15 from 16 so that it could be represented in a word. * * 42 1/19/98 7:30p Pkeet * Added support for 16 bit bumpmaps by adding lower colour resolution and a smaller size for * 'CBumpAnglePair.' * * 41 97/11/24 16:58 Speter * Reduced bump resolution 1 bit per dimension, for 16-bit bump-maps. fGetPhi[B,L] now access * BumpTable conversion arrays. * * 40 97/09/18 20:54 Speter * Changed behaviour of SetBump(b_light_phi) flag. Separated fGetPhi() into B and L versions. * * 39 97/08/11 10:12p Pkeet * Added the 'dir3MakeNormalFast' member function to CBumpAnglePair. * * 38 97/08/11 17:59 Speter * Changed CBumpAnglePair uint32 conversion operator to simply return value; much more * manageable. * * 37 97/08/08 6:11p Pkeet * Gave the 'SetBump' member function an optional parameter to set the light phi. * * 36 97-05-06 15:53 Speter * Now store bump curves using optimised axis-swap bump transform * * 35 97/03/24 15:23 Speter * Now explicitly differentiate PHI_L from PHI_B in constants and usage (rather than just using * PHI), allowing them to vary arbitrarily. * * 34 97/02/19 10:38 Speter * Changed CBumpMap constructor which takes a solid colour to take a TPixel rather than CColour. * * 33 97/02/07 19:11 Speter * Replaced r3ObjToTexture with faster, better mx3ObjToTexture, propagated change. * * 32 97/02/03 3:50p Pkeet * Added 'ApplyCurves' test to do. * * 31 1/29/97 12:18p Pkeet * Added code for using object space for bumpmap normals. Added code for subdividing triangles * to use the face normals. * * 30 1/28/97 2:29p Pkeet * Reduced theta to 7 bits. * * 29 1/27/97 4:47p Pkeet * Temporarilty increased the theta range to eight bits. * * 28 97/01/10 17:30 Speter * Updated for new raster classes. * * 27 1/08/97 7:43p Pkeet * Removed a semicolon from a parameter in a CBump constructor. * * 26 97/01/07 12:04 Speter * Changed CMesh to use rptr<>. * * 25 96/12/31 17:02 Speter * Updated for rptr. * * 24 96/12/02 18:52 Speter * Added to do. * * 23 96/11/27 19:33 Speter * Fixed bump angle conversion so that phi spans the full range -pi/2 to pi/2. * * 22 96/11/20 11:53 Speter * Added constructor for CBumpAnglePair from CDir3<>. * * 21 96/11/11 18:51 Speter * Replaced misleading operator =(CDir3<>) with SetBump(CDir3<>) function. Added u4GetBump(). * Fixed SetColour() to clear the colour field before oring it in. * Changed iCURVE_MASK to iMASK_CURVE to match other mask constants. * * 20 11/07/96 3:28p Pkeet * Added the 'r3ObjToTexture' function. Changed the default value for bumpiness. * * 19 11/05/96 7:04p Pkeet * Added a constructor that uses the width and height. * * 18 10/14/96 10:05a Pkeet * Added the 'uint32' conversion operator. * * 17 96/10/04 18:02 Speter * Added necessary include of Texture.hpp. * * 16 96/09/27 11:31 Speter * Oops. Changed in geometry types to <>. * * 15 9/24/96 2:57p Pkeet * Added comments to meet coding standards. * * 14 9/18/96 5:11p Pkeet * Implemented the 'DrawBump' procedure. * * 13 96/09/16 11:41 Speter * Simplified CBumpMap class to be just a raster. * * 12 96/09/13 14:28 Speter * Removed some unneeded includes and extern statements. * Moved some inline code into .cpp file, so that FastBumpMath.hpp no longer needed. * * * 11 9/12/96 4:20p Pkeet * Added 'uint8* au1_table' parameter to 'pu1GetSubBumpTable' member function. * * 10 9/12/96 3:16p Pkeet * Reduced size of bumpmap to intensity table by half because bumpmap phi values can be positive * only. * * 9 9/11/96 3:02p Pkeet * Moved bumapping table and generation function to 'FastBumpTable.hpp' module. * * 8 9/11/96 11:37a Pkeet * Changed 'SetPhi' and 'GetPhi' member functions to use bitfields instead of shift-masks. This * is a temporary measure to get the code to work because previous code did not maintain the * sign bit. * * 7 9/06/96 6:01p Pkeet * Moved lookup functions to external assembly routines. * * 6 9/05/96 6:34p Pkeet * Changed bumpmap to use the 'CRasterSimple' class. Added lighting parameters to the * 'MakeBumpmap' Table function. * * 5 9/05/96 2:28p Pkeet * Added texturing to bumpmapping. * * 4 9/04/96 5:16p Pkeet * Added file-based bumpmaps. * * 3 8/30/96 2:57p Pkeet * Moved trigonometric functions to "FastBumpMath" module. * * 2 8/30/96 1:02p Pkeet * Added fast arcsin conversion for phi. * * 1 8/29/96 5:53p Pkeet * Initial implementation. * **********************************************************************************************/ #ifndef HEADER_LIB_RENDERER_PRIMITIVES_FASTBUMP_HPP #define HEADER_LIB_RENDERER_PRIMITIVES_FASTBUMP_HPP // // Includes. // #include "Lib/View/Raster.hpp" #include "Lib/Renderer/Texture.hpp" #include "Lib/Transform/Transform.hpp" #include "Lib/GeomDBase/Mesh.hpp" // // Defines. // // Bumpmap table sizes. #define iTHETA_BITS (6) #define iPHI_L_BITS (6) #define iPHI_B_BITS (4) // Minimum and maximum angle values. #define iMIN_THETA (0) #define iMAX_THETA ((1 << iTHETA_BITS) - 1) #define iMAX_PHI_L ((1 << (iPHI_L_BITS - 1)) - 1) #define iMIN_PHI_L (-iMAX_PHI_L) #define iMAX_PHI_B ((1 << iPHI_B_BITS) - 1) #define iMIN_PHI_B (0) // Shift values. #define iSHIFT_PHI_B (0) #define iSHIFT_THETA (iSHIFT_PHI_B + iPHI_B_BITS) #define iSHIFT_PHI_L (iSHIFT_THETA + iTHETA_BITS) // Sign bit for PhiL. #define iSIGN_BIT_PHI_L (1 << (iSHIFT_PHI_L + iPHI_L_BITS - 1)) // Masks. #define iMASK_THETA (((1 << iTHETA_BITS) - 1) << iSHIFT_THETA) #define iMASK_PHI_B (((1 << iPHI_B_BITS) - 1) << iSHIFT_PHI_B) #define iMASK_PHI_L (((1 << iPHI_L_BITS) - 1) << iSHIFT_PHI_L) #define iMASK_ANGLETABLE_LOOKUP ((1 << (iTHETA_BITS + iPHI_B_BITS)) - 1) // Bumpmap to intensity lookup table size. #define iBUMP_TABLE_SIZE (1 << (iTHETA_BITS + iPHI_L_BITS + iPHI_B_BITS)) // Default bumpiness value for heightfield to bumpmap conversions. #define fDEFAULT_BUMPINESS (0.025f) // Subdivides the bumps with the face normal if true. #define bSUBDIVIDE_BUMPS (0) #if iBUMPMAP_RESOLUTION == 16 // NOTE: iCURVE_BIT is shared with the color. #define iCURVE_BIT (15) #define iMASK_COLOUR (0xFC00) #define iBUMPMAP_COLOURBITS (6) #define iSHIFT_COLOUR (iTHETA_BITS + iPHI_B_BITS) typedef uint16 TBumpRes; #else #define iCURVE_BIT (22) #define iMASK_COLOUR 0xFF000000 #define iBUMPMAP_COLOURBITS (8) #define iSHIFT_COLOUR ((sizeof(int32) - sizeof(uint8)) * 8) typedef uint32 TBumpRes; #endif // iBUMPMAP_RESOLUTION // Curvature added flag. #define iMASK_CURVE (1 << iCURVE_BIT) #define iMASK_BUMP (iMASK_CURVE - 1) // // Class definitions. // //********************************************************************************************* // class CBumpAnglePair // // Class describes a bumpmap pixel containing an indexed colour value and polar coordinates. // // Prefix: bang // //************************************** { public: TBumpRes br; // Colour and angle information. public: //***************************************************************************************** // // Constructors. // //***************************************************************************************** // CBumpAnglePair ( ) // // Default constructor. // //************************************** { br = 0; } //***************************************************************************************** // CBumpAnglePair ( uint u_value ) // // Default constructor with value. // //************************************** { br = u_value; } //***************************************************************************************** // CBumpAnglePair ( int i_phi, uint u_theta ) // // Construct from an angle pair. // //************************************** { br = 0; SetTheta(u_theta); SetPhiB(i_phi); } //***************************************************************************************** // CBumpAnglePair ( const CDir3<>& rdir3 ) // // Construct from a normal. // //********************************** { br = 0; SetBump(rdir3); } //***************************************************************************************** // // Member functions to set or get states. // //***************************************************************************************** // operator TBumpRes const ( ) // // Returns the value. // //************************************** { return br; } //***************************************************************************************** // void SetTheta ( uint u_theta ) // // Sets the theta value. // // Notes: // Error checking is not required for theta's range because it will automatically // wrap around. // //************************************** { // Set the bits representing theta to zero. br &= ~iMASK_THETA; // Shift the bits representing the new theta into position. br |= (u_theta << iSHIFT_THETA) & iMASK_THETA; } //***************************************************************************************** void SetPhiB // ( int i_phi ) // // Sets the lower phi value. This function will clamp the phi value so that it can only be // positive. // //************************************** { Assert(bWithin(i_phi, -iMAX_PHI_B, iMAX_PHI_B)); // Clamp phi to a positive value. SetMax(i_phi, 0); // Set the bits representing the bumpmap's phi to zero. br &= ~iMASK_PHI_B; // Shift the bits representing the new phi into position. br |= (i_phi << iSHIFT_PHI_B) & iMASK_PHI_B; } //***************************************************************************************** // void SetPhiL ( int i_phi ) // // Sets the lower phi value. // //************************************** { Assert(bWithin(i_phi, iMIN_PHI_L, iMAX_PHI_L)); // Set the values representing the light's phi to zero. br &= ~iMASK_PHI_L; // Shift the light's phi value and or it in. br |= (i_phi << iSHIFT_PHI_L) & iMASK_PHI_L; } //***************************************************************************************** // void SetBump ( CBumpAnglePair bang ) // // Copies just the bump info. // //************************************** { br &= ~iMASK_BUMP; br |= bang.brGetBump(); } //********************************************************************************************* // void SetBump ( const CDir3<>& rdir3, bool b_set_light_phi = false // Whether to set as light angle; else bump angle. ); // // Converts a normal into a pair of angles. Stores the phi value as either a light phi // or bump phi, depending on b_set_light_phi. // //************************************** //***************************************************************************************** // void SetColour ( uint8 u1_colour // Colour represented by a palette index value. ) // // Sets the index colour. // //************************************** { br &= ~iMASK_COLOUR; br |= (uint32)u1_colour << iSHIFT_COLOUR; } //***************************************************************************************** // void SetCurveFlag ( bool b_curve = true ) // // Sets a flag indicating that curvature has been applied to a pixel. // //************************************** { br &= ~iMASK_CURVE; br |= (b_curve) ? (iMASK_CURVE) : (0); } //***************************************************************************************** // // Member functions to get states. // //***************************************************************************************** // uint uGetTheta ( ) // // Returns the value of theta. // //************************************** { return (br & iMASK_THETA) >> iSHIFT_THETA; } //***************************************************************************************** // TBumpRes brIsolateTheta ( ) // // Returns the value of theta shifted to its representative position. // //************************************** { return br & iMASK_THETA; } //***************************************************************************************** // int iGetPhiB ( ) // // Returns the bumpmap's phi value. // //************************************** { return (br & iMASK_PHI_B) >> iSHIFT_PHI_B; } //***************************************************************************************** // int iGetPhiL ( ) // // Returns the light's phi value. // //************************************** { int32 i4_PhiL; // Storage for return value. // If phi is negative, make sure that the sign bit is carried. if (br & iSIGN_BIT_PHI_L) { i4_PhiL = -1; i4_PhiL &= ~iMASK_PHI_L; i4_PhiL |= br; } else { i4_PhiL = br; } i4_PhiL >>= iSHIFT_PHI_L; return i4_PhiL; } //***************************************************************************************** // TBumpRes brGetBump ( ) // // Returns the total bump angle information. // //************************************** { return br & iMASK_BUMP; } //***************************************************************************************** // uint8 u1GetColour ( ) // // Returns the index colour value. // //************************************** { return br >> iSHIFT_COLOUR; } //***************************************************************************************** // bool bGetCurveFlag ( ) // // Returns 'true' if the curvature has already been added to the pixel, otherwise returns // 'false.' // //************************************** { return (br & iMASK_CURVE) != 0; } //********************************************************************************************* // // Other member functions. // //********************************************************************************************* CDir3<> dir3MakeNormal ( ); // // Returns the normal described by the angle pair. // //************************************** //********************************************************************************************* CDir3<> dir3MakeNormalFast ( ); // // Returns the normal described by the angle pair. // //************************************** //********************************************************************************************* // float fGetPhiB ( ); // // Gets the phi bump angle in radians. // //************************************** //********************************************************************************************* // float fGetPhiL ( ); // // Gets the phi light angle in radians. // //************************************** //********************************************************************************************* // float fGetTheta ( ) // // Gets the theta angle in radians. // //************************************** { return float(uGetTheta()) * float(d2_PI) / float(1 << iTHETA_BITS); } //********************************************************************************************* // uint8* pu1GetSubBumpTable ( uint8* au1_table // Table containing intensity or arccos values. ) // // Returns a pointer to the location in the bumpmap to intensity table base on phi. // //************************************** { Assert(au1_table); CBumpAnglePair bangp_index; // Set the phi lighting value. bangp_index.SetPhiL(iGetPhiL()); // Return the position in the table referenced by the index value. return &au1_table[bangp_index.br]; } }; //********************************************************************************************* // class CBumpMap: public CRasterMemT // // A raster containing a bumpmap. // // Prefix: bmap // //************************************** { private: typedef CRasterMemT TParent; public: static CMatrix3<> mx3Reverse; // Reverse transform for the bumpmap. public: //***************************************************************************************** // // Constructor. // //***************************************************************************************** // CBumpMap ( char* str_height_filename, // Bumpmap (heightfield) bitmap file name. char* str_texture_filename, // Texture bitmap file name. float f_bumpiness = fDEFAULT_BUMPINESS // Value to multiply height by to get vertical // value in pixels. ); // // Constructs a bumpmap given filenames to a height map and texture map. // //************************************** //***************************************************************************************** // CBumpMap ( int i_width, // Height of bumpmap. int i_height, // Width of bumpmap. const CPal* ppal, // Pointer to a palette. TPixel pix_solid = 0 // Entry in palette to use. ); // // Constructs a bumpmap of a solid colour given a width, a height and a palette. // //************************************** //***************************************************************************************** // CBumpMap ( rptr pras_heightmap, // Height field representing bumpmap. rptr pras_texture, // Bitmap representing texture. float f_bumpiness = fDEFAULT_BUMPINESS // Value to multiply height by to get // vertical value in pixels. ); // // Constructs a bumpmap given rasters representing a height map and a texture map. // //************************************** //***************************************************************************************** // // Member functions. // //***************************************************************************************** // void DrawBump ( CTexCoord tc_0, // Position and normal for vertex 0. CDir3<> dir3_normal_0, CTexCoord tc_1, // Position and normal for vertex 1. CDir3<> dir3_normal_1, CTexCoord tc_2, // Position and normal for vertex 2. CDir3<> dir3_normal_2, const CMatrix3<>& mx3_reverse // Reverse transform. ); // // Draws a bump on a bumpmap surface by interpolating normals at each of the vertices. // //************************************** //***************************************************************************************** // void DrawBumps ( CTexCoord tc_0, // Position and normal for vertex 0. CDir3<> dir3_normal_0, CTexCoord tc_1, // Position and normal for vertex 1. CDir3<> dir3_normal_1, CTexCoord tc_2, // Position and normal for vertex 2. CDir3<> dir3_normal_2, CDir3<> dir3_normal_face, // Normal for newly created vertex // (usually the face normal). const CMatrix3<>& mx3_reverse // Reverse transform. ); // // Draws a bump on a bumpmap surface by splitting the triangle into three, and using the // face normal as the new vertex normals. In theory, this should provide a better curve // 'fit.' // //************************************** protected: //***************************************************************************************** void DrawBumpTest(); //***************************************************************************************** void AddHeightField(rptr pras_heightmap, float f_bumpiness = 0.1f); //***************************************************************************************** void AddTexture(rptr pras_texture); }; // // Bump curvature definitions. // //***************************************************************************************** CMatrix3<> mx3ObjToTexture ( const CVector3<>& v3_0, const CVector3<>& v3_1, const CVector3<>& v3_2, const CDir3<>& d3, const CTexCoord& tc_0, const CTexCoord& tc_1, const CTexCoord& tc_2 ); //***************************************************************************************** void SwapAxesVertical ( CVector3<>& v3, const CDir3<>& d3_control ); //***************************************************************************************** void SwapAxesVertical ( CMatrix3<>& mx3, const CDir3<>& d3_control ); //***************************************************************************************** void ApplyCurves ( rptr pmsh ); //***************************************************************************************** // void FastBumpCleanup(); // // Cleanup anything that has been allocated by bump map creation. // //************************************** //********************************************************************************************** // class CPixelFormatBumpmap: public CPixelFormat // // Modification of CPixelFormat palette which extracts colour index from CBumpAnglePair. // //************************************** { public: //****************************************************************************************** CPixelFormatBumpmap() : CPixelFormat(iBUMPMAP_RESOLUTION, 0, 0, 0, true) { // Make sure it's the same size as its base class, because we copy different version // of CPixelFormat around to each other. Assert(sizeof(*this) == sizeof(CPixelFormat)); } //****************************************************************************************** // // Overrides. // //****************************************************************************************** TPixel pixFromColour(CColour clr) const { // Do a palette search to find best match to clr. // Return a CBumpAnglePair with default angles. CBumpAnglePair bang; Assert(ppalAttached); bang.SetColour(ppalAttached->u1MatchEntry(clr)); // Set a dummy bump value to avoid generating a value of 0. bang.SetTheta(1); return bang; } //****************************************************************************************** CColour clrFromPixel(TPixel pix) const { // Just extract colour from bump angle, and look up in palette. int i_index = ((CBumpAnglePair&)pix).u1GetColour(); Assert(ppalAttached); return ppalAttached->aclrPalette[i_index]; } }; #endif