1162 lines
26 KiB
C++
1162 lines
26 KiB
C++
//====== Copyright c 1996-2007, Valve Corporation, All rights reserved. =======//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//=============================================================================//
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "cmdsyncmesh.h"
|
|
#include "cmdhandlers.h"
|
|
|
|
#include <io.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <direct.h>
|
|
|
|
#include <atlenc.h>
|
|
|
|
#include "hammer.h"
|
|
#include "Box3D.h" // For units
|
|
#include "History.h"
|
|
#include "MainFrm.h"
|
|
#include "GlobalFunctions.h"
|
|
#include "MapDoc.h"
|
|
#include "MapView.h"
|
|
|
|
#include "ToolManager.h"
|
|
|
|
#include "smartptr.h"
|
|
|
|
#include "utlhash.h"
|
|
#include "generichash.h"
|
|
#include "utlbuffer.h"
|
|
#include "valve_ipc_win32.h"
|
|
|
|
#include "threadtools.h"
|
|
|
|
#include "mapsolid.h"
|
|
#include "mapentity.h"
|
|
|
|
#include "chunkfile.h"
|
|
|
|
#include "texturebrowser.h"
|
|
|
|
#include "vmfentitysupport.h"
|
|
#include "vmfmeshdatasupport.h"
|
|
|
|
|
|
//
|
|
// Fwd declarations
|
|
//
|
|
|
|
class CToolHandler_SyncMesh;
|
|
|
|
|
|
//
|
|
// Friendly structures declarations
|
|
//
|
|
|
|
class CMapDoc_Friendly : public CMapDoc
|
|
{
|
|
friend class CToolHandler_SyncMesh;
|
|
};
|
|
static CMapDoc_Friendly * _AsFriendlyDoc( CDocument *pDoc )
|
|
{
|
|
if ( pDoc )
|
|
{
|
|
ASSERT_KINDOF( CMapDoc, pDoc );
|
|
}
|
|
return static_cast< CMapDoc_Friendly * >( pDoc );
|
|
}
|
|
|
|
//
|
|
// Settings
|
|
//
|
|
|
|
static bool s_opt_vmf_bStoreMdl = true;
|
|
static bool s_opt_vmf_bStoreDmx = true;
|
|
static bool s_opt_vmf_bStoreMa = true;
|
|
|
|
static bool s_opt_maya_bMeshAtOrigin = false;
|
|
static bool s_opt_maya_bReplaceSelection = true;
|
|
|
|
static HWND s_hMainWnd = NULL;
|
|
|
|
//
|
|
// Utility routines
|
|
//
|
|
|
|
BOOL PrepareEmptyTempHammerDir( CString &sPath )
|
|
{
|
|
// Create the temp directory
|
|
char const *pszVproject = getenv( "VPROJECT" );
|
|
if ( !pszVproject )
|
|
return FALSE;
|
|
|
|
sPath = pszVproject;
|
|
sPath.Replace( '\\', '/' );
|
|
sPath.Append( "/models/.hammer.tmp" );
|
|
|
|
// Remove the folder
|
|
if ( !access( sPath, 00) )
|
|
{
|
|
SHFILEOPSTRUCT sfo;
|
|
memset( &sfo, 0, sizeof( sfo ) );
|
|
sfo.hwnd = AfxGetMainWnd()->GetSafeHwnd();
|
|
sfo.wFunc = FO_DELETE;
|
|
sfo.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
|
|
CArrayAutoPtr< char > bufPathCopy( new char [ sPath.GetLength() + 10 ] );
|
|
sprintf( bufPathCopy.Get(), "%s/*.*%c%c", sPath, 0, 0 );
|
|
Q_FixSlashes( bufPathCopy.Get() );
|
|
sfo.pFrom = bufPathCopy.Get();
|
|
if ( SHFileOperation( &sfo ) )
|
|
return FALSE;
|
|
}
|
|
|
|
// Create it empty
|
|
BOOL bDir = !mkdir( sPath ) || ( errno == EEXIST );
|
|
if ( !bDir )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL GetMdlCacheDir( CString &sPath )
|
|
{
|
|
// Create the temp directory
|
|
char const *pszVproject = getenv( "VPROJECT" );
|
|
if ( !pszVproject )
|
|
return FALSE;
|
|
|
|
sPath = pszVproject;
|
|
sPath.Replace( '\\', '/' );
|
|
sPath.Append( "/models/.hammer.mdlcache" );
|
|
|
|
// Create it empty
|
|
BOOL bDir = !mkdir( sPath ) || ( errno == EEXIST );
|
|
if ( !bDir )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
CString GetSystemTempDir()
|
|
{
|
|
CString sPath;
|
|
|
|
if ( char const *pszTemp = getenv("TEMP") )
|
|
{
|
|
sPath = pszTemp;
|
|
}
|
|
else if ( char const *pszTmp = getenv("TMP") )
|
|
{
|
|
sPath = pszTmp;
|
|
}
|
|
else
|
|
{
|
|
sPath = "c:";
|
|
}
|
|
|
|
sPath.Replace( '\\', '/' );
|
|
return sPath;
|
|
}
|
|
|
|
int ComputeBufferHash( const void *lpvBuffer, size_t numBytes )
|
|
{
|
|
return HashBlock( lpvBuffer, numBytes );
|
|
}
|
|
|
|
int ComputeFileHash( const char *pszFilename )
|
|
{
|
|
CUtlBuffer bufFile;
|
|
|
|
if ( g_pFileSystem->ReadFile( pszFilename, NULL, bufFile ) )
|
|
return ComputeBufferHash( bufFile.Base(), bufFile.TellPut() );
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL IsFileReadable( const char *pszFilename )
|
|
{
|
|
return 0 == access( pszFilename, 04 );
|
|
}
|
|
|
|
CString GetOtherFileName( CString &sFile, char const *szNewExt, int nCurExtLen )
|
|
{
|
|
return sFile.Left( sFile.GetLength() - nCurExtLen ) + szNewExt;
|
|
}
|
|
|
|
void SwitchActiveWindow( HWND hWndActivate )
|
|
{
|
|
HWND hWndFg = GetForegroundWindow();
|
|
if ( hWndActivate == hWndFg )
|
|
return;
|
|
|
|
DWORD dwThreadIdFg, dwThreadIdActivate;
|
|
dwThreadIdFg = GetWindowThreadProcessId( hWndFg, NULL );
|
|
dwThreadIdActivate = GetWindowThreadProcessId( hWndActivate, NULL );
|
|
|
|
if ( dwThreadIdActivate != dwThreadIdFg )
|
|
AttachThreadInput( dwThreadIdFg, dwThreadIdActivate, TRUE );
|
|
|
|
SetForegroundWindow( hWndActivate );
|
|
BringWindowToTop( hWndActivate );
|
|
|
|
if ( dwThreadIdActivate != dwThreadIdFg )
|
|
AttachThreadInput( dwThreadIdFg, dwThreadIdActivate, FALSE );
|
|
|
|
if ( IsIconic( hWndActivate ) )
|
|
ShowWindow( hWndActivate, SW_RESTORE );
|
|
else
|
|
ShowWindow( hWndActivate, SW_SHOW );
|
|
}
|
|
|
|
//
|
|
// Command handler classes
|
|
//
|
|
|
|
class CToolHandler_SyncMesh : public IToolHandlerInfo
|
|
{
|
|
public:
|
|
CToolHandler_SyncMesh();
|
|
|
|
public:
|
|
virtual BOOL UpdateCmdUI( CCmdUI *pCmdUI );
|
|
virtual BOOL Execute( UINT uMsg );
|
|
|
|
public:
|
|
BOOL RequestDmxLoad( char const *pszCookie, char const *pszDmxFile );
|
|
BOOL RequestTexture( char const *pszTextureString );
|
|
void AppMainLoopIdle();
|
|
|
|
protected:
|
|
BOOL Setup();
|
|
BOOL CanExecute();
|
|
|
|
protected:
|
|
BOOL CopySelToClipboard();
|
|
BOOL CreateTempDoc();
|
|
BOOL PasteSelToTempDoc();
|
|
BOOL CreateTempFileName();
|
|
BOOL ExportTempDoc();
|
|
BOOL CloseTempDoc();
|
|
BOOL NotifyMaya();
|
|
|
|
protected:
|
|
BOOL ProcessDmxRequest();
|
|
BOOL DmxPrepareModelFiles();
|
|
BOOL DmxDeleteOrigObjects();
|
|
BOOL DmxCreateStaticProp();
|
|
|
|
protected:
|
|
CWinApp *m_pApp;
|
|
CMapDoc_Friendly *m_pDoc;
|
|
CMapView *m_pView;
|
|
CSelection *m_pSelection;
|
|
|
|
CSelection m_origSelection;
|
|
Vector m_vecSelectionCenter;
|
|
|
|
IHammerClipboard *m_pCopiedObjects;
|
|
CMapDoc_Friendly *m_pTempDoc;
|
|
CString m_strTempFileName;
|
|
|
|
CString m_strMayaRequest;
|
|
CString m_strCookie;
|
|
|
|
CString m_strReqCookie;
|
|
CString m_strReqDmxFileName;
|
|
CDialog *m_pAsyncDialogRequest;
|
|
CThreadFastMutex m_mtxRequest;
|
|
|
|
CString m_strDmxFileName;
|
|
CString m_strMdlFileName;
|
|
}
|
|
g_ToolHandlerSyncMesh;
|
|
|
|
IToolHandlerInfo *g_pToolHandlerSyncMesh = &g_ToolHandlerSyncMesh;
|
|
|
|
static void AppMainLoopIdle_Delegate()
|
|
{
|
|
g_ToolHandlerSyncMesh.AppMainLoopIdle();
|
|
}
|
|
|
|
CToolHandler_SyncMesh::CToolHandler_SyncMesh()
|
|
{
|
|
m_pApp = NULL;
|
|
m_pDoc = NULL;
|
|
m_pView = NULL;
|
|
m_pSelection = NULL;
|
|
|
|
m_pCopiedObjects = NULL;
|
|
m_pTempDoc = NULL;
|
|
|
|
m_pAsyncDialogRequest = NULL;
|
|
|
|
AppRegisterMessageLoopFn( AppMainLoopIdle_Delegate );
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IPC Server class
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class CHammerIpcServer : public CValveIpcServerUtl
|
|
{
|
|
public:
|
|
CHammerIpcServer() : CValveIpcServerUtl( "HAMMER_IPC_SERVER" )
|
|
{
|
|
AppRegisterPostInitFn( AppInit );
|
|
AppRegisterPreShutdownFn( AppShutdown );
|
|
}
|
|
|
|
static void AppInit() { g_HammerIpcServer.EnsureRegisteredAndRunning(); }
|
|
static void AppShutdown() { g_HammerIpcServer.EnsureStoppedAndUnregistered(); }
|
|
|
|
public:
|
|
virtual BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res );
|
|
}
|
|
g_HammerIpcServer;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Async texture browser interaction
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class CTextureBrowser_Async : public CTextureBrowser
|
|
{
|
|
public:
|
|
CTextureBrowser_Async();
|
|
|
|
protected:
|
|
virtual INT_PTR DoModal();
|
|
|
|
protected:
|
|
virtual void OnResult( INT_PTR nModalResult );
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Tool handler implementation
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void CToolHandler_SyncMesh::AppMainLoopIdle()
|
|
{
|
|
s_hMainWnd = AfxGetMainWnd()->GetSafeHwnd();
|
|
|
|
CDialog *pAsyncDlg = NULL;
|
|
// Fetch requests
|
|
{
|
|
AUTO_LOCK( m_mtxRequest );
|
|
|
|
if ( !m_strReqDmxFileName.IsEmpty() )
|
|
{
|
|
m_strDmxFileName = m_strReqDmxFileName;
|
|
m_strReqDmxFileName.Empty();
|
|
m_strReqCookie.Empty();
|
|
}
|
|
|
|
if ( m_pAsyncDialogRequest )
|
|
{
|
|
pAsyncDlg = m_pAsyncDialogRequest;
|
|
}
|
|
}
|
|
|
|
if ( !m_strDmxFileName.IsEmpty() )
|
|
{
|
|
ProcessDmxRequest();
|
|
m_strDmxFileName.Empty();
|
|
SwitchActiveWindow( s_hMainWnd );
|
|
}
|
|
|
|
if ( pAsyncDlg )
|
|
{
|
|
SwitchActiveWindow( s_hMainWnd );
|
|
pAsyncDlg->DoModal();
|
|
m_pAsyncDialogRequest = NULL;
|
|
}
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::UpdateCmdUI( CCmdUI *pCmdUI )
|
|
{
|
|
s_hMainWnd = AfxGetMainWnd()->GetSafeHwnd();
|
|
|
|
BOOL bEnabled =
|
|
Setup() &&
|
|
CanExecute()
|
|
;
|
|
pCmdUI->Enable( bEnabled );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::Execute( UINT uMsg )
|
|
{
|
|
// Execute the command
|
|
BOOL bSuccess =
|
|
Setup() &&
|
|
CanExecute();
|
|
if ( !bSuccess )
|
|
return FALSE;
|
|
|
|
// Determine if we are processing brushed primitives or model selection
|
|
// also remember the selection
|
|
m_origSelection = *m_pSelection;
|
|
m_pSelection->GetBoundsCenter( m_vecSelectionCenter );
|
|
|
|
bSuccess =
|
|
CopySelToClipboard() &&
|
|
CreateTempDoc() &&
|
|
PasteSelToTempDoc() &&
|
|
CreateTempFileName() &&
|
|
ExportTempDoc();
|
|
|
|
CloseTempDoc();
|
|
|
|
if ( bSuccess )
|
|
{
|
|
NotifyMaya();
|
|
}
|
|
else
|
|
{
|
|
AfxMessageBox( "Failed to prepare mesh for Maya", MB_ICONSTOP | MB_OK );
|
|
}
|
|
|
|
// Set new tool to pointer
|
|
ToolManager()->SetTool( TOOL_POINTER );
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Stage by stage implementation
|
|
//
|
|
|
|
BOOL CToolHandler_SyncMesh::Setup()
|
|
{
|
|
m_pApp = AfxGetApp();
|
|
if ( !m_pApp )
|
|
return FALSE;
|
|
|
|
m_pDoc = _AsFriendlyDoc( CMapDoc::GetActiveMapDoc() );
|
|
if ( !m_pDoc )
|
|
return FALSE;
|
|
|
|
m_pView = m_pDoc->GetActiveMapView();
|
|
if ( !m_pView )
|
|
return FALSE;
|
|
|
|
m_pSelection = m_pDoc->GetSelection();
|
|
if ( !m_pSelection )
|
|
return FALSE;
|
|
|
|
if ( m_pSelection->IsEmpty() ||
|
|
m_pSelection->GetCount() < 1 )
|
|
return FALSE;
|
|
|
|
m_pTempDoc = NULL;
|
|
m_pCopiedObjects = NULL;
|
|
m_strTempFileName.Empty();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::CanExecute()
|
|
{
|
|
if ( GetMainWnd()->IsShellSessionActive() )
|
|
return FALSE;
|
|
|
|
if ( ToolManager()->GetActiveToolID() == TOOL_FACEEDIT_MATERIAL )
|
|
return FALSE;
|
|
|
|
// Check selection objects
|
|
const CMapObjectList *pList = m_pSelection->GetList();
|
|
for ( int k = 0; k < pList->Count(); ++ k )
|
|
{
|
|
CMapClass *pElem = (CUtlReference< CMapClass >)pList->Element( k );
|
|
|
|
MAPCLASSTYPE eType = pElem->GetType();
|
|
( void ) eType;
|
|
|
|
if ( pElem->IsMapClass( MAPCLASS_TYPE( CMapEntity ) ) )
|
|
{
|
|
if ( !static_cast< CMapEntity * >( pElem )->IsClass( "prop_static" ) )
|
|
return FALSE;
|
|
else
|
|
continue;
|
|
}
|
|
else if ( pElem->IsMapClass( MAPCLASS_TYPE( CMapSolid ) ) )
|
|
continue;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::CopySelToClipboard()
|
|
{
|
|
m_pCopiedObjects = IHammerClipboard::CreateInstance();
|
|
m_pDoc->Copy( m_pCopiedObjects );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::CreateTempDoc()
|
|
{
|
|
CDocTemplate *pTemplate = NULL;
|
|
if ( POSITION pos = m_pApp->GetFirstDocTemplatePosition() )
|
|
{
|
|
pTemplate = m_pApp->GetNextDocTemplate( pos );
|
|
}
|
|
|
|
if ( !pTemplate )
|
|
return FALSE;
|
|
|
|
// Force a new document file created
|
|
m_pTempDoc = _AsFriendlyDoc( pTemplate->OpenDocumentFile( NULL ) );
|
|
if ( !m_pTempDoc )
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::PasteSelToTempDoc()
|
|
{
|
|
// Force snapping off in the doc
|
|
bool bSnapping = m_pTempDoc->IsSnapEnabled();
|
|
if ( bSnapping )
|
|
m_pTempDoc->OnMapSnaptogrid();
|
|
|
|
// Perform the paste
|
|
Vector vecPasteOffset( 0, 0, 0 ), vecSelCenter;
|
|
m_pSelection->GetBoundsCenter( vecSelCenter );
|
|
vecPasteOffset -= vecSelCenter;
|
|
|
|
if ( !s_opt_maya_bMeshAtOrigin )
|
|
vecPasteOffset = Vector( 0, 0, 0 );
|
|
|
|
m_pTempDoc->Paste( m_pCopiedObjects, m_pTempDoc->GetMapWorld(),
|
|
vecPasteOffset, QAngle(0, 0, 0), NULL, false, NULL);
|
|
|
|
// Restore snapping mode
|
|
if ( bSnapping )
|
|
m_pTempDoc->OnMapSnaptogrid();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::CreateTempFileName()
|
|
{
|
|
m_strTempFileName = GetSystemTempDir() + "/hammer_geom.vmf";
|
|
|
|
ATLTRACE( "[SyncMesh] Temp file name is '%s'\n", (LPCTSTR) m_strTempFileName );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::ExportTempDoc()
|
|
{
|
|
extern BOOL bSaveVisiblesOnly;
|
|
CAutoPushPop< BOOL > _auto_bSaveVisiblesOnly( bSaveVisiblesOnly, FALSE );
|
|
|
|
BOOL bSaved = m_pTempDoc->OnSaveDocument( m_strTempFileName );
|
|
if ( !bSaved )
|
|
return FALSE;
|
|
|
|
m_strMayaRequest = "hammerBrush";
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::CloseTempDoc()
|
|
{
|
|
if ( m_pCopiedObjects )
|
|
{
|
|
m_pCopiedObjects->Destroy();
|
|
m_pCopiedObjects = NULL;
|
|
}
|
|
|
|
if ( m_pTempDoc )
|
|
{
|
|
m_pTempDoc->SetModifiedFlag( FALSE );
|
|
m_pTempDoc->OnFileClose();
|
|
}
|
|
|
|
// Activate the view that used to be active
|
|
if ( m_pView )
|
|
{
|
|
m_pDoc->SetActiveView( m_pView );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::NotifyMaya()
|
|
{
|
|
CValveIpcClientUtl ipc( "MAYA_VST_UIHOOK_IPC_SERVER" );
|
|
while ( !ipc.Connect() )
|
|
{
|
|
int iResponse = AfxMessageBox(
|
|
"Cannot connect to Maya.\n"
|
|
"Make sure you have Maya running and proper plug-ins loaded and try again.",
|
|
MB_ICONWARNING | MB_RETRYCANCEL );
|
|
if ( iResponse != IDRETRY )
|
|
return FALSE;
|
|
}
|
|
|
|
// Update the cookie
|
|
m_strCookie.Format( "%d", 1 + atoi( ( LPCTSTR ) m_strCookie ) );
|
|
|
|
CUtlBuffer cmd;
|
|
|
|
cmd.PutString( m_strMayaRequest );
|
|
cmd.PutString( m_strCookie );
|
|
cmd.PutString( m_strTempFileName );
|
|
|
|
CUtlBuffer res;
|
|
res.EnsureCapacity( 2 * MAX_PATH );
|
|
|
|
if ( !ipc.ExecuteCommand( cmd, res ) )
|
|
goto comm_error;
|
|
|
|
int uCode = res.GetInt();
|
|
switch ( uCode )
|
|
{
|
|
case 0: // Error
|
|
{
|
|
char chErrorString[ MAX_PATH ] = {0};
|
|
sprintf( chErrorString, "Generic Error" );
|
|
res.GetString( chErrorString, sizeof( chErrorString ) - 1 );
|
|
AfxMessageBox( chErrorString, MB_ICONSTOP );
|
|
return FALSE;
|
|
}
|
|
|
|
case 1: // OK
|
|
return TRUE;
|
|
|
|
default:
|
|
goto comm_error;
|
|
}
|
|
|
|
comm_error:
|
|
AfxMessageBox(
|
|
"Cannot communicate with Maya.\n"
|
|
"Make sure you have Maya running and proper plug-ins loaded and try again.",
|
|
MB_ICONSTOP | MB_OK );
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::RequestDmxLoad( char const *pszCookie, char const *pszDmxFile )
|
|
{
|
|
if ( stricmp( pszCookie, m_strCookie ) )
|
|
return FALSE;
|
|
|
|
AUTO_LOCK( m_mtxRequest );
|
|
m_strReqDmxFileName = pszDmxFile;
|
|
m_strReqCookie = pszCookie;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::RequestTexture( char const *pszTextureString )
|
|
{
|
|
AFX_MANAGE_STATE( AfxGetStaticModuleState() );
|
|
|
|
if ( ::GetLastActivePopup( s_hMainWnd ) != s_hMainWnd )
|
|
return FALSE;
|
|
|
|
{
|
|
AUTO_LOCK( m_mtxRequest );
|
|
if ( m_pAsyncDialogRequest )
|
|
return FALSE;
|
|
|
|
CTextureBrowser_Async *pBrowser = new CTextureBrowser_Async;
|
|
if ( !pBrowser )
|
|
return FALSE;
|
|
m_pAsyncDialogRequest = pBrowser;
|
|
|
|
pBrowser->SetTextureFormat( tfVMT );
|
|
if ( pszTextureString )
|
|
{
|
|
pBrowser->SetInitialTexture( pszTextureString );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::ProcessDmxRequest()
|
|
{
|
|
CWaitCursor curWait;
|
|
CUtlInplaceBuffer bufIndex( 0, 0, CUtlInplaceBuffer::TEXT_BUFFER );
|
|
|
|
if ( !DmxDeleteOrigObjects() )
|
|
goto failed;
|
|
|
|
if ( !g_pFileSystem->ReadFile( m_strDmxFileName, NULL, bufIndex ) )
|
|
goto failed;
|
|
|
|
while ( char *pszEntry = bufIndex.InplaceGetLinePtr() )
|
|
{
|
|
if ( !*pszEntry )
|
|
continue;
|
|
|
|
m_strDmxFileName = pszEntry;
|
|
m_strDmxFileName += ".dmx";
|
|
|
|
if ( DmxPrepareModelFiles() )
|
|
DmxCreateStaticProp();
|
|
|
|
}
|
|
|
|
m_strCookie.Format( "%d", 1 + atoi( ( LPCTSTR ) m_strCookie ) ); // TODO: for now we just bump the cookie and prevent subsequent import
|
|
return TRUE;
|
|
|
|
failed:
|
|
AfxMessageBox( "Failed to apply Maya-edited geometry", MB_ICONSTOP | MB_OK );
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::DmxPrepareModelFiles()
|
|
{
|
|
if ( !s_opt_vmf_bStoreMdl )
|
|
return TRUE;
|
|
|
|
// Create the temp directory
|
|
char const *pszVproject = getenv( "VPROJECT" );
|
|
CString sPath;
|
|
if ( !PrepareEmptyTempHammerDir( sPath ) )
|
|
return FALSE;
|
|
|
|
// Copy the dmx
|
|
CString sDmx = sPath + "/mayamesh.dmx";
|
|
if ( !CopyFile( m_strDmxFileName, sDmx, FALSE ) )
|
|
return FALSE;
|
|
|
|
// Create a sample .qc
|
|
CString sQc = sPath + "/mayamesh.qc";
|
|
if ( FILE *fQc = fopen( sQc, "wt" ) )
|
|
{
|
|
fprintf( fQc,
|
|
" $modelname .hammer.tmp/studiomdl.mdl \n"
|
|
" $scale 1.0 \n"
|
|
" $body \"Body\" \"mayamesh.dmx\" \n"
|
|
" $staticprop \n"
|
|
" $upaxis y \n"
|
|
" $sequence \"idle\" \"mayamesh\" fps 30 \n"
|
|
" $collisionmodel \"mayamesh.dmx\" { $automass $concave }\n"
|
|
);
|
|
fclose( fQc );
|
|
}
|
|
else
|
|
return FALSE;
|
|
|
|
// Compile the mdl
|
|
CString sExeName;
|
|
sExeName.Format( "%s/../bin/studiomdl.exe", pszVproject );
|
|
CString sMdlCmdLine;
|
|
sMdlCmdLine.Format( "%s/../bin/studiomdl.exe -nop4 -fastbuild \"%s\"",
|
|
pszVproject, ( LPCTSTR ) sQc );
|
|
|
|
STARTUPINFO si;
|
|
memset( &si, 0, sizeof( si ) );
|
|
si.cb = sizeof( si );
|
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
|
si.wShowWindow = SW_HIDE;
|
|
|
|
PROCESS_INFORMATION pi;
|
|
memset( &pi, 0, sizeof( pi ) );
|
|
|
|
// system( sMdlCmdLine );
|
|
BOOL bCreatedProcess = CreateProcess(
|
|
sExeName.GetBuffer(), sMdlCmdLine.GetBuffer(),
|
|
NULL, NULL, FALSE, 0, NULL, NULL,
|
|
&si, &pi );
|
|
bCreatedProcess;
|
|
|
|
if ( pi.hThread )
|
|
{
|
|
CloseHandle( pi.hThread );
|
|
pi.hThread = NULL;
|
|
}
|
|
|
|
if ( pi.hProcess )
|
|
{
|
|
WaitForSingleObject( pi.hProcess, INFINITE );
|
|
CloseHandle( pi.hProcess );
|
|
pi.hProcess = NULL;
|
|
}
|
|
|
|
// We should have the model now
|
|
CString sMdl = sPath + "/studiomdl.mdl";
|
|
if ( !IsFileReadable( sMdl ) )
|
|
return FALSE;
|
|
|
|
m_strMdlFileName = sMdl;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::DmxDeleteOrigObjects()
|
|
{
|
|
// Make sure we are still in the same document and same selection
|
|
if ( m_pDoc != m_origSelection.GetMapDoc() )
|
|
return FALSE;
|
|
|
|
if ( s_opt_maya_bReplaceSelection )
|
|
{
|
|
m_pDoc->OnEditDelete();
|
|
}
|
|
else
|
|
{
|
|
GetHistory()->MarkUndoPosition( m_pSelection->GetList(), "Delete" );
|
|
|
|
// Delete objects in selection
|
|
const CMapObjectList &lst = *m_origSelection.GetList();
|
|
for ( int k = 0; k < lst.Count(); ++ k )
|
|
{
|
|
CMapClass *pObj = (CUtlReference< CMapClass >)lst.Element( k );
|
|
m_pDoc->DeleteObject( pObj );
|
|
}
|
|
m_pSelection->RemoveAll();
|
|
m_pDoc->SetModifiedFlag();
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL CToolHandler_SyncMesh::DmxCreateStaticProp()
|
|
{
|
|
// Make sure we are still in the same document and same selection
|
|
if ( m_pDoc != m_origSelection.GetMapDoc() )
|
|
return FALSE;
|
|
|
|
// Compute model hash
|
|
int iMdlHash = ComputeFileHash( m_strDmxFileName );
|
|
if ( !iMdlHash )
|
|
return FALSE;
|
|
|
|
// Crack the DMX file coordinates
|
|
float vecDmxOffset[3] = {0};
|
|
if ( m_strDmxFileName.GetLength() > 37 &&
|
|
m_strDmxFileName[ m_strDmxFileName.GetLength() - 37 ] == '@' )
|
|
{
|
|
int arrV[4] = {0};
|
|
for ( int k = 0; k < 4; ++ k )
|
|
{
|
|
CString sParse = m_strDmxFileName.Mid( m_strDmxFileName.GetLength() - 37 + 1 + 8 * k, 8 );
|
|
arrV[k] = strtoul( sParse, NULL, 16 );
|
|
}
|
|
for ( int k = 1; k < 4; ++ k )
|
|
{
|
|
vecDmxOffset[ k - 1 ] = reinterpret_cast< float & >( arrV[ k ] );
|
|
}
|
|
}
|
|
|
|
// Move the model to the mdl cache section
|
|
CString sMdlPath;
|
|
if ( !GetMdlCacheDir( sMdlPath ) )
|
|
return FALSE;
|
|
|
|
CString sMdlCacheFile;
|
|
sMdlCacheFile.Format( "%s/%08X.mdl", ( LPCTSTR ) sMdlPath, iMdlHash );
|
|
CString sMdlCacheFileRel = sMdlCacheFile.Mid( strlen( getenv( "VPROJECT" ) ) + 1 );
|
|
|
|
if ( s_opt_vmf_bStoreMdl )
|
|
{
|
|
char const * arrFiles[] = { ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
|
|
bool arrRequired[] = { true, true, true, false, false };
|
|
for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
|
|
{
|
|
CString sSrc = GetOtherFileName( m_strMdlFileName, arrFiles[ j ], 4 );
|
|
CString sDest = GetOtherFileName( sMdlCacheFile, arrFiles[j], 4 );
|
|
CopyFile( sSrc, sDest, FALSE );
|
|
if ( !IsFileReadable( sDest ) && arrRequired[ j ] )
|
|
return FALSE;
|
|
}
|
|
|
|
// Patch the MDL file
|
|
if ( FILE *fp = fopen( sMdlCacheFile, "r+b" ) )
|
|
{
|
|
fseek( fp, 12, SEEK_SET );
|
|
fprintf( fp, "%s%c%c", ( (LPCTSTR) sMdlCacheFileRel ) + strlen( "models/" ), 0, 0 );
|
|
fclose( fp );
|
|
}
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
if ( s_opt_vmf_bStoreDmx )
|
|
{
|
|
// Copy the dmx file as well
|
|
CString sDmxCacheFile;
|
|
sDmxCacheFile.Format( "%s/%08X.dmx", ( LPCTSTR ) sMdlPath, iMdlHash );
|
|
CopyFile( m_strDmxFileName, sDmxCacheFile, FALSE );
|
|
if ( !IsFileReadable( sDmxCacheFile ) )
|
|
return FALSE;
|
|
}
|
|
|
|
if ( s_opt_vmf_bStoreMa )
|
|
{
|
|
// Copy the maya file as well
|
|
CString sMayaCacheFile;
|
|
sMayaCacheFile.Format( "%s/%08X.ma", ( LPCTSTR ) sMdlPath, iMdlHash );
|
|
CopyFile( GetOtherFileName( m_strDmxFileName, ".ma", 4 ), sMayaCacheFile, FALSE );
|
|
if ( !IsFileReadable( sMayaCacheFile ) )
|
|
return FALSE;
|
|
}
|
|
|
|
// Create the static prop
|
|
Vector vecEntityPos( vecDmxOffset[0], vecDmxOffset[1], vecDmxOffset[2] );
|
|
if ( s_opt_maya_bMeshAtOrigin )
|
|
vecEntityPos += m_vecSelectionCenter;
|
|
|
|
CMapEntity *pEnt = m_pDoc->CreateEntity( "prop_static",
|
|
vecEntityPos.x, vecEntityPos.y, vecEntityPos.z );
|
|
pEnt->SetKeyValue( "model", sMdlCacheFileRel ); // TODO: proper DMX/MDL encoding
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Async texture browser implementation
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
CTextureBrowser_Async::CTextureBrowser_Async() :
|
|
CTextureBrowser( AfxGetMainWnd() )
|
|
{
|
|
NULL;
|
|
}
|
|
|
|
INT_PTR CTextureBrowser_Async::DoModal()
|
|
{
|
|
INT_PTR nResult = CTextureBrowser::DoModal();
|
|
OnResult( nResult );
|
|
delete this;
|
|
return nResult;
|
|
}
|
|
|
|
void CTextureBrowser_Async::OnResult( INT_PTR nModalResult )
|
|
{
|
|
CValveIpcClientUtl ipc( "MAYA_VST_UIHOOK_IPC_SERVER" );
|
|
if ( !ipc.Connect() )
|
|
return;
|
|
|
|
CUtlBuffer cmd;
|
|
|
|
cmd.PutString( "textureSelected" );
|
|
if ( nModalResult == IDOK )
|
|
{
|
|
cmd.PutString( m_cTextureWindow.szCurTexture );
|
|
}
|
|
else
|
|
{
|
|
cmd.PutString( "" );
|
|
}
|
|
|
|
CUtlBuffer res;
|
|
res.EnsureCapacity( 2 * MAX_PATH );
|
|
|
|
ipc.ExecuteCommand( cmd, res );
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Handling commands from Maya
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL CHammerIpcServer::ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res )
|
|
{
|
|
char szCmd[ MAX_PATH ] = {0};
|
|
cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
|
|
|
|
if ( !stricmp( szCmd, "mayaDmx" ) )
|
|
{
|
|
cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
|
|
CString sCookie = szCmd;
|
|
|
|
cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
|
|
char const *szDmxName = szCmd;
|
|
|
|
if ( !g_ToolHandlerSyncMesh.RequestDmxLoad( sCookie, szDmxName ) )
|
|
{
|
|
res.PutInt( 0 );
|
|
res.PutString( "Invalid mesh synchronization request!" );
|
|
return TRUE;
|
|
}
|
|
|
|
res.PutInt( 1 );
|
|
return TRUE;
|
|
}
|
|
|
|
if ( !stricmp( szCmd, "textureBrowse" ) )
|
|
{
|
|
cmd.GetString( szCmd, sizeof( szCmd ) - 1 );
|
|
char const *szTextureName = szCmd;
|
|
|
|
if ( !g_ToolHandlerSyncMesh.RequestTexture( szTextureName ) )
|
|
{
|
|
res.PutInt( 0 );
|
|
res.PutString( "Cannot request texture!" );
|
|
return TRUE;
|
|
}
|
|
|
|
res.PutInt( 1 );
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Special implementation of custom load/save chunks for entities
|
|
//
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
class CSyncMesh_SaveLoadHandler : public CVmfMeshDataSupport_SaveLoadHandler
|
|
{
|
|
public:
|
|
CSyncMesh_SaveLoadHandler();
|
|
~CSyncMesh_SaveLoadHandler();
|
|
|
|
public:
|
|
virtual char const *GetCustomSectionName() { return "meshdata"; }
|
|
|
|
public:
|
|
virtual ChunkFileResult_t SaveVMF( CChunkFile *pFile, IMapEntity_SaveInfo_t *pSaveInfo );
|
|
|
|
protected:
|
|
virtual ChunkFileResult_t OnFileDataLoaded( CUtlBuffer &bufData );
|
|
virtual ChunkFileResult_t OnFileDataWriting( CChunkFile *pFile, char const *szHash );
|
|
};
|
|
static CSyncMesh_SaveLoadHandler g_syncmesh_saveloadhandler;
|
|
|
|
CSyncMesh_SaveLoadHandler::CSyncMesh_SaveLoadHandler()
|
|
{
|
|
VmfInstallMapEntitySaveLoadHandler( this );
|
|
}
|
|
|
|
CSyncMesh_SaveLoadHandler::~CSyncMesh_SaveLoadHandler()
|
|
{
|
|
NULL;
|
|
}
|
|
|
|
ChunkFileResult_t CSyncMesh_SaveLoadHandler::SaveVMF( CChunkFile *pFile, IMapEntity_SaveInfo_t *pSaveInfo )
|
|
{
|
|
if ( !m_pEntity->IsClass( "prop_static" ) )
|
|
return ChunkFile_Ok;
|
|
|
|
LPCTSTR szModelName = m_pEntity->GetKeyValue( "model" );
|
|
if ( !szModelName || !*szModelName )
|
|
return ChunkFile_Ok;
|
|
|
|
CString sMdlPath;
|
|
if ( !GetMdlCacheDir( sMdlPath ) )
|
|
return ChunkFile_Ok;
|
|
|
|
CString sMdlRelPath = sMdlPath.Mid( strlen( getenv( "VPROJECT" ) ) + 1 ) + "/";
|
|
if ( !StringHasPrefix( szModelName, sMdlRelPath ) )
|
|
return ChunkFile_Ok;
|
|
|
|
// Model is under our special cache path
|
|
char szModelHash[ 16 ] = {0};
|
|
sprintf( szModelHash, "%.8s", szModelName + sMdlRelPath.GetLength() );
|
|
return WriteDataChunk( pFile, szModelHash );
|
|
}
|
|
|
|
ChunkFileResult_t CSyncMesh_SaveLoadHandler::OnFileDataWriting( CChunkFile *pFile, char const *szHash )
|
|
{
|
|
ChunkFileResult_t eResult;
|
|
|
|
LPCTSTR szModelName = m_pEntity->GetKeyValue( "model" );
|
|
|
|
CString sMdlPath;
|
|
GetMdlCacheDir( sMdlPath );
|
|
|
|
CString sMdlRelPath = sMdlPath.Mid( strlen( getenv( "VPROJECT" ) ) + 1 ) + "/";
|
|
|
|
char szModelHash[ 16 ] = {0};
|
|
sprintf( szModelHash, "%.8s", szModelName + sMdlRelPath.GetLength() );
|
|
|
|
// Write files
|
|
char const * arrFiles[] = { ".ma", ".dmx", ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
|
|
char const * arrNames[] = { "maa", "dmx", "mdl", "vvd", "vtx", "phy", "ss2" };
|
|
bool bOptNames[] = { s_opt_vmf_bStoreMa, s_opt_vmf_bStoreDmx, s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl,
|
|
s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl, s_opt_vmf_bStoreMdl };
|
|
bool bOptRequired[] = { true, true, true, true, true, false };
|
|
for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
|
|
{
|
|
if ( !bOptNames[j] )
|
|
continue;
|
|
|
|
CString sSrc = sMdlPath + CString( "/" ) + CString( szModelHash ) + arrFiles[j];
|
|
|
|
CUtlBuffer bufFile;
|
|
if ( !g_pFileSystem->ReadFile( sSrc, NULL, bufFile ) )
|
|
{
|
|
if ( !bOptRequired[ j ] )
|
|
continue;
|
|
else
|
|
return ChunkFile_OpenFail;
|
|
}
|
|
|
|
eResult = WriteBufferData( pFile, bufFile, arrNames[j] );
|
|
if ( eResult != ChunkFile_Ok )
|
|
return eResult;
|
|
}
|
|
|
|
return ChunkFile_Ok;
|
|
}
|
|
|
|
ChunkFileResult_t CSyncMesh_SaveLoadHandler::OnFileDataLoaded( CUtlBuffer &bufData )
|
|
{
|
|
char const * arrFiles[] = { ".ma", ".dmx", ".mdl", ".vvd", ".dx90.vtx", ".phy", ".ss2" };
|
|
char const * arrNames[] = { "maa", "dmx", "mdl", "vvd", "vtx", "phy", "ss2" };
|
|
|
|
char const *pFileExt = NULL;
|
|
|
|
// Determine the file name to save
|
|
for ( int j = 0; j < ARRAYSIZE( arrFiles ); ++ j )
|
|
{
|
|
if ( !stricmp( m_hLoadHeader.sPrefix, arrNames[j] ) )
|
|
{
|
|
pFileExt = arrFiles[j];
|
|
break;
|
|
}
|
|
}
|
|
if ( !pFileExt )
|
|
return ChunkFile_Fail;
|
|
|
|
// The filename
|
|
CString sSaveFileName;
|
|
if ( !GetMdlCacheDir( sSaveFileName ) )
|
|
return ChunkFile_Fail;
|
|
sSaveFileName += "/";
|
|
sSaveFileName += m_hLoadHeader.sHash;
|
|
sSaveFileName += pFileExt;
|
|
|
|
// We have file data, save it
|
|
if ( FILE *f = fopen( sSaveFileName, "wb" ) )
|
|
{
|
|
fwrite( bufData.Base(), 1, bufData.TellPut(), f );
|
|
fclose( f );
|
|
}
|
|
else
|
|
return ChunkFile_Fail;
|
|
|
|
return ChunkFile_Ok;
|
|
}
|
|
|