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