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