windows-nt/Source/XPSP1/NT/multimedia/directx/dxg/ref8/rast/ctexfilt.cpp
2020-09-26 16:20:57 +08:00

361 lines
14 KiB
C++

///////////////////////////////////////////////////////////////////////////////
// Copyright (C) Microsoft Corporation, 2000.
//
// ctexfilt.cpp
//
// Direct3D Reference Device - Cube Texture Map Filtering
//
///////////////////////////////////////////////////////////////////////////////
#include "pch.cpp"
#pragma hdrstop
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void
RefRast::ComputeCubeTextureFilter( int iStage, FLOAT fCrd[] )
{
#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)
// determine which map face the texture coordinate normal is facing
UINT uMap;
if ( fabs(fCrd[0]) > fabs(fCrd[1]) )
{
if ( fabs(fCrd[0]) > fabs(fCrd[2]) )
uMap = POS_NX | ((fCrd[0] < 0.0) ? (NEG_NORM) : 0);
else
uMap = POS_NZ | ((fCrd[2] < 0.0) ? (NEG_NORM) : 0);
}
else
{
if ( fabs(fCrd[1]) > fabs(fCrd[2]) )
uMap = POS_NY | ((fCrd[1] < 0.0) ? (NEG_NORM) : 0);
else
uMap = POS_NZ | ((fCrd[2] < 0.0) ? (NEG_NORM) : 0);
}
// munged texture coordinate and gradient info for cubemaps
D3DCUBEMAP_FACES Face; // face index (0..5) to which normal is (mostly) pointing
FLOAT fMajor; // coord in major direction
FLOAT fMapCrd[2]; // coords into 2D map
FLOAT fMajorGrad[2]; // dMajor/d(X,Y)
FLOAT fMapGrad[2][2]; // d(U/Major,V/Major)/d(X,Y)
#define _MapFaceParams( _Face, _IM, _bFlipM, _IU, _bFlipU, _IV, _bFlipV ) \
{ \
Face = D3DCUBEMAP_FACE_##_Face; \
fMajor = (_bFlipM) ? (-fCrd[_IM]) : ( fCrd[_IM]); \
fMapCrd[0] = (_bFlipU) ? (-fCrd[_IU]) : ( fCrd[_IU]); \
fMapCrd[1] = (_bFlipV) ? (-fCrd[_IV]) : ( fCrd[_IV]); \
fMajorGrad[0] = m_TexCvg[iStage].fGradients[_IM][0]; if (_bFlipM) fMajorGrad[0] = -fMajorGrad[0]; \
fMajorGrad[1] = m_TexCvg[iStage].fGradients[_IM][1]; if (_bFlipM) fMajorGrad[1] = -fMajorGrad[1]; \
fMapGrad[0][0] = m_TexCvg[iStage].fGradients[_IU][0]; if (_bFlipU) fMapGrad[0][0] = -fMapGrad[0][0]; \
fMapGrad[0][1] = m_TexCvg[iStage].fGradients[_IU][1]; if (_bFlipU) fMapGrad[0][1] = -fMapGrad[0][1]; \
fMapGrad[1][0] = m_TexCvg[iStage].fGradients[_IV][0]; if (_bFlipV) fMapGrad[1][0] = -fMapGrad[1][0]; \
fMapGrad[1][1] = m_TexCvg[iStage].fGradients[_IV][1]; if (_bFlipV) fMapGrad[1][1] = -fMapGrad[1][1]; \
}
switch (uMap)
{
case POS_NX: _MapFaceParams( POSITIVE_X, 0,0, 2,1, 1,1 ); break;
case POS_NY: _MapFaceParams( POSITIVE_Y, 1,0, 0,0, 2,0 ); break;
case POS_NZ: _MapFaceParams( POSITIVE_Z, 2,0, 0,0, 1,1 ); break;
case NEG_NX: _MapFaceParams( NEGATIVE_X, 0,1, 2,0, 1,1 ); break;
case NEG_NY: _MapFaceParams( NEGATIVE_Y, 1,1, 0,0, 2,1 ); break;
case NEG_NZ: _MapFaceParams( NEGATIVE_Z, 2,1, 0,1, 1,1 ); break;
}
// compute gradients prior to normalizing map coords
FLOAT fInvMajor = 1.F/fMajor;
if ( m_TexFlt[iStage].CvgFilter != D3DTEXF_NONE )
{
// Compute d(U/Major)/dx, d(U/Major)/dy, d(V/Major)/dx, d(V/Major)/dy.
//
// i.e., for d(U/Major))/dx
// Given: U' = unprojected U0 coord (fMapCrd[0])
// U0 = U'/Major (fMapCrd[0]/fMajor)
// U1 = (U' + dU'/dX)/(Major + dMajor/dX)
//
// d(U/Major)/dx = U1 - U0
// = (Major*(dU'/dX) - U'*(dMajor/dX)) / (Major * (Major + dMajor/dX))
// (Use FLT_MAX if denominator is zero)
float fDenom;
fDenom = fMajor * (fMajor + fMajorGrad[0]);
if( 0 == fDenom )
{
fMapGrad[0][0] = fMapGrad[1][0] = FLT_MAX;
}
else
{
fDenom = 1.F/fDenom;
fMapGrad[0][0] = (fMajor*fMapGrad[0][0] - fMapCrd[0]*fMajorGrad[0])*fDenom;
fMapGrad[1][0] = (fMajor*fMapGrad[1][0] - fMapCrd[1]*fMajorGrad[0])*fDenom;
}
fDenom = fMajor * (fMajor + fMajorGrad[1]);
if( 0 == fDenom )
{
fMapGrad[0][1] = fMapGrad[1][1] = FLT_MAX;
}
else
{
fDenom = 1.F/fDenom;
fMapGrad[0][1] = (fMajor*fMapGrad[0][1] - fMapCrd[0]*fMajorGrad[1])*fDenom;
fMapGrad[1][1] = (fMajor*fMapGrad[1][1] - fMapCrd[1]*fMajorGrad[1])*fDenom;
}
// scale gradients to texture LOD 0 size; scale by .5F to match coord scale below
fMapGrad[0][0] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][0]*.5F;
fMapGrad[0][1] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][0]*.5F;
fMapGrad[1][0] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][1]*.5F;
fMapGrad[1][1] *= m_pRD->m_pTexture[iStage]->m_fTexels[0][1]*.5F;
ComputeCubeCoverage( fMapGrad, m_TexCvg[iStage].fLOD );
ComputePerLODControls( iStage );
}
// normalize map coords (-1. to 1. range), then map to 0. to 1.
fMapCrd[0] = (fMapCrd[0]*fInvMajor)*.5F + .5F;
fMapCrd[1] = (fMapCrd[1]*fInvMajor)*.5F + .5F;
int iL;
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[iL].iLOD = Face + 6*m_TexCvg[iStage].iLODMap[iL];
m_TexFlt[iStage].pSamples[iL].fWgt = m_TexCvg[iStage].fLODFrc[iL];
ComputePointSampleCoords( iStage, m_TexFlt[iStage].pSamples[iL].iLOD,
fMapCrd, m_TexFlt[iStage].pSamples[iL].iCrd );
m_TexFlt[iStage].cSamples++;
}
break;
case D3DTEXF_LINEAR:
for ( iL = 0; iL < m_TexCvg[iStage].cLOD; iL++ )
{
if ( 0 == m_TexCvg[iStage].iLODMap[iL] )
{
// TODO: correct sampling position near edges on map 0
}
INT32 iCrdMap[2][2];
FLOAT fCrdFrc[2][2];
ComputeLinearSampleCoords(
iStage, 6*m_TexCvg[iStage].iLODMap[iL]+Face, fMapCrd,
iCrdMap[0], iCrdMap[1], fCrdFrc[0], fCrdFrc[1] );
SetUpCubeMapLinearSample( iStage, Face,
6*m_TexCvg[iStage].iLODMap[iL]+Face, m_TexCvg[iStage].fLODFrc[iL],
iCrdMap, fCrdFrc );
}
break;
}
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
void
RefRast::SetUpCubeMapLinearSample(
int iStage, D3DCUBEMAP_FACES Face,
INT32 iLODMap, FLOAT fLODScale,
INT32 (*iCrd)[2], FLOAT (*fFrc)[2] )
{
int iC,iS;
INT32 iCrdMax[2];
iCrdMax[0] = m_pRD->m_pTexture[iStage]->m_cTexels[iLODMap][0] - 1;
iCrdMax[1] = m_pRD->m_pTexture[iStage]->m_cTexels[iLODMap][1] - 1;
// form flags indicating if sample coordinate is out in either direction
UINT uOut[2][2] = { 0, 0, 0, 0, };
for ( iC = 0; iC < 2; iC++ )
{
if ( iCrd[iC][0] < 0 ) uOut[iC][0] = 1;
if ( iCrd[iC][0] > iCrdMax[0] ) uOut[iC][0] = 2;
if ( iCrd[iC][1] < 0 ) uOut[iC][1] = 1;
if ( iCrd[iC][1] > iCrdMax[1] ) uOut[iC][1] = 2;
}
// compute sample weights and per-sample out flags
FLOAT fWgtS[4]; BOOL bOutS[4];
for ( iS = 0; iS < 4; iS ++ )
{
fWgtS[iS] = fLODScale*fFrc[iS&1][0]*fFrc[iS>>1][1];
bOutS[iS] = uOut[iS&1][0] || uOut[iS>>1][1];
}
// compute per-sample coords; discard samples which are off in corner;
// conditionally remap to adjacent face
INT32 iCrdS[4][2];
D3DCUBEMAP_FACES FaceS[4];
for ( iS = 0; iS < 4; iS ++ )
{
iCrdS[iS][0] = iCrd[iS&1][0];
iCrdS[iS][1] = iCrd[iS>>1][1];
FaceS[iS] = Face;
if ( uOut[iS&1][0] && uOut[iS>>1][1] )
{
// sample is out on both sides, so don't take this sample (set weight to
// zero) and divide it's weight evenly between the two singly-out samples
FLOAT fWgtDist = fWgtS[iS]*.5f;
fWgtS[iS] = 0.f;
for ( int iSp = 0; iSp < 4; iSp ++ )
{
if (iSp == iS) continue;
if (bOutS[iSp]) fWgtS[iSp] += fWgtDist; // will hit 2 of 4
}
continue;
}
if ( bOutS[iS] )
{
// sample is out on one side - remap coordinate only adjacent face
DoCubeRemap( iCrdS[iS], iCrdMax, FaceS[iS], uOut[iS&1][0], uOut[iS>>1][1] );
}
}
// form the samples
TextureSample* pS = &m_TexFlt[iStage].pSamples[m_TexFlt[iStage].cSamples];
for ( iS = 0; iS < 4; iS ++ )
{
pS->iLOD = iLODMap - Face + FaceS[iS];
pS->fWgt = fWgtS[iS];
pS->iCrd[0] = iCrdS[iS][0];
pS->iCrd[1] = iCrdS[iS][1];
pS++; m_TexFlt[iStage].cSamples++;
}
}
//
// uCubeEdgeTable
//
// This table looks up how to map a given [0] and [1] that are out of range
// on their primary face. The first (leftmost) index to the table is the current
// face. The second index is 0 if [1] is in range, 1 if [1] is negative
// and 2 if [1] is larger than the texture. Likewise, the last index is 0
// if [0] is in range, 1 if [0] is negative, and 2 if [0] is larger than
// than the texture.
//
// defines for the actions returned by the uCubeEdgeTable
//
#define CET_FACEMASK 0x0F // new face
#define CET_0MASK 0x30 // coord [0] mask
#define CET_00 0x00 // new face [0] is old face [0]
#define CET_0c0 0x10 // new face [0] is old face ~[0]
#define CET_01 0x20 // new face [0] is old face [1]
#define CET_0c1 0x30 // new face [0] is old face ~[1]
#define CET_1MASK 0xC0 // coord [1] mask
#define CET_10 0x00 // new face [1] is old face [0]
#define CET_1c0 0x40 // new face [1] is old face ~[0]
#define CET_11 0x80 // new face [1] is old face [1]
#define CET_1c1 0xC0 // new face [1] is old face ~[1]
#define CET_INVALID 0xFF // invalid entry (out on two sides)
#define _SetCET( _Face, _Crd0, _Crd1 ) (_Face)|(CET_0##_Crd0)|(CET_1##_Crd1)
static UINT CubeEdgeTable[6][3][3] = {
{
{ _SetCET( 0, 0, 1 ), _SetCET( 4, c0, 1 ), _SetCET( 5, c0, 1 ), },
{ _SetCET( 2, c1, c0 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 3, 1, 0 ), CET_INVALID, CET_INVALID, },
},
{
{ _SetCET( 1, 0, 1 ), _SetCET( 5, c0, 1 ), _SetCET( 4, c0, 1 ), },
{ _SetCET( 2, 1, 0 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 3, c1, c0 ), CET_INVALID, CET_INVALID, },
},
{
{ _SetCET( 2, 0, 1 ), _SetCET( 1, 1, 0 ), _SetCET( 0, c1, c0 ), },
{ _SetCET( 5, c0, 1 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 4, 0, c1 ), CET_INVALID, CET_INVALID, },
},
{
{ _SetCET( 3, 0, 1 ), _SetCET( 1, c1, c0 ), _SetCET( 0, 1, 0 ), },
{ _SetCET( 4, 0, c1 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 5, c0, 1 ), CET_INVALID, CET_INVALID, },
},
{
{ _SetCET( 4, 0, 1 ), _SetCET( 1, c0, 1 ), _SetCET( 0, c0, 1 ), },
{ _SetCET( 2, 0, c1 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 3, 0, c1 ), CET_INVALID, CET_INVALID, },
},
{
{ _SetCET( 5, 0, 1 ), _SetCET( 0, c0, 1 ), _SetCET( 1, c0, 1 ), },
{ _SetCET( 2, c0, 1 ), CET_INVALID, CET_INVALID, },
{ _SetCET( 3, c0, 1 ), CET_INVALID, CET_INVALID, },
},
};
//-----------------------------------------------------------------------------
//
// DoCubeRemap - Interprets the edge table and munges coords and face.
//
//-----------------------------------------------------------------------------
void
DoCubeRemap(
INT32 iCrd[], INT32 iCrdMax[],
D3DCUBEMAP_FACES& Face, UINT uOut0, UINT uOut1)
{
UINT Table = CubeEdgeTable[Face][uOut1][uOut0];
_ASSERT( Table != CET_INVALID, "Illegal cube map lookup" );
INT32 iCrdIn[2];
iCrdIn[0] = iCrd[0];
iCrdIn[1] = iCrd[1];
switch ( Table & CET_0MASK )
{
default:
case CET_00: iCrd[0] = iCrdIn[0]; break;
case CET_0c0: iCrd[0] = iCrdMax[0]-iCrdIn[0]; break;
case CET_01: iCrd[0] = iCrdIn[1]; break;
case CET_0c1: iCrd[0] = iCrdMax[1]-iCrdIn[1]; break;
}
switch ( Table & CET_1MASK )
{
default:
case CET_10: iCrd[1] = iCrdIn[0]; break;
case CET_1c0: iCrd[1] = iCrdMax[0]-iCrdIn[0]; break;
case CET_11: iCrd[1] = iCrdIn[1]; break;
case CET_1c1: iCrd[1] = iCrdMax[1]-iCrdIn[1]; break;
}
Face = (D3DCUBEMAP_FACES)(Table & CET_FACEMASK);
}
//-----------------------------------------------------------------------------
//
// Computes level of detail for cube mapping, looks better if
// we err on the side of fuzziness.
//
//-----------------------------------------------------------------------------
void
ComputeCubeCoverage( const FLOAT (*fGradients)[2], FLOAT& fLOD )
{
// compute length of coverage in U and V axis
FLOAT fLenX = RR_LENGTH( fGradients[0][0], fGradients[1][0] );
FLOAT fLenY = RR_LENGTH( fGradients[0][1], fGradients[1][1] );
FLOAT fCoverage;
#if 0
// take average since one length can be pathologically small
// for large areas of triangles when cube mapping
fCoverage = (fLenX+fLenY)/2;
#else
// use the MAX of the lengths
fCoverage = MAX(fLenX,fLenY);
#endif
// take log2 of coverage for LOD
fLOD = RR_LOG2(fCoverage);
}
///////////////////////////////////////////////////////////////////////////////
// end