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.

1161 lines
26 KiB

  1. //====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
  2. //
  3. // Purpose:
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #include "stdafx.h"
  9. #include "cmdsyncmesh.h"
  10. #include "cmdhandlers.h"
  11. #include <io.h>
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <direct.h>
  15. #include <atlenc.h>
  16. #include "hammer.h"
  17. #include "Box3D.h" // For units
  18. #include "History.h"
  19. #include "MainFrm.h"
  20. #include "GlobalFunctions.h"
  21. #include "MapDoc.h"
  22. #include "MapView.h"
  23. #include "ToolManager.h"
  24. #include "smartptr.h"
  25. #include "utlhash.h"
  26. #include "generichash.h"
  27. #include "utlbuffer.h"
  28. #include "valve_ipc_win32.h"
  29. #include "threadtools.h"
  30. #include "mapsolid.h"
  31. #include "mapentity.h"
  32. #include "chunkfile.h"
  33. #include "texturebrowser.h"
  34. #include "vmfentitysupport.h"
  35. #include "vmfmeshdatasupport.h"
  36. //
  37. // Fwd declarations
  38. //
  39. class CToolHandler_SyncMesh;
  40. //
  41. // Friendly structures declarations
  42. //
  43. class CMapDoc_Friendly : public CMapDoc
  44. {
  45. friend class CToolHandler_SyncMesh;
  46. };
  47. static CMapDoc_Friendly * _AsFriendlyDoc( CDocument *pDoc )
  48. {
  49. if ( pDoc )
  50. {
  51. ASSERT_KINDOF( CMapDoc, pDoc );
  52. }
  53. return static_cast< CMapDoc_Friendly * >( pDoc );
  54. }
  55. //
  56. // Settings
  57. //
  58. static bool s_opt_vmf_bStoreMdl = true;
  59. static bool s_opt_vmf_bStoreDmx = true;
  60. static bool s_opt_vmf_bStoreMa = true;
  61. static bool s_opt_maya_bMeshAtOrigin = false;
  62. static bool s_opt_maya_bReplaceSelection = true;
  63. static HWND s_hMainWnd = NULL;
  64. //
  65. // Utility routines
  66. //
  67. BOOL PrepareEmptyTempHammerDir( CString &sPath )
  68. {
  69. // Create the temp directory
  70. char const *pszVproject = getenv( "VPROJECT" );
  71. if ( !pszVproject )
  72. return FALSE;
  73. sPath = pszVproject;
  74. sPath.Replace( '\\', '/' );
  75. sPath.Append( "/models/.hammer.tmp" );
  76. // Remove the folder
  77. if ( !access( sPath, 00) )
  78. {
  79. SHFILEOPSTRUCT sfo;
  80. memset( &sfo, 0, sizeof( sfo ) );
  81. sfo.hwnd = AfxGetMainWnd()->GetSafeHwnd();
  82. sfo.wFunc = FO_DELETE;
  83. sfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
  84. CArrayAutoPtr< char > bufPathCopy( new char [ sPath.GetLength() + 10 ] );
  85. sprintf( bufPathCopy.Get(), "%s/*.*%c%c", sPath, 0, 0 );
  86. Q_FixSlashes( bufPathCopy.Get() );
  87. sfo.pFrom = bufPathCopy.Get();
  88. if ( SHFileOperation( &sfo ) )
  89. return FALSE;
  90. }
  91. // Create it empty
  92. BOOL bDir = !mkdir( sPath ) || ( errno == EEXIST );
  93. if ( !bDir )
  94. return FALSE;
  95. return TRUE;
  96. }
  97. BOOL GetMdlCacheDir( CString &sPath )
  98. {
  99. // Create the temp directory
  100. char const *pszVproject = getenv( "VPROJECT" );
  101. if ( !pszVproject )
  102. return FALSE;
  103. sPath = pszVproject;
  104. sPath.Replace( '\\', '/' );
  105. sPath.Append( "/models/.hammer.mdlcache" );
  106. // Create it empty
  107. BOOL bDir = !mkdir( sPath ) || ( errno == EEXIST );
  108. if ( !bDir )
  109. return FALSE;
  110. return TRUE;
  111. }
  112. CString GetSystemTempDir()
  113. {
  114. CString sPath;
  115. if ( char const *pszTemp = getenv("TEMP") )
  116. {
  117. sPath = pszTemp;
  118. }
  119. else if ( char const *pszTmp = getenv("TMP") )
  120. {
  121. sPath = pszTmp;
  122. }
  123. else
  124. {
  125. sPath = "c:";
  126. }
  127. sPath.Replace( '\\', '/' );
  128. return sPath;
  129. }
  130. int ComputeBufferHash( const void *lpvBuffer, size_t numBytes )
  131. {
  132. return HashBlock( lpvBuffer, numBytes );
  133. }
  134. int ComputeFileHash( const char *pszFilename )
  135. {
  136. CUtlBuffer bufFile;
  137. if ( g_pFileSystem->ReadFile( pszFilename, NULL, bufFile ) )
  138. return ComputeBufferHash( bufFile.Base(), bufFile.TellPut() );
  139. return 0;
  140. }
  141. BOOL IsFileReadable( const char *pszFilename )
  142. {
  143. return 0 == access( pszFilename, 04 );
  144. }
  145. CString GetOtherFileName( CString &sFile, char const *szNewExt, int nCurExtLen )
  146. {
  147. return sFile.Left( sFile.GetLength() - nCurExtLen ) + szNewExt;
  148. }
  149. void SwitchActiveWindow( HWND hWndActivate )
  150. {
  151. HWND hWndFg = GetForegroundWindow();
  152. if ( hWndActivate == hWndFg )
  153. return;
  154. DWORD dwThreadIdFg, dwThreadIdActivate;
  155. dwThreadIdFg = GetWindowThreadProcessId( hWndFg, NULL );
  156. dwThreadIdActivate = GetWindowThreadProcessId( hWndActivate, NULL );
  157. if ( dwThreadIdActivate != dwThreadIdFg )
  158. AttachThreadInput( dwThreadIdFg, dwThreadIdActivate, TRUE );
  159. SetForegroundWindow( hWndActivate );
  160. BringWindowToTop( hWndActivate );
  161. if ( dwThreadIdActivate != dwThreadIdFg )
  162. AttachThreadInput( dwThreadIdFg, dwThreadIdActivate, FALSE );
  163. if ( IsIconic( hWndActivate ) )
  164. ShowWindow( hWndActivate, SW_RESTORE );
  165. else
  166. ShowWindow( hWndActivate, SW_SHOW );
  167. }
  168. //
  169. // Command handler classes
  170. //
  171. class CToolHandler_SyncMesh : public IToolHandlerInfo
  172. {
  173. public:
  174. CToolHandler_SyncMesh();
  175. public:
  176. virtual BOOL UpdateCmdUI( CCmdUI *pCmdUI );
  177. virtual BOOL Execute( UINT uMsg );
  178. public:
  179. BOOL RequestDmxLoad( char const *pszCookie, char const *pszDmxFile );
  180. BOOL RequestTexture( char const *pszTextureString );
  181. void AppMainLoopIdle();
  182. protected:
  183. BOOL Setup();
  184. BOOL CanExecute();
  185. protected:
  186. BOOL CopySelToClipboard();
  187. BOOL CreateTempDoc();
  188. BOOL PasteSelToTempDoc();
  189. BOOL CreateTempFileName();
  190. BOOL ExportTempDoc();
  191. BOOL CloseTempDoc();
  192. BOOL NotifyMaya();
  193. protected:
  194. BOOL ProcessDmxRequest();
  195. BOOL DmxPrepareModelFiles();
  196. BOOL DmxDeleteOrigObjects();
  197. BOOL DmxCreateStaticProp();
  198. protected:
  199. CWinApp *m_pApp;
  200. CMapDoc_Friendly *m_pDoc;
  201. CMapView *m_pView;
  202. CSelection *m_pSelection;
  203. CSelection m_origSelection;
  204. Vector m_vecSelectionCenter;
  205. IHammerClipboard *m_pCopiedObjects;
  206. CMapDoc_Friendly *m_pTempDoc;
  207. CString m_strTempFileName;
  208. CString m_strMayaRequest;
  209. CString m_strCookie;
  210. CString m_strReqCookie;
  211. CString m_strReqDmxFileName;
  212. CDialog *m_pAsyncDialogRequest;
  213. CThreadFastMutex m_mtxRequest;
  214. CString m_strDmxFileName;
  215. CString m_strMdlFileName;
  216. }
  217. g_ToolHandlerSyncMesh;
  218. IToolHandlerInfo *g_pToolHandlerSyncMesh = &g_ToolHandlerSyncMesh;
  219. static void AppMainLoopIdle_Delegate()
  220. {
  221. g_ToolHandlerSyncMesh.AppMainLoopIdle();
  222. }
  223. CToolHandler_SyncMesh::CToolHandler_SyncMesh()
  224. {
  225. m_pApp = NULL;
  226. m_pDoc = NULL;
  227. m_pView = NULL;
  228. m_pSelection = NULL;
  229. m_pCopiedObjects = NULL;
  230. m_pTempDoc = NULL;
  231. m_pAsyncDialogRequest = NULL;
  232. AppRegisterMessageLoopFn( AppMainLoopIdle_Delegate );
  233. }
  234. //////////////////////////////////////////////////////////////////////////
  235. //
  236. // IPC Server class
  237. //
  238. //////////////////////////////////////////////////////////////////////////
  239. class CHammerIpcServer : public CValveIpcServerUtl
  240. {
  241. public:
  242. CHammerIpcServer() : CValveIpcServerUtl( "HAMMER_IPC_SERVER" )
  243. {
  244. AppRegisterPostInitFn( AppInit );
  245. AppRegisterPreShutdownFn( AppShutdown );
  246. }
  247. static void AppInit() { g_HammerIpcServer.EnsureRegisteredAndRunning(); }
  248. static void AppShutdown() { g_HammerIpcServer.EnsureStoppedAndUnregistered(); }
  249. public:
  250. virtual BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res );
  251. }
  252. g_HammerIpcServer;
  253. //////////////////////////////////////////////////////////////////////////
  254. //
  255. // Async texture browser interaction
  256. //
  257. //////////////////////////////////////////////////////////////////////////
  258. class CTextureBrowser_Async : public CTextureBrowser
  259. {
  260. public:
  261. CTextureBrowser_Async();
  262. protected:
  263. virtual INT_PTR DoModal();
  264. protected:
  265. virtual void OnResult( INT_PTR nModalResult );
  266. };
  267. //////////////////////////////////////////////////////////////////////////
  268. //
  269. // Tool handler implementation
  270. //
  271. //////////////////////////////////////////////////////////////////////////
  272. void CToolHandler_SyncMesh::AppMainLoopIdle()
  273. {
  274. s_hMainWnd = AfxGetMainWnd()->GetSafeHwnd();
  275. CDialog *pAsyncDlg = NULL;
  276. // Fetch requests
  277. {
  278. AUTO_LOCK( m_mtxRequest );
  279. if ( !m_strReqDmxFileName.IsEmpty() )
  280. {
  281. m_strDmxFileName = m_strReqDmxFileName;
  282. m_strReqDmxFileName.Empty();
  283. m_strReqCookie.Empty();
  284. }
  285. if ( m_pAsyncDialogRequest )
  286. {
  287. pAsyncDlg = m_pAsyncDialogRequest;
  288. }
  289. }
  290. if ( !m_strDmxFileName.IsEmpty() )
  291. {
  292. ProcessDmxRequest();
  293. m_strDmxFileName.Empty();
  294. SwitchActiveWindow( s_hMainWnd );
  295. }
  296. if ( pAsyncDlg )
  297. {
  298. SwitchActiveWindow( s_hMainWnd );
  299. pAsyncDlg->DoModal();
  300. m_pAsyncDialogRequest = NULL;
  301. }
  302. }
  303. BOOL CToolHandler_SyncMesh::UpdateCmdUI( CCmdUI *pCmdUI )
  304. {
  305. s_hMainWnd = AfxGetMainWnd()->GetSafeHwnd();
  306. BOOL bEnabled =
  307. Setup() &&
  308. CanExecute()
  309. ;
  310. pCmdUI->Enable( bEnabled );
  311. return TRUE;
  312. }
  313. BOOL CToolHandler_SyncMesh::Execute( UINT uMsg )
  314. {
  315. // Execute the command
  316. BOOL bSuccess =
  317. Setup() &&
  318. CanExecute();
  319. if ( !bSuccess )
  320. return FALSE;
  321. // Determine if we are processing brushed primitives or model selection
  322. // also remember the selection
  323. m_origSelection = *m_pSelection;
  324. m_pSelection->GetBoundsCenter( m_vecSelectionCenter );
  325. bSuccess =
  326. CopySelToClipboard() &&
  327. CreateTempDoc() &&
  328. PasteSelToTempDoc() &&
  329. CreateTempFileName() &&
  330. ExportTempDoc();
  331. CloseTempDoc();
  332. if ( bSuccess )
  333. {
  334. NotifyMaya();
  335. }
  336. else
  337. {
  338. AfxMessageBox( "Failed to prepare mesh for Maya", MB_ICONSTOP | MB_OK );
  339. }
  340. // Set new tool to pointer
  341. ToolManager()->SetTool( TOOL_POINTER );
  342. return TRUE;
  343. }
  344. //
  345. // Stage by stage implementation
  346. //
  347. BOOL CToolHandler_SyncMesh::Setup()
  348. {
  349. m_pApp = AfxGetApp();
  350. if ( !m_pApp )
  351. return FALSE;
  352. m_pDoc = _AsFriendlyDoc( CMapDoc::GetActiveMapDoc() );
  353. if ( !m_pDoc )
  354. return FALSE;
  355. m_pView = m_pDoc->GetActiveMapView();
  356. if ( !m_pView )
  357. return FALSE;
  358. m_pSelection = m_pDoc->GetSelection();
  359. if ( !m_pSelection )
  360. return FALSE;
  361. if ( m_pSelection->IsEmpty() ||
  362. m_pSelection->GetCount() < 1 )
  363. return FALSE;
  364. m_pTempDoc = NULL;
  365. m_pCopiedObjects = NULL;
  366. m_strTempFileName.Empty();
  367. return TRUE;
  368. }
  369. BOOL CToolHandler_SyncMesh::CanExecute()
  370. {
  371. if ( GetMainWnd()->IsShellSessionActive() )
  372. return FALSE;
  373. if ( ToolManager()->GetActiveToolID() == TOOL_FACEEDIT_MATERIAL )
  374. return FALSE;
  375. // Check selection objects
  376. const CMapObjectList *pList = m_pSelection->GetList();
  377. for ( int k = 0; k < pList->Count(); ++ k )
  378. {
  379. CMapClass *pElem = (CUtlReference< CMapClass >)pList->Element( k );
  380. MAPCLASSTYPE eType = pElem->GetType();
  381. ( void ) eType;
  382. if ( pElem->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
  383. {
  384. if ( !static_cast< CMapEntity * >( pElem )->IsClass( "prop_static" ) )
  385. return FALSE;
  386. else
  387. continue;
  388. }
  389. else if ( pElem->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
  390. continue;
  391. else
  392. return FALSE;
  393. }
  394. return TRUE;
  395. }
  396. BOOL CToolHandler_SyncMesh::CopySelToClipboard()
  397. {
  398. m_pCopiedObjects = IHammerClipboard::CreateInstance();
  399. m_pDoc->Copy( m_pCopiedObjects );
  400. return TRUE;
  401. }
  402. BOOL CToolHandler_SyncMesh::CreateTempDoc()
  403. {
  404. CDocTemplate *pTemplate = NULL;
  405. if ( POSITION pos = m_pApp->GetFirstDocTemplatePosition() )
  406. {
  407. pTemplate = m_pApp->GetNextDocTemplate( pos );
  408. }
  409. if ( !pTemplate )
  410. return FALSE;
  411. // Force a new document file created
  412. m_pTempDoc = _AsFriendlyDoc( pTemplate->OpenDocumentFile( NULL ) );
  413. if ( !m_pTempDoc )
  414. return FALSE;
  415. return TRUE;
  416. }
  417. BOOL CToolHandler_SyncMesh::PasteSelToTempDoc()
  418. {
  419. // Force snapping off in the doc
  420. bool bSnapping = m_pTempDoc->IsSnapEnabled();
  421. if ( bSnapping )
  422. m_pTempDoc->OnMapSnaptogrid();
  423. // Perform the paste
  424. Vector vecPasteOffset( 0, 0, 0 ), vecSelCenter;
  425. m_pSelection->GetBoundsCenter( vecSelCenter );
  426. vecPasteOffset -= vecSelCenter;
  427. if ( !s_opt_maya_bMeshAtOrigin )
  428. vecPasteOffset = Vector( 0, 0, 0 );
  429. m_pTempDoc->Paste( m_pCopiedObjects, m_pTempDoc->GetMapWorld(),
  430. vecPasteOffset, QAngle(0, 0, 0), NULL, false, NULL);
  431. // Restore snapping mode
  432. if ( bSnapping )
  433. m_pTempDoc->OnMapSnaptogrid();
  434. return TRUE;
  435. }
  436. BOOL CToolHandler_SyncMesh::CreateTempFileName()
  437. {
  438. m_strTempFileName = GetSystemTempDir() + "/hammer_geom.vmf";
  439. ATLTRACE( "[SyncMesh] Temp file name is '%s'\n", (LPCTSTR) m_strTempFileName );
  440. return TRUE;
  441. }
  442. BOOL CToolHandler_SyncMesh::ExportTempDoc()
  443. {
  444. extern BOOL bSaveVisiblesOnly;
  445. CAutoPushPop< BOOL > _auto_bSaveVisiblesOnly( bSaveVisiblesOnly, FALSE );
  446. BOOL bSaved = m_pTempDoc->OnSaveDocument( m_strTempFileName );
  447. if ( !bSaved )
  448. return FALSE;
  449. m_strMayaRequest = "hammerBrush";
  450. return TRUE;
  451. }
  452. BOOL CToolHandler_SyncMesh::CloseTempDoc()
  453. {
  454. if ( m_pCopiedObjects )
  455. {
  456. m_pCopiedObjects->Destroy();
  457. m_pCopiedObjects = NULL;
  458. }
  459. if ( m_pTempDoc )
  460. {
  461. m_pTempDoc->SetModifiedFlag( FALSE );
  462. m_pTempDoc->OnFileClose();
  463. }
  464. // Activate the view that used to be active
  465. if ( m_pView )
  466. {
  467. m_pDoc->SetActiveView( m_pView );
  468. }
  469. return TRUE;
  470. }
  471. BOOL CToolHandler_SyncMesh::NotifyMaya()
  472. {
  473. CValveIpcClientUtl ipc( "MAYA_VST_UIHOOK_IPC_SERVER" );
  474. while ( !ipc.Connect() )
  475. {
  476. int iResponse = AfxMessageBox(
  477. "Cannot connect to Maya.\n"
  478. "Make sure you have Maya running and proper plug-ins loaded and try again.",
  479. MB_ICONWARNING | MB_RETRYCANCEL );
  480. if ( iResponse != IDRETRY )
  481. return FALSE;
  482. }
  483. // Update the cookie
  484. m_strCookie.Format( "%d", 1 + atoi( ( LPCTSTR ) m_strCookie ) );
  485. CUtlBuffer cmd;
  486. cmd.PutString( m_strMayaRequest );
  487. cmd.PutString( m_strCookie );
  488. cmd.PutString( m_strTempFileName );
  489. CUtlBuffer res;
  490. res.EnsureCapacity( 2 * MAX_PATH );
  491. if ( !ipc.ExecuteCommand( cmd, res ) )
  492. goto comm_error;
  493. int uCode = res.GetInt();
  494. switch ( uCode )
  495. {
  496. case 0: // Error
  497. {
  498. char chErrorString[ MAX_PATH ] = {0};
  499. sprintf( chErrorString, "Generic Error" );
  500. res.GetString( chErrorString, sizeof( chErrorString ) - 1 );
  501. AfxMessageBox( chErrorString, MB_ICONSTOP );
  502. return FALSE;
  503. }
  504. case 1: // OK
  505. return TRUE;
  506. default:
  507. goto comm_error;
  508. }
  509. comm_error:
  510. AfxMessageBox(
  511. "Cannot communicate with Maya.\n"
  512. "Make sure you have Maya running and proper plug-ins loaded and try again.",
  513. MB_ICONSTOP | MB_OK );
  514. return FALSE;
  515. }
  516. BOOL CToolHandler_SyncMesh::RequestDmxLoad( char const *pszCookie, char const *pszDmxFile )
  517. {
  518. if ( stricmp( pszCookie, m_strCookie ) )
  519. return FALSE;
  520. AUTO_LOCK( m_mtxRequest );
  521. m_strReqDmxFileName = pszDmxFile;
  522. m_strReqCookie = pszCookie;
  523. return TRUE;
  524. }
  525. BOOL CToolHandler_SyncMesh::RequestTexture( char const *pszTextureString )
  526. {
  527. AFX_MANAGE_STATE( AfxGetStaticModuleState() );
  528. if ( ::GetLastActivePopup( s_hMainWnd ) != s_hMainWnd )
  529. return FALSE;
  530. {
  531. AUTO_LOCK( m_mtxRequest );
  532. if ( m_pAsyncDialogRequest )
  533. return FALSE;
  534. CTextureBrowser_Async *pBrowser = new CTextureBrowser_Async;
  535. if ( !pBrowser )
  536. return FALSE;
  537. m_pAsyncDialogRequest = pBrowser;
  538. pBrowser->SetTextureFormat( tfVMT );
  539. if ( pszTextureString )
  540. {
  541. pBrowser->SetInitialTexture( pszTextureString );
  542. }
  543. }
  544. return TRUE;
  545. }
  546. BOOL CToolHandler_SyncMesh::ProcessDmxRequest()
  547. {
  548. CWaitCursor curWait;
  549. CUtlInplaceBuffer bufIndex( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER );
  550. if ( !DmxDeleteOrigObjects() )
  551. goto failed;
  552. if ( !g_pFileSystem->ReadFile( m_strDmxFileName, NULL, bufIndex ) )
  553. goto failed;
  554. while ( char *pszEntry = bufIndex.InplaceGetLinePtr() )
  555. {
  556. if ( !*pszEntry )
  557. continue;
  558. m_strDmxFileName = pszEntry;
  559. m_strDmxFileName += ".dmx";
  560. if ( DmxPrepareModelFiles() )
  561. DmxCreateStaticProp();
  562. }
  563. m_strCookie.Format( "%d", 1 + atoi( ( LPCTSTR ) m_strCookie ) ); // TODO: for now we just bump the cookie and prevent subsequent import
  564. return TRUE;
  565. failed:
  566. AfxMessageBox( "Failed to apply Maya-edited geometry", MB_ICONSTOP | MB_OK );
  567. return TRUE;
  568. }
  569. BOOL CToolHandler_SyncMesh::DmxPrepareModelFiles()
  570. {
  571. if ( !s_opt_vmf_bStoreMdl )
  572. return TRUE;
  573. // Create the temp directory
  574. char const *pszVproject = getenv( "VPROJECT" );
  575. CString sPath;
  576. if ( !PrepareEmptyTempHammerDir( sPath ) )
  577. return FALSE;
  578. // Copy the dmx
  579. CString sDmx = sPath + "/mayamesh.dmx";
  580. if ( !CopyFile( m_strDmxFileName, sDmx, FALSE ) )
  581. return FALSE;
  582. // Create a sample .qc
  583. CString sQc = sPath + "/mayamesh.qc";
  584. if ( FILE *fQc = fopen( sQc, "wt" ) )
  585. {
  586. fprintf( fQc,
  587. " $modelname .hammer.tmp/studiomdl.mdl \n"
  588. " $scale 1.0 \n"
  589. " $body \"Body\" \"mayamesh.dmx\" \n"
  590. " $staticprop \n"
  591. " $upaxis y \n"
  592. " $sequence \"idle\" \"mayamesh\" fps 30 \n"
  593. " $collisionmodel \"mayamesh.dmx\" { $automass $concave }\n"
  594. );
  595. fclose( fQc );
  596. }
  597. else
  598. return FALSE;
  599. // Compile the mdl
  600. CString sExeName;
  601. sExeName.Format( "%s/../bin/studiomdl.exe", pszVproject );
  602. CString sMdlCmdLine;
  603. sMdlCmdLine.Format( "%s/../bin/studiomdl.exe -nop4 -fastbuild \"%s\"",
  604. pszVproject, ( LPCTSTR ) sQc );
  605. STARTUPINFO si;
  606. memset( &si, 0, sizeof( si ) );
  607. si.cb = sizeof( si );
  608. si.dwFlags = STARTF_USESHOWWINDOW;
  609. si.wShowWindow = SW_HIDE;
  610. PROCESS_INFORMATION pi;
  611. memset( &pi, 0, sizeof( pi ) );
  612. // system( sMdlCmdLine );
  613. BOOL bCreatedProcess = CreateProcess(
  614. sExeName.GetBuffer(), sMdlCmdLine.GetBuffer(),
  615. NULL, NULL, FALSE, 0, NULL, NULL,
  616. &si, &pi );
  617. bCreatedProcess;
  618. if ( pi.hThread )
  619. {
  620. CloseHandle( pi.hThread );
  621. pi.hThread = NULL;
  622. }
  623. if ( pi.hProcess )
  624. {
  625. WaitForSingleObject( pi.hProcess, INFINITE );
  626. CloseHandle( pi.hProcess );
  627. pi.hProcess = NULL;
  628. }
  629. // We should have the model now
  630. CString sMdl = sPath + "/studiomdl.mdl";
  631. if ( !IsFileReadable( sMdl ) )
  632. return FALSE;
  633. m_strMdlFileName = sMdl;
  634. return TRUE;
  635. }
  636. BOOL CToolHandler_SyncMesh::DmxDeleteOrigObjects()
  637. {
  638. // Make sure we are still in the same document and same selection
  639. if ( m_pDoc != m_origSelection.GetMapDoc() )
  640. return FALSE;
  641. if ( s_opt_maya_bReplaceSelection )
  642. {
  643. m_pDoc->OnEditDelete();
  644. }
  645. else
  646. {
  647. GetHistory()->MarkUndoPosition( m_pSelection->GetList(), "Delete" );
  648. // Delete objects in selection
  649. const CMapObjectList &lst = *m_origSelection.GetList();
  650. for ( int k = 0; k < lst.Count(); ++ k )
  651. {
  652. CMapClass *pObj = (CUtlReference< CMapClass >)lst.Element( k );
  653. m_pDoc->DeleteObject( pObj );
  654. }
  655. m_pSelection->RemoveAll();
  656. m_pDoc->SetModifiedFlag();
  657. }
  658. return TRUE;
  659. }
  660. BOOL CToolHandler_SyncMesh::DmxCreateStaticProp()
  661. {
  662. // Make sure we are still in the same document and same selection
  663. if ( m_pDoc != m_origSelection.GetMapDoc() )
  664. return FALSE;
  665. // Compute model hash
  666. int iMdlHash = ComputeFileHash( m_strDmxFileName );
  667. if ( !iMdlHash )
  668. return FALSE;
  669. // Crack the DMX file coordinates
  670. float vecDmxOffset[3] = {0};
  671. if ( m_strDmxFileName.GetLength() > 37 &&
  672. m_strDmxFileName[ m_strDmxFileName.GetLength() - 37 ] == '@' )
  673. {
  674. int arrV[4] = {0};
  675. for ( int k = 0; k < 4; ++ k )
  676. {
  677. CString sParse = m_strDmxFileName.Mid( m_strDmxFileName.GetLength() - 37 + 1 + 8 * k, 8 );
  678. arrV[k] = strtoul( sParse, NULL, 16 );
  679. }
  680. for ( int k = 1; k < 4; ++ k )
  681. {
  682. vecDmxOffset[ k - 1 ] = reinterpret_cast< float & >( arrV[ k ] );
  683. }
  684. }
  685. // Move the model to the mdl cache section
  686. CString sMdlPath;
  687. if ( !GetMdlCacheDir( sMdlPath ) )
  688. return FALSE;
  689. CString sMdlCacheFile;
  690. sMdlCacheFile.Format( "%s/%08X.mdl", ( LPCTSTR ) sMdlPath, iMdlHash );
  691. CString sMdlCacheFileRel = sMdlCacheFile.Mid( strlen( getenv( "VPROJECT" ) ) + 1 );
  692. if ( s_opt_vmf_bStoreMdl )
  693. {
  694. char const * arrFiles[] = { ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
  695. bool arrRequired[] = { true, true, true, false, false };
  696. for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
  697. {
  698. CString sSrc = GetOtherFileName( m_strMdlFileName, arrFiles[ j ], 4 );
  699. CString sDest = GetOtherFileName( sMdlCacheFile, arrFiles[j], 4 );
  700. CopyFile( sSrc, sDest, FALSE );
  701. if ( !IsFileReadable( sDest ) && arrRequired[ j ] )
  702. return FALSE;
  703. }
  704. // Patch the MDL file
  705. if ( FILE *fp = fopen( sMdlCacheFile, "r+b" ) )
  706. {
  707. fseek( fp, 12, SEEK_SET );
  708. fprintf( fp, "%s%c%c", ( (LPCTSTR) sMdlCacheFileRel ) + strlen( "models/" ), 0, 0 );
  709. fclose( fp );
  710. }
  711. else
  712. return FALSE;
  713. }
  714. if ( s_opt_vmf_bStoreDmx )
  715. {
  716. // Copy the dmx file as well
  717. CString sDmxCacheFile;
  718. sDmxCacheFile.Format( "%s/%08X.dmx", ( LPCTSTR ) sMdlPath, iMdlHash );
  719. CopyFile( m_strDmxFileName, sDmxCacheFile, FALSE );
  720. if ( !IsFileReadable( sDmxCacheFile ) )
  721. return FALSE;
  722. }
  723. if ( s_opt_vmf_bStoreMa )
  724. {
  725. // Copy the maya file as well
  726. CString sMayaCacheFile;
  727. sMayaCacheFile.Format( "%s/%08X.ma", ( LPCTSTR ) sMdlPath, iMdlHash );
  728. CopyFile( GetOtherFileName( m_strDmxFileName, ".ma", 4 ), sMayaCacheFile, FALSE );
  729. if ( !IsFileReadable( sMayaCacheFile ) )
  730. return FALSE;
  731. }
  732. // Create the static prop
  733. Vector vecEntityPos( vecDmxOffset[0], vecDmxOffset[1], vecDmxOffset[2] );
  734. if ( s_opt_maya_bMeshAtOrigin )
  735. vecEntityPos += m_vecSelectionCenter;
  736. CMapEntity *pEnt = m_pDoc->CreateEntity( "prop_static",
  737. vecEntityPos.x, vecEntityPos.y, vecEntityPos.z );
  738. pEnt->SetKeyValue( "model", sMdlCacheFileRel ); // TODO: proper DMX/MDL encoding
  739. return TRUE;
  740. }
  741. //////////////////////////////////////////////////////////////////////////
  742. //
  743. // Async texture browser implementation
  744. //
  745. //////////////////////////////////////////////////////////////////////////
  746. CTextureBrowser_Async::CTextureBrowser_Async() :
  747. CTextureBrowser( AfxGetMainWnd() )
  748. {
  749. NULL;
  750. }
  751. INT_PTR CTextureBrowser_Async::DoModal()
  752. {
  753. INT_PTR nResult = CTextureBrowser::DoModal();
  754. OnResult( nResult );
  755. delete this;
  756. return nResult;
  757. }
  758. void CTextureBrowser_Async::OnResult( INT_PTR nModalResult )
  759. {
  760. CValveIpcClientUtl ipc( "MAYA_VST_UIHOOK_IPC_SERVER" );
  761. if ( !ipc.Connect() )
  762. return;
  763. CUtlBuffer cmd;
  764. cmd.PutString( "textureSelected" );
  765. if ( nModalResult == IDOK )
  766. {
  767. cmd.PutString( m_cTextureWindow.szCurTexture );
  768. }
  769. else
  770. {
  771. cmd.PutString( "" );
  772. }
  773. CUtlBuffer res;
  774. res.EnsureCapacity( 2 * MAX_PATH );
  775. ipc.ExecuteCommand( cmd, res );
  776. }
  777. //////////////////////////////////////////////////////////////////////////
  778. //
  779. // Handling commands from Maya
  780. //
  781. //////////////////////////////////////////////////////////////////////////
  782. BOOL CHammerIpcServer::ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res )
  783. {
  784. char szCmd[ MAX_PATH ] = {0};
  785. cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
  786. if ( !stricmp( szCmd, "mayaDmx" ) )
  787. {
  788. cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
  789. CString sCookie = szCmd;
  790. cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
  791. char const *szDmxName = szCmd;
  792. if ( !g_ToolHandlerSyncMesh.RequestDmxLoad( sCookie, szDmxName ) )
  793. {
  794. res.PutInt( 0 );
  795. res.PutString( "Invalid mesh synchronization request!" );
  796. return TRUE;
  797. }
  798. res.PutInt( 1 );
  799. return TRUE;
  800. }
  801. if ( !stricmp( szCmd, "textureBrowse" ) )
  802. {
  803. cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
  804. char const *szTextureName = szCmd;
  805. if ( !g_ToolHandlerSyncMesh.RequestTexture( szTextureName ) )
  806. {
  807. res.PutInt( 0 );
  808. res.PutString( "Cannot request texture!" );
  809. return TRUE;
  810. }
  811. res.PutInt( 1 );
  812. return TRUE;
  813. }
  814. return FALSE;
  815. }
  816. //////////////////////////////////////////////////////////////////////////
  817. //
  818. // Special implementation of custom load/save chunks for entities
  819. //
  820. //////////////////////////////////////////////////////////////////////////
  821. class CSyncMesh_SaveLoadHandler : public CVmfMeshDataSupport_SaveLoadHandler
  822. {
  823. public:
  824. CSyncMesh_SaveLoadHandler();
  825. ~CSyncMesh_SaveLoadHandler();
  826. public:
  827. virtual char const *GetCustomSectionName() { return "meshdata"; }
  828. public:
  829. virtual ChunkFileResult_t SaveVMF( CChunkFile *pFile, IMapEntity_SaveInfo_t *pSaveInfo );
  830. protected:
  831. virtual ChunkFileResult_t OnFileDataLoaded( CUtlBuffer &bufData );
  832. virtual ChunkFileResult_t OnFileDataWriting( CChunkFile *pFile, char const *szHash );
  833. };
  834. static CSyncMesh_SaveLoadHandler g_syncmesh_saveloadhandler;
  835. CSyncMesh_SaveLoadHandler::CSyncMesh_SaveLoadHandler()
  836. {
  837. VmfInstallMapEntitySaveLoadHandler( this );
  838. }
  839. CSyncMesh_SaveLoadHandler::~CSyncMesh_SaveLoadHandler()
  840. {
  841. NULL;
  842. }
  843. ChunkFileResult_t CSyncMesh_SaveLoadHandler::SaveVMF( CChunkFile *pFile, IMapEntity_SaveInfo_t *pSaveInfo )
  844. {
  845. if ( !m_pEntity->IsClass( "prop_static" ) )
  846. return ChunkFile_Ok;
  847. LPCTSTR szModelName = m_pEntity->GetKeyValue( "model" );
  848. if ( !szModelName || !*szModelName )
  849. return ChunkFile_Ok;
  850. CString sMdlPath;
  851. if ( !GetMdlCacheDir( sMdlPath ) )
  852. return ChunkFile_Ok;
  853. CString sMdlRelPath = sMdlPath.Mid( strlen( getenv( "VPROJECT" ) ) + 1 ) + "/";
  854. if ( !StringHasPrefix( szModelName, sMdlRelPath ) )
  855. return ChunkFile_Ok;
  856. // Model is under our special cache path
  857. char szModelHash[ 16 ] = {0};
  858. sprintf( szModelHash, "%.8s", szModelName + sMdlRelPath.GetLength() );
  859. return WriteDataChunk( pFile, szModelHash );
  860. }
  861. ChunkFileResult_t CSyncMesh_SaveLoadHandler::OnFileDataWriting( CChunkFile *pFile, char const *szHash )
  862. {
  863. ChunkFileResult_t eResult;
  864. LPCTSTR szModelName = m_pEntity->GetKeyValue( "model" );
  865. CString sMdlPath;
  866. GetMdlCacheDir( sMdlPath );
  867. CString sMdlRelPath = sMdlPath.Mid( strlen( getenv( "VPROJECT" ) ) + 1 ) + "/";
  868. char szModelHash[ 16 ] = {0};
  869. sprintf( szModelHash, "%.8s", szModelName + sMdlRelPath.GetLength() );
  870. // Write files
  871. char const * arrFiles[] = { ".ma", ".dmx", ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
  872. char const * arrNames[] = { "maa", "dmx", "mdl", "vvd", "vtx", "phy", "ss2" };
  873. bool bOptNames[] = { s_opt_vmf_bStoreMa, s_opt_vmf_bStoreDmx, s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl,
  874. s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl };
  875. bool bOptRequired[] = { true, true, true, true, true, false };
  876. for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
  877. {
  878. if ( !bOptNames[j] )
  879. continue;
  880. CString sSrc = sMdlPath + CString( "/" ) + CString( szModelHash ) + arrFiles[j];
  881. CUtlBuffer bufFile;
  882. if ( !g_pFileSystem->ReadFile( sSrc, NULL, bufFile ) )
  883. {
  884. if ( !bOptRequired[ j ] )
  885. continue;
  886. else
  887. return ChunkFile_OpenFail;
  888. }
  889. eResult = WriteBufferData( pFile, bufFile, arrNames[j] );
  890. if ( eResult != ChunkFile_Ok )
  891. return eResult;
  892. }
  893. return ChunkFile_Ok;
  894. }
  895. ChunkFileResult_t CSyncMesh_SaveLoadHandler::OnFileDataLoaded( CUtlBuffer &bufData )
  896. {
  897. char const * arrFiles[] = { ".ma", ".dmx", ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
  898. char const * arrNames[] = { "maa", "dmx", "mdl", "vvd", "vtx", "phy", "ss2" };
  899. char const *pFileExt = NULL;
  900. // Determine the file name to save
  901. for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
  902. {
  903. if ( !stricmp( m_hLoadHeader.sPrefix, arrNames[j] ) )
  904. {
  905. pFileExt = arrFiles[j];
  906. break;
  907. }
  908. }
  909. if ( !pFileExt )
  910. return ChunkFile_Fail;
  911. // The filename
  912. CString sSaveFileName;
  913. if ( !GetMdlCacheDir( sSaveFileName ) )
  914. return ChunkFile_Fail;
  915. sSaveFileName += "/";
  916. sSaveFileName += m_hLoadHeader.sHash;
  917. sSaveFileName += pFileExt;
  918. // We have file data, save it
  919. if ( FILE *f = fopen( sSaveFileName, "wb" ) )
  920. {
  921. fwrite( bufData.Base(), 1, bufData.TellPut(), f );
  922. fclose( f );
  923. }
  924. else
  925. return ChunkFile_Fail;
  926. return ChunkFile_Ok;
  927. }