JurassicParkTrespasser/jp2_pc/Source/Lib/Renderer/Primitives/FastBump.hpp
2018-01-01 23:07:24 +01:00

961 lines
27 KiB
C++

/***********************************************************************************************
*
* 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 <float> 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<CBumpAnglePair>
//
// A raster containing a bumpmap.
//
// Prefix: bmap
//
//**************************************
{
private:
typedef CRasterMemT<CBumpAnglePair> 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<CRaster> pras_heightmap, // Height field representing bumpmap.
rptr<CRaster> 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<CRaster> pras_heightmap, float f_bumpiness = 0.1f);
//*****************************************************************************************
void AddTexture(rptr<CRaster> 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<CMesh> 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