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.

1631 lines
42 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //===========================================================================//
  8. //
  9. // Half-Life Model Viewer (c) 1999 by Mete Ciragan
  10. //
  11. // file: mdlviewer.cpp
  12. // last modified: Jun 03 1999, Mete Ciragan
  13. // copyright: The programs and associated files contained in this
  14. // distribution were developed by Mete Ciragan. The programs
  15. // are not in the public domain, but they are freely
  16. // distributable without licensing fees. These programs are
  17. // provided without guarantee or warrantee expressed or
  18. // implied.
  19. //
  20. // version: 1.2
  21. //
  22. // email: [email protected]
  23. // web: http://www.swissquake.ch/chumbalum-soft/
  24. //
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <mxtk/mx.h>
  29. #include <mxtk/mxTga.h>
  30. #include <mxtk/mxEvent.h>
  31. #include "mdlviewer.h"
  32. #include "ViewerSettings.h"
  33. #include "MatSysWin.h"
  34. #include "ControlPanel.h"
  35. #include "StudioModel.h"
  36. #include "FileAssociation.h"
  37. #include "tier1/strtools.h"
  38. #include "tier0/icommandline.h"
  39. #include "filesystem.h"
  40. #include "ifilesystemopendialog.h"
  41. #include "appframework/appframework.h"
  42. #include "istudiorender.h"
  43. #include "materialsystem/imaterialsystem.h"
  44. #include "vphysics_interface.h"
  45. #include "Datacache/imdlcache.h"
  46. #include "datacache/idatacache.h"
  47. #include "filesystem_init.h"
  48. #include "materialsystem/imaterialsystemhardwareconfig.h"
  49. #include "SoundEmitterSystem/isoundemittersystembase.h"
  50. #include "soundsystem/isoundsystem.h"
  51. #include "tier1/tier1.h"
  52. #include "valve_ipc_win32.h"
  53. #include "threadtools.h"
  54. #include "vstdlib/IKeyValuesSystem.h"
  55. bool g_bOldFileDialogs = false;
  56. MDLViewer *g_MDLViewer = 0;
  57. char g_appTitle[] = "Half-Life Model Viewer v1.22";
  58. static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" };
  59. extern int g_dxlevel;
  60. bool g_bInError = false;
  61. //-----------------------------------------------------------------------------
  62. // Singleton interfaces
  63. //-----------------------------------------------------------------------------
  64. IStudioRender *g_pStudioRender;
  65. IMDLCache *g_pMDLCache;
  66. IPhysicsSurfaceProps *physprop;
  67. IPhysicsCollision *physcollision;
  68. IFileSystem *g_pFileSystem;
  69. IMaterialSystem *g_pMaterialSystem;
  70. IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig;
  71. IStudioDataCache *g_pStudioDataCache;
  72. IDataCache *g_pDataCache;
  73. ISoundEmitterSystemBase *g_pSoundEmitterBase;
  74. ISoundSystem *g_pSoundSystem;
  75. CreateInterfaceFn g_Factory;
  76. // Filesystem dialog module wrappers.
  77. CSysModule *g_pFSDialogModule = 0;
  78. CreateInterfaceFn g_FSDialogFactory = 0;
  79. class CHlmvIpcServer : public CValveIpcServerUtl
  80. {
  81. public:
  82. CHlmvIpcServer() : CValveIpcServerUtl( "HLMV_IPC_SERVER" ) {}
  83. ~CHlmvIpcServer();
  84. public:
  85. bool HasCommands();
  86. void AppendCommand( char *pszCommand );
  87. char *GetCommand();
  88. void PopCommand();
  89. protected:
  90. virtual BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res );
  91. protected:
  92. CThreadFastMutex m_mtx;
  93. CUtlVector< char * > m_lstCommands;
  94. }
  95. g_HlmvIpcServer;
  96. CValveIpcClientUtl g_HlmvIpcClient( "HLMV_IPC_SERVER" );
  97. bool g_bHlmvMaster = false; // This hlmv is controlling a controlled hlmv instance
  98. bool g_bHlmvControlled = false; // This hlmv is being controlled by a master hlmv instance
  99. void LoadFileSystemDialogModule()
  100. {
  101. Assert( !g_pFSDialogModule );
  102. // Load the module with the file system open dialog.
  103. const char *pDLLName = "FileSystemOpenDialog.dll";
  104. g_pFSDialogModule = Sys_LoadModule( pDLLName );
  105. if ( g_pFSDialogModule )
  106. {
  107. g_FSDialogFactory = Sys_GetFactory( g_pFSDialogModule );
  108. }
  109. if ( !g_pFSDialogModule || !g_FSDialogFactory )
  110. {
  111. if ( g_pFSDialogModule )
  112. {
  113. Sys_UnloadModule( g_pFSDialogModule );
  114. g_pFSDialogModule = NULL;
  115. }
  116. }
  117. }
  118. void UnloadFileSystemDialogModule()
  119. {
  120. if ( g_pFSDialogModule )
  121. {
  122. Sys_UnloadModule( g_pFSDialogModule );
  123. g_pFSDialogModule = 0;
  124. }
  125. }
  126. void
  127. MDLViewer::initRecentFiles ()
  128. {
  129. for (int i = 0; i < 8; i++)
  130. {
  131. if (strlen (recentFiles[i]))
  132. {
  133. mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, recentFiles[i]);
  134. }
  135. else
  136. {
  137. mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, "(empty)");
  138. mb->setEnabled (IDC_FILE_RECENTMODELS1 + i, false);
  139. }
  140. }
  141. }
  142. void
  143. MDLViewer::loadRecentFiles ()
  144. {
  145. char path[256];
  146. strcpy (path, mx::getApplicationPath ());
  147. strcat (path, "/hlmv.rf");
  148. FILE *file = fopen (path, "rb");
  149. if (file)
  150. {
  151. fread (recentFiles, sizeof recentFiles, 1, file);
  152. fclose (file);
  153. }
  154. }
  155. void
  156. MDLViewer::saveRecentFiles ()
  157. {
  158. char path[256];
  159. strcpy (path, mx::getApplicationPath ());
  160. strcat (path, "/hlmv.rf");
  161. FILE *file = fopen (path, "wb");
  162. if (file)
  163. {
  164. fwrite (recentFiles, sizeof recentFiles, 1, file);
  165. fclose (file);
  166. }
  167. }
  168. struct AccelTableEntry_t
  169. {
  170. unsigned short key;
  171. unsigned short command;
  172. unsigned char flags;
  173. };
  174. AccelTableEntry_t accelTable[] = {{VK_F1, IDC_FLUSH_SHADERS, mx::ACCEL_VIRTKEY},
  175. {VK_F5, IDC_FILE_REFRESH, mx::ACCEL_VIRTKEY},
  176. {'u', IDC_FILE_UNLOADALLMERGEDMODELS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  177. {'U', IDC_FILE_UNLOADALLMERGEDMODELS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  178. {'w', IDC_ACCEL_WIREFRAME, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  179. {'W', IDC_ACCEL_WIREFRAME, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  180. {'a', IDC_ACCEL_ATTACHMENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  181. {'A', IDC_ACCEL_ATTACHMENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  182. {'g', IDC_ACCEL_GROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  183. {'G', IDC_ACCEL_GROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  184. {'h', IDC_ACCEL_HITBOXES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  185. {'H', IDC_ACCEL_HITBOXES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  186. {'o', IDC_ACCEL_BONES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  187. {'O', IDC_ACCEL_BONES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  188. {'b', IDC_ACCEL_BACKGROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  189. {'B', IDC_ACCEL_BACKGROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  190. {'m', IDC_ACCEL_MOVEMENT, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  191. {'M', IDC_ACCEL_MOVEMENT, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  192. {'n', IDC_ACCEL_NORMALS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  193. {'N', IDC_ACCEL_NORMALS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  194. {'t', IDC_ACCEL_TANGENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  195. {'T', IDC_ACCEL_TANGENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  196. {'s', IDC_ACCEL_SHADOW, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
  197. {'S', IDC_ACCEL_SHADOW, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY}};
  198. #define NUM_ACCELERATORS ARRAYSIZE( accelTable )
  199. MDLViewer::MDLViewer ()
  200. : mxWindow (0, 0, 0, 0, 0, g_appTitle, mxWindow::Normal)
  201. {
  202. d_MatSysWindow = 0;
  203. d_cpl = 0;
  204. // create menu stuff
  205. mb = new mxMenuBar (this);
  206. mxMenu *menuFile = new mxMenu ();
  207. menuOptions = new mxMenu ();
  208. menuView = new mxMenu ();
  209. mxMenu *menuHelp = new mxMenu ();
  210. mb->addMenu ("File", menuFile);
  211. mb->addMenu ("Options", menuOptions);
  212. mb->addMenu ("View", menuView);
  213. mb->addMenu ("Help", menuHelp);
  214. mxMenu *menuRecentModels = new mxMenu ();
  215. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS1);
  216. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS2);
  217. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS3);
  218. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS4);
  219. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS5);
  220. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS6);
  221. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS7);
  222. menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS8);
  223. if ( g_bOldFileDialogs )
  224. {
  225. menuFile->add ("Load Model...", IDC_FILE_LOADMODEL);
  226. menuFile->add ("(Steam) Load Model...", IDC_FILE_LOADMODEL_STEAM);
  227. }
  228. else
  229. {
  230. menuFile->add ("Load Model...", IDC_FILE_LOADMODEL_STEAM);
  231. }
  232. menuFile->add( "Refresh (F5)", IDC_FILE_REFRESH );
  233. menuFile->addSeparator ();
  234. if ( g_bOldFileDialogs )
  235. {
  236. menuFile->add ("Load Weapon...", IDC_FILE_LOADMERGEDMODEL);
  237. menuFile->add ("(Steam) Load Weapon...", IDC_FILE_LOADMERGEDMODEL_STEAM);
  238. }
  239. else
  240. {
  241. menuFile->add ("Load Weapon...", IDC_FILE_LOADMERGEDMODEL_STEAM);
  242. }
  243. mxMenu *menuUnloadWeapon = new mxMenu ();
  244. menuUnloadWeapon->add ("Unload All Merged Models (Ctrl-U)", IDC_FILE_UNLOADALLMERGEDMODELS);
  245. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL1);
  246. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL2);
  247. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL3);
  248. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL4);
  249. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL5);
  250. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL6);
  251. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL7);
  252. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL8);
  253. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL9);
  254. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL10);
  255. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL11);
  256. menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL12);
  257. for ( int i = IDC_FILE_UNLOADMERGEDMODEL1; i <= IDC_FILE_UNLOADMERGEDMODEL12; i++ )
  258. {
  259. menuUnloadWeapon->setEnabled( i, false );
  260. }
  261. menuFile->addMenu ("Unload Weapon", menuUnloadWeapon);
  262. menuFile->addSeparator ();
  263. menuFile->add ("Load Background Texture...", IDC_FILE_LOADBACKGROUNDTEX);
  264. menuFile->add ("Load Ground Texture...", IDC_FILE_LOADGROUNDTEX);
  265. menuFile->addSeparator ();
  266. menuFile->add ("Unload Ground Texture", IDC_FILE_UNLOADGROUNDTEX);
  267. menuFile->addSeparator ();
  268. menuFile->addMenu ("Recent Models", menuRecentModels);
  269. menuFile->addSeparator ();
  270. menuFile->add ("Exit", IDC_FILE_EXIT);
  271. menuFile->setEnabled(IDC_FILE_LOADBACKGROUNDTEX, false);
  272. menuFile->setEnabled(IDC_FILE_LOADGROUNDTEX, false);
  273. menuFile->setEnabled(IDC_FILE_UNLOADGROUNDTEX, false);
  274. menuOptions->add ("Background Color...", IDC_OPTIONS_COLORBACKGROUND);
  275. menuOptions->add ("Ground Color...", IDC_OPTIONS_COLORGROUND);
  276. menuOptions->add ("Light Color...", IDC_OPTIONS_COLORLIGHT);
  277. menuOptions->add ("Ambient Color...", IDC_OPTIONS_COLORAMBIENT);
  278. menuOptions->addSeparator ();
  279. menuOptions->add ("Center View", IDC_OPTIONS_CENTERVIEW);
  280. menuOptions->add ("Viewmodel Mode", IDC_OPTIONS_VIEWMODEL);
  281. #ifdef WIN32
  282. menuOptions->addSeparator ();
  283. menuOptions->add ("Make Screenshot...", IDC_OPTIONS_MAKESCREENSHOT);
  284. //menuOptions->add ("Dump Model Info", IDC_OPTIONS_DUMP);
  285. #endif
  286. menuView->add ("File Associations...", IDC_VIEW_FILEASSOCIATIONS);
  287. menuView->setEnabled( IDC_VIEW_FILEASSOCIATIONS, false );
  288. menuView->addSeparator ();
  289. menuView->add ("Show Activities", IDC_VIEW_ACTIVITIES);
  290. menuView->add ("Show hidden", IDC_VIEW_HIDDEN );
  291. #ifdef WIN32
  292. menuHelp->add ("Goto Homepage...", IDC_HELP_GOTOHOMEPAGE);
  293. menuHelp->addSeparator ();
  294. #endif
  295. menuHelp->add ("About...", IDC_HELP_ABOUT);
  296. d_MatSysWindow = new MatSysWindow (this, 0, 0, 100, 100, "", mxWindow::Normal);
  297. #ifdef WIN32
  298. // SetWindowLong ((HWND) d_MatSysWindow->getHandle (), GWL_EXSTYLE, WS_EX_CLIENTEDGE);
  299. #endif
  300. d_cpl = new ControlPanel (this);
  301. d_cpl->setMatSysWindow (d_MatSysWindow);
  302. g_MatSysWindow = d_MatSysWindow;
  303. g_FileAssociation = new FileAssociation ();
  304. loadRecentFiles ();
  305. initRecentFiles ();
  306. LoadViewerRootSettings( );
  307. // FIXME: where do I actually find the domain size of the viewport, especially for multi-monitor
  308. // try to catch weird initialization error
  309. if (g_viewerSettings.xpos < -16384)
  310. g_viewerSettings.xpos = 20;
  311. if (g_viewerSettings.ypos < -16384)
  312. g_viewerSettings.ypos = 20;
  313. g_viewerSettings.ypos = max( 0, g_viewerSettings.ypos );
  314. g_viewerSettings.width = max( 640, g_viewerSettings.width );
  315. g_viewerSettings.height = max( 700, g_viewerSettings.height );
  316. setBounds( g_viewerSettings.xpos, g_viewerSettings.ypos, g_viewerSettings.width, g_viewerSettings.height );
  317. setVisible (true);
  318. setTimer( 200 );
  319. CUtlVector< mx::Accel_t > accelerators;
  320. mx::Accel_t accel;
  321. for (int i=0; i < NUM_ACCELERATORS; i++)
  322. {
  323. accel.flags = accelTable[i].flags ;
  324. accel.key = accelTable[i].key;
  325. accel.command = accelTable[i].command;
  326. accelerators.AddToTail( accel );
  327. }
  328. mx::createAccleratorTable( accelerators.Count(), accelerators.Base() );
  329. g_HlmvIpcServer.EnsureRegisteredAndRunning();
  330. if ( !g_HlmvIpcServer.IsRunning() )
  331. {
  332. menuOptions->addSeparator();
  333. menuOptions->add ( "Link HLMV", IDC_OPTIONS_LINKHLMV );
  334. menuOptions->add ( "Unlink HLMV", IDC_OPTIONS_UNLINKHLMV );
  335. menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, true );
  336. menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
  337. }
  338. }
  339. MDLViewer::~MDLViewer ()
  340. {
  341. g_HlmvIpcServer.EnsureStoppedAndUnregistered();
  342. saveRecentFiles ();
  343. SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel );
  344. SaveViewerRootSettings( );
  345. #ifdef WIN32
  346. DeleteFile ("hlmv.cfg");
  347. DeleteFile ("midump.txt");
  348. #endif
  349. }
  350. //-----------------------------------------------------------------------------
  351. // Purpose: Reloads the currently loaded model file.
  352. //-----------------------------------------------------------------------------
  353. void MDLViewer::Refresh( void )
  354. {
  355. KeyValuesSystem()->InvalidateCache();
  356. g_pStudioModel->ReleaseStudioModel();
  357. g_pMDLCache->Flush();
  358. if ( recentFiles[0][0] != '\0' )
  359. {
  360. char szFile[MAX_PATH];
  361. strcpy( szFile, recentFiles[0] );
  362. g_pMaterialSystem->ReloadMaterials();
  363. d_cpl->loadModel( szFile );
  364. }
  365. }
  366. //-----------------------------------------------------------------------------
  367. // Purpose: Loads the file and updates the MRU list.
  368. // Input : pszFile - File to load.
  369. //-----------------------------------------------------------------------------
  370. void MDLViewer::LoadModelFile( const char *pszFile, int slot )
  371. {
  372. // copy off name, pszFile may be point into recentFiles array
  373. char filename[1024];
  374. strcpy( filename, pszFile );
  375. LoadModelResult_t eLoaded = d_cpl->loadModel( filename, slot );
  376. if ( eLoaded != LoadModel_Success )
  377. {
  378. switch (eLoaded)
  379. {
  380. case LoadModel_LoadFail:
  381. {
  382. mxMessageBox (this, "Error loading model.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
  383. break;
  384. }
  385. case LoadModel_PostLoadFail:
  386. {
  387. mxMessageBox (this, "Error post-loading model.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
  388. break;
  389. }
  390. case LoadModel_NoModel:
  391. {
  392. mxMessageBox (this, "Error loading model. The model has no vertices.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
  393. break;
  394. }
  395. }
  396. return;
  397. }
  398. if (slot == -1)
  399. {
  400. int i;
  401. for (i = 0; i < 8; i++)
  402. {
  403. if (!mx_strcasecmp( recentFiles[i], filename ))
  404. break;
  405. }
  406. // shift down existing recent files
  407. for (i = ((i > 7) ? 7 : i); i > 0; i--)
  408. {
  409. strcpy (recentFiles[i], recentFiles[i-1]);
  410. }
  411. strcpy( recentFiles[0], filename );
  412. initRecentFiles ();
  413. setLabel( "%s", filename );
  414. }
  415. else
  416. {
  417. mb->modify (IDC_FILE_UNLOADMERGEDMODEL1 + slot, IDC_FILE_UNLOADMERGEDMODEL1 + slot, pszFile);
  418. mb->setEnabled (IDC_FILE_UNLOADMERGEDMODEL1 + slot, true);
  419. }
  420. }
  421. //-----------------------------------------------------------------------------
  422. // Purpose: Takes a TGA screenshot of the given filename and exits.
  423. // Input : pszFile - File to load.
  424. //-----------------------------------------------------------------------------
  425. void MDLViewer::SaveScreenShot( const char *pszFile )
  426. {
  427. char filename[1024];
  428. strcpy( filename, pszFile );
  429. LoadModelResult_t eLoaded = d_cpl->loadModel( filename );
  430. //
  431. // Screenshot mode. Write a screenshot file and exit.
  432. //
  433. if ( eLoaded == LoadModel_Success )
  434. {
  435. g_viewerSettings.bgColor[0] = 117.0f / 255.0f;
  436. g_viewerSettings.bgColor[1] = 196.0f / 255.0f;
  437. g_viewerSettings.bgColor[2] = 219.0f / 255.0f;
  438. // Build the name of the TGA to write.
  439. char szScreenShot[256];
  440. strcpy(szScreenShot, filename);
  441. char *pchDot = strrchr(szScreenShot, '.');
  442. if (pchDot)
  443. {
  444. strcpy(pchDot, ".tga");
  445. }
  446. else
  447. {
  448. strcat(szScreenShot, ".tga");
  449. }
  450. // Center the view and write the TGA.
  451. d_cpl->centerView();
  452. d_MatSysWindow->dumpViewport(szScreenShot);
  453. }
  454. // Shut down.
  455. mx::quit();
  456. return;
  457. }
  458. void MDLViewer::DumpText( const char *pszFile )
  459. {
  460. char filename[1024];
  461. strcpy( filename, pszFile );
  462. LoadModelResult_t eLoaded = d_cpl->loadModel( filename );
  463. //
  464. // Screenshot mode. Write a screenshot file and exit.
  465. //
  466. if ( eLoaded == LoadModel_Success )
  467. {
  468. if ( g_pStudioModel->m_bIsTransparent )
  469. {
  470. Msg("%s is transparent\n", filename );
  471. }
  472. if ( g_pStudioModel->m_bHasProxy )
  473. {
  474. Msg("%s has material proxies\n", filename );
  475. }
  476. }
  477. // Shut down.
  478. mx::quit();
  479. }
  480. const char* MDLViewer::SteamGetOpenFilename()
  481. {
  482. if ( !g_FSDialogFactory )
  483. return NULL;
  484. static char filename[MAX_PATH];
  485. IFileSystemOpenDialog *pDlg;
  486. pDlg = (IFileSystemOpenDialog*)g_FSDialogFactory( FILESYSTEMOPENDIALOG_VERSION, NULL );
  487. if ( !pDlg )
  488. {
  489. char str[512];
  490. Q_snprintf( str, sizeof( str ), "Can't create %s interface.", FILESYSTEMOPENDIALOG_VERSION );
  491. MessageBox( NULL, str, "Error", MB_OK );
  492. return NULL;
  493. }
  494. pDlg->Init( g_Factory, NULL );
  495. pDlg->AddFileMask( "*.jpg" );
  496. pDlg->AddFileMask( "*.mdl" );
  497. pDlg->SetInitialDir( "models", "game" );
  498. pDlg->SetFilterMdlAndJpgFiles( true );
  499. if (pDlg->DoModal() == IDOK)
  500. {
  501. pDlg->GetFilename( filename, sizeof( filename ) );
  502. pDlg->Release();
  503. return filename;
  504. }
  505. else
  506. {
  507. pDlg->Release();
  508. return NULL;
  509. }
  510. }
  511. int
  512. MDLViewer::handleEvent (mxEvent *event)
  513. {
  514. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  515. switch (event->event)
  516. {
  517. case mxEvent::Action:
  518. {
  519. switch (event->action)
  520. {
  521. case IDC_FILE_LOADMODEL:
  522. {
  523. const char *ptr = mxGetOpenFileName (this, 0, "*.mdl");
  524. if (ptr)
  525. {
  526. LoadModelFile( ptr );
  527. }
  528. }
  529. break;
  530. case IDC_FILE_LOADMODEL_STEAM:
  531. {
  532. const char *pFilename = SteamGetOpenFilename();
  533. if ( pFilename )
  534. {
  535. LoadModelFile( pFilename );
  536. }
  537. }
  538. break;
  539. case IDC_FILE_LOADMERGEDMODEL:
  540. {
  541. const char *ptr = mxGetOpenFileName (this, 0, "*.mdl");
  542. if (ptr)
  543. {
  544. // find the first free slot
  545. int iChosenSlot = 0;
  546. for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
  547. {
  548. if ( g_viewerSettings.mergeModelFile[i][0] == 0 )
  549. {
  550. iChosenSlot = i;
  551. break;
  552. }
  553. }
  554. strcpy( g_viewerSettings.mergeModelFile[iChosenSlot], ptr );
  555. LoadModelFile( ptr, iChosenSlot );
  556. }
  557. }
  558. break;
  559. case IDC_FILE_LOADMERGEDMODEL_STEAM:
  560. {
  561. const char *pFilename = SteamGetOpenFilename();
  562. if ( pFilename )
  563. {
  564. // find the first free slot
  565. int iChosenSlot = 0;
  566. for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
  567. {
  568. if ( g_viewerSettings.mergeModelFile[i][0] == 0 )
  569. {
  570. iChosenSlot = i;
  571. break;
  572. }
  573. }
  574. strcpy( g_viewerSettings.mergeModelFile[iChosenSlot], pFilename );
  575. LoadModelFile( pFilename, iChosenSlot );
  576. }
  577. }
  578. break;
  579. case IDC_FILE_UNLOADMERGEDMODEL1:
  580. case IDC_FILE_UNLOADMERGEDMODEL2:
  581. case IDC_FILE_UNLOADMERGEDMODEL3:
  582. case IDC_FILE_UNLOADMERGEDMODEL4:
  583. case IDC_FILE_UNLOADMERGEDMODEL5:
  584. case IDC_FILE_UNLOADMERGEDMODEL6:
  585. case IDC_FILE_UNLOADMERGEDMODEL7:
  586. case IDC_FILE_UNLOADMERGEDMODEL8:
  587. case IDC_FILE_UNLOADMERGEDMODEL9:
  588. case IDC_FILE_UNLOADMERGEDMODEL10:
  589. case IDC_FILE_UNLOADMERGEDMODEL11:
  590. case IDC_FILE_UNLOADMERGEDMODEL12:
  591. {
  592. int i = event->action - IDC_FILE_UNLOADMERGEDMODEL1;
  593. // FIXME: move to d_cpl
  594. if (g_pStudioExtraModel[i])
  595. {
  596. V_strcpy_safe( g_viewerSettings.mergeModelFile[i], "" );
  597. g_pStudioExtraModel[i]->FreeModel( false );
  598. delete g_pStudioExtraModel[i];
  599. g_pStudioExtraModel[i] = NULL;
  600. mb->modify (IDC_FILE_UNLOADMERGEDMODEL1 + i, IDC_FILE_UNLOADMERGEDMODEL1 + i, "(empty)");
  601. mb->setEnabled (IDC_FILE_UNLOADMERGEDMODEL1 + i, false);
  602. }
  603. }
  604. break;
  605. case IDC_FILE_UNLOADALLMERGEDMODELS:
  606. d_cpl->UnloadAllMergedModels();
  607. break;
  608. case IDC_FILE_REFRESH:
  609. {
  610. Refresh();
  611. break;
  612. }
  613. case IDC_FLUSH_SHADERS:
  614. {
  615. CCommand args;
  616. args.Tokenize( "mat_flushshaders" );
  617. ConCommandBase *pCommandBase = g_pCVar->FindCommandBase( args[0] );
  618. if ( !pCommandBase )
  619. {
  620. ConWarning( "Unknown command or convar '%s'!\n", args[0] );
  621. break;
  622. }
  623. if ( pCommandBase->IsCommand() )
  624. {
  625. ConCommand *pCommand = static_cast<ConCommand*>( pCommandBase );
  626. pCommand->Dispatch( args );
  627. }
  628. }
  629. break;
  630. case IDC_FILE_LOADBACKGROUNDTEX:
  631. case IDC_FILE_LOADGROUNDTEX:
  632. {
  633. const char *ptr = mxGetOpenFileName (this, 0, "*.*");
  634. if (ptr)
  635. {
  636. if (0 /* d_MatSysWindow->loadTexture (ptr, event->action - IDC_FILE_LOADBACKGROUNDTEX) */)
  637. {
  638. if (event->action == IDC_FILE_LOADBACKGROUNDTEX)
  639. d_cpl->setShowBackground (true);
  640. else
  641. d_cpl->setShowGround (true);
  642. }
  643. else
  644. mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
  645. }
  646. }
  647. break;
  648. case IDC_FILE_UNLOADGROUNDTEX:
  649. {
  650. // d_MatSysWindow->loadTexture (0, 1);
  651. d_cpl->setShowGround (false);
  652. }
  653. break;
  654. case IDC_FILE_RECENTMODELS1:
  655. case IDC_FILE_RECENTMODELS2:
  656. case IDC_FILE_RECENTMODELS3:
  657. case IDC_FILE_RECENTMODELS4:
  658. case IDC_FILE_RECENTMODELS5:
  659. case IDC_FILE_RECENTMODELS6:
  660. case IDC_FILE_RECENTMODELS7:
  661. case IDC_FILE_RECENTMODELS8:
  662. {
  663. int i = event->action - IDC_FILE_RECENTMODELS1;
  664. LoadModelFile( recentFiles[i] );
  665. }
  666. break;
  667. case IDC_FILE_EXIT:
  668. {
  669. redraw ();
  670. mx::quit ();
  671. }
  672. break;
  673. case IDC_OPTIONS_COLORBACKGROUND:
  674. case IDC_OPTIONS_COLORGROUND:
  675. case IDC_OPTIONS_COLORLIGHT:
  676. case IDC_OPTIONS_COLORAMBIENT:
  677. {
  678. float *cols[4] = { g_viewerSettings.bgColor, g_viewerSettings.gColor, g_viewerSettings.lColor, g_viewerSettings.aColor };
  679. float *col = cols[event->action - IDC_OPTIONS_COLORBACKGROUND];
  680. int r = (int) (col[0] * 255.0f);
  681. int g = (int) (col[1] * 255.0f);
  682. int b = (int) (col[2] * 255.0f);
  683. if (mxChooseColor (this, &r, &g, &b))
  684. {
  685. col[0] = (float) r / 255.0f;
  686. col[1] = (float) g / 255.0f;
  687. col[2] = (float) b / 255.0f;
  688. }
  689. }
  690. break;
  691. case IDC_OPTIONS_CENTERVIEW:
  692. d_cpl->centerView ();
  693. if ( g_bHlmvMaster )
  694. {
  695. SendModelTransformToLinkedHlmv();
  696. }
  697. break;
  698. case IDC_OPTIONS_CENTERVERTS:
  699. //d_cpl->centerVerts( );
  700. if ( g_bHlmvMaster )
  701. {
  702. SendModelTransformToLinkedHlmv();
  703. }
  704. break;
  705. case IDC_OPTIONS_VIEWMODEL:
  706. {
  707. d_cpl->viewmodelView();
  708. if ( g_bHlmvMaster )
  709. {
  710. SendModelTransformToLinkedHlmv();
  711. }
  712. }
  713. break;
  714. case IDC_OPTIONS_MAKESCREENSHOT:
  715. {
  716. char *ptr = (char *) mxGetSaveFileName (this, "", "*.tga");
  717. if (ptr)
  718. {
  719. if (!strstr (ptr, ".tga"))
  720. strcat (ptr, ".tga");
  721. d_MatSysWindow->dumpViewport (ptr);
  722. }
  723. }
  724. break;
  725. case IDC_OPTIONS_DUMP:
  726. d_cpl->dumpModelInfo ();
  727. break;
  728. case IDC_OPTIONS_SYNCHLMVCAMERA:
  729. SendModelTransformToLinkedHlmv();
  730. break;
  731. case IDC_OPTIONS_LINKHLMV:
  732. if ( !g_bHlmvMaster && !g_HlmvIpcServer.IsRunning() && g_HlmvIpcClient.Connect() )
  733. {
  734. CUtlBuffer cmd;
  735. CUtlBuffer res;
  736. // Make connection to other hlmv
  737. cmd.PutString( "hlmvLink" );
  738. cmd.PutChar( '\0' );
  739. if ( g_HlmvIpcClient.ExecuteCommand( cmd, res ) )
  740. {
  741. g_bHlmvMaster = true;
  742. }
  743. g_HlmvIpcClient.Disconnect();
  744. SendModelTransformToLinkedHlmv();
  745. SendLightRotToLinkedHlmv();
  746. menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, true );
  747. menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, false );
  748. menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, false );
  749. menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, true );
  750. }
  751. break;
  752. case IDC_OPTIONS_UNLINKHLMV:
  753. if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
  754. {
  755. CUtlBuffer cmd;
  756. CUtlBuffer res;
  757. // Break connection to linked hlmv
  758. cmd.PutString( "hlmvUnlink" );
  759. cmd.PutChar( '\0' );
  760. g_HlmvIpcClient.ExecuteCommand( cmd, res );
  761. g_bHlmvMaster = false;
  762. g_HlmvIpcClient.Disconnect();
  763. menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
  764. menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, true );
  765. menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, true );
  766. menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
  767. }
  768. break;
  769. case IDC_VIEW_FILEASSOCIATIONS:
  770. g_FileAssociation->setAssociation (0);
  771. g_FileAssociation->setVisible (true);
  772. break;
  773. case IDC_VIEW_ACTIVITIES:
  774. g_viewerSettings.showActivities = !g_viewerSettings.showActivities;
  775. menuView->setChecked( event->action, g_viewerSettings.showActivities );
  776. d_cpl->initSequenceChoices();
  777. d_cpl->resetControlPanel();
  778. break;
  779. case IDC_VIEW_HIDDEN:
  780. g_viewerSettings.showHidden = !g_viewerSettings.showHidden;
  781. menuView->setChecked( event->action, g_viewerSettings.showHidden );
  782. d_cpl->initSequenceChoices();
  783. d_cpl->resetControlPanel();
  784. break;
  785. #ifdef WIN32
  786. case IDC_HELP_GOTOHOMEPAGE:
  787. ShellExecute (0, "open", "http://www.swissquake.ch/chumbalum-soft/index.html", 0, 0, SW_SHOW);
  788. break;
  789. #endif
  790. case IDC_HELP_ABOUT:
  791. mxMessageBox (this,
  792. "Half-Life Model Viewer v2.0 (c) 2004 Valve Corp.\n"
  793. "Portions (c) 1999 by Mete Ciragan\n\n"
  794. "Left-drag inside circle to spin.\n"
  795. "Left-drag outside circle to rotate.\n"
  796. "Right-drag to zoom.\n"
  797. "Shift-left-drag to x-y-pan.\n"
  798. "Shift-right-drag to z-pan.\n"
  799. "Ctrl-left-drag to move light.\n\n"
  800. "Build:\t" __DATE__ ".\n"
  801. "Email:\t[email protected]\n"
  802. "Web:\thttp://www.swissquake.ch/chumbalum-soft/", "About Half-Life Model Viewer",
  803. MX_MB_OK | MX_MB_INFORMATION);
  804. break;
  805. case IDC_ACCEL_WIREFRAME:
  806. d_cpl->setOverlayWireframe( !g_viewerSettings.overlayWireframe );
  807. break;
  808. case IDC_ACCEL_ATTACHMENTS:
  809. d_cpl->setShowAttachments( !g_viewerSettings.showAttachments );
  810. break;
  811. case IDC_ACCEL_GROUND:
  812. d_cpl->setShowGround( !g_viewerSettings.showGround );
  813. break;
  814. case IDC_ACCEL_HITBOXES:
  815. d_cpl->setShowHitBoxes( !g_viewerSettings.showHitBoxes );
  816. break;
  817. case IDC_ACCEL_BONES:
  818. d_cpl->setShowBones( !g_viewerSettings.showBones );
  819. break;
  820. case IDC_ACCEL_BACKGROUND:
  821. d_cpl->setShowBackground( !g_viewerSettings.showBackground );
  822. break;
  823. case IDC_ACCEL_MOVEMENT:
  824. d_cpl->setShowMovement( !g_viewerSettings.showMovement );
  825. break;
  826. case IDC_ACCEL_NORMALS:
  827. d_cpl->setShowNormals( !g_viewerSettings.showNormals );
  828. break;
  829. case IDC_ACCEL_TANGENTS:
  830. d_cpl->setShowTangentFrame( !g_viewerSettings.showTangentFrame );
  831. break;
  832. case IDC_ACCEL_SHADOW:
  833. d_cpl->setShowShadow( !g_viewerSettings.showShadow );
  834. break;
  835. } //switch (event->action)
  836. } // mxEvent::Action
  837. break;
  838. case mxEvent::Size:
  839. {
  840. g_viewerSettings.xpos = x();
  841. g_viewerSettings.ypos = y();
  842. g_viewerSettings.width = w();
  843. g_viewerSettings.height = h();
  844. int w = event->width;
  845. int h = event->height;
  846. int y = mb->getHeight ();
  847. #ifdef WIN32
  848. #define HEIGHT 240
  849. #else
  850. #define HEIGHT 140
  851. h -= 40;
  852. #endif
  853. d_MatSysWindow->setBounds (0, y, w, h - HEIGHT); // !!
  854. d_cpl->setBounds (0, y + h - HEIGHT, w, HEIGHT);
  855. }
  856. break;
  857. case mxEvent::PosChanged:
  858. {
  859. g_viewerSettings.xpos = x();
  860. g_viewerSettings.ypos = y();
  861. }
  862. break;
  863. case KeyDown:
  864. d_MatSysWindow->handleEvent(event);
  865. d_cpl->handleEvent(event);
  866. break;
  867. case mxEvent::Activate:
  868. {
  869. if (event->action)
  870. {
  871. mx::setIdleWindow( getMatSysWindow() );
  872. }
  873. else
  874. {
  875. mx::setIdleWindow( 0 );
  876. }
  877. }
  878. break;
  879. case mxEvent::Timer:
  880. {
  881. if ( g_HlmvIpcServer.HasCommands() )
  882. {
  883. // Execute next command at next msg pump, ~ 1/60th s (60Hz) if controlled, 0.1s if not
  884. if ( g_bHlmvControlled )
  885. {
  886. // Clear up to 10 pending commands if controlled
  887. for ( int nCmdCount = 0; nCmdCount < 10 && g_HlmvIpcServer.HasCommands(); ++nCmdCount )
  888. {
  889. handleIpcCommand( g_HlmvIpcServer.GetCommand() );
  890. g_HlmvIpcServer.PopCommand();
  891. }
  892. setTimer( 17 );
  893. }
  894. else
  895. {
  896. handleIpcCommand( g_HlmvIpcServer.GetCommand() );
  897. g_HlmvIpcServer.PopCommand();
  898. setTimer( 100 );
  899. }
  900. }
  901. else if ( !g_HlmvIpcServer.IsRunning() )
  902. {
  903. // Keep trying to establish our server slot if another instance quits
  904. BOOL bIsRunning = g_HlmvIpcServer.IsRunning();
  905. g_HlmvIpcServer.EnsureRegisteredAndRunning();
  906. if ( !bIsRunning && g_HlmvIpcServer.IsRunning() )
  907. {
  908. g_bHlmvMaster = false;
  909. menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, false );
  910. menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
  911. menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
  912. menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
  913. }
  914. // Attempt every 1.0 s
  915. setTimer( 1000 );
  916. }
  917. else
  918. {
  919. // Idling, poll command queue @ ~60Hz if controlled, 0.5s ( 2Hz ) otherwise
  920. if ( g_bHlmvControlled )
  921. {
  922. setTimer( 17 );
  923. }
  924. else
  925. {
  926. setTimer( 500 );
  927. }
  928. }
  929. }
  930. break;
  931. } // event->event
  932. return 1;
  933. }
  934. void TranslateMayaToHLMVCoordinates( const Vector &vMayaPos, const QAngle &vMayaRot, Vector &vHLMVPos, QAngle &vHLMVAngles )
  935. {
  936. vHLMVPos.Init( vMayaPos.z, vMayaPos.x, vMayaPos.y );
  937. vHLMVAngles[PITCH] = -vMayaRot[0];
  938. vHLMVAngles[YAW] = vMayaRot[1] + 180;
  939. vHLMVAngles[ROLL] = -vMayaRot[2];
  940. }
  941. void MDLViewer::handleIpcCommand( char *szCommand )
  942. {
  943. MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
  944. if ( !strcmp( "reload", szCommand ) )
  945. {
  946. Refresh();
  947. if ( HWND hWnd = (HWND) getHandle() )
  948. {
  949. if ( ::IsIconic( hWnd ) )
  950. ::ShowWindow( hWnd, SW_RESTORE );
  951. ::BringWindowToTop( hWnd );
  952. ::SetForegroundWindow( hWnd );
  953. ::SetFocus( hWnd );
  954. }
  955. }
  956. else if ( V_strncasecmp( "cameraTo", szCommand, 8 ) == 0 )
  957. {
  958. // Read the camera position and angles from Maya.
  959. Vector vMayaPos;
  960. QAngle vMayaRot;
  961. char szFirstPart[32];
  962. sscanf( szCommand, "%s %f %f %f %f %f %f",
  963. szFirstPart,
  964. &vMayaPos.x, &vMayaPos.y, &vMayaPos.z,
  965. &vMayaRot.x, &vMayaRot.y, &vMayaRot.z );
  966. //
  967. // Obviously, this could all be simplified, but it's nice to make it easy to see what it's doing here.
  968. //
  969. // Translate the camera position/angles from Maya space to model space.
  970. // In model space, +X=forward, +Y=left, and +Z=up
  971. // (i.e. the model faces forward along +X)
  972. Vector vCameraPos;
  973. QAngle vCameraAngles;
  974. TranslateMayaToHLMVCoordinates( vMayaPos, vMayaRot, vCameraPos, vCameraAngles );
  975. // Now, build a matrix from model space to camera space. The way we're defining camera space,
  976. // the axes point the same way as in model space (+X=forward, +Y=left, +Z=up).
  977. // This is a standard put-stuff-in-camera-space matrix (backtranslate and then backrotate).
  978. matrix3x4_t mModelToCameraRot, mModelToCameraTrans, mModelToCameraFull;
  979. // Backtranslate..
  980. SetIdentityMatrix( mModelToCameraTrans );
  981. MatrixSetColumn( -vCameraPos, 3, mModelToCameraTrans );
  982. // Backrotate..
  983. AngleMatrix( vCameraAngles, mModelToCameraRot );
  984. MatrixTranspose( mModelToCameraRot );
  985. // Concatenate.
  986. MatrixMultiply( mModelToCameraRot, mModelToCameraTrans, mModelToCameraFull );
  987. // Now we need to convert the camera space from above to HLMV's specific camera space. This just means negating
  988. // the X and Y axes because HLMV's camera space is (+X=back, +Y=left, +Z=up).
  989. matrix3x4_t mCameraToWorld(
  990. -1, 0, 0, 0,
  991. 0, -1, 0, 0,
  992. 0, 0, 1, 0 );
  993. // Blat all these matrices together.
  994. matrix3x4_t mFinal;
  995. MatrixMultiply( mCameraToWorld, mModelToCameraFull, mFinal );
  996. // Tell HLMV our new fancy transform to use, and then we'll see the model from the same place Maya did.
  997. g_pStudioModel->SetModelTransform( mFinal );
  998. // Redraw.
  999. d_MatSysWindow->redraw();
  1000. }
  1001. else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvModelTransform" ) )
  1002. {
  1003. matrix3x4_t m;
  1004. sscanf( szCommand, "%*s %f %f %f %f %f %f %f %f %f %f %f %f",
  1005. &m.m_flMatVal[0][0], &m.m_flMatVal[0][1], &m.m_flMatVal[0][2], &m.m_flMatVal[0][3],
  1006. &m.m_flMatVal[1][0], &m.m_flMatVal[1][1], &m.m_flMatVal[1][2], &m.m_flMatVal[1][3],
  1007. &m.m_flMatVal[2][0], &m.m_flMatVal[2][1], &m.m_flMatVal[2][2], &m.m_flMatVal[2][3] );
  1008. // Tell HLMV our new fancy transform to use, and then we'll see the model from the same place Maya did.
  1009. g_pStudioModel->SetModelTransform( m );
  1010. // Redraw.
  1011. d_MatSysWindow->redraw();
  1012. }
  1013. else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvLightRot" ) )
  1014. {
  1015. sscanf( szCommand, "%*s %f %f %f",
  1016. &g_viewerSettings.lightrot[0], &g_viewerSettings.lightrot[1], &g_viewerSettings.lightrot[2] );
  1017. // Redraw.
  1018. d_MatSysWindow->redraw();
  1019. }
  1020. else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvForceFrame" ) )
  1021. {
  1022. float flFrame = 0.0f;
  1023. sscanf( szCommand, "%*s %f", &flFrame );
  1024. d_cpl->SetFrameSlider( flFrame );
  1025. d_cpl->setFrame( flFrame );
  1026. d_cpl->setSpeedScale( 0 );
  1027. // Redraw.
  1028. d_MatSysWindow->redraw();
  1029. }
  1030. else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvLink" ) )
  1031. {
  1032. if ( !g_bHlmvControlled )
  1033. {
  1034. g_bHlmvControlled = true;
  1035. CUtlString label( "LINKED: " );
  1036. label += getLabel();
  1037. setLabel( label.Get() );
  1038. }
  1039. }
  1040. else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvUnlink" ) )
  1041. {
  1042. g_bHlmvControlled = false;
  1043. const char *pszLabel = getLabel();
  1044. if ( StringHasPrefixCaseSensitive( pszLabel, "LINKED: " ) )
  1045. {
  1046. setLabel( pszLabel + 8 ); // Skip past "LINKED: "
  1047. }
  1048. }
  1049. }
  1050. void
  1051. MDLViewer::redraw ()
  1052. {
  1053. /*
  1054. mxEvent event;
  1055. event.event = mxEvent::Size;
  1056. event.width = w2 ();
  1057. event.height = h2 ();
  1058. handleEvent (&event);
  1059. */
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Purpose:
  1063. // Output : int
  1064. //-----------------------------------------------------------------------------
  1065. int MDLViewer::GetCurrentHitboxSet( void )
  1066. {
  1067. return d_cpl ? d_cpl->GetCurrentHitboxSet() : 0;
  1068. }
  1069. //-----------------------------------------------------------------------------
  1070. // Sends the model transform to the controlled hlmv instance
  1071. //-----------------------------------------------------------------------------
  1072. void MDLViewer::SendModelTransformToLinkedHlmv()
  1073. {
  1074. if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
  1075. {
  1076. matrix3x4_t m;
  1077. g_pStudioModel->GetModelTransform( m );
  1078. CUtlBuffer cmd;
  1079. CUtlBuffer res;
  1080. cmd.Printf( "%s %f %f %f %f %f %f %f %f %f %f %f %f",
  1081. "hlmvModelTransform",
  1082. m.m_flMatVal[0][0], m.m_flMatVal[0][1], m.m_flMatVal[0][2], m.m_flMatVal[0][3],
  1083. m.m_flMatVal[1][0], m.m_flMatVal[1][1], m.m_flMatVal[1][2], m.m_flMatVal[1][3],
  1084. m.m_flMatVal[2][0], m.m_flMatVal[2][1], m.m_flMatVal[2][2], m.m_flMatVal[2][3] );
  1085. g_HlmvIpcClient.ExecuteCommand( cmd, res );
  1086. g_HlmvIpcClient.Disconnect();
  1087. }
  1088. }
  1089. //-----------------------------------------------------------------------------
  1090. // Sends the light rotation to the controlled hlmv instance
  1091. //-----------------------------------------------------------------------------
  1092. void MDLViewer::SendLightRotToLinkedHlmv()
  1093. {
  1094. if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
  1095. {
  1096. CUtlBuffer cmdLightRot;
  1097. CUtlBuffer resLightRot;
  1098. cmdLightRot.Printf( "%s %f %f %f",
  1099. "hlmvLightRot",
  1100. g_viewerSettings.lightrot[0], g_viewerSettings.lightrot[1], g_viewerSettings.lightrot[2] );
  1101. g_HlmvIpcClient.ExecuteCommand( cmdLightRot, resLightRot );
  1102. g_HlmvIpcClient.Disconnect();
  1103. }
  1104. }
  1105. SpewRetval_t HLMVSpewFunc( SpewType_t spewType, char const *pMsg )
  1106. {
  1107. g_bInError = true;
  1108. switch (spewType)
  1109. {
  1110. case SPEW_ERROR:
  1111. MessageBox(NULL, pMsg, "FATAL ERROR", MB_OK);
  1112. g_bInError = false;
  1113. return SPEW_ABORT;
  1114. default:
  1115. OutputDebugString(pMsg);
  1116. g_bInError = false;
  1117. #ifdef _DEBUG
  1118. return spewType == SPEW_ASSERT ? SPEW_DEBUGGER : SPEW_CONTINUE;
  1119. #else
  1120. return SPEW_CONTINUE;
  1121. #endif
  1122. }
  1123. }
  1124. //-----------------------------------------------------------------------------
  1125. // The application object
  1126. //-----------------------------------------------------------------------------
  1127. class CHLModelViewerApp : public CSteamAppSystemGroup
  1128. {
  1129. public:
  1130. // Methods of IApplication
  1131. virtual bool Create();
  1132. virtual bool PreInit();
  1133. virtual int Main();
  1134. virtual void PostShutdown();
  1135. virtual void Destroy();
  1136. };
  1137. //-----------------------------------------------------------------------------
  1138. // Create all singleton systems
  1139. //-----------------------------------------------------------------------------
  1140. bool CHLModelViewerApp::Create()
  1141. {
  1142. SpewOutputFunc( HLMVSpewFunc );
  1143. g_dxlevel = CommandLine()->ParmValue( "-dx", 0 );
  1144. g_bOldFileDialogs = ( CommandLine()->FindParm( "-olddialogs" ) != 0 );
  1145. AppSystemInfo_t appSystems[] =
  1146. {
  1147. { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
  1148. { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
  1149. { "vphysics.dll", VPHYSICS_INTERFACE_VERSION },
  1150. { "datacache.dll", DATACACHE_INTERFACE_VERSION },
  1151. { "datacache.dll", MDLCACHE_INTERFACE_VERSION },
  1152. { "datacache.dll", STUDIO_DATA_CACHE_INTERFACE_VERSION },
  1153. { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
  1154. { "soundsystem.dll", SOUNDSYSTEM_INTERFACE_VERSION },
  1155. { "", "" } // Required to terminate the list
  1156. };
  1157. if ( !AddSystems( appSystems ) )
  1158. return false;
  1159. g_pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
  1160. g_pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
  1161. g_pMaterialSystemHardwareConfig = (IMaterialSystemHardwareConfig*)FindSystem( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION );
  1162. g_pStudioRender = (IStudioRender*)FindSystem( STUDIO_RENDER_INTERFACE_VERSION );
  1163. g_pDataCache = (IDataCache*)FindSystem( DATACACHE_INTERFACE_VERSION );
  1164. g_pMDLCache = (IMDLCache*)FindSystem( MDLCACHE_INTERFACE_VERSION );
  1165. g_pStudioDataCache = (IStudioDataCache*)FindSystem( STUDIO_DATA_CACHE_INTERFACE_VERSION );
  1166. physcollision = (IPhysicsCollision *)FindSystem( VPHYSICS_COLLISION_INTERFACE_VERSION );
  1167. physprop = (IPhysicsSurfaceProps *)FindSystem( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION );
  1168. g_pSoundEmitterBase = (ISoundEmitterSystemBase *)FindSystem( SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
  1169. g_pSoundSystem = (ISoundSystem *)FindSystem( SOUNDSYSTEM_INTERFACE_VERSION );
  1170. if ( !g_pFileSystem || !physprop || !physcollision || !g_pMaterialSystem || !g_pStudioRender || !g_pMDLCache || !g_pDataCache )
  1171. {
  1172. Error("Unable to load required library interface!\n");
  1173. }
  1174. const char *pShaderDLL = CommandLine()->ParmValue("-shaderdll");
  1175. const char *pArg;
  1176. if ( CommandLine()->CheckParm( "-shaderapi", &pArg ))
  1177. {
  1178. pShaderDLL = pArg;
  1179. }
  1180. if(!pShaderDLL)
  1181. {
  1182. pShaderDLL = "shaderapidx9.dll";
  1183. }
  1184. g_pMaterialSystem->SetShaderAPI( pShaderDLL );
  1185. g_Factory = GetFactory();
  1186. return true;
  1187. }
  1188. void CHLModelViewerApp::Destroy()
  1189. {
  1190. g_pFileSystem = NULL;
  1191. g_pMaterialSystem = NULL;
  1192. g_pMaterialSystemHardwareConfig = NULL;
  1193. g_pStudioRender = NULL;
  1194. g_pDataCache = NULL;
  1195. g_pMDLCache = NULL;
  1196. g_pStudioDataCache = NULL;
  1197. physcollision = NULL;
  1198. physprop = NULL;
  1199. }
  1200. //-----------------------------------------------------------------------------
  1201. // Init, shutdown
  1202. //-----------------------------------------------------------------------------
  1203. bool CHLModelViewerApp::PreInit( )
  1204. {
  1205. CreateInterfaceFn factory = GetFactory();
  1206. ConnectTier1Libraries( &factory, 1 );
  1207. ConVar_Register( 0 );
  1208. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  1209. // Add paths...
  1210. if ( !SetupSearchPaths( NULL, false, true ) )
  1211. return false;
  1212. // Get the adapter from the command line....
  1213. const char *pAdapterString;
  1214. int nAdapter = 0;
  1215. if (CommandLine()->CheckParm( "-adapter", &pAdapterString ))
  1216. {
  1217. nAdapter = atoi( pAdapterString );
  1218. }
  1219. int nAdapterFlags = 0;
  1220. if ( CommandLine()->CheckParm( "-ref" ) )
  1221. {
  1222. nAdapterFlags |= MATERIAL_INIT_REFERENCE_RASTERIZER;
  1223. }
  1224. g_pMaterialSystem->SetAdapter( nAdapter, nAdapterFlags );
  1225. g_bOldFileDialogs = true;
  1226. if ( CommandLine()->FindParm( "-NoSteamdDialog" ) )
  1227. g_bOldFileDialogs = false;
  1228. LoadFileSystemDialogModule();
  1229. return true;
  1230. }
  1231. void CHLModelViewerApp::PostShutdown()
  1232. {
  1233. UnloadFileSystemDialogModule();
  1234. DisconnectTier1Libraries();
  1235. }
  1236. //-----------------------------------------------------------------------------
  1237. // main application
  1238. //-----------------------------------------------------------------------------
  1239. int CHLModelViewerApp::Main()
  1240. {
  1241. g_pMaterialSystem->ModInit();
  1242. g_pSoundEmitterBase->ModInit();
  1243. g_pDataCache->SetSize( 64 * 1024 * 1024 );
  1244. //mx::setDisplayMode (0, 0, 0);
  1245. g_MDLViewer = new MDLViewer ();
  1246. g_MDLViewer->setMenuBar (g_MDLViewer->getMenuBar ());
  1247. g_pStudioModel->Init();
  1248. g_pStudioModel->ModelInit();
  1249. g_pStudioModel->ClearLookTargets( );
  1250. // Load up the initial model
  1251. const char *pMdlName = NULL;
  1252. int nParmCount = CommandLine()->ParmCount();
  1253. if ( nParmCount > 1 )
  1254. {
  1255. pMdlName = CommandLine()->GetParm( nParmCount - 1 );
  1256. }
  1257. if ( pMdlName && Q_stristr( pMdlName, ".mdl" ) )
  1258. {
  1259. char absPath[MAX_PATH];
  1260. Q_MakeAbsolutePath( absPath, sizeof( absPath ), pMdlName );
  1261. if ( CommandLine()->FindParm( "-screenshot" ) )
  1262. {
  1263. g_MDLViewer->SaveScreenShot( absPath );
  1264. }
  1265. else if ( CommandLine()->FindParm( "-dump" ) )
  1266. {
  1267. g_MDLViewer->DumpText( absPath );
  1268. }
  1269. else
  1270. {
  1271. g_MDLViewer->LoadModelFile( absPath );
  1272. }
  1273. }
  1274. int nRetVal = mx::run ();
  1275. g_pStudioModel->Shutdown();
  1276. g_pMaterialSystem->ModShutdown();
  1277. return nRetVal;
  1278. }
  1279. static bool CHLModelViewerApp_SuggestGameInfoDirFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
  1280. {
  1281. const char *pMdlName = NULL;
  1282. int nParmCount = CommandLine()->ParmCount();
  1283. if ( nParmCount > 1 )
  1284. {
  1285. pMdlName = CommandLine()->GetParm( nParmCount - 1 );
  1286. }
  1287. if ( pMdlName && Q_stristr( pMdlName, ".mdl" ) )
  1288. {
  1289. Q_MakeAbsolutePath( pchPathBuffer, nBufferLength, pMdlName );
  1290. if ( pbBubbleDirectories )
  1291. *pbBubbleDirectories = true;
  1292. return true;
  1293. }
  1294. return false;
  1295. }
  1296. //-----------------------------------------------------------------------------
  1297. // Main entry point
  1298. //-----------------------------------------------------------------------------
  1299. int main (int argc, char *argv[])
  1300. {
  1301. CommandLine()->CreateCmdLine( argc, argv );
  1302. mx::init( argc, argv );
  1303. // make sure we start in the right directory
  1304. char szName[256];
  1305. strcpy( szName, mx::getApplicationPath() );
  1306. // mx_setcwd (szName);
  1307. // Set game info directory suggestion callback
  1308. SetSuggestGameInfoDirFn( CHLModelViewerApp_SuggestGameInfoDirFn );
  1309. CHLModelViewerApp hlmodelviewerApp;
  1310. CSteamApplication steamApplication( &hlmodelviewerApp );
  1311. return steamApplication.Run();
  1312. }
  1313. //
  1314. // Implementation of IPC server
  1315. //
  1316. CHlmvIpcServer::~CHlmvIpcServer()
  1317. {
  1318. for ( int k = 0; k < m_lstCommands.Count(); ++ k )
  1319. {
  1320. delete [] m_lstCommands[k];
  1321. }
  1322. m_lstCommands.Purge();
  1323. }
  1324. bool CHlmvIpcServer::HasCommands()
  1325. {
  1326. AUTO_LOCK( m_mtx );
  1327. return m_lstCommands.Count() > 0;
  1328. }
  1329. void CHlmvIpcServer::AppendCommand( char *pszCommand )
  1330. {
  1331. AUTO_LOCK( m_mtx );
  1332. m_lstCommands.AddToTail( pszCommand );
  1333. }
  1334. char * CHlmvIpcServer::GetCommand()
  1335. {
  1336. AUTO_LOCK( m_mtx );
  1337. return m_lstCommands.Count() ? m_lstCommands[0] : "";
  1338. }
  1339. void CHlmvIpcServer::PopCommand()
  1340. {
  1341. AUTO_LOCK( m_mtx );
  1342. if ( m_lstCommands.Count() )
  1343. {
  1344. delete [] m_lstCommands[0];
  1345. m_lstCommands.Remove( 0 );
  1346. }
  1347. }
  1348. BOOL CHlmvIpcServer::ExecuteCommand(CUtlBuffer &cmd, CUtlBuffer &res)
  1349. {
  1350. char *szCommand = ( char * ) cmd.Base();
  1351. int nLen = strlen( szCommand );
  1352. while ( nLen > 0 && V_isspace( szCommand[ nLen - 1 ] ) )
  1353. -- nLen;
  1354. if ( nLen <= 0 )
  1355. return FALSE;
  1356. char *pchCopy = new char[ nLen + 1 ];
  1357. memcpy( pchCopy, szCommand, nLen );
  1358. pchCopy[ nLen ] = 0;
  1359. AppendCommand( pchCopy );
  1360. res.PutInt( 0 );
  1361. return TRUE;
  1362. }