Counter Strike : Global Offensive Source Code
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.

1263 lines
35 KiB

  1. //========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. /*
  9. glapp.c - Simple OpenGL shell
  10. There are several options allowed on the command line. They are:
  11. -height : what window/screen height do you want to use?
  12. -width : what window/screen width do you want to use?
  13. -bpp : what color depth do you want to use?
  14. -window : create a rendering window rather than full-screen
  15. -fov : use a field of view other than 90 degrees
  16. */
  17. //
  18. // Half-Life Model Viewer (c) 1999 by Mete Ciragan
  19. //
  20. // file: MatSysWindow.cpp
  21. // last modified: May 04 1999, Mete Ciragan
  22. // copyright: The programs and associated files contained in this
  23. // distribution were developed by Mete Ciragan. The programs
  24. // are not in the public domain, but they are freely
  25. // distributable without licensing fees. These programs are
  26. // provided without guarantee or warrantee expressed or
  27. // implied.
  28. //
  29. // version: 1.2
  30. //
  31. // email: [email protected]
  32. // web: http://www.swissquake.ch/chumbalum-soft/
  33. //
  34. #include <mxtk/mx.h>
  35. #include <mxtk/mxMessageBox.h>
  36. #include <mxtk/mxTga.h>
  37. #include <mxtk/mxPcx.h>
  38. #include <mxtk/mxBmp.h>
  39. #include <mxtk/mxMatSysWindow.h>
  40. // #include "gl.h"
  41. // #include <GL/glu.h>
  42. #include <stdio.h>
  43. #include <stdlib.h>
  44. #include <time.h>
  45. #include "tier1/convar.h"
  46. #include <string.h>
  47. #include "MatSysWin.h"
  48. #include "MDLViewer.h"
  49. #include "StudioModel.h"
  50. #include "ControlPanel.h"
  51. #include "ViewerSettings.h"
  52. #include "materialsystem/imaterialsystem.h"
  53. #include "materialsystem/imaterialproxyfactory.h"
  54. #include "FileSystem.h"
  55. #include <KeyValues.h>
  56. #include "materialsystem/IMesh.h"
  57. #include "materialsystem/IMaterialSystemHardwareConfig.h"
  58. #include "materialsystem/ITexture.h"
  59. #include "materialsystem/MaterialSystem_Config.h"
  60. #include "tier0/dbg.h"
  61. #include "istudiorender.h"
  62. #include "tier0/icommandline.h"
  63. #include "mathlib/vmatrix.h"
  64. #include "studio_render.h"
  65. #include "vstdlib/cvar.h"
  66. #include "SoundEmitterSystem/isoundemittersystembase.h"
  67. #include "soundsystem/isoundsystem.h"
  68. #include "soundchars.h"
  69. #include "mathlib/softbodyenvironment.h"
  70. #include "valve_ipc_win32.h"
  71. extern char g_appTitle[];
  72. extern bool g_bInError;
  73. extern int g_dxlevel;
  74. extern ISoundEmitterSystemBase *g_pSoundEmitterBase;
  75. extern CValveIpcClientUtl g_HlmvIpcClient;
  76. extern bool g_bHlmvMaster;
  77. extern CSoftbodyEnvironment g_SoftbodyEnvironment;
  78. void UpdateSounds()
  79. {
  80. static double prev = 0;
  81. double curr = (double) mx::getTickCount () / 1000.0;
  82. if ( prev != 0 )
  83. {
  84. double dt = (curr - prev);
  85. g_pSoundSystem->Update( dt * g_viewerSettings.speedScale );
  86. }
  87. prev = curr;
  88. }
  89. // FIXME: move all this to mxMatSysWin
  90. class DummyMaterialProxyFactory : public IMaterialProxyFactory
  91. {
  92. public:
  93. virtual IMaterialProxy *CreateProxy( const char *proxyName ) {return NULL;}
  94. virtual void DeleteProxy( IMaterialProxy *pProxy ) {}
  95. virtual CreateInterfaceFn GetFactory() {return NULL;}
  96. };
  97. DummyMaterialProxyFactory g_DummyMaterialProxyFactory;
  98. static void ReleaseMaterialSystemObjects( int nChangeFlags )
  99. {
  100. StudioModel::ReleaseStudioModel();
  101. }
  102. static void RestoreMaterialSystemObjects( int nChangeFlags )
  103. {
  104. StudioModel::RestoreStudioModel();
  105. g_ControlPanel->OnLoadModel();
  106. }
  107. static ConVar mat_bumpmap( "mat_bumpmap", "1" );
  108. static ConVar mat_specular( "mat_specular", "1" );
  109. static ConVar mat_parallaxmap( "mat_parallaxmap", "0" );
  110. static ConVar mat_displacementmap( "mat_displacementmap", "1" );
  111. void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig)
  112. {
  113. mat_bumpmap.SetValue( g_viewerSettings.enableNormalMapping );
  114. mat_displacementmap.SetValue( g_viewerSettings.enableDisplacementMapping );
  115. mat_specular.SetValue( g_viewerSettings.enableSpecular );
  116. mat_parallaxmap.SetValue( g_viewerSettings.enableParallaxMapping );
  117. }
  118. MatSysWindow *g_MatSysWindow = 0;
  119. Vector g_vright( 50, 50, 0 ); // needs to be set to viewer's right in order for chrome to work
  120. IMaterial *g_materialBackground = NULL;
  121. IMaterial *g_materialWireframe = NULL;
  122. IMaterial *g_materialWireframeVertexColor = NULL;
  123. IMaterial *g_materialWireframeVertexColorNoCull = NULL;
  124. IMaterial *g_materialDebugCopyBaseTexture = NULL;
  125. IMaterial *g_materialFlatshaded = NULL;
  126. IMaterial *g_materialSmoothshaded = NULL;
  127. IMaterial *g_materialBones = NULL;
  128. IMaterial *g_materialLines = NULL;
  129. IMaterial *g_materialFloor = NULL;
  130. IMaterial *g_materialVertexColor = NULL;
  131. IMaterial *g_materialShadow = NULL;
  132. IMaterial *g_materialArcActive = NULL;
  133. IMaterial *g_materialArcInActive = NULL;
  134. IMaterial *g_materialDebugText = NULL;
  135. MatSysWindow::MatSysWindow (mxWindow *parent, int x, int y, int w, int h, const char *label, int style)
  136. : mxMatSysWindow (parent, x, y, w, h, label, style)
  137. {
  138. g_pMaterialSystem->SetMaterialProxyFactory( &g_DummyMaterialProxyFactory );
  139. m_pCubemapTexture = NULL;
  140. m_hWnd = (HWND)getHandle();
  141. MaterialSystem_Config_t config;
  142. config = g_pMaterialSystem->GetCurrentConfigForVideoCard();
  143. InitMaterialSystemConfig(&config);
  144. if ( g_dxlevel != 0 )
  145. {
  146. config.dxSupportLevel = g_dxlevel;
  147. }
  148. // config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0;
  149. config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
  150. config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
  151. if (!g_pMaterialSystem->SetMode( ( void * )m_hWnd, config ) )
  152. {
  153. return;
  154. }
  155. g_pMaterialSystem->OverrideConfig( config, false );
  156. g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
  157. g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );
  158. m_pCubemapTexture = g_pMaterialSystem->FindTexture( "hlmv/cubemap", NULL, true );
  159. m_pCubemapTexture->IncrementReferenceCount();
  160. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  161. pRenderContext->BindLocalCubemap( m_pCubemapTexture );
  162. g_materialBackground = g_pMaterialSystem->FindMaterial("hlmv/background", TEXTURE_GROUP_OTHER, true);
  163. g_materialWireframe = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true);
  164. g_materialWireframeVertexColor = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
  165. // test: create this from code - you need a vmt to make $nocull 1 happen, can't do it from the render context
  166. {
  167. KeyValues *pVMTKeyValues = new KeyValues( "Wireframe" );
  168. pVMTKeyValues->SetInt("$ignorez", 1);
  169. pVMTKeyValues->SetInt("$nocull", 1);
  170. pVMTKeyValues->SetInt("$vertexcolor", 1);
  171. pVMTKeyValues->SetInt("$decal", 1);
  172. g_materialWireframeVertexColorNoCull = g_pMaterialSystem->CreateMaterial( "debug/wireframenocull", pVMTKeyValues );
  173. }
  174. {
  175. KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
  176. pVMTKeyValues->SetString("$basetexture", "vgui/white" );
  177. g_materialDebugCopyBaseTexture = g_pMaterialSystem->CreateMaterial( "debug/copybasetexture", pVMTKeyValues );
  178. }
  179. g_materialFlatshaded = g_pMaterialSystem->FindMaterial("debug/debugdrawflatpolygons", TEXTURE_GROUP_OTHER, true);
  180. g_materialSmoothshaded = g_pMaterialSystem->FindMaterial("debug/debugmrmfullbright2", TEXTURE_GROUP_OTHER, true);
  181. g_materialBones = g_pMaterialSystem->FindMaterial("debug/debugskeleton", TEXTURE_GROUP_OTHER, true);
  182. g_materialLines = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
  183. g_materialFloor = g_pMaterialSystem->FindMaterial("hlmv/floor", TEXTURE_GROUP_OTHER, true);
  184. g_materialVertexColor = g_pMaterialSystem->FindMaterial("debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true);
  185. g_materialShadow = g_pMaterialSystem->FindMaterial("hlmv/shadow", TEXTURE_GROUP_OTHER, true);
  186. g_materialDebugText = g_pMaterialSystem->FindMaterial("hlmv/debugtext", TEXTURE_GROUP_OTHER, true);
  187. {
  188. KeyValues *pVMTKeyValues = new KeyValues( "UnLitGeneric" );
  189. pVMTKeyValues->SetInt("$nocull", 1);
  190. pVMTKeyValues->SetInt("$vertexcolor", 1);
  191. pVMTKeyValues->SetFloat("$alpha", 0.5f);
  192. g_materialArcActive = g_pMaterialSystem->CreateMaterial( "hlmv/arc_active", pVMTKeyValues );
  193. }
  194. {
  195. KeyValues *pVMTKeyValues = new KeyValues( "UnLitGeneric" );
  196. pVMTKeyValues->SetInt("$nocull", 1);
  197. pVMTKeyValues->SetInt("$vertexcolor", 1);
  198. pVMTKeyValues->SetFloat("$alpha", 0.2f);
  199. g_materialArcInActive = g_pMaterialSystem->CreateMaterial( "hlmv/arc_inactive", pVMTKeyValues );
  200. }
  201. if (!parent)
  202. setVisible (true);
  203. else
  204. mx::setIdleWindow (this);
  205. }
  206. MatSysWindow::~MatSysWindow ()
  207. {
  208. if (m_pCubemapTexture)
  209. {
  210. m_pCubemapTexture->DecrementReferenceCount();
  211. }
  212. mx::setIdleWindow (0);
  213. }
  214. int
  215. MatSysWindow::handleEvent (mxEvent *event)
  216. {
  217. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  218. static float oldrx = 0, oldry = 0, oldtz = 50, oldtx = 0, oldty = 0;
  219. static float oldlrx = 0, oldlry = 0;
  220. static int oldx, oldy;
  221. switch (event->event)
  222. {
  223. case mxEvent::Idle:
  224. {
  225. static double prev;
  226. double curr = (double) mx::getTickCount () / 1000.0;
  227. double dt = (curr - prev);
  228. // clamp to 100fps
  229. if (dt >= 0.0 && dt < 0.01)
  230. {
  231. Sleep( 10 - dt * 1000.0 );
  232. return 1;
  233. }
  234. if ( prev != 0.0 )
  235. {
  236. // dt = 0.001;
  237. g_pStudioModel->AdvanceFrame ( dt * g_viewerSettings.speedScale );
  238. if ( g_viewerSettings.animateWeapons )
  239. {
  240. const char *pszMainSequenceName = g_pStudioModel->GetSequenceName( g_pStudioModel->GetSequence() );
  241. if ( pszMainSequenceName )
  242. {
  243. for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
  244. {
  245. if ( !g_pStudioExtraModel[ i ] )
  246. continue;
  247. // match weapon sequence and frame to marine
  248. int iSequence = g_pStudioExtraModel[ i ]->LookupSequence( pszMainSequenceName );
  249. if ( iSequence == -1 )
  250. {
  251. g_pStudioExtraModel[ i ]->SetFrame( 0 );
  252. }
  253. else
  254. {
  255. g_pStudioExtraModel[ i ]->SetSequence( iSequence );
  256. g_pStudioExtraModel[ i ]->SetFrame( g_pStudioModel->GetCycle() * g_pStudioExtraModel[ i ]->GetMaxFrame() );
  257. }
  258. }
  259. }
  260. }
  261. g_ControlPanel->updateFrameSlider( );
  262. g_ControlPanel->updateGroundSpeed( );
  263. }
  264. prev = curr;
  265. g_pStudioModel->SetSoftbodyOrientation();
  266. for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
  267. {
  268. if ( g_pStudioExtraModel[ i ] != NULL )
  269. {
  270. g_pStudioExtraModel[ i ]->SetSoftbodyOrientation();
  271. }
  272. }
  273. if ( g_viewerSettings.simulateSoftbodies )
  274. {
  275. g_SoftbodyEnvironment.Step( dt * g_viewerSettings.speedScale );
  276. }
  277. if (!g_viewerSettings.pause)
  278. redraw ();
  279. g_ControlPanel->updateTransitionAmount();
  280. UpdateSounds();
  281. return 1;
  282. }
  283. break;
  284. case mxEvent::MouseUp:
  285. {
  286. g_viewerSettings.mousedown = false;
  287. if ( g_pWidgetControl )
  288. g_pWidgetControl->m_WidgetState = WIDGET_STATE_NONE;
  289. }
  290. break;
  291. case mxEvent::MouseDown:
  292. {
  293. g_viewerSettings.mousedown = true;
  294. if ( g_pWidgetControl != NULL && g_viewerSettings.highlightHitbox >= 0 )
  295. {
  296. g_pWidgetControl->WidgetMouseDown( event->x, event->y );
  297. g_pWidgetControl->SetStateUsingInputColor( getViewportPixelColor( event->x, event->y ) );
  298. if ( g_pWidgetControl->m_WidgetState != WIDGET_STATE_NONE )
  299. return 1;
  300. }
  301. oldrx = g_pStudioModel->m_angles[0];
  302. oldry = g_pStudioModel->m_angles[1];
  303. oldtx = g_pStudioModel->m_origin[0];
  304. oldty = g_pStudioModel->m_origin[1];
  305. oldtz = g_pStudioModel->m_origin[2];
  306. oldx = event->x;
  307. oldy = event->y;
  308. oldlrx = g_viewerSettings.lightrot[1];
  309. oldlry = g_viewerSettings.lightrot[0];
  310. g_viewerSettings.pause = false;
  311. float r = 1.0/3.0 * min( w(), h() );
  312. float d = sqrt( ( float )( (event->x - w()/2) * (event->x - w()/2) + (event->y - h()/2) * (event->y - h()/2) ) );
  313. if ( d < r || !g_viewerSettings.allowOrbitYaw )
  314. g_viewerSettings.rotating = false;
  315. else
  316. g_viewerSettings.rotating = true;
  317. return 1;
  318. }
  319. break;
  320. case mxEvent::MouseDrag:
  321. {
  322. if (event->buttons & mxEvent::MouseLeftButton)
  323. {
  324. if ( g_pWidgetControl && g_pWidgetControl->m_WidgetState != WIDGET_STATE_NONE )
  325. {
  326. g_pWidgetControl->WidgetMouseDrag( event->x, event->y );
  327. break;
  328. }
  329. if ( g_viewerSettings.dotaMode )
  330. {
  331. if (event->modifiers & mxEvent::KeyShift)
  332. {
  333. g_pStudioModel->m_origin[1] = oldty - (float) (event->x - oldx);
  334. g_pStudioModel->m_origin[2] = oldtz + (float) (event->y - oldy);
  335. }
  336. else
  337. {
  338. float rx = (float) (event->x - oldx);
  339. oldx = event->x;
  340. QAngle movement;
  341. matrix3x4_t tmp1, tmp2, tmp3;
  342. movement = QAngle( 0, rx, 0 );
  343. AngleMatrix( g_pStudioModel->m_angles, tmp1 );
  344. AngleMatrix( movement, tmp2 );
  345. ConcatTransforms( tmp1, tmp2, tmp3 );
  346. MatrixAngles( tmp3, g_pStudioModel->m_angles );
  347. }
  348. }
  349. else if (event->modifiers & mxEvent::KeyShift)
  350. {
  351. g_pStudioModel->m_origin[1] = oldty - (float) (event->x - oldx) / 8.0;
  352. g_pStudioModel->m_origin[2] = oldtz + (float) (event->y - oldy) / 8.0;
  353. }
  354. else if (event->modifiers & mxEvent::KeyCtrl)
  355. {
  356. float ry = (float) (event->y - oldy);
  357. float rx = (float) (event->x - oldx);
  358. oldx = event->x;
  359. oldy = event->y;
  360. QAngle movement = QAngle( ry, rx, 0 );
  361. matrix3x4_t tmp1, tmp2, tmp3;
  362. AngleMatrix( g_viewerSettings.lightrot, tmp1 );
  363. AngleMatrix( movement, tmp2 );
  364. ConcatTransforms( tmp2, tmp1, tmp3 );
  365. MatrixAngles( tmp3, g_viewerSettings.lightrot );
  366. // g_viewerSettings.lightrot[0] = oldlrx + (float) (event->y - oldy);
  367. // g_viewerSettings.lightrot[1] = oldlry + (float) (event->x - oldx);
  368. }
  369. else
  370. {
  371. if (!g_viewerSettings.rotating)
  372. {
  373. float ry = (float) (event->y - oldy);
  374. float rx = (float) (event->x - oldx);
  375. oldx = event->x;
  376. oldy = event->y;
  377. QAngle movement;
  378. matrix3x4_t tmp1, tmp2, tmp3;
  379. movement = QAngle( 0, rx, 0 );
  380. AngleMatrix( g_pStudioModel->m_angles, tmp1 );
  381. AngleMatrix( movement, tmp2 );
  382. ConcatTransforms( tmp1, tmp2, tmp3 );
  383. MatrixAngles( tmp3, g_pStudioModel->m_angles );
  384. movement = QAngle( ry, 0, 0 );
  385. AngleMatrix( g_pStudioModel->m_angles, tmp1 );
  386. AngleMatrix( movement, tmp2 );
  387. ConcatTransforms( tmp2, tmp1, tmp3 );
  388. MatrixAngles( tmp3, g_pStudioModel->m_angles );
  389. }
  390. else
  391. {
  392. float ang1 = (180 / 3.1415) * atan2( oldx - w()/2.0, oldy - h()/2.0 );
  393. float ang2 = (180 / 3.1415) * atan2( event->x - w()/2.0, event->y - h()/2.0 );
  394. oldx = event->x;
  395. oldy = event->y;
  396. QAngle movement = QAngle( 0, 0, ang2 - ang1 );
  397. matrix3x4_t tmp1, tmp2, tmp3;
  398. AngleMatrix( g_pStudioModel->m_angles, tmp1 );
  399. AngleMatrix( movement, tmp2 );
  400. ConcatTransforms( tmp2, tmp1, tmp3 );
  401. MatrixAngles( tmp3, g_pStudioModel->m_angles );
  402. }
  403. }
  404. }
  405. else if (event->buttons & mxEvent::MouseRightButton)
  406. {
  407. if ( !g_viewerSettings.dotaMode )
  408. {
  409. g_pStudioModel->m_origin[0] = oldtx + (float) (event->y - oldy);
  410. }
  411. }
  412. if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
  413. {
  414. CUtlBuffer cmd;
  415. CUtlBuffer res;
  416. matrix3x4_t m;
  417. g_pStudioModel->GetModelTransform( m );
  418. cmd.Printf( "%s %f %f %f %f %f %f %f %f %f %f %f %f",
  419. "hlmvModelTransform",
  420. m.m_flMatVal[0][0], m.m_flMatVal[0][1], m.m_flMatVal[0][2], m.m_flMatVal[0][3],
  421. m.m_flMatVal[1][0], m.m_flMatVal[1][1], m.m_flMatVal[1][2], m.m_flMatVal[1][3],
  422. m.m_flMatVal[2][0], m.m_flMatVal[2][1], m.m_flMatVal[2][2], m.m_flMatVal[2][3] );
  423. g_HlmvIpcClient.ExecuteCommand( cmd, res );
  424. g_HlmvIpcClient.Disconnect();
  425. }
  426. redraw ();
  427. return 1;
  428. }
  429. break;
  430. case mxEvent::KeyDown:
  431. {
  432. switch (event->key)
  433. {
  434. case VK_SPACE:
  435. {
  436. int iSeq = g_pStudioModel->GetSequence ();
  437. if (iSeq == g_pStudioModel->SetSequence (iSeq + 1))
  438. {
  439. g_pStudioModel->SetSequence (0);
  440. }
  441. }
  442. break;
  443. }
  444. }
  445. break;
  446. case mxEvent::DropFile:
  447. {
  448. V_strlower( event->szChars );
  449. g_ControlPanel->AddQCRecordPath( event->szChars );
  450. break;
  451. }
  452. break;
  453. } // switch (event->event)
  454. return 1;
  455. }
  456. void DrawBackground()
  457. {
  458. if (!g_viewerSettings.showBackground)
  459. return;
  460. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  461. pRenderContext->Bind(g_materialBackground);
  462. pRenderContext->MatrixMode(MATERIAL_MODEL);
  463. pRenderContext->PushMatrix();
  464. pRenderContext->LoadIdentity();
  465. pRenderContext->MatrixMode(MATERIAL_VIEW);
  466. pRenderContext->PushMatrix();
  467. pRenderContext->LoadIdentity();
  468. {
  469. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  470. CMeshBuilder meshBuilder;
  471. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  472. float dist=-15000.0f;
  473. float tMin=0, tMax=1;
  474. meshBuilder.Position3f(-dist, dist, dist);
  475. meshBuilder.TexCoord2f( 0, tMin,tMax );
  476. meshBuilder.Color4ub( 255, 255, 255, 255 );
  477. meshBuilder.AdvanceVertex();
  478. meshBuilder.Position3f( dist, dist, dist);
  479. meshBuilder.TexCoord2f( 0, tMax,tMax );
  480. meshBuilder.Color4ub( 255, 255, 255, 255 );
  481. meshBuilder.AdvanceVertex();
  482. meshBuilder.Position3f( dist,-dist, dist);
  483. meshBuilder.TexCoord2f( 0, tMax,tMin );
  484. meshBuilder.Color4ub( 255, 255, 255, 255 );
  485. meshBuilder.AdvanceVertex();
  486. meshBuilder.Position3f(-dist,-dist, dist);
  487. meshBuilder.TexCoord2f( 0, tMin,tMin );
  488. meshBuilder.Color4ub( 255, 255, 255, 255 );
  489. meshBuilder.AdvanceVertex();
  490. meshBuilder.End();
  491. pMesh->Draw();
  492. }
  493. }
  494. void DrawHelpers()
  495. {
  496. if (g_viewerSettings.mousedown && g_viewerSettings.showOrbitCircle )
  497. {
  498. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  499. pRenderContext->Bind( g_materialBones );
  500. pRenderContext->MatrixMode(MATERIAL_MODEL);
  501. pRenderContext->LoadIdentity();
  502. pRenderContext->MatrixMode(MATERIAL_VIEW);
  503. pRenderContext->LoadIdentity();
  504. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  505. CMeshBuilder meshBuilder;
  506. meshBuilder.Begin( pMesh, MATERIAL_LINES, 360 / 5 );
  507. if (g_viewerSettings.rotating)
  508. meshBuilder.Color3ub( 255, 255, 0 );
  509. else
  510. meshBuilder.Color3ub( 0, 255, 0 );
  511. for (int i = 0; i < 360; i += 5)
  512. {
  513. float a = i * (3.151492653/180.0f);
  514. if (g_viewerSettings.rotating)
  515. meshBuilder.Color3ub( 255, 255, 0 );
  516. else
  517. meshBuilder.Color3ub( 0, 255, 0 );
  518. meshBuilder.Position3f( sin( a ), cos( a ), -3.0f );
  519. meshBuilder.AdvanceVertex();
  520. }
  521. meshBuilder.End();
  522. pMesh->Draw();
  523. }
  524. }
  525. void DrawGroundPlane()
  526. {
  527. if (!g_viewerSettings.showGround)
  528. return;
  529. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  530. pRenderContext->Bind(g_materialFloor);
  531. pRenderContext->MatrixMode(MATERIAL_MODEL);
  532. pRenderContext->PushMatrix();;
  533. pRenderContext->LoadIdentity();
  534. pRenderContext->MatrixMode(MATERIAL_VIEW);
  535. pRenderContext->PushMatrix();;
  536. pRenderContext->LoadIdentity();
  537. pRenderContext->MatrixMode( MATERIAL_VIEW );
  538. pRenderContext->LoadIdentity( );
  539. pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
  540. pRenderContext->Rotate( -90, 0, 0, 1 );
  541. pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
  542. pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
  543. pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
  544. pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
  545. static Vector tMap( 0, 0, 0 );
  546. static Vector dxMap( 1, 0, 0 );
  547. static Vector dyMap( 0, 1, 0 );
  548. Vector deltaPos;
  549. QAngle deltaAngles;
  550. g_pStudioModel->GetMovement( g_pStudioModel->m_prevGroundCycles, deltaPos, deltaAngles );
  551. IMesh* pMesh = pRenderContext->GetDynamicMesh();
  552. CMeshBuilder meshBuilder;
  553. meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
  554. float scale = 10.0;
  555. float dist=-100.0f;
  556. float dpdd = scale / dist;
  557. tMap.x = tMap.x + dxMap.x * deltaPos.x * dpdd + dxMap.y * deltaPos.y * dpdd;
  558. tMap.y = tMap.y + dyMap.x * deltaPos.x * dpdd + dyMap.y * deltaPos.y * dpdd;
  559. while (tMap.x < 0.0) tMap.x += 1.0;
  560. while (tMap.x > 1.0) tMap.x += -1.0;
  561. while (tMap.y < 0.0) tMap.y += 1.0;
  562. while (tMap.y > 1.0) tMap.y += -1.0;
  563. VectorYawRotate( dxMap, -deltaAngles.y, dxMap );
  564. VectorYawRotate( dyMap, -deltaAngles.y, dyMap );
  565. // ARRGHHH, this is right but I don't know what I've done
  566. meshBuilder.Position3f( -dist, dist, 0 );
  567. meshBuilder.TexCoord2f( 0, tMap.x + (-dxMap.x - dyMap.x) * scale, tMap.y + (dxMap.y + dyMap.y) * scale );
  568. meshBuilder.Color4ub( 128, 128, 128, 255 );
  569. meshBuilder.AdvanceVertex();
  570. meshBuilder.Position3f( dist, dist, 0 );
  571. meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x - dyMap.x) * scale, tMap.y + (-dxMap.y + dyMap.y) * scale );
  572. meshBuilder.Color4ub( 128, 128, 128, 255 );
  573. meshBuilder.AdvanceVertex();
  574. meshBuilder.Position3f( dist, -dist, 0 );
  575. meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x + dyMap.x) * scale, tMap.y + (-dxMap.y - dyMap.y) * scale );
  576. meshBuilder.Color4ub( 128, 128, 128, 255 );
  577. meshBuilder.AdvanceVertex();
  578. meshBuilder.Position3f( -dist, -dist, 0 );
  579. meshBuilder.TexCoord2f( 0, tMap.x - (dxMap.x - dyMap.x) * scale, tMap.y - (-dxMap.y + dyMap.y) * scale );
  580. meshBuilder.Color4ub( 128, 128, 128, 255 );
  581. meshBuilder.AdvanceVertex();
  582. // draw underside
  583. meshBuilder.Position3f( -dist, dist, 0 );
  584. meshBuilder.TexCoord2f( 0, tMap.x + (-dxMap.x - dyMap.x) * scale, tMap.y + (dxMap.y + dyMap.y) * scale );
  585. meshBuilder.Color4ub( 128, 128, 128, 128 );
  586. meshBuilder.AdvanceVertex();
  587. meshBuilder.Position3f( -dist, -dist, 0 );
  588. meshBuilder.TexCoord2f( 0, tMap.x - (dxMap.x - dyMap.x) * scale, tMap.y - (-dxMap.y + dyMap.y) * scale );
  589. meshBuilder.Color4ub( 128, 128, 128, 128 );
  590. meshBuilder.AdvanceVertex();
  591. meshBuilder.Position3f( dist, -dist, 0 );
  592. meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x + dyMap.x) * scale, tMap.y + (-dxMap.y - dyMap.y) * scale );
  593. meshBuilder.Color4ub( 128, 128, 128, 128 );
  594. meshBuilder.AdvanceVertex();
  595. meshBuilder.Position3f( dist, dist, 0 );
  596. meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x - dyMap.x) * scale, tMap.y + (-dxMap.y + dyMap.y) * scale );
  597. meshBuilder.Color4ub( 128, 128, 128, 128 );
  598. meshBuilder.AdvanceVertex();
  599. meshBuilder.End();
  600. pMesh->Draw();
  601. pRenderContext->MatrixMode(MATERIAL_MODEL);
  602. pRenderContext->PopMatrix();
  603. pRenderContext->MatrixMode(MATERIAL_VIEW);
  604. pRenderContext->PopMatrix();
  605. }
  606. void DrawMovementBoxes()
  607. {
  608. if (!g_viewerSettings.showMovement)
  609. return;
  610. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  611. pRenderContext->Bind(g_materialFloor);
  612. pRenderContext->MatrixMode(MATERIAL_MODEL);
  613. pRenderContext->PushMatrix();
  614. pRenderContext->LoadIdentity();
  615. pRenderContext->MatrixMode(MATERIAL_VIEW);
  616. pRenderContext->PushMatrix();
  617. pRenderContext->LoadIdentity();
  618. pRenderContext->MatrixMode( MATERIAL_VIEW );
  619. pRenderContext->LoadIdentity( );
  620. pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
  621. pRenderContext->Rotate( -90, 0, 0, 1 );
  622. pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
  623. pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
  624. pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
  625. pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
  626. static matrix3x4_t mStart( 1, 0, 0, 0 , 0, 1, 0, 0 , 0, 0, 1, 0 );
  627. matrix3x4_t mTemp;
  628. static float prevframes[5];
  629. Vector deltaPos;
  630. QAngle deltaAngles;
  631. g_pStudioModel->GetMovement( prevframes, deltaPos, deltaAngles );
  632. AngleMatrix( deltaAngles, deltaPos, mTemp );
  633. MatrixInvert( mTemp, mTemp );
  634. ConcatTransforms( mTemp, mStart, mStart );
  635. Vector bboxMin, bboxMax;
  636. g_pStudioModel->ExtractBbox( bboxMin, bboxMax );
  637. static float prevCycle = 0.0;
  638. if (fabs( g_pStudioModel->GetFrame( 0 ) - prevCycle) > 0.5)
  639. {
  640. SetIdentityMatrix( mStart );
  641. }
  642. prevCycle = g_pStudioModel->GetFrame( 0 );
  643. // starting position
  644. {
  645. float color[] = { 0.7, 1, 0, 0.5 };
  646. float wirecolor[] = { 1, 1, 0, 1.0 };
  647. g_pStudioModel->drawTransparentBox( bboxMin, bboxMax, mStart, color, wirecolor );
  648. }
  649. // current position
  650. {
  651. float color[] = { 1, 0.7, 0, 0.5 };
  652. float wirecolor[] = { 1, 0, 0, 1.0 };
  653. SetIdentityMatrix( mTemp );
  654. g_pStudioModel->drawTransparentBox( bboxMin, bboxMax, mTemp, color, wirecolor );
  655. }
  656. pRenderContext->MatrixMode(MATERIAL_MODEL);
  657. pRenderContext->PopMatrix();
  658. pRenderContext->MatrixMode(MATERIAL_VIEW);
  659. pRenderContext->PopMatrix();
  660. }
  661. char const *HLMV_TranslateSoundName( char const *soundname, StudioModel *model )
  662. {
  663. if ( Q_stristr( soundname, ".wav" ) )
  664. return PSkipSoundChars( soundname );
  665. if ( model )
  666. {
  667. return PSkipSoundChars( g_pSoundEmitterBase->GetWavFileForSound( soundname, model->GetFileName() ) );
  668. }
  669. return PSkipSoundChars( g_pSoundEmitterBase->GetWavFileForSound( soundname, NULL ) );
  670. }
  671. void PlaySound( const char *pSoundName, StudioModel *pStudioModel )
  672. {
  673. // Play Sound
  674. if (!g_viewerSettings.playSounds)
  675. return;
  676. if ( pSoundName == NULL || pSoundName[ 0 ] == '\0' )
  677. return;
  678. const char *pSoundFileName = HLMV_TranslateSoundName( pSoundName, pStudioModel );
  679. char filename[ 256 ];
  680. sprintf( filename, "sound/%s", pSoundFileName );
  681. CAudioSource *pAudioSource = g_pSoundSystem->FindOrAddSound( filename );
  682. if ( pAudioSource == NULL )
  683. return;
  684. float volume = VOL_NORM;
  685. gender_t gender = GENDER_NONE;
  686. if ( pStudioModel )
  687. {
  688. gender = g_pSoundEmitterBase->GetActorGender( pStudioModel->GetFileName() );
  689. }
  690. CSoundParameters params;
  691. if ( !Q_stristr( pSoundName, ".wav" ) &&
  692. g_pSoundEmitterBase->GetParametersForSound( pSoundName, params, gender ) )
  693. {
  694. volume = params.volume;
  695. }
  696. g_pSoundSystem->PlaySound( pAudioSource, volume, NULL );
  697. }
  698. // copied from baseentity.cpp
  699. // HACK: This must match the #define in cl_animevent.h in the client .dll code!!!
  700. #define CL_EVENT_SOUND 5004
  701. #define CL_EVENT_FOOTSTEP_LEFT 6004
  702. #define CL_EVENT_FOOTSTEP_RIGHT 6005
  703. #define CL_EVENT_MFOOTSTEP_LEFT 6006
  704. #define CL_EVENT_MFOOTSTEP_RIGHT 6007
  705. // copied from scriptevent.h
  706. #define SCRIPT_EVENT_SOUND 1004 // Play named wave file (on CHAN_BODY)
  707. #define SCRIPT_EVENT_SOUND_VOICE 1008 // Play named wave file (on CHAN_VOICE)
  708. void PlaySounds( StudioModel *pStudioModel )
  709. {
  710. if ( pStudioModel == NULL )
  711. return;
  712. int iLayer = g_ControlPanel->getFrameSelection();
  713. float flFrame = pStudioModel->GetFrame( iLayer );
  714. float flTime = flFrame / 30.0f; // pStudioModel->GetSequenceTime()
  715. float prevtime = flTime - pStudioModel->GetTimeDelta();
  716. float currtime = flTime;
  717. float duration = pStudioModel->GetDuration();
  718. prevtime = fmod( prevtime, duration );
  719. currtime = fmod( currtime, duration );
  720. float prevcycle = prevtime / duration;
  721. float currcycle = currtime / duration;
  722. CStudioHdr *pStudioHdr = pStudioModel->GetStudioHdr();
  723. if ( pStudioHdr == NULL )
  724. return;
  725. int seq = pStudioModel->GetSequence();
  726. mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( seq );
  727. for ( int i = 0; i < (int)seqdesc.numevents; ++i )
  728. {
  729. mstudioevent_t *pEvent = seqdesc.pEvent( i );
  730. #if defined( _DEBUG )
  731. const char *pEventName = pEvent->pszEventName();
  732. NOTE_UNUSED( pEventName );
  733. #endif
  734. if ( pEvent->cycle <= prevcycle || pEvent->cycle > currcycle )
  735. continue;
  736. // largely copied from BuildAnimationEventSoundList in baseentity.cpp
  737. switch ( pEvent->event )
  738. {
  739. case 0:
  740. if ( Q_strcmp( pEvent->pszEventName(), "AE_CL_PLAYSOUND" ) == 0 )
  741. {
  742. PlaySound( pEvent->pszOptions(), pStudioModel );
  743. continue;
  744. }
  745. break;
  746. case CL_EVENT_SOUND: // Old-style client .dll animation event
  747. // fall-through intentional
  748. case SCRIPT_EVENT_SOUND:
  749. // fall-through intentional
  750. case SCRIPT_EVENT_SOUND_VOICE:
  751. PlaySound( pEvent->pszOptions(), pStudioModel );
  752. break;
  753. case CL_EVENT_FOOTSTEP_LEFT:
  754. case CL_EVENT_FOOTSTEP_RIGHT:
  755. {
  756. char soundname[256];
  757. char const *options = pEvent->pszOptions();
  758. if ( !options || !options[0] )
  759. {
  760. options = "NPC_CombineS";
  761. }
  762. Q_snprintf( soundname, 256, "%s.RunFootstepLeft", options );
  763. PlaySound( soundname, pStudioModel );
  764. Q_snprintf( soundname, 256, "%s.RunFootstepRight", options );
  765. PlaySound( soundname, pStudioModel );
  766. Q_snprintf( soundname, 256, "%s.FootstepLeft", options );
  767. PlaySound( soundname, pStudioModel );
  768. Q_snprintf( soundname, 256, "%s.FootstepRight", options );
  769. PlaySound( soundname, pStudioModel );
  770. }
  771. break;
  772. /*
  773. case AE_CL_PLAYSOUND:
  774. if ( !( pEvent->type & AE_TYPE_CLIENT ) )
  775. break;
  776. if ( pEvent->options[0] )
  777. {
  778. PlaySound( pEvent->options, pStudioModel );
  779. }
  780. else
  781. {
  782. Warning( "-- Error --: empty soundname, .qc error on AE_CL_PLAYSOUND in model %s, sequence %s, animevent # %i\n",
  783. pStudioHdr->name(), seqdesc.pszLabel(), i + 1 );
  784. }
  785. break;
  786. */
  787. default:
  788. break;
  789. }
  790. }
  791. }
  792. void
  793. MatSysWindow::draw ()
  794. {
  795. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  796. if ( g_bInError || !g_pStudioModel->GetStudioRender() )
  797. return;
  798. static bool bInDraw = false;
  799. if (bInDraw)
  800. return;
  801. bInDraw = true;
  802. UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
  803. CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
  804. g_pMaterialSystem->BeginFrame( 0 );
  805. g_pStudioModel->GetStudioRender()->BeginFrame();
  806. pRenderContext->ClearColor3ub(g_viewerSettings.bgColor[0] * 255, g_viewerSettings.bgColor[1] * 255, g_viewerSettings.bgColor[2] * 255);
  807. // pRenderContext->ClearColor3ub(0, 0, 0 );
  808. pRenderContext->ClearBuffers(true, true);
  809. pRenderContext->Viewport( 0, 0, w(), h() );
  810. pRenderContext->MatrixMode( MATERIAL_PROJECTION );
  811. pRenderContext->LoadIdentity( );
  812. pRenderContext->PerspectiveX(g_viewerSettings.fov, (float)w() / (float)h(), 1.0f, 20000.0f);
  813. DrawBackground();
  814. DrawGroundPlane();
  815. DrawMovementBoxes();
  816. DrawHelpers();
  817. pRenderContext->MatrixMode( MATERIAL_VIEW );
  818. pRenderContext->LoadIdentity( );
  819. // FIXME: why is this needed? Doesn't SetView() override this?
  820. pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
  821. pRenderContext->Rotate( -90, 0, 0, 1 );
  822. g_pStudioModel->ClearLookTargets();
  823. g_pStudioModel->AddLookTarget( Vector( 0, 0, 0 ), g_pStudioModel->GetSolveHeadTurn() ? 1.0f : 0.0f );
  824. int polycount = g_pStudioModel->DrawModel( false, PASS_MODELONLY );
  825. g_pStudioModel->GetStudioRender()->EndFrame();
  826. UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
  827. g_ControlPanel->setModelInfo();
  828. int lod;
  829. float metric;
  830. metric = g_pStudioModel->GetLodMetric();
  831. lod = g_pStudioModel->GetLodUsed();
  832. g_ControlPanel->setLOD( lod, true, false );
  833. g_ControlPanel->setLODMetric( metric );
  834. g_ControlPanel->setPolycount( polycount );
  835. g_ControlPanel->setTransparent( g_pStudioModel->m_bIsTransparent );
  836. g_ControlPanel->updatePoseParameters( );
  837. // draw what ever else is loaded
  838. int i;
  839. for (i = 0; i < HLMV_MAX_MERGED_MODELS; i++)
  840. {
  841. if (g_pStudioExtraModel[i] != NULL)
  842. {
  843. g_pStudioModel->GetStudioRender()->BeginFrame();
  844. g_pStudioExtraModel[i]->DrawModel( true );
  845. g_pStudioModel->GetStudioRender()->EndFrame();
  846. }
  847. }
  848. g_pStudioModel->GetStudioRender()->BeginFrame();
  849. g_pStudioModel->DrawModel( false, PASS_EXTRASONLY );
  850. g_pStudioModel->GetStudioRender()->EndFrame();
  851. g_pStudioModel->IncrementFramecounter();
  852. PlaySounds( g_pStudioModel );
  853. UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
  854. g_pMaterialSystem->SwapBuffers();
  855. g_pMaterialSystem->EndFrame();
  856. bInDraw = false;
  857. }
  858. /*
  859. int
  860. MatSysWindow::loadTexture (const char *filename, int name)
  861. {
  862. if (!filename || !strlen (filename))
  863. {
  864. if (d_textureNames[name])
  865. {
  866. glDeleteTextures (1, (const GLuint *) &d_textureNames[name]);
  867. d_textureNames[name] = 0;
  868. if (name == 0)
  869. strcpy (g_viewerSettings.backgroundTexFile, "");
  870. else
  871. strcpy (g_viewerSettings.groundTexFile, "");
  872. }
  873. return 0;
  874. }
  875. mxImage *image = 0;
  876. char ext[16];
  877. strcpy (ext, mx_getextension (filename));
  878. if (!mx_strcasecmp (ext, ".tga"))
  879. image = mxTgaRead (filename);
  880. else if (!mx_strcasecmp (ext, ".pcx"))
  881. image = mxPcxRead (filename);
  882. else if (!mx_strcasecmp (ext, ".bmp"))
  883. image = mxBmpRead (filename);
  884. if (image)
  885. {
  886. if (name == 0)
  887. strcpy (g_viewerSettings.backgroundTexFile, filename);
  888. else
  889. strcpy (g_viewerSettings.groundTexFile, filename);
  890. d_textureNames[name] = name + 1;
  891. if (image->bpp == 8)
  892. {
  893. mstudiotexture_t texture;
  894. texture.width = image->width;
  895. texture.height = image->height;
  896. g_pStudioModel->UploadTexture (&texture, (byte *) image->data, (byte *) image->palette, name + 1);
  897. }
  898. else
  899. {
  900. glBindTexture (GL_TEXTURE_2D, d_textureNames[name]);
  901. glTexImage2D (GL_TEXTURE_2D, 0, 3, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data);
  902. glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
  903. glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  904. glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  905. }
  906. delete image;
  907. return name + 1;
  908. }
  909. return 0;
  910. }
  911. */
  912. void MatSysWindow::dumpViewport (const char *filename)
  913. {
  914. dumpViewportWithLabel( filename, "" );
  915. }
  916. Color MatSysWindow::getViewportPixelColor( int x, int y )
  917. {
  918. redraw ();
  919. HDC hDC = GetDC ((HWND) getHandle ());
  920. COLORREF color = GetPixel(hDC, x, y);
  921. ReleaseDC ((HWND) getHandle (), hDC);
  922. Color temp;
  923. temp.SetColor( GetRValue( color ), GetGValue( color ), GetBValue( color ), 255 );
  924. return temp;
  925. }
  926. // Save HDC as a bitmap:
  927. void MatSysWindow::dumpViewportWithLabel(const char *filename, const char *label)
  928. {
  929. redraw ();
  930. //int w = w2 ();
  931. //int h = h2 ();
  932. RECT client;
  933. HDC hDC = GetDC ((HWND) getHandle ());
  934. RECT rcClient;
  935. bool bOk = false;
  936. HBITMAP hImage = NULL;
  937. GetClientRect ((HWND) getHandle (), &rcClient);
  938. client = rcClient;
  939. if ((hImage = CreateCompatibleBitmap (hDC, rcClient.right, rcClient.bottom)) != NULL)
  940. {
  941. HDC hMemDC;
  942. HBITMAP hDCBmp;
  943. if ((hMemDC = CreateCompatibleDC (hDC)) != NULL)
  944. {
  945. hDCBmp = (HBITMAP) SelectObject (hMemDC, hImage);
  946. BitBlt (hMemDC, 0, 0, rcClient.right, rcClient.bottom, hDC, 0, 0, SRCCOPY);
  947. if ( strlen(label) > 0 )
  948. {
  949. HFONT font = CreateFont( 24, 0, 0, 0, 700, false, false, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial" );
  950. SetBkColor(hMemDC, RGB(60,60,60) );
  951. SetTextColor(hMemDC, RGB(255,255,255) );
  952. HFONT hFontOld = (HFONT)SelectObject( hMemDC, font );
  953. DrawText( hMemDC, label, strlen(label), &rcClient, DT_TOP|DT_CENTER);
  954. SelectObject( hMemDC, hFontOld );
  955. }
  956. SelectObject (hMemDC, hDCBmp);
  957. DeleteDC (hMemDC);
  958. bOk = true;
  959. }
  960. }
  961. ReleaseDC ((HWND) getHandle (), hDC);
  962. if (! bOk)
  963. {
  964. if (hImage)
  965. {
  966. DeleteObject (hImage);
  967. hImage = NULL;
  968. }
  969. }
  970. if ( !hImage )
  971. {
  972. mxMessageBox (this, "Screenshot failure: Couldn't capture the model window.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  973. return;
  974. }
  975. if ( hImage != NULL) {
  976. UINT uiBytesPerRow = 3 * client.right; // RGB takes 24 bits
  977. UINT uiRemainderForPadding;
  978. if ((uiRemainderForPadding = uiBytesPerRow % sizeof (DWORD)) > 0)
  979. {
  980. uiBytesPerRow += (sizeof (DWORD) - uiRemainderForPadding);
  981. }
  982. UINT uiBytesPerAllRows = uiBytesPerRow * client.bottom;
  983. PBYTE pDataBits;
  984. if ((pDataBits = new BYTE [uiBytesPerAllRows]) != NULL)
  985. {
  986. BITMAPINFOHEADER bmi = {0};
  987. BITMAPFILEHEADER bmf = {0};
  988. HDC hDC = GetDC ((HWND) getHandle ());
  989. // Prepare to get the data out of HBITMAP:
  990. bmi.biSize = sizeof (bmi);
  991. bmi.biPlanes = 1;
  992. bmi.biBitCount = 24;
  993. bmi.biHeight = client.bottom;
  994. bmi.biWidth = client.right;
  995. GetDIBits (hDC, hImage, 0, client.bottom, pDataBits, (BITMAPINFO*) &bmi, DIB_RGB_COLORS);
  996. ReleaseDC ((HWND) getHandle (), hDC);
  997. // Fill the file header:
  998. bmf.bfOffBits = sizeof (bmf) + sizeof (bmi);
  999. bmf.bfSize = bmf.bfOffBits + uiBytesPerAllRows;
  1000. bmf.bfType = 0x4D42;
  1001. FILE* pFile;
  1002. if ((pFile = fopen (filename, "wb")) != NULL)
  1003. {
  1004. fwrite (&bmf, sizeof (bmf), 1, pFile);
  1005. fwrite (&bmi, sizeof (bmi), 1, pFile);
  1006. fwrite (pDataBits, sizeof (BYTE), uiBytesPerAllRows, pFile);
  1007. fclose (pFile);
  1008. }
  1009. delete [] pDataBits;
  1010. }
  1011. DeleteObject (hImage);
  1012. }
  1013. }