Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2017 lines
74 KiB

///////////////////////////////////////////////////////////////////////////////
// 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