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

2017 lines
74 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) Microsoft Corporation, 1998.
  3. //
  4. // texture.cpp
  5. //
  6. // Direct3D Reference Rasterizer - Texture Map Sampling & Filtering Methods
  7. //
  8. ///////////////////////////////////////////////////////////////////////////////
  9. #include "pch.cpp"
  10. #pragma hdrstop
  11. //-----------------------------------------------------------------------------
  12. //
  13. // overload new & delete so that it can be allocated from caller-controlled
  14. // pool
  15. //
  16. //-----------------------------------------------------------------------------
  17. void*
  18. RRTexture::operator new(size_t)
  19. {
  20. void* pMem = (void*)MEMALLOC( sizeof(RRTexture) );
  21. _ASSERTa( NULL != pMem, "malloc failure on Tex object", return NULL; );
  22. return pMem;
  23. }
  24. //-----------------------------------------------------------------------------
  25. void
  26. RRTexture::operator delete(void* pv,size_t)
  27. {
  28. MEMFREE( pv );
  29. }
  30. //-----------------------------------------------------------------------------
  31. //
  32. // Constructor/Destructor
  33. //
  34. //-----------------------------------------------------------------------------
  35. RRTexture::RRTexture( void )
  36. {
  37. memset( this, 0, sizeof(*this) );
  38. }
  39. //-----------------------------------------------------------------------------
  40. RRTexture::~RRTexture( void )
  41. {
  42. }
  43. //-----------------------------------------------------------------------------
  44. //
  45. // Validate - Updates private data. Must be called anytime public data is
  46. // altered.
  47. //
  48. //-----------------------------------------------------------------------------
  49. BOOL
  50. RRTexture::Validate( void )
  51. {
  52. // validate inputs
  53. BOOL bFail0 = ( m_cLOD >= RRTEX_MAXCLOD ); // too many LODs
  54. BOOL bFail1 = !( IsPowerOf2( m_iWidth ) ); // gotta be power of two
  55. BOOL bFail2 = !( IsPowerOf2( m_iHeight ) );
  56. if ( bFail0 || bFail1 || bFail2 )
  57. {
  58. DPFRR(1,"RRTexture::Validate failed (%d,%d,%d)", bFail0, bFail1, bFail2);
  59. return FALSE;
  60. }
  61. // set internal size reps
  62. m_iTexSize[0] = (INT16)m_iWidth;
  63. m_iTexSize[1] = (INT16)m_iHeight;
  64. // mask is size-1 because these have to be power-of-two
  65. m_uTexMask[0] = (UINT16)m_iTexSize[0]-1;
  66. m_uTexMask[1] = (UINT16)m_iTexSize[1]-1;
  67. // shift is log2 of size
  68. m_iTexShift[0] = (INT16)FindFirstSetBit( m_iTexSize[0], 16 );
  69. m_iTexShift[1] = (INT16)FindFirstSetBit( m_iTexSize[1], 16 );
  70. // compute the 'has alpha' flag
  71. m_bHasAlpha = FALSE;
  72. switch ( m_SurfType )
  73. {
  74. case RR_STYPE_B8G8R8A8:
  75. case RR_STYPE_B5G5R5A1:
  76. case RR_STYPE_B4G4R4A4:
  77. case RR_STYPE_L8A8:
  78. case RR_STYPE_L4A4:
  79. case RR_STYPE_B2G3R3A8:
  80. case RR_STYPE_DXT1:
  81. case RR_STYPE_DXT2:
  82. case RR_STYPE_DXT3:
  83. case RR_STYPE_DXT4:
  84. case RR_STYPE_DXT5:
  85. m_bHasAlpha = TRUE;
  86. break;
  87. case RR_STYPE_PALETTE4:
  88. case RR_STYPE_PALETTE8:
  89. m_bHasAlpha = ( m_uFlags & RR_TEXTURE_ALPHAINPALETTE ) ? TRUE : FALSE;
  90. break;
  91. }
  92. return TRUE;
  93. }
  94. //-----------------------------------------------------------------------------
  95. //
  96. // DoLookupAndFilter - Called once per active texture stage to compute
  97. // coverage (level-of-detail) and invoke texel read and filtering routines.
  98. // Returns filtered texel.
  99. //
  100. //-----------------------------------------------------------------------------
  101. void
  102. RRTexture::DoLookupAndFilter(
  103. INT32 iStage,
  104. RRTextureCoord TCoord, // local copy
  105. RRColor& TextureColor)
  106. {
  107. // check for potential mip mapping
  108. BOOL bDoMipMap = ( m_cLOD > 0 ) && ( m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] > D3DTFP_NONE );
  109. // check for requirement to do level-of-detail (coverage) computation - either
  110. // for mipmap or per-pixel filter selection
  111. BOOL bComputeLOD = bDoMipMap ||
  112. ( m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] != m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] );
  113. // check for anisotropic filtering in either mag filter or in min filter
  114. BOOL bDoAniso =
  115. ( D3DTFG_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] ) ||
  116. ( bComputeLOD && (D3DTFN_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) );
  117. if ( bDoMipMap || bDoAniso || bComputeLOD )
  118. {
  119. // here if doing mipmapping or anisotropic filtering, or just have a mismatch
  120. // between the min and mag filters, so compute level of detail (and maybe aniso
  121. // coverage)
  122. // scale gradients to texture LOD 0 size
  123. TCoord.fDUDX *= (FLOAT)m_iTexSize[0];
  124. TCoord.fDUDY *= (FLOAT)m_iTexSize[0];
  125. TCoord.fDVDX *= (FLOAT)m_iTexSize[1];
  126. TCoord.fDVDY *= (FLOAT)m_iTexSize[1];
  127. // compute level of detail (and maybe anisotropic controls)
  128. FLOAT fLOD, fAnisoRatio, fAnisoDelta[2];
  129. (bDoAniso)
  130. ? ComputeAnisotropicLevelOfDetail( TCoord, (FLOAT)m_pStageState[iStage].m_dwVal[D3DTSS_MAXANISOTROPY],
  131. fLOD, fAnisoRatio,fAnisoDelta )
  132. : ComputeSimpleLevelOfDetail ( TCoord, fLOD );
  133. // Uncomment the line below to see the anisotropy by color. White is 1:1, darker is more
  134. // anisotropy.
  135. //#define COLOR_BY_ANISOTROPY 1
  136. #ifdef COLOR_BY_ANISOTROPY
  137. static RRColor PseudoColors[16] =
  138. {
  139. 0xffffffff,
  140. 0xffffff00,
  141. 0xffff00ff,
  142. 0xff00ffff,
  143. 0xff888888,
  144. 0xff0000ff,
  145. 0xff00ff00,
  146. 0xffff0000,
  147. 0xff444444,
  148. 0xff888800,
  149. 0xff880088,
  150. 0xff008888,
  151. 0xff222222,
  152. 0xff000088,
  153. 0xff008800,
  154. 0xff880000,
  155. };
  156. INT32 iPseudoColor = (INT32)(fAnisoRatio - .5); // round, and make 1.0F == index 0
  157. iPseudoColor = min(max(iPseudoColor, 0), 15);
  158. TextureColor = PseudoColors[iPseudoColor];
  159. return;
  160. #endif
  161. // apply bias and compute integer (n.5) LOD
  162. INT16 iLOD = 0;
  163. if ( bComputeLOD )
  164. {
  165. // apply LOD offset
  166. fLOD += m_pStageState[iStage].m_fVal[D3DTSS_MIPMAPLODBIAS];
  167. // convert LOD to n.5 fixed point integer
  168. iLOD = AS_INT16( fLOD + FLOAT_5_SNAP );
  169. }
  170. // determine if magnifying or minifying
  171. BOOL bMagnify = ( iLOD <= 0 );
  172. // zero out LOD if not mipmapping
  173. if ( !bDoMipMap ) { iLOD = 0; }
  174. // do different filtering for magnify vs. minify
  175. if ( bMagnify )
  176. {
  177. // here for magnify - do either (non-anisotropic) magnify or
  178. // anisotropic filter
  179. if ( D3DTFG_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] )
  180. {
  181. DoAniso( iStage, TCoord, iLOD,fAnisoRatio,fAnisoDelta, TextureColor );
  182. }
  183. else
  184. {
  185. DoMagnify( iStage, TCoord, TextureColor );
  186. }
  187. }
  188. else
  189. {
  190. // here for minify - do either simple minify, trilerp,
  191. // or anisotropic filter
  192. if ( D3DTFN_ANISOTROPIC == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] )
  193. {
  194. DoAniso( iStage, TCoord, iLOD,fAnisoRatio,fAnisoDelta, TextureColor );
  195. }
  196. else
  197. {
  198. if ( !bDoMipMap ||
  199. ( bDoMipMap && ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] ) ) )
  200. {
  201. DoMinify( iStage, TCoord, iLOD, TextureColor );
  202. }
  203. else
  204. {
  205. DoTrilerp( iStage, TCoord, iLOD, TextureColor );
  206. }
  207. }
  208. }
  209. }
  210. else
  211. {
  212. // here for no mipmaps and matching (and non-aniso) min and mag filters,
  213. // so just apply mag filter
  214. DoMagnify( iStage, TCoord, TextureColor );
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. //
  219. // DoMapLookupLerp - Performs texture index ADDRESS processing followed by
  220. // a lookup within a single DD surface (a single LOD within a chain of DD
  221. // surfaces). Dies BILINEAR filter operation for lookup.
  222. //
  223. // This is called once per pixel for BILINEAR, twice per pixel when
  224. // doing mipmap trilinear interpolation.
  225. //
  226. // * texture index inputs are n.5 fixed point
  227. // * LOD input is 0..n count where 0 indicates the largest LOD
  228. //
  229. //-----------------------------------------------------------------------------
  230. RRColor RRTexture::DoMapLookupLerp(INT32 iStage, INT32 iU, INT32 iV, INT16 iLOD)
  231. {
  232. // extract fraction bits
  233. UINT8 uUFrac = iU&0x1f;
  234. UINT8 uVFrac = iV&0x1f;
  235. // take floor for (0,0) sample coords
  236. INT16 iU0 = iU>>5;
  237. INT16 iV0 = iV>>5;
  238. // take ceiling for (1,1) sample coords
  239. INT16 iU1 = iU0+1;
  240. INT16 iV1 = iV0+1;
  241. BOOL bColorKeyMatched00 = FALSE;
  242. BOOL bColorKeyMatched01 = FALSE;
  243. BOOL bColorKeyMatched10 = FALSE;
  244. BOOL bColorKeyMatched11 = FALSE;
  245. // grab four adjacent samples (or border color)
  246. RRColor Texel00 = DoMapLookupNearest( iStage, iU0, iV0, iLOD, bColorKeyMatched00);
  247. RRColor Texel01 = DoMapLookupNearest( iStage, iU1, iV0, iLOD, bColorKeyMatched01);
  248. RRColor Texel10 = DoMapLookupNearest( iStage, iU0, iV1, iLOD, bColorKeyMatched10);
  249. RRColor Texel11 = DoMapLookupNearest( iStage, iU1, iV1, iLOD, bColorKeyMatched11);
  250. // only set 'colorkey matched' if at least one matched value has
  251. // a non-zero contribution (note that it is not possible for 00
  252. // to have no contribution)
  253. if (uUFrac == 0x00) {
  254. // 01 and 11 have zero weight if U fraction is zero
  255. bColorKeyMatched01 = bColorKeyMatched11 = FALSE;
  256. }
  257. if (uVFrac == 0x00) {
  258. // 10 and 11 have zero weight if V fraction is zero
  259. bColorKeyMatched10 = bColorKeyMatched11 = FALSE;
  260. }
  261. // merge colorkey match info from previous invocation
  262. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched00 || bColorKeyMatched01 ||
  263. bColorKeyMatched10 || bColorKeyMatched11;
  264. // do bilinear filter
  265. RRColor Texel;
  266. BiLerpColor( Texel, Texel00,Texel01, Texel10,Texel11, uUFrac,uVFrac);
  267. return Texel;
  268. }
  269. //-----------------------------------------------------------------------------
  270. //
  271. // DoMapLookupNearest - Performs texture index ADDRESS processing followed by
  272. // a lookup within a single DD surface (a single LOD within a chain of DD
  273. // surfaces). Does NEAREST operation for lookup.
  274. //
  275. // This is called once per pixel for NEAREST , twice per pixel when
  276. // doing mipmap trilinear interpolation
  277. //
  278. // * texture index inputs are n.0 fixed point
  279. // * LOD input is 0..n count where 0 indicates the largest LOD
  280. // * texture index extend mode processing is also performed here - this works
  281. // for power-of-two texture sizes only.
  282. //
  283. //-----------------------------------------------------------------------------
  284. RRColor RRTexture::DoMapLookupNearest(INT32 iStage, INT32 iU, INT32 iV, INT16 iLOD, BOOL &bColorKeyMatched)
  285. {
  286. // LSB-aligned masks of index bits within current LOD
  287. INT16 iUMask = m_uTexMask[0] >> iLOD;
  288. INT16 iVMask = m_uTexMask[1] >> iLOD;
  289. // boolean for BORDER - if true then use border color for corresponding sample
  290. BOOL bUseBorder = FALSE;
  291. // not matched by default
  292. bColorKeyMatched = FALSE;
  293. // do texture ADDRESS processing for U axis
  294. switch ( m_pStageState[iStage].m_dwVal[D3DTSS_ADDRESSU] )
  295. {
  296. case D3DTADDRESS_WRAP:
  297. // just lop off non-fractional bits
  298. iU &= iUMask;
  299. break;
  300. case D3DTADDRESS_MIRROR:
  301. // lop off non-fractional bits + flip index if LSB (non-fraction) is set
  302. BOOL bFlip;
  303. bFlip = iU & (iUMask+1); iU &= iUMask; if (bFlip) {iU = iUMask - iU;}
  304. break;
  305. case D3DTADDRESS_BORDER:
  306. // compute booleans for which of 4 samples should use border color
  307. if ((iU < 0) || (iU > iUMask)) { bUseBorder = TRUE;}
  308. break;
  309. case D3DTADDRESS_CLAMP:
  310. // use texels on texture map edge
  311. iU = MAX( 0, MIN( iU, iUMask ) );
  312. break;
  313. }
  314. // do texture ADDRESS processing for V axis
  315. switch ( m_pStageState[iStage].m_dwVal[D3DTSS_ADDRESSV] )
  316. {
  317. case D3DTADDRESS_WRAP:
  318. iV &= iVMask;
  319. break;
  320. case D3DTADDRESS_MIRROR:
  321. BOOL bFlip;
  322. bFlip = iV & (iVMask+1); iV &= iVMask; if (bFlip) {iV = iVMask - iV;}
  323. break;
  324. case D3DTADDRESS_BORDER:
  325. if ((iV < 0) || (iV > iVMask)) { bUseBorder = TRUE; }
  326. break;
  327. case D3DTADDRESS_CLAMP:
  328. iV = MAX( 0, MIN( iV, iVMask ) );
  329. break;
  330. }
  331. // just lookup and return texel at (iU0,iV0)
  332. RRColor Texel;
  333. (bUseBorder)
  334. ? Texel = m_pStageState[iStage].m_dwVal[D3DTSS_BORDERCOLOR]
  335. : ReadColor( iU, iV, iLOD, Texel, bColorKeyMatched );
  336. return Texel;
  337. }
  338. ///////////////////////////////////////////////////////////////////////////////
  339. // //
  340. // Texture Filtering Routines //
  341. // //
  342. ///////////////////////////////////////////////////////////////////////////////
  343. //-----------------------------------------------------------------------------
  344. //
  345. // DoLookup - Does a full lookup given floating point U, V and handles all
  346. // nearest vs bilinear and LOD issues.
  347. //
  348. //-----------------------------------------------------------------------------
  349. RRColor RRTexture::DoLookup(INT32 iStage, float U, float V, INT16 iLOD, BOOL bNearest)
  350. {
  351. INT32 cUPixels = 1 << MAX(m_iTexShift[0]-iLOD,0);
  352. INT32 cVPixels = 1 << MAX(m_iTexShift[1]-iLOD,0);
  353. FLOAT fScaledU = ( U * (FLOAT)cUPixels ) -.5f;
  354. FLOAT fScaledV = ( V * (FLOAT)cVPixels ) -.5f;
  355. if(bNearest)
  356. {
  357. INT32 iU, iV;
  358. // truncate to -infinity to be compatible with ANDing off low order
  359. // bits of a fixed point fScaledCoord. This makes the generation of
  360. // iCoord more hardware like, and does not make a glitch at 0 for
  361. // a wrapped texture.
  362. if (U >= 0.0f)
  363. {
  364. iU = fScaledU + .5f;
  365. }
  366. else
  367. {
  368. iU = fScaledU - .5f;
  369. }
  370. if (V >= 0.0f)
  371. {
  372. iV = fScaledV + .5f;
  373. }
  374. else
  375. {
  376. iV = fScaledV - .5f;
  377. }
  378. BOOL bColorKeyMatched = FALSE;
  379. RRColor Texel = DoMapLookupNearest(iStage,iU,iV,iLOD,bColorKeyMatched);
  380. // merge colorkey match info from previous invocation
  381. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched;
  382. return Texel;
  383. }
  384. else
  385. {
  386. INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
  387. INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP );
  388. return DoMapLookupLerp(iStage,iU,iV,iLOD);
  389. }
  390. }
  391. //-----------------------------------------------------------------------------
  392. //
  393. // DoMagnify - This is used for all magnification filter modes except
  394. // anisotropic.
  395. //
  396. // Currently only POINT and BILINEAR are supported.
  397. //
  398. //-----------------------------------------------------------------------------
  399. void
  400. RRTexture::DoMagnify(INT32 iStage, RRTextureCoord& TCoord, RRColor& Texel )
  401. {
  402. // do lookup, applying MAG filter
  403. Texel = DoLookup( iStage, TCoord.fU, TCoord.fV, 0,
  404. (D3DTFG_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER]) );
  405. }
  406. //-----------------------------------------------------------------------------
  407. //
  408. // DoMinify - This is used for POINT and BILINEAR modes (non-trilinear)
  409. // for minification, and also handles POINT mip filter (nearest LOD).
  410. //
  411. // iLOD is n.5 fixed point
  412. //
  413. //-----------------------------------------------------------------------------
  414. void
  415. RRTexture::DoMinify(INT32 iStage, RRTextureCoord& TCoord, INT16 iLOD, RRColor& Texel )
  416. {
  417. // round and drop fraction from LOD (is n.5 fixed point)
  418. iLOD += 0x10; iLOD &= ~(0x1f);
  419. // convert to n.0
  420. iLOD >>= 5;
  421. // clamp LOD to number of available levels
  422. iLOD = MIN( iLOD, m_cLOD );
  423. // do lookup, applying MIN filter
  424. Texel = DoLookup( iStage, TCoord.fU, TCoord.fV, iLOD,
  425. (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) );
  426. }
  427. //-----------------------------------------------------------------------------
  428. //
  429. // DoTrilerp - Computes level of detail and invokes either: single-map
  430. // lookup & filter for magnify; or trilinear lookup and filter for minify
  431. //
  432. //-----------------------------------------------------------------------------
  433. void
  434. RRTexture::DoTrilerp(INT32 iStage, RRTextureCoord& TCoord, INT16 iLOD, RRColor& Texel)
  435. {
  436. // clamp LOD to number of available levels
  437. iLOD = MIN( iLOD, (m_cLOD)<<5 );
  438. // compute index for two adjacent LODs (with clamp)
  439. INT16 iLODHi = iLOD>>5; // floor
  440. INT16 iLODLo = MIN(iLODHi+1,m_cLOD);
  441. // check for filter type for within LOD map
  442. BOOL bNearest = (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]);
  443. // trilerp - look up each map then lerp between them
  444. // important for colorkey to not include texels with no contribution
  445. if (0x00 != (iLOD&0x1f))
  446. {
  447. RRColor Texel0 = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODHi, bNearest);
  448. RRColor Texel1 = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODLo, bNearest);
  449. LerpColor( Texel, Texel0, Texel1, iLOD&0x1f );
  450. }
  451. else
  452. {
  453. Texel = DoLookup(iStage, TCoord.fU, TCoord.fV, iLODHi, bNearest);
  454. }
  455. }
  456. //-----------------------------------------------------------------------------
  457. //
  458. // DoAniso - Handles anisotropic filtering of either magnified (single
  459. // map lookup) or minified (two adjacent map lookup) samples. The computation
  460. // of level of detail and anisotropic coverage information (fRatio,fDelta[]) is
  461. // done prior to this function.
  462. //
  463. // This performs only anisotropic filtering, and is called only for minification
  464. // when the MINFILTER is set to ANISOTROPIC or for magnification when the
  465. // MAGFILTER is set to ANISOTROPIC.
  466. //
  467. //-----------------------------------------------------------------------------
  468. void
  469. RRTexture::DoAniso(INT32 iStage, RRTextureCoord& TCoord,
  470. INT16 iLOD, FLOAT fRatio, FLOAT fDelta[],
  471. RRColor& Texel)
  472. {
  473. // set boolean if magnifying
  474. BOOL bMagnify = (iLOD <= 0);
  475. // clamp LOD to number of available levels
  476. iLOD = MIN( MAX( iLOD, 0 ), (m_cLOD)<<5 );
  477. // compute index for two adjacent LODs (with clamp)
  478. // 0 is the larger LOD, 1 is the smaller LOD
  479. INT16 iLODHi, iLODLo;
  480. if ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] )
  481. {
  482. // here for nearest MIP filter
  483. // round and drop fraction from LOD (is n.5 fixed point)
  484. iLOD += 0x10; iLOD &= ~(0x1f);
  485. // convert to n.0
  486. iLODHi = iLOD >> 5;
  487. }
  488. else
  489. {
  490. // here for linear MIP filter
  491. iLODHi = iLOD >> 5; // floor for larger LOD
  492. if ( !bMagnify )
  493. {
  494. // ceiling+clamp for smaller LOD
  495. iLODLo = MIN( iLODHi+1, m_cLOD );
  496. }
  497. }
  498. // compute boolean true if only sampling one map - this is the case if
  499. // we are magnifying or if the MIPFILTER is set to NEAREST or if the
  500. // LOD fraction is zero
  501. BOOL bSingleMap =
  502. bMagnify ||
  503. (D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER]) ||
  504. (0x00 == (iLOD&0x1f));
  505. // working copy of texture coordinates
  506. FLOAT fU = TCoord.fU;
  507. FLOAT fV = TCoord.fV;
  508. // fDelta is in texels. Compute correction factor for each LOD we care about
  509. FLOAT fUStepScaleHi = 1.0F/(FLOAT)MAX(m_iWidth >> iLODHi, 1);
  510. FLOAT fVStepScaleHi = 1.0F/(FLOAT)MAX(m_iHeight >> iLODHi, 1);
  511. FLOAT fUStepScaleLo = 0.F;
  512. FLOAT fVStepScaleLo = 0.F;
  513. if ( !bSingleMap )
  514. {
  515. fUStepScaleLo = 1.0F/(FLOAT)MAX(m_iWidth >> iLODLo, 1);
  516. fVStepScaleLo = 1.0F/(FLOAT)MAX(m_iHeight >> iLODLo, 1);
  517. }
  518. // colors for holding partial results during filtering
  519. RRColor TexelP, TexelP0, TexelP1; // Plus side texels
  520. RRColor TexelM, TexelM0, TexelM1; // Minus side texels
  521. //
  522. // key on ratio to either do single lookup, <2:1 processing (two lookups),
  523. // or full aniso walk
  524. //
  525. if (fRatio == 1.)
  526. {
  527. // here for no anisotropy - do single trilerp
  528. if ( bSingleMap )
  529. {
  530. // single map lookup for magnify
  531. Texel = DoLookup( iStage, fU, fV, iLODHi, FALSE);
  532. }
  533. else
  534. {
  535. // trilerp for minify
  536. TexelP0 = DoLookup( iStage, fU, fV, iLODHi, FALSE);
  537. TexelP1 = DoLookup( iStage, fU, fV, iLODLo, FALSE);
  538. LerpColor( Texel, TexelP0, TexelP1, iLOD&0x1f );
  539. }
  540. }
  541. else if (fRatio <= 2.)
  542. {
  543. // here for 2:1 or less - do two lookups and average them
  544. // compute x,y steps from sample center
  545. FLOAT fStep = .5*(fRatio-1.);
  546. FLOAT fUStep = fDelta[0]*fStep;
  547. FLOAT fVStep = fDelta[1]*fStep;
  548. // do + side lookup
  549. if ( bSingleMap )
  550. {
  551. // single map lookup for magnify
  552. TexelP = DoLookup( iStage, fU+fUStep*fUStepScaleHi, fV+fVStep*fVStepScaleHi, iLODHi, FALSE);
  553. }
  554. else
  555. {
  556. // trilerp for minify
  557. TexelP0 = DoLookup( iStage, fU+fUStep*fUStepScaleHi, fV+fVStep*fVStepScaleHi, iLODHi, FALSE);
  558. TexelP1 = DoLookup( iStage, fU+fUStep*fUStepScaleLo, fV+fVStep*fVStepScaleLo, iLODLo, FALSE);
  559. LerpColor( TexelP, TexelP0, TexelP1, iLOD&0x1f );
  560. }
  561. // do - side lookup
  562. if ( bSingleMap )
  563. {
  564. // single map lookup for magnify
  565. TexelM = DoLookup( iStage, fU-fUStep*fUStepScaleHi, fV-fVStep*fVStepScaleHi, iLODHi, FALSE);
  566. }
  567. else
  568. {
  569. // trilerp for minify
  570. TexelM0 = DoLookup( iStage, fU-fUStep*fUStepScaleHi, fV-fVStep*fVStepScaleHi, iLODHi, FALSE);
  571. TexelM1 = DoLookup( iStage, fU-fUStep*fUStepScaleLo, fV-fVStep*fVStepScaleLo, iLODLo, FALSE);
  572. LerpColor( TexelM, TexelM0, TexelM1, iLOD&0x1f );
  573. }
  574. // take average for final texel
  575. LerpColor( Texel, TexelP, TexelM, 0x10 );
  576. }
  577. else
  578. {
  579. // here for > 2:1 - walk line of anisotropy; walks out from the center
  580. // sample point taking two sets of samples (outriggers) per loop, one
  581. // sample at a positive offset from the center (along the aniso line)
  582. // and the other at a negative offset from the center
  583. // this section does stepping for both LODs even though LOD[1] axis
  584. // is not used for magnify case (only the lookup and lerp(s) are skipped)
  585. // n.5 fixed point versions of step values
  586. FLOAT fUStep = fDelta[0];
  587. FLOAT fVStep = fDelta[1];
  588. // initialize + and - step parameters - first step is half distance
  589. FLOAT fUHiP = fU + fUStep*fUStepScaleHi*0.5F;
  590. FLOAT fVHiP = fV + fVStep*fVStepScaleHi*0.5F;
  591. FLOAT fULoP = fU + fUStep*fUStepScaleLo*0.5F;
  592. FLOAT fVLoP = fV + fVStep*fVStepScaleLo*0.5F;
  593. FLOAT fUHiM = fU - fUStep*fUStepScaleHi*0.5F;
  594. FLOAT fVHiM = fV - fVStep*fVStepScaleHi*0.5F;
  595. FLOAT fULoM = fU - fUStep*fUStepScaleLo*0.5F;
  596. FLOAT fVLoM = fV - fVStep*fVStepScaleLo*0.5F;
  597. // step and accumulate color channels
  598. FLOAT fTexelAcc[4] = { 0.f, 0.f, 0.f, 0.f }; // fp accumulation of texel color
  599. FLOAT fRatioRem = fRatio;
  600. FLOAT fInvRatio = 1./fRatio;
  601. BOOL bDone = FALSE;
  602. while (1)
  603. {
  604. // do + side lookup
  605. if ( bSingleMap )
  606. {
  607. // single map lookup for magnify
  608. TexelP = DoLookup( iStage, fUHiP, fVHiP, iLODHi, FALSE );
  609. }
  610. else
  611. {
  612. // trilerp for minify
  613. TexelP0 = DoLookup( iStage, fUHiP, fVHiP, iLODHi, FALSE );
  614. TexelP1 = DoLookup( iStage, fULoP, fVLoP, iLODLo, FALSE );
  615. LerpColor( TexelP, TexelP0, TexelP1, iLOD&0x1f );
  616. }
  617. // do - side lookup
  618. if ( bSingleMap )
  619. {
  620. // single map lookup for magnify
  621. TexelM = DoLookup( iStage, fUHiM, fVHiM, iLODHi, FALSE );
  622. }
  623. else
  624. {
  625. // trilerp for minify
  626. TexelM0 = DoLookup( iStage, fUHiM, fVHiM, iLODHi, FALSE );
  627. TexelM1 = DoLookup( iStage, fULoM, fVLoM, iLODLo, FALSE );
  628. LerpColor( TexelM, TexelM0, TexelM1, iLOD&0x1f );
  629. }
  630. // compute scaling for these samples
  631. FLOAT fAccScale = fInvRatio;
  632. if ( fRatioRem < 2.f )
  633. {
  634. // scale for last outriggers is half of remainder (each)
  635. fAccScale = fRatioRem*.5f*fInvRatio;
  636. bDone = TRUE;
  637. }
  638. // do accumulations
  639. fTexelAcc[0] += fAccScale * FLOAT(TexelP.A);
  640. fTexelAcc[1] += fAccScale * FLOAT(TexelP.R);
  641. fTexelAcc[2] += fAccScale * FLOAT(TexelP.G);
  642. fTexelAcc[3] += fAccScale * FLOAT(TexelP.B);
  643. fTexelAcc[0] += fAccScale * FLOAT(TexelM.A);
  644. fTexelAcc[1] += fAccScale * FLOAT(TexelM.R);
  645. fTexelAcc[2] += fAccScale * FLOAT(TexelM.G);
  646. fTexelAcc[3] += fAccScale * FLOAT(TexelM.B);
  647. // bail from here if last outrigger
  648. if (bDone) { break; }
  649. // advance to next outriggers
  650. fUHiP += fUStep*fUStepScaleHi;
  651. fVHiP += fVStep*fVStepScaleHi;
  652. fULoP += fUStep*fUStepScaleLo;
  653. fVLoP += fVStep*fVStepScaleLo;
  654. fUHiM -= fUStep*fUStepScaleHi;
  655. fVHiM -= fVStep*fVStepScaleHi;
  656. fULoM -= fUStep*fUStepScaleLo;
  657. fVLoM -= fVStep*fVStepScaleLo;
  658. fRatioRem -= 2.f;
  659. }
  660. // clamp accumulator and copy into RRColor for return
  661. Texel.A = MIN( 1.f, fTexelAcc[0] );
  662. Texel.R = MIN( 1.f, fTexelAcc[1] );
  663. Texel.G = MIN( 1.f, fTexelAcc[2] );
  664. Texel.B = MIN( 1.f, fTexelAcc[3] );
  665. }
  666. }
  667. //-----------------------------------------------------------------------------
  668. //
  669. // DoBumpMapping - Called once per buxel to compute the bump map delta's
  670. // and the bump map modulate factor to be used in the next texturing stage.
  671. //
  672. //-----------------------------------------------------------------------------
  673. void
  674. RRTexture::DoBumpMapping(
  675. INT32 iStage,
  676. RRTextureCoord TCoord,
  677. FLOAT& fBumpMapUDelta, FLOAT& fBumpMapVDelta, RRColor& BumpMapModulate)
  678. {
  679. // do full lookup using enabled filtering
  680. RRColor Buxel;
  681. DoLookupAndFilter(iStage, TCoord, Buxel);
  682. FLOAT fDU = Buxel.R; // follows convention from read color routine
  683. FLOAT fDV = Buxel.G;
  684. FLOAT fL = Buxel.B;
  685. // grab transform from renderstate
  686. FLOAT fM00 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT00];
  687. FLOAT fM01 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT01];
  688. FLOAT fM10 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT10];
  689. FLOAT fM11 = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVMAT11];
  690. // apply transforms to deltas from map to form delta return values
  691. fBumpMapUDelta = fM00 * fDU + fM10 * fDV;
  692. fBumpMapVDelta = fM01 * fDU + fM11 * fDV;
  693. // apply scale/bias/clamp to luminance and form RRColor for return
  694. if (m_pStageState[iStage].m_dwVal[D3DTSS_COLOROP] == D3DTOP_BUMPENVMAPLUMINANCE)
  695. {
  696. FLOAT fLScale = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVLSCALE];
  697. FLOAT fLOff = m_pStageState[iStage].m_fVal[D3DTSS_BUMPENVLOFFSET];
  698. fL = fL * fLScale + fLOff;
  699. fL = min(max(fL, 0.0f), 1.0F);
  700. BumpMapModulate.R = fL;
  701. BumpMapModulate.G = fL;
  702. BumpMapModulate.B = fL;
  703. }
  704. else
  705. {
  706. // if not BUMPENVMAPLUMINANCE, always return full intensity white
  707. BumpMapModulate.R = 1.0F;
  708. BumpMapModulate.G = 1.0F;
  709. BumpMapModulate.B = 1.0F;
  710. }
  711. BumpMapModulate.A = 1.0F;
  712. }
  713. ///////////////////////////////////////////////////////////////////////////////
  714. // //
  715. // Texture Mapping Utility Functions //
  716. // //
  717. ///////////////////////////////////////////////////////////////////////////////
  718. //
  719. // various approximations and tricks to speed up the texture map coverage
  720. // computations
  721. //
  722. // these have not been really thoroughly tested, so use at your own risk...
  723. //
  724. // Integer value of first exponent bit in a float. Provides a scaling factor
  725. // for exponent values extracted directly from float representation.
  726. #define FLOAT_EXPSCALE ((FLOAT)0x00800000)
  727. #define FLOAT_OOEXPSCALE ((FLOAT)(1.0 / (double)FLOAT_EXPSCALE))
  728. // Integer representation of 1.0f.
  729. #define INT32_FLOAT_ONE 0x3f800000
  730. static inline FLOAT
  731. RR_LOG2(FLOAT f)
  732. {
  733. return (FLOAT)(AS_INT32(f) - INT32_FLOAT_ONE) * FLOAT_OOEXPSCALE;
  734. }
  735. static inline FLOAT
  736. RR_ALOG2(FLOAT f)
  737. {
  738. INT32 i = (INT32)(f * FLOAT_EXPSCALE) + INT32_FLOAT_ONE;
  739. return AS_FLOAT((long int)i);
  740. }
  741. static inline FLOAT
  742. RR_ABSF(FLOAT f)
  743. {
  744. UINT32 i = AS_UINT32(f) & 0x7fffffff;
  745. return AS_FLOAT((unsigned long int)i);
  746. }
  747. static inline FLOAT
  748. RR_SQRT(FLOAT f)
  749. {
  750. INT32 i = (AS_INT32(f) >> 1) + (INT32_FLOAT_ONE >> 1);
  751. return AS_FLOAT((long int)i);
  752. }
  753. //
  754. // Steve Gabriel's version of an octagonal approximation euclidian distance -
  755. // return is approximating sqrt(fX*fX + fY*fY)
  756. //
  757. static inline FLOAT
  758. RR_LENGTH(FLOAT fX, FLOAT fY)
  759. {
  760. fX = RR_ABSF(fX);
  761. fY = RR_ABSF(fY);
  762. return ((11.0f/32.0f)*(fX + fY) + (21.0f/32.0f)*max(fX, fY));
  763. }
  764. //-----------------------------------------------------------------------------
  765. //
  766. // Computes level of detail for standard trilinear mipmapping, in which
  767. // the four texture index gradients are consolidated into a single number
  768. // to select level of detail.
  769. //
  770. // The basic approach is to compute the lengths of the pixel coverage for
  771. // the X and Y extent of the approximate pixel coverage area. These two
  772. // lengths are then combined in one of several possible methods for the
  773. // single LOD result.
  774. //
  775. // There are several other ways of doing this which are less computationally
  776. // expensive but also produce less desirable results...
  777. //
  778. //-----------------------------------------------------------------------------
  779. void
  780. ComputeSimpleLevelOfDetail( const RRTextureCoord& TCoord, FLOAT& fLOD )
  781. {
  782. // compute length of coverage in U and V axis
  783. FLOAT fLenX = RR_LENGTH( TCoord.fDUDX, TCoord.fDVDX );
  784. FLOAT fLenY = RR_LENGTH( TCoord.fDUDY, TCoord.fDVDY );
  785. FLOAT fCoverage;
  786. switch ( 3 /* LOD computation type */ )
  787. {
  788. // this is probably the best of the lot
  789. case 1 /* AREA */ : fCoverage = RR_SQRT(fLenX*fLenY); break;
  790. // we have not actually tried this one yet, but think it might
  791. // be pretty good
  792. case 2 /* AVERAGE */ : fCoverage = (fLenX+fLenY)/2; break;
  793. // these are fairly inexpensive, but MAX is a bit too fuzzy
  794. // and MIN is a bit too sharp
  795. case 3 /* MAX */ : fCoverage = MAX( fLenX, fLenY ); break;
  796. case 4 /* MIN */ : fCoverage = MIN( fLenX, fLenY ); break;
  797. // these are really inexpensive, but look terrible - you might as
  798. // well just point sample...
  799. case 5 /* MINGRAD */ : fCoverage = MIN( MIN( MIN( TCoord.fDUDX,
  800. TCoord.fDVDX ),
  801. TCoord.fDUDY ),
  802. TCoord.fDVDY ); break;
  803. case 6 /* MAXGRAD */ : fCoverage = MAX( MAX( MAX( TCoord.fDUDX,
  804. TCoord.fDVDX ),
  805. TCoord.fDUDY ),
  806. TCoord.fDVDY ); break;
  807. }
  808. // take log2 of coverage for LOD
  809. fLOD = RR_LOG2(fCoverage);
  810. }
  811. //-----------------------------------------------------------------------------
  812. //
  813. // Computes level of detail and other factors in preparation for anisotropic
  814. // filtering.
  815. //
  816. //-----------------------------------------------------------------------------
  817. void
  818. ComputeAnisotropicLevelOfDetail(
  819. const RRTextureCoord& TCoord, FLOAT fMaxAniso, // inputs
  820. FLOAT& fLOD, FLOAT& fRatio, FLOAT fDelta[] ) // outputs
  821. {
  822. // compute axis lengths and determinant
  823. FLOAT fLenX2 = (TCoord.fDUDX*TCoord.fDUDX)+(TCoord.fDVDX*TCoord.fDVDX);
  824. FLOAT fLenY2 = (TCoord.fDUDY*TCoord.fDUDY)+(TCoord.fDVDY*TCoord.fDVDY);
  825. FLOAT fDet = RR_ABSF((TCoord.fDUDX*TCoord.fDVDY)-(TCoord.fDUDY*TCoord.fDVDX));
  826. // select major axis
  827. BOOL bXMajor = (fLenX2 > fLenY2);
  828. // TODO: can and probably should do this part in log2 domain
  829. // select and normalize steps; compute aniso ratio
  830. FLOAT fMaj2 = (bXMajor) ? (fLenX2) : (fLenY2);
  831. FLOAT fMaj = RR_SQRT(fMaj2);
  832. FLOAT fMajNorm = 1./fMaj;
  833. fDelta[0] = ( bXMajor ? TCoord.fDUDX : TCoord.fDUDY ) * fMajNorm;
  834. fDelta[1] = ( bXMajor ? TCoord.fDVDX : TCoord.fDVDY ) * fMajNorm;
  835. fRatio = (fDet != 0.F) ? (fMaj2/fDet) : (FLT_MAX);
  836. // clamp ratio and compute LOD
  837. FLOAT fMin;
  838. if ( fRatio > fMaxAniso )
  839. {
  840. // ratio is clamped - LOD is based on ratio (preserves area)
  841. fRatio = fMaxAniso;
  842. fMin = fMaj/fRatio;
  843. }
  844. else
  845. {
  846. // ratio not clamped - LOD is based on area
  847. fMin = fDet/fMaj;
  848. }
  849. // clamp to top LOD
  850. if (fMin < 1.0)
  851. {
  852. fRatio = MAX( 1.0, fRatio*fMin );
  853. fMin = 1.0;
  854. }
  855. // take log2 of minor for LOD
  856. fLOD = RR_LOG2(fMin);
  857. }
  858. ///////////////////////////////////////////////////////////////////////////////
  859. // //
  860. // Environment mapping routines //
  861. // //
  862. ///////////////////////////////////////////////////////////////////////////////
  863. //-----------------------------------------------------------------------------
  864. //
  865. // Processes the environment mapping normal and converts to a standard
  866. // U, V coord range for subsequent routines.
  867. //
  868. //-----------------------------------------------------------------------------
  869. void
  870. RRTexture::DoEnvProcessNormal(INT32 iStage,
  871. RREnvTextureCoord ECoord, // local copy
  872. RRColor& TextureColor)
  873. {
  874. #define ENV_RIGHT 0
  875. #define ENV_LEFT 1
  876. #define ENV_TOP 2
  877. #define ENV_BOTTOM 3
  878. #define ENV_FRONT 4
  879. #define ENV_BACK 5
  880. #define POS_NX 1
  881. #define POS_NY 2
  882. #define POS_NZ 3
  883. #define NEG_NORM 4
  884. #define NEG_NX (NEG_NORM | POS_NX)
  885. #define NEG_NY (NEG_NORM | POS_NY)
  886. #define NEG_NZ (NEG_NORM | POS_NZ)
  887. // If we add per pixel normal reflection
  888. // FLOAT fENX = ECoord.fENX;
  889. // FLOAT fENY = ECoord.fENY;
  890. // FLOAT fENZ = ECoord.fENZ;
  891. //
  892. // FLOAT fNDotE = ECoord.fNX*fENX + ECoord.fNY*fENY + ECoord.fNZ*fENZ;
  893. // FLOAT fNDotN = ECoord.fNX*ECoord.fNX + ECoord.fNY*ECoord.fNY + ECoord.fNZ*ECoord.fNZ;
  894. // fNDotE *= 2.0F;
  895. // ECoord.fNX = ECoord.fNX*fNDotE - fENX*fNDotN;
  896. // ECoord.fNY = ECoord.fNY*fNDotE - fENY*fNDotN;
  897. // ECoord.fNZ = ECoord.fNZ*fNDotE - fENZ*fNDotN;
  898. // determine which is the dominant normal
  899. UINT32 uMap;
  900. FLOAT fAbsNX = fabs(ECoord.fNX);
  901. FLOAT fAbsNY = fabs(ECoord.fNY);
  902. FLOAT fAbsNZ = fabs(ECoord.fNZ);
  903. if (fAbsNX > fAbsNY) {
  904. if (fAbsNX > fAbsNZ)
  905. // fNX
  906. uMap = POS_NX | ((ECoord.fNX < 0.0) ? (NEG_NORM) : 0);
  907. else
  908. // fNZ
  909. uMap = POS_NZ | ((ECoord.fNZ < 0.0) ? (NEG_NORM) : 0);
  910. } else {
  911. if (fAbsNY > fAbsNZ)
  912. // fNY
  913. uMap = POS_NY | ((ECoord.fNY < 0.0) ? (NEG_NORM) : 0);
  914. else
  915. // fNZ
  916. uMap = POS_NZ | ((ECoord.fNZ < 0.0) ? (NEG_NORM) : 0);
  917. }
  918. RRTextureCoord TCoord;
  919. switch (uMap) {
  920. case POS_NX:
  921. TCoord.fDUDX = -ECoord.fDNZDX;
  922. TCoord.fDVDX = -ECoord.fDNYDX;
  923. TCoord.fDUDY = -ECoord.fDNZDY;
  924. TCoord.fDVDY = -ECoord.fDNYDY;
  925. TCoord.fU = -ECoord.fNZ;
  926. TCoord.fV = -ECoord.fNY;
  927. DoEnvLookupAndFilter(iStage, ENV_RIGHT, ECoord.fNX, ECoord.fDNXDX, ECoord.fDNXDY, TCoord, TextureColor);
  928. break;
  929. case POS_NY:
  930. TCoord.fDUDX = ECoord.fDNXDX;
  931. TCoord.fDVDX = ECoord.fDNZDX;
  932. TCoord.fDUDY = ECoord.fDNXDY;
  933. TCoord.fDVDY = ECoord.fDNZDY;
  934. TCoord.fU = ECoord.fNX;
  935. TCoord.fV = ECoord.fNZ;
  936. DoEnvLookupAndFilter(iStage, ENV_TOP, ECoord.fNY, ECoord.fDNYDX, ECoord.fDNYDY, TCoord, TextureColor);
  937. break;
  938. case POS_NZ:
  939. TCoord.fDUDX = ECoord.fDNXDX;
  940. TCoord.fDVDX = -ECoord.fDNYDX;
  941. TCoord.fDUDY = ECoord.fDNXDY;
  942. TCoord.fDVDY = -ECoord.fDNYDY;
  943. TCoord.fU = ECoord.fNX;
  944. TCoord.fV = -ECoord.fNY;
  945. DoEnvLookupAndFilter(iStage, ENV_FRONT, ECoord.fNZ, ECoord.fDNZDX, ECoord.fDNZDY, TCoord, TextureColor);
  946. break;
  947. case NEG_NX:
  948. TCoord.fDUDX = ECoord.fDNZDX;
  949. TCoord.fDVDX = -ECoord.fDNYDX;
  950. TCoord.fDUDY = ECoord.fDNZDY;
  951. TCoord.fDVDY = -ECoord.fDNYDY;
  952. TCoord.fU = ECoord.fNZ;
  953. TCoord.fV = -ECoord.fNY;
  954. DoEnvLookupAndFilter(iStage, ENV_LEFT, -ECoord.fNX, -ECoord.fDNXDX, -ECoord.fDNXDY, TCoord, TextureColor);
  955. break;
  956. case NEG_NY:
  957. TCoord.fDUDX = ECoord.fDNXDX;
  958. TCoord.fDVDX = -ECoord.fDNZDX;
  959. TCoord.fDUDY = ECoord.fDNXDY;
  960. TCoord.fDVDY = -ECoord.fDNZDY;
  961. TCoord.fU = ECoord.fNX;
  962. TCoord.fV = -ECoord.fNZ;
  963. DoEnvLookupAndFilter(iStage, ENV_BOTTOM, -ECoord.fNY, -ECoord.fDNYDX, -ECoord.fDNYDY, TCoord, TextureColor);
  964. break;
  965. case NEG_NZ:
  966. TCoord.fDUDX = -ECoord.fDNXDX;
  967. TCoord.fDVDX = -ECoord.fDNYDX;
  968. TCoord.fDUDY = -ECoord.fDNXDY;
  969. TCoord.fDVDY = -ECoord.fDNYDY;
  970. TCoord.fU = -ECoord.fNX;
  971. TCoord.fV = -ECoord.fNY;
  972. DoEnvLookupAndFilter(iStage, ENV_BACK, -ECoord.fNZ, -ECoord.fDNZDX, -ECoord.fDNZDY, TCoord, TextureColor);
  973. break;
  974. }
  975. }
  976. //-----------------------------------------------------------------------------
  977. //
  978. // DoEnvLookupAndFilter - Environment mapped version.
  979. // Called once per active texture stage to compute
  980. // coverage (level-of-detail) and invoke texel read and filtering routines.
  981. //
  982. //-----------------------------------------------------------------------------
  983. void
  984. RRTexture::DoEnvLookupAndFilter(INT32 iStage, INT16 iFace, FLOAT fMajor, FLOAT fDMDX, FLOAT fDMDY,
  985. RRTextureCoord TCoord, // local copy
  986. RRColor& TextureColor)
  987. {
  988. if (m_pDDSLcl[iFace])
  989. {
  990. // faces exist
  991. FLOAT fInvMajor = 1.0/fMajor;
  992. // compute d(U/Major)/dx, etc. using rule for differentiating quotients
  993. TCoord.fDUDX = (fMajor*TCoord.fDUDX - TCoord.fU*fDMDX)*fInvMajor*fInvMajor;
  994. TCoord.fDUDY = (fMajor*TCoord.fDUDY - TCoord.fU*fDMDY)*fInvMajor*fInvMajor;
  995. TCoord.fDVDX = (fMajor*TCoord.fDVDX - TCoord.fV*fDMDX)*fInvMajor*fInvMajor;
  996. TCoord.fDVDY = (fMajor*TCoord.fDVDY - TCoord.fV*fDMDY)*fInvMajor*fInvMajor;
  997. // convert to -1 to 1 range
  998. TCoord.fU *= fInvMajor;
  999. TCoord.fV *= fInvMajor;
  1000. // convert to 0.0 to 1.0
  1001. TCoord.fU = (TCoord.fU*.5 + .5);
  1002. TCoord.fV = (TCoord.fV*.5 + .5);
  1003. // check for potential mip mapping
  1004. BOOL bDoMipMap = ( m_cLOD > 0 ) && ( m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] > D3DTFP_NONE );
  1005. // check for requirement to do level-of-detail (coverage) computation - either
  1006. // for mipmap or per-pixel filter selection
  1007. BOOL bComputeLOD = bDoMipMap ||
  1008. ( m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] != m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER] );
  1009. if ( bDoMipMap || bComputeLOD )
  1010. {
  1011. // here if doing mipmapping or anisotropic filtering, or just have a mismatch
  1012. // between the min and mag filters, so compute level of detail (and maybe aniso
  1013. // coverage)
  1014. // scale gradients to texture LOD 0 size
  1015. TCoord.fDUDX *= (FLOAT)m_iTexSize[0]*.5F;
  1016. TCoord.fDUDY *= (FLOAT)m_iTexSize[0]*.5F;
  1017. TCoord.fDVDX *= (FLOAT)m_iTexSize[1]*.5F;
  1018. TCoord.fDVDY *= (FLOAT)m_iTexSize[1]*.5F;
  1019. FLOAT fLOD;
  1020. ComputeEnvMapLevelOfDetail(TCoord, fLOD);
  1021. // apply bias and compute integer (n.5) LOD
  1022. INT16 iLOD = 0;
  1023. if ( bComputeLOD )
  1024. {
  1025. // apply LOD offset
  1026. fLOD += m_pStageState[iStage].m_fVal[D3DTSS_MIPMAPLODBIAS];
  1027. // convert LOD to n.5 fixed point integer
  1028. iLOD = AS_INT16( fLOD + FLOAT_5_SNAP );
  1029. }
  1030. // determine if magnifying or minifying
  1031. BOOL bMagnify = ( iLOD <= 0 );
  1032. // zero out LOD if not mipmapping
  1033. if ( !bDoMipMap ) { iLOD = 0; }
  1034. // do different filtering for magnify vs. minify
  1035. if ( bMagnify )
  1036. {
  1037. DoEnvMagnify( iStage, TCoord, iFace, TextureColor );
  1038. }
  1039. else
  1040. {
  1041. if ( !bDoMipMap ||
  1042. ( bDoMipMap && ( D3DTFP_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MIPFILTER] ) ) )
  1043. {
  1044. DoEnvMinify( iStage, TCoord, iFace, iLOD, TextureColor );
  1045. }
  1046. else
  1047. {
  1048. DoEnvTrilerp( iStage, TCoord, iFace, iLOD, TextureColor );
  1049. }
  1050. }
  1051. }
  1052. else
  1053. {
  1054. // here for no mipmaps and matching (and non-aniso) min and mag filters,
  1055. // so just apply mag filter
  1056. DoEnvMagnify( iStage, TCoord, iFace, TextureColor );
  1057. }
  1058. }
  1059. else
  1060. {
  1061. // face doesn't exit, return empty face color
  1062. TextureColor = m_dwEmptyFaceColor;
  1063. }
  1064. }
  1065. //-----------------------------------------------------------------------------
  1066. //
  1067. // TexelAvg4 - Averages 4 source texels into 1 destination texel for A, R, G,
  1068. // and B.
  1069. //
  1070. //-----------------------------------------------------------------------------
  1071. static void TexelAvg4(RRColor& Texel, RRColor Texel0, RRColor Texel1, RRColor Texel2, RRColor Texel3)
  1072. {
  1073. Texel.A = Texel0.A + Texel1.A + Texel2.A + Texel3.A;
  1074. Texel.R = Texel0.R + Texel1.R + Texel2.R + Texel3.R;
  1075. Texel.G = Texel0.G + Texel1.G + Texel2.G + Texel3.G;
  1076. Texel.B = Texel0.B + Texel1.B + Texel2.B + Texel3.B;
  1077. Texel.A = Texel.A * 0.25f;
  1078. Texel.R = Texel.R * 0.25f;
  1079. Texel.G = Texel.G * 0.25f;
  1080. Texel.B = Texel.B * 0.25f;
  1081. }
  1082. //-----------------------------------------------------------------------------
  1083. //
  1084. // DoEnvLookup - Does a full lookup given floating point U, V and handles all
  1085. // nearest vs bilinear and LOD issues.
  1086. //
  1087. //-----------------------------------------------------------------------------
  1088. RRColor RRTexture::DoEnvLookup(INT32 iStage, RRTextureCoord TCoord, INT16 iFace, INT16 iLOD, BOOL bNearest)
  1089. {
  1090. FLOAT U = TCoord.fU;
  1091. FLOAT V = TCoord.fV;
  1092. RRColor Texel;
  1093. INT32 cUPixels = 1 << MAX(m_iTexShift[0]-iLOD,0);
  1094. INT32 cVPixels = 1 << MAX(m_iTexShift[1]-iLOD,0);
  1095. FLOAT fScaledU = ( U * (FLOAT)cUPixels ) -.5f;
  1096. FLOAT fScaledV = ( V * (FLOAT)cVPixels ) -.5f;
  1097. // LSB-aligned masks of index bits within current LOD
  1098. INT16 iUMask = m_uTexMask[0] >> iLOD;
  1099. INT16 iVMask = m_uTexMask[1] >> iLOD;
  1100. if(bNearest)
  1101. {
  1102. INT32 iU, iV;
  1103. // truncate to -infinity to be compatible with ANDing off low order
  1104. // bits of a fixed point fScaledCoord. This makes the generation of
  1105. // iCoord more hardware like, and does not make a glitch at 0 for
  1106. // a wrapped texture.
  1107. if (U >= 0.0f)
  1108. {
  1109. iU = fScaledU + .5f;
  1110. }
  1111. else
  1112. {
  1113. iU = fScaledU - .5f;
  1114. }
  1115. if (V >= 0.0f)
  1116. {
  1117. iV = fScaledV + .5f;
  1118. }
  1119. else
  1120. {
  1121. iV = fScaledV - .5f;
  1122. }
  1123. // clamp
  1124. iU = MAX( 0, MIN( iU, iUMask ) );
  1125. iV = MAX( 0, MIN( iV, iVMask ) );
  1126. BOOL bColorKeyMatched = FALSE;
  1127. // "LOD" just used to access correct map
  1128. ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched);
  1129. // merge colorkey match info from previous invocation
  1130. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched;
  1131. }
  1132. else
  1133. {
  1134. if ((m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] == D3DTFG_FLATCUBIC) ||
  1135. (m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER] == D3DTFG_GAUSSIANCUBIC))
  1136. {
  1137. // use wider 3x3 trapezoid filter
  1138. //
  1139. // For the top LOD, if we are interpolating beyond the edge of the
  1140. // texture, correct the interpolation to minimize the artifacts seen in
  1141. // small, diffuse environment maps which tend to emphasize the edges
  1142. // of the cubemap.
  1143. //
  1144. if (iLOD == 0)
  1145. {
  1146. FLOAT fFracU = 0.0f;
  1147. FLOAT fCorrectionU = 0.0f;
  1148. FLOAT fCorrectionV = 0.0f;
  1149. if ((fScaledU < 0.5f) || (fScaledU > ((FLOAT)iUMask-0.5f)))
  1150. {
  1151. // U crosses a boundary, clamp fScaledV
  1152. if (fScaledU < 0.5f)
  1153. {
  1154. // make fFrac always positive
  1155. fFracU = 0.5f-fScaledU;
  1156. }
  1157. else
  1158. {
  1159. fFracU = fScaledU - ((FLOAT)iUMask-0.5f);
  1160. }
  1161. // 2.0/2.0 = 1.0 provides the perfect correction at the cube corner.
  1162. // This can be seen be looking at the derivative of the intersection
  1163. // of a cone and a cube at the cube corner.
  1164. //
  1165. // correction must be corrected for the filter width (hence the *0.5f)
  1166. fCorrectionV = -fFracU*(TCoord.fV-.5f)*0.5f;
  1167. }
  1168. if ((fScaledV < 0.5f) || (fScaledV > ((FLOAT)iVMask-0.5f)))
  1169. {
  1170. // V crosses a boundary, clamp fScaledU
  1171. FLOAT fFracV;
  1172. if (fScaledV < 0.5f)
  1173. {
  1174. // make fFrac always positive
  1175. fFracV = 0.5f-fScaledV;
  1176. }
  1177. else
  1178. {
  1179. fFracV = fScaledV - ((FLOAT)iVMask-0.5f);
  1180. }
  1181. fCorrectionU = -fFracV*(TCoord.fU-.5f)*0.5f;
  1182. if (fFracU != 0.0f)
  1183. {
  1184. // At the corners of the cube, we need to blend away the
  1185. // edge correction so that it is 0 at exactly the corner
  1186. // center. This linear function does fine.
  1187. fCorrectionU *= (1.0f - fFracU);
  1188. fCorrectionV *= (1.0f - fFracV);
  1189. }
  1190. }
  1191. fScaledU += fCorrectionU;
  1192. fScaledV += fCorrectionV;
  1193. }
  1194. INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
  1195. INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP );
  1196. // extract fraction bits
  1197. UINT8 uUFrac = iU&0x1f;
  1198. UINT8 uVFrac = iV&0x1f;
  1199. // take floor
  1200. INT16 iU0 = iU>>5;
  1201. INT16 iV0 = iV>>5;
  1202. // average to find the center texel
  1203. INT32 iUC = (uUFrac >= 0x10) ? (iU0 + 1) : (iU0);
  1204. INT32 iVC = (uVFrac >= 0x10) ? (iV0 + 1) : (iV0);
  1205. // get 9 surrounding samples
  1206. // VU VU VU
  1207. RRColor Texel00, Texel01, Texel02;
  1208. RRColor Texel10, Texel11, Texel12;
  1209. RRColor Texel20, Texel21, Texel22;
  1210. BOOL bColorKeyMatchedT = FALSE;
  1211. DoEnvReMap(iUC-1, iVC-1, iUMask, iVMask, iFace, iLOD, Texel00, bColorKeyMatchedT);
  1212. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1213. DoEnvReMap(iUC-0, iVC-1, iUMask, iVMask, iFace, iLOD, Texel01, bColorKeyMatchedT);
  1214. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1215. DoEnvReMap(iUC+1, iVC-1, iUMask, iVMask, iFace, iLOD, Texel02, bColorKeyMatchedT);
  1216. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1217. DoEnvReMap(iUC-1, iVC+0, iUMask, iVMask, iFace, iLOD, Texel10, bColorKeyMatchedT);
  1218. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1219. DoEnvReMap(iUC-0, iVC+0, iUMask, iVMask, iFace, iLOD, Texel11, bColorKeyMatchedT);
  1220. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1221. DoEnvReMap(iUC+1, iVC+0, iUMask, iVMask, iFace, iLOD, Texel12, bColorKeyMatchedT);
  1222. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1223. DoEnvReMap(iUC-1, iVC+1, iUMask, iVMask, iFace, iLOD, Texel20, bColorKeyMatchedT);
  1224. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1225. DoEnvReMap(iUC-0, iVC+1, iUMask, iVMask, iFace, iLOD, Texel21, bColorKeyMatchedT);
  1226. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1227. DoEnvReMap(iUC+1, iVC+1, iUMask, iVMask, iFace, iLOD, Texel22, bColorKeyMatchedT);
  1228. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatchedT;
  1229. // sum the samples into 4 areas
  1230. RRColor TexelT00, TexelT01, TexelT10, TexelT11;
  1231. TexelAvg4(TexelT00, Texel00, Texel01, Texel10, Texel11);
  1232. TexelAvg4(TexelT01, Texel01, Texel02, Texel11, Texel12);
  1233. TexelAvg4(TexelT10, Texel10, Texel11, Texel20, Texel21);
  1234. TexelAvg4(TexelT11, Texel11, Texel12, Texel21, Texel22);
  1235. // correct the fraction to be around the center sample
  1236. uUFrac = (uUFrac + 0x10) & 0x1f;
  1237. uVFrac = (uVFrac + 0x10) & 0x1f;
  1238. // use a bilerp to get the final sample
  1239. BiLerpColor( Texel, TexelT00,TexelT01, TexelT10,TexelT11, uUFrac,uVFrac);
  1240. }
  1241. else
  1242. {
  1243. // bilinear
  1244. //
  1245. // For the top LOD, if we are interpolating beyond the edge of the
  1246. // texture, correct the interpolation to minimize the artifacts seen in
  1247. // small, diffuse environment maps which tend to emphasize the edges
  1248. // of the cubemap.
  1249. //
  1250. if (iLOD == 0)
  1251. {
  1252. FLOAT fFracU = 0.0f;
  1253. FLOAT fCorrectionU = 0.0f;
  1254. FLOAT fCorrectionV = 0.0f;
  1255. if ((fScaledU < 0.0f) || (fScaledU > (FLOAT)iUMask))
  1256. {
  1257. // U crosses a boundary, clamp fScaledV
  1258. if (fScaledU < 0.0f)
  1259. {
  1260. // make fFrac always positive
  1261. fFracU = -fScaledU;
  1262. }
  1263. else
  1264. {
  1265. fFracU = fScaledU - (FLOAT)iUMask;
  1266. }
  1267. // 2.0/2.0 = 1.0 provides the perfect correction at the cube corner.
  1268. // This can be seen be looking at the derivative of the intersection
  1269. // of a cone and a cube at the cube corner.
  1270. fCorrectionV = -fFracU*(TCoord.fV-.5f);
  1271. }
  1272. if ((fScaledV < 0.0f) || (fScaledV > (FLOAT)iVMask))
  1273. {
  1274. // V crosses a boundary, clamp fScaledU
  1275. FLOAT fFracV;
  1276. if (fScaledV < 0.0f)
  1277. {
  1278. // make fFrac always positive
  1279. fFracV = -fScaledV;
  1280. }
  1281. else
  1282. {
  1283. fFracV = fScaledV - (FLOAT)iVMask;
  1284. }
  1285. fCorrectionU = -fFracV*(TCoord.fU-.5f);
  1286. if (fFracU != 0.0f)
  1287. {
  1288. // At the corners of the cube, we need to blend away the
  1289. // edge correction so that it is 0 at exactly the corner
  1290. // center. This linear function does fine.
  1291. fCorrectionU *= 2.0f*(.5f - fFracU);
  1292. fCorrectionV *= 2.0f*(.5f - fFracV);
  1293. }
  1294. }
  1295. fScaledU += fCorrectionU;
  1296. fScaledV += fCorrectionV;
  1297. }
  1298. INT32 iU = AS_INT32( (DOUBLE)fScaledU + DOUBLE_5_SNAP );// or: iU = fScaledU*32. + .5;
  1299. INT32 iV = AS_INT32( (DOUBLE)fScaledV + DOUBLE_5_SNAP );
  1300. // extract fraction bits
  1301. UINT8 uUFrac = iU&0x1f;
  1302. UINT8 uVFrac = iV&0x1f;
  1303. // take floor for (0,0) sample coords
  1304. INT16 iU0 = iU>>5;
  1305. INT16 iV0 = iV>>5;
  1306. // take ceiling for (1,1) sample coords
  1307. INT16 iU1 = iU0+1;
  1308. INT16 iV1 = iV0+1;
  1309. // grab four adjacent samples
  1310. RRColor Texel00, Texel01, Texel10, Texel11;
  1311. BOOL bColorKeyMatched00 = FALSE;
  1312. BOOL bColorKeyMatched01 = FALSE;
  1313. BOOL bColorKeyMatched10 = FALSE;
  1314. BOOL bColorKeyMatched11 = FALSE;
  1315. DoEnvReMap(iU0, iV0, iUMask, iVMask, iFace, iLOD, Texel00, bColorKeyMatched00);
  1316. DoEnvReMap(iU1, iV0, iUMask, iVMask, iFace, iLOD, Texel01, bColorKeyMatched01);
  1317. DoEnvReMap(iU0, iV1, iUMask, iVMask, iFace, iLOD, Texel10, bColorKeyMatched10);
  1318. DoEnvReMap(iU1, iV1, iUMask, iVMask, iFace, iLOD, Texel11, bColorKeyMatched11);
  1319. // only set 'colorkey matched' if at least one matched value has
  1320. // a non-zero contribution (note that it is not possible for 00
  1321. // to have no contribution)
  1322. if (uUFrac == 0x00) {
  1323. // 01 and 11 have zero weight if U fraction is zero
  1324. bColorKeyMatched01 = bColorKeyMatched11 = FALSE;
  1325. }
  1326. if (uVFrac == 0x00) {
  1327. // 10 and 11 have zero weight if V fraction is zero
  1328. bColorKeyMatched10 = bColorKeyMatched11 = FALSE;
  1329. }
  1330. // merge colorkey match info from previous invocation
  1331. m_bColorKeyMatched = m_bColorKeyMatched || bColorKeyMatched00 || bColorKeyMatched01 ||
  1332. bColorKeyMatched10 || bColorKeyMatched11;
  1333. // do bilinear filter
  1334. BiLerpColor( Texel, Texel00,Texel01, Texel10,Texel11, uUFrac,uVFrac);
  1335. }
  1336. }
  1337. return Texel;
  1338. }
  1339. //-----------------------------------------------------------------------------
  1340. //
  1341. // DoEnvMagnify - This is used for all magnification filter modes except
  1342. // anisotropic.
  1343. //
  1344. // Currently only POINT and BILINEAR are supported.
  1345. //
  1346. //-----------------------------------------------------------------------------
  1347. void
  1348. RRTexture::DoEnvMagnify(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, RRColor& Texel )
  1349. {
  1350. // do lookup, applying MAG filter
  1351. Texel = DoEnvLookup( iStage, TCoord, iFace, 0,
  1352. (D3DTFG_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MAGFILTER]) );
  1353. }
  1354. //-----------------------------------------------------------------------------
  1355. //
  1356. // DoEnvMinify - This is used for POINT and BILINEAR modes (non-trilinear)
  1357. // for minification, and also handles POINT mip filter (nearest LOD).
  1358. //
  1359. // iLOD is n.5 fixed point
  1360. //
  1361. //-----------------------------------------------------------------------------
  1362. void
  1363. RRTexture::DoEnvMinify(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, INT16 iLOD, RRColor& Texel )
  1364. {
  1365. // round and drop fraction from LOD (is n.5 fixed point)
  1366. iLOD += 0x10; iLOD &= ~(0x1f);
  1367. // convert to n.0
  1368. iLOD >>= 5;
  1369. // clamp LOD to number of available levels
  1370. iLOD = MIN( iLOD, m_cLOD );
  1371. // do lookup, applying MIN filter
  1372. Texel = DoEnvLookup( iStage, TCoord, iFace, iLOD,
  1373. (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]) );
  1374. }
  1375. //-----------------------------------------------------------------------------
  1376. //
  1377. // DoEnvTrilerp - Does trilinear environment map filtering.
  1378. //
  1379. //-----------------------------------------------------------------------------
  1380. void
  1381. RRTexture::DoEnvTrilerp(INT32 iStage, RRTextureCoord& TCoord, INT16 iFace, INT16 iLOD, RRColor& Texel)
  1382. {
  1383. // clamp LOD to number of available levels
  1384. iLOD = MIN( iLOD, (m_cLOD)<<5 );
  1385. // compute index for two adjacent LODs (with clamp)
  1386. INT16 iLODHi = iLOD>>5; // floor
  1387. INT16 iLODLo = MIN(iLODHi+1,m_cLOD);
  1388. // check for filter type for within LOD map
  1389. BOOL bNearest = (D3DTFN_POINT == m_pStageState[iStage].m_dwVal[D3DTSS_MINFILTER]);
  1390. // trilerp - look up each map then lerp between them
  1391. // important for colorkey to not include texels with no contribution
  1392. if (0x00 != (iLOD&0x1f))
  1393. {
  1394. // trilerp - look up each map then lerp between them
  1395. RRColor Texel0 = DoEnvLookup(iStage, TCoord, iFace, iLODHi, bNearest);
  1396. RRColor Texel1 = DoEnvLookup(iStage, TCoord, iFace, iLODLo, bNearest);
  1397. LerpColor( Texel, Texel0, Texel1, iLOD&0x1f );
  1398. }
  1399. else
  1400. {
  1401. Texel = DoEnvLookup(iStage, TCoord, iFace, iLODHi, bNearest);
  1402. }
  1403. }
  1404. //
  1405. // uEnvEdgeTable
  1406. //
  1407. // This table looks up how to map a given U and V that are out of range
  1408. // on their primary face. The first index to the table is the current
  1409. // face. The second index is 0 if V is in range, 1 if V is negative
  1410. // and 2 if V is larger than the texture. Likewise, the last index is 0
  1411. // if U is in range, 1 if U is negative, and 2 if U is larger than
  1412. // than the texture.
  1413. //
  1414. // For the underdefined cases where 2 coordinates are out at the same time,
  1415. // we do the U wrap but not V.
  1416. //
  1417. // defines for the actions returned by the uEnvEdgeTable
  1418. //
  1419. #define EET_FACEMASK 0x07 // new face
  1420. #define EET_FU 0x10 // flip U on the texture map
  1421. #define EET_FV 0x20 // flip V on the texture map
  1422. #define EET_SUV 0x40 // swap U and V
  1423. //
  1424. // When both U and V are out, it is arbitrary which other
  1425. // face you pick. However, picking any one face other than the base
  1426. // face biases the result in visually disturbing ways. Therefore,
  1427. // take them both and average them.
  1428. //
  1429. static UINT8 uEnvEdgeTable[6][3][3] =
  1430. {
  1431. { // U0 NU PU // face 0
  1432. {0xff, 4, 5}, // V in range
  1433. {EET_FU|EET_SUV|2, 0xff, 0xff}, // V Neg
  1434. {EET_FV|EET_SUV|3, 0xff, 0xff}, // V too large
  1435. },
  1436. { // face 1
  1437. {0xff, 5, 4},
  1438. {EET_FV|EET_SUV|2, 0xff, 0xff},
  1439. {EET_FU|EET_SUV|3, 0xff, 0xff},
  1440. },
  1441. { // face 2
  1442. {0xff, EET_FU|EET_SUV|1, EET_FV|EET_SUV|0},
  1443. {EET_FU|EET_FV|5, 0xff, 0xff},
  1444. {4, 0xff, 0xff},
  1445. },
  1446. { // face 3
  1447. {0xff, EET_FV|EET_SUV|1, EET_FU|EET_SUV|0},
  1448. {4, 0xff, 0xff},
  1449. {EET_FU|EET_FV|5, 0xff, 0xff},
  1450. },
  1451. { // face 4
  1452. {0xff, 1, 0},
  1453. {2, 0xff, 0xff},
  1454. {3, 0xff, 0xff},
  1455. },
  1456. { // face 5
  1457. {0xff, 0, 1},
  1458. {EET_FU|EET_FV|2, 0xff, 0xff},
  1459. {EET_FU|EET_FV|3, 0xff, 0xff},
  1460. },
  1461. };
  1462. //-----------------------------------------------------------------------------
  1463. //
  1464. // DoTableInterp - Environment mapping utility.
  1465. // Interprets the edge table and does a lookup
  1466. //
  1467. //-----------------------------------------------------------------------------
  1468. void
  1469. RRTexture::DoTableInterp(INT16 iU, INT16 iV, INT16 iUMask, INT16 iVMask, INT16 iFace, INT16 iLOD,
  1470. UINT8 uUSign, UINT8 uVSign, RRColor &Texel, BOOL &bColorKeyMatched)
  1471. {
  1472. UINT8 uTable = uEnvEdgeTable[iFace][uVSign][uUSign];
  1473. _ASSERT( uTable != 0xff, "Illegal environment map lookup" );
  1474. if (uTable & EET_FU)
  1475. {
  1476. iU = iUMask - iU;
  1477. }
  1478. if (uTable & EET_FV)
  1479. {
  1480. iV = iVMask - iV;
  1481. }
  1482. if (uTable & EET_SUV)
  1483. {
  1484. INT16 iT = iU;
  1485. iU = iV;
  1486. iV = iT;
  1487. }
  1488. iFace = uTable & EET_FACEMASK;
  1489. ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched);
  1490. }
  1491. //-----------------------------------------------------------------------------
  1492. //
  1493. // DoEnvReMap - Environment mapping utility.
  1494. // Determines if either of the texture coordinates are out of range, and
  1495. // remaps the coordinate to the correct coordinate on the proper face of the
  1496. // environment cube.
  1497. //
  1498. //-----------------------------------------------------------------------------
  1499. void
  1500. RRTexture::DoEnvReMap(INT16 iU, INT16 iV, INT16 iUMask, INT16 iVMask, INT16 iFace, INT16 iLOD, RRColor &Texel,
  1501. BOOL &bColorKeyMatched)
  1502. {
  1503. UINT8 iUNeg = (UINT8)(iU < 0);
  1504. UINT8 iUPos = (UINT8)(iU > iUMask);
  1505. UINT8 iVNeg = (UINT8)(iV < 0);
  1506. UINT8 iVPos = (UINT8)(iV > iVMask);
  1507. if (!(iUNeg || iUPos || iVNeg || iVPos))
  1508. {
  1509. ReadColor(iU, iV, iFace + iLOD*6, Texel, bColorKeyMatched);
  1510. }
  1511. else
  1512. {
  1513. // put all U,V's in range with wrap function
  1514. INT16 iUMasked = iU & iUMask;
  1515. INT16 iVMasked = iV & iVMask;
  1516. INT16 iUClampd = min(max(iU, 0), iUMask);
  1517. INT16 iVClampd = min(max(iV, 0), iVMask);
  1518. UINT8 uUSign = (iUNeg) | (iUPos<<1);
  1519. UINT8 uVSign = (iVNeg) | (iVPos<<1);
  1520. if ((uVSign != 0) && (uUSign != 0))
  1521. {
  1522. // off the edge of the map in two directions. Go off each direction individually,
  1523. // and average the result.
  1524. RRColor Texel0, Texel1;
  1525. DoTableInterp(iUClampd, iVMasked, iUMask, iVMask, iFace, iLOD, 0, uVSign, Texel0, bColorKeyMatched);
  1526. DoTableInterp(iUMasked, iVClampd, iUMask, iVMask, iFace, iLOD, uUSign, 0, Texel1, bColorKeyMatched);
  1527. LerpColor( Texel, Texel0, Texel1, 0x10 );
  1528. }
  1529. else
  1530. {
  1531. DoTableInterp(iUMasked, iVMasked, iUMask, iVMask, iFace, iLOD, uUSign, uVSign, Texel, bColorKeyMatched);
  1532. }
  1533. }
  1534. }
  1535. //-----------------------------------------------------------------------------
  1536. //
  1537. // RRTexture::Initialize()
  1538. //
  1539. //-----------------------------------------------------------------------------
  1540. HRESULT
  1541. RRTexture::Initialize( LPDDRAWI_DDRAWSURFACE_LCL pLcl)
  1542. {
  1543. HRESULT hr = D3D_OK;
  1544. RRSurfaceType SurfType;
  1545. DDSCAPS2 ddscaps;
  1546. memset(&ddscaps, 0, sizeof(ddscaps));
  1547. m_iWidth = DDSurf_Width(pLcl);
  1548. m_iHeight = DDSurf_Height(pLcl);
  1549. m_cLOD = 0;
  1550. HR_RET(FindOutSurfFormat(&(DDSurf_PixFmt(pLcl)), &SurfType));
  1551. if ((SurfType == RR_STYPE_DXT1) ||
  1552. (SurfType == RR_STYPE_DXT2) ||
  1553. (SurfType == RR_STYPE_DXT3) ||
  1554. (SurfType == RR_STYPE_DXT4) ||
  1555. (SurfType == RR_STYPE_DXT5))
  1556. {
  1557. // Note, here is the assumption that:
  1558. // 1) width and height are reported correctly by the driver that
  1559. // created the surface
  1560. // 2) The allocation of the memory is contiguous (as done by hel)
  1561. m_iPitch[0] = ((m_iWidth+3)>>2) *
  1562. g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1];
  1563. }
  1564. else
  1565. {
  1566. m_iPitch[0] = DDSurf_Pitch(pLcl);
  1567. }
  1568. m_SurfType = SurfType;
  1569. if (SurfType == RR_STYPE_PALETTE8 ||
  1570. SurfType == RR_STYPE_PALETTE4)
  1571. {
  1572. if (pLcl->lpDDPalette)
  1573. {
  1574. LPDDRAWI_DDRAWPALETTE_GBL pPal = pLcl->lpDDPalette->lpLcl->lpGbl;
  1575. m_pPalette = (DWORD*)pPal->lpColorTable;
  1576. if (pPal->dwFlags & DDRAWIPAL_ALPHA)
  1577. {
  1578. m_uFlags |= RR_TEXTURE_ALPHAINPALETTE;
  1579. }
  1580. else
  1581. {
  1582. m_uFlags &= ~RR_TEXTURE_ALPHAINPALETTE;
  1583. }
  1584. }
  1585. }
  1586. if (!ValidTextureSize((INT16)m_iWidth, (INT16)IntLog2(m_iWidth),
  1587. (INT16)m_iHeight, (INT16)IntLog2(m_iHeight)))
  1588. {
  1589. return DDERR_INVALIDPARAMS;
  1590. }
  1591. if (pLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP)
  1592. {
  1593. m_uFlags |= RR_TEXTURE_ENVMAP;
  1594. LPDDRAWI_DDRAWSURFACE_LCL pDDSNextLcl;
  1595. ddscaps.dwCaps = DDSCAPS_TEXTURE;
  1596. if (!(pLcl->lpSurfMore->ddsCapsEx.dwCaps2 & DDSCAPS2_CUBEMAP_POSITIVEX))
  1597. {
  1598. ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP|DDSCAPS2_CUBEMAP_POSITIVEX;
  1599. hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pDDSNextLcl);
  1600. if ((hr != D3D_OK) && (hr != DDERR_NOTFOUND))
  1601. {
  1602. return hr;
  1603. }
  1604. if (hr == DDERR_NOTFOUND)
  1605. {
  1606. m_pDDSLcl[0] = NULL;
  1607. }
  1608. else
  1609. {
  1610. // use POSITIVEX surface to query others, if it exists
  1611. pLcl = pDDSNextLcl;
  1612. m_pDDSLcl[0] = pLcl;
  1613. }
  1614. }
  1615. else
  1616. {
  1617. m_pDDSLcl[0] = pLcl;
  1618. }
  1619. // get rest of top level surfaces, in order
  1620. for (INT i = 1; i < 6; i++)
  1621. {
  1622. switch(i)
  1623. {
  1624. case 1: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEX; break;
  1625. case 2: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEY; break;
  1626. case 3: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEY; break;
  1627. case 4: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_POSITIVEZ; break;
  1628. case 5: ddscaps.dwCaps2 = DDSCAPS2_CUBEMAP_NEGATIVEZ; break;
  1629. }
  1630. ddscaps.dwCaps2 |= DDSCAPS2_CUBEMAP;
  1631. hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pDDSNextLcl);
  1632. if ((hr != D3D_OK) && (hr != DDERR_NOTFOUND))
  1633. {
  1634. return hr;
  1635. }
  1636. if (hr == DDERR_NOTFOUND)
  1637. {
  1638. m_pDDSLcl[i] = NULL;
  1639. }
  1640. else
  1641. {
  1642. m_pDDSLcl[i] = pDDSNextLcl;
  1643. }
  1644. }
  1645. for (i = 0; i < 6; i++)
  1646. {
  1647. pLcl = m_pDDSLcl[i];
  1648. m_cLOD = 0;
  1649. if (pLcl)
  1650. {
  1651. // Check for mipmap if any.
  1652. LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl;
  1653. // iPreSizeU and iPreSizeV store the size(u and v) of the previous level
  1654. // mipmap. They are init'ed with the first texture size.
  1655. INT16 iPreSizeU = (INT16)m_iWidth, iPreSizeV = (INT16)m_iHeight;
  1656. for (;;)
  1657. {
  1658. ddscaps.dwCaps = DDSCAPS_TEXTURE;
  1659. ddscaps.dwCaps2 = DDSCAPS2_MIPMAPSUBLEVEL;
  1660. hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl);
  1661. if (hr != D3D_OK && hr != DDERR_NOTFOUND)
  1662. {
  1663. return hr;
  1664. }
  1665. if (hr == DDERR_NOTFOUND)
  1666. {
  1667. break;
  1668. }
  1669. m_cLOD ++;
  1670. pLcl = pTmpSLcl;
  1671. m_pDDSLcl[m_cLOD*6 + i] = pLcl;
  1672. if ((SurfType == RR_STYPE_DXT1) ||
  1673. (SurfType == RR_STYPE_DXT2) ||
  1674. (SurfType == RR_STYPE_DXT3) ||
  1675. (SurfType == RR_STYPE_DXT4) ||
  1676. (SurfType == RR_STYPE_DXT5))
  1677. {
  1678. // Note, here is the assumption that:
  1679. // 1) width and height are reported correctly by the driver that
  1680. // created the surface
  1681. // 2) The allocation of the memory is contiguous (as done by ddhel)
  1682. m_iPitch[m_cLOD] = (((m_iWidth>>m_cLOD)+3)>>2) *
  1683. g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1];
  1684. }
  1685. else
  1686. {
  1687. m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl);
  1688. }
  1689. m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl);
  1690. // Check for invalid mipmap texture size
  1691. if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) ||
  1692. !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl)))
  1693. {
  1694. return DDERR_INVALIDPARAMS;
  1695. }
  1696. iPreSizeU = (INT16)DDSurf_Width(pLcl);
  1697. iPreSizeV = (INT16)DDSurf_Height(pLcl);
  1698. }
  1699. }
  1700. }
  1701. }
  1702. else
  1703. {
  1704. if (pLcl->ddsCaps.dwCaps & DDSCAPS_ZBUFFER)
  1705. {
  1706. m_uFlags |= RR_TEXTURE_SHADOWMAP;
  1707. }
  1708. m_pDDSLcl[0] = pLcl;
  1709. // Check for mipmap if any.
  1710. LPDDRAWI_DDRAWSURFACE_LCL pTmpSLcl;
  1711. // iPreSizeU and iPreSizeV store the size(u and v) of the previous
  1712. // level mipmap. They are init'ed with the first texture size.
  1713. INT16 iPreSizeU = (INT16)m_iWidth, iPreSizeV = (INT16)m_iHeight;
  1714. for (;;)
  1715. {
  1716. ddscaps.dwCaps = DDSCAPS_TEXTURE;
  1717. hr = DDGetAttachedSurfaceLcl( pLcl, &ddscaps, &pTmpSLcl);
  1718. if (hr != D3D_OK && hr != DDERR_NOTFOUND)
  1719. {
  1720. return hr;
  1721. }
  1722. if (hr == DDERR_NOTFOUND)
  1723. {
  1724. break;
  1725. }
  1726. m_cLOD ++;
  1727. pLcl = pTmpSLcl;
  1728. m_pDDSLcl[m_cLOD] = pLcl;
  1729. if ((SurfType == RR_STYPE_DXT1) ||
  1730. (SurfType == RR_STYPE_DXT2) ||
  1731. (SurfType == RR_STYPE_DXT3) ||
  1732. (SurfType == RR_STYPE_DXT4) ||
  1733. (SurfType == RR_STYPE_DXT5))
  1734. {
  1735. // Note, here is the assumption that:
  1736. // 1) width and height are reported correctly by the driver that
  1737. // created the surface
  1738. // 2) The allocation of the memory is contiguous (as done by ddhel)
  1739. m_iPitch[m_cLOD] = (((m_iWidth>>m_cLOD)+3)>>2) *
  1740. g_DXTBlkSize[(int)SurfType - (int)RR_STYPE_DXT1];
  1741. }
  1742. else
  1743. {
  1744. m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl);
  1745. }
  1746. m_iPitch[m_cLOD] = DDSurf_Pitch(pLcl);
  1747. // Check for invalid mipmap texture size
  1748. if (!ValidMipmapSize(iPreSizeU, (INT16)DDSurf_Width(pLcl)) ||
  1749. !ValidMipmapSize(iPreSizeV, (INT16)DDSurf_Height(pLcl)))
  1750. {
  1751. return DDERR_INVALIDPARAMS;
  1752. }
  1753. iPreSizeU = (INT16)DDSurf_Width(pLcl);
  1754. iPreSizeV = (INT16)DDSurf_Height(pLcl);
  1755. }
  1756. }
  1757. m_cLODDDS = m_cLOD;
  1758. if ( !(Validate()) )
  1759. {
  1760. return DDERR_GENERIC;
  1761. }
  1762. return D3D_OK;
  1763. }
  1764. //-----------------------------------------------------------------------------
  1765. //
  1766. // Computes level of detail for environment mapping, looks better if
  1767. // we err on the side of fuzziness.
  1768. //
  1769. //-----------------------------------------------------------------------------
  1770. void
  1771. ComputeEnvMapLevelOfDetail( const RRTextureCoord& TCoord, FLOAT& fLOD )
  1772. {
  1773. // compute length of coverage in U and V axis
  1774. FLOAT fLenX = RR_LENGTH( TCoord.fDUDX, TCoord.fDVDX );
  1775. FLOAT fLenY = RR_LENGTH( TCoord.fDUDY, TCoord.fDVDY );
  1776. // take max
  1777. FLOAT fCoverage = MAX(fLenX, fLenY);
  1778. // take log2 of coverage for LOD
  1779. fLOD = RR_LOG2(fCoverage);
  1780. }
  1781. //-----------------------------------------------------------------------------
  1782. //
  1783. // RRTexture::DoTextureTransform - Performs the homogeneous texture transform.
  1784. //
  1785. //-----------------------------------------------------------------------------
  1786. void RRTexture::DoTextureTransform( INT32 iStage, BOOL bAlreadyXfmd,
  1787. FLOAT* pfC, FLOAT* pfO, FLOAT* pfQ )
  1788. {
  1789. LPD3DMATRIX pM = (LPD3DMATRIX)&m_pStageState[iStage].m_dwVal[D3DTSSI_MATRIX];
  1790. DWORD dwFlags = m_pStageState[iStage].m_dwVal[D3DTSS_TEXTURETRANSFORMFLAGS];
  1791. DWORD dwCount = dwFlags & (D3DTTFF_PROJECTED-1);
  1792. pfO[0] = pfC[0];
  1793. pfO[1] = pfC[1];
  1794. pfO[2] = pfC[2];
  1795. pfO[3] = pfC[3];
  1796. *pfQ = 1.0f;
  1797. if (dwCount != D3DTTFF_DISABLE)
  1798. {
  1799. if( bAlreadyXfmd == FALSE )
  1800. {
  1801. FLOAT x = pfC[0];
  1802. FLOAT y = pfC[1];
  1803. FLOAT z = pfC[2];
  1804. FLOAT w = pfC[3];
  1805. pfO[0] = x*pM->_11 + y*pM->_21 + z*pM->_31 + w*pM->_41;
  1806. pfO[1] = x*pM->_12 + y*pM->_22 + z*pM->_32 + w*pM->_42;
  1807. pfO[2] = x*pM->_13 + y*pM->_23 + z*pM->_33 + w*pM->_43;
  1808. pfO[3] = x*pM->_14 + y*pM->_24 + z*pM->_34 + w*pM->_44;
  1809. }
  1810. if (dwFlags & D3DTTFF_PROJECTED)
  1811. {
  1812. DWORD dwQI = dwCount - 1;
  1813. _ASSERT((dwQI >= 1)&&(dwQI <= 3), "Illegal D3DTTFF_COUNT with D3DTTFF_PROJECTED");
  1814. *pfQ = pfO[dwQI];
  1815. }
  1816. }
  1817. }
  1818. ///////////////////////////////////////////////////////////////////////////////
  1819. // end