📄 mdlviewer.cpp
字号:
//
// Half-Life Model Viewer (c) 1999 by Mete Ciragan
//
// file: mdlviewer.cpp
// last modified: Jun 03 1999, Mete Ciragan
// copyright: The programs and associated files contained in this
// distribution were developed by Mete Ciragan. The programs
// are not in the public domain, but they are freely
// distributable without licensing fees. These programs are
// provided without guarantee or warrantee expressed or
// implied.
//
// version: 1.2
//
// email: mete@swissquake.ch
// web: http://www.swissquake.ch/chumbalum-soft/
//
#include "cbase.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <mx/mx.h>
#include <mx/mxTga.h>
#include <mx/mxEvent.h>
#include "mdlviewer.h"
#include "ViewerSettings.h"
#include "MatSysWin.h"
#include "ControlPanel.h"
#include "FlexPanel.h"
#include "StudioModel.h"
#include "cmdlib.h"
#include "mxExpressionTray.h"
#include "mxStatusWindow.h"
#include "ChoreoView.h"
#include "ifaceposersound.h"
#include "ifaceposerworkspace.h"
#include "expclass.h"
#include "PhonemeEditor.h"
#include "FileSystem.h"
#include "FileSystem_Tools.h"
#include "ExpressionTool.h"
#include "ControlPanel.h"
#include "choreowidgetdrawhelper.h"
#include "choreoviewcolors.h"
#include "tabwindow.h"
#include "faceposer_models.h"
#include "choiceproperties.h"
#include "choreoscene.h"
#include "choreoactor.h"
#include "vstdlib/strtools.h"
#include "InputProperties.h"
#include "GestureTool.h"
#include "SoundEmitterSystemBase.h"
#include "RampTool.h"
#include "SceneRampTool.h"
#include "vstdlib/icommandline.h"
#include "phonemeextractor.h"
#define WINDOW_TAB_OFFSET 24
MDLViewer *g_MDLViewer = 0;
char g_appTitle[] = "Half-Life Face Poser";
static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" };
void
MDLViewer::initRecentFiles ()
{
for (int i = 0; i < 8; i++)
{
if (strlen (recentFiles[i]))
{
mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, recentFiles[i]);
}
else
{
mb->modify (IDC_FILE_RECENTMODELS1 + i, IDC_FILE_RECENTMODELS1 + i, "(empty)");
mb->setEnabled (IDC_FILE_RECENTMODELS1 + i, false);
}
}
}
void
MDLViewer::loadRecentFiles ()
{
char path[256];
strcpy (path, mx::getApplicationPath ());
strcat (path, "/hlmv.rf");
FILE *file = fopen (path, "rb");
if (file)
{
fread (recentFiles, sizeof recentFiles, 1, file);
fclose (file);
}
}
void
MDLViewer::saveRecentFiles ()
{
char path[256];
strcpy (path, mx::getApplicationPath ());
strcat (path, "/hlmv.rf");
FILE *file = fopen (path, "wb");
if (file)
{
fwrite (recentFiles, sizeof recentFiles, 1, file);
fclose (file);
}
}
bool MDLViewer::Closing( void )
{
Con_Printf( "Checking for sound script changes...\n" );
// Save any changed sound script files
int c = soundemitter->GetNumSoundScripts();
for ( int i = 0; i < c; i++ )
{
if ( !soundemitter->IsSoundScriptDirty( i ) )
continue;
char const *scriptname = soundemitter->GetSoundScriptName( i );
if ( !scriptname )
continue;
if ( !filesystem->FileExists( scriptname ) ||
!filesystem->IsFileWritable( scriptname ) )
{
continue;
}
soundemitter->SaveChangesToSoundScript( i );
}
SaveWindowPositions();
models->SaveModelList();
models->CloseAllModels();
return true;
}
#define IDC_GRIDSETTINGS_FPS 1001
#define IDC_GRIDSETTINGS_SNAP 1002
class CFlatButton : public mxButton
{
public:
CFlatButton( mxWindow *parent, int id )
: mxButton( parent, 0, 0, 0, 0, "", id )
{
HWND wnd = (HWND)getHandle();
DWORD exstyle = GetWindowLong( wnd, GWL_EXSTYLE );
exstyle |= WS_EX_CLIENTEDGE;
SetWindowLong( wnd, GWL_EXSTYLE, exstyle );
DWORD style = GetWindowLong( wnd, GWL_STYLE );
style &= ~WS_BORDER;
SetWindowLong( wnd, GWL_STYLE, style );
}
};
class CMDLViewerGridSettings : public mxWindow
{
public:
typedef mxWindow BaseClass;
CMDLViewerGridSettings( mxWindow *parent, int x, int y, int w, int h ) :
mxWindow( parent, x, y, w, h )
{
FacePoser_AddWindowStyle( this, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS );
m_btnFPS = new CFlatButton( this, IDC_GRIDSETTINGS_FPS );
m_btnGridSnap = new CFlatButton( this, IDC_GRIDSETTINGS_SNAP );
}
void Init( void )
{
if ( g_pChoreoView )
{
CChoreoScene *scene = g_pChoreoView->GetScene();
if ( scene )
{
char sz[ 256 ];
Q_snprintf( sz, sizeof( sz ), "%i fps", scene->GetSceneFPS() );
m_btnFPS->setLabel( sz );
Q_snprintf( sz, sizeof( sz ), "snap: %s", scene->IsUsingFrameSnap() ? "on" : "off" );
m_btnGridSnap->setLabel( sz );
m_btnFPS->setVisible( true );
m_btnGridSnap->setVisible( true );
return;
}
}
m_btnFPS->setVisible( false );
m_btnGridSnap->setVisible( false );
}
virtual int handleEvent( mxEvent *event )
{
int iret = 0;
switch ( event->event )
{
default:
break;
case mxEvent::Size:
{
int leftedge = w2() * 0.45f;
m_btnFPS->setBounds( 0, 0, leftedge, h2() );
m_btnGridSnap->setBounds( leftedge, 0, w2() - leftedge, h2() );
iret = 1;
}
break;
case mxEvent::Action:
{
iret = 1;
switch ( event->action )
{
default:
iret = 0;
break;
case IDC_GRIDSETTINGS_FPS:
{
if ( g_pChoreoView )
{
CChoreoScene *scene = g_pChoreoView->GetScene();
if ( scene )
{
int currentFPS = scene->GetSceneFPS();
CInputParams params;
memset( ¶ms, 0, sizeof( params ) );
strcpy( params.m_szDialogTitle, "Change FPS" );
Q_snprintf( params.m_szInputText, sizeof( params.m_szInputText ),
"%i", currentFPS );
strcpy( params.m_szPrompt, "Current FPS:" );
if ( InputProperties( ¶ms ) )
{
int newFPS = atoi( params.m_szInputText );
if ( ( newFPS > 0 ) && ( newFPS != currentFPS ) )
{
g_pChoreoView->SetDirty( true );
g_pChoreoView->PushUndo( "Change Scene FPS" );
scene->SetSceneFPS( newFPS );
g_pChoreoView->PushRedo( "Change Scene FPS" );
Init();
Con_Printf( "FPS changed to %i\n", newFPS );
}
}
}
}
}
break;
case IDC_GRIDSETTINGS_SNAP:
{
if ( g_pChoreoView )
{
CChoreoScene *scene = g_pChoreoView->GetScene();
if ( scene )
{
g_pChoreoView->SetDirty( true );
g_pChoreoView->PushUndo( "Change Snap Frame" );
scene->SetUsingFrameSnap( !scene->IsUsingFrameSnap() );
g_pChoreoView->PushRedo( "Change Snap Frame" );
Init();
Con_Printf( "Time frame snapping: %s\n",
scene->IsUsingFrameSnap() ? "on" : "off" );
}
}
}
break;
}
}
}
return iret;
}
bool PaintBackground( void )
{
CChoreoWidgetDrawHelper drawHelper( this );
RECT rc;
drawHelper.GetClientRect( rc );
drawHelper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rc );
return false;
}
private:
CFlatButton *m_btnFPS;
CFlatButton *m_btnGridSnap;
};
#define IDC_MODELTAB_LOAD 1000
#define IDC_MODELTAB_CLOSE 1001
#define IDC_MODELTAB_CLOSEALL 1002
#define IDC_MODELTAB_CENTERONFACE 1003
#define IDC_MODELTAB_ASSOCIATEACTOR 1004
#define IDC_MODELTAB_TOGGLE3DVIEW 1005
#define IDC_MODELTAB_SHOWALL 1006
#define IDC_MODELTAB_HIDEALL 1007
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
class CMDLViewerModelTab : public CTabWindow
{
public:
typedef CTabWindow BaseClass;
CMDLViewerModelTab( mxWindow *parent, int x, int y, int w, int h, int id = 0, int style = 0 ) :
CTabWindow( parent, x, y, w, h, id, style )
{
SetInverted( true );
}
virtual void ShowRightClickMenu( int mx, int my )
{
mxPopupMenu *pop = new mxPopupMenu();
Assert( pop );
char const *current = "";
char const *filename = "";
int idx = getSelectedIndex();
if ( idx >= 0 )
{
current = models->GetModelName( idx );
filename = models->GetModelFileName( idx );
}
if ( models->Count() < MAX_FP_MODELS )
{
pop->add( "Load Model...", IDC_MODELTAB_LOAD );
}
if ( idx >= 0 )
{
pop->add( va( "Close '%s'", current ), IDC_MODELTAB_CLOSE );
}
if ( models->Count() > 0 )
{
pop->add( "Close All", IDC_MODELTAB_CLOSEALL );
}
if ( idx >= 0 )
{
pop->addSeparator();
pop->add( va( "Center %s's face", current ), IDC_MODELTAB_CENTERONFACE );
CChoreoScene *scene = g_pChoreoView->GetScene();
if ( scene )
{
// See if there is already an actor with this model associated
int c = scene->GetNumActors();
bool hasassoc = false;
for ( int i = 0; i < c; i++ )
{
CChoreoActor *a = scene->GetActor( i );
Assert( a );
if ( stricmp( a->GetFacePoserModelName(), filename ) )
continue;
hasassoc = true;
break;
}
if ( hasassoc )
{
pop->add( va( "Change associated actor for %s", current ), IDC_MODELTAB_ASSOCIATEACTOR );
}
else
{
pop->add( va( "Associate actor to %s", current ), IDC_MODELTAB_ASSOCIATEACTOR );
}
}
pop->addSeparator();
bool visible = models->IsModelShownIn3DView( idx );
if ( visible )
{
pop->add( va( "Remove %s from 3D View", current ), IDC_MODELTAB_TOGGLE3DVIEW );
}
else
{
pop->add( va( "Show %s in 3D View", current ), IDC_MODELTAB_TOGGLE3DVIEW );
}
}
if ( models->Count() > 0 )
{
pop->addSeparator();
pop->add( "Show All", IDC_MODELTAB_SHOWALL );
pop->add( "Hide All", IDC_MODELTAB_HIDEALL );
}
// Convert click position
POINT pt;
pt.x = mx;
pt.y = my;
// Convert coordinate space
pop->popup( this, pt.x, pt.y );
}
virtual int handleEvent( mxEvent *event )
{
int iret = 0;
switch ( event->event )
{
default:
break;
case mxEvent::Action:
{
iret = 1;
switch ( event->action )
{
default:
iret = 0;
break;
case IDC_MODELTAB_SHOWALL:
case IDC_MODELTAB_HIDEALL:
{
bool show = ( event->action == IDC_MODELTAB_SHOWALL ) ? true : false;
int c = models->Count();
for ( int i = 0; i < c ; i++ )
{
models->ShowModelIn3DView( i, show );
}
}
break;
case IDC_MODELTAB_LOAD:
{
const char *ptr = mxGetOpenFileName(
this,
FacePoser_MakeWindowsSlashes( va( "%s/models/", GetGameDirectory() ) ),
"*.mdl" );
if (ptr)
{
g_MDLViewer->LoadModelFile( ptr );
}
}
break;
case IDC_MODELTAB_CLOSE:
{
int idx = getSelectedIndex();
if ( idx >= 0 )
{
models->FreeModel( idx );
}
}
break;
case IDC_MODELTAB_CLOSEALL:
{
models->CloseAllModels();
}
break;
case IDC_MODELTAB_CENTERONFACE:
{
g_pControlPanel->CenterOnFace();
}
break;
case IDC_MODELTAB_TOGGLE3DVIEW:
{
int idx = getSelectedIndex();
if ( idx >= 0 )
{
bool visible = models->IsModelShownIn3DView( idx );
models->ShowModelIn3DView( idx, !visible );
}
}
break;
case IDC_MODELTAB_ASSOCIATEACTOR:
{
int idx = getSelectedIndex();
if ( idx >= 0 )
{
char const *modelname = models->GetModelFileName( idx );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -