Team Fortress 2 Source Code as on 22/4/2020
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.

928 lines
23 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include "stdafx.h"
  8. #include "d3dapp.h"
  9. #include "d3dx8math.h"
  10. #include "mathlib/mathlib.h"
  11. #include "ScratchPad3D.h"
  12. #include "tier1/strtools.h"
  13. #define SPColorExpand( c ) (c).m_vColor.x, (c).m_vColor.y, (c).m_vColor.z, (c).m_flAlpha
  14. class VertPosDiffuse
  15. {
  16. public:
  17. inline void Init(Vector const &vPos, float r, float g, float b, float a)
  18. {
  19. m_Pos = vPos;
  20. SetDiffuse( r, g, b, a );
  21. }
  22. inline void SetDiffuse( float r, float g, float b, float a )
  23. {
  24. m_Diffuse[0] = (unsigned char)(b * 255.9f);
  25. m_Diffuse[1] = (unsigned char)(g * 255.9f);
  26. m_Diffuse[2] = (unsigned char)(r * 255.9f);
  27. m_Diffuse[3] = (unsigned char)(a * 255.9f);
  28. }
  29. inline void SetDiffuse( Vector const &vColor )
  30. {
  31. SetDiffuse( vColor.x, vColor.y, vColor.z, 1 );
  32. }
  33. inline void SetTexCoords( const Vector2D &tCoords )
  34. {
  35. m_tCoords = tCoords;
  36. }
  37. static inline DWORD GetFVF() {return D3DFVF_DIFFUSE | D3DFVF_XYZ | D3DFVF_TEX1;}
  38. Vector m_Pos;
  39. unsigned char m_Diffuse[4];
  40. Vector2D m_tCoords;
  41. };
  42. class PosController
  43. {
  44. public:
  45. Vector m_vPos;
  46. QAngle m_vAngles;
  47. };
  48. int g_nLines, g_nPolygons;
  49. PosController g_ViewController;
  50. Vector g_IdentityBasis[3] = {Vector(1,0,0), Vector(0,1,0), Vector(0,0,1)};
  51. Vector g_ViewerPos;
  52. Vector g_ViewerBasis[3];
  53. VMatrix g_mModelView;
  54. CScratchPad3D *g_pScratchPad = NULL;
  55. FILETIME g_LastWriteTime;
  56. char g_Filename[256];
  57. // ------------------------------------------------------------------------------------------ //
  58. // Helper functions.
  59. // ------------------------------------------------------------------------------------------ //
  60. inline float FClamp(float val, float min, float max)
  61. {
  62. return (val < min) ? min : (val > max ? max : val);
  63. }
  64. inline float FMin(float val1, float val2)
  65. {
  66. return (val1 < val2) ? val1 : val2;
  67. }
  68. inline float FMax(float val1, float val2)
  69. {
  70. return (val1 > val2) ? val1 : val2;
  71. }
  72. inline float CosDegrees(float angle)
  73. {
  74. return (float)cos(DEG2RAD(angle));
  75. }
  76. inline float SinDegrees(float angle)
  77. {
  78. return (float)sin(DEG2RAD(angle));
  79. }
  80. inline float FRand(float a, float b)
  81. {
  82. return a + (b - a) * ((float)rand() / VALVE_RAND_MAX);
  83. }
  84. void CheckResult( HRESULT hr )
  85. {
  86. if ( FAILED( hr ) )
  87. {
  88. Assert( 0 );
  89. }
  90. }
  91. void DrawLine2(const Vector &vFrom, const Vector &vTo, float r1, float g1, float b1, float a1, float r2, float g2, float b2, float a2)
  92. {
  93. VertPosDiffuse verts[2];
  94. verts[0].Init( vFrom, r1, g1, b1, 1 );
  95. verts[1].Init( vTo, r2, g2, b2, 1 );
  96. CheckResult ( g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE ) );
  97. CheckResult( g_pDevice->SetTexture( 0, NULL ) );
  98. CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_LINELIST, 1, verts, sizeof(verts[0]) ) );
  99. ++g_nLines;
  100. }
  101. void DrawLine(const Vector &vFrom, const Vector &vTo, float r, float g, float b, float a)
  102. {
  103. DrawLine2(vFrom, vTo, r,g,b,a, r,g,b,a);
  104. }
  105. // zAngle's range is [-90,90].
  106. // When zAngle is 0, the position is in the middle of the sphere (vertically).
  107. // When zAngle is 90, the position is at the top of the sphere.
  108. // When zAngle is -90, the position is at the bottom of the sphere.
  109. Vector CalcSphereVecAngles(float xyAngle, float zAngle, float fRadius)
  110. {
  111. Vector vec;
  112. vec.x = CosDegrees(xyAngle) * CosDegrees(zAngle);
  113. vec.y = SinDegrees(xyAngle) * CosDegrees(zAngle);
  114. vec.z = SinDegrees(zAngle);
  115. return vec * fRadius;
  116. }
  117. // Figure out the rotation to look from vEye to vDest.
  118. void SetupLookAt( const Vector &vEye, const Vector &vDest, Vector basis[3] )
  119. {
  120. basis[0] = (vDest - vEye); // Forward.
  121. VectorNormalize( basis[0] );
  122. basis[2].Init(0.0f, 0.0f, 1.0f); // Up.
  123. basis[1] = basis[2].Cross(basis[0]); // Left.
  124. VectorNormalize( basis[1] );
  125. basis[2] = basis[0].Cross(basis[1]); // Regenerate up.
  126. VectorNormalize( basis[2] );
  127. }
  128. D3DMATRIX* VEngineToTempD3DMatrix( VMatrix const &mat )
  129. {
  130. static VMatrix ret;
  131. ret = mat.Transpose();
  132. return (D3DMATRIX*)&ret;
  133. }
  134. void UpdateView(float mouseDeltaX, float mouseDeltaY)
  135. {
  136. VMatrix mRot;
  137. PosController *pController;
  138. pController = &g_ViewController;
  139. // WorldCraft-like interface..
  140. if( Sys_HasFocus() )
  141. {
  142. Vector vForward, vUp, vRight;
  143. AngleVectors( pController->m_vAngles, &vForward, &vRight, &vUp );
  144. static float fAngleScale = 0.4f;
  145. static float fDistScale = 0.5f;
  146. if( Sys_GetKeyState( APPKEY_LBUTTON ) )
  147. {
  148. if( Sys_GetKeyState( APPKEY_RBUTTON ) )
  149. {
  150. // Ok, move forward and backwards.
  151. pController->m_vPos += vForward * -mouseDeltaY * fDistScale;
  152. pController->m_vPos += vRight * mouseDeltaX * fDistScale;
  153. }
  154. else
  155. {
  156. pController->m_vAngles.y += -mouseDeltaX * fAngleScale;
  157. pController->m_vAngles.x += mouseDeltaY * fAngleScale;
  158. }
  159. }
  160. else if( Sys_GetKeyState( APPKEY_RBUTTON ) )
  161. {
  162. pController->m_vPos += vUp * -mouseDeltaY * fDistScale;
  163. pController->m_vPos += vRight * mouseDeltaX * fDistScale;
  164. }
  165. }
  166. // Set the projection matrix to 90 degrees.
  167. D3DXMATRIX matProj;
  168. D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, Sys_ScreenWidth() / (float)Sys_ScreenHeight(), 1.0f, 10000.0f );
  169. g_pDevice->SetTransform( D3DTS_PROJECTION, &matProj );
  170. // This matrix converts from D3D coordinates (X=right, Y=up, Z=forward)
  171. // to VEngine coordinates (X=forward, Y=left, Z=up).
  172. VMatrix mD3DToVEngine(
  173. 0.0f, 0.0f, 1.0f, 0.0f,
  174. -1.0f, 0.0f, 0.0f, 0.0f,
  175. 0.0f, 1.0f, 0.0f, 0.0f,
  176. 0.0f, 0.0f, 0.0f, 1.0f);
  177. g_ViewerPos = pController->m_vPos;
  178. mRot = SetupMatrixAngles( pController->m_vAngles );
  179. g_mModelView = ~mD3DToVEngine * mRot.Transpose3x3() * SetupMatrixTranslation(-g_ViewerPos);
  180. CheckResult( g_pDevice->SetTransform( D3DTS_VIEW, VEngineToTempD3DMatrix(g_mModelView) ) );
  181. // World matrix is identity..
  182. VMatrix mIdentity = SetupMatrixIdentity();
  183. CheckResult( g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)&mIdentity ) );
  184. }
  185. // ------------------------------------------------------------------------------------------ //
  186. // ScratchPad3D command implementation.
  187. // ------------------------------------------------------------------------------------------ //
  188. void CommandRender_Point( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  189. {
  190. CScratchPad3D::CCommand_Point *pCmd = (CScratchPad3D::CCommand_Point*)pInCmd;
  191. g_pDevice->SetRenderState( D3DRS_POINTSIZE, *((DWORD*)&pCmd->m_flPointSize) );
  192. VertPosDiffuse vert;
  193. vert.Init( pCmd->m_Vert.m_vPos, SPColorExpand(pCmd->m_Vert.m_vColor) );
  194. g_pDevice->DrawPrimitiveUP( D3DPT_POINTLIST, 1, &vert, sizeof(vert) );
  195. }
  196. VertPosDiffuse g_LineBatchVerts[1024];
  197. int g_nLineBatchVerts = 0;
  198. void CommandRender_LinesStart( IDirect3DDevice8 *pDevice )
  199. {
  200. // Set states for line drawing.
  201. CheckResult( g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, TRUE ) );
  202. CheckResult( g_pDevice->SetTexture( 0, NULL ) );
  203. }
  204. void CommandRender_LinesStop( IDirect3DDevice8 *pDevice )
  205. {
  206. CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_LINELIST, g_nLineBatchVerts / 2, g_LineBatchVerts, sizeof(g_LineBatchVerts[0]) ) );
  207. g_nLines += g_nLineBatchVerts / 2;
  208. g_nLineBatchVerts = 0;
  209. }
  210. void CommandRender_Line( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  211. {
  212. CScratchPad3D::CCommand_Line *pCmd = (CScratchPad3D::CCommand_Line*)pInCmd;
  213. // Flush out the line cache?
  214. if ( g_nLineBatchVerts == sizeof( g_LineBatchVerts ) / sizeof( g_LineBatchVerts[0] ) )
  215. {
  216. CommandRender_LinesStop( pDevice );
  217. }
  218. g_LineBatchVerts[g_nLineBatchVerts].m_Pos = pCmd->m_Verts[0].m_vPos;
  219. g_LineBatchVerts[g_nLineBatchVerts].SetDiffuse( SPColorExpand( pCmd->m_Verts[0].m_vColor ) );
  220. ++g_nLineBatchVerts;
  221. g_LineBatchVerts[g_nLineBatchVerts].m_Pos = pCmd->m_Verts[1].m_vPos;
  222. g_LineBatchVerts[g_nLineBatchVerts].SetDiffuse( SPColorExpand( pCmd->m_Verts[1].m_vColor ) );
  223. ++g_nLineBatchVerts;
  224. }
  225. void CommandRender_Polygon( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  226. {
  227. VertPosDiffuse verts[65];
  228. CScratchPad3D::CCommand_Polygon *pCmd = (CScratchPad3D::CCommand_Polygon*)pInCmd;
  229. int nVerts = min( 64, pCmd->m_Verts.Size() );
  230. for( int i=0; i < nVerts; i++ )
  231. {
  232. verts[i].m_Pos[0] = pCmd->m_Verts[i].m_vPos.x;
  233. verts[i].m_Pos[1] = pCmd->m_Verts[i].m_vPos.y;
  234. verts[i].m_Pos[2] = pCmd->m_Verts[i].m_vPos.z;
  235. verts[i].SetDiffuse( SPColorExpand( pCmd->m_Verts[i].m_vColor ) );
  236. }
  237. // Draw wireframe manually since D3D draws internal edges of the triangle fan.
  238. DWORD dwFillMode;
  239. g_pDevice->GetRenderState( D3DRS_FILLMODE, &dwFillMode );
  240. if( dwFillMode == D3DFILL_WIREFRAME )
  241. {
  242. if( nVerts >= 2 )
  243. {
  244. g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, nVerts-1, verts, sizeof(verts[0]) );
  245. verts[nVerts] = verts[0];
  246. g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 1, &verts[nVerts-1], sizeof(verts[0]) );
  247. }
  248. }
  249. else
  250. {
  251. CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, nVerts - 2, verts, sizeof(verts[0]) ) );
  252. }
  253. ++g_nPolygons;
  254. }
  255. void CommandRender_Matrix( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  256. {
  257. CScratchPad3D::CCommand_Matrix *pCmd = (CScratchPad3D::CCommand_Matrix*)pInCmd;
  258. VMatrix mTransposed = pCmd->m_mMatrix.Transpose();
  259. CheckResult( g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)mTransposed.m ) );
  260. }
  261. void CommandRender_RenderState( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  262. {
  263. CScratchPad3D::CCommand_RenderState *pCmd = (CScratchPad3D::CCommand_RenderState*)pInCmd;
  264. switch( pCmd->m_State )
  265. {
  266. case IScratchPad3D::RS_FillMode:
  267. {
  268. if( pCmd->m_Val == IScratchPad3D::FillMode_Wireframe )
  269. g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
  270. else
  271. g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  272. }
  273. break;
  274. case IScratchPad3D::RS_ZRead:
  275. {
  276. g_pDevice->SetRenderState( D3DRS_ZENABLE, pCmd->m_Val );
  277. }
  278. break;
  279. case IScratchPad3D::RS_ZBias:
  280. {
  281. g_pDevice->SetRenderState( D3DRS_ZBIAS, pCmd->m_Val );
  282. }
  283. break;
  284. }
  285. }
  286. class CCachedTextData : public CScratchPad3D::ICachedRenderData
  287. {
  288. public:
  289. CCachedTextData()
  290. {
  291. m_pTexture = NULL;
  292. }
  293. ~CCachedTextData()
  294. {
  295. if ( m_pTexture )
  296. m_pTexture->Release();
  297. }
  298. virtual void Release()
  299. {
  300. delete this;
  301. }
  302. IDirect3DTexture8 *m_pTexture;
  303. int m_BitmapWidth;
  304. int m_BitmapHeight;
  305. int m_nChars;
  306. };
  307. void GenerateTextGreyscaleBitmap(
  308. const char *pText,
  309. CUtlVector<unsigned char> &bitmap,
  310. int *pWidth,
  311. int *pHeight )
  312. {
  313. *pWidth = *pHeight = 0;
  314. // Create a bitmap, font, and HDC.
  315. HDC hDC = CreateCompatibleDC( NULL );
  316. Assert( hDC );
  317. HFONT hFont = ::CreateFontA(
  318. 18, // font height
  319. 0, 0, 0,
  320. FW_MEDIUM,
  321. false,
  322. false,
  323. false,
  324. ANSI_CHARSET,
  325. OUT_DEFAULT_PRECIS,
  326. CLIP_DEFAULT_PRECIS,
  327. ANTIALIASED_QUALITY,
  328. DEFAULT_PITCH | FF_DONTCARE,
  329. "Arial" );
  330. Assert( hDC );
  331. if ( !hFont )
  332. {
  333. DeleteDC( hDC );
  334. return;
  335. }
  336. // Create a bitmap. Allow for width of 512. Hopefully, that can fit all the text we need.
  337. int bigImageWidth = 512;
  338. int bigImageHeight = 64;
  339. BITMAPINFOHEADER bmi;
  340. memset( &bmi, 0, sizeof( bmi ) );
  341. bmi.biSize = sizeof(BITMAPINFOHEADER);
  342. bmi.biWidth = bigImageWidth;
  343. bmi.biHeight = -bigImageHeight;
  344. bmi.biBitCount = 24;
  345. bmi.biPlanes = 1;
  346. bmi.biCompression = BI_RGB;
  347. void *pBits = NULL;
  348. HBITMAP hBitmap = CreateDIBSection( hDC,
  349. (BITMAPINFO*)&bmi, DIB_RGB_COLORS, (void**)&pBits, NULL, 0 );
  350. Assert( hBitmap && pBits );
  351. if ( !hBitmap )
  352. {
  353. DeleteObject( hFont );
  354. DeleteDC( hDC );
  355. return;
  356. }
  357. // Select the font and bitmap into the DC.
  358. HFONT hOldFont = (HFONT)SelectObject( hDC, hFont );
  359. HBITMAP hOldBitmap = (HBITMAP)SelectObject( hDC, hBitmap );
  360. // Draw the text into the DC.
  361. SIZE size;
  362. int textLen = strlen( pText );
  363. GetTextExtentPoint32( hDC, pText, textLen, &size );
  364. TextOut( hDC, 0, 0, pText, textLen );
  365. // Now copy the bits out.
  366. const unsigned char *pSrcBase = (const unsigned char*)pBits;
  367. *pWidth = size.cx;
  368. *pHeight = size.cy;
  369. bitmap.SetSize( size.cy * size.cx );
  370. for ( int y=0; y < size.cy; y++ )
  371. {
  372. for ( int x=0; x < size.cx; x++ )
  373. {
  374. const unsigned char *pSrc = &pSrcBase[ (y*bigImageWidth+x) * 3 ];
  375. unsigned char *pDest = &bitmap[y * size.cx + x];
  376. int avg = (pSrc[0] + pSrc[1] + pSrc[2]) / 3;
  377. *pDest = 0xFF - (unsigned char)avg;
  378. }
  379. }
  380. // Unselect the objects from the DC and cleanup everything.
  381. SelectObject( hDC, hOldFont );
  382. DeleteObject( hFont );
  383. SelectObject( hDC, hOldBitmap );
  384. DeleteObject( hBitmap );
  385. DeleteDC( hDC );
  386. }
  387. IDirect3DTexture8* MakeD3DTextureFromBitmap(
  388. CUtlVector<unsigned char> &bitmap,
  389. int width,
  390. int height,
  391. bool bSolidBackground )
  392. {
  393. IDirect3DTexture8 *pRet = NULL;
  394. HRESULT hr = g_pDevice->CreateTexture(
  395. width,
  396. height,
  397. 1,
  398. 0,
  399. D3DFMT_A8R8G8B8,
  400. D3DPOOL_MANAGED,
  401. &pRet );
  402. if ( !pRet || FAILED( hr ) )
  403. return NULL;
  404. // Lock the texture and fill it up.
  405. D3DLOCKED_RECT lockedRect;
  406. hr = pRet->LockRect( 0, &lockedRect, NULL, 0 );
  407. if ( FAILED( hr ) )
  408. {
  409. Assert( false );
  410. pRet->Release();
  411. return NULL;
  412. }
  413. // Now fill it up.
  414. unsigned char *pDestData = (unsigned char*)lockedRect.pBits;
  415. for ( int y=0; y < height; y++ )
  416. {
  417. for ( int x=0; x < width; x++ )
  418. {
  419. unsigned char *pDestPixel = &pDestData[ (y*lockedRect.Pitch + x*4) ];
  420. unsigned char cSrcColor = bitmap[y*width+x];
  421. if ( bSolidBackground )
  422. {
  423. pDestPixel[3] = 0xFF;
  424. pDestPixel[0] = pDestPixel[1] = pDestPixel[2] = cSrcColor;
  425. }
  426. else
  427. {
  428. pDestPixel[0] = pDestPixel[1] = pDestPixel[2] = pDestPixel[3] = 0xFF;
  429. pDestPixel[3] = cSrcColor;
  430. }
  431. }
  432. }
  433. pRet->UnlockRect( 0 );
  434. return pRet;
  435. }
  436. void CommandRender_Text( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice )
  437. {
  438. CScratchPad3D::CCommand_Text *pCmd = (CScratchPad3D::CCommand_Text*)pInCmd;
  439. CTextParams *pParams = &pCmd->m_TextParams;
  440. // First, see if we've already generated the texture for this string.
  441. CCachedTextData *pCached = (CCachedTextData*)pCmd->m_pCachedRenderData;
  442. if ( !pCached )
  443. {
  444. // Generate a bitmap for this text string.
  445. CUtlVector<unsigned char> bitmap;
  446. int width, height;
  447. GenerateTextGreyscaleBitmap( pCmd->m_String.Base(), bitmap, &width, &height );
  448. // Convert the bitmap into a D3D texture.
  449. pCached = new CCachedTextData;
  450. pCached->m_pTexture = MakeD3DTextureFromBitmap( bitmap, width, height, pParams->m_bSolidBackground );
  451. pCached->m_BitmapWidth = width;
  452. pCached->m_BitmapHeight = height;
  453. pCached->m_nChars = strlen( pCmd->m_String.Base() );
  454. // Cache it.
  455. pCmd->m_pCachedRenderData = pCached;
  456. }
  457. // Figure out its orientation vectors.
  458. Vector vForward, vRight, vUp;
  459. AngleVectors( pParams->m_vAngles, &vForward, &vRight, &vUp );
  460. // Backface removal?
  461. bool bFlip = true;
  462. if ( vForward.Dot( g_ViewerPos - pParams->m_vPos ) < 0 )
  463. {
  464. if ( pParams->m_bTwoSided )
  465. bFlip = false;
  466. else
  467. return;
  468. }
  469. // This is really kludgy, but it's the best info we have here.
  470. float flTotalWidth = pParams->m_flLetterWidth * pCached->m_nChars;
  471. float flAvgCharWidth = (float)flTotalWidth / pCached->m_nChars;
  472. float flTotalHeight = flAvgCharWidth * 3;
  473. Vector vShift( 0, 0, 0 );
  474. if ( pParams->m_bCentered )
  475. vShift = vRight * ( -flTotalWidth/2 ) + vUp * ( flTotalHeight/2 );
  476. // Now draw the quad with the texture in it.
  477. VertPosDiffuse quad[5]; // Leave space for 1 more for the line strip for the border.
  478. quad[0].m_Pos = pParams->m_vPos;
  479. quad[1].m_Pos = pParams->m_vPos + vRight * flTotalWidth;
  480. quad[2].m_Pos = quad[1].m_Pos - vUp * flTotalHeight;
  481. quad[3].m_Pos = pParams->m_vPos - vUp * flTotalHeight;
  482. // Set tex coords.
  483. if ( bFlip )
  484. {
  485. quad[0].m_tCoords.Init( 1, 0 );
  486. quad[1].m_tCoords.Init( 0, 0 );
  487. quad[2].m_tCoords.Init( 0, 1 );
  488. quad[3].m_tCoords.Init( 1, 1 );
  489. }
  490. else
  491. {
  492. quad[0].m_tCoords.Init( 0, 0 );
  493. quad[1].m_tCoords.Init( 1, 0 );
  494. quad[2].m_tCoords.Init( 1, 1 );
  495. quad[3].m_tCoords.Init( 0, 1 );
  496. }
  497. for ( int i=0; i < 4; i++ )
  498. {
  499. quad[i].m_Pos += vShift;
  500. quad[i].SetDiffuse( pParams->m_vColor.x, pParams->m_vColor.y, pParams->m_vColor.z, pParams->m_flAlpha );
  501. }
  502. // Draw.
  503. // Backup render states.
  504. DWORD tss[][3] = {
  505. { D3DTSS_COLOROP, D3DTOP_MODULATE, 0 },
  506. { D3DTSS_COLORARG1, D3DTA_DIFFUSE, 0 },
  507. { D3DTSS_COLORARG2, D3DTA_TEXTURE, 0 },
  508. { D3DTSS_ALPHAOP, D3DTOP_MODULATE, 0 },
  509. { D3DTSS_ALPHAARG1, D3DTA_DIFFUSE, 0 },
  510. { D3DTSS_ALPHAARG2, D3DTA_TEXTURE, 0 }
  511. };
  512. #define NUM_TSS ( sizeof( tss ) / sizeof( tss[0] ) )
  513. DWORD rss[][3] = {
  514. { D3DRS_ALPHABLENDENABLE, TRUE, 0 },
  515. { D3DRS_FILLMODE, D3DFILL_SOLID, 0 },
  516. { D3DRS_SRCBLEND, D3DBLEND_SRCALPHA, 0 },
  517. { D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA, 0 },
  518. { D3DRS_FILLMODE, D3DFILL_SOLID, 0 }
  519. };
  520. #define NUM_RSS ( sizeof( rss ) / sizeof( rss[0] ) )
  521. for ( int i=0; i < NUM_TSS; i++ )
  522. {
  523. g_pDevice->GetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], &tss[i][2] );
  524. g_pDevice->SetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], tss[i][1] );
  525. }
  526. for ( int i=0; i < NUM_RSS; i++ )
  527. {
  528. g_pDevice->GetRenderState( (D3DRENDERSTATETYPE)rss[i][0], &rss[i][2] );
  529. g_pDevice->SetRenderState( (D3DRENDERSTATETYPE)rss[i][0], rss[i][1] );
  530. }
  531. g_pDevice->SetTexture( 0, pCached->m_pTexture );
  532. CheckResult( g_pDevice->DrawPrimitiveUP( D3DPT_TRIANGLEFAN, 2, quad, sizeof(quad[0]) ) );
  533. g_pDevice->SetTexture( 0, NULL );
  534. ++g_nPolygons;
  535. // Restore render states.
  536. for ( int i=0; i < NUM_TSS; i++ )
  537. g_pDevice->SetTextureStageState( 0, (D3DTEXTURESTAGESTATETYPE)tss[i][0], tss[i][2] );
  538. for ( int i=0; i < NUM_RSS; i++ )
  539. g_pDevice->SetRenderState( (D3DRENDERSTATETYPE)rss[i][0], rss[i][2] );
  540. // Draw wireframe outline..
  541. if ( pParams->m_bOutline )
  542. {
  543. DWORD fillMode;
  544. g_pDevice->GetRenderState( D3DRS_FILLMODE, &fillMode );
  545. g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_WIREFRAME );
  546. quad[4] = quad[0];
  547. g_pDevice->DrawPrimitiveUP( D3DPT_LINESTRIP, 4, quad, sizeof(quad[0]) );
  548. g_pDevice->SetRenderState( D3DRS_FILLMODE, fillMode );
  549. }
  550. }
  551. typedef void (*CommandRenderFunction_Start)( IDirect3DDevice8 *pDevice );
  552. typedef void (*CommandRenderFunction_Stop)( IDirect3DDevice8 *pDevice );
  553. typedef void (*CommandRenderFunction)( CScratchPad3D::CBaseCommand *pInCmd, IDirect3DDevice8 *pDevice );
  554. class CCommandRenderFunctions
  555. {
  556. public:
  557. CommandRenderFunction_Start m_StartFn;
  558. CommandRenderFunction_Start m_StopFn;
  559. CommandRenderFunction m_RenderFn;
  560. };
  561. CCommandRenderFunctions g_CommandRenderFunctions[CScratchPad3D::COMMAND_NUMCOMMANDS] =
  562. {
  563. { NULL, NULL, CommandRender_Point },
  564. { CommandRender_LinesStart, CommandRender_LinesStop, CommandRender_Line },
  565. { NULL, NULL, CommandRender_Polygon },
  566. { NULL, NULL, CommandRender_Matrix },
  567. { NULL, NULL, CommandRender_RenderState },
  568. { NULL, NULL, CommandRender_Text }
  569. };
  570. void RunCommands( )
  571. {
  572. // Set all the initial states.
  573. g_pDevice->SetRenderState( D3DRS_FILLMODE, D3DFILL_SOLID );
  574. g_pDevice->SetRenderState( D3DRS_ZENABLE, D3DZB_TRUE );
  575. VMatrix mIdentity = SetupMatrixIdentity();
  576. g_pDevice->SetTransform( D3DTS_WORLD, (D3DMATRIX*)&mIdentity );
  577. int iLastCmd = -1;
  578. for( int i=0; i < g_pScratchPad->m_Commands.Size(); i++ )
  579. {
  580. CScratchPad3D::CBaseCommand *pCmd = g_pScratchPad->m_Commands[i];
  581. if( pCmd->m_iCommand >= 0 && pCmd->m_iCommand < CScratchPad3D::COMMAND_NUMCOMMANDS )
  582. {
  583. // Call the start/stop handlers for this command type if they exist.
  584. // These can be used to batch primitives.
  585. if ( pCmd->m_iCommand != iLastCmd )
  586. {
  587. if ( iLastCmd != -1 )
  588. {
  589. if ( g_CommandRenderFunctions[iLastCmd].m_StopFn )
  590. g_CommandRenderFunctions[iLastCmd].m_StopFn( g_pDevice );
  591. }
  592. iLastCmd = pCmd->m_iCommand;
  593. if ( g_CommandRenderFunctions[pCmd->m_iCommand].m_StartFn )
  594. g_CommandRenderFunctions[pCmd->m_iCommand].m_StartFn( g_pDevice );
  595. }
  596. g_CommandRenderFunctions[pCmd->m_iCommand].m_RenderFn( pCmd, g_pDevice );
  597. }
  598. }
  599. // Call the final stop function.
  600. if ( iLastCmd != -1 )
  601. {
  602. if ( g_CommandRenderFunctions[iLastCmd].m_StopFn )
  603. g_CommandRenderFunctions[iLastCmd].m_StopFn( g_pDevice );
  604. }
  605. }
  606. bool CheckForNewFile( bool bForce )
  607. {
  608. // See if the file has changed..
  609. HANDLE hFile = CreateFile(
  610. g_pScratchPad->m_pFilename,
  611. GENERIC_READ,
  612. FILE_SHARE_READ,
  613. NULL,
  614. OPEN_EXISTING,
  615. 0,
  616. NULL );
  617. if( !hFile )
  618. return false;
  619. FILETIME createTime, accessTime, writeTime;
  620. if( !GetFileTime( hFile, &createTime, &accessTime, &writeTime ) )
  621. {
  622. CloseHandle( hFile );
  623. return false;
  624. }
  625. bool bChange = false;
  626. if( memcmp(&writeTime, &g_LastWriteTime, sizeof(writeTime)) != 0 || bForce )
  627. {
  628. bChange = g_pScratchPad->LoadCommandsFromFile();
  629. if( bChange )
  630. {
  631. memcpy( &g_LastWriteTime, &writeTime, sizeof(writeTime) );
  632. }
  633. }
  634. CloseHandle( hFile );
  635. return bChange;
  636. }
  637. // ------------------------------------------------------------------------------------------ //
  638. // App callbacks.
  639. // ------------------------------------------------------------------------------------------ //
  640. void UpdateWindowText()
  641. {
  642. char str[512];
  643. sprintf( str, "ScratchPad3DViewer: <%s> lines: %d, polygons: %d", g_Filename, g_nLines, g_nPolygons );
  644. Sys_SetWindowText( str );
  645. }
  646. void AppInit()
  647. {
  648. // Viewer info.
  649. g_ViewController.m_vPos.Init( -200, 0, 0 );
  650. g_ViewController.m_vAngles.Init( 0, 0, 0 );
  651. char const *pFilename = Sys_FindArg( "-file", "scratch.pad" );
  652. Q_strncpy( g_Filename, pFilename, sizeof( g_Filename ) );
  653. IFileSystem *pFileSystem = ScratchPad3D_SetupFileSystem();
  654. if( !pFileSystem || pFileSystem->Init() != INIT_OK )
  655. {
  656. Sys_Quit();
  657. }
  658. // FIXME: I took this out of scratchpad 3d, not sure if this is even necessary any more
  659. pFileSystem->AddSearchPath( ".", "PLATFORM" );
  660. g_pScratchPad = new CScratchPad3D( pFilename, pFileSystem, false );
  661. g_nLines = g_nPolygons = 0;
  662. UpdateWindowText();
  663. g_pDevice->SetRenderState( D3DRS_EDGEANTIALIAS, FALSE );
  664. g_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE );
  665. g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
  666. g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_DIFFUSE );
  667. g_pDevice->SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
  668. g_pDevice->SetTexture( 0, NULL );
  669. // Setup point scaling parameters.
  670. float flOne=1;
  671. float flZero=0;
  672. g_pDevice->SetRenderState( D3DRS_POINTSCALEENABLE, TRUE );
  673. g_pDevice->SetRenderState( D3DRS_POINTSCALE_A, *((DWORD*)&flZero) );
  674. g_pDevice->SetRenderState( D3DRS_POINTSCALE_B, *((DWORD*)&flZero) );
  675. g_pDevice->SetRenderState( D3DRS_POINTSCALE_C, *((DWORD*)&flOne) );
  676. memset( &g_LastWriteTime, 0, sizeof(g_LastWriteTime) );
  677. }
  678. void AppRender( float frametime, float mouseDeltaX, float mouseDeltaY, bool bInvalidRect )
  679. {
  680. g_nLines = 0;
  681. g_nPolygons = 0;
  682. g_pDevice->SetVertexShader( VertPosDiffuse::GetFVF() );
  683. if( !bInvalidRect &&
  684. !Sys_GetKeyState( APPKEY_LBUTTON ) &&
  685. !Sys_GetKeyState( APPKEY_RBUTTON ) &&
  686. !CheckForNewFile(false) )
  687. {
  688. Sys_Sleep( 100 );
  689. return;
  690. }
  691. g_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0, 1, 0 );
  692. g_pDevice->BeginScene();
  693. UpdateView( mouseDeltaX, mouseDeltaY );
  694. RunCommands();
  695. g_pDevice->EndScene();
  696. g_pDevice->Present( NULL, NULL, NULL, NULL );
  697. UpdateWindowText();
  698. }
  699. void AppPreResize()
  700. {
  701. for( int i=0; i < g_pScratchPad->m_Commands.Size(); i++ )
  702. {
  703. CScratchPad3D::CBaseCommand *pCmd = g_pScratchPad->m_Commands[i];
  704. if ( pCmd->m_iCommand == CScratchPad3D::COMMAND_TEXT )
  705. {
  706. // Delete the cached data if there is any.
  707. pCmd->ReleaseCachedRenderData();
  708. }
  709. }
  710. }
  711. void AppPostResize()
  712. {
  713. }
  714. void AppExit( )
  715. {
  716. }
  717. void AppKey( int key, int down )
  718. {
  719. if( key == 27 )
  720. {
  721. Sys_Quit();
  722. }
  723. else if( toupper(key) == 'U' )
  724. {
  725. CheckForNewFile( true );
  726. AppRender( 0.1f, 0, 0, true );
  727. }
  728. }
  729. void AppChar( int key )
  730. {
  731. }