mirror of https://github.com/tongzx/nt5src
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.
612 lines
25 KiB
612 lines
25 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Microsoft Corporation, 2000.
|
|
//
|
|
// texfilt.cpp
|
|
//
|
|
// Direct3D Reference Device - Texture Map Filtering Methods
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.cpp"
|
|
#pragma hdrstop
|
|
|
|
void
|
|
RefRast::UpdateTextureControls( void )
|
|
{
|
|
for (int iStage=0; iStage<m_pRD->m_cActiveTextureStages; iStage++)
|
|
{
|
|
// check for requirement to do level-of-detail (coverage) computation - either
|
|
// for mipmap or per-pixel filter selection
|
|
BOOL bComputeLOD =
|
|
( m_pRD->GetTSS(iStage)[D3DTSS_MIPFILTER] == D3DTEXF_POINT ) ||
|
|
( m_pRD->GetTSS(iStage)[D3DTSS_MIPFILTER] == D3DTEXF_LINEAR ) ||
|
|
( m_pRD->GetTSS(iStage)[D3DTSS_MAGFILTER] != m_pRD->GetTSS(iStage)[D3DTSS_MINFILTER] );
|
|
|
|
// check for anisotropic filtering in either mag filter or in min filter
|
|
BOOL bDoAniso =
|
|
( D3DTEXF_ANISOTROPIC == m_pRD->GetTSS(iStage)[D3DTSS_MAGFILTER] ) ||
|
|
( bComputeLOD && (D3DTEXF_ANISOTROPIC == m_pRD->GetTSS(iStage)[D3DTSS_MINFILTER]) );
|
|
|
|
// compute filter type for coverage computation
|
|
if (bDoAniso) m_TexFlt[iStage].CvgFilter = D3DTEXF_ANISOTROPIC;
|
|
else if (bComputeLOD) m_TexFlt[iStage].CvgFilter = D3DTEXF_LINEAR;
|
|
else m_TexFlt[iStage].CvgFilter = D3DTEXF_NONE;
|
|
|
|
// compute filter type for magnify (also used for non-LOD case)
|
|
switch ( m_pRD->GetTSS(iStage)[D3DTSS_MAGFILTER] )
|
|
{
|
|
default:
|
|
case D3DTEXF_POINT: m_TexFlt[iStage].MagFilter = D3DTEXF_POINT; break;
|
|
case D3DTEXF_FLATCUBIC:
|
|
case D3DTEXF_GAUSSIANCUBIC:
|
|
case D3DTEXF_LINEAR: m_TexFlt[iStage].MagFilter = D3DTEXF_LINEAR; break;
|
|
case D3DTEXF_ANISOTROPIC: m_TexFlt[iStage].MagFilter = D3DTEXF_ANISOTROPIC; break;
|
|
}
|
|
|
|
// compute filter type(s) for minify
|
|
switch ( m_pRD->GetTSS(iStage)[D3DTSS_MINFILTER] )
|
|
{
|
|
default:
|
|
case D3DTEXF_POINT: m_TexFlt[iStage].MinFilter = D3DTEXF_POINT; break;
|
|
case D3DTEXF_LINEAR: m_TexFlt[iStage].MinFilter = D3DTEXF_LINEAR; break;
|
|
case D3DTEXF_ANISOTROPIC: m_TexFlt[iStage].MinFilter = D3DTEXF_ANISOTROPIC; break;
|
|
}
|
|
|
|
switch ( m_pRD->GetTSS(iStage)[D3DTSS_MIPFILTER] )
|
|
{
|
|
default:
|
|
case D3DTEXF_NONE: m_TexFlt[iStage].MipFilter = D3DTEXF_NONE; break;
|
|
case D3DTEXF_POINT: m_TexFlt[iStage].MipFilter = D3DTEXF_POINT; break;
|
|
case D3DTEXF_LINEAR: m_TexFlt[iStage].MipFilter = D3DTEXF_LINEAR; break;
|
|
}
|
|
|
|
// set default state
|
|
m_TexCvg[iStage].fLOD = 0.f;
|
|
m_TexCvg[iStage].iLOD = 0;
|
|
m_TexCvg[iStage].iLODMap[0] = 0;
|
|
m_TexCvg[iStage].iLODMap[1] = 0;
|
|
m_TexCvg[iStage].fLODFrc[0] = 1.f;
|
|
m_TexCvg[iStage].fLODFrc[1] = 1.f;
|
|
m_TexCvg[iStage].bMagnify = FALSE;
|
|
m_TexCvg[iStage].cLOD = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// called once per each set of 2x2 samples
|
|
//
|
|
void
|
|
RefRast::ComputeTextureCoverage( int iStage, FLOAT (*fGradients)[2] )
|
|
{
|
|
if ( !m_pRD->m_pTexture[iStage] ) return;
|
|
if ( m_pRD->m_pTexture[iStage]->m_uFlags & RR_TEXTURE_CUBEMAP )
|
|
{
|
|
// store gradients for cubemaps
|
|
memcpy( m_TexCvg[iStage].fGradients, fGradients, 3*2*sizeof(FLOAT) );
|
|
return;
|
|
}
|
|
|
|
if ( D3DTEXF_NONE == m_TexFlt[iStage].CvgFilter ) return;
|
|
|
|
// scale gradients to texture LOD 0 size
|
|
for (int iD=0; iD < m_pRD->m_pTexture[iStage]->m_cDimension; iD++ )
|
|
{
|
|
fGradients[iD][0] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][iD];
|
|
fGradients[iD][1] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][iD];
|
|
}
|
|
|
|
if ( (m_TexFlt[iStage].CvgFilter == D3DTEXF_ANISOTROPIC) &&
|
|
(m_pRD->m_pTexture[iStage]->m_cDimension == 2) ) // do aniso for 2D textures only
|
|
{
|
|
ComputeAnisoCoverage( fGradients, MIN( 16.f, (FLOAT)m_pRD->GetTSS(iStage)[D3DTSS_MAXANISOTROPY]),
|
|
m_TexCvg[iStage].fLOD, m_TexCvg[iStage].fAnisoRatio, m_TexCvg[iStage].fAnisoLine );
|
|
}
|
|
else
|
|
{
|
|
ComputeMipCoverage( fGradients, m_TexCvg[iStage].fLOD, m_pRD->m_pTexture[iStage]->m_cDimension );
|
|
m_TexCvg[iStage].fAnisoRatio = 1.f;
|
|
}
|
|
|
|
ComputePerLODControls( iStage );
|
|
}
|
|
|
|
//
|
|
// called by ComputeTextureCoverage and ComputeCubeTextureFilter
|
|
//
|
|
void
|
|
RefRast::ComputePerLODControls( int iStage )
|
|
{
|
|
m_TexCvg[iStage].fLOD += m_pRD->GetTSSf(iStage)[D3DTSS_MIPMAPLODBIAS];
|
|
m_TexCvg[iStage].iLOD = AS_INT16( m_TexCvg[iStage].fLOD + FLOAT_5_SNAP );
|
|
m_TexCvg[iStage].bMagnify = (m_TexCvg[iStage].iLOD <= 0);
|
|
|
|
m_TexCvg[iStage].cLOD = 1;
|
|
m_TexCvg[iStage].fLODFrc[0] = 1.f;
|
|
if ( m_TexCvg[iStage].bMagnify || ( m_TexFlt[iStage].MipFilter == D3DTEXF_NONE ) )
|
|
{
|
|
m_TexCvg[iStage].iLODMap[0] = 0;
|
|
// clamp to max LOD
|
|
m_TexCvg[iStage].iLODMap[0] = MAX( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->GetTSS(iStage)[D3DTSS_MAXMIPLEVEL] );
|
|
// clamp to available maps
|
|
m_TexCvg[iStage].iLODMap[0] = MIN( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->m_pTexture[iStage]->m_cLOD );
|
|
}
|
|
else if ( m_TexFlt[iStage].MipFilter == D3DTEXF_POINT )
|
|
{
|
|
// round and truncate (add .5 and shift off fractional bits)
|
|
m_TexCvg[iStage].iLODMap[0] = (m_TexCvg[iStage].iLOD + (1<<(RRTEX_LODFRAC-1))) >> RRTEX_LODFRAC;
|
|
// clamp to max LOD
|
|
m_TexCvg[iStage].iLODMap[0] = MAX( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->GetTSS(iStage)[D3DTSS_MAXMIPLEVEL] );
|
|
// clamp to available maps
|
|
m_TexCvg[iStage].iLODMap[0] = MIN( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->m_pTexture[iStage]->m_cLOD );
|
|
}
|
|
else // mip filter D3DTEXF_LINEAR
|
|
{
|
|
// compute index for two adjacent LODs
|
|
m_TexCvg[iStage].iLODMap[0] = m_TexCvg[iStage].iLOD >> RRTEX_LODFRAC; // floor
|
|
m_TexCvg[iStage].iLODMap[1] = m_TexCvg[iStage].iLODMap[0] + 1;
|
|
// clamp to max LOD
|
|
m_TexCvg[iStage].iLODMap[0] = MAX( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->GetTSS(iStage)[D3DTSS_MAXMIPLEVEL] );
|
|
m_TexCvg[iStage].iLODMap[1] = MAX( m_TexCvg[iStage].iLODMap[1], (INT32)m_pRD->GetTSS(iStage)[D3DTSS_MAXMIPLEVEL] );
|
|
// clamp to available maps
|
|
m_TexCvg[iStage].iLODMap[0] = MIN( m_TexCvg[iStage].iLODMap[0], (INT32)m_pRD->m_pTexture[iStage]->m_cLOD );
|
|
m_TexCvg[iStage].iLODMap[1] = MIN( m_TexCvg[iStage].iLODMap[1], (INT32)m_pRD->m_pTexture[iStage]->m_cLOD );
|
|
|
|
// check that both maps actually contribute to texel
|
|
if ( (m_TexCvg[iStage].iLODMap[0] != m_TexCvg[iStage].iLODMap[1]) &&
|
|
(m_TexCvg[iStage].iLOD & RRTEX_LODFRACMASK) )
|
|
{
|
|
m_TexCvg[iStage].fLODFrc[1] = (FLOAT)(m_TexCvg[iStage].iLOD & RRTEX_LODFRACMASK) * RRTEX_LODFRACF;
|
|
m_TexCvg[iStage].fLODFrc[0] = 1.f - m_TexCvg[iStage].fLODFrc[1];
|
|
m_TexCvg[iStage].cLOD = 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
RefRast::ComputePointSampleCoords(
|
|
int iStage, INT32 iLOD, FLOAT fCrd[],
|
|
INT32 iCrd[] )
|
|
{
|
|
for (int iD=0; iD<m_pRD->m_pTexture[iStage]->m_cDimension; iD++)
|
|
{
|
|
FLOAT fScaledCrd =
|
|
( fCrd[iD] * m_pRD->m_pTexture[iStage]->m_fTexels[iLOD][iD] ) - .5f;
|
|
// 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 ( fCrd[iD] >= 0.f ) iCrd[iD] = (INT32)( fScaledCrd + .5f );
|
|
else iCrd[iD] = (INT32)( fScaledCrd - .5f );
|
|
}
|
|
}
|
|
|
|
void
|
|
RefRast::ComputeLinearSampleCoords(
|
|
int iStage, INT32 iLOD, FLOAT fCrd[],
|
|
INT32 iCrdFlr[], INT32 iCrdClg[], FLOAT fCrdFrcF[], FLOAT fCrdFrcC[] )
|
|
{
|
|
for (int iD=0; iD<m_pRD->m_pTexture[iStage]->m_cDimension; iD++)
|
|
{
|
|
FLOAT fScaledCrd =
|
|
( fCrd[iD] * m_pRD->m_pTexture[iStage]->m_fTexels[iLOD][iD] ) - .5f;
|
|
INT32 iCrd = FloatToNdot5(fScaledCrd);
|
|
iCrdFlr[iD] = iCrd >> RRTEX_MAPFRAC;
|
|
iCrdClg[iD] = iCrdFlr[iD] + 1;
|
|
fCrdFrcC[iD] = (FLOAT)(iCrd & RRTEX_MAPFRACMASK) * RRTEX_MAPFRACF;
|
|
fCrdFrcF[iD] = 1.f - fCrdFrcC[iD];
|
|
}
|
|
}
|
|
|
|
void
|
|
RefRast::SetUp1DTextureSample(
|
|
int iStage, int Start,
|
|
INT32 iLODMap, FLOAT fLODScale,
|
|
INT32 iCrdF, INT32 iCrdC,
|
|
FLOAT fCrdFrcF, FLOAT fCrdFrcC )
|
|
{
|
|
m_TexFlt[iStage].pSamples[Start+0].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+1].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+0].iCrd[0] = iCrdF;
|
|
m_TexFlt[iStage].pSamples[Start+1].iCrd[0] = iCrdC;
|
|
m_TexFlt[iStage].pSamples[Start+0].fWgt = fCrdFrcF*fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+1].fWgt = fCrdFrcC*fLODScale;
|
|
}
|
|
|
|
#define _Set2( _DstAr, _Src0, _Src1 ) \
|
|
_DstAr[0] = _Src0; _DstAr[1] = _Src1;
|
|
|
|
void
|
|
RefRast::SetUp2DTextureSample(
|
|
int iStage, int Start,
|
|
INT32 iLODMap, FLOAT fLODScale,
|
|
INT32 iCrdF[], INT32 iCrdC[],
|
|
FLOAT fCrdFrcF[], FLOAT fCrdFrcC[] )
|
|
{
|
|
m_TexFlt[iStage].pSamples[Start+0].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+1].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+2].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+3].iLOD = iLODMap;
|
|
_Set2( m_TexFlt[iStage].pSamples[Start+0].iCrd, iCrdF[0], iCrdF[1] )
|
|
_Set2( m_TexFlt[iStage].pSamples[Start+1].iCrd, iCrdC[0], iCrdF[1] )
|
|
_Set2( m_TexFlt[iStage].pSamples[Start+2].iCrd, iCrdC[0], iCrdC[1] )
|
|
_Set2( m_TexFlt[iStage].pSamples[Start+3].iCrd, iCrdF[0], iCrdC[1] )
|
|
m_TexFlt[iStage].pSamples[Start+0].fWgt = fCrdFrcF[0] * fCrdFrcF[1] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+1].fWgt = fCrdFrcC[0] * fCrdFrcF[1] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+2].fWgt = fCrdFrcC[0] * fCrdFrcC[1] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+3].fWgt = fCrdFrcF[0] * fCrdFrcC[1] * fLODScale;
|
|
}
|
|
|
|
#define _Set3( _DstAr, _Src0, _Src1, _Src2 ) \
|
|
_DstAr[0] = _Src0; _DstAr[1] = _Src1; _DstAr[2] = _Src2;
|
|
|
|
void
|
|
RefRast::SetUp3DTextureSample(
|
|
int iStage, int Start,
|
|
INT32 iLODMap, FLOAT fLODScale,
|
|
INT32 iCrdF[], INT32 iCrdC[],
|
|
FLOAT fCrdFrcF[], FLOAT fCrdFrcC[] )
|
|
{
|
|
m_TexFlt[iStage].pSamples[Start+0].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+1].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+2].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+3].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+4].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+5].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+6].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+7].iLOD = iLODMap;
|
|
m_TexFlt[iStage].pSamples[Start+0].fWgt = fCrdFrcF[0] * fCrdFrcF[1] * fCrdFrcF[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+1].fWgt = fCrdFrcC[0] * fCrdFrcF[1] * fCrdFrcF[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+2].fWgt = fCrdFrcC[0] * fCrdFrcC[1] * fCrdFrcF[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+3].fWgt = fCrdFrcF[0] * fCrdFrcC[1] * fCrdFrcF[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+4].fWgt = fCrdFrcF[0] * fCrdFrcF[1] * fCrdFrcC[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+5].fWgt = fCrdFrcC[0] * fCrdFrcF[1] * fCrdFrcC[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+6].fWgt = fCrdFrcC[0] * fCrdFrcC[1] * fCrdFrcC[2] * fLODScale;
|
|
m_TexFlt[iStage].pSamples[Start+7].fWgt = fCrdFrcF[0] * fCrdFrcC[1] * fCrdFrcC[2] * fLODScale;
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+0].iCrd, iCrdF[0], iCrdF[1], iCrdF[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+1].iCrd, iCrdC[0], iCrdF[1], iCrdF[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+2].iCrd, iCrdC[0], iCrdC[1], iCrdF[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+3].iCrd, iCrdF[0], iCrdC[1], iCrdF[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+4].iCrd, iCrdF[0], iCrdF[1], iCrdC[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+5].iCrd, iCrdC[0], iCrdF[1], iCrdC[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+6].iCrd, iCrdC[0], iCrdC[1], iCrdC[2] )
|
|
_Set3( m_TexFlt[iStage].pSamples[Start+7].iCrd, iCrdF[0], iCrdC[1], iCrdC[2] )
|
|
}
|
|
|
|
//
|
|
// called once for each pixel
|
|
//
|
|
void
|
|
RefRast::ComputeTextureFilter( int iStage, FLOAT fCrd[] )
|
|
{
|
|
m_TexFlt[iStage].cSamples = 0;
|
|
if ( !m_pRD->m_pTexture[iStage] ) return;
|
|
|
|
if ( m_pRD->m_pTexture[iStage]->m_uFlags & RR_TEXTURE_CUBEMAP )
|
|
{
|
|
ComputeCubeTextureFilter( iStage, fCrd );
|
|
return;
|
|
}
|
|
// here for 1,2,3D texture
|
|
int iL,iD;
|
|
#define _PerDimension(_Par) for (_Par=0;_Par<m_pRD->m_pTexture[iStage]->m_cDimension;_Par++)
|
|
D3DTEXTUREFILTERTYPE Filter =
|
|
m_TexCvg[iStage].bMagnify ? m_TexFlt[iStage].MagFilter : m_TexFlt[iStage].MinFilter;
|
|
switch ( Filter )
|
|
{
|
|
default:
|
|
case D3DTEXF_POINT:
|
|
for ( iL = 0; iL < m_TexCvg[iStage].cLOD; iL++ )
|
|
{
|
|
m_TexFlt[iStage].pSamples[m_TexFlt[iStage].cSamples].iLOD = m_TexCvg[iStage].iLODMap[iL];
|
|
m_TexFlt[iStage].pSamples[m_TexFlt[iStage].cSamples].fWgt = m_TexCvg[iStage].fLODFrc[iL];
|
|
ComputePointSampleCoords( iStage, m_TexCvg[iStage].iLODMap[iL], fCrd,
|
|
m_TexFlt[iStage].pSamples[m_TexFlt[iStage].cSamples].iCrd );
|
|
m_TexFlt[iStage].cSamples++;
|
|
}
|
|
break;
|
|
|
|
case D3DTEXF_LINEAR:
|
|
for ( iL = 0; iL < m_TexCvg[iStage].cLOD; iL++ )
|
|
{
|
|
INT32 iCrdFlr[3], iCrdClg[3];
|
|
FLOAT fCrdFrcF[3], fCrdFrcC[3];
|
|
ComputeLinearSampleCoords(
|
|
iStage, m_TexCvg[iStage].iLODMap[iL], fCrd,
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
switch ( m_pRD->m_pTexture[iStage]->m_cDimension )
|
|
{
|
|
default:
|
|
case 1:
|
|
SetUp1DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], m_TexCvg[iStage].fLODFrc[iL],
|
|
iCrdFlr[0], iCrdClg[0], fCrdFrcF[0], fCrdFrcC[0] );
|
|
m_TexFlt[iStage].cSamples += 2;
|
|
break;
|
|
case 2:
|
|
SetUp2DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], m_TexCvg[iStage].fLODFrc[iL],
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
m_TexFlt[iStage].cSamples += 4;
|
|
break;
|
|
case 3:
|
|
SetUp3DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], m_TexCvg[iStage].fLODFrc[iL],
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
m_TexFlt[iStage].cSamples += 8;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case D3DTEXF_ANISOTROPIC:
|
|
for ( iL = 0; iL < m_TexCvg[iStage].cLOD; iL++ )
|
|
{
|
|
FLOAT fStepScale[3];
|
|
fStepScale[0] = 1.f/m_pRD->m_pTexture[iStage]->m_fTexels[m_TexCvg[iStage].iLODMap[iL]][0];
|
|
fStepScale[1] = 1.f/m_pRD->m_pTexture[iStage]->m_fTexels[m_TexCvg[iStage].iLODMap[iL]][1];
|
|
fStepScale[2] = 0.f;
|
|
|
|
FLOAT fUnitStep[3];
|
|
_PerDimension(iD) { fUnitStep[iD] = fStepScale[iD]*m_TexCvg[iStage].fAnisoLine[iD]; }
|
|
|
|
int cAnisoSamples;
|
|
FLOAT fACrd[16][3];
|
|
FLOAT fAScale[16];
|
|
if ( m_TexCvg[iStage].fAnisoRatio <= 1.f )
|
|
{
|
|
// just like mip D3DTEXF_LINEAR
|
|
cAnisoSamples = 1; fAScale[0] = 1.f;
|
|
_PerDimension(iD) { fACrd[0][iD] = fCrd[iD]; }
|
|
}
|
|
else if ( m_TexCvg[iStage].fAnisoRatio <= 2.f )
|
|
{
|
|
// take two sets of samples and average
|
|
cAnisoSamples = 2; fAScale[0] = fAScale[1] = .5f;
|
|
FLOAT fStepSize = .5f*(m_TexCvg[iStage].fAnisoRatio - 1.f);
|
|
_PerDimension(iD)
|
|
{
|
|
FLOAT fStep = fStepSize*fUnitStep[iD];
|
|
fACrd[0][iD] = fCrd[iD] + fStep;
|
|
fACrd[1][iD] = fCrd[iD] - fStep;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// walk line of anisotropy in both directions from center point
|
|
FLOAT fInvRatio = 1.f/m_TexCvg[iStage].fAnisoRatio;
|
|
FLOAT fRatioRemainder = m_TexCvg[iStage].fAnisoRatio;
|
|
// start steps centered 1/2 away
|
|
_PerDimension(iD)
|
|
{
|
|
fACrd[0][iD] = fCrd[iD] + fUnitStep[iD]*.5f;
|
|
fACrd[1][iD] = fCrd[iD] - fUnitStep[iD]*.5f;
|
|
}
|
|
cAnisoSamples = 0;
|
|
do
|
|
{
|
|
fAScale[cAnisoSamples+0] = fInvRatio;
|
|
fAScale[cAnisoSamples+1] = fInvRatio;
|
|
if ( fRatioRemainder < 2.f )
|
|
{
|
|
fAScale[cAnisoSamples+0] *= .5f*fRatioRemainder;
|
|
fAScale[cAnisoSamples+1] *= .5f*fRatioRemainder;
|
|
}
|
|
if ( fRatioRemainder > 2.f )
|
|
{
|
|
_PerDimension(iD)
|
|
{
|
|
fACrd[cAnisoSamples+2][iD] = fACrd[cAnisoSamples+0][iD] + fUnitStep[iD];
|
|
fACrd[cAnisoSamples+3][iD] = fACrd[cAnisoSamples+1][iD] - fUnitStep[iD];
|
|
}
|
|
}
|
|
cAnisoSamples += 2;
|
|
fRatioRemainder -= 2.f;
|
|
}
|
|
while ( fRatioRemainder > 0.f );
|
|
}
|
|
for ( int iS = 0; iS < cAnisoSamples; iS ++ )
|
|
{
|
|
INT32 iCrdFlr[3], iCrdClg[3];
|
|
FLOAT fCrdFrcF[3], fCrdFrcC[3];
|
|
ComputeLinearSampleCoords(
|
|
iStage, m_TexCvg[iStage].iLODMap[iL], fACrd[iS],
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
FLOAT fSampleScale = fAScale[iS]*m_TexCvg[iStage].fLODFrc[iL];
|
|
switch ( m_pRD->m_pTexture[iStage]->m_cDimension )
|
|
{
|
|
default:
|
|
case 1:
|
|
SetUp1DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], fSampleScale,
|
|
iCrdFlr[0], iCrdClg[0], fCrdFrcF[0], fCrdFrcC[0] );
|
|
m_TexFlt[iStage].cSamples += 2;
|
|
break;
|
|
case 2:
|
|
SetUp2DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], fSampleScale,
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
m_TexFlt[iStage].cSamples += 4;
|
|
break;
|
|
case 3:
|
|
SetUp3DTextureSample( iStage, m_TexFlt[iStage].cSamples, m_TexCvg[iStage].iLODMap[iL], fSampleScale,
|
|
iCrdFlr, iCrdClg, fCrdFrcF, fCrdFrcC );
|
|
m_TexFlt[iStage].cSamples += 8;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
const DWORD g_D3DTSS_ADDRESS_MAP[3] = { D3DTSS_ADDRESSU, D3DTSS_ADDRESSV, D3DTSS_ADDRESSW };
|
|
|
|
void
|
|
RefRast::SampleTexture( INT32 iStage, FLOAT fCol[] )
|
|
{
|
|
if ( m_pRD->m_pTexture[iStage] == NULL )
|
|
{
|
|
// return opaque black if no texture bound
|
|
fCol[0] = fCol[1] = fCol[2] = 0.f;
|
|
fCol[3] = 1.f;
|
|
return;
|
|
}
|
|
fCol[0] = fCol[1] = fCol[2] = fCol[3] = 0.f;
|
|
TextureSample* pS = m_TexFlt[iStage].pSamples;
|
|
RDSurface2D* pTex = m_pRD->m_pTexture[iStage];
|
|
for (int iS = 0; iS < m_TexFlt[iStage].cSamples; iS++, pS++ )
|
|
{
|
|
if ( pS->fWgt )
|
|
{
|
|
BOOL bUseBorder = FALSE;
|
|
for (int iD=0; iD < pTex->m_cDimension; iD++)
|
|
{
|
|
INT32 iCrdMax = (pTex->m_cTexels[pS->iLOD][iD] - 1);
|
|
if ( ( pS->iCrd[iD] < 0) || ( pS->iCrd[iD] > iCrdMax ) )
|
|
{
|
|
switch ( m_pRD->GetTSS(iStage)[g_D3DTSS_ADDRESS_MAP[iD]] )
|
|
{
|
|
case D3DTADDRESS_WRAP:
|
|
// Pow-2 texture:
|
|
// pS->iCrd[iD] = pS->iCrd[iD] & iCrdMax;
|
|
|
|
// Non-Pow-2 texture:
|
|
pS->iCrd[iD] %= (iCrdMax + 1);
|
|
if( pS->iCrd[iD] < 0 )
|
|
pS->iCrd[iD] = iCrdMax + 1 + pS->iCrd[iD];
|
|
break;
|
|
case D3DTADDRESS_MIRROR:
|
|
// Pow-2 texture:
|
|
// lop off non-fractional bits + flip index if LSB (non-fraction) is set
|
|
// BOOL bFlip; bFlip = pS->iCrd[iD] & (iCrdMax+1);
|
|
// pS->iCrd[iD] &= iCrdMax;
|
|
// if (bFlip) { pS->iCrd[iD] = iCrdMax - pS->iCrd[iD]; }
|
|
|
|
// Non-Pow-2 texture:
|
|
if( pS->iCrd[iD] < 0 )
|
|
pS->iCrd[iD] = -pS->iCrd[iD] - 1;
|
|
BOOL bFlip; bFlip = ((pS->iCrd[iD]/(iCrdMax + 1)) & 1);
|
|
pS->iCrd[iD] %= (iCrdMax + 1);
|
|
if( bFlip ) pS->iCrd[iD] = iCrdMax - pS->iCrd[iD];
|
|
|
|
break;
|
|
case D3DTADDRESS_BORDER:
|
|
bUseBorder = TRUE;
|
|
break;
|
|
case D3DTADDRESS_MIRRORONCE:
|
|
if ( pS->iCrd[iD] < 0 ) pS->iCrd[iD] = (-pS->iCrd[iD]) - 1;
|
|
// fall through to clamp for outside of -1 to +1 range
|
|
case D3DTADDRESS_CLAMP:
|
|
pS->iCrd[iD] = MAX( 0, MIN( pS->iCrd[iD], iCrdMax ) );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
RDColor Texel;
|
|
(bUseBorder)
|
|
? Texel = m_pRD->GetTSS(iStage)[D3DTSS_BORDERCOLOR]
|
|
: pTex->ReadColor(
|
|
pS->iCrd[0], pS->iCrd[1], pS->iCrd[2], pS->iLOD,
|
|
Texel, m_bPixelDiscard[m_iPix] );
|
|
|
|
fCol[0] += ( Texel.R * pS->fWgt );
|
|
fCol[1] += ( Texel.G * pS->fWgt );
|
|
fCol[2] += ( Texel.B * pS->fWgt );
|
|
fCol[3] += ( Texel.A * pS->fWgt );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// 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 per-dimensional extent of the approximate pixel coverage area. The
|
|
// max of lengths are used for the single LOD result.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
ComputeMipCoverage( const FLOAT (*fGradients)[2], FLOAT& fLOD, int cDim )
|
|
{
|
|
// compute length of coverage in each dimension
|
|
FLOAT fLen[2];
|
|
switch (cDim)
|
|
{
|
|
default:
|
|
case 1: fLOD = 0.f; return;
|
|
case 2:
|
|
fLen[0] = RR_LENGTH( fGradients[0][0], fGradients[1][0] );
|
|
fLen[1] = RR_LENGTH( fGradients[0][1], fGradients[1][1] );
|
|
break;
|
|
case 3:
|
|
fLen[0] = RR_SQRT(
|
|
(fGradients[0][0]*fGradients[0][0]) +
|
|
(fGradients[1][0]*fGradients[1][0]) +
|
|
(fGradients[2][0]*fGradients[2][0]) );
|
|
fLen[1] = RR_SQRT(
|
|
(fGradients[0][1]*fGradients[0][1]) +
|
|
(fGradients[1][1]*fGradients[1][1]) +
|
|
(fGradients[2][1]*fGradients[2][1]) );
|
|
break;
|
|
}
|
|
|
|
// take the MAX for the coverage
|
|
FLOAT fCoverage = MAX( fLen[0], fLen[1] );
|
|
|
|
// take log2 of coverage for LOD
|
|
fLOD = RR_LOG2(fCoverage);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Computes level of detail and other factors in preparation for anisotropic
|
|
// filtering. This is for 2D texture maps only.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
void
|
|
ComputeAnisoCoverage(
|
|
const FLOAT (*fGradients)[2], FLOAT fMaxAniso, // inputs
|
|
FLOAT& fLOD, FLOAT& fRatio, FLOAT fDelta[] ) // outputs
|
|
{
|
|
// compute axis lengths and determinant
|
|
FLOAT fLenX2 = (fGradients[0][0]*fGradients[0][0])+(fGradients[1][0]*fGradients[1][0]);
|
|
FLOAT fLenY2 = (fGradients[0][1]*fGradients[0][1])+(fGradients[1][1]*fGradients[1][1]);
|
|
FLOAT fDet = RR_ABSF((fGradients[0][0]*fGradients[1][1])-(fGradients[0][1]*fGradients[1][0]));
|
|
|
|
// select major axis
|
|
BOOL bXMajor = (fLenX2 > fLenY2);
|
|
|
|
// select and normalize steps; compute aniso ratio
|
|
FLOAT fMaj2 = (bXMajor) ? (fLenX2) : (fLenY2);
|
|
FLOAT fMaj = RR_SQRT(fMaj2);
|
|
FLOAT fMajNorm = 1./fMaj;
|
|
fDelta[0] = ( bXMajor ? fGradients[0][0] : fGradients[0][1] ) * fMajNorm;
|
|
fDelta[1] = ( bXMajor ? fGradients[1][0] : fGradients[1][1] ) * fMajNorm;
|
|
if( !FLOAT_EQZ(fDet) )
|
|
fRatio = fMaj2/fDet;
|
|
else
|
|
fRatio = 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);
|
|
}
|
|
|
|
// end
|