|
|
///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 1998.
//
// texture.cpp
//
// Direct3D Reference Rasterizer - Texture Map Sampling & Filtering Methods
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
//-----------------------------------------------------------------------------
//
// overload new & delete so that it can be allocated from caller-controlled
// pool
//
//-----------------------------------------------------------------------------
void* RRTexture::operator new(size_t) { void* pMem = (void*)MEMALLOC( sizeof(RRTexture) ); _ASSERTa( NULL != pMem, "malloc failure on Tex object", return NULL; ); return pMem; } //-----------------------------------------------------------------------------
void RRTexture::operator delete(void* pv,size_t) { MEMFREE( pv ); }
//-----------------------------------------------------------------------------
//
// Constructor/Destructor
//
//-----------------------------------------------------------------------------
RRTexture::RRTexture( void ) { memset( this, 0, sizeof(*this) ); } //-----------------------------------------------------------------------------
RRTexture::~RRTexture( void ) { }
//-----------------------------------------------------------------------------
//
// Validate - Updates private data. Must be called anytime public data is
// altered.
//
//-----------------------------------------------------------------------------
BOOL RRTexture::Validate( void ) { // validate inputs
BOOL bFail0 = ( m_cLOD >= RRTEX_MAXCLOD ); // too many LODs
BOOL bFail1 = !( IsPowerOf2( m_iWidth ) ); // gotta be power of two
BOOL bFail2 = !( IsPowerOf2( m_iHeight ) ); if ( bFail0 || bFail1 || bFail2 ) { DPFRR(1,"RRTexture::Validate failed (%d,%d,%d)", bFail0, bFail1, bFail2); return FALSE; }
// set internal size reps
m_iTexSize[0] = (INT16)m_iWidth; m_iTexSize[1] = (INT16)m_iHeight;
// mask is size-1 because these have to be power-of-two
m_uTexMask[0] = (UINT16)m_iTexSize[0]-1; m_uTexMask[1] = (UINT16)m_iTexSize[1]-1; // shift is log2 of size
m_iTexShift[0] = (INT16)FindFirstSetBit( m_iTexSize[0], 16 ); m_iTexShift[1] = (INT16)FindFirstSetBit( m_iTexSize[1], 16 );
// compute the 'has alpha' flag
m_bHasAlpha = FALSE; switch ( m_SurfType ) { case RR_STYPE_B8G8R8A8: case RR_STYPE_B5G5R5A1: case RR_STYPE_B4G4R4A4: case RR_STYPE_L8A8: case RR_STYPE_L4A4: case RR_STYPE_B2G3R3A8: case RR_STYPE_DXT1: case RR_STYPE_DXT2: case RR_STYPE_DXT3: case RR_STYPE_DXT4: case RR_STYPE_DXT5: m_bHasAlpha = TRUE; break; case RR_STYPE_PALETTE4: case RR_STYPE_PALETTE8: m_bHasAlpha = ( m_uFlags & RR_TEXTURE_ALPHAINPALETTE ) ? TRUE : FALSE; break; }
return TRUE; }
//-----------------------------------------------------------------------------
//
// DoLookupAndFilter - Called once per active texture stage to compute
// coverage (level-of-detail) and invoke texel read and filtering routines.
// Returns filtered texel.
//
//-----------------------------------------------------------------------------
void RRTexture::DoLookupAndFilter( INT32 iStage, RRTextureCoord TCoord, // local copy
RRColor& TextureColor) { // check for potential mip mapping
BOOL bDoMipMap = ( m_cLOD > 0 ) && ( m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] > D3DTFP_NONE );
// check for requirement to do level-of-detail (coverage) computation - either
// for mipmap or per-pixel filter selection
BOOL bComputeLOD = bDoMipMap || ( m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] != m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] );
// check for anisotropic filtering in either mag filter or in min filter
BOOL bDoAniso = ( D3DTFG_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] ) || ( bComputeLOD && (D3DTFN_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) );
if ( bDoMipMap || bDoAniso || bComputeLOD ) { // here if doing mipmapping or anisotropic filtering, or just have a mismatch
// between the min and mag filters, so compute level of detail (and maybe aniso
// coverage)
// scale gradients to texture LOD 0 size
TCoord.fDUDX *= (FLOAT)m_iTexSize[0]; TCoord.fDUDY *= (FLOAT)m_iTexSize[0]; TCoord.fDVDX *= (FLOAT)m_iTexSize[1]; TCoord.fDVDY *= (FLOAT)m_iTexSize[1];
// compute level of detail (and maybe anisotropic controls)
FLOAT fLOD, fAnisoRatio, fAnisoDelta[2]; (bDoAniso) ? ComputeAnisotropicLevelOfDetail( TCoord, (FLOAT)m_pStageState[iStage].m_dwVal[D3DTSS_MAXANISOTROPY], fLOD, fAnisoRatio,fAnisoDelta ) : ComputeSimpleLevelOfDetail ( TCoord, fLOD );
// Uncomment the line below to see the anisotropy by color. White is 1:1, darker is more
// anisotropy.
//#define COLOR_BY_ANISOTROPY 1
#ifdef COLOR_BY_ANISOTROPY
static RRColor PseudoColors[16] = { 0xffffffff, 0xffffff00, 0xffff00ff, 0xff00ffff,
0xff888888, 0xff0000ff, 0xff00ff00, 0xffff0000,
0xff444444, 0xff888800, 0xff880088, 0xff008888,
0xff222222, 0xff000088, 0xff008800, 0xff880000, }; INT32 iPseudoColor = (INT32)(fAnisoRatio - .5); // round, and make 1.0F == index 0
iPseudoColor = min(max(iPseudoColor, 0), 15);
TextureColor = PseudoColors[iPseudoColor]; return; #endif
// apply bias and compute integer (n.5) LOD
INT16 iLOD = 0; if ( bComputeLOD ) { // apply LOD offset
fLOD += m_pStageState[iStage].m_fVal[D3DTSS_MIPMAPLODBIAS]; // convert LOD to n.5 fixed point integer
iLOD = AS_INT16( fLOD + FLOAT_5_SNAP ); }
// determine if magnifying or minifying
BOOL bMagnify = ( iLOD <= 0 );
// zero out LOD if not mipmapping
if ( !bDoMipMap ) { iLOD = 0; }
// do different filtering for magnify vs. minify
if ( bMagnify ) { // here for magnify - do either (non-anisotropic) magnify or
// anisotropic filter
if ( D3DTFG_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] ) { DoAniso( iStage, TCoord, iLOD,fAnisoRatio,fAnisoDelta, TextureColor ); } else { DoMagnify( iStage, TCoord, TextureColor ); } } else { // here for minify - do either simple minify, trilerp,
// or anisotropic filter
if ( D3DTFN_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] ) { DoAniso( iStage, TCoord, iLOD,fAnisoRatio,fAnisoDelta, TextureColor ); } else { if ( !bDoMipMap || ( bDoMipMap && ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] ) ) ) { DoMinify( iStage, TCoord, iLOD, TextureColor ); } else { DoTrilerp( iStage, TCoord, iLOD, TextureColor ); } } } } else { // here for no mipmaps and matching (and non-aniso) min and mag filters,
// so just apply mag filter
DoMagnify( iStage, TCoord, TextureColor ); } }
//-----------------------------------------------------------------------------
//
// DoMapLookupLerp - Performs texture index ADDRESS processing followed by
// a lookup within a single DD surface (a single LOD within a chain of DD
// surfaces). Dies BILINEAR filter operation for lookup.
//
// This is called once per pixel for BILINEAR, twice per pixel when
// doing mipmap trilinear interpolation.
//
// * texture index inputs are n.5 fixed point
// * LOD input is 0..n count where 0 indicates the largest LOD
//
//-----------------------------------------------------------------------------
RRColor RRTexture::DoMapLookupLerp(INT32 iStage, INT32 iU, INT32 iV, INT16 iLOD) { // extract fraction bits
UINT8 uUFrac = iU&0x1f; UINT8 uVFrac = iV&0x1f;
// take floor for (0,0) sample coords
INT16 iU0 = iU>>5; INT16 iV0 = iV>>5; // take ceiling for (1,1) sample coords
INT16 iU1 = iU0+1; INT16 iV1 = iV0+1;
BOOL bColorKeyMatched00 = FALSE; BOOL bColorKeyMatched01 = FALSE; BOOL bColorKeyMatched10 = FALSE; BOOL bColorKeyMatched11 = FALSE;
// grab four adjacent samples (or border color)
RRColor Texel00 = DoMapLookupNearest( iStage, iU0, iV0, iLOD, bColorKeyMatched00); RRColor Texel01 = DoMapLookupNearest( iStage, iU1, iV0, iLOD, bColorKeyMatched01); RRColor Texel10 = DoMapLookupNearest( iStage, iU0, iV1, iLOD, bColorKeyMatched10); RRColor Texel11 = DoMapLookupNearest( iStage, iU1, iV1, iLOD, bColorKeyMatched11);
// only set 'colorkey matched' if at least one matched value has
// a non-zero contribution (note that it is not possible for 00
// to have no contribution)
if (uUFrac == 0x00) { // 01 and 11 have zero weight if U fraction is zero
bColorKeyMatched01 = bColorKeyMatched11 = FALSE; } if (uVFrac == 0x00) { // 10 and 11 have zero weight if V fraction is zero
bColorKeyMatched10 = bColorKeyMatched11 = FALSE; }
// merge colorkey match info from previous invocation
m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched00 || bColorKeyMatched01 || bColorKeyMatched10 || bColorKeyMatched11;
// do bilinear filter
RRColor Texel; BiLerpColor( Texel, Texel00,Texel01, Texel10,Texel11, uUFrac,uVFrac); return Texel; }
//-----------------------------------------------------------------------------
//
// DoMapLookupNearest - Performs texture index ADDRESS processing followed by
// a lookup within a single DD surface (a single LOD within a chain of DD
// surfaces). Does NEAREST operation for lookup.
//
// This is called once per pixel for NEAREST , twice per pixel when
// doing mipmap trilinear interpolation
//
// * texture index inputs are n.0 fixed point
// * LOD input is 0..n count where 0 indicates the largest LOD
// * texture index extend mode processing is also performed here - this works
// for power-of-two texture sizes only.
//
//-----------------------------------------------------------------------------
RRColor RRTexture::DoMapLookupNearest(INT32 iStage, INT32 iU, INT32 iV, INT16 iLOD, BOOL &bColorKeyMatched) { // LSB-aligned masks of index bits within current LOD
INT16 iUMask = m_uTexMask[0] >> iLOD; INT16 iVMask = m_uTexMask[1] >> iLOD;
// boolean for BORDER - if true then use border color for corresponding sample
BOOL bUseBorder = FALSE;
// not matched by default
bColorKeyMatched = FALSE;
// do texture ADDRESS processing for U axis
switch ( m_pStageState[iStage].m_dwVal[D3DTSS_ADDRESSU] ) { case D3DTADDRESS_WRAP: // just lop off non-fractional bits
iU &= iUMask; break; case D3DTADDRESS_MIRROR: // lop off non-fractional bits + flip index if LSB (non-fraction) is set
BOOL bFlip; bFlip = iU & (iUMask+1); iU &= iUMask; if (bFlip) {iU = iUMask - iU;} break;
case D3DTADDRESS_BORDER: // compute booleans for which of 4 samples should use border color
if ((iU < 0) || (iU > iUMask)) { bUseBorder = TRUE;} break;
case D3DTADDRESS_CLAMP: // use texels on texture map edge
iU = MAX( 0, MIN( iU, iUMask ) ); break; }
// do texture ADDRESS processing for V axis
switch ( m_pStageState[iStage].m_dwVal[D3DTSS_ADDRESSV] ) { case D3DTADDRESS_WRAP: iV &= iVMask; break; case D3DTADDRESS_MIRROR: BOOL bFlip; bFlip = iV & (iVMask+1); iV &= iVMask; if (bFlip) {iV = iVMask - iV;} break;
case D3DTADDRESS_BORDER: if ((iV < 0) || (iV > iVMask)) { bUseBorder = TRUE; } break;
case D3DTADDRESS_CLAMP: iV = MAX( 0, MIN( iV, iVMask ) ); break; }
// just lookup and return texel at (iU0,iV0)
RRColor Texel; (bUseBorder) ? Texel = m_pStageState[iStage].m_dwVal[D3DTSS_BORDERCOLOR] : ReadColor( iU, iV, iLOD, Texel, bColorKeyMatched ); return Texel; }
///////////////////////////////////////////////////////////////////////////////
// //
// Texture Filtering Routines //
// //
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// DoLookup - Does a full lookup given floating point U, V and handles all
// nearest vs bilinear and LOD issues.
//
//-----------------------------------------------------------------------------
RRColor RRTexture::DoLookup(INT32 iStage, float U, float V, INT16 iLOD, BOOL bNearest) { INT32 cUPixels = 1 << MAX(m_iTexShift[0]-iLOD,0); INT32 cVPixels = 1 << MAX(m_iTexShift[1]-iLOD,0); FLOAT fScaledU = ( U * (FLOAT)cUPixels ) -.5f; FLOAT fScaledV = ( V * (FLOAT)cVPixels ) -.5f; if(bNearest) { INT32 iU, iV; // truncate to -infinity to be compatible with ANDing off low order
// bits of a fixed point fScaledCoord. This makes the generation of
// iCoord more hardware like, and does not make a glitch at 0 for
// a wrapped texture.
if (U >= 0.0f) { iU = fScaledU + .5f; } else { iU = fScaledU - .5f; } if (V >= 0.0f) { iV = fScaledV + .5f; } else { iV = fScaledV - .5f; } BOOL bColorKeyMatched = FALSE; RRColor Texel = DoMapLookupNearest(iStage,iU,iV,iLOD,bColorKeyMatched); // merge colorkey match info from previous invocation
m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched; return Texel; } else { INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP ); return DoMapLookupLerp(iStage,iU,iV,iLOD); } }
//-----------------------------------------------------------------------------
//
// DoMagnify - This is used for all magnification filter modes except
// anisotropic.
//
// Currently only POINT and BILINEAR are supported.
//
//-----------------------------------------------------------------------------
void RRTexture::DoMagnify(INT32 iStage, RRTextureCoord& TCoord, RRColor& Texel ) { // do lookup, applying MAG filter
Texel = DoLookup( iStage, TCoord.fU, TCoord.fV, 0, (D3DTFG_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER]) ); }
//-----------------------------------------------------------------------------
//
// DoMinify - This is used for POINT and BILINEAR modes (non-trilinear)
// for minification, and also handles POINT mip filter (nearest LOD).
//
// iLOD is n.5 fixed point
//
//-----------------------------------------------------------------------------
void RRTexture::DoMinify(INT32 iStage, RRTextureCoord& TCoord, INT16 iLOD, RRColor& Texel ) { // round and drop fraction from LOD (is n.5 fixed point)
iLOD += 0x10; iLOD &= ~(0x1f); // convert to n.0
iLOD >>= 5; // clamp LOD to number of available levels
iLOD = MIN( iLOD, m_cLOD );
// do lookup, applying MIN filter
Texel = DoLookup( iStage, TCoord.fU, TCoord.fV, iLOD, (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) ); }
//-----------------------------------------------------------------------------
//
// DoTrilerp - Computes level of detail and invokes either: single-map
// lookup & filter for magnify; or trilinear lookup and filter for minify
//
//-----------------------------------------------------------------------------
void RRTexture::DoTrilerp(INT32 iStage, RRTextureCoord& TCoord, INT16 iLOD, RRColor& Texel) { // clamp LOD to number of available levels
iLOD = MIN( iLOD, (m_cLOD)<<5 ); // compute index for two adjacent LODs (with clamp)
INT16 iLODHi = iLOD>>5; // floor
INT16 iLODLo = MIN(iLODHi+1,m_cLOD);
// check for filter type for within LOD map
BOOL bNearest = (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]);
// trilerp - look up each map then lerp between them
// important for colorkey to not include texels with no contribution
if (0x00 != (iLOD&0x1f)) { RRColor Texel0 = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODHi, bNearest); RRColor Texel1 = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODLo, bNearest); LerpColor( Texel, Texel0, Texel1, iLOD&0x1f ); } else { Texel = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODHi, bNearest); } }
//-----------------------------------------------------------------------------
//
// DoAniso - Handles anisotropic filtering of either magnified (single
// map lookup) or minified (two adjacent map lookup) samples. The computation
// of level of detail and anisotropic coverage information (fRatio,fDelta[]) is
// done prior to this function.
//
// This performs only anisotropic filtering, and is called only for minification
// when the MINFILTER is set to ANISOTROPIC or for magnification when the
// MAGFILTER is set to ANISOTROPIC.
//
//-----------------------------------------------------------------------------
void RRTexture::DoAniso(INT32 iStage, RRTextureCoord& TCoord, INT16 iLOD, FLOAT fRatio, FLOAT fDelta[], RRColor& Texel) { // set boolean if magnifying
BOOL bMagnify = (iLOD <= 0); // clamp LOD to number of available levels
iLOD = MIN( MAX( iLOD, 0 ), (m_cLOD)<<5 );
// compute index for two adjacent LODs (with clamp)
// 0 is the larger LOD, 1 is the smaller LOD
INT16 iLODHi, iLODLo; if ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] ) { // here for nearest MIP filter
// round and drop fraction from LOD (is n.5 fixed point)
iLOD += 0x10; iLOD &= ~(0x1f); // convert to n.0
iLODHi = iLOD >> 5; } else { // here for linear MIP filter
iLODHi = iLOD >> 5; // floor for larger LOD
if ( !bMagnify ) { // ceiling+clamp for smaller LOD
iLODLo = MIN( iLODHi+1, m_cLOD ); } }
// compute boolean true if only sampling one map - this is the case if
// we are magnifying or if the MIPFILTER is set to NEAREST or if the
// LOD fraction is zero
BOOL bSingleMap = bMagnify || (D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER]) || (0x00 == (iLOD&0x1f));
// working copy of texture coordinates
FLOAT fU = TCoord.fU; FLOAT fV = TCoord.fV;
// fDelta is in texels. Compute correction factor for each LOD we care about
FLOAT fUStepScaleHi = 1.0F/(FLOAT)MAX(m_iWidth >> iLODHi, 1); FLOAT fVStepScaleHi = 1.0F/(FLOAT)MAX(m_iHeight >> iLODHi, 1); FLOAT fUStepScaleLo = 0.F; FLOAT fVStepScaleLo = 0.F;
if ( !bSingleMap ) { fUStepScaleLo = 1.0F/(FLOAT)MAX(m_iWidth >> iLODLo, 1); fVStepScaleLo = 1.0F/(FLOAT)MAX(m_iHeight >> iLODLo, 1); }
// colors for holding partial results during filtering
RRColor TexelP, TexelP0, TexelP1; // Plus side texels
RRColor TexelM, TexelM0, TexelM1; // Minus side texels
//
// key on ratio to either do single lookup, <2:1 processing (two lookups),
// or full aniso walk
//
if (fRatio == 1.) { // here for no anisotropy - do single trilerp
if ( bSingleMap ) { // single map lookup for magnify
Texel = DoLookup( iStage, fU, fV, iLODHi, FALSE); } else { // trilerp for minify
TexelP0 = DoLookup( iStage, fU, fV, iLODHi, FALSE); TexelP1 = DoLookup( iStage, fU, fV, iLODLo, FALSE); LerpColor( Texel, TexelP0, TexelP1, iLOD&0x1f ); } } else if (fRatio <= 2.) { // here for 2:1 or less - do two lookups and average them
// compute x,y steps from sample center
FLOAT fStep = .5*(fRatio-1.); FLOAT fUStep = fDelta[0]*fStep; FLOAT fVStep = fDelta[1]*fStep;
// do + side lookup
if ( bSingleMap ) { // single map lookup for magnify
TexelP = DoLookup( iStage, fU+fUStep*fUStepScaleHi, fV+fVStep*fVStepScaleHi, iLODHi, FALSE); } else { // trilerp for minify
TexelP0 = DoLookup( iStage, fU+fUStep*fUStepScaleHi, fV+fVStep*fVStepScaleHi, iLODHi, FALSE); TexelP1 = DoLookup( iStage, fU+fUStep*fUStepScaleLo, fV+fVStep*fVStepScaleLo, iLODLo, FALSE); LerpColor( TexelP, TexelP0, TexelP1, iLOD&0x1f ); }
// do - side lookup
if ( bSingleMap ) { // single map lookup for magnify
TexelM = DoLookup( iStage, fU-fUStep*fUStepScaleHi, fV-fVStep*fVStepScaleHi, iLODHi, FALSE); } else { // trilerp for minify
TexelM0 = DoLookup( iStage, fU-fUStep*fUStepScaleHi, fV-fVStep*fVStepScaleHi, iLODHi, FALSE); TexelM1 = DoLookup( iStage, fU-fUStep*fUStepScaleLo, fV-fVStep*fVStepScaleLo, iLODLo, FALSE); LerpColor( TexelM, TexelM0, TexelM1, iLOD&0x1f ); }
// take average for final texel
LerpColor( Texel, TexelP, TexelM, 0x10 ); } else { // here for > 2:1 - walk line of anisotropy; walks out from the center
// sample point taking two sets of samples (outriggers) per loop, one
// sample at a positive offset from the center (along the aniso line)
// and the other at a negative offset from the center
// this section does stepping for both LODs even though LOD[1] axis
// is not used for magnify case (only the lookup and lerp(s) are skipped)
// n.5 fixed point versions of step values
FLOAT fUStep = fDelta[0]; FLOAT fVStep = fDelta[1];
// initialize + and - step parameters - first step is half distance
FLOAT fUHiP = fU + fUStep*fUStepScaleHi*0.5F; FLOAT fVHiP = fV + fVStep*fVStepScaleHi*0.5F; FLOAT fULoP = fU + fUStep*fUStepScaleLo*0.5F; FLOAT fVLoP = fV + fVStep*fVStepScaleLo*0.5F; FLOAT fUHiM = fU - fUStep*fUStepScaleHi*0.5F; FLOAT fVHiM = fV - fVStep*fVStepScaleHi*0.5F; FLOAT fULoM = fU - fUStep*fUStepScaleLo*0.5F; FLOAT fVLoM = fV - fVStep*fVStepScaleLo*0.5F;
// step and accumulate color channels
FLOAT fTexelAcc[4] = { 0.f, 0.f, 0.f, 0.f }; // fp accumulation of texel color
FLOAT fRatioRem = fRatio; FLOAT fInvRatio = 1./fRatio; BOOL bDone = FALSE; while (1) { // do + side lookup
if ( bSingleMap ) { // single map lookup for magnify
TexelP = DoLookup( iStage, fUHiP, fVHiP, iLODHi, FALSE ); } else { // trilerp for minify
TexelP0 = DoLookup( iStage, fUHiP, fVHiP, iLODHi, FALSE ); TexelP1 = DoLookup( iStage, fULoP, fVLoP, iLODLo, FALSE ); LerpColor( TexelP, TexelP0, TexelP1, iLOD&0x1f ); }
// do - side lookup
if ( bSingleMap ) { // single map lookup for magnify
TexelM = DoLookup( iStage, fUHiM, fVHiM, iLODHi, FALSE ); } else { // trilerp for minify
TexelM0 = DoLookup( iStage, fUHiM, fVHiM, iLODHi, FALSE ); TexelM1 = DoLookup( iStage, fULoM, fVLoM, iLODLo, FALSE ); LerpColor( TexelM, TexelM0, TexelM1, iLOD&0x1f ); }
// compute scaling for these samples
FLOAT fAccScale = fInvRatio; if ( fRatioRem < 2.f ) { // scale for last outriggers is half of remainder (each)
fAccScale = fRatioRem*.5f*fInvRatio; bDone = TRUE; }
// do accumulations
fTexelAcc[0] += fAccScale * FLOAT(TexelP.A); fTexelAcc[1] += fAccScale * FLOAT(TexelP.R); fTexelAcc[2] += fAccScale * FLOAT(TexelP.G); fTexelAcc[3] += fAccScale * FLOAT(TexelP.B);
fTexelAcc[0] += fAccScale * FLOAT(TexelM.A); fTexelAcc[1] += fAccScale * FLOAT(TexelM.R); fTexelAcc[2] += fAccScale * FLOAT(TexelM.G); fTexelAcc[3] += fAccScale * FLOAT(TexelM.B);
// bail from here if last outrigger
if (bDone) { break; }
// advance to next outriggers
fUHiP += fUStep*fUStepScaleHi; fVHiP += fVStep*fVStepScaleHi; fULoP += fUStep*fUStepScaleLo; fVLoP += fVStep*fVStepScaleLo; fUHiM -= fUStep*fUStepScaleHi; fVHiM -= fVStep*fVStepScaleHi; fULoM -= fUStep*fUStepScaleLo; fVLoM -= fVStep*fVStepScaleLo; fRatioRem -= 2.f; }
// clamp accumulator and copy into RRColor for return
Texel.A = MIN( 1.f, fTexelAcc[0] ); Texel.R = MIN( 1.f, fTexelAcc[1] ); Texel.G = MIN( 1.f, fTexelAcc[2] ); Texel.B = MIN( 1.f, fTexelAcc[3] ); } }
//-----------------------------------------------------------------------------
//
// DoBumpMapping - Called once per buxel to compute the bump map delta's
// and the bump map modulate factor to be used in the next texturing stage.
//
//-----------------------------------------------------------------------------
void RRTexture::DoBumpMapping( INT32 iStage, RRTextureCoord TCoord, FLOAT& fBumpMapUDelta, FLOAT& fBumpMapVDelta, RRColor& BumpMapModulate) { // do full lookup using enabled filtering
RRColor Buxel; DoLookupAndFilter(iStage, TCoord, Buxel);
FLOAT fDU = Buxel.R; // follows convention from read color routine
FLOAT fDV = Buxel.G; FLOAT fL = Buxel.B;
// grab transform from renderstate
FLOAT fM00 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT00]; FLOAT fM01 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT01]; FLOAT fM10 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT10]; FLOAT fM11 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT11];
// apply transforms to deltas from map to form delta return values
fBumpMapUDelta = fM00 * fDU + fM10 * fDV; fBumpMapVDelta = fM01 * fDU + fM11 * fDV;
// apply scale/bias/clamp to luminance and form RRColor for return
if (m_pStageState[iStage].m_dwVal[D3DTSS_COLOROP] == D3DTOP_BUMPENVMAPLUMINANCE) { FLOAT fLScale = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVLSCALE]; FLOAT fLOff = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVLOFFSET]; fL = fL * fLScale + fLOff; fL = min(max(fL, 0.0f), 1.0F); BumpMapModulate.R = fL; BumpMapModulate.G = fL; BumpMapModulate.B = fL; } else { // if not BUMPENVMAPLUMINANCE, always return full intensity white
BumpMapModulate.R = 1.0F; BumpMapModulate.G = 1.0F; BumpMapModulate.B = 1.0F; } BumpMapModulate.A = 1.0F; }
///////////////////////////////////////////////////////////////////////////////
// //
// Texture Mapping Utility Functions //
// //
///////////////////////////////////////////////////////////////////////////////
//
// various approximations and tricks to speed up the texture map coverage
// computations
//
// these have not been really thoroughly tested, so use at your own risk...
//
// Integer value of first exponent bit in a float. Provides a scaling factor
// for exponent values extracted directly from float representation.
#define FLOAT_EXPSCALE ((FLOAT)0x00800000)
#define FLOAT_OOEXPSCALE ((FLOAT)(1.0 / (double)FLOAT_EXPSCALE))
// Integer representation of 1.0f.
#define INT32_FLOAT_ONE 0x3f800000
static inline FLOAT RR_LOG2(FLOAT f) { return (FLOAT)(AS_INT32(f) - INT32_FLOAT_ONE) * FLOAT_OOEXPSCALE; }
static inline FLOAT RR_ALOG2(FLOAT f) { INT32 i = (INT32)(f * FLOAT_EXPSCALE) + INT32_FLOAT_ONE; return AS_FLOAT((long int)i); }
static inline FLOAT RR_ABSF(FLOAT f) { UINT32 i = AS_UINT32(f) & 0x7fffffff; return AS_FLOAT((unsigned long int)i); }
static inline FLOAT RR_SQRT(FLOAT f) { INT32 i = (AS_INT32(f) >> 1) + (INT32_FLOAT_ONE >> 1); return AS_FLOAT((long int)i); }
//
// Steve Gabriel's version of an octagonal approximation euclidian distance -
// return is approximating sqrt(fX*fX + fY*fY)
//
static inline FLOAT RR_LENGTH(FLOAT fX, FLOAT fY) { fX = RR_ABSF(fX); fY = RR_ABSF(fY); return ((11.0f/32.0f)*(fX + fY) + (21.0f/32.0f)*max(fX, fY)); }
//-----------------------------------------------------------------------------
//
// Computes level of detail for standard trilinear mipmapping, in which
// the four texture index gradients are consolidated into a single number
// to select level of detail.
//
// The basic approach is to compute the lengths of the pixel coverage for
// the X and Y extent of the approximate pixel coverage area. These two
// lengths are then combined in one of several possible methods for the
// single LOD result.
//
// There are several other ways of doing this which are less computationally
// expensive but also produce less desirable results...
//
//-----------------------------------------------------------------------------
void ComputeSimpleLevelOfDetail( const RRTextureCoord& TCoord, FLOAT& fLOD ) { // compute length of coverage in U and V axis
FLOAT fLenX = RR_LENGTH( TCoord.fDUDX, TCoord.fDVDX ); FLOAT fLenY = RR_LENGTH( TCoord.fDUDY, TCoord.fDVDY ); FLOAT fCoverage;
switch ( 3 /* LOD computation type */ ) {
// this is probably the best of the lot
case 1 /* AREA */ : fCoverage = RR_SQRT(fLenX*fLenY); break;
// we have not actually tried this one yet, but think it might
// be pretty good
case 2 /* AVERAGE */ : fCoverage = (fLenX+fLenY)/2; break;
// these are fairly inexpensive, but MAX is a bit too fuzzy
// and MIN is a bit too sharp
case 3 /* MAX */ : fCoverage = MAX( fLenX, fLenY ); break; case 4 /* MIN */ : fCoverage = MIN( fLenX, fLenY ); break;
// these are really inexpensive, but look terrible - you might as
// well just point sample...
case 5 /* MINGRAD */ : fCoverage = MIN( MIN( MIN( TCoord.fDUDX, TCoord.fDVDX ), TCoord.fDUDY ), TCoord.fDVDY ); break; case 6 /* MAXGRAD */ : fCoverage = MAX( MAX( MAX( TCoord.fDUDX, TCoord.fDVDX ), TCoord.fDUDY ), TCoord.fDVDY ); break; }
// take log2 of coverage for LOD
fLOD = RR_LOG2(fCoverage); }
//-----------------------------------------------------------------------------
//
// Computes level of detail and other factors in preparation for anisotropic
// filtering.
//
//-----------------------------------------------------------------------------
void ComputeAnisotropicLevelOfDetail( const RRTextureCoord& TCoord, FLOAT fMaxAniso, // inputs
FLOAT& fLOD, FLOAT& fRatio, FLOAT fDelta[] ) // outputs
{ // compute axis lengths and determinant
FLOAT fLenX2 = (TCoord.fDUDX*TCoord.fDUDX)+(TCoord.fDVDX*TCoord.fDVDX); FLOAT fLenY2 = (TCoord.fDUDY*TCoord.fDUDY)+(TCoord.fDVDY*TCoord.fDVDY); FLOAT fDet = RR_ABSF((TCoord.fDUDX*TCoord.fDVDY)-(TCoord.fDUDY*TCoord.fDVDX));
// select major axis
BOOL bXMajor = (fLenX2 > fLenY2);
// TODO: can and probably should do this part in log2 domain
// select and normalize steps; compute aniso ratio
FLOAT fMaj2 = (bXMajor) ? (fLenX2) : (fLenY2); FLOAT fMaj = RR_SQRT(fMaj2); FLOAT fMajNorm = 1./fMaj; fDelta[0] = ( bXMajor ? TCoord.fDUDX : TCoord.fDUDY ) * fMajNorm; fDelta[1] = ( bXMajor ? TCoord.fDVDX : TCoord.fDVDY ) * fMajNorm; fRatio = (fDet != 0.F) ? (fMaj2/fDet) : (FLT_MAX);
// clamp ratio and compute LOD
FLOAT fMin; if ( fRatio > fMaxAniso ) { // ratio is clamped - LOD is based on ratio (preserves area)
fRatio = fMaxAniso; fMin = fMaj/fRatio; } else { // ratio not clamped - LOD is based on area
fMin = fDet/fMaj; }
// clamp to top LOD
if (fMin < 1.0) { fRatio = MAX( 1.0, fRatio*fMin ); fMin = 1.0; }
// take log2 of minor for LOD
fLOD = RR_LOG2(fMin); }
///////////////////////////////////////////////////////////////////////////////
// //
// Environment mapping routines //
// //
///////////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------------
//
// Processes the environment mapping normal and converts to a standard
// U, V coord range for subsequent routines.
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvProcessNormal(INT32 iStage, RREnvTextureCoord ECoord, // local copy
RRColor& TextureColor) { #define ENV_RIGHT 0
#define ENV_LEFT 1
#define ENV_TOP 2
#define ENV_BOTTOM 3
#define ENV_FRONT 4
#define ENV_BACK 5
#define POS_NX 1
#define POS_NY 2
#define POS_NZ 3
#define NEG_NORM 4
#define NEG_NX (NEG_NORM | POS_NX)
#define NEG_NY (NEG_NORM | POS_NY)
#define NEG_NZ (NEG_NORM | POS_NZ)
// If we add per pixel normal reflection
// FLOAT fENX = ECoord.fENX;
// FLOAT fENY = ECoord.fENY;
// FLOAT fENZ = ECoord.fENZ;
//
// FLOAT fNDotE = ECoord.fNX*fENX + ECoord.fNY*fENY + ECoord.fNZ*fENZ;
// FLOAT fNDotN = ECoord.fNX*ECoord.fNX + ECoord.fNY*ECoord.fNY + ECoord.fNZ*ECoord.fNZ;
// fNDotE *= 2.0F;
// ECoord.fNX = ECoord.fNX*fNDotE - fENX*fNDotN;
// ECoord.fNY = ECoord.fNY*fNDotE - fENY*fNDotN;
// ECoord.fNZ = ECoord.fNZ*fNDotE - fENZ*fNDotN;
// determine which is the dominant normal
UINT32 uMap; FLOAT fAbsNX = fabs(ECoord.fNX); FLOAT fAbsNY = fabs(ECoord.fNY); FLOAT fAbsNZ = fabs(ECoord.fNZ);
if (fAbsNX > fAbsNY) { if (fAbsNX > fAbsNZ) // fNX
uMap = POS_NX | ((ECoord.fNX < 0.0) ? (NEG_NORM) : 0); else // fNZ
uMap = POS_NZ | ((ECoord.fNZ < 0.0) ? (NEG_NORM) : 0); } else { if (fAbsNY > fAbsNZ) // fNY
uMap = POS_NY | ((ECoord.fNY < 0.0) ? (NEG_NORM) : 0); else // fNZ
uMap = POS_NZ | ((ECoord.fNZ < 0.0) ? (NEG_NORM) : 0); }
RRTextureCoord TCoord;
switch (uMap) { case POS_NX: TCoord.fDUDX = -ECoord.fDNZDX; TCoord.fDVDX = -ECoord.fDNYDX; TCoord.fDUDY = -ECoord.fDNZDY; TCoord.fDVDY = -ECoord.fDNYDY; TCoord.fU = -ECoord.fNZ; TCoord.fV = -ECoord.fNY; DoEnvLookupAndFilter(iStage, ENV_RIGHT, ECoord.fNX, ECoord.fDNXDX, ECoord.fDNXDY, TCoord, TextureColor); break;
case POS_NY: TCoord.fDUDX = ECoord.fDNXDX; TCoord.fDVDX = ECoord.fDNZDX; TCoord.fDUDY = ECoord.fDNXDY; TCoord.fDVDY = ECoord.fDNZDY; TCoord.fU = ECoord.fNX; TCoord.fV = ECoord.fNZ; DoEnvLookupAndFilter(iStage, ENV_TOP, ECoord.fNY, ECoord.fDNYDX, ECoord.fDNYDY, TCoord, TextureColor); break;
case POS_NZ: TCoord.fDUDX = ECoord.fDNXDX; TCoord.fDVDX = -ECoord.fDNYDX; TCoord.fDUDY = ECoord.fDNXDY; TCoord.fDVDY = -ECoord.fDNYDY; TCoord.fU = ECoord.fNX; TCoord.fV = -ECoord.fNY; DoEnvLookupAndFilter(iStage, ENV_FRONT, ECoord.fNZ, ECoord.fDNZDX, ECoord.fDNZDY, TCoord, TextureColor); break;
case NEG_NX: TCoord.fDUDX = ECoord.fDNZDX; TCoord.fDVDX = -ECoord.fDNYDX; TCoord.fDUDY = ECoord.fDNZDY; TCoord.fDVDY = -ECoord.fDNYDY; TCoord.fU = ECoord.fNZ; TCoord.fV = -ECoord.fNY; DoEnvLookupAndFilter(iStage, ENV_LEFT, -ECoord.fNX, -ECoord.fDNXDX, -ECoord.fDNXDY, TCoord, TextureColor); break;
case NEG_NY: TCoord.fDUDX = ECoord.fDNXDX; TCoord.fDVDX = -ECoord.fDNZDX; TCoord.fDUDY = ECoord.fDNXDY; TCoord.fDVDY = -ECoord.fDNZDY; TCoord.fU = ECoord.fNX; TCoord.fV = -ECoord.fNZ; DoEnvLookupAndFilter(iStage, ENV_BOTTOM, -ECoord.fNY, -ECoord.fDNYDX, -ECoord.fDNYDY, TCoord, TextureColor); break;
case NEG_NZ: TCoord.fDUDX = -ECoord.fDNXDX; TCoord.fDVDX = -ECoord.fDNYDX; TCoord.fDUDY = -ECoord.fDNXDY; TCoord.fDVDY = -ECoord.fDNYDY; TCoord.fU = -ECoord.fNX; TCoord.fV = -ECoord.fNY; DoEnvLookupAndFilter(iStage, ENV_BACK, -ECoord.fNZ, -ECoord.fDNZDX, -ECoord.fDNZDY, TCoord, TextureColor); break; } }
//-----------------------------------------------------------------------------
//
// DoEnvLookupAndFilter - Environment mapped version.
// Called once per active texture stage to compute
// coverage (level-of-detail) and invoke texel read and filtering routines.
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvLookupAndFilter(INT32 iStage, INT16 iFace, FLOAT fMajor, FLOAT fDMDX, FLOAT fDMDY, RRTextureCoord TCoord, // local copy
RRColor& TextureColor) { if (m_pDDSLcl[iFace]) { // faces exist
FLOAT fInvMajor = 1.0/fMajor;
// compute d(U/Major)/dx, etc. using rule for differentiating quotients
TCoord.fDUDX = (fMajor*TCoord.fDUDX - TCoord.fU*fDMDX)*fInvMajor*fInvMajor; TCoord.fDUDY = (fMajor*TCoord.fDUDY - TCoord.fU*fDMDY)*fInvMajor*fInvMajor; TCoord.fDVDX = (fMajor*TCoord.fDVDX - TCoord.fV*fDMDX)*fInvMajor*fInvMajor; TCoord.fDVDY = (fMajor*TCoord.fDVDY - TCoord.fV*fDMDY)*fInvMajor*fInvMajor;
// convert to -1 to 1 range
TCoord.fU *= fInvMajor; TCoord.fV *= fInvMajor;
// convert to 0.0 to 1.0
TCoord.fU = (TCoord.fU*.5 + .5); TCoord.fV = (TCoord.fV*.5 + .5);
// check for potential mip mapping
BOOL bDoMipMap = ( m_cLOD > 0 ) && ( m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] > D3DTFP_NONE );
// check for requirement to do level-of-detail (coverage) computation - either
// for mipmap or per-pixel filter selection
BOOL bComputeLOD = bDoMipMap || ( m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] != m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] );
if ( bDoMipMap || bComputeLOD ) { // here if doing mipmapping or anisotropic filtering, or just have a mismatch
// between the min and mag filters, so compute level of detail (and maybe aniso
// coverage)
// scale gradients to texture LOD 0 size
TCoord.fDUDX *= (FLOAT)m_iTexSize[0]*.5F; TCoord.fDUDY *= (FLOAT)m_iTexSize[0]*.5F; TCoord.fDVDX *= (FLOAT)m_iTexSize[1]*.5F; TCoord.fDVDY *= (FLOAT)m_iTexSize[1]*.5F;
FLOAT fLOD; ComputeEnvMapLevelOfDetail(TCoord, fLOD);
// apply bias and compute integer (n.5) LOD
INT16 iLOD = 0; if ( bComputeLOD ) { // apply LOD offset
fLOD += m_pStageState[iStage].m_fVal[D3DTSS_MIPMAPLODBIAS]; // convert LOD to n.5 fixed point integer
iLOD = AS_INT16( fLOD + FLOAT_5_SNAP ); }
// determine if magnifying or minifying
BOOL bMagnify = ( iLOD <= 0 );
// zero out LOD if not mipmapping
if ( !bDoMipMap ) { iLOD = 0; }
// do different filtering for magnify vs. minify
if ( bMagnify ) { DoEnvMagnify( iStage, TCoord, iFace, TextureColor ); } else { if ( !bDoMipMap || ( bDoMipMap && ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] ) ) ) { DoEnvMinify( iStage, TCoord, iFace, iLOD, TextureColor ); } else { DoEnvTrilerp( iStage, TCoord, iFace, iLOD, TextureColor ); } } } else { // here for no mipmaps and matching (and non-aniso) min and mag filters,
// so just apply mag filter
DoEnvMagnify( iStage, TCoord, iFace, TextureColor ); } } else { // face doesn't exit, return empty face color
TextureColor = m_dwEmptyFaceColor; } }
//-----------------------------------------------------------------------------
//
// TexelAvg4 - Averages 4 source texels into 1 destination texel for A, R, G,
// and B.
//
//-----------------------------------------------------------------------------
static void TexelAvg4(RRColor& Texel, RRColor Texel0, RRColor Texel1, RRColor Texel2, RRColor Texel3) { Texel.A = Texel0.A + Texel1.A + Texel2.A + Texel3.A; Texel.R = Texel0.R + Texel1.R + Texel2.R + Texel3.R; Texel.G = Texel0.G + Texel1.G + Texel2.G + Texel3.G; Texel.B = Texel0.B + Texel1.B + Texel2.B + Texel3.B; Texel.A = Texel.A * 0.25f; Texel.R = Texel.R * 0.25f; Texel.G = Texel.G * 0.25f; Texel.B = Texel.B * 0.25f; }
//-----------------------------------------------------------------------------
//
// DoEnvLookup - Does a full lookup given floating point U, V and handles all
// nearest vs bilinear and LOD issues.
//
//-----------------------------------------------------------------------------
RRColor RRTexture::DoEnvLookup(INT32 iStage, RRTextureCoord TCoord, INT16 iFace, INT16 iLOD, BOOL bNearest) { FLOAT U = TCoord.fU; FLOAT V = TCoord.fV; RRColor Texel; INT32 cUPixels = 1 << MAX(m_iTexShift[0]-iLOD,0); INT32 cVPixels = 1 << MAX(m_iTexShift[1]-iLOD,0); FLOAT fScaledU = ( U * (FLOAT)cUPixels ) -.5f; FLOAT fScaledV = ( V * (FLOAT)cVPixels ) -.5f;
// LSB-aligned masks of index bits within current LOD
INT16 iUMask = m_uTexMask[0] >> iLOD; INT16 iVMask = m_uTexMask[1] >> iLOD;
if(bNearest) { INT32 iU, iV; // truncate to -infinity to be compatible with ANDing off low order
// bits of a fixed point fScaledCoord. This makes the generation of
// iCoord more hardware like, and does not make a glitch at 0 for
// a wrapped texture.
if (U >= 0.0f) { iU = fScaledU + .5f; } else { iU = fScaledU - .5f; } if (V >= 0.0f) { iV = fScaledV + .5f; } else { iV = fScaledV - .5f; }
// clamp
iU = MAX( 0, MIN( iU, iUMask ) ); iV = MAX( 0, MIN( iV, iVMask ) );
BOOL bColorKeyMatched = FALSE;
// "LOD" just used to access correct map
ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched);
// merge colorkey match info from previous invocation
m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched; } else { if ((m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] == D3DTFG_FLATCUBIC) || (m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] == D3DTFG_GAUSSIANCUBIC)) { // use wider 3x3 trapezoid filter
//
// For the top LOD, if we are interpolating beyond the edge of the
// texture, correct the interpolation to minimize the artifacts seen in
// small, diffuse environment maps which tend to emphasize the edges
// of the cubemap.
//
if (iLOD == 0) { FLOAT fFracU = 0.0f; FLOAT fCorrectionU = 0.0f; FLOAT fCorrectionV = 0.0f; if ((fScaledU < 0.5f) || (fScaledU > ((FLOAT)iUMask-0.5f))) { // U crosses a boundary, clamp fScaledV
if (fScaledU < 0.5f) { // make fFrac always positive
fFracU = 0.5f-fScaledU; } else { fFracU = fScaledU - ((FLOAT)iUMask-0.5f); } // 2.0/2.0 = 1.0 provides the perfect correction at the cube corner.
// This can be seen be looking at the derivative of the intersection
// of a cone and a cube at the cube corner.
//
// correction must be corrected for the filter width (hence the *0.5f)
fCorrectionV = -fFracU*(TCoord.fV-.5f)*0.5f; } if ((fScaledV < 0.5f) || (fScaledV > ((FLOAT)iVMask-0.5f))) { // V crosses a boundary, clamp fScaledU
FLOAT fFracV; if (fScaledV < 0.5f) { // make fFrac always positive
fFracV = 0.5f-fScaledV; } else { fFracV = fScaledV - ((FLOAT)iVMask-0.5f); } fCorrectionU = -fFracV*(TCoord.fU-.5f)*0.5f; if (fFracU != 0.0f) { // At the corners of the cube, we need to blend away the
// edge correction so that it is 0 at exactly the corner
// center. This linear function does fine.
fCorrectionU *= (1.0f - fFracU); fCorrectionV *= (1.0f - fFracV); } } fScaledU += fCorrectionU; fScaledV += fCorrectionV; }
INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP );
// extract fraction bits
UINT8 uUFrac = iU&0x1f; UINT8 uVFrac = iV&0x1f;
// take floor
INT16 iU0 = iU>>5; INT16 iV0 = iV>>5;
// average to find the center texel
INT32 iUC = (uUFrac >= 0x10) ? (iU0 + 1) : (iU0); INT32 iVC = (uVFrac >= 0x10) ? (iV0 + 1) : (iV0);
// get 9 surrounding samples
// VU VU VU
RRColor Texel00, Texel01, Texel02; RRColor Texel10, Texel11, Texel12; RRColor Texel20, Texel21, Texel22; BOOL bColorKeyMatchedT = FALSE; DoEnvReMap(iUC-1, iVC-1, iUMask, iVMask, iFace, iLOD, Texel00, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC-0, iVC-1, iUMask, iVMask, iFace, iLOD, Texel01, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC+1, iVC-1, iUMask, iVMask, iFace, iLOD, Texel02, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
DoEnvReMap(iUC-1, iVC+0, iUMask, iVMask, iFace, iLOD, Texel10, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC-0, iVC+0, iUMask, iVMask, iFace, iLOD, Texel11, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC+1, iVC+0, iUMask, iVMask, iFace, iLOD, Texel12, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
DoEnvReMap(iUC-1, iVC+1, iUMask, iVMask, iFace, iLOD, Texel20, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC-0, iVC+1, iUMask, iVMask, iFace, iLOD, Texel21, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT; DoEnvReMap(iUC+1, iVC+1, iUMask, iVMask, iFace, iLOD, Texel22, bColorKeyMatchedT); m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
// sum the samples into 4 areas
RRColor TexelT00, TexelT01, TexelT10, TexelT11; TexelAvg4(TexelT00, Texel00, Texel01, Texel10, Texel11); TexelAvg4(TexelT01, Texel01, Texel02, Texel11, Texel12); TexelAvg4(TexelT10, Texel10, Texel11, Texel20, Texel21); TexelAvg4(TexelT11, Texel11, Texel12, Texel21, Texel22);
// correct the fraction to be around the center sample
uUFrac = (uUFrac + 0x10) & 0x1f; uVFrac = (uVFrac + 0x10) & 0x1f;
// use a bilerp to get the final sample
BiLerpColor( Texel, TexelT00,TexelT01, TexelT10,TexelT11, uUFrac,uVFrac); } else { // bilinear
//
// For the top LOD, if we are interpolating beyond the edge of the
// texture, correct the interpolation to minimize the artifacts seen in
// small, diffuse environment maps which tend to emphasize the edges
// of the cubemap.
//
if (iLOD == 0) { FLOAT fFracU = 0.0f; FLOAT fCorrectionU = 0.0f; FLOAT fCorrectionV = 0.0f; if ((fScaledU < 0.0f) || (fScaledU > (FLOAT)iUMask)) { // U crosses a boundary, clamp fScaledV
if (fScaledU < 0.0f) { // make fFrac always positive
fFracU = -fScaledU; } else { fFracU = fScaledU - (FLOAT)iUMask; } // 2.0/2.0 = 1.0 provides the perfect correction at the cube corner.
// This can be seen be looking at the derivative of the intersection
// of a cone and a cube at the cube corner.
fCorrectionV = -fFracU*(TCoord.fV-.5f); } if ((fScaledV < 0.0f) || (fScaledV > (FLOAT)iVMask)) { // V crosses a boundary, clamp fScaledU
FLOAT fFracV; if (fScaledV < 0.0f) { // make fFrac always positive
fFracV = -fScaledV; } else { fFracV = fScaledV - (FLOAT)iVMask; } fCorrectionU = -fFracV*(TCoord.fU-.5f); if (fFracU != 0.0f) { // At the corners of the cube, we need to blend away the
// edge correction so that it is 0 at exactly the corner
// center. This linear function does fine.
fCorrectionU *= 2.0f*(.5f - fFracU); fCorrectionV *= 2.0f*(.5f - fFracV); } } fScaledU += fCorrectionU; fScaledV += fCorrectionV; }
INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP );
// extract fraction bits
UINT8 uUFrac = iU&0x1f; UINT8 uVFrac = iV&0x1f;
// take floor for (0,0) sample coords
INT16 iU0 = iU>>5; INT16 iV0 = iV>>5; // take ceiling for (1,1) sample coords
INT16 iU1 = iU0+1; INT16 iV1 = iV0+1;
// grab four adjacent samples
RRColor Texel00, Texel01, Texel10, Texel11; BOOL bColorKeyMatched00 = FALSE; BOOL bColorKeyMatched01 = FALSE; BOOL bColorKeyMatched10 = FALSE; BOOL bColorKeyMatched11 = FALSE;
DoEnvReMap(iU0, iV0, iUMask, iVMask, iFace, iLOD, Texel00, bColorKeyMatched00); DoEnvReMap(iU1, iV0, iUMask, iVMask, iFace, iLOD, Texel01, bColorKeyMatched01); DoEnvReMap(iU0, iV1, iUMask, iVMask, iFace, iLOD, Texel10, bColorKeyMatched10); DoEnvReMap(iU1, iV1, iUMask, iVMask, iFace, iLOD, Texel11, bColorKeyMatched11);
// only set 'colorkey matched' if at least one matched value has
// a non-zero contribution (note that it is not possible for 00
// to have no contribution)
if (uUFrac == 0x00) { // 01 and 11 have zero weight if U fraction is zero
bColorKeyMatched01 = bColorKeyMatched11 = FALSE; } if (uVFrac == 0x00) { // 10 and 11 have zero weight if V fraction is zero
bColorKeyMatched10 = bColorKeyMatched11 = FALSE; }
// merge colorkey match info from previous invocation
m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched00 || bColorKeyMatched01 || bColorKeyMatched10 || bColorKeyMatched11;
// do bilinear filter
BiLerpColor( Texel, Texel00,Texel01, Texel10,Texel11, uUFrac,uVFrac); } } return Texel; }
//-----------------------------------------------------------------------------
//
// DoEnvMagnify - This is used for all magnification filter modes except
// anisotropic.
//
// Currently only POINT and BILINEAR are supported.
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvMagnify(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, RRColor& Texel ) { // do lookup, applying MAG filter
Texel = DoEnvLookup( iStage, TCoord, iFace, 0, (D3DTFG_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER]) ); }
//-----------------------------------------------------------------------------
//
// DoEnvMinify - This is used for POINT and BILINEAR modes (non-trilinear)
// for minification, and also handles POINT mip filter (nearest LOD).
//
// iLOD is n.5 fixed point
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvMinify(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, INT16 iLOD, RRColor& Texel ) { // round and drop fraction from LOD (is n.5 fixed point)
iLOD += 0x10; iLOD &= ~(0x1f); // convert to n.0
iLOD >>= 5; // clamp LOD to number of available levels
iLOD = MIN( iLOD, m_cLOD );
// do lookup, applying MIN filter
Texel = DoEnvLookup( iStage, TCoord, iFace, iLOD, (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) ); }
//-----------------------------------------------------------------------------
//
// DoEnvTrilerp - Does trilinear environment map filtering.
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvTrilerp(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, INT16 iLOD, RRColor& Texel) { // clamp LOD to number of available levels
iLOD = MIN( iLOD, (m_cLOD)<<5 ); // compute index for two adjacent LODs (with clamp)
INT16 iLODHi = iLOD>>5; // floor
INT16 iLODLo = MIN(iLODHi+1,m_cLOD);
// check for filter type for within LOD map
BOOL bNearest = (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]);
// trilerp - look up each map then lerp between them
// important for colorkey to not include texels with no contribution
if (0x00 != (iLOD&0x1f)) { // trilerp - look up each map then lerp between them
RRColor Texel0 = DoEnvLookup(iStage, TCoord, iFace, iLODHi, bNearest); RRColor Texel1 = DoEnvLookup(iStage, TCoord, iFace, iLODLo, bNearest); LerpColor( Texel, Texel0, Texel1, iLOD&0x1f ); } else { Texel = DoEnvLookup(iStage, TCoord, iFace, iLODHi, bNearest); } }
//
// uEnvEdgeTable
//
// This table looks up how to map a given U and V that are out of range
// on their primary face. The first index to the table is the current
// face. The second index is 0 if V is in range, 1 if V is negative
// and 2 if V is larger than the texture. Likewise, the last index is 0
// if U is in range, 1 if U is negative, and 2 if U is larger than
// than the texture.
//
// For the underdefined cases where 2 coordinates are out at the same time,
// we do the U wrap but not V.
//
// defines for the actions returned by the uEnvEdgeTable
//
#define EET_FACEMASK 0x07 // new face
#define EET_FU 0x10 // flip U on the texture map
#define EET_FV 0x20 // flip V on the texture map
#define EET_SUV 0x40 // swap U and V
//
// When both U and V are out, it is arbitrary which other
// face you pick. However, picking any one face other than the base
// face biases the result in visually disturbing ways. Therefore,
// take them both and average them.
//
static UINT8 uEnvEdgeTable[6][3][3] = { { // U0 NU PU // face 0
{0xff, 4, 5}, // V in range
{EET_FU|EET_SUV|2, 0xff, 0xff}, // V Neg
{EET_FV|EET_SUV|3, 0xff, 0xff}, // V too large
}, { // face 1
{0xff, 5, 4}, {EET_FV|EET_SUV|2, 0xff, 0xff}, {EET_FU|EET_SUV|3, 0xff, 0xff}, }, { // face 2
{0xff, EET_FU|EET_SUV|1, EET_FV|EET_SUV|0}, {EET_FU|EET_FV|5, 0xff, 0xff}, {4, 0xff, 0xff}, }, { // face 3
{0xff, EET_FV|EET_SUV|1, EET_FU|EET_SUV|0}, {4, 0xff, 0xff}, {EET_FU|EET_FV|5, 0xff, 0xff}, }, { // face 4
{0xff, 1, 0}, {2, 0xff, 0xff}, {3, 0xff, 0xff}, }, { // face 5
{0xff, 0, 1}, {EET_FU|EET_FV|2, 0xff, 0xff}, {EET_FU|EET_FV|3, 0xff, 0xff}, }, };
//-----------------------------------------------------------------------------
//
// DoTableInterp - Environment mapping utility.
// Interprets the edge table and does a lookup
//
//-----------------------------------------------------------------------------
void RRTexture::DoTableInterp(INT16 iU, INT16 iV, INT16 iUMask, INT16 iVMask, INT16 iFace, INT16 iLOD, UINT8 uUSign, UINT8 uVSign, RRColor &Texel, BOOL &bColorKeyMatched) { UINT8 uTable = uEnvEdgeTable[iFace][uVSign][uUSign]; _ASSERT( uTable != 0xff, "Illegal environment map lookup" ); if (uTable & EET_FU) { iU = iUMask - iU; } if (uTable & EET_FV) { iV = iVMask - iV; } if (uTable & EET_SUV) { INT16 iT = iU; iU = iV; iV = iT; } iFace = uTable & EET_FACEMASK; ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched); }
//-----------------------------------------------------------------------------
//
// DoEnvReMap - Environment mapping utility.
// Determines if either of the texture coordinates are out of range, and
// remaps the coordinate to the correct coordinate on the proper face of the
// environment cube.
//
//-----------------------------------------------------------------------------
void RRTexture::DoEnvReMap(INT16 iU, INT16 iV, INT16 iUMask, INT16 iVMask, INT16 iFace, INT16 iLOD, RRColor &Texel, BOOL &bColorKeyMatched) { UINT8 iUNeg = (UINT8)(iU < 0); UINT8 iUPos = (UINT8)(iU > iUMask); UINT8 iVNeg = (UINT8)(iV < 0); UINT8 iVPos = (UINT8)(iV > iVMask);
if (!(iUNeg || iUPos || iVNeg || iVPos)) { ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched); } else { // put all U,V's in range with wrap function
INT16 iUMasked = iU & iUMask; INT16 iVMasked = iV & iVMask; INT16 iUClampd = min(max(iU, 0), iUMask); INT16 iVClampd = min(max(iV, 0), iVMask); UINT8 uUSign = (iUNeg) | (iUPos<<1); UINT8 uVSign = (iVNeg) | (iVPos<<1); if ((uVSign != 0) && (uUSign != 0)) { // off the edge of the map in two directions. Go off each direction individually,
// and average the result.
RRColor Texel0, Texel1; DoTableInterp(iUClampd, iVMasked, iUMask, iVMask, iFace, iLOD, 0, uVSign, Texel0, bColorKeyMatched); DoTableInterp(iUMasked, iVClampd, iUMask, iVMask, iFace, iLOD, uUSign, 0, Texel1, bColorKeyMatched); LerpColor( Texel, Texel0, Texel1, 0x10 ); } else { DoTableInterp(iUMasked, iVMasked, iUMask, iVMask, iFace, iLOD, uUSign, uVSign, Texel, bColorKeyMatched); } } }
//-----------------------------------------------------------------------------
//
// RRTexture::Initialize()
//
//-----------------------------------------------------------------------------
HRESULT RRTexture::Initialize( LPDDRAWI_DDRAWSURFACE_LCL pLcl) { HRESULT hr = D3D_OK; RRSurfaceType SurfType; DDSCAPS2 ddscaps; memset(&ddscaps, 0, sizeof(ddscaps));
m_iWidth = DDSurf_Width(pLcl); m_iHeight = DDSurf_Height(pLcl); m_cLOD = 0; HR_RET(FindOutSurfFormat(&(DDSurf_PixFmt(pLcl)), &SurfType));
if ((SurfType == RR_STYPE_DXT1) || (SurfType == RR_STYPE_DXT2) || (SurfType == RR_STYPE_DXT3) || (SurfType == RR_STYPE_DXT4) || (SurfType == RR_STYPE_DXT5)) { // Note, here is the assumption that:
// 1) width and height are reported correctly by the driver that
// created the surface
// 2) The allocation of the memory is contiguous (as done by hel)
m_iPitch[0] = ((m_iWidth+3)>>2) * g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1]; } else { m_iPitch[0] = DDSurf_Pitch(pLcl); } m_SurfType = SurfType;
if (SurfType == RR_STYPE_PALETTE8 || SurfType == RR_STYPE_PALETTE4) { if (pLcl->lpDDPalette) { LPDDRAWI_DDRAWPALETTE_GBL pPal = pLcl->lpDDPalette->lpLcl->lpGbl; m_pPalette = (DWORD*)pPal->lpColorTable; if (pPal->dwFlags & DDRAWIPAL_ALPHA) { m_uFlags |= RR_TEXTURE_ALPHAINPALETTE; } else { m_uFlags &= ~RR_TEXTURE_ALPHAINPALETTE; } } }
if (!ValidTextureSize((INT16)m_iWidth, (INT16)IntLog2(m_iWidth), (INT16)m_iHeight, (INT16)IntLog2(m_iHeight))) { return DDERR_INVALIDPARAMS; }
if (pLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP) { m_uFlags |= RR_TEXTURE_ENVMAP;
LPDDRAWI_DDRAWSURFACE_LCL pDDSNextLcl; ddscaps.dwCaps = DDSCAPS_TEXTURE;
if (!(pLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX)) { ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP|DDSCAPS2_CUBEMAP_POSITIVEX;
hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pDDSNextLcl);
if ((hr != D3D_OK) && (hr != DDERR_NOTFOUND)) { return hr; } if (hr == DDERR_NOTFOUND) { m_pDDSLcl[0] = NULL; } else { // use POSITIVEX surface to query others, if it exists
pLcl = pDDSNextLcl; m_pDDSLcl[0] = pLcl; } } else { m_pDDSLcl[0] = pLcl; }
// get rest of top level surfaces, in order
for (INT i = 1; i < 6; i++) { switch(i) { case 1: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEX; break; case 2: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEY; break; case 3: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEY; break; case 4: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEZ; break; case 5: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEZ; break; } ddscaps.dwCaps2 |= DDSCAPS2_CUBEMAP; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pDDSNextLcl); if ((hr != D3D_OK) && (hr != DDERR_NOTFOUND)) { return hr; } if (hr == DDERR_NOTFOUND) { m_pDDSLcl[i] = NULL; } else { m_pDDSLcl[i] = pDDSNextLcl; } }
for (i = 0; i < 6; i++) { pLcl = m_pDDSLcl[i]; m_cLOD = 0;
if (pLcl) { // Check for mipmap if any.
LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl;
// iPreSizeU and iPreSizeV store the size(u and v) of the previous level
// mipmap. They are init'ed with the first texture size.
INT16 iPreSizeU = (INT16)m_iWidth, iPreSizeV = (INT16)m_iHeight; for (;;) { ddscaps.dwCaps = DDSCAPS_TEXTURE; ddscaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl); if (hr != D3D_OK && hr != DDERR_NOTFOUND) { return hr; } if (hr == DDERR_NOTFOUND) { break; } m_cLOD ++; pLcl = pTmpSLcl; m_pDDSLcl[m_cLOD*6 + i] = pLcl; if ((SurfType == RR_STYPE_DXT1) || (SurfType == RR_STYPE_DXT2) || (SurfType == RR_STYPE_DXT3) || (SurfType == RR_STYPE_DXT4) || (SurfType == RR_STYPE_DXT5)) { // Note, here is the assumption that:
// 1) width and height are reported correctly by the driver that
// created the surface
// 2) The allocation of the memory is contiguous (as done by ddhel)
m_iPitch[m_cLOD] = (((m_iWidth>>m_cLOD)+3)>>2) * g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1]; } else { m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl); } m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl); // Check for invalid mipmap texture size
if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) || !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl))) { return DDERR_INVALIDPARAMS; } iPreSizeU = (INT16)DDSurf_Width(pLcl); iPreSizeV = (INT16)DDSurf_Height(pLcl); } } } } else { if (pLcl->ddsCaps.dwCaps & DDSCAPS_ZBUFFER) { m_uFlags |= RR_TEXTURE_SHADOWMAP; }
m_pDDSLcl[0] = pLcl; // Check for mipmap if any.
LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl; // iPreSizeU and iPreSizeV store the size(u and v) of the previous
// level mipmap. They are init'ed with the first texture size.
INT16 iPreSizeU = (INT16)m_iWidth, iPreSizeV = (INT16)m_iHeight; for (;;) { ddscaps.dwCaps = DDSCAPS_TEXTURE; hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl); if (hr != D3D_OK && hr != DDERR_NOTFOUND) { return hr; } if (hr == DDERR_NOTFOUND) { break; } m_cLOD ++; pLcl = pTmpSLcl; m_pDDSLcl[m_cLOD] = pLcl; if ((SurfType == RR_STYPE_DXT1) || (SurfType == RR_STYPE_DXT2) || (SurfType == RR_STYPE_DXT3) || (SurfType == RR_STYPE_DXT4) || (SurfType == RR_STYPE_DXT5)) { // Note, here is the assumption that:
// 1) width and height are reported correctly by the driver that
// created the surface
// 2) The allocation of the memory is contiguous (as done by ddhel)
m_iPitch[m_cLOD] = (((m_iWidth>>m_cLOD)+3)>>2) * g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1]; } else { m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl); } m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl); // Check for invalid mipmap texture size
if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) || !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl))) { return DDERR_INVALIDPARAMS; } iPreSizeU = (INT16)DDSurf_Width(pLcl); iPreSizeV = (INT16)DDSurf_Height(pLcl); } }
m_cLODDDS = m_cLOD;
if ( !(Validate()) ) { return DDERR_GENERIC; }
return D3D_OK; }
//-----------------------------------------------------------------------------
//
// Computes level of detail for environment mapping, looks better if
// we err on the side of fuzziness.
//
//-----------------------------------------------------------------------------
void ComputeEnvMapLevelOfDetail( const RRTextureCoord& TCoord, FLOAT& fLOD ) { // compute length of coverage in U and V axis
FLOAT fLenX = RR_LENGTH( TCoord.fDUDX, TCoord.fDVDX ); FLOAT fLenY = RR_LENGTH( TCoord.fDUDY, TCoord.fDVDY );
// take max
FLOAT fCoverage = MAX(fLenX, fLenY);
// take log2 of coverage for LOD
fLOD = RR_LOG2(fCoverage); }
//-----------------------------------------------------------------------------
//
// RRTexture::DoTextureTransform - Performs the homogeneous texture transform.
//
//-----------------------------------------------------------------------------
void RRTexture::DoTextureTransform( INT32 iStage, BOOL bAlreadyXfmd, FLOAT* pfC, FLOAT* pfO, FLOAT* pfQ ) { LPD3DMATRIX pM = (LPD3DMATRIX)&m_pStageState[iStage].m_dwVal[D3DTSSI_MATRIX]; DWORD dwFlags = m_pStageState[iStage].m_dwVal[D3DTSS_TEXTURETRANSFORMFLAGS]; DWORD dwCount = dwFlags & (D3DTTFF_PROJECTED-1); pfO[0] = pfC[0]; pfO[1] = pfC[1]; pfO[2] = pfC[2]; pfO[3] = pfC[3]; *pfQ = 1.0f; if (dwCount != D3DTTFF_DISABLE) { if( bAlreadyXfmd == FALSE ) { FLOAT x = pfC[0]; FLOAT y = pfC[1]; FLOAT z = pfC[2]; FLOAT w = pfC[3];
pfO[0] = x*pM->_11 + y*pM->_21 + z*pM->_31 + w*pM->_41; pfO[1] = x*pM->_12 + y*pM->_22 + z*pM->_32 + w*pM->_42; pfO[2] = x*pM->_13 + y*pM->_23 + z*pM->_33 + w*pM->_43; pfO[3] = x*pM->_14 + y*pM->_24 + z*pM->_34 + w*pM->_44; }
if (dwFlags & D3DTTFF_PROJECTED) { DWORD dwQI = dwCount - 1;
_ASSERT((dwQI >= 1)&&(dwQI <= 3), "Illegal D3DTTFF_COUNT with D3DTTFF_PROJECTED");
*pfQ = pfO[dwQI]; } } }
///////////////////////////////////////////////////////////////////////////////
// end
|