summaryrefslogtreecommitdiff
path: root/utils/hlmv
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlmv
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'utils/hlmv')
-rw-r--r--utils/hlmv/anorms.h169
-rw-r--r--utils/hlmv/attachments_window.cpp349
-rw-r--r--utils/hlmv/attachments_window.h63
-rw-r--r--utils/hlmv/controlpanel.cpp3989
-rw-r--r--utils/hlmv/controlpanel.h480
-rw-r--r--utils/hlmv/debugdrawmodel.cpp761
-rw-r--r--utils/hlmv/debugdrawmodel.h30
-rw-r--r--utils/hlmv/fileassociation.cpp288
-rw-r--r--utils/hlmv/fileassociation.h98
-rw-r--r--utils/hlmv/hlmv.rc84
-rw-r--r--utils/hlmv/hlmv.vpc204
-rw-r--r--utils/hlmv/icon1.icobin0 -> 29926 bytes
-rw-r--r--utils/hlmv/makefile59
-rw-r--r--utils/hlmv/matsyswin.cpp1182
-rw-r--r--utils/hlmv/matsyswin.h179
-rw-r--r--utils/hlmv/mdlviewer.cpp1631
-rw-r--r--utils/hlmv/mdlviewer.h163
-rw-r--r--utils/hlmv/mxLineEdit2.cpp31
-rw-r--r--utils/hlmv/mxLineEdit2.h31
-rw-r--r--utils/hlmv/pakviewer.cpp516
-rw-r--r--utils/hlmv/pakviewer.h99
-rw-r--r--utils/hlmv/physmesh.cpp619
-rw-r--r--utils/hlmv/physmesh.h72
-rw-r--r--utils/hlmv/resource.h23
-rw-r--r--utils/hlmv/studio_flex.cpp76
-rw-r--r--utils/hlmv/studio_render.cpp2368
-rw-r--r--utils/hlmv/studio_render.h26
-rw-r--r--utils/hlmv/studio_utils.cpp1224
-rw-r--r--utils/hlmv/studiomodel.h483
-rw-r--r--utils/hlmv/sys.h14
-rw-r--r--utils/hlmv/sys_win.cpp37
-rw-r--r--utils/hlmv/viewersettings.cpp704
-rw-r--r--utils/hlmv/viewersettings.h157
33 files changed, 16209 insertions, 0 deletions
diff --git a/utils/hlmv/anorms.h b/utils/hlmv/anorms.h
new file mode 100644
index 0000000..43499ac
--- /dev/null
+++ b/utils/hlmv/anorms.h
@@ -0,0 +1,169 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+{-0.525731f, 0.000000f, 0.850651f},
+{-0.442863f, 0.238856f, 0.864188f},
+{-0.295242f, 0.000000f, 0.955423f},
+{-0.309017f, 0.500000f, 0.809017f},
+{-0.162460f, 0.262866f, 0.951056f},
+{0.000000f, 0.000000f, 1.000000f},
+{0.000000f, 0.850651f, 0.525731f},
+{-0.147621f, 0.716567f, 0.681718f},
+{0.147621f, 0.716567f, 0.681718f},
+{0.000000f, 0.525731f, 0.850651f},
+{0.309017f, 0.500000f, 0.809017f},
+{0.525731f, 0.000000f, 0.850651f},
+{0.295242f, 0.000000f, 0.955423f},
+{0.442863f, 0.238856f, 0.864188f},
+{0.162460f, 0.262866f, 0.951056f},
+{-0.681718f, 0.147621f, 0.716567f},
+{-0.809017f, 0.309017f, 0.500000f},
+{-0.587785f, 0.425325f, 0.688191f},
+{-0.850651f, 0.525731f, 0.000000f},
+{-0.864188f, 0.442863f, 0.238856f},
+{-0.716567f, 0.681718f, 0.147621f},
+{-0.688191f, 0.587785f, 0.425325f},
+{-0.500000f, 0.809017f, 0.309017f},
+{-0.238856f, 0.864188f, 0.442863f},
+{-0.425325f, 0.688191f, 0.587785f},
+{-0.716567f, 0.681718f, -0.147621f},
+{-0.500000f, 0.809017f, -0.309017f},
+{-0.525731f, 0.850651f, 0.000000f},
+{0.000000f, 0.850651f, -0.525731f},
+{-0.238856f, 0.864188f, -0.442863f},
+{0.000000f, 0.955423f, -0.295242f},
+{-0.262866f, 0.951056f, -0.162460f},
+{0.000000f, 1.000000f, 0.000000f},
+{0.000000f, 0.955423f, 0.295242f},
+{-0.262866f, 0.951056f, 0.162460f},
+{0.238856f, 0.864188f, 0.442863f},
+{0.262866f, 0.951056f, 0.162460f},
+{0.500000f, 0.809017f, 0.309017f},
+{0.238856f, 0.864188f, -0.442863f},
+{0.262866f, 0.951056f, -0.162460f},
+{0.500000f, 0.809017f, -0.309017f},
+{0.850651f, 0.525731f, 0.000000f},
+{0.716567f, 0.681718f, 0.147621f},
+{0.716567f, 0.681718f, -0.147621f},
+{0.525731f, 0.850651f, 0.000000f},
+{0.425325f, 0.688191f, 0.587785f},
+{0.864188f, 0.442863f, 0.238856f},
+{0.688191f, 0.587785f, 0.425325f},
+{0.809017f, 0.309017f, 0.500000f},
+{0.681718f, 0.147621f, 0.716567f},
+{0.587785f, 0.425325f, 0.688191f},
+{0.955423f, 0.295242f, 0.000000f},
+{1.000000f, 0.000000f, 0.000000f},
+{0.951056f, 0.162460f, 0.262866f},
+{0.850651f, -0.525731f, 0.000000f},
+{0.955423f, -0.295242f, 0.000000f},
+{0.864188f, -0.442863f, 0.238856f},
+{0.951056f, -0.162460f, 0.262866f},
+{0.809017f, -0.309017f, 0.500000f},
+{0.681718f, -0.147621f, 0.716567f},
+{0.850651f, 0.000000f, 0.525731f},
+{0.864188f, 0.442863f, -0.238856f},
+{0.809017f, 0.309017f, -0.500000f},
+{0.951056f, 0.162460f, -0.262866f},
+{0.525731f, 0.000000f, -0.850651f},
+{0.681718f, 0.147621f, -0.716567f},
+{0.681718f, -0.147621f, -0.716567f},
+{0.850651f, 0.000000f, -0.525731f},
+{0.809017f, -0.309017f, -0.500000f},
+{0.864188f, -0.442863f, -0.238856f},
+{0.951056f, -0.162460f, -0.262866f},
+{0.147621f, 0.716567f, -0.681718f},
+{0.309017f, 0.500000f, -0.809017f},
+{0.425325f, 0.688191f, -0.587785f},
+{0.442863f, 0.238856f, -0.864188f},
+{0.587785f, 0.425325f, -0.688191f},
+{0.688191f, 0.587785f, -0.425325f},
+{-0.147621f, 0.716567f, -0.681718f},
+{-0.309017f, 0.500000f, -0.809017f},
+{0.000000f, 0.525731f, -0.850651f},
+{-0.525731f, 0.000000f, -0.850651f},
+{-0.442863f, 0.238856f, -0.864188f},
+{-0.295242f, 0.000000f, -0.955423f},
+{-0.162460f, 0.262866f, -0.951056f},
+{0.000000f, 0.000000f, -1.000000f},
+{0.295242f, 0.000000f, -0.955423f},
+{0.162460f, 0.262866f, -0.951056f},
+{-0.442863f, -0.238856f, -0.864188f},
+{-0.309017f, -0.500000f, -0.809017f},
+{-0.162460f, -0.262866f, -0.951056f},
+{0.000000f, -0.850651f, -0.525731f},
+{-0.147621f, -0.716567f, -0.681718f},
+{0.147621f, -0.716567f, -0.681718f},
+{0.000000f, -0.525731f, -0.850651f},
+{0.309017f, -0.500000f, -0.809017f},
+{0.442863f, -0.238856f, -0.864188f},
+{0.162460f, -0.262866f, -0.951056f},
+{0.238856f, -0.864188f, -0.442863f},
+{0.500000f, -0.809017f, -0.309017f},
+{0.425325f, -0.688191f, -0.587785f},
+{0.716567f, -0.681718f, -0.147621f},
+{0.688191f, -0.587785f, -0.425325f},
+{0.587785f, -0.425325f, -0.688191f},
+{0.000000f, -0.955423f, -0.295242f},
+{0.000000f, -1.000000f, 0.000000f},
+{0.262866f, -0.951056f, -0.162460f},
+{0.000000f, -0.850651f, 0.525731f},
+{0.000000f, -0.955423f, 0.295242f},
+{0.238856f, -0.864188f, 0.442863f},
+{0.262866f, -0.951056f, 0.162460f},
+{0.500000f, -0.809017f, 0.309017f},
+{0.716567f, -0.681718f, 0.147621f},
+{0.525731f, -0.850651f, 0.000000f},
+{-0.238856f, -0.864188f, -0.442863f},
+{-0.500000f, -0.809017f, -0.309017f},
+{-0.262866f, -0.951056f, -0.162460f},
+{-0.850651f, -0.525731f, 0.000000f},
+{-0.716567f, -0.681718f, -0.147621f},
+{-0.716567f, -0.681718f, 0.147621f},
+{-0.525731f, -0.850651f, 0.000000f},
+{-0.500000f, -0.809017f, 0.309017f},
+{-0.238856f, -0.864188f, 0.442863f},
+{-0.262866f, -0.951056f, 0.162460f},
+{-0.864188f, -0.442863f, 0.238856f},
+{-0.809017f, -0.309017f, 0.500000f},
+{-0.688191f, -0.587785f, 0.425325f},
+{-0.681718f, -0.147621f, 0.716567f},
+{-0.442863f, -0.238856f, 0.864188f},
+{-0.587785f, -0.425325f, 0.688191f},
+{-0.309017f, -0.500000f, 0.809017f},
+{-0.147621f, -0.716567f, 0.681718f},
+{-0.425325f, -0.688191f, 0.587785f},
+{-0.162460f, -0.262866f, 0.951056f},
+{0.442863f, -0.238856f, 0.864188f},
+{0.162460f, -0.262866f, 0.951056f},
+{0.309017f, -0.500000f, 0.809017f},
+{0.147621f, -0.716567f, 0.681718f},
+{0.000000f, -0.525731f, 0.850651f},
+{0.425325f, -0.688191f, 0.587785f},
+{0.587785f, -0.425325f, 0.688191f},
+{0.688191f, -0.587785f, 0.425325f},
+{-0.955423f, 0.295242f, 0.000000f},
+{-0.951056f, 0.162460f, 0.262866f},
+{-1.000000f, 0.000000f, 0.000000f},
+{-0.850651f, 0.000000f, 0.525731f},
+{-0.955423f, -0.295242f, 0.000000f},
+{-0.951056f, -0.162460f, 0.262866f},
+{-0.864188f, 0.442863f, -0.238856f},
+{-0.951056f, 0.162460f, -0.262866f},
+{-0.809017f, 0.309017f, -0.500000f},
+{-0.864188f, -0.442863f, -0.238856f},
+{-0.951056f, -0.162460f, -0.262866f},
+{-0.809017f, -0.309017f, -0.500000f},
+{-0.681718f, 0.147621f, -0.716567f},
+{-0.681718f, -0.147621f, -0.716567f},
+{-0.850651f, 0.000000f, -0.525731f},
+{-0.688191f, 0.587785f, -0.425325f},
+{-0.587785f, 0.425325f, -0.688191f},
+{-0.425325f, 0.688191f, -0.587785f},
+{-0.425325f, -0.688191f, -0.587785f},
+{-0.587785f, -0.425325f, -0.688191f},
+{-0.688191f, -0.587785f, -0.425325f},
diff --git a/utils/hlmv/attachments_window.cpp b/utils/hlmv/attachments_window.cpp
new file mode 100644
index 0000000..5a3b5b9
--- /dev/null
+++ b/utils/hlmv/attachments_window.cpp
@@ -0,0 +1,349 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "attachments_window.h"
+#include "ControlPanel.h"
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "MatSysWin.h"
+
+
+#define IDC_ATTACHMENT_LIST (IDC_ATTACHMENT_WINDOW_FIRST+0)
+#define IDC_ATTACHMENT_LIST_BONES (IDC_ATTACHMENT_WINDOW_FIRST+1)
+#define IDC_ATTACHMENT_TRANSLATION (IDC_ATTACHMENT_WINDOW_FIRST+2)
+#define IDC_ATTACHMENT_ROTATION (IDC_ATTACHMENT_WINDOW_FIRST+3)
+#define IDC_ATTACHMENT_QC_STRING (IDC_ATTACHMENT_WINDOW_FIRST+4)
+
+
+CAttachmentsWindow::CAttachmentsWindow( ControlPanel* pParent ) : mxWindow( pParent, 0, 0, 0, 0 )
+{
+ m_pControlPanel = pParent;
+ g_viewerSettings.m_iEditAttachment = -1;
+}
+
+
+void CAttachmentsWindow::Init( )
+{
+ int left, top;
+ left = 5;
+ top = 0;
+
+ // Attachment selection list
+ new mxLabel( this, left + 3, top + 4, 60, 18, "Attachment" );
+ m_cAttachmentList = new mxListBox( this, left, top + 20, 260, 100, IDC_ATTACHMENT_LIST );
+ m_cAttachmentList->add ("None");
+ m_cAttachmentList->select (0);
+ mxToolTip::add (m_cAttachmentList, "Select an attachment to modify");
+
+ left = 280;
+ new mxLabel( this, left + 3, top + 4, 60, 18, "Attach To Bone" );
+ m_cBoneList = new mxListBox( this, left, top + 20, 260, 100, IDC_ATTACHMENT_LIST_BONES );
+ m_cBoneList->add ("None");
+ m_cBoneList->select( 0 );
+ mxToolTip::add( m_cBoneList, "Select a bone to attach to" );
+
+
+ left = 5;
+ top = 120;
+ new mxLabel( this, left + 3, top + 4, 60, 18, "Translation" );
+ m_cTranslation = new mxLineEdit2( this, left + 70, top, 90, 25, "10 20 30", IDC_ATTACHMENT_TRANSLATION );
+
+
+ left = 170;
+ top = 120;
+ new mxLabel( this, left + 3, top + 4, 60, 18, "Rotation" );
+ m_cRotation = new mxLineEdit2( this, left + 70, top, 90, 25, "0 90 180", IDC_ATTACHMENT_ROTATION );
+
+
+ top = 145;
+ left = 5;
+ new mxLabel( this, left, top, 60, 18, "QC String" );
+ m_cQCString = new mxLineEdit2( this, left + 70, top, 400, 25, "$attachment \"controlpanel0_ur\" \"Vgui\" -22 -15 4 rotate 0 0 0", IDC_ATTACHMENT_QC_STRING );
+}
+
+
+void CAttachmentsWindow::OnLoadModel()
+{
+ int iPrevEdit = g_viewerSettings.m_iEditAttachment;
+ PopulateBoneList();
+ PopulateAttachmentsList();
+
+ if ( iPrevEdit >= 0 && iPrevEdit < m_cAttachmentList->getItemCount())
+ {
+ m_cAttachmentList->select( iPrevEdit + 1 );
+ }
+ g_viewerSettings.m_iEditAttachment = iPrevEdit;
+ UpdateStrings();
+}
+
+
+void CAttachmentsWindow::OnTabSelected()
+{
+ // for now, keep selection
+ // g_viewerSettings.m_iEditAttachment = m_cAttachmentList->getSelectedIndex() - 1;
+}
+
+
+void CAttachmentsWindow::OnTabUnselected()
+{
+ // for now, keep selection
+ // g_viewerSettings.m_iEditAttachment = -1;
+}
+
+
+void CAttachmentsWindow::PopulateAttachmentsList()
+{
+ m_cAttachmentList->removeAll();
+
+ m_cAttachmentList->add( "(none)" );
+
+ if ( g_pStudioModel )
+ {
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ if (pHdr->GetNumAttachments())
+ {
+ for ( int i = 0; i < pHdr->GetNumAttachments(); i++ )
+ {
+ m_cAttachmentList->add ( pHdr->pAttachment(i).pszName() );
+ }
+
+ m_cAttachmentList->select (0);
+ OnSelChangeAttachmentList();
+ return;
+ }
+ }
+
+ m_cAttachmentList->select (0);
+}
+
+
+void CAttachmentsWindow::PopulateBoneList()
+{
+ m_cBoneList->removeAll();
+
+ if ( g_pStudioModel )
+ {
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ if (pHdr->numbones())
+ {
+ for ( int i = 0; i < pHdr->numbones(); i++ )
+ {
+ m_cBoneList->add ( pHdr->pBone(i)->pszName() );
+ }
+
+ m_cBoneList->select (0);
+ return;
+ }
+ }
+
+ m_cBoneList->add( "None" );
+ m_cBoneList->select (0);
+}
+
+
+int CAttachmentsWindow::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( !g_pStudioModel )
+ return 0;
+
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ switch( event->action )
+ {
+ case IDC_ATTACHMENT_LIST:
+ {
+ OnSelChangeAttachmentList();
+ }
+ break;
+
+ case IDC_ATTACHMENT_LIST_BONES:
+ {
+ int iAttachment = g_viewerSettings.m_iEditAttachment;
+ int iBone = m_cBoneList->getSelectedIndex();
+
+ if ( iAttachment >= 0 &&
+ iAttachment < pHdr->GetNumAttachments() &&
+ iBone >= 0 &&
+ iBone < pHdr->numbones() )
+ {
+ pHdr->SetAttachmentBone( iAttachment, iBone );
+ UpdateStrings();
+ }
+ }
+ break;
+
+ case IDC_ATTACHMENT_TRANSLATION:
+ {
+ int iAttachment = g_viewerSettings.m_iEditAttachment;
+
+ if ( iAttachment >= 0 &&
+ iAttachment < pHdr->GetNumAttachments() )
+ {
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pHdr->pAttachment( iAttachment );
+
+ Vector vTrans( 0, 0, 0 );
+ char curText[512];
+ m_cTranslation->getText( curText, sizeof( curText ) );
+ sscanf( curText, "%f %f %f", &vTrans.x, &vTrans.y, &vTrans.z );
+
+ pAttachment.local[0][3] = vTrans.x;
+ pAttachment.local[1][3] = vTrans.y;
+ pAttachment.local[2][3] = vTrans.z;
+
+ UpdateStrings( true, false, false );
+ }
+ }
+ break;
+
+ case IDC_ATTACHMENT_ROTATION:
+ {
+ int iAttachment = g_viewerSettings.m_iEditAttachment;
+
+ if ( iAttachment >= 0 &&
+ iAttachment < pHdr->GetNumAttachments() )
+ {
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pHdr->pAttachment( iAttachment );
+
+ QAngle vRotation( 0, 0, 0 );
+ char curText[512];
+ m_cRotation->getText( curText, sizeof( curText ) );
+ sscanf( curText, "%f %f %f", &vRotation.x, &vRotation.y, &vRotation.z );
+
+ Vector vTrans = GetCurrentTranslation();
+ AngleMatrix( vRotation, vTrans, pAttachment.local );
+
+ UpdateStrings( true, false, false );
+ }
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void CAttachmentsWindow::OnSelChangeAttachmentList()
+{
+ CStudioHdr *pStudioHdr = g_pStudioModel ? g_pStudioModel->GetStudioHdr() : NULL;
+
+ if ( !pStudioHdr )
+ return;
+
+ int iAttachment = m_cAttachmentList->getSelectedIndex() - 1;
+ if ( iAttachment >= 0 && iAttachment < pStudioHdr->GetNumAttachments() )
+ {
+ g_viewerSettings.m_iEditAttachment = iAttachment;
+
+ // Init the bone list index.
+ int iBone = g_pStudioModel->GetStudioHdr()->GetAttachmentBone( iAttachment );
+ m_cBoneList->select( iBone );
+ }
+ else
+ {
+ g_viewerSettings.m_iEditAttachment = -1;
+ }
+
+ UpdateStrings();
+}
+
+
+Vector CAttachmentsWindow::GetCurrentTranslation()
+{
+ CStudioHdr *pStudioHdr = g_pStudioModel ? g_pStudioModel->GetStudioHdr() : NULL;
+
+ int iAttachment = m_cAttachmentList->getSelectedIndex() - 1;
+ if ( pStudioHdr && iAttachment >= 0 && iAttachment < pStudioHdr->GetNumAttachments() )
+ {
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pStudioHdr->pAttachment( iAttachment );
+
+ return Vector( pAttachment.local[0][3],
+ pAttachment.local[1][3],
+ pAttachment.local[2][3] );
+ }
+ else
+ {
+ return vec3_origin;
+ }
+}
+
+
+Vector CAttachmentsWindow::GetCurrentRotation()
+{
+ CStudioHdr *pStudioHdr = g_pStudioModel ? g_pStudioModel->GetStudioHdr() : NULL;
+
+ int iAttachment = m_cAttachmentList->getSelectedIndex() - 1;
+ if ( pStudioHdr && iAttachment >= 0 && iAttachment < pStudioHdr->GetNumAttachments() )
+ {
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pStudioHdr->pAttachment( iAttachment );
+
+ float angles[3];
+ MatrixAngles( pAttachment.local, angles );
+ return Vector( angles[0], angles[1], angles[2] );
+ }
+ else
+ {
+ return vec3_origin;
+ }
+}
+
+
+void CAttachmentsWindow::UpdateStrings( bool bUpdateQC, bool bUpdateTranslation, bool bUpdateRotation )
+{
+ char str[1024];
+
+ int iAttachment = -1;
+ CStudioHdr* pHdr = NULL;
+ if ( g_pStudioModel )
+ {
+ pHdr = g_pStudioModel->GetStudioHdr();
+ iAttachment = m_cAttachmentList->getSelectedIndex() - 1;
+ if ( iAttachment < 0 || iAttachment >= pHdr->GetNumAttachments() )
+ iAttachment = -1;
+ }
+
+ if ( iAttachment == -1 )
+ {
+ m_cTranslation->setText( "(none)" );
+ m_cRotation->setText( "(none)" );
+ m_cQCString->setText( "(none)" );
+ }
+ else
+ {
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pHdr->pAttachment( iAttachment );
+ int iBone= pHdr->GetAttachmentBone( iAttachment );
+ Vector vTranslation = GetCurrentTranslation();
+ Vector vRotation = GetCurrentRotation();
+
+ if ( bUpdateQC )
+ {
+ sprintf( str, "$attachment \"%s\" \"%s\" %.2f %.2f %.2f rotate %.0f %.0f %.0f",
+ pAttachment.pszName(),
+ pHdr->pBone( iBone )->pszName(),
+ VectorExpand( vTranslation ),
+ VectorExpand( vRotation ) );
+
+ m_cQCString->setText( str );
+ }
+
+ if ( bUpdateTranslation )
+ {
+ sprintf( str, "%.2f %.2f %.2f", VectorExpand( vTranslation ) );
+ m_cTranslation->setText( str );
+ }
+
+ if ( bUpdateRotation )
+ {
+ sprintf( str, "%.0f %.0f %.0f", VectorExpand( vRotation ) );
+ m_cRotation->setText( str );
+ }
+ }
+}
+
+
diff --git a/utils/hlmv/attachments_window.h b/utils/hlmv/attachments_window.h
new file mode 100644
index 0000000..481d62a
--- /dev/null
+++ b/utils/hlmv/attachments_window.h
@@ -0,0 +1,63 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef ATTACHMENTS_WINDOW_H
+#define ATTACHMENTS_WINDOW_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+#include <mxtk/mx.h>
+#include "mxLineEdit2.h"
+#include "mathlib/vector.h"
+
+
+class ControlPanel;
+
+
+class CAttachmentsWindow : public mxWindow
+{
+public:
+ CAttachmentsWindow( ControlPanel* pParent );
+ void Init( );
+
+ void OnLoadModel();
+
+ void OnTabSelected();
+ void OnTabUnselected();
+
+ virtual int handleEvent( mxEvent *event );
+
+
+private:
+
+ void OnSelChangeAttachmentList();
+
+ void PopulateAttachmentsList();
+ void PopulateBoneList();
+ void UpdateStrings( bool bUpdateQC=true, bool bUpdateTranslation=true, bool bUpdateRotation=true );
+
+ Vector GetCurrentTranslation();
+ Vector GetCurrentRotation();
+
+
+private:
+
+ ControlPanel *m_pControlPanel;
+ mxListBox *m_cAttachmentList;
+ mxListBox *m_cBoneList;
+
+ mxLineEdit2 *m_cTranslation;
+ mxLineEdit2 *m_cRotation;
+ mxLineEdit2 *m_cQCString;
+};
+
+
+#endif // ATTACHMENTS_WINDOW_H
diff --git a/utils/hlmv/controlpanel.cpp b/utils/hlmv/controlpanel.cpp
new file mode 100644
index 0000000..e4c5da5
--- /dev/null
+++ b/utils/hlmv/controlpanel.cpp
@@ -0,0 +1,3989 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: ControlPanel.cpp
+// last modified: May 29 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include "ControlPanel.h"
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "IStudioRender.h"
+#include "MatSysWin.h"
+#include "vphysics/constraints.h"
+#include "physmesh.h"
+#include "sys.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include <mxtk/mxBmp.h>
+#include "vphysics_interface.h"
+#include "utlvector.h"
+#include "utlsymbol.h"
+#include "UtlBuffer.h"
+#include "attachments_window.h"
+#include "istudiorender.h"
+#include "studio_render.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "tier1/KeyValues.h"
+#include "tier0/icommandline.h"
+#include "valve_ipc_win32.h"
+#include "mdlviewer.h"
+
+extern char g_appTitle[];
+extern IPhysicsSurfaceProps *physprop;
+extern bool LoadPhysicsProperties( void );
+extern ISoundEmitterSystemBase *g_pSoundEmitterBase;
+extern CValveIpcClientUtl g_HlmvIpcClient;
+extern bool g_bHlmvMaster;
+
+
+//-----------------------------------------------------------------------------
+// Reads all of the physics materials from surface property file
+//-----------------------------------------------------------------------------
+
+static void ReadPhysicsMaterials( mxChoice *plist )
+{
+ LoadPhysicsProperties();
+ plist->removeAll();
+
+ if ( !physprop || !physprop->SurfacePropCount() )
+ {
+ plist->add("default");
+ plist->select(0);
+ return;
+ }
+
+ for ( int i = 0; i < physprop->SurfacePropCount(); i++ )
+ {
+ plist->add(physprop->GetPropName( i ) );
+ }
+ plist->select(0);
+}
+
+
+//-----------------------------------------------------------------------------
+// Populates a control with all the physics bones
+//-----------------------------------------------------------------------------
+
+static bool PopulatePhysicsBoneList( mxChoice* pChoice )
+{
+ pChoice->removeAll();
+
+ if ( g_pStudioModel->Physics_GetBoneCount() )
+ {
+ for ( int i = 0; i < g_pStudioModel->Physics_GetBoneCount(); i++ )
+ {
+ pChoice->add (g_pStudioModel->Physics_GetBoneName( i ) );
+ }
+ }
+ else
+ {
+ pChoice->add( "None" );
+ pChoice->select (0);
+ return false;
+ }
+
+ pChoice->select (0);
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Populates a control with all the sound names
+//-----------------------------------------------------------------------------
+
+static void PopulateSoundNameList( mxListBox *pListBox )
+{
+ pListBox->removeAll();
+
+ if ( g_pSoundEmitterBase )
+ {
+ for ( int i = g_pSoundEmitterBase->First(); i != g_pSoundEmitterBase->InvalidIndex(); i = g_pSoundEmitterBase->Next( i ) )
+ {
+ pListBox->add( g_pSoundEmitterBase->GetSoundName( i ) );
+ }
+ }
+
+ pListBox->select( 0 );
+ pListBox->deselect( 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Populates a control with all the physics bones
+//-----------------------------------------------------------------------------
+
+bool PopulateBoneList( mxChoice* pChoice, bool bAlwaysAddNone = false )
+{
+ pChoice->removeAll();
+
+ if ( bAlwaysAddNone )
+ {
+ pChoice->add( "(None)" );
+ }
+
+ if ( g_pStudioModel )
+ {
+ CStudioHdr *pHdr = g_pStudioModel->GetStudioHdr();
+ if ( pHdr && pHdr->numbones() > 0 )
+ {
+ for ( int i = 0; i < pHdr->numbones(); i++ )
+ {
+ pChoice->add ( pHdr->pBone(i)->pszName() );
+ }
+
+ pChoice->select(0);
+ return true;
+ }
+ }
+
+ if ( !bAlwaysAddNone )
+ {
+ pChoice->add( "(None)" );
+ }
+
+ pChoice->select(0);
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Populates a control with all the attachments
+//-----------------------------------------------------------------------------
+
+void PopulateAttachmentsList( mxChoice *pChoice )
+{
+ pChoice->removeAll();
+
+ pChoice->add( "(none)" );
+
+ if ( g_pStudioModel )
+ {
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ if ( pHdr && pHdr->GetNumAttachments() > 0 )
+ {
+ for ( int i = 0; i < pHdr->GetNumAttachments(); i++ )
+ {
+ pChoice->add( pHdr->pAttachment(i).pszName() );
+ }
+ }
+ }
+
+ pChoice->select(0);
+}
+
+//-----------------------------------------------------------------------------
+// Sets the text of a lineedit to the current frame
+//-----------------------------------------------------------------------------
+void SetFrameString( mxLineEdit2 *pLineEdit, int iLayer )
+{
+ float flFrame = g_pStudioModel->GetFrame( iLayer );
+ int nFrame = int( flFrame + 0.5f );
+
+ char msg[ 16 ];
+ sprintf( msg, "%d", nFrame );
+ pLineEdit->setText( msg );
+}
+
+//-----------------------------------------------------------------------------
+// Finds the index of a surface prop
+//-----------------------------------------------------------------------------
+
+static int FindSurfaceProp( const char* pSurfaceProp )
+{
+ for (int i = 0; i < physprop->SurfacePropCount(); ++i)
+ {
+ if (!stricmp( physprop->GetPropName( i ), pSurfaceProp ))
+ return i;
+ }
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// The tab associated with bone control
+//-----------------------------------------------------------------------------
+
+class CBoneControlWindow : public mxWindow
+{
+public:
+ CBoneControlWindow( ControlPanel* pParent );
+
+ void Init( );
+
+ // This gets called when the model is loaded and unloaded
+ void OnLoadModel();
+ void OnUnloadModel();
+
+ // Handles various events
+ int handleEvent (mxEvent *event);
+
+ // Called when we're selected
+ void OnTabSelected();
+
+ int GetHitboxSet( void );
+private:
+ // Called when the bone is selected
+ void OnBoneSelected( int boneIndex );
+ void OnBoneHighlighted( bool isChecked );
+ void OnHitboxHighlighted( bool isChecked );
+ void OnShowDefaultPose( bool isChecked );
+ void OnHitboxSelected( int hitbox );
+ void OnHitboxGroupChanged( );
+ void OnHitboxChanged( );
+ void OnHitboxSetChanged( void );
+ void OnAddHitbox( );
+ void OnDeleteHitbox( );
+ void OnGenerateQC( );
+ void OnAutogenerateHitboxes( bool isChecked );
+
+ // Writes out qc-style text to a utlbuffer
+ bool SerializeQC( CUtlBuffer& buf );
+
+ // Sets the surface property
+ void OnSurfaceProp( int propIndex );
+
+ // Duplictes the surface property to all children
+ void OnSurfacePropApplyToChildren( );
+
+ void RefreshHitbox( );
+ void ComputeHitboxList( );
+
+ void ComputeHitboxSetList( void );
+ void OnHitboxAddSet( void );
+ void OnHitboxDeleteSet( void );
+ void OnHitboxSetChangeName( void );
+
+ // Generates a list of all hitboxes per bone
+ void PopulateHitboxLists();
+
+ // Applies a surface property to all children
+ void OnSurfacePropApplyToChildren_R( int bone, CUtlSymbol prop );
+
+ // Selects the bone
+ mxChoice* m_cBone;
+
+ mxChoice* m_cHitboxSet;
+
+ mxLineEdit *m_eHitboxSetName;
+ mxButton *m_bHitboxSetUpdateName;
+ mxButton *m_bAddHitboxSet;
+ mxButton *m_bDeleteHitboxSet;
+
+ // Selects a hitbox associated with a bone
+ mxChoice* m_cHitbox;
+
+ // The materials to assign to the bone
+ mxChoice* m_cSurfaceProp;
+
+ // Are we highlighting bones?
+ mxCheckBox* m_cBoneHighlight;
+
+ // Are we highlighting hitboxes?
+ mxCheckBox* m_cHitboxHighlight;
+
+ // Should we show the default pose?
+ mxCheckBox* m_cShowDefaultPose;
+
+ // Should hitboxes be autogenerated?
+ mxCheckBox* m_cAutoHitbox;
+
+ // The control panel to which we're attached
+ ControlPanel* m_pControlPanel;
+
+ // Hitbox group
+ mxLineEdit* m_eHitboxGroup;
+
+ // Hitbox name
+ mxLineEdit* m_eHitboxName;
+
+
+ // Hitbox origin
+ mxLineEdit* m_eOriginX;
+ mxLineEdit* m_eOriginY;
+ mxLineEdit* m_eOriginZ;
+
+ // Hitbox size
+ mxLineEdit* m_eSizeX;
+ mxLineEdit* m_eSizeY;
+ mxLineEdit* m_eSizeZ;
+
+ // Hitbox buttons
+ mxButton* m_bUpdateHitbox;
+ mxButton* m_bAddHitbox;
+ mxButton* m_bDeleteHitbox;
+
+ // The list of hitboxes per bone...
+ typedef CUtlVector< int > BoneHitboxList_t;
+ typedef CUtlVector< BoneHitboxList_t > BoneHitboxes_t;
+
+ CUtlVector< BoneHitboxes_t > m_SetBoneHitBoxes;
+
+ // Currently selected hitbox + bone
+ int m_Bone;
+ int m_Hitbox;
+ int m_nHitboxSet;
+};
+
+
+//-----------------------------------------------------------------------------
+// constructor
+//-----------------------------------------------------------------------------
+
+CBoneControlWindow::CBoneControlWindow( ControlPanel* pParent ) : mxWindow( pParent, 0, 0, 0, 0 )
+{
+ m_pControlPanel = pParent;
+
+ m_nHitboxSet = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Sets up all the controls in the window
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::Init( )
+{
+ int left, top;
+ left = 5;
+ top = 0;
+
+ // Bone selection list
+ new mxLabel (this, left + 3, top + 4, 30, 18, "Bone");
+ m_cBone = new mxChoice (this, left, top + 20, 260, 22, IDC_BONE_BONELIST);
+ m_cBone->add ("None");
+ m_cBone->select (0);
+ mxToolTip::add (m_cBone, "Select a bone to modify");
+
+ // Show bone checkbox
+ m_cBoneHighlight = new mxCheckBox (this, left, top + 45, 140, 20, "Highlight Bone", IDC_BONE_HIGHLIGHT_BONE);
+ mxToolTip::add (m_cBoneHighlight, "Toggle display of the bone being modified");
+
+ // Bone surface property selection
+ new mxLabel (this, left + 3, top + 68, 100, 18, "Bone Surface Prop");
+ m_cSurfaceProp = new mxChoice( this, left, top + 85, 140, 22, IDC_BONE_SURFACEPROP );
+ mxToolTip::add (m_cSurfaceProp, "Select a surface property to apply to the bone");
+
+ // FIXME: We can't read the surface props yet because the vphysics path isn't known!!!
+ // This will only add 'default' to the list
+ ReadPhysicsMaterials( m_cSurfaceProp );
+
+ // This button will apply the surface prop to all children
+ mxButton *btnApplyChild = new mxButton( this, left, top + 115, 140, 20, "Apply to Children", IDC_BONE_APPLY_TO_CHILDREN );
+ mxToolTip::add (btnApplyChild, "Apply the surface property to all child bones");
+
+ // Use autogenerated hitboxes
+ m_cAutoHitbox = new mxCheckBox (this, left, top + 140, 140, 20, "Autogenerate Hitboxes", IDC_BONE_USE_AUTOGENERATED_HITBOXES);
+ mxToolTip::add (m_cAutoHitbox, "When this is checked, studiomdl will automatically generate hitboxes");
+
+ m_cShowDefaultPose = new mxCheckBox (this, left, top + 160, 140, 20, "Show Default Pose", IDC_BONE_SHOW_DEFAULT_POSE);
+ mxToolTip::add (m_cShowDefaultPose, "Toggles display of the default physics pose");
+
+ left = 160;
+
+ new mxLabel( this, left + 3, top + 50, 80, 18, "Hitbox Set" );
+ m_cHitboxSet = new mxChoice (this, left, top + 66, 100, 22, IDC_BONE_HITBOXSET);
+ m_cHitboxSet->add ("unnamed");
+ m_cHitboxSet->select (0);
+ mxToolTip::add (m_cHitboxSet, "Change hitbox set");
+
+ m_eHitboxSetName = new mxLineEdit(this, left, top + 96, 100, 18, "", IDC_BONE_HITBOXSETNAME_EDIT);
+ mxToolTip::add (m_eHitboxSetName, "Type in a name and hit the set button");
+
+ m_bHitboxSetUpdateName = new mxButton( this, left, top + 116, 100, 16, "Set Name", IDC_BONE_HITBOXSETNAME );
+ mxToolTip::add (m_bHitboxSetUpdateName, "Press to set hitbox set name from above text field");
+
+ m_bAddHitboxSet = new mxButton( this, left, top + 140, 100, 20, "Add set", IDC_BONE_HITBOXADDSET );
+ mxToolTip::add (m_bAddHitboxSet, "Add a hitbox set");
+ m_bDeleteHitboxSet= new mxButton( this, left, top + 162, 100, 20, "Delete set", IDC_BONE_HITBOXDELETESET );
+ mxToolTip::add (m_bDeleteHitboxSet, "Remove a hitbox set");
+
+ left += 120;
+
+ // Hitbox selection list
+ new mxLabel (this, left + 3, top + 4, 30, 18, "Hitbox");
+ m_cHitbox = new mxChoice (this, left, top + 20, 100, 22, IDC_BONE_HITBOXLIST);
+ m_cHitbox->add ("None");
+ m_cHitbox->select (0);
+ mxToolTip::add (m_cHitbox, "Select a hitbox to modify");
+
+ // Show hitbox checkbox
+ m_cHitboxHighlight = new mxCheckBox (this, left + 3, top + 45, 100, 20, "Highlight Hitbox", IDC_BONE_HIGHLIGHT_HITBOX);
+ mxToolTip::add (m_cHitboxHighlight, "Toggle display of the hitbox being modified");
+
+ // Hitbox group
+ new mxLabel (this, left + 3, top + 80, 80, 18, "Hitbox Group");
+ m_eHitboxGroup = new mxLineEdit(this, left + 80, top + 75, 50, 22, "", IDC_BONE_HITBOX_GROUP);
+ mxToolTip::add (m_eHitboxGroup, "The group of the current hitbox");
+
+ // Hitbox name
+ new mxLabel (this, left + 133, top + 80, 80, 18, "Hitbox Name");
+ m_eHitboxName = new mxLineEdit(this, left + 133+80, top + 75, 50, 22, "", IDC_BONE_HITBOX_NAME);
+ m_eHitboxName->setEnabled(false);
+ mxToolTip::add (m_eHitboxName, "The name of the current hitbox");
+
+ // Hitbox origin
+ new mxLabel (this, left + 3, top + 110, 80, 18, "Hitbox Origin");
+ new mxLabel (this, left + 133, top + 110, 10, 18, "X");
+ new mxLabel (this, left + 198, top + 110, 10, 18, "Y");
+ new mxLabel (this, left + 263, top + 110, 10, 18, "Z");
+ m_eOriginX = new mxLineEdit(this, left + 80, top + 105, 50, 22, "", IDC_BONE_HITBOX_ORIGINX);
+ m_eOriginY = new mxLineEdit(this, left + 145, top + 105, 50, 22, "", IDC_BONE_HITBOX_ORIGINY);
+ m_eOriginZ = new mxLineEdit(this, left + 210, top + 105, 50, 22, "", IDC_BONE_HITBOX_ORIGINZ);
+
+ // Hitbox size
+ new mxLabel (this, left + 3, top + 140, 80, 18, "Hitbox Size");
+ new mxLabel (this, left + 133, top + 140, 10, 18, "X");
+ new mxLabel (this, left + 198, top + 140, 10, 18, "Y");
+ new mxLabel (this, left + 263, top + 140, 10, 18, "Z");
+ m_eSizeX = new mxLineEdit(this, left + 80, top + 135, 50, 22, "", IDC_BONE_HITBOX_SIZEX);
+ m_eSizeY = new mxLineEdit(this, left + 145, top + 135, 50, 22, "", IDC_BONE_HITBOX_SIZEY);
+ m_eSizeZ = new mxLineEdit(this, left + 210, top + 135, 50, 22, "", IDC_BONE_HITBOX_SIZEZ);
+
+ // Update hitboxes here
+ m_bUpdateHitbox = new mxButton( this, left, top + 163, 100, 20, "Update Hitbox", IDC_BONE_UPDATE_HITBOX );
+ mxToolTip::add (m_bUpdateHitbox, "Apply hitbox group, origin, and size to the hitbox");
+
+ left += 160;
+
+ // Generate a QC file
+ mxButton* btnGenerateQC = new mxButton (this, left, top + 10, 100, 20, "Generate QC", IDC_BONE_GENERATEQC );
+ mxToolTip::add (btnGenerateQC, "Copy a .qc file snippet to the clipboard");
+
+ // Add, remove hitboxes here
+ m_bAddHitbox = new mxButton( this, left, top + 30, 100, 20, "Add Hitbox", IDC_BONE_ADD_HITBOX );
+ m_bDeleteHitbox = new mxButton( this, left, top + 50, 100, 20, "Delete Hitbox", IDC_BONE_DELETE_HITBOX );
+ mxToolTip::add (m_bAddHitbox, "Create a new hitbox attached to the current bone");
+ mxToolTip::add (m_bDeleteHitbox, "Delete the currently selected hitbox");
+
+ OnAutogenerateHitboxes( false );
+}
+
+//-----------------------------------------------------------------------------
+// Generates a list of all hitboxes per bone
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::PopulateHitboxLists()
+{
+ // Find the surface prop associated with this bone
+ if (!g_pStudioModel)
+ return;
+
+ m_SetBoneHitBoxes.RemoveAll();
+ for (int set = 0; set < g_pStudioModel->m_HitboxSets.Count(); set++ )
+ {
+ m_SetBoneHitBoxes.AddToTail();
+
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ set ].m_Hitboxes;
+ for ( unsigned short i = list.Head(); i != list.InvalidIndex(); i = list.Next(i) )
+ {
+ mstudiobbox_t* pHitbox = &list[i].m_BBox;
+ m_SetBoneHitBoxes[ set ].EnsureCount( pHitbox->bone + 1 );
+ m_SetBoneHitBoxes[ set ][ pHitbox->bone ].AddToTail( i );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// This gets called when the model is loaded and unloaded
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnLoadModel()
+{
+ // Now that the vphysics path is known, we can load the surface props
+ ReadPhysicsMaterials( m_cSurfaceProp );
+
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ m_cAutoHitbox->setChecked( (pHdr->flags() & STUDIOHDR_FLAGS_AUTOGENERATED_HITBOX) != 0 );
+ OnAutogenerateHitboxes( m_cAutoHitbox->isChecked() );
+
+ // Determine all bones for this model
+ PopulateBoneList( m_cBone );
+
+ ComputeHitboxSetList();
+ PopulateHitboxLists();
+
+ OnBoneSelected( 0 );
+ g_bDrawModelInfoValid = false;
+}
+
+void CBoneControlWindow::OnUnloadModel()
+{
+ m_cBone->removeAll();
+ m_cBone->add ("None");
+ m_cBone->select (0);
+
+ m_cAutoHitbox->setChecked( false );
+}
+
+//-----------------------------------------------------------------------------
+// Called when we're selected
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnTabSelected()
+{
+ // Make the selected bone and highlight state match
+ OnBoneSelected( m_cBone->getSelectedIndex() );
+ OnBoneHighlighted( m_cBoneHighlight->isChecked() );
+ OnHitboxHighlighted( m_cHitboxHighlight->isChecked() );
+ OnShowDefaultPose( m_cShowDefaultPose->isChecked() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CBoneControlWindow::GetHitboxSet( void )
+{
+ return m_nHitboxSet;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::ComputeHitboxSetList( void )
+{
+ m_cHitboxSet->removeAll();
+
+ for ( int i = 0 ; i < g_pStudioModel->m_HitboxSets.Count(); i++ )
+ {
+ const char *name = g_pStudioModel->m_HitboxSets[ i ].m_Name;
+ m_cHitboxSet->add( name );
+ }
+ m_cHitboxSet->select( 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Recomputes the hitbox list
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::ComputeHitboxList( )
+{
+ // Reset the hitbox list
+ m_cHitbox->removeAll();
+ int count = 0;
+ if ( m_SetBoneHitBoxes.Count() > 0 )
+ {
+ if (m_SetBoneHitBoxes[ m_nHitboxSet ].Count() > m_Bone)
+ count = m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Count();
+ if (count > 0)
+ {
+ for (int i = 0; i < count; ++i )
+ {
+ char buf[32];
+ sprintf( buf, "%d", m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone][i] );
+ m_cHitbox->add( buf );
+ }
+ }
+ else
+ {
+ m_cHitbox->add ("None");
+ }
+ }
+ else
+ {
+ m_cHitbox->add ("None");
+ }
+
+ OnHitboxSelected(0);
+}
+
+//-----------------------------------------------------------------------------
+// Here's what we do when a new bone is selected
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnBoneSelected( int boneIndex )
+{
+ // Reset the surface prop
+ if (!g_pStudioModel)
+ return;
+
+ if ( physprop && g_pStudioModel->m_SurfaceProps.Count() )
+ {
+ // Find the surface prop associated with this bone
+ const char* pSurfaceProp = g_pStudioModel->m_SurfaceProps[boneIndex].String();
+ int idx = FindSurfaceProp( pSurfaceProp );
+
+ // Can't find it? Then apply the default one
+ if (idx < 0)
+ idx = FindSurfaceProp( "default" );
+ if (idx < 0)
+ idx = 0;
+ m_cSurfaceProp->select(idx);
+ }
+ else
+ {
+ m_cSurfaceProp->select(0);
+ }
+
+ // Store off the bone
+ m_Bone = boneIndex;
+ if (m_cBoneHighlight->isChecked())
+ g_viewerSettings.highlightBone = m_Bone;
+
+ ComputeHitboxList();
+
+ // select the render/highlight bone
+ m_pControlPanel->redraw();
+}
+
+
+//-----------------------------------------------------------------------------
+// When the bone is highlighted or not
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnShowDefaultPose( bool isChecked )
+{
+ g_viewerSettings.showPhysicsPreview = isChecked;
+}
+
+//-----------------------------------------------------------------------------
+// When the bone is highlighted or not
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnBoneHighlighted( bool isChecked )
+{
+ g_viewerSettings.highlightBone = isChecked ? m_Bone : -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// When the hitbox is highlighted or not
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxHighlighted( bool isChecked )
+{
+ g_viewerSettings.highlightHitbox = isChecked ? m_Hitbox : -1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Refreshes the hitbox size
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::RefreshHitbox( )
+{
+ if ( m_nHitboxSet < 0 || m_nHitboxSet >= g_pStudioModel->m_HitboxSets.Count() )
+ {
+ m_nHitboxSet = 0;
+ }
+
+ if ( m_Hitbox >= 0 && g_pStudioModel->m_HitboxSets.Count() > 0 )
+ {
+ // Set the hitbox size + origin + group
+ mstudiobbox_t* pHitbox = &g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes[ m_Hitbox ].m_BBox;
+
+ Vector origin, size;
+ VectorSubtract( pHitbox->bbmax, pHitbox->bbmin, size );
+ VectorAdd( pHitbox->bbmax, pHitbox->bbmin, origin );
+ origin *= 0.5f;
+
+ m_eHitboxGroup->setLabel( "%i", pHitbox->group );
+ const char *hitboxname = g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes[ m_Hitbox ].m_Name;
+ m_eHitboxName->setLabel( hitboxname );
+
+ m_eOriginX->setLabel("%.3f", origin.x );
+ m_eOriginY->setLabel("%.3f", origin.y );
+ m_eOriginZ->setLabel("%.3f", origin.z );
+ m_eSizeX->setLabel("%.3f", size.x );
+ m_eSizeY->setLabel("%.3f", size.y );
+ m_eSizeZ->setLabel("%.3f", size.z );
+ }
+ else
+ {
+ m_eHitboxGroup->setLabel( "" );
+ m_eHitboxName->setLabel( "" );
+ m_eOriginX->setLabel("");
+ m_eOriginY->setLabel("");
+ m_eOriginZ->setLabel("");
+ m_eSizeX->setLabel("");
+ m_eSizeY->setLabel("");
+ m_eSizeZ->setLabel("");
+ }
+}
+
+//-----------------------------------------------------------------------------
+// When a hitbox is selected
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnHitboxSelected( int hitbox )
+{
+ if ( m_nHitboxSet < 0 || m_nHitboxSet >= g_pStudioModel->m_HitboxSets.Size() )
+ {
+ m_nHitboxSet = 0;
+ }
+
+ m_cHitbox->select(hitbox);
+
+ if ( m_SetBoneHitBoxes.Count() == 0 ||
+ (m_SetBoneHitBoxes[ m_nHitboxSet ].Count() <= m_Bone) ||
+ (m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Count() <= hitbox))
+ {
+ m_Hitbox = -1;
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+
+ // This'll cause no boxes to be drawn
+ if (m_cHitboxHighlight->isChecked())
+ g_viewerSettings.highlightHitbox = pHdr ? pHdr->numbones() + 1 : 1;
+ }
+ else
+ {
+ m_Hitbox = m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone][hitbox];
+ if (m_cHitboxHighlight->isChecked())
+ g_viewerSettings.highlightHitbox = m_Hitbox;
+ }
+
+ RefreshHitbox();
+}
+
+
+//-----------------------------------------------------------------------------
+// Hitbox size/origin changed
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnAutogenerateHitboxes( bool isChecked )
+{
+ m_eHitboxGroup->setEnabled( !isChecked );
+ m_eHitboxName->setEnabled( !isChecked );
+ m_eOriginX->setEnabled( !isChecked );
+ m_eOriginY->setEnabled( !isChecked );
+ m_eOriginZ->setEnabled( !isChecked );
+ m_eSizeX->setEnabled( !isChecked );
+ m_eSizeY->setEnabled( !isChecked );
+ m_eSizeZ->setEnabled( !isChecked );
+ m_bUpdateHitbox->setEnabled( !isChecked );
+ m_bAddHitbox->setEnabled( !isChecked );
+ m_bDeleteHitbox->setEnabled( !isChecked );
+
+ m_cHitboxSet->setEnabled( !isChecked );
+ m_eHitboxSetName->setEnabled( !isChecked );
+ m_bHitboxSetUpdateName->setEnabled( !isChecked );
+ m_bAddHitboxSet->setEnabled( !isChecked );
+ m_bDeleteHitboxSet->setEnabled( !isChecked );
+}
+
+//-----------------------------------------------------------------------------
+// Hitbox size/origin changed
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnHitboxChanged( )
+{
+ if ( m_nHitboxSet < 0 || m_nHitboxSet >= g_pStudioModel->m_HitboxSets.Count() )
+ {
+ m_nHitboxSet = 0;
+ }
+
+ if ( m_Hitbox < 0 || g_pStudioModel->m_HitboxSets.Count() <= 0 )
+ {
+ // Blat out the hitbox size + origin + group
+ RefreshHitbox();
+ return;
+ }
+
+ Vector size, origin;
+ mstudiobbox_t* pHitbox;
+ const char *pGroup;
+
+ // Gotta do it this way since getLabel whacks the previous return result to getLable
+ const char* pLabel = m_eOriginX->getLabel();
+ if (!pLabel)
+ goto errOut;
+ origin.x = atof( pLabel );
+
+ pLabel = m_eOriginY->getLabel();
+ if (!pLabel)
+ goto errOut;
+ origin.y = atof( pLabel );
+
+ pLabel = m_eOriginZ->getLabel();
+ if (!pLabel)
+ goto errOut;
+ origin.z = atof( pLabel );
+
+ pLabel = m_eSizeX->getLabel();
+ if (!pLabel)
+ goto errOut;
+ size.x = atof( pLabel );
+
+ pLabel = m_eSizeY->getLabel();
+ if (!pLabel)
+ goto errOut;
+ size.y = atof( pLabel );
+
+ pLabel = m_eSizeZ->getLabel();
+ if (!pLabel)
+ goto errOut;
+ size.z = atof( pLabel );
+
+ pHitbox = &g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes[m_Hitbox].m_BBox;
+
+ // Recompute the hitbox from the new data
+ VectorMA( origin, -0.5f, size, pHitbox->bbmin );
+ VectorMA( origin, 0.5f, size, pHitbox->bbmax );
+
+ pGroup = m_eHitboxGroup->getLabel();
+ if (pGroup)
+ {
+ pHitbox->group = atol( pGroup );
+ }
+
+ // Store off the hitbox name
+ g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes[m_Hitbox].m_Name = m_eHitboxName->getLabel();
+
+errOut:
+ RefreshHitbox();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Hitbox set changed
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxSetChanged( void )
+{
+ m_Hitbox = 0;
+ m_nHitboxSet = m_cHitboxSet->getSelectedIndex();
+
+ PopulateHitboxLists();
+
+ // Repopulate other controls
+ ComputeHitboxList();
+ RefreshHitbox();
+ //OnHitboxGroupChanged( ); // Refresh hitbox will update the group label from the hitbox's group. No need to turn that back into a group #
+}
+
+
+//-----------------------------------------------------------------------------
+// Hitbox size/origin changed
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxGroupChanged( )
+{
+ if ( m_Hitbox >= 0 && g_pStudioModel->m_HitboxSets.Count() > 0 )
+ {
+ const char *pGroup = m_eHitboxGroup->getLabel();
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes;
+ if ( pGroup && list.IsInList( m_Hitbox ) )
+ {
+ mstudiobbox_t* pHitbox = &list[m_Hitbox].m_BBox;
+ pHitbox->group = atol( pGroup );
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Add, remove hitboxes
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnAddHitbox( )
+{
+ if (!g_pStudioModel)
+ return;
+
+ // Remove the 'none' entry
+ if (m_SetBoneHitBoxes[ m_nHitboxSet ].Count() > 0 &&
+ m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Count() == 0)
+ {
+ m_cHitbox->removeAll();
+ }
+
+ int i = g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes.AddToTail();
+ HitboxInfo_t &hitbox = g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes[i];
+ hitbox.m_BBox.bone = m_Bone;
+ hitbox.m_BBox.group = 0;
+ hitbox.m_BBox.bbmin.Init( -8, -8, -8 );
+ hitbox.m_BBox.bbmax.Init( 8, 8, 8 );
+ hitbox.m_BBox.szhitboxnameindex = 0;
+
+ m_SetBoneHitBoxes[ m_nHitboxSet ].EnsureCount( m_Bone + 1 );
+ m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].AddToTail(i);
+
+ char buf[32];
+ sprintf(buf, "%d", i );
+ m_cHitbox->add ( buf );
+ OnHitboxSelected(m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Count() - 1);
+}
+
+void CBoneControlWindow::OnDeleteHitbox( )
+{
+ if ( !g_pStudioModel || ( m_Hitbox < 0 ) )
+ return;
+
+ g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Hitboxes.Remove(m_Hitbox);
+ for (int i = m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Count(); --i >= 0; )
+ {
+ if (m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone][i] == m_Hitbox)
+ {
+ m_SetBoneHitBoxes[ m_nHitboxSet ][m_Bone].Remove(i);
+ break;
+ }
+ }
+
+ // Recompute the list of hitboxes
+ ComputeHitboxList();
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets the surface property
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnSurfaceProp( int propIndex )
+{
+ if (g_pStudioModel->IsModelLoaded())
+ {
+ // Store off the new surface prop symbol
+ CUtlSymbol prop( physprop->GetPropName( propIndex ) );
+ g_pStudioModel->m_SurfaceProps[m_Bone] = prop;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Duplictes the surface property to all children
+//-----------------------------------------------------------------------------
+
+void CBoneControlWindow::OnSurfacePropApplyToChildren_R( int bone, CUtlSymbol prop )
+{
+ g_pStudioModel->m_SurfaceProps[bone] = prop;
+
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ for ( int i = 0; i < pHdr->numbones(); i++ )
+ {
+ mstudiobone_t* pBone = pHdr->pBone(i);
+ if (pBone->parent == bone)
+ {
+ OnSurfacePropApplyToChildren_R( i, prop );
+ }
+ }
+}
+
+void CBoneControlWindow::OnSurfacePropApplyToChildren( )
+{
+ if (!g_pStudioModel)
+ return;
+
+ CUtlSymbol prop = g_pStudioModel->m_SurfaceProps[m_Bone];
+ OnSurfacePropApplyToChildren_R( m_Bone, prop );
+}
+
+//-----------------------------------------------------------------------------
+// Writes out qc-style text to a utlbuffer
+//-----------------------------------------------------------------------------
+
+bool CBoneControlWindow::SerializeQC( CUtlBuffer& buf )
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (!hdr)
+ return false;
+
+ buf.Printf("// .qc block generated by HLMV begins.\n\n");
+
+ // Print out the surface props
+ buf.Printf( "$surfaceprop \"%s\"\n", g_pStudioModel->m_SurfaceProps[0].String() );
+ Assert( g_pStudioModel->m_SurfaceProps.Count() == hdr->numbones() );
+
+ int i;
+ for ( i = 1; i < g_pStudioModel->m_SurfaceProps.Count(); ++i)
+ {
+ mstudiobone_t* pBone = hdr->pBone(i);
+
+ // Don't bother printing out the name if it's got the same
+ // surface prop as the parent does
+ if (pBone->parent >= 0)
+ {
+ if (!stricmp( g_pStudioModel->m_SurfaceProps[i].String(),
+ g_pStudioModel->m_SurfaceProps[pBone->parent].String() ))
+ continue;
+ }
+
+ buf.Printf( "$jointsurfaceprop \"%s\"\t \"%s\"\n", pBone->pszName(),
+ g_pStudioModel->m_SurfaceProps[i].String() );
+ }
+
+ if (!m_cAutoHitbox->isChecked())
+ {
+ buf.Printf("\n");
+
+ for ( i = 0 ; i < g_pStudioModel->m_HitboxSets.Count(); i++ )
+ {
+ buf.Printf( "\n$hboxset \"%s\"\n\n", g_pStudioModel->m_HitboxSets[ i ].m_Name.Get() );
+
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ i ].m_Hitboxes;
+ for ( unsigned short j = list.Head(); j != list.InvalidIndex(); j = list.Next(j) )
+ {
+ mstudiobbox_t &hitbox = list[j].m_BBox;
+ mstudiobone_t* pBone = hdr->pBone( hitbox.bone );
+ buf.Printf( "$hbox %d \"%s\"\t %7.2f %7.2f %7.2f %7.2f %7.2f %7.2f",
+ hitbox.group, pBone->pszName(),
+ hitbox.bbmin.x, hitbox.bbmin.y, hitbox.bbmin.z,
+ hitbox.bbmax.x, hitbox.bbmax.y, hitbox.bbmax.z );
+ if ( !list[j].m_Name.IsEmpty() )
+ {
+ buf.Printf( " \"%s\"", list[j].m_Name.Get() );
+ }
+ buf.Printf( "\n" );
+ }
+ }
+ }
+
+ buf.Printf("\n// .qc block generated by HLMV ends.\n\n");
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Generates the QC file and copies it to the clipboard
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnGenerateQC( )
+{
+ CUtlBuffer outbuf( 0, 0, CUtlBuffer::TEXT_BUFFER );
+ SerializeQC( outbuf );
+ if ( outbuf.TellPut() )
+ {
+ // Null-terminate the string so CopyString works...
+ outbuf.PutChar('\0');
+ Sys_CopyStringToClipboard( (const char*)outbuf.Base() );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxAddSet( void )
+{
+ char sz[ 32 ];
+ sprintf( sz, "set%02i", g_pStudioModel->m_HitboxSets.Count() + 1 );
+
+ int newsetnumber = g_pStudioModel->m_HitboxSets.AddToTail();
+ g_pStudioModel->m_HitboxSets[ newsetnumber ].m_Name = sz;
+
+ ComputeHitboxSetList();
+
+ m_cHitboxSet->select( newsetnumber );
+
+ OnHitboxSetChanged();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxDeleteSet( void )
+{
+ // Can't remove last element
+ if ( m_nHitboxSet == 0 )
+ {
+ return;
+ }
+
+ g_pStudioModel->m_HitboxSets.Remove( m_nHitboxSet );
+
+ ComputeHitboxSetList();
+
+ m_cHitboxSet->select( 0 );
+
+ OnHitboxSetChanged();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CBoneControlWindow::OnHitboxSetChangeName( void )
+{
+ if ( g_pStudioModel->m_HitboxSets.Count() <= 0 )
+ return;
+
+ char newname[ 512 ];
+
+ strcpy( newname, m_eHitboxSetName->getLabel() );
+ if ( !newname[ 0 ] )
+ return;
+
+ g_pStudioModel->m_HitboxSets[ m_nHitboxSet ].m_Name = newname;
+
+ int oldsel = m_nHitboxSet;
+
+ ComputeHitboxSetList();
+
+ m_cHitboxSet->select( oldsel );
+
+ OnHitboxSetChanged();
+}
+
+//-----------------------------------------------------------------------------
+// Responds to events on controls in the window
+//-----------------------------------------------------------------------------
+
+int CBoneControlWindow::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( event->event == mxEvent::KeyDown )
+ {
+ // FIXME: calling this forces the edit box to lose position
+ // OnHitboxChanged( );
+ return 1;
+ }
+
+ switch( event->action )
+ {
+ case IDC_BONE_BONELIST:
+ if (g_pStudioModel->IsModelLoaded())
+ {
+ OnHitboxGroupChanged( );
+ OnBoneSelected( m_cBone->getSelectedIndex() );
+ }
+ break;
+
+ case IDC_BONE_HIGHLIGHT_BONE:
+ OnBoneHighlighted( ((mxCheckBox *) event->widget)->isChecked() );
+ break;
+
+ case IDC_BONE_HIGHLIGHT_HITBOX:
+ OnHitboxHighlighted( ((mxCheckBox *) event->widget)->isChecked() );
+ break;
+
+ case IDC_BONE_SHOW_DEFAULT_POSE:
+ OnShowDefaultPose( ((mxCheckBox *) event->widget)->isChecked() );
+ break;
+
+ case IDC_BONE_HITBOXLIST:
+ OnHitboxGroupChanged( );
+ OnHitboxSelected( m_cHitbox->getSelectedIndex() );
+ break;
+
+ case IDC_BONE_HITBOXSET:
+ OnHitboxSetChanged();
+ break;
+
+ case IDC_BONE_HITBOXADDSET:
+ OnHitboxAddSet();
+ break;
+ case IDC_BONE_HITBOXDELETESET:
+ OnHitboxDeleteSet();
+ break;
+ case IDC_BONE_HITBOXSETNAME:
+ OnHitboxSetChangeName();
+ break;
+
+ case IDC_BONE_UPDATE_HITBOX:
+ OnHitboxChanged( );
+ break;
+
+ case IDC_BONE_ADD_HITBOX:
+ OnHitboxGroupChanged( );
+ OnAddHitbox( );
+ break;
+
+ case IDC_BONE_DELETE_HITBOX:
+ OnDeleteHitbox( );
+ break;
+
+ case IDC_BONE_USE_AUTOGENERATED_HITBOXES:
+ if (g_pStudioModel->IsModelLoaded())
+ OnAutogenerateHitboxes( m_cAutoHitbox->isChecked() );
+ break;
+
+ case IDC_BONE_SURFACEPROP:
+ OnSurfaceProp( m_cSurfaceProp->getSelectedIndex() );
+ break;
+
+ case IDC_BONE_APPLY_TO_CHILDREN:
+ if (g_pStudioModel->IsModelLoaded())
+ OnSurfacePropApplyToChildren( );
+ break;
+
+ case IDC_BONE_GENERATEQC:
+ if (g_pStudioModel->IsModelLoaded())
+ {
+ OnHitboxGroupChanged( );
+ OnGenerateQC( );
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+
+//-----------------------------------------------------------------------------
+// Singleton instance
+//-----------------------------------------------------------------------------
+
+ControlPanel *g_ControlPanel = 0;
+
+
+
+ControlPanel::ControlPanel( mxWindow *parent )
+: mxWindow( parent, 0, 0, 0, 0, "Control Panel", mxWindow::Normal )
+{
+ InitViewerSettings ( "hlmv" );
+
+ // frame slider
+ new mxLabel( this, 5, 220, 50, 18, "Frame" );
+ slForceFrame = new mxSlider( this, 55, 220, 450, 18, IDC_FORCEFRAME );
+ slForceFrame->setRange( 0, 1.0 );
+ slForceFrame->setValue( 0.0 );
+ slForceFrame->setSteps( 1, 1 );
+ mxToolTip::add( slForceFrame, "Force To Frame" );
+ lForcedFrame = new mxLabel( this, 505, 220, 30, 18, "0" );
+
+ // create tabcontrol with subdialog windows
+ tab = new mxTab( this, 0, 20, 0, 0, IDC_TAB );
+#ifdef WIN32
+ SetWindowLong( ( HWND )tab->getHandle(), GWL_EXSTYLE, WS_EX_CLIENTEDGE );
+#endif
+
+ SetupRenderWindow( tab );
+ SetupSequenceWindow( tab );
+ SetupBodyWindow( tab );
+ SetupFlexWindow( tab );
+ SetupPhysicsWindow( tab );
+ SetupBoneControlWindow( tab );
+ SetupAttachmentsWindow( tab );
+ SetupIKRuleWindow( tab );
+ SetupEventWindow( tab );
+
+ g_ControlPanel = this;
+
+ iSelectionToSequence = NULL;
+ iSequenceToSelection = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with render control
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupRenderWindow( mxTab* pTab )
+{
+ wRender = new mxWindow (this, 0, 0, 0, 0);
+ tab->add (wRender, "Render");
+ cRenderMode = new mxChoice (wRender, 5, 2, 100, 22, IDC_RENDERMODE);
+ cRenderMode->add ("Wireframe");
+// cRenderMode->add ("Flatshaded");
+ cRenderMode->add ("Smoothshaded");
+ cRenderMode->add ("Textured");
+ cRenderMode->add ("BoneWeights");
+ cRenderMode->add ("BadVertexData");
+ cRenderMode->add ("UV Chart");
+ cRenderMode->select (2);
+ mxToolTip::add (cRenderMode, "Select Render Mode");
+ cbGround = new mxCheckBox (wRender, 125, 5, 150, 20, "Ground (Ctrl-G)", IDC_GROUND);
+ cbGround->setEnabled( true );
+ cbMovement = new mxCheckBox (wRender, 125, 25, 150, 20, "Movement (Ctrl-M)", IDC_MOVEMENT);
+ cbMovement->setEnabled( true );
+ cbBackground = new mxCheckBox (wRender, 125, 45, 150, 20, "Background (Ctrl-B)", IDC_BACKGROUND);
+ cbBackground->setEnabled( true );
+ cbHitBoxes = new mxCheckBox (wRender, 125, 65, 150, 20, "Hit Boxes (Ctrl-H)", IDC_HITBOXES);
+ cbSequenceBoxes = new mxCheckBox (wRender, 125, 85, 150, 20, "Seq. Boxes", IDC_SEQUENCEBOXES);
+ cbShadow = new mxCheckBox (wRender, 125, 105, 150, 20, "Shadow (Ctrl-S)", IDC_SHADOW);
+ cbSoftwareSkin = new mxCheckBox (wRender, 125, 125, 150, 20, "Software Skin", IDC_SOFTWARESKIN);
+ cbSoftwareSkin->setEnabled( true );
+ cbOverbright2 = new mxCheckBox (wRender, 125, 145, 150, 20, "Enable Overbrightening", IDC_OVERBRIGHT2);
+ cbOverbright2->setEnabled( true );
+ cbOverbright2->setChecked( true );
+ setOverbright( true );
+
+ cbAttachments = new mxCheckBox (wRender, 5, 45, 120, 20, "Attachments (Ctrl-A)", IDC_ATTACHMENTS);
+ cbAttachments->setEnabled( true );
+
+ cbBones = new mxCheckBox (wRender, 5, 65, 120, 20, "Bones (Ctrl-O)", IDC_BONES);
+
+ cbNormals = new mxCheckBox (wRender, 5, 85, 120, 20, "Normals (Ctrl-N)", IDC_NORMALS);
+ cbNormals->setEnabled( true );
+ cbNormals->setChecked( false );
+
+ cbTangentFrame = new mxCheckBox( wRender, 5, 105, 120, 20, "Tangents (Ctrl-T)", IDC_TANGENTFRAME );
+ cbTangentFrame->setEnabled( true );
+ cbTangentFrame->setChecked( false );
+
+ cbOverlayWireframe = new mxCheckBox (wRender, 5, 125, 120, 20, "Wireframe (Ctrl-W)", IDC_OVERLAY_WIREFRAME);
+ cbOverlayWireframe->setEnabled( true );
+ cbOverlayWireframe->setChecked( false );
+
+// cbParallaxMap = new mxCheckBox (wRender, 5, 125, 100, 20, "Parallax Mapping", IDC_PARALLAXMAP);
+// cbParallaxMap->setEnabled( true );
+// cbParallaxMap->setChecked( true );
+
+ cbSpecular = new mxCheckBox (wRender, 5, 145, 120, 20, "Specular", IDC_SPECULAR);
+ cbSpecular->setEnabled( true );
+ cbSpecular->setChecked( true );
+
+ cbNormalMap = new mxCheckBox (wRender, 5, 25, 100, 20, "Normal Mapping", IDC_NORMALMAP);
+ cbNormalMap->setEnabled( true );
+ cbNormalMap->setChecked( true );
+
+ cbRunIK = new mxCheckBox (wRender, 275, 65, 150, 20, "Enable IK", IDC_RUNIK);
+ cbEnableHead = new mxCheckBox (wRender, 275, 85, 150, 20, "Head Turn", IDC_HEADTURN);
+
+ cbIllumPosition = new mxCheckBox (wRender, 275, 105, 150, 20, "Illum. Position", IDC_ILLUMPOSITION);
+
+ cbPlaySounds = new mxCheckBox (wRender, 275, 125, 150, 20, "Play Sounds", IDC_PLAYSOUNDS);
+
+ cbShowOriginAxis = new mxCheckBox (wRender, 275, 145, 150, 20, "Show Origin Axis", IDC_SHOWORIGINAXIS);
+
+ new mxLabel (wRender, 275, 170, 45, 18, "Axis Len:");
+ leOriginAxisLength = new mxSlider(wRender, 320, 165, 105, 22, IDC_ORIGINAXISLENGTH);
+ leOriginAxisLength->setRange( 1, 100 );
+ leOriginAxisLength->setValue( 10 );
+
+ new mxCheckBox (wRender, 275, 5, 150, 20, "Physics Model", IDC_PHYSICSMODEL);
+ cHighlightBone = new mxChoice (wRender, 275, 25, 150, 22, IDC_PHYSICSHIGHLIGHT);
+ cHighlightBone->add ("None");
+ cHighlightBone->select (0);
+ mxToolTip::add (cHighlightBone, "Select Physics Bone to highlight");
+
+ new mxLabel (wRender, 5, 170, 30, 18, "FOV:");
+ leFOV = new mxLineEdit(wRender, 35, 165, 30, 22, "65", IDC_RENDER_FOV);
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with sequence control
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupSequenceWindow( mxTab* pTab )
+{
+ mxWindow *wSequence = new mxWindow (this, 0, 0, 0, 0);
+ tab->add (wSequence, "Sequence");
+
+ for ( int i = 0; i < MAX_SEQUENCES; i++ )
+ {
+ cSequence[i] = new mxChoice (wSequence, 5, 5 + i * 22, 200, 22, IDC_SEQUENCE0+i);
+ mxToolTip::add (cSequence[i], "Select Sequence");
+ slSequence[i] = new mxSlider (wSequence, 208, 5 + i * 22, 80, 18, IDC_SEQUENCESCALE0+i);
+ slSequence[i]->setRange (0, 1.0, 100);
+ slSequence[i]->setValue (0.0);
+
+ rbFrameSelection[i] = new mxRadioButton (wSequence, 300, 5 + i * 22, 35, 22, "", IDC_FRAMESELECTION0+i, i == 0);
+ }
+ slSequence[0]->setVisible( false );
+
+ laGroundSpeed = new mxLabel( wSequence, 208, 5, 80, 18, "" );
+
+ for ( int i = 0; i < NUM_POSEPARAMETERS; i++ )
+ {
+ int x, y;
+ x = 334;
+ y = 2 + (i % 8) * 17;
+
+ cPoseParameter[i] = new mxChoice (wSequence, 520, y, 96, 22, IDC_POSEPARAMETER+i);
+ cPoseParameter[i]->setVisible( false );
+
+ slPoseParameter[i] = new mxSlider (wSequence, x, y, 140, 16, IDC_POSEPARAMETER_SCALE+i);
+ slPoseParameter[i]->setRange (0.0, 1.0, 1000);
+ mxToolTip::add (slPoseParameter[i], "Parameter");
+ slPoseParameter[i]->setVisible( false );
+
+ lePoseParameter[i] = new mxLineEdit ( wSequence, x + 146, y, 40, 16, "X", IDC_POSEPARAMETER_VALUE+i );
+ lePoseParameter[i]->setVisible( false );
+ }
+
+ slSpeedScale = new mxSlider (wSequence, 5, 115, 200, 18, IDC_SPEEDSCALE);
+ slSpeedScale->setRange (0, 1.0, 100);
+ slSpeedScale->setValue (1.0);
+ mxToolTip::add (slSpeedScale, "Speed Scale");
+ laFPS = new mxLabel (wSequence, 208, 115, 128, 22, "" );
+
+ new mxCheckBox (wSequence, 5, 142, 150, 20, "Blend Sequence Changes", IDC_BLENDSEQUENCECHANGES);
+ new mxButton( wSequence, 155, 142, 80, 20, "Blend Now", IDC_BLENDNOW );
+ laBlendAmount = new mxLabel( wSequence, 240, 142, 60, 20, "" );
+
+ slBlendTime = new mxSlider( wSequence, 308, 142, 200, 18, IDC_BLENDTIME );
+ slBlendTime->setRange( 0, 1.0, 100 );
+ slBlendTime->setValue( DEFAULT_BLEND_TIME );
+ laBlendTime = new mxLabel( wSequence, 540, 142, 80, 22, "" );
+
+ new mxLabel (wSequence, 5, 170, 90, 18, "Activity modifiers:");
+ cActivityModifiers = new mxChoice (wSequence, 105, 166, 350, 22, IDC_ACTIVITY_MODIFIERS);
+
+ new mxCheckBox (wSequence, 460, 166, 350, 22, "Animate weapons", IDC_ANIMATEWEAPONS);
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with body control
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupBodyWindow( mxTab* pTab )
+{
+ mxWindow *wBody = new mxWindow (this, 0, 0, 0, 0);
+ pTab->add (wBody, "Model");
+ cBodypart = new mxChoice (wBody, 5, 5, 100, 22, IDC_BODYPART);
+ mxToolTip::add (cBodypart, "Choose a bodypart");
+ cSubmodel = new mxChoice (wBody, 110, 5, 100, 22, IDC_SUBMODEL);
+ mxToolTip::add (cSubmodel, "Choose a submodel of current bodypart");
+ cController = new mxChoice (wBody, 5, 30, 100, 22, IDC_CONTROLLER);
+ mxToolTip::add (cController, "Choose a bone controller");
+ slController = new mxSlider (wBody, 105, 32, 100, 18, IDC_CONTROLLERVALUE);
+ slController->setRange (0, 255);
+ mxToolTip::add (slController, "Change current bone controller value");
+ lModelInfo1 = new mxLabel (wBody, 220, 5, 120, 80, "No Model.");
+ lModelInfo2 = new mxLabel (wBody, 340, 5, 120, 130, "");
+ cSkin = new mxChoice (wBody, 5, 55, 100, 22, IDC_SKINS);
+ mxToolTip::add (cSkin, "Choose a skin family");
+ new mxLabel (wBody, 5, 170, 90, 18, "Materials used:");
+ cMaterials = new mxChoice (wBody, 105, 166, 350, 22, IDC_MATERIALS);
+ mxToolTip::add (cMaterials, "Select material for UV Chart view");
+
+ lModelInfo3 = new mxLabel (wBody, 220, 100, 220, 18, "");
+ lModelInfo4 = new mxLabel (wBody, 220, 118, 260, 18, "");
+ lModelInfo5 = new mxLabel (wBody, 220, 136, 120, 18, "");
+ setTransparent( false );
+
+ cbAutoLOD = new mxCheckBox (wBody, 5, 80, 100, 20, "Auto LOD", IDC_AUTOLOD);
+ cbAutoLOD->setEnabled( true );
+ cLODChoice = new mxChoice( wBody, 5, 101, 100, 22, IDC_LODCHOICE);
+ mxToolTip::add (cLODChoice, "Select model LOD to render");
+ new mxLabel (wBody, 5, 126, 60, 18, "LOD Switch:");
+ leLODSwitch = new mxLineEdit(wBody, 70, 126, 35, 22, "", IDC_LODSWITCH);
+ new mxLabel (wBody, 5, 151, 60, 18, "LOD Metric:" );
+ lLODMetric = new mxLabel( wBody, 70, 151, 35, 22, "" );
+
+ new mxLabel( wBody, 505, 5, 100, 18, "VMTs Loaded:" );
+ cMessageList = new mxListBox( wBody, 500, 25, 540, 160, IDC_MESSAGES );
+ cMessageList->add ("None");
+ cMessageList->select (1);
+ mxToolTip::add (cMessageList, "Materials (VMT files) this model has loaded");
+
+ new mxLabel( wBody, 785, 5, 100, 18, "Shader:" );
+ cShaderUsed = new mxListBox( wBody, 830, 3, 210, 28, IDC_SHADERS );
+ cShaderUsed->add ("Select material to show shader");
+ cShaderUsed->select (0);
+ mxToolTip::add (cShaderUsed, "Shader Used");
+
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with flexes
+//-----------------------------------------------------------------------------
+
+#define FLEX_DROPDOWN_WIDTH 90
+#define FLEX_SLIDER_WIDTH 100
+#define FLEX_ROWS_OF_SLIDERS 8
+
+void ControlPanel::SetupFlexWindow( mxTab* pTab )
+{
+ mxWindow *wFlex = new mxWindow (this, 0, 0, 0, 0);
+ pTab->add (wFlex, "Flex");
+ for (int i = 0; i < NUM_FLEX_SLIDERS; i++)
+ {
+ int w = (i / FLEX_ROWS_OF_SLIDERS) * (FLEX_SLIDER_WIDTH + FLEX_DROPDOWN_WIDTH + 4) + 5;
+ int h = (i % FLEX_ROWS_OF_SLIDERS) * 20 + 5;
+
+ cFlex[i] = new mxChoice (wFlex, w, h, FLEX_DROPDOWN_WIDTH, 22, IDC_FLEX + i);
+ mxToolTip::add (cFlex[i], "Select Flex");
+ slFlexScale[i] = new mxSlider (wFlex, w + FLEX_DROPDOWN_WIDTH, h, FLEX_SLIDER_WIDTH, 18, IDC_FLEXSCALE + i);
+ slFlexScale[i]->setRange (0, 1.0);
+ slFlexScale[i]->setValue (0);
+ mxToolTip::add (slFlexScale[i], "Flex Scale");
+ }
+
+ {
+ int h = FLEX_ROWS_OF_SLIDERS * 20 + 5;
+ new mxButton( wFlex, 5, h + 1, FLEX_DROPDOWN_WIDTH - 2, 18, "Reset", IDC_FLEXDEFAULTS );
+
+ int w = ( FLEX_DROPDOWN_WIDTH + 4 ) + 5;
+ new mxButton( wFlex, w, h + 1, FLEX_DROPDOWN_WIDTH - 2, 18, "Random", IDC_FLEXRANDOM );
+
+ w += ( FLEX_DROPDOWN_WIDTH + 4 ) + 5;
+ new mxButton( wFlex, w, h + 1, FLEX_DROPDOWN_WIDTH - 2, 18, "Zero", IDC_FLEXZERO );
+
+ w += ( FLEX_DROPDOWN_WIDTH + 4 ) + 5;
+ new mxButton( wFlex, w, h + 1, FLEX_DROPDOWN_WIDTH - 2, 18, "One", IDC_FLEXONE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with physics
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupPhysicsWindow( mxTab* pTab )
+{
+ // Physics Window
+ mxWindow *wPhysics = new mxWindow (this, 0, 0, 0, 0);
+ pTab->add (wPhysics, "Physics");
+
+ new mxLabel (wPhysics, 5, 33, 30, 18, "Mass");
+ leMass = new mxLineEdit(wPhysics, 35, 30, 70, 22, "", IDC_PHYS_MASS);
+
+ cPhysicsBone = new mxChoice (wPhysics, 5, 5, 345, 22, IDC_PHYS_BONE);
+ cPhysicsBone->add ("None");
+ cPhysicsBone->select (0);
+
+ new mxCheckBox (wPhysics, 5, 55, 100, 20, "Highlight", IDC_PHYSICSMODEL);
+ int x = 5;
+ int y = 80;
+
+ rbConstraintAxis[0] = new mxRadioButton (wPhysics, x, y, 35, 22, "X", IDC_PHYS_CON_AXIS_X, true);
+ rbConstraintAxis[1] = new mxRadioButton (wPhysics, x+35, y, 35, 22, "Y", IDC_PHYS_CON_AXIS_Y);
+ rbConstraintAxis[2] = new mxRadioButton (wPhysics, x+70, y, 35, 22, "Z", IDC_PHYS_CON_AXIS_Z);
+ setPhysicsAxis( 0 );
+ y += 25;
+
+ new mxLabel (wPhysics, x, y, 45, 18, "Friction");
+ slPhysicsFriction = new mxSlider (wPhysics, x+50, y, 180, 18, IDC_PHYS_CON_FRICTION);
+ slPhysicsFriction->setRange (0, 1000.0, 1000);
+ slPhysicsFriction->setValue (0);
+ slPhysicsFriction->setSteps( 1, 1 );
+ lPhysicsFriction = new mxLabel (wPhysics, x+230, y, 30, 18, "0");
+
+ x = 135;
+ y = 30;
+
+ new mxLabel (wPhysics, 115, y, 30, 18, "Min");
+ slPhysicsConMin = new mxSlider (wPhysics, x, y, 180, 18, IDC_PHYS_CON_MIN);
+ slPhysicsConMin->setRange (-180, 180.0, 360);
+ slPhysicsConMin->setValue (-90);
+ slPhysicsConMin->setSteps( 1, 1 );
+ lPhysicsConMin = new mxLabel (wPhysics, x+180, y, 30, 18, "-90");
+ y += 25;
+
+ new mxLabel (wPhysics, 115, y, 30, 18, "Max");
+ slPhysicsConMax = new mxSlider (wPhysics, x, y, 180, 18, IDC_PHYS_CON_MAX);
+ slPhysicsConMax->setRange (-180.0, 180.0, 360);
+ slPhysicsConMax->setValue (90.0);
+ slPhysicsConMax->setSteps( 1, 1 );
+ lPhysicsConMax = new mxLabel (wPhysics, x+180, y, 30, 18, "90");
+ y += 25;
+
+ new mxLabel (wPhysics, 115, y, 30, 18, "Test");
+ slPhysicsConTest = new mxSlider (wPhysics, x, y, 55, 18, IDC_PHYS_CON_TEST);
+ slPhysicsConTest->setRange (0, 1.0, 100);
+ slPhysicsConTest->setValue (0);
+
+ cbLinked = new mxCheckBox (wPhysics, 200, y, 50, 20, "Link", IDC_PHYS_CON_LINK_LIMITS);
+ mxToolTip::add (cbLinked, "Link mins/maxs to be symmetric");
+ new mxButton (wPhysics, 250, y, 80, 22, "Generate QC", IDC_PHYS_QCFILE);
+
+ x += 220;
+ y = 5;
+ new mxLabel (wPhysics, x, y, 55, 18, "Mass Bias");
+ new mxLabel (wPhysics, x, y+25, 55, 18, "Inertia");
+ new mxLabel (wPhysics, x, y+50, 55, 18, "Damping");
+ new mxLabel (wPhysics, x, y+75, 55, 18, "Rot Damp");
+ new mxLabel (wPhysics, x, y+100+3, 55, 18, "Material");
+ x += 55;
+
+ slPhysicsParamMassBias = new mxSlider (wPhysics, x, y, 76, 18, IDC_PHYS_P_MASSBIAS );
+ slPhysicsParamMassBias->setRange (0, 10.0, 100);
+ slPhysicsParamMassBias->setValue (1.0);
+ slPhysicsParamMassBias->setSteps( 1, 1 );
+ lPhysicsParamMassBias = new mxLabel (wPhysics, x+80, y, 30, 18, "1.0");
+ y += 25;
+
+ slPhysicsParamInertia = new mxSlider (wPhysics, x, y, 76, 18, IDC_PHYS_P_INERTIA );
+ slPhysicsParamInertia->setRange (0, 10.0, 100);
+ slPhysicsParamInertia->setValue (1.0);
+ slPhysicsParamInertia->setSteps( 1, 1 );
+ lPhysicsParamInertia = new mxLabel (wPhysics, x+80, y, 30, 18, "1.0");
+ y += 25;
+
+ slPhysicsParamDamping = new mxSlider (wPhysics, x, y, 76, 18, IDC_PHYS_P_DAMPING );
+ slPhysicsParamDamping->setRange (0, 1.0, 100);
+ slPhysicsParamDamping->setValue (0.01);
+ slPhysicsParamDamping->setSteps( 1, 1 );
+ lPhysicsParamDamping = new mxLabel (wPhysics, x+80, y, 30, 18, "0.5");
+ y += 25;
+
+ slPhysicsParamRotDamping = new mxSlider (wPhysics, x, y, 76, 18, IDC_PHYS_P_ROT_DAMPING );
+ slPhysicsParamRotDamping->setRange (0, 10.0, 200);
+ slPhysicsParamRotDamping->setValue (0.2);
+ slPhysicsParamRotDamping->setSteps( 1, 1 );
+ lPhysicsParamRotDamping = new mxLabel (wPhysics, x+80, y, 30, 18, "0.2");
+ y += 25;
+
+ lPhysicsMaterial = new mxLabel( wPhysics, x, y+3, 110, 18, "default" );
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with ik rules
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupIKRuleWindow( mxTab *pTab )
+{
+ mxWindow *wIKRule = new mxWindow( this, 0, 0, 0, 0 );
+ pTab->add( wIKRule, "IKRule" );
+
+ new mxLabel( wIKRule, 5, 5, 80, 20, "Chain:" );
+ cIKChain = new mxChoice( wIKRule, 90, 5, 50, 20, IDC_IKRULE_CHAIN );
+ cIKChain->add( "lfoot" );
+ cIKChain->add( "rfoot" );
+ cIKChain->add( "lhand" );
+ cIKChain->add( "rhand" );
+ cIKChain->select( 0 );
+ mxToolTip::add( cIKChain, "Select IK Chain" );
+
+ new mxLabel( wIKRule, 145, 5, 80, 20, "Type:" );
+ cIKType = new mxChoice( wIKRule, 230, 5, 80, 20, IDC_IKRULE_CHOICE );
+ cIKType->add( "footstep" );
+ cIKType->add( "touch" );
+ cIKType->add( "release" );
+ cIKType->add( "attachment" );
+ cIKType->add( "unlatch" );
+ cIKType->select( 0 );
+ mxToolTip::add( cIKType, "Select IK Type" );
+
+ lIKTouch = new mxLabel( wIKRule, 315, 5, 80, 20, "Bone:" );
+ cIKTouch = new mxChoice( wIKRule, 400, 5, 200, 20, IDC_IKRULE_TOUCH );
+ PopulateBoneList( cIKTouch );
+ mxToolTip::add( cIKTouch, "Select Touched Bone" );
+
+ lIKAttachment = new mxLabel( wIKRule, 315, 5, 80, 20, "Attachment:" );
+ leIKAttachment = new mxLineEdit( wIKRule, 400, 5, 80, 20, "", IDC_IKRULE_ATTACHMENT );
+
+ cbIKRangeToggle = new mxCheckBox( wIKRule, 5, 30, 80, 20, "Range", IDC_IKRULE_RANGE_TOGGLE );
+ mxToolTip::add( cbIKRangeToggle, "Toggle range option" );
+ new mxButton( wIKRule, 90, 30, 30, 20, "start", IDC_IKRULE_RANGE_START_NOW );
+ leIKRangeStart = new mxLineEdit2( wIKRule, 120, 30, 35, 20, "..", IDC_IKRULE_RANGE_START );
+ new mxButton( wIKRule, 160, 30, 30, 20, "peak", IDC_IKRULE_RANGE_PEAK_NOW );
+ leIKRangePeak = new mxLineEdit2( wIKRule, 190, 30, 35, 20, "..", IDC_IKRULE_RANGE_PEAK );
+ new mxButton( wIKRule, 230, 30, 30, 20, "tail", IDC_IKRULE_RANGE_TAIL_NOW );
+ leIKRangeTail = new mxLineEdit2( wIKRule, 260, 30, 35, 20, "..", IDC_IKRULE_RANGE_TAIL );
+ new mxButton( wIKRule, 300, 30, 30, 20, "end", IDC_IKRULE_RANGE_END_NOW );
+ leIKRangeEnd = new mxLineEdit2( wIKRule, 330, 30, 35, 20, "..", IDC_IKRULE_RANGE_END );
+
+ cbIKContactToggle = new mxCheckBox( wIKRule, 5, 55, 80, 20, "Contact", IDC_IKRULE_CONTACT_TOGGLE );
+ mxToolTip::add( cbIKContactToggle, "Toggle contact option" );
+ new mxButton( wIKRule, 90, 55, 30, 20, "frame", IDC_IKRULE_CONTACT_FRAME_NOW );
+ leIKContactFrame = new mxLineEdit2( wIKRule, 120, 55, 35, 20, "", IDC_IKRULE_CONTACT_FRAME );
+
+ new mxLabel( wIKRule, 5, 80, 80, 20, "Transform using:" );
+ cIKUsing = new mxChoice( wIKRule, 90, 80, 80, 20, IDC_IKRULE_USING );
+ cIKUsing->add( "neither" );
+ cIKUsing->add( "source" );
+ cIKUsing->add( "sequence" );
+ cIKUsing->select( 0 );
+ mxToolTip::add( cIKUsing, "Choose Transform To Use" );
+
+ new mxLabel( wIKRule, 5, 105, 80, 20, "QC String:" );
+ leIKQCString = new mxLineEdit2( wIKRule, 90, 105, 500, 20, "", IDC_IKRULE_QC_STRING );
+ UpdateIKRuleWindow();
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with events
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupEventWindow( mxTab *pTab )
+{
+ mxWindow *wEvents = new mxWindow( this, 0, 0, 0, 0 );
+ pTab->add( wEvents, "Events" );
+
+ new mxLabel( wEvents, 5, 5, 80, 20, "Sound:" );
+ mxButton *bSoundFrameNow = new mxButton( wEvents, 90, 5, 30, 20, "frame", IDC_EVENT_SOUND_FRAME_NOW );
+ mxToolTip::add( bSoundFrameNow, "Set sound start at the current frame" );
+ leEventSoundFrame = new mxLineEdit2( wEvents, 120, 5, 35, 20, "0", IDC_EVENT_SOUND_FRAME );
+ lbEventSoundName = new mxListBox( wEvents, 160, 5, 300, 170, IDC_EVENT_SOUND_NAME );
+ PopulateSoundNameList( lbEventSoundName );
+ mxToolTip::add( lbEventSoundName, "Select Sound Name" );
+
+ new mxLabel( wEvents, 5, 170, 80, 20, "QC String:" );
+ leEventQCString = new mxLineEdit2( wEvents, 90, 170, 450, 20, "", IDC_EVENT_QC_STRING );
+ BuildEventQCString();
+}
+
+//-----------------------------------------------------------------------------
+// Sets up the window dealing with bone control
+//-----------------------------------------------------------------------------
+
+void ControlPanel::SetupBoneControlWindow( mxTab* pTab )
+{
+ m_pBoneWindow = new CBoneControlWindow(this);
+ pTab->add (m_pBoneWindow, "Bones");
+ m_pBoneWindow->Init();
+}
+
+
+void ControlPanel::SetupAttachmentsWindow( mxTab *pTab )
+{
+ m_pAttachmentsWindow = new CAttachmentsWindow(this);
+ pTab->add( m_pAttachmentsWindow, "Attachments" );
+ m_pAttachmentsWindow->Init();
+}
+
+
+int ControlPanel::GetCurrentHitboxSet( void )
+{
+ return m_pBoneWindow ? m_pBoneWindow->GetHitboxSet() : 0;
+}
+
+ControlPanel::~ControlPanel()
+{
+ g_ControlPanel = NULL;
+}
+
+void ControlPanel::OnDelete()
+{
+ // for some reason, the destructor only gets called from mx when breakpoints are set,
+ // so to be safe, clear the pointer (possibly twice)
+ g_ControlPanel = NULL;
+}
+
+void ControlPanel::BuildEventQCString()
+{
+ if ( g_ControlPanel == NULL )
+ return;
+
+ char qcstr[ 256 ];
+ Q_strcpy( qcstr, "{ event AE_CL_PLAYSOUND " );
+ Q_strcat( qcstr, leEventSoundFrame->getLabel(), sizeof(qcstr) );
+ Q_strcat( qcstr, " \"", sizeof(qcstr) );
+ int i = lbEventSoundName->getSelectedIndex();
+ Q_strcat( qcstr, lbEventSoundName->getItemText( i ), sizeof(qcstr) );
+ Q_strcat( qcstr, "\" }", sizeof(qcstr) );
+
+ leEventQCString->setText( qcstr );
+}
+
+void ControlPanel::BuildIKRuleQCString()
+{
+ if ( g_ControlPanel == NULL )
+ return;
+
+ char qcstr[ 256 ];
+ Q_strcpy( qcstr, "ikrule " );
+ Q_strcat( qcstr, cIKChain->getLabel(), sizeof(qcstr) );
+ Q_strcat( qcstr, " ", sizeof(qcstr) );
+ const char *pType = cIKType->getLabel();
+ Q_strcat( qcstr, pType, sizeof(qcstr) );
+
+ if ( Q_strcmp( pType, "touch" ) == 0 )
+ {
+ Q_strcat( qcstr, " \"", sizeof(qcstr) );
+ if ( cIKTouch->getSelectedIndex() > 0 )
+ {
+ Q_strcat( qcstr, cIKTouch->getLabel(), sizeof(qcstr) );
+ }
+ Q_strcat( qcstr, "\"", sizeof(qcstr) );
+ }
+ else if ( Q_strcmp( pType, "attachment" ) == 0 )
+ {
+ Q_strcat( qcstr, " \"", sizeof(qcstr) );
+ Q_strcat( qcstr, leIKAttachment->getLabel(), sizeof(qcstr) );
+ Q_strcat( qcstr, "\"", sizeof(qcstr) );
+ }
+
+ if ( cbIKRangeToggle->isChecked() )
+ {
+ Q_strcat( qcstr, " range ", sizeof(qcstr) );
+
+ char str[ 20 ];
+ leIKRangeStart->getText( str, sizeof( str ) );
+ Q_strcat( qcstr, str, sizeof(qcstr) );
+ Q_strcat( qcstr, " ", sizeof(qcstr) );
+ leIKRangePeak->getText( str, sizeof( str ) );
+ Q_strcat( qcstr, str, sizeof(qcstr) );
+ Q_strcat( qcstr, " ", sizeof(qcstr) );
+ leIKRangeTail->getText( str, sizeof( str ) );
+ Q_strcat( qcstr, str, sizeof(qcstr) );
+ Q_strcat( qcstr, " ", sizeof(qcstr) );
+ leIKRangeEnd->getText( str, sizeof( str ) );
+ Q_strcat( qcstr, str, sizeof(qcstr) );
+ }
+
+ if ( cbIKContactToggle->isChecked() )
+ {
+ Q_strcat( qcstr, " contact ", sizeof(qcstr) );
+
+ char str[ 20 ];
+ leIKContactFrame->getText( str, sizeof( str ) );
+ Q_strcat( qcstr, str, sizeof(qcstr) );
+ }
+
+ int nUsing = cIKUsing->getSelectedIndex();
+ if ( nUsing > 0 )
+ {
+ if ( nUsing == 1 ) // source
+ {
+ Q_strcat( qcstr, " usesource", sizeof(qcstr) );
+ }
+ else if ( nUsing == 2 ) // sequence
+ {
+ Q_strcat( qcstr, " usesequence", sizeof(qcstr) );
+ }
+ }
+
+ leIKQCString->setText( qcstr );
+}
+
+void ControlPanel::UpdateIKRuleWindow()
+{
+ const char *pIKType = cIKType->getLabel();
+ bool bIsTouch = Q_strcmp( pIKType, "touch" ) == 0;
+ bool bIsAttachment = Q_strcmp( pIKType, "attachment" ) == 0;
+
+ lIKTouch->setVisible( bIsTouch );
+ cIKTouch->setVisible( bIsTouch );
+ lIKAttachment->setVisible( bIsAttachment );
+ leIKAttachment->setVisible( bIsAttachment );
+
+ BuildIKRuleQCString();
+}
+
+int
+ControlPanel::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( !g_ControlPanel )
+ return 1;
+
+ if ( event->event == mxEvent::Size )
+ {
+ tab->setBounds( 0, 0, event->width, max( 0, event->height - 20 ) );
+ return 1;
+ }
+
+ if ( event->event == mxEvent::KeyDown )
+ {
+ if ( tab->getSelectedIndex() == 4 )
+ {
+ handlePhysicsKey( event );
+ }
+
+ if (event->action >= IDC_POSEPARAMETER_VALUE && event->action < IDC_POSEPARAMETER_VALUE + NUM_POSEPARAMETERS)
+ {
+ int index = event->action - IDC_POSEPARAMETER_VALUE;
+ int poseparam = cPoseParameter[index]->getSelectedIndex();
+
+ float value = atof( lePoseParameter[index]->getLabel() );
+ setBlend( poseparam, value );
+ slPoseParameter[index]->setValue( g_pStudioModel->GetPoseParameter( poseparam ) );
+ return 1;
+ }
+
+ switch (event->key)
+ {
+ case 27:
+ if (!getParent ()) // fullscreen mode ?
+ mx::quit ();
+ break;
+
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ g_viewerSettings.renderMode = event->key - '1';
+ break;
+
+ case '-':
+ g_viewerSettings.speedScale -= 0.1f;
+ if (g_viewerSettings.speedScale < 0.0f)
+ g_viewerSettings.speedScale = 0.0f;
+ break;
+
+ case '+':
+ g_viewerSettings.speedScale += 0.1f;
+ if (g_viewerSettings.speedScale > 5.0f)
+ g_viewerSettings.speedScale = 5.0f;
+ break;
+ default:
+ return 0;
+ }
+
+ return 1;
+ }
+
+ switch (event->action)
+ {
+ case IDC_TAB:
+ {
+ int tabIndex = tab->getSelectedIndex();
+
+ // g_viewerSettings.highlightBone = -1;
+ g_viewerSettings.highlightHitbox = -1;
+ g_viewerSettings.showTexture = (tabIndex == 3) ? true : false;
+ g_viewerSettings.showPhysicsPreview = (tabIndex == 4) ? true : false;
+ setHighlightBone(cHighlightBone->getSelectedIndex());
+
+ if (tabIndex == 4)
+ {
+ setupPhysicsBone(cPhysicsBone->getSelectedIndex());
+ }
+
+ if (tabIndex == 5)
+ {
+ m_pBoneWindow->OnTabSelected();
+ }
+
+ if ( tabIndex == 6 )
+ {
+ m_pAttachmentsWindow->OnTabSelected();
+ }
+ else
+ {
+ m_pAttachmentsWindow->OnTabUnselected();
+ }
+ }
+ break;
+
+ case IDC_RENDERMODE:
+ {
+ int index = cRenderMode->getSelectedIndex();
+ if (index >= 0)
+ {
+ setRenderMode (index);
+ }
+ }
+ break;
+
+ case IDC_PHYSICSHIGHLIGHT:
+ {
+ int index = cHighlightBone->getSelectedIndex();
+ setHighlightBone(index);
+ }
+ break;
+
+ case IDC_RENDER_FOV:
+ {
+ mxLineEdit *pLineEdit = ((mxLineEdit *) event->widget);
+ const char *pText = pLineEdit->getLabel();
+ float val = atof( pText );
+ g_viewerSettings.fov = val;
+ break;
+ }
+
+ case IDC_ORIGINAXISLENGTH:
+ {
+ g_viewerSettings.originAxisLength = reinterpret_cast< mxSlider * >( event->widget )->getValue();
+ break;
+ }
+
+ case IDC_LODCHOICE:
+ {
+ int index = cLODChoice->getSelectedIndex();
+ if( index >= 0 )
+ {
+ setLOD( index, false, false );
+ }
+ break;
+ }
+
+ case IDC_LODSWITCH:
+ {
+ mxLineEdit *pLineEdit = ((mxLineEdit *) event->widget);
+ const char *pText = pLineEdit->getLabel();
+ float val = atof( pText );
+ g_pStudioModel->SetLODSwitchValue( g_viewerSettings.lod, val );
+ break;
+ }
+
+ case IDC_AUTOLOD:
+ setAutoLOD (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_SOFTWARESKIN:
+ setSoftwareSkin(((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_OVERBRIGHT2:
+ setOverbright(((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_GROUND:
+ setShowGround (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_MOVEMENT:
+ setShowMovement (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_BACKGROUND:
+ setShowBackground (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_HITBOXES:
+ g_viewerSettings.showHitBoxes = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_SEQUENCEBOXES:
+ g_viewerSettings.showSequenceBoxes = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_SHADOW:
+ g_viewerSettings.showShadow = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_ILLUMPOSITION:
+ g_viewerSettings.showIllumPosition = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_RUNIK:
+ g_viewerSettings.enableIK = ((mxCheckBox *) event->widget)->isChecked();
+ g_viewerSettings.enableTargetIK = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_HEADTURN:
+ g_pStudioModel->SetSolveHeadTurn( ((mxCheckBox *) event->widget)->isChecked() ? 1 : 0 );
+ break;
+
+ case IDC_PHYSICSMODEL:
+ g_viewerSettings.showPhysicsModel = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_BONES:
+ g_viewerSettings.showBones = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_PLAYSOUNDS:
+ g_viewerSettings.playSounds = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_NORMALMAP:
+ g_viewerSettings.enableNormalMapping = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+// case IDC_PARALLAXMAP:
+// g_viewerSettings.enableParallaxMapping = ((mxCheckBox *) event->widget)->isChecked();
+// break;
+
+ case IDC_SPECULAR:
+ g_viewerSettings.enableSpecular = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_NORMALS:
+ g_viewerSettings.showNormals = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_TANGENTFRAME:
+ g_viewerSettings.showTangentFrame = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_OVERLAY_WIREFRAME:
+ g_viewerSettings.overlayWireframe = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_ATTACHMENTS:
+ g_viewerSettings.showAttachments = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+
+ case IDC_SHOWORIGINAXIS:
+ setShowOriginAxis (((mxCheckBox *) event->widget)->isChecked());
+ break;
+
+ case IDC_MESSAGES:
+ {
+ int index = cMessageList->getSelectedIndex();
+ if (index >= 0)
+ {
+ studiohdr_t* pStudioR = g_pStudioModel->GetStudioRenderHdr();
+ if ( pStudioR )
+ {
+ IMaterial *pMaterials[128];
+ g_pStudioRender->GetMaterialList( pStudioR, ARRAYSIZE( pMaterials ), &pMaterials[0] );
+
+ cShaderUsed->removeAll();
+ if ( pMaterials[index]->IsErrorMaterial() )
+ {
+ cShaderUsed->add( "*** ERROR *** Can't load VMT");
+ }
+ else
+ {
+ cShaderUsed->add( pMaterials[index]->GetShaderName() );
+ }
+ }
+ }
+ }
+ break;
+
+ case IDC_SEQUENCE0:
+ case IDC_SEQUENCE1:
+ case IDC_SEQUENCE2:
+ case IDC_SEQUENCE3:
+ case IDC_SEQUENCE4:
+ {
+ int i = event->action - IDC_SEQUENCE0;
+ int index = ((mxChoice *) event->widget)->getSelectedIndex();
+ if (index >= 0)
+ {
+ index = iSelectionToSequence[index];
+ if (i == 0)
+ {
+ setSequence (index);
+ showActivityModifiers( index );
+ }
+ else
+ {
+ setOverlaySequence( i, index, slSequence[i]->getValue() );
+ }
+ }
+ }
+ break;
+
+ case IDC_SEQUENCESCALE0:
+ case IDC_SEQUENCESCALE1:
+ case IDC_SEQUENCESCALE2:
+ case IDC_SEQUENCESCALE3:
+ case IDC_SEQUENCESCALE4:
+ {
+ int i = event->action - IDC_SEQUENCESCALE0;
+ int index = cSequence[i]->getSelectedIndex();
+ if (index >= 0)
+ {
+ index = iSelectionToSequence[index];
+ if (i == 0)
+ {
+ setSequence (index);
+ showActivityModifiers( index );
+ }
+ else
+ {
+ setOverlaySequence( i, index, (float)((mxSlider *) event->widget)->getValue() );
+ }
+ }
+ }
+ break;
+
+ case IDC_FRAMESELECTION0:
+ case IDC_FRAMESELECTION1:
+ case IDC_FRAMESELECTION2:
+ case IDC_FRAMESELECTION3:
+ case IDC_FRAMESELECTION4:
+ {
+ updateFrameSelection();
+ }
+ break;
+
+ case IDC_BLENDTIME:
+ {
+ char sz[ 256 ];
+ sprintf( sz, "%.2f s", (float)((mxSlider *) event->widget)->getValue() );
+
+ g_pStudioModel->SetBlendTime( ((mxSlider *) event->widget)->getValue() );
+ laBlendTime->setLabel( sz );
+ }
+ break;
+ case IDC_BLENDSEQUENCECHANGES:
+ g_viewerSettings.blendSequenceChanges = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+ case IDC_ANIMATEWEAPONS:
+ g_viewerSettings.animateWeapons = ((mxCheckBox *) event->widget)->isChecked();
+ break;
+ case IDC_BLENDNOW:
+ startBlending();
+ break;
+ case IDC_SPEEDSCALE:
+ {
+ setSpeedScale( ((mxSlider *) event->widget)->getValue() );
+ }
+ break;
+
+ case IDC_FORCEFRAME:
+ {
+ setFrame( ((mxSlider *) event->widget)->getValue() );
+
+ // stop the animation
+ setSpeedScale( 0 );
+
+ if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
+ {
+ CUtlBuffer cmd;
+ CUtlBuffer res;
+ cmd.Printf( "%s %f", "hlmvForceFrame", reinterpret_cast< mxSlider * >( event->widget )->getValue() );
+ g_HlmvIpcClient.ExecuteCommand( cmd, res );
+ g_HlmvIpcClient.Disconnect();
+ }
+ }
+ break;
+
+ case IDC_BODYPART:
+ {
+ int index = cBodypart->getSelectedIndex();
+ if (index >= 0)
+ {
+ g_pStudioModel->SetBodygroup( cBodypart->getSelectedIndex() );
+ setBodypart (index);
+ }
+ }
+ break;
+
+ case IDC_SUBMODEL:
+ {
+ int index = cSubmodel->getSelectedIndex();
+ if (index >= 0)
+ {
+ setSubmodel (index);
+ }
+ }
+ break;
+
+ case IDC_CONTROLLER:
+ {
+ int index = cController->getSelectedIndex();
+ if (index >= 0)
+ setBoneController (index);
+ }
+ break;
+
+ case IDC_CONTROLLERVALUE:
+ {
+ int index = cController->getSelectedIndex();
+ if (index >= 0)
+ {
+ float flValue = slController->getValue();
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(index);
+ if (pbonecontroller->end < pbonecontroller->start)
+ {
+ flValue *= -1.0f;
+ }
+ setBoneControllerValue(index, flValue);
+ }
+ }
+ break;
+
+ case IDC_SKINS:
+ {
+ int index = cSkin->getSelectedIndex();
+ if (index >= 0)
+ {
+ g_pStudioModel->SetSkin (index);
+ g_viewerSettings.skin = index;
+ d_MatSysWindow->redraw();
+ }
+ }
+ break;
+
+ case IDC_MATERIALS:
+ {
+ int index = cMaterials->getSelectedIndex();
+ if (index >= 0)
+ {
+ g_viewerSettings.materialIndex = index;
+ }
+ }
+ break;
+
+ case IDC_IKRULE_CHAIN:
+ case IDC_IKRULE_CHOICE:
+ case IDC_IKRULE_RANGE_TOGGLE:
+ case IDC_IKRULE_RANGE_START:
+ case IDC_IKRULE_RANGE_PEAK:
+ case IDC_IKRULE_RANGE_TAIL:
+ case IDC_IKRULE_RANGE_END:
+ case IDC_IKRULE_CONTACT_TOGGLE:
+ case IDC_IKRULE_CONTACT_FRAME:
+ case IDC_IKRULE_USING:
+ UpdateIKRuleWindow();
+ break;
+
+ case IDC_IKRULE_TOUCH:
+ case IDC_IKRULE_ATTACHMENT:
+ BuildIKRuleQCString();
+ break;
+
+ case IDC_IKRULE_RANGE_START_NOW:
+ SetFrameString( leIKRangeStart, getFrameSelection() );
+ break;
+
+ case IDC_IKRULE_RANGE_PEAK_NOW:
+ SetFrameString( leIKRangePeak, getFrameSelection() );
+ break;
+
+ case IDC_IKRULE_RANGE_TAIL_NOW:
+ SetFrameString( leIKRangeTail, getFrameSelection() );
+ break;
+
+ case IDC_IKRULE_RANGE_END_NOW:
+ SetFrameString( leIKRangeEnd, getFrameSelection() );
+ break;
+
+ case IDC_IKRULE_CONTACT_FRAME_NOW:
+ SetFrameString( leIKContactFrame, getFrameSelection() );
+ break;
+
+ case IDC_IKRULE_QC_STRING:
+ // ignore edits to the qc text box
+ break;
+
+ case IDC_EVENT_SOUND_FRAME:
+ case IDC_EVENT_SOUND_NAME:
+ BuildEventQCString();
+ break;
+
+ case IDC_EVENT_SOUND_FRAME_NOW:
+ SetFrameString( leEventSoundFrame, getFrameSelection() );
+ break;
+
+ case IDC_EVENT_QC_STRING:
+ // ignore edits to the qc text box
+ break;
+
+ default:
+ {
+ if ( event->action == IDC_FLEXDEFAULTS )
+ {
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ g_pStudioModel->SetFlexController( i, 0 );
+ }
+ for (int i = 0; i < NUM_FLEX_SLIDERS; i++)
+ {
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[i]->getSelectedIndex();
+ slFlexScale[ i ]->setValue( g_pStudioModel->GetFlexControllerRaw( index ) );
+ }
+ }
+ else if ( event->action == IDC_FLEXRANDOM )
+ {
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ float r = rand() / float( VALVE_RAND_MAX );
+ g_pStudioModel->SetFlexController( i, r );
+ }
+
+ for (int i = 0; i < NUM_FLEX_SLIDERS; i++)
+ {
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[i]->getSelectedIndex();
+ slFlexScale[ i ]->setValue( g_pStudioModel->GetFlexControllerRaw( index ) );
+ }
+ }
+ else if ( event->action == IDC_FLEXZERO )
+ {
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ g_pStudioModel->SetFlexController( i, 0 );
+ }
+
+ for (int i = 0; i < NUM_FLEX_SLIDERS; i++)
+ {
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[i]->getSelectedIndex();
+ slFlexScale[ i ]->setValue( g_pStudioModel->GetFlexControllerRaw( index ) );
+ }
+ }
+ else if ( event->action == IDC_FLEXONE )
+ {
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ g_pStudioModel->SetFlexController( i, 1 );
+ }
+
+ for (int i = 0; i < NUM_FLEX_SLIDERS; i++)
+ {
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[i]->getSelectedIndex();
+ slFlexScale[ i ]->setValue( g_pStudioModel->GetFlexControllerRaw( index ) );
+ }
+ }
+ else if (event->action >= IDC_FLEX && event->action < IDC_FLEX + NUM_FLEX_SLIDERS)
+ {
+ int flex = (event->action - IDC_FLEX);
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[flex]->getSelectedIndex();
+
+ if (index >= 0)
+ {
+ slFlexScale[flex]->setValue(g_pStudioModel->GetFlexControllerRaw(index));
+ }
+ }
+ else if (event->action >= IDC_FLEXSCALE && event->action < IDC_FLEXSCALE + NUM_FLEX_SLIDERS)
+ {
+ int flex = (event->action - IDC_FLEXSCALE);
+ LocalFlexController_t index = (LocalFlexController_t)cFlex[flex]->getSelectedIndex();
+
+ g_pStudioModel->SetFlexControllerRaw( index, ((mxSlider *) event->widget)->getValue() );
+ }
+ else if ( event->action >= IDC_PHYS_FIRST && event->action <= IDC_PHYS_LAST )
+ {
+ return handlePhysicsEvent( event );
+ }
+ else if (event->action >= IDC_POSEPARAMETER && event->action < IDC_POSEPARAMETER + NUM_POSEPARAMETERS)
+ {
+ int index = event->action - IDC_POSEPARAMETER;
+ int poseparam = cPoseParameter[index]->getSelectedIndex();
+
+ float flMin, flMax;
+ if (g_pStudioModel->GetPoseParameterRange( poseparam, &flMin, &flMax ))
+ {
+ slPoseParameter[index]->setRange( flMin, flMax, 1000 );
+ slPoseParameter[index]->setValue( g_pStudioModel->GetPoseParameter( poseparam ) );
+ lePoseParameter[index]->setLabel( "%.1f", g_pStudioModel->GetPoseParameter( poseparam ) );
+ }
+ }
+ else if (event->action >= IDC_POSEPARAMETER_SCALE && event->action < IDC_POSEPARAMETER_SCALE + NUM_POSEPARAMETERS)
+ {
+ int index = event->action - IDC_POSEPARAMETER_SCALE;
+ int poseparam = cPoseParameter[index]->getSelectedIndex();
+
+ setBlend( poseparam, ((mxSlider *) event->widget)->getValue() );
+ lePoseParameter[index]->setLabel( "%.1f", ((mxSlider *) event->widget)->getValue() );
+ }
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+
+void
+ControlPanel::dumpModelInfo()
+{
+#if 0
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ DeleteFile ("midump.txt");
+ FILE *file = fopen ("midump.txt", "wt");
+ if (file)
+ {
+ byte *phdr = (byte *) hdr;
+ int i;
+
+ fprintf (file, "id: %c%c%c%c\n", phdr[0], phdr[1], phdr[2], phdr[3]);
+ fprintf (file, "version: %d\n", hdr->version);
+ fprintf (file, "name: \"%s\"\n", hdr->name);
+ fprintf (file, "length: %d\n\n", hdr->length);
+
+ fprintf (file, "eyeposition: %f %f %f\n", hdr->eyeposition[0], hdr->eyeposition[1], hdr->eyeposition[2]);
+ fprintf (file, "min: %f %f %f\n", hdr->min[0], hdr->min[1], hdr->min[2]);
+ fprintf (file, "max: %f %f %f\n", hdr->max[0], hdr->max[1], hdr->max[2]);
+ fprintf (file, "bbmin: %f %f %f\n", hdr->bbmin[0], hdr->bbmin[1], hdr->bbmin[2]);
+ fprintf (file, "bbmax: %f %f %f\n", hdr->bbmax[0], hdr->bbmax[1], hdr->bbmax[2]);
+
+ fprintf (file, "flags: %d\n\n", hdr->flags);
+
+ fprintf (file, "numbones: %d\n", hdr->numbones);
+ for (i = 0; i < hdr->numbones; i++)
+ {
+ mstudiobone_t *pbones = (mstudiobone_t *) (phdr + hdr->boneindex);
+ fprintf (file, "\nbone %d.name: \"%s\"\n", i + 1, pbones[i].name);
+ fprintf (file, "bone %d.parent: %d\n", i + 1, pbones[i].parent);
+ fprintf (file, "bone %d.flags: %d\n", i + 1, pbones[i].flags);
+ fprintf (file, "bone %d.bonecontroller: %d %d %d %d %d %d\n", i + 1, pbones[i].bonecontroller[0], pbones[i].bonecontroller[1], pbones[i].bonecontroller[2], pbones[i].bonecontroller[3], pbones[i].bonecontroller[4], pbones[i].bonecontroller[5]);
+ fprintf (file, "bone %d.value: %f %f %f %f %f %f\n", i + 1, pbones[i].value[0], pbones[i].value[1], pbones[i].value[2], pbones[i].value[3], pbones[i].value[4], pbones[i].value[5]);
+ fprintf (file, "bone %d.scale: %f %f %f %f %f %f\n", i + 1, pbones[i].scale[0], pbones[i].scale[1], pbones[i].scale[2], pbones[i].scale[3], pbones[i].scale[4], pbones[i].scale[5]);
+ }
+
+ fprintf (file, "\nnumbonecontrollers: %d\n", hdr->numbonecontrollers);
+ for (i = 0; i < hdr->numbonecontrollers; i++)
+ {
+ mstudiobonecontroller_t *pbonecontrollers = (mstudiobonecontroller_t *) (phdr + hdr->bonecontrollerindex);
+ fprintf (file, "\nbonecontroller %d.bone: %d\n", i + 1, pbonecontrollers[i].bone);
+ fprintf (file, "bonecontroller %d.type: %d\n", i + 1, pbonecontrollers[i].type);
+ fprintf (file, "bonecontroller %d.start: %f\n", i + 1, pbonecontrollers[i].start);
+ fprintf (file, "bonecontroller %d.end: %f\n", i + 1, pbonecontrollers[i].end);
+ fprintf (file, "bonecontroller %d.rest: %d\n", i + 1, pbonecontrollers[i].rest);
+ fprintf (file, "bonecontroller %d.index: %d\n", i + 1, pbonecontrollers[i].index);
+ }
+
+ fprintf (file, "\nnumhitboxes: %d\n", hdr->numhitboxes);
+ for (i = 0; i < hdr->numhitboxes; i++)
+ {
+ mstudiobbox_t *pbboxes = (mstudiobbox_t *) (phdr + hdr->hitboxindex);
+ fprintf (file, "\nhitbox %d.bone: %d\n", i + 1, pbboxes[i].bone);
+ fprintf (file, "hitbox %d.group: %d\n", i + 1, pbboxes[i].group);
+ fprintf (file, "hitbox %d.bbmin: %f %f %f\n", i + 1, pbboxes[i].bbmin[0], pbboxes[i].bbmin[1], pbboxes[i].bbmin[2]);
+ fprintf (file, "hitbox %d.bbmax: %f %f %f\n", i + 1, pbboxes[i].bbmax[0], pbboxes[i].bbmax[1], pbboxes[i].bbmax[2]);
+ }
+
+ fprintf (file, "\nnumseq: %d\n", hdr->GetNumSeq());
+ for (i = 0; i < hdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t *pseqdescs = (mstudioseqdesc_t *) (phdr + hdr->seqindex);
+ fprintf (file, "\nseqdesc %d.label: \"%s\"\n", i + 1, pseqdescs[i].label);
+ fprintf (file, "seqdesc %d.fps: %f\n", i + 1, pseqdescs[i].fps);
+ fprintf (file, "seqdesc %d.flags: %d\n", i + 1, pseqdescs[i].flags);
+ fprintf (file, "<...>\n");
+ }
+/*
+ fprintf (file, "\nnumseqgroups: %d\n", hdr->GetNumSeq()groups);
+ for (i = 0; i < hdr->GetNumSeq()groups; i++)
+ {
+ mstudioseqgroup_t *pseqgroups = (mstudioseqgroup_t *) (phdr + hdr->seqgroupindex);
+ fprintf (file, "\nseqgroup %d.label: \"%s\"\n", i + 1, pseqgroups[i].label);
+ fprintf (file, "\nseqgroup %d.namel: \"%s\"\n", i + 1, pseqgroups[i].name);
+ fprintf (file, "\nseqgroup %d.data: %d\n", i + 1, pseqgroups[i].data);
+ }
+*/
+ hdr = g_pStudioModel->getTextureHeader();
+ fprintf (file, "\nnumtextures: %d\n", hdr->numtextures);
+ fprintf (file, "textureindex: %d\n", hdr->textureindex);
+ fprintf (file, "texturedataindex: %d\n", hdr->texturedataindex);
+ for (i = 0; i < hdr->numtextures; i++)
+ {
+ mstudiotexture_t *ptextures = (mstudiotexture_t *) ((byte *) hdr + hdr->textureindex);
+ fprintf (file, "\ntexture %d.name: \"%s\"\n", i + 1, ptextures[i].name);
+ fprintf (file, "texture %d.flags: %d\n", i + 1, ptextures[i].flags);
+ fprintf (file, "texture %d.width: %d\n", i + 1, ptextures[i].width);
+ fprintf (file, "texture %d.height: %d\n", i + 1, ptextures[i].height);
+ fprintf (file, "texture %d.index: %d\n", i + 1, ptextures[i].index);
+ }
+
+ hdr = g_pStudioModel->GetStudioHdr();
+ fprintf (file, "\nnumskinref: %d\n", hdr->numskinref);
+ fprintf (file, "numskinfamilies: %d\n", hdr->numskinfamilies);
+
+ fprintf (file, "\nnumbodyparts: %d\n", hdr->numbodyparts);
+ for (i = 0; i < hdr->numbodyparts; i++)
+ {
+ mstudiobodyparts_t *pbodyparts = (mstudiobodyparts_t *) ((byte *) hdr + hdr->bodypartindex);
+ fprintf (file, "\nbodypart %d.name: \"%s\"\n", i + 1, pbodyparts[i].name);
+ fprintf (file, "bodypart %d.nummodels: %d\n", i + 1, pbodyparts[i].nummodels);
+ fprintf (file, "bodypart %d.base: %d\n", i + 1, pbodyparts[i].base);
+ fprintf (file, "bodypart %d.modelindex: %d\n", i + 1, pbodyparts[i].modelindex);
+ }
+
+ fprintf (file, "\nnumattachments: %d\n", hdr->numattachments);
+ for (i = 0; i < hdr->numattachments; i++)
+ {
+ mstudioattachment_t *pattachments = (mstudioattachment_t *) ((byte *) hdr + hdr->attachmentindex);
+ fprintf (file, "attachment %d.name: \"%s\"\n", i + 1, pattachments[i].name);
+ }
+
+ fclose (file);
+
+ ShellExecute ((HWND) getHandle(), "open", "midump.txt", 0, 0, SW_SHOW);
+ }
+ }
+#endif
+}
+
+
+LoadModelResult_t ControlPanel::loadModel(const char *filename)
+{
+ SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel );
+
+ g_pStudioModel->FreeModel( false );
+
+ if (!g_pStudioModel->LoadModel( filename ))
+ {
+ return LoadModel_LoadFail;
+ }
+
+ if (!g_pStudioModel->PostLoadModel( filename ))
+ {
+ return LoadModel_PostLoadFail;
+ }
+
+ if (!g_pStudioModel->HasModel())
+ {
+ return LoadModel_NoModel;
+ }
+
+ OnLoadModel( );
+
+ return LoadModel_Success;
+}
+
+void ControlPanel::OnLoadModel( void )
+{
+ int i;
+ m_bVMTInfoLoaded = false;
+
+ if (!g_pStudioModel->HasModel())
+ return;
+
+ initSequenceChoices();
+ initBodypartChoices();
+ initBoneControllers();
+ initSkinChoices();
+ initMaterialChoices();
+ initPhysicsBones();
+ initLODs();
+ initFlexes();
+
+ setModelInfo();
+
+ UnloadAllMergedModels();
+
+ const bool bNoModelSettings = LoadViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel );
+ if ( !bNoModelSettings )
+ {
+ InitViewerSettings( "hlmv" );
+ setSequence( 0 );
+ showActivityModifiers( 0 );
+ setSpeedScale( 1.0 );
+ }
+
+ resetControlPanel();
+
+ /*!!
+ for (i = 0; i < 32; i++)
+ g_viewerSettings.submodels[i] = 0;
+ for (i = 0; i < 8; i++)
+ g_viewerSettings.controllers[i] = 0;
+ */
+
+ setupPhysics();
+ m_pBoneWindow->OnLoadModel();
+ m_pAttachmentsWindow->OnLoadModel();
+
+ PopulateBoneList( cIKTouch, true );
+
+ mx_setcwd (mx_getpath (g_pStudioModel->GetFileName()));
+
+ for ( i = 0; i < HLMV_MAX_MERGED_MODELS; ++i )
+ {
+ if ( strlen( g_viewerSettings.mergeModelFile[i] ) != 0 )
+ {
+ loadModel( g_viewerSettings.mergeModelFile[i], i );
+ }
+ }
+
+ // Center the model if we don't have last view position data in the registry
+ if ( !bNoModelSettings )
+ {
+ // Need to call this twice for some reason. Really - I don't have OCD!
+ centerView();
+ centerView();
+ }
+}
+
+
+LoadModelResult_t ControlPanel::loadModel(const char *filename, int slot )
+{
+ if (slot == -1)
+ {
+ return loadModel( filename );
+ }
+
+ if (g_pStudioExtraModel[slot] == NULL)
+ {
+ g_pStudioExtraModel[slot] = new StudioModel;
+ }
+ else
+ {
+ g_pStudioExtraModel[slot]->FreeModel( false );
+ }
+
+ if (g_pStudioExtraModel[slot]->LoadModel( filename ))
+ {
+ if (g_pStudioExtraModel[slot]->PostLoadModel( filename ))
+ {
+ connectFlexes( g_pStudioExtraModel[slot]->GetStudioHdr() );
+ if ( g_MDLViewer && g_MDLViewer->getMenuBar() )
+ {
+ g_MDLViewer->getMenuBar()->modify (IDC_FILE_UNLOADMERGEDMODEL1 + slot, IDC_FILE_UNLOADMERGEDMODEL1 + slot, filename);
+ g_MDLViewer->getMenuBar()->setEnabled (IDC_FILE_UNLOADMERGEDMODEL1 + slot, true);
+ }
+ return LoadModel_Success;
+ }
+ else
+ {
+ return LoadModel_PostLoadFail;
+ }
+ }
+
+ return LoadModel_LoadFail;
+}
+
+
+void
+ControlPanel::resetControlPanel( void )
+{
+ setSequence( g_pStudioModel->GetSequence() );
+ showActivityModifiers( g_pStudioModel->GetSequence() );
+ setOverlaySequence( 1, g_pStudioModel->GetOverlaySequence( 0 ), g_pStudioModel->GetOverlaySequenceWeight( 0 ) );
+ setOverlaySequence( 2, g_pStudioModel->GetOverlaySequence( 1 ), g_pStudioModel->GetOverlaySequenceWeight( 1 ) );
+ setOverlaySequence( 3, g_pStudioModel->GetOverlaySequence( 2 ), g_pStudioModel->GetOverlaySequenceWeight( 2 ) );
+ setOverlaySequence( 4, g_pStudioModel->GetOverlaySequence( 3 ), g_pStudioModel->GetOverlaySequenceWeight( 3 ) );
+
+ setSpeedScale( g_viewerSettings.speedScale );
+
+ cbGround->setChecked( g_viewerSettings.showGround );
+ cbMovement->setChecked( g_viewerSettings.showMovement );
+ cbShadow->setChecked( g_viewerSettings.showShadow );
+ cbNormalMap->setChecked( g_viewerSettings.enableNormalMapping );
+ cbIllumPosition->setChecked( g_viewerSettings.showIllumPosition );
+
+ cbHitBoxes->setChecked( g_viewerSettings.showHitBoxes );
+ cbBones->setChecked( g_viewerSettings.showBones );
+ cbPlaySounds->setChecked( g_viewerSettings.playSounds );
+ cbSequenceBoxes->setChecked( g_viewerSettings.showSequenceBoxes );
+ cbRunIK->setChecked( g_viewerSettings.enableIK );
+
+ cbBackground->setChecked( g_viewerSettings.showBackground );
+ cbSoftwareSkin->setChecked( g_viewerSettings.softwareSkin );
+ cbOverbright2->setChecked( g_viewerSettings.overbright );
+ cbAttachments->setChecked( g_viewerSettings.showAttachments );
+ cbNormals->setChecked( g_viewerSettings.showNormals );
+ cbEnableHead->setChecked( g_pStudioModel->GetSolveHeadTurn() ? 1 : 0 );
+
+ cbShowOriginAxis->setChecked( g_viewerSettings.showOriginAxis );
+ setOriginAxisLength( g_viewerSettings.originAxisLength );
+}
+
+
+void
+ControlPanel::setRenderMode (int mode)
+{
+ g_viewerSettings.renderMode = mode;
+ d_MatSysWindow->redraw();
+}
+
+
+void
+ControlPanel::setHighlightBone( int index )
+{
+ if ( index >= 0 )
+ {
+ g_viewerSettings.highlightPhysicsBone = index;
+ }
+}
+
+void
+ControlPanel::setLOD( int index, bool setLODchoice, bool force )
+{
+#if 1
+ if( !force && ( g_viewerSettings.lod == index ) )
+ {
+ return;
+ }
+#endif
+ g_viewerSettings.lod = index;
+ float lodSwitch = g_pStudioModel->GetLODSwitchValue( index );
+ char tmp[128];
+ sprintf( tmp, "%0.0f", lodSwitch );
+ leLODSwitch->setLabel( tmp );
+ HWND wnd = ( HWND )leLODSwitch->getHandle();
+ if( setLODchoice )
+ {
+ cLODChoice->select( index );
+ }
+ setModelInfo();
+ InvalidateRect( wnd, NULL, TRUE );
+ UpdateWindow( wnd );
+}
+
+void
+ControlPanel::setLODMetric( float metric )
+{
+ static int saveMetric = -10;
+ int intMetric = ( int )metric;
+ if( intMetric == saveMetric )
+ {
+ return;
+ }
+ saveMetric = intMetric;
+ char tmp[128];
+ sprintf( tmp, "%d", intMetric );
+ lLODMetric->setLabel( tmp );
+}
+
+void
+ControlPanel::setPolycount( int polycount )
+{
+ static int savePolycount = -10;
+ if( polycount == savePolycount )
+ {
+ return;
+ }
+ savePolycount = polycount;
+ char tmp[128];
+ sprintf( tmp, "Shader Draw Count: %d", polycount );
+ lModelInfo3->setLabel( tmp );
+}
+
+void ControlPanel::setModelInfo( int nVertCount, int nIndexCount, int nTriCount )
+{
+ static int nSaveVertCount = -10;
+ static int nSaveIndexCount = -10;
+ static int nSaveTriCount = -10;
+
+ if ( nVertCount == nSaveVertCount && nIndexCount == nSaveIndexCount && nTriCount == nSaveTriCount )
+ {
+ return;
+ }
+
+ nSaveVertCount = nVertCount;
+ nSaveIndexCount = nIndexCount;
+ nSaveTriCount = nTriCount;
+
+ char tmp[ 128 ];
+ sprintf( tmp, "Verts: %d Indexes: %d Triangles: %d", nVertCount, nIndexCount, nTriCount );
+ lModelInfo4->setLabel( tmp );
+}
+
+void
+ControlPanel::setTransparent( bool isTransparent )
+{
+ static int saveTransparent = -1;
+ if( (int)isTransparent == saveTransparent )
+ return;
+
+ saveTransparent = isTransparent;
+ char tmp[128];
+ sprintf( tmp, "Model is: %s", isTransparent ? "transparent" : "opaque" );
+ lModelInfo5->setLabel( tmp );
+}
+
+void
+ControlPanel::updatePoseParameters( )
+{
+ for (int i = 0; i < NUM_POSEPARAMETERS; i++)
+ {
+ if (slPoseParameter[i]->isEnabled())
+ {
+ int j = cPoseParameter[i]->getSelectedIndex();
+ float value = g_pStudioModel->GetPoseParameter( j );
+
+ float temp = atof( lePoseParameter[i]->getLabel( ) );
+
+ if (fabs( temp - value ) > 0.1)
+ {
+ slPoseParameter[i]->setValue( value );
+ lePoseParameter[i]->setLabel( "%.1f", value );
+ }
+ }
+ }
+}
+
+void
+ControlPanel::setShowGround (bool b)
+{
+ g_viewerSettings.showGround = b;
+ cbGround->setChecked (b);
+}
+
+void
+ControlPanel::setAutoLOD( bool b )
+{
+ g_viewerSettings.autoLOD = b;
+ cbAutoLOD->setChecked( b );
+}
+
+void
+ControlPanel::setSoftwareSkin( bool b )
+{
+ g_viewerSettings.softwareSkin = b;
+ cbSoftwareSkin->setChecked( b );
+}
+
+void
+ControlPanel::setOverbright( bool b )
+{
+ g_viewerSettings.overbright = b;
+ cbOverbright2->setChecked( b );
+}
+
+void
+ControlPanel::setShowMovement (bool b)
+{
+ g_viewerSettings.showMovement = b;
+ cbMovement->setChecked (b);
+}
+
+void
+ControlPanel::setShowBackground (bool b)
+{
+ g_viewerSettings.showBackground = b;
+ cbBackground->setChecked (b);
+}
+
+void
+ControlPanel::setShowNormals (bool b)
+{
+ g_viewerSettings.showNormals = b;
+ cbNormals->setChecked (b);
+}
+
+void
+ControlPanel::setShowTangentFrame (bool b)
+{
+ g_viewerSettings.showTangentFrame = b;
+ cbTangentFrame->setChecked (b);
+}
+
+void
+ControlPanel::setOverlayWireframe (bool b)
+{
+ g_viewerSettings.overlayWireframe = b;
+ cbOverlayWireframe->setChecked (b);
+}
+
+void
+ControlPanel::setShowShadow (bool b)
+{
+ g_viewerSettings.showShadow = b;
+ cbShadow->setChecked (b);
+}
+
+void
+ControlPanel::setShowHitBoxes (bool b)
+{
+ g_viewerSettings.showHitBoxes = b;
+ cbHitBoxes->setChecked (b);
+}
+
+void
+ControlPanel::setShowBones (bool b)
+{
+ g_viewerSettings.showBones = b;
+ cbBones->setChecked (b);
+}
+
+void
+ControlPanel::setShowAttachments (bool b)
+{
+ g_viewerSettings.showAttachments = b;
+ cbAttachments->setChecked (b);
+}
+
+
+void
+ControlPanel::setPlaySounds (bool b)
+{
+ g_viewerSettings.playSounds = b;
+ cbPlaySounds->setChecked (b);
+}
+
+void
+ControlPanel::setShowOriginAxis (bool b)
+{
+ g_viewerSettings.showOriginAxis = b;
+ cbShowOriginAxis->setChecked (b);
+}
+
+struct SortInfo_t
+{
+ int m_nSequence;
+ const char *m_pName;
+ int m_nType;
+};
+
+int SortSequenceFunc( const void *p1, const void *p2 )
+{
+ const SortInfo_t* pSort1 = (const SortInfo_t*)p1;
+ const SortInfo_t* pSort2 = (const SortInfo_t*)p2;
+ if ( pSort1->m_nType < pSort2->m_nType )
+ return -10000;
+ if ( pSort1->m_nType > pSort2->m_nType )
+ return 10000;
+ return Q_stricmp( pSort1->m_pName, pSort2->m_pName );
+}
+
+void ControlPanel::CreateSortedSequenceList( CStudioHdr* hdr, int *pSequence )
+{
+ int nSequenceCount = hdr->GetNumSeq();
+ SortInfo_t *pSort = (SortInfo_t*)_alloca( nSequenceCount * sizeof(SortInfo_t) );
+
+ // Set up sort info
+ for ( int j = 0; j < nSequenceCount; j++ )
+ {
+ pSort[j].m_nSequence = j;
+ if ( g_viewerSettings.showActivities )
+ {
+ pSort[j].m_pName = hdr->pSeqdesc(j).pszActivityName();
+ }
+ else
+ {
+ pSort[j].m_pName = hdr->pSeqdesc(j).pszLabel();
+ }
+
+ pSort[j].m_nType = 0;
+
+ const char *pKeyValuesText = Studio_GetKeyValueText( hdr, j );
+ if ( !pKeyValuesText )
+ continue;
+
+ KeyValues *pKeyValues = new KeyValues( "sort" );
+
+ if ( pKeyValues->LoadFromBuffer( "mdl", pKeyValuesText ) )
+ {
+ KeyValues *pFacePoserKeys = pKeyValues->FindKey( "faceposer" );
+ if ( pFacePoserKeys )
+ {
+ const char *pType = pFacePoserKeys->GetString( "type", "" );
+ if ( !Q_stricmp( pType, "posture" ) )
+ {
+ pSort[j].m_nType = 2;
+ }
+ else if ( !Q_stricmp( pType, "gesture" ) )
+ {
+ pSort[j].m_nType = 1;
+ }
+ }
+ }
+ pKeyValues->deleteThis();
+ }
+
+ if ( !CommandLine()->CheckParm( "-nosort" ) )
+ {
+ qsort( pSort, nSequenceCount, sizeof(SortInfo_t), SortSequenceFunc );
+ }
+
+ for ( int i = 0; i < nSequenceCount; ++i )
+ {
+ pSequence[i] = pSort[i].m_nSequence;
+ }
+}
+
+void ControlPanel::initSequenceChoices()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ int nSequenceCount = hdr->GetNumSeq();
+ int *pSequence = (int*)_alloca( nSequenceCount * sizeof(int) );
+ CreateSortedSequenceList( hdr, pSequence );
+
+ if (iSelectionToSequence)
+ {
+ free( iSelectionToSequence );
+ }
+ iSelectionToSequence = (int*)malloc( nSequenceCount * sizeof(int) );
+
+ if (iSequenceToSelection)
+ {
+ free( iSequenceToSelection );
+ }
+ iSequenceToSelection = (int*)malloc( nSequenceCount * sizeof(int) );
+
+ for (int i = 0; i < MAX_SEQUENCES; i++)
+ {
+ cSequence[i]->removeAll();
+
+ int k = 0;
+ for (int j = 0; j < nSequenceCount; j++)
+ {
+ int nSequence = pSequence[j];
+
+ if (g_viewerSettings.showHidden || !(hdr->pSeqdesc(nSequence).flags & STUDIO_HIDDEN))
+ {
+ if (g_viewerSettings.showActivities)
+ {
+ cSequence[i]->add( hdr->pSeqdesc(nSequence).pszActivityName() );
+ }
+ else
+ {
+ cSequence[i]->add( hdr->pSeqdesc(nSequence).pszLabel() );
+ }
+ iSelectionToSequence[k] = nSequence;
+ iSequenceToSelection[nSequence] = k;
+ k++;
+ }
+ else
+ {
+ // previous valid selection
+ iSequenceToSelection[nSequence] = (k > 0) ? (k - 1) : 0;
+ }
+ }
+ cSequence[i]->select( 0 );
+ slSequence[i]->setValue( 0 );
+ }
+ }
+
+ float flMin, flMax;
+ for (int i = 0; i < NUM_POSEPARAMETERS; i++)
+ {
+ if (g_pStudioModel->GetPoseParameterRange( i, &flMin, &flMax ))
+ {
+ cPoseParameter[i]->removeAll();
+ for (int j = 0; j < hdr->GetNumPoseParameters(); j++)
+ {
+ cPoseParameter[i]->add( hdr->pPoseParameter(j).pszName() );
+ }
+ cPoseParameter[i]->select( i );
+ cPoseParameter[i]->setEnabled( true );
+ cPoseParameter[i]->setVisible( true );
+
+ slPoseParameter[i]->setEnabled( true );
+ slPoseParameter[i]->setRange( flMin, flMax, 1000 );
+ mxToolTip::add (slPoseParameter[i], hdr->pPoseParameter(i).pszName() );
+ slPoseParameter[i]->setVisible( true );
+ lePoseParameter[i]->setVisible( true );
+ lePoseParameter[i]->setLabel( "%.1f", 0.0 );
+ }
+ else
+ {
+ cPoseParameter[i]->setEnabled( false );
+ cPoseParameter[i]->setVisible( false );
+ slPoseParameter[i]->setEnabled( false );
+ slPoseParameter[i]->setVisible( false );
+ lePoseParameter[i]->setVisible( false );
+ }
+ slPoseParameter[i]->setValue( 0.0 );
+ setBlend( i, 0.0 );
+ }
+
+ if ( hdr )
+ {
+ for (int i = 0; i < hdr->GetNumPoseParameters(); i++)
+ {
+ setBlend( i, 0.0 );
+ }
+ }
+}
+
+
+void ControlPanel::setSequence(int index)
+{
+ cSequence[0]->select( iSequenceToSelection[index] );
+ g_pStudioModel->SetSequence(index);
+
+ updateFrameSelection();
+ updateGroundSpeed();
+}
+
+
+void ControlPanel::updateGroundSpeed( void )
+{
+ char sz[100];
+ float flGroundSpeed = g_pStudioModel->GetCurrentVelocity();
+ sprintf( sz, "Speed: %.2f", flGroundSpeed );
+ laGroundSpeed->setLabel( sz );
+}
+
+
+void
+ControlPanel::setOverlaySequence(int num, int index, float weight)
+{
+ cSequence[num]->select( iSequenceToSelection[index] );
+ g_pStudioModel->SetOverlaySequence( num-1, index, weight );
+ slSequence[num]->setValue( weight );
+
+ updateFrameSelection();
+}
+
+
+void ControlPanel::startBlending( void )
+{
+ g_pStudioModel->StartBlending();
+}
+
+void ControlPanel::updateTransitionAmount( void )
+{
+ char sz[ 256 ];
+ sprintf( sz, "%.3f %%", 100.0f * g_pStudioModel->GetTransitionAmount() );
+ laBlendAmount->setLabel( sz );
+}
+
+
+int ControlPanel::getFrameSelection( void )
+{
+ for ( int i = 0; i < MAX_SEQUENCES; i++ )
+ if ( rbFrameSelection[i]->isChecked() )
+ return i;
+ return 0;
+}
+
+void ControlPanel::setFrame( float frame )
+{
+ int iLayer = getFrameSelection();
+ int iFrame = g_pStudioModel->SetFrame( iLayer, frame );
+ char buf[128];
+ sprintf(buf, "%3d", iFrame );
+ lForcedFrame->setLabel( buf );
+}
+
+
+void ControlPanel::updateFrameSelection( void )
+{
+ int iLayer = getFrameSelection();
+ int maxFrame = g_pStudioModel->GetMaxFrame( iLayer );
+ slForceFrame->setRange( 0, maxFrame, maxFrame );
+ slForceFrame->setSteps(1,1);
+}
+
+void ControlPanel::updateFrameSlider( void )
+{
+ int iLayer = getFrameSelection();
+ float flFrame = g_pStudioModel->GetFrame( iLayer );
+ char buf[128];
+ sprintf(buf, "%3.1f", flFrame );
+ lForcedFrame->setLabel( buf );
+ slForceFrame->setValue( flFrame );
+}
+
+void ControlPanel::setSpeedScale( float scale )
+{
+ slSpeedScale->setValue( scale );
+ g_viewerSettings.speedScale = scale;
+
+ updateSpeedScale( );
+}
+
+
+void ControlPanel::updateSpeedScale( void )
+{
+ char szFPS[32];
+ sprintf( szFPS, "x %.2f = %.1f fps", g_viewerSettings.speedScale, g_viewerSettings.speedScale * g_pStudioModel->GetFPS( ) );
+ laFPS->setLabel( szFPS );
+}
+
+
+void ControlPanel::setBlend(int index, float value )
+{
+ g_pStudioModel->SetPoseParameter( index, value );
+ // reset number of frames....
+ updateFrameSelection( );
+
+ updateGroundSpeed( );
+}
+
+void
+ControlPanel::initBodypartChoices()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ int i;
+ mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);
+
+ cBodypart->removeAll();
+ if (hdr->numbodyparts() > 0)
+ {
+ for (i = 0; i < hdr->numbodyparts(); i++)
+ cBodypart->add (pbodyparts[i].pszName());
+
+ cBodypart->select (0);
+
+ cSubmodel->removeAll();
+ for (i = 0; i < pbodyparts[0].nummodels; i++)
+ {
+ char str[64];
+ sprintf (str, "Submodel %d", i );
+ cSubmodel->add (str);
+ }
+ cSubmodel->select (0);
+ }
+ }
+}
+
+
+
+void
+ControlPanel::setBodypart (int index)
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ //cBodypart->setEn
+ cBodypart->select (index);
+ if (index < hdr->numbodyparts())
+ {
+ mstudiobodyparts_t *pbodyparts = hdr->pBodypart(0);
+ cSubmodel->removeAll();
+
+ for (int i = 0; i < pbodyparts[index].nummodels; i++)
+ {
+ char str[64];
+ sprintf (str, "Submodel %d", i );
+ cSubmodel->add (str);
+ }
+ cSubmodel->select (0);
+ //g_pStudioModel->SetBodygroup (index, 0);
+ }
+ }
+ setModelInfo();
+}
+
+
+
+void
+ControlPanel::setSubmodel (int index)
+{
+ g_pStudioModel->SetBodygroup (cBodypart->getSelectedIndex(), index);
+ //!!g_viewerSettings.submodels[cBodypart->getSelectedIndex()] = index;
+ setModelInfo();
+}
+
+
+
+void
+ControlPanel::initPhysicsBones()
+{
+ cHighlightBone->removeAll();
+ cHighlightBone->add( "None" );
+ for ( int i = 0; i < g_pStudioModel->Physics_GetBoneCount(); i++ )
+ {
+ cHighlightBone->add (g_pStudioModel->Physics_GetBoneName( i ) );
+ }
+ cHighlightBone->select (0);
+}
+
+void
+ControlPanel::initLODs()
+{
+ cLODChoice->removeAll();
+ for ( int i = 0; i < g_pStudioModel->GetNumLODs(); i++ )
+ {
+ char tmp[10];
+ sprintf( tmp, "%d", i );
+ cLODChoice->add( tmp );
+ }
+ setLOD( 0, true, true );
+}
+
+void
+ControlPanel::initBoneControllers()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ cController->setEnabled (hdr->numbonecontrollers() > 0);
+ slController->setEnabled (hdr->numbonecontrollers() > 0);
+ cController->removeAll();
+
+ int i = 0;
+ for (i; i < hdr->numbonecontrollers(); i++)
+ {
+ char str[32];
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(i);
+ sprintf (str, "Controller %d", pbonecontroller->inputfield);
+ cController->add (str);
+ }
+
+ if (hdr->numbonecontrollers() > 0)
+ {
+ cController->select (0);
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(0);
+ slController->setRange (pbonecontroller->start, pbonecontroller->end);
+ slController->setValue (0);
+ }
+ }
+}
+
+
+
+void
+ControlPanel::setBoneController (int index)
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(index);
+ slController->setRange ( pbonecontroller->start, pbonecontroller->end);
+ slController->setValue (0);
+ }
+}
+
+
+
+void
+ControlPanel::setBoneControllerValue (int index, float value)
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(index);
+ g_pStudioModel->SetController (pbonecontroller->inputfield, value);
+ }
+}
+
+
+
+void
+ControlPanel::initSkinChoices()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ cSkin->setEnabled (hdr->numskinfamilies() > 0);
+ cSkin->removeAll();
+
+ for (int i = 0; i < hdr->numskinfamilies(); i++)
+ {
+ char str[32];
+ sprintf (str, "Skin %d", i );
+ cSkin->add (str);
+ }
+
+ cSkin->select (0);
+ g_pStudioModel->SetSkin (0);
+ g_viewerSettings.skin = 0;
+ }
+}
+
+void ControlPanel::initMaterialChoices()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if (hdr)
+ {
+ const studiohdr_t *pStudioHdr = hdr->GetRenderHdr();
+ if (pStudioHdr)
+ {
+ cMaterials->setEnabled(pStudioHdr->numtextures > 0);
+ cMaterials->removeAll();
+
+ for (int i = 0; i < pStudioHdr->numtextures; i++)
+ {
+ char str[512];
+ sprintf (str, "%s", pStudioHdr->pTexture(i)->pszName() );
+ cMaterials->add (str);
+ }
+
+ cMaterials->select (0);
+ g_viewerSettings.materialIndex = 0;
+ }
+ }
+}
+
+void ControlPanel::showActivityModifiers( int sequence )
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ if ( !hdr->SequencesAvailable() )
+ return;
+
+ if ( sequence < 0 || sequence >= hdr->GetNumSeq() )
+ return;
+
+ mstudioseqdesc_t &desc = hdr->pSeqdesc( sequence );
+
+ cActivityModifiers->setEnabled( desc.numactivitymodifiers > 0);
+ cActivityModifiers->removeAll();
+
+ for (int i = 0; i < desc.numactivitymodifiers; i++)
+ {
+ char str[512];
+ sprintf (str, "%s", desc.pActivityModifier( i )->pszName() );
+ cActivityModifiers->add (str);
+ }
+
+ cActivityModifiers->select (0);
+}
+
+void
+ControlPanel::setModelInfo()
+{
+ static char str[2048];
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+
+ if (!hdr)
+ return;
+
+ static int checkSum = 0;
+ static int boneLODCount = 0;
+ static int numBatches = 0;
+ if ( g_pStudioModel && !m_bVMTInfoLoaded )
+ {
+ UpdateMaterialList();
+ }
+
+ if( checkSum == hdr->GetRenderHdr()->checksum && boneLODCount == g_DrawModelResults.m_NumHardwareBones && numBatches == g_DrawModelResults.m_NumBatches)
+ {
+ return;
+ }
+
+ checkSum = hdr->GetRenderHdr()->checksum;
+ boneLODCount = g_DrawModelResults.m_NumHardwareBones;
+ numBatches = g_DrawModelResults.m_NumBatches;
+
+ int hbcount = 0;
+ for ( int s = 0; s < hdr->numhitboxsets(); s++ )
+ {
+ hbcount += hdr->iHitboxCount( s );
+ }
+
+ sprintf (str,
+ "Total bones: %d\n"
+ "HW Bones: %d\n"
+ "Batches: %d\n"
+ "Bone Controllers: %d\n"
+ "Hit Boxes: %d in %d sets\n"
+ "Sequences: %d\n",
+ hdr->numbones(),
+ boneLODCount,
+ numBatches,
+ hdr->numbonecontrollers(),
+ hbcount,
+ hdr->numhitboxsets(),
+ hdr->GetNumSeq()
+ );
+
+ lModelInfo1->setLabel (str);
+
+ sprintf (str,
+ "Materials: %d\n"
+ "Skin Families: %d\n"
+ "Bodyparts: %d\n"
+ "Attachments: %d\n",
+ g_DrawModelResults.m_NumMaterials,
+ hdr->numskinfamilies(),
+ hdr->numbodyparts(),
+ hdr->GetNumAttachments());
+
+ lModelInfo2->setLabel (str);
+}
+
+void ControlPanel::UpdateMaterialList( )
+{
+ cMessageList->removeAll();
+ studiohdr_t* pStudioR = g_pStudioModel->GetStudioRenderHdr();
+ if ( pStudioR )
+ {
+ IMaterial *pMaterials[128];
+ int nMaterials = g_pStudioRender->GetMaterialList( pStudioR, ARRAYSIZE( pMaterials ), &pMaterials[0] );
+
+ for ( int i = 0; i < nMaterials; i++ )
+ {
+ char c_MaterialLine[256];
+ Q_strcpy( c_MaterialLine, "" );
+
+ if ( pMaterials[i]->IsErrorMaterial() )
+ {
+ Q_strcat( c_MaterialLine, "*** ERROR *** Model attempted to load one or more VMTs it can't find." , sizeof( c_MaterialLine ) );
+ }
+ else
+ {
+ Q_strcat( c_MaterialLine, pMaterials[i]->GetName() , sizeof( c_MaterialLine ) );
+ }
+ cMessageList->add( c_MaterialLine );
+ }
+ m_bVMTInfoLoaded = true;
+ }
+}
+
+
+extern matrix3x4_t g_viewtransform;
+
+void ControlPanel::centerView( )
+{
+ Vector min, max;
+
+ int hitboxset = 0; //g_MDLViewer->GetCurrentHitboxSet();
+
+ min.Init( 999999,999999,999999);
+ max.Init( -999999,-999999,-999999);
+
+ matrix3x4_t rootxform;
+ MatrixInvert( g_viewtransform, rootxform );
+
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ hitboxset ].m_Hitboxes;
+ for (unsigned short j = list.Head(); j != list.InvalidIndex(); j = list.Next(j) )
+ {
+ mstudiobbox_t *pBBox = &list[j].m_BBox;
+
+ Vector tmpmin, tmpmax;
+ matrix3x4_t bonesetup;
+
+ ConcatTransforms( rootxform, *g_pStudioModel->BoneToWorld( pBBox->bone ), bonesetup );
+
+ TransformAABB( bonesetup, pBBox->bbmin, pBBox->bbmax, tmpmin, tmpmax );
+
+ if (tmpmin.x < min.x) min.x = tmpmin.x;
+ if (tmpmin.y < min.y) min.y = tmpmin.y;
+ if (tmpmin.z < min.z) min.z = tmpmin.z;
+ if (tmpmax.x > max.x) max.x = tmpmax.x;
+ if (tmpmax.y > max.y) max.y = tmpmax.y;
+ if (tmpmax.z > max.z) max.z = tmpmax.z;
+ }
+
+ if (min.x > max.x)
+ {
+ g_pStudioModel->ExtractBbox(min, max);
+ }
+
+ float dx = max[0] - min[0];
+ float dy = max[1] - min[1];
+ float dz = max[2] - min[2];
+
+ // Determine the distance from the model such that it will fit in the screen.
+ float d = dx;
+ if (dy > d)
+ d = dy;
+ if (dz > d)
+ d = dz;
+
+ g_pStudioModel->m_origin[0] = d * 1.34f;
+ g_pStudioModel->m_origin[1] = 0.0f;
+ g_pStudioModel->m_origin[2] = min[2] + dz / 2;
+ g_pStudioModel->m_angles[0] = 0.0f;
+ g_pStudioModel->m_angles[1] = 0.0f;
+ g_pStudioModel->m_angles[2] = 0.0f;
+ g_viewerSettings.lightrot[0] = 0.0f;
+ g_viewerSettings.lightrot[1] = 180.0f; // light should aim at models front
+ g_viewerSettings.lightrot[2] = 0.0f;
+ setFOV( 65.0f );
+ d_MatSysWindow->redraw();
+}
+
+void ControlPanel::viewmodelView()
+{
+ // Sit the camera at the origin with a 54 degree FOV for viewmodels
+ g_pStudioModel->m_origin[0] = 0.0f;
+ g_pStudioModel->m_origin[1] = 0.0f;
+ g_pStudioModel->m_origin[2] = 0.0f;
+ g_pStudioModel->m_angles[0] = 0.0f;
+ g_pStudioModel->m_angles[1] = 180.0f;
+ g_pStudioModel->m_angles[2] = 0.0f;
+ g_viewerSettings.lightrot[0] = 0.0f;
+ g_viewerSettings.lightrot[1] = 180.0f; // light should aim at models front
+ g_viewerSettings.lightrot[2] = 0.0f;
+ setFOV( 54.0f );
+ d_MatSysWindow->redraw();
+}
+
+void ControlPanel::setFOV( float fov )
+{
+ char buf[64];
+ g_viewerSettings.fov = fov;
+ sprintf( buf, "%.0f", fov );
+ leFOV->setLabel( buf );
+}
+
+
+void ControlPanel::setOriginAxisLength( float originAxisLength )
+{
+ leOriginAxisLength->setValue( originAxisLength );
+}
+
+
+void ControlPanel::initFlexes()
+{
+ CStudioHdr *hdr = g_pStudioModel->GetStudioHdr();
+ LocalFlexController_t i;
+ int j;
+
+ if (hdr)
+ {
+ for (j = 0; j < NUM_FLEX_SLIDERS; j++)
+ {
+ cFlex[j]->removeAll();
+ for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ cFlex[j]->add( hdr->pFlexcontroller(i)->pszName() );
+ }
+ if ( false ) // TODO: Add a configuration option to do it the sensible way
+ {
+ cFlex[j]->select( j );
+ }
+ else
+ {
+ cFlex[j]->select( hdr->numflexcontrollers() - NUM_FLEX_SLIDERS + j );
+ }
+ }
+ }
+
+ for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ g_pStudioModel->SetFlexController( i, 0.0f );
+ hdr->pFlexcontroller( i )->localToGlobal = i;
+ }
+
+ for (j = 0; j < NUM_FLEX_SLIDERS; j++)
+ {
+ i = (LocalFlexController_t)cFlex[j]->getSelectedIndex();
+
+ if (i >= 0)
+ {
+ slFlexScale[j]->setValue( g_pStudioModel->GetFlexControllerRaw( i ) );
+ }
+ }
+}
+
+void ControlPanel::connectFlexes( CStudioHdr *hdr )
+{
+ if ( !g_pStudioModel )
+ return;
+
+ LocalFlexController_t i;
+ LocalFlexController_t j;
+
+ CStudioHdr *root = g_pStudioModel->GetStudioHdr();
+
+ if (hdr && root)
+ {
+ for (i = LocalFlexController_t(0); i < hdr->numflexcontrollers(); i++)
+ {
+ for ( j = LocalFlexController_t(0); j < root->numflexcontrollers(); j++)
+ {
+ if ( stricmp( hdr->pFlexcontroller(i)->pszName(), root->pFlexcontroller(j)->pszName() ) == 0 )
+ {
+ hdr->pFlexcontroller(i)->localToGlobal = root->pFlexcontroller(j)->localToGlobal;
+ break;
+ }
+ }
+ }
+ }
+}
+
+
+static void UpdateSliderLabel( mxSlider *slider, mxLabel *label )
+{
+ float value = slider->getValue();
+ char buf[80];
+ sprintf( buf, "%0.2f", value );
+ label->setLabel( buf );
+}
+
+void ControlPanel::UpdateConstraintSliders( int clamp )
+{
+ float vmin = slPhysicsConMin->getValue();
+ float vmax = slPhysicsConMax->getValue();
+
+ // reflect mins/maxs
+ if ( cbLinked->isChecked() )
+ {
+ if ( clamp == IDC_PHYS_CON_MIN && vmin <= 0 )
+ {
+ vmax = -vmin;
+ }
+ else if ( vmax >= 0 )
+ {
+ vmin = -vmax;
+ }
+ }
+
+ if ( vmax < vmin )
+ {
+ if ( clamp == IDC_PHYS_CON_MIN )
+ vmin = vmax;
+ else
+ vmax = vmin;
+ }
+
+ slPhysicsConMin->setValue( vmin );
+ slPhysicsConMax->setValue( vmax );
+ UpdateSliderLabel( slPhysicsConMin, lPhysicsConMin );
+ UpdateSliderLabel( slPhysicsConMax, lPhysicsConMax );
+ UpdateSliderLabel( slPhysicsFriction, lPhysicsFriction );
+}
+
+
+int ControlPanel::handlePhysicsEvent( mxEvent *event )
+{
+ switch( event->action )
+ {
+ case IDC_PHYS_BONE:
+ setupPhysicsBone( cPhysicsBone->getSelectedIndex() );
+ break;
+ case IDC_PHYS_CON_AXIS_X:
+ case IDC_PHYS_CON_AXIS_Y:
+ case IDC_PHYS_CON_AXIS_Z:
+ setupPhysicsAxis( cPhysicsBone->getSelectedIndex(), event->action - IDC_PHYS_CON_AXIS_X );
+ break;
+ case IDC_PHYS_CON_TYPE_FREE:
+ case IDC_PHYS_CON_TYPE_FIXED:
+ case IDC_PHYS_CON_TYPE_LIMIT:
+ break;
+ case IDC_PHYS_CON_MIN:
+ case IDC_PHYS_CON_MAX:
+ case IDC_PHYS_CON_FRICTION:
+ UpdateConstraintSliders( event->action );
+ break;
+ case IDC_PHYS_CON_TEST:
+ g_pStudioModel->Physics_SetPreview( cPhysicsBone->getSelectedIndex(), getPhysicsAxis(), slPhysicsConTest->getValue() );
+ break;
+ case IDC_PHYS_P_MASSBIAS:
+ UpdateSliderLabel( slPhysicsParamMassBias, lPhysicsParamMassBias );
+ break;
+ case IDC_PHYS_P_INERTIA:
+ UpdateSliderLabel( slPhysicsParamInertia, lPhysicsParamInertia );
+ break;
+ case IDC_PHYS_P_DAMPING:
+ UpdateSliderLabel( slPhysicsParamDamping, lPhysicsParamDamping );
+ break;
+ case IDC_PHYS_P_ROT_DAMPING:
+ UpdateSliderLabel( slPhysicsParamRotDamping, lPhysicsParamRotDamping );
+ break;
+ case IDC_PHYS_QCFILE:
+ {
+ writePhysicsData();
+ char *pOut = g_pStudioModel->Physics_DumpQC();
+ if ( pOut )
+ {
+ Sys_CopyStringToClipboard( pOut );
+ }
+ delete[] pOut;
+ return 1;
+ }
+ break;
+
+ }
+ writePhysicsData();
+ return 1;
+}
+
+
+void ControlPanel::handlePhysicsKey( mxEvent *event )
+{
+ if ( event->key == '[' || event->key == ']' )
+ {
+ int boneIndex = cPhysicsBone->getSelectedIndex();
+ int boneCount = cPhysicsBone->getItemCount();
+ int axisIndex = getPhysicsAxis();
+ int axisCount = 3;
+
+ if ( event->key == '[' )
+ {
+ axisIndex = (axisIndex+axisCount-1) % axisCount;
+ if ( axisIndex == (axisCount-1) )
+ {
+ boneIndex = (boneIndex+boneCount-1) % boneCount;
+ }
+ }
+ else
+ {
+ axisIndex = (axisIndex+1) % axisCount;
+ if ( axisIndex == 0 )
+ {
+ boneIndex = (boneIndex+1) % boneCount;
+ }
+ }
+ setPhysicsAxis( axisIndex );
+ cPhysicsBone->select( boneIndex );
+ setupPhysicsBone( boneIndex );
+ }
+}
+
+
+void ControlPanel::setupPhysics( void )
+{
+ if (!PopulatePhysicsBoneList( cPhysicsBone) )
+ return;
+
+ cPhysicsBone->select (0);
+ setPhysicsAxis(0);
+ setupPhysicsBone( 0 );
+
+ char buf[64];
+ sprintf( buf, "%.2f", g_pStudioModel->Physics_GetMass() );
+ leMass->setLabel( buf );
+
+ // Default to "None"
+ setHighlightBone(0);
+}
+
+static void SetSlider( mxSlider *slider, mxLabel *label, float value )
+{
+ slider->setValue( value );
+ UpdateSliderLabel( slider, label );
+}
+
+int ControlPanel::getPhysicsAxis( void )
+{
+ for ( int i = 0; i < 3; i++ )
+ if ( rbConstraintAxis[i]->isChecked() )
+ return i;
+
+ return 0;
+}
+
+
+void ControlPanel::setPhysicsAxis( int axisIndex )
+{
+ for ( int i = 0; i < 3; i++ )
+ {
+ rbConstraintAxis[i]->setChecked( (i==axisIndex)?true : false );
+ }
+}
+
+
+void ControlPanel::setupPhysicsBone( int boneIndex )
+{
+ if (!g_pStudioModel->m_pPhysics)
+ return;
+
+ int axisIndex = getPhysicsAxis();
+ setupPhysicsAxis( boneIndex, axisIndex );
+ // read per-bone data
+ hlmvsolid_t solid;
+ g_pStudioModel->Physics_GetData( boneIndex, &solid, NULL );
+
+ SetSlider( slPhysicsParamMassBias, lPhysicsParamMassBias, solid.massBias );
+ SetSlider( slPhysicsParamInertia, lPhysicsParamInertia, solid.params.inertia );
+ SetSlider( slPhysicsParamDamping, lPhysicsParamDamping, solid.params.damping );
+ SetSlider( slPhysicsParamRotDamping, lPhysicsParamRotDamping, solid.params.rotdamping );
+
+ // Find the bone
+ CStudioHdr* pHdr = g_pStudioModel->GetStudioHdr();
+ for ( int i = 0; i < pHdr->numbones(); i++ )
+ {
+ mstudiobone_t* pBone = pHdr->pBone(i);
+ if (!stricmp(pBone->pszName(), solid.name ))
+ {
+ // Once found, set the surface property accordingly
+ lPhysicsMaterial->setLabel( g_pStudioModel->m_SurfaceProps[i].String() );
+ break;
+ }
+ }
+
+ // select the render/highlight bone
+ setHighlightBone( boneIndex + 1 );
+
+ redraw();
+}
+
+
+void ControlPanel::setupPhysicsAxis( int boneIndex, int axis )
+{
+ // read the per-axis data
+ constraint_ragdollparams_t constraint;
+
+ g_pStudioModel->Physics_GetData( boneIndex, NULL, &constraint );
+ setPhysicsAxis( axis );
+ SetSlider( slPhysicsConMin, lPhysicsConMin, constraint.axes[axis].minRotation );
+ SetSlider( slPhysicsConMax, lPhysicsConMax, constraint.axes[axis].maxRotation );
+ SetSlider( slPhysicsFriction, lPhysicsFriction, constraint.axes[axis].torque );
+ g_pStudioModel->Physics_SetPreview( -1, 0, 0 );
+ redraw();
+}
+
+void ControlPanel::writePhysicsData( void )
+{
+ int boneIndex = cPhysicsBone->getSelectedIndex();
+ int axis = getPhysicsAxis();
+
+ hlmvsolid_t solid;
+ constraint_ragdollparams_t constraint;
+ // read the existing data
+ g_pStudioModel->Physics_GetData( boneIndex, &solid, &constraint );
+
+ const char *pMass = leMass->getLabel();
+ float mass = 0;
+ if ( pMass )
+ {
+ mass = atof( pMass );
+ }
+
+ g_pStudioModel->Physics_SetMass( mass );
+
+ // write back the data we're editing on this dialog
+ constraint.axes[axis].minRotation = slPhysicsConMin->getValue();
+ constraint.axes[axis].maxRotation = slPhysicsConMax->getValue();
+ constraint.axes[axis].torque = slPhysicsFriction->getValue();
+
+ solid.massBias = slPhysicsParamMassBias->getValue();
+ solid.index = boneIndex;
+ //solid.params.mass;
+ solid.params.inertia = slPhysicsParamInertia->getValue();
+ solid.params.damping = slPhysicsParamDamping->getValue();
+ solid.params.rotdamping = slPhysicsParamRotDamping->getValue();
+
+ // store it in the model
+ g_pStudioModel->Physics_SetData( boneIndex, &solid, &constraint );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void ControlPanel::SetFrameSlider( float flFrame )
+{
+ slForceFrame->setValue( flFrame );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+void ControlPanel::UnloadAllMergedModels()
+{
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; ++i )
+ {
+ g_MDLViewer->getMenuBar()->modify( IDC_FILE_UNLOADMERGEDMODEL1 + i, IDC_FILE_UNLOADMERGEDMODEL1 + i, "(empty)" );
+ g_MDLViewer->getMenuBar()->setEnabled( IDC_FILE_UNLOADMERGEDMODEL1 + i, false );
+ V_strcpy_safe( g_viewerSettings.mergeModelFile[i], "" );
+
+ if ( g_pStudioExtraModel[i] )
+ {
+ g_pStudioExtraModel[i]->FreeModel( false );
+ delete g_pStudioExtraModel[i];
+ g_pStudioExtraModel[i] = NULL;
+ }
+ }
+} \ No newline at end of file
diff --git a/utils/hlmv/controlpanel.h b/utils/hlmv/controlpanel.h
new file mode 100644
index 0000000..73c2c88
--- /dev/null
+++ b/utils/hlmv/controlpanel.h
@@ -0,0 +1,480 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: ControlPanel.h
+// last modified: May 29 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#ifndef INCLUDED_CONTROLPANEL
+#define INCLUDED_CONTROLPANEL
+
+
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+
+
+#define IDC_TAB 1901
+#define IDC_RENDERMODE 2001
+#define IDC_GROUND 2003
+#define IDC_MOVEMENT 2004
+#define IDC_BACKGROUND 2005
+#define IDC_HITBOXES 2006
+#define IDC_BONES 2007
+#define IDC_ATTACHMENTS 2008
+#define IDC_PHYSICSMODEL 2009
+#define IDC_PHYSICSHIGHLIGHT 2010
+#define IDC_LODCHOICE 2011
+#define IDC_AUTOLOD 2012
+#define IDC_LODSWITCH 2013
+#define IDC_SOFTWARESKIN 2014
+#define IDC_OVERBRIGHT2 2015
+#define IDC_RENDER_FOV 2016
+#define IDC_SEQUENCEBOXES 2017
+#define IDC_RUNIK 2018
+#define IDC_HEADTURN 2019
+#define IDC_NORMALS 2020
+#define IDC_TANGENTFRAME 2021
+#define IDC_NORMALMAP 2022
+#define IDC_SPECULAR 2023
+#define IDC_SHADOW 2024
+#define IDC_ILLUMPOSITION 2025
+#define IDC_OVERLAY_WIREFRAME 2026
+#define IDC_PLAYSOUNDS 2027
+#define IDC_MESSAGES 2028
+#define IDC_SHADERS 2029
+//#define IDC_PARALLAXMAP 2030
+#define IDC_SHOWORIGINAXIS 2029
+#define IDC_ORIGINAXISLENGTH 2030
+
+#define MAX_SEQUENCES 5
+#define IDC_SEQUENCE0 3000
+#define IDC_SEQUENCE1 3001
+#define IDC_SEQUENCE2 3002
+#define IDC_SEQUENCE3 3003
+#define IDC_SEQUENCE4 3004
+
+#define IDC_SEQUENCESCALE0 3005
+#define IDC_SEQUENCESCALE1 3006
+#define IDC_SEQUENCESCALE2 3007
+#define IDC_SEQUENCESCALE3 3008
+#define IDC_SEQUENCESCALE4 3009
+
+#define IDC_FRAMESELECTION0 3010
+#define IDC_FRAMESELECTION1 3011
+#define IDC_FRAMESELECTION2 3012
+#define IDC_FRAMESELECTION3 3013
+#define IDC_FRAMESELECTION4 3014
+
+#define NUM_POSEPARAMETERS 8
+#define IDC_POSEPARAMETER_SCALE 3100
+#define IDC_POSEPARAMETER 3120
+#define IDC_POSEPARAMETER_VALUE 3140
+
+#define IDC_SPEEDSCALE 3201
+#define IDC_FORCEFRAME 3202
+#define IDC_BLENDSEQUENCECHANGES 3203
+#define IDC_BLENDNOW 3204
+#define IDC_BLENDTIME 3205
+#define IDC_ACTIVITY_MODIFIERS 3206
+#define IDC_ANIMATEWEAPONS 3207
+
+#define IDC_BODYPART 4001
+#define IDC_SUBMODEL 4002
+#define IDC_CONTROLLER 4003
+#define IDC_CONTROLLERVALUE 4004
+#define IDC_SKINS 4005
+#define IDC_MATERIALS 4006
+
+#define IDC_BONE_BONELIST 5000
+#define IDC_BONE_GENERATEQC 5001
+#define IDC_BONE_HIGHLIGHT_BONE 5002
+#define IDC_BONE_HITBOXLIST 5003
+#define IDC_BONE_SURFACEPROP 5004
+#define IDC_BONE_HIGHLIGHT_HITBOX 5005
+#define IDC_BONE_ADD_HITBOX 5006
+#define IDC_BONE_DELETE_HITBOX 5007
+#define IDC_BONE_APPLY_TO_CHILDREN 5008
+#define IDC_BONE_SHOW_DEFAULT_POSE 5009
+#define IDC_BONE_HITBOX_ORIGINX 5010
+#define IDC_BONE_HITBOX_ORIGINY 5011
+#define IDC_BONE_HITBOX_ORIGINZ 5012
+#define IDC_BONE_HITBOX_SIZEX 5013
+#define IDC_BONE_HITBOX_SIZEY 5014
+#define IDC_BONE_HITBOX_SIZEZ 5015
+#define IDC_BONE_HITBOX_GROUP 5016
+#define IDC_BONE_UPDATE_HITBOX 5017
+#define IDC_BONE_USE_AUTOGENERATED_HITBOXES 5018
+#define IDC_BONE_HITBOXSET 5019
+#define IDC_BONE_HITBOXADDSET 5020
+#define IDC_BONE_HITBOXDELETESET 5021
+#define IDC_BONE_HITBOXSETNAME 5022
+#define IDC_BONE_HITBOXSETNAME_EDIT 5023
+
+// This range is reserved for the attachment window.
+#define IDC_ATTACHMENT_WINDOW_FIRST 5024
+#define IDC_ATTACHMENT_WINDOW_LAST 5100
+#define IDC_BONE_HITBOX_NAME 5101
+#define IDC_BONE_SAVE_HITBOXES 5102
+#define IDC_BONE_LOAD_HITBOXES 5103
+
+#define IDC_FLEX 7001
+#define IDC_FLEXDEFAULTS 7002
+#define IDC_FLEXRANDOM 7003
+#define IDC_FLEXZERO 7004
+#define IDC_FLEXONE 7005
+#define IDC_FLEXSCALE 7101
+
+#define NUM_FLEX_SLIDERS 48
+
+#define IDC_PHYS_FIRST 7501
+#define IDC_PHYS_BONE 7501
+#define IDC_PHYS_CON_LINK_LIMITS 7502
+#define IDC_PHYS_MATERIAL 7503
+#define IDC_PHYS_CON_MIN 7504
+#define IDC_PHYS_CON_MAX 7505
+#define IDC_PHYS_CON_TEST 7506
+#define IDC_PHYS_P_MASSBIAS 7507
+#define IDC_PHYS_CON_FRICTION 7508
+//#define IDC_PHYS_P_ELASTICITY 7509
+#define IDC_PHYS_P_INERTIA 7510
+#define IDC_PHYS_P_DAMPING 7511
+#define IDC_PHYS_P_ROT_DAMPING 7512
+#define IDC_PHYS_MASS 7513
+#define IDC_PHYS_QCFILE 7514
+#define IDC_PHYS_CON_AXIS_X 7515
+#define IDC_PHYS_CON_AXIS_Y 7516
+#define IDC_PHYS_CON_AXIS_Z 7517
+#define IDC_PHYS_CON_TYPE_FREE 7518
+#define IDC_PHYS_CON_TYPE_FIXED 7519
+#define IDC_PHYS_CON_TYPE_LIMIT 7520
+#define IDC_PHYS_LAST 7599
+
+#define MAX_ANIMS 4
+#define IDC_ANIMX 8020 // through 8023 ( MAX_ANIMS )
+#define IDC_ANIMY 8030 // through 8033 ( MAX_ANIMS )
+
+#define IDC_IKRULE_CHAIN 9000
+#define IDC_IKRULE_CHOICE 9001
+#define IDC_IKRULE_TOUCH 9002
+#define IDC_IKRULE_ATTACHMENT 9003
+#define IDC_IKRULE_RANGE_TOGGLE 9004
+#define IDC_IKRULE_RANGE_START 9005
+#define IDC_IKRULE_RANGE_PEAK 9006
+#define IDC_IKRULE_RANGE_TAIL 9007
+#define IDC_IKRULE_RANGE_END 9008
+#define IDC_IKRULE_RANGE_START_NOW 9009
+#define IDC_IKRULE_RANGE_PEAK_NOW 9010
+#define IDC_IKRULE_RANGE_TAIL_NOW 9011
+#define IDC_IKRULE_RANGE_END_NOW 9012
+#define IDC_IKRULE_CONTACT_TOGGLE 9013
+#define IDC_IKRULE_CONTACT_FRAME_NOW 9014
+#define IDC_IKRULE_CONTACT_FRAME 9015
+#define IDC_IKRULE_USING 9016
+#define IDC_IKRULE_QC_STRING 9017
+
+#define IDC_EVENT_SOUND_FRAME_NOW 6000
+#define IDC_EVENT_SOUND_FRAME 6001
+#define IDC_EVENT_SOUND_NAME 6002
+#define IDC_EVENT_QC_STRING 6003
+
+
+class mxTab;
+class mxChoice;
+class mxCheckBox;
+class mxSlider;
+class mxLineEdit;
+class mxLineEdit2;
+class mxLabel;
+class mxListBox;
+class mxButton;
+class mxRadioButton;
+class MatSysWindow;
+class TextureWindow;
+class CBoneControlWindow;
+class CAttachmentsWindow;
+class CStudioHdr;
+
+
+// Return codes from loadModel.
+enum LoadModelResult_t
+{
+ LoadModel_Success = 0,
+ LoadModel_LoadFail,
+ LoadModel_NoModel,
+ LoadModel_PostLoadFail,
+};
+
+
+class ControlPanel : public mxWindow
+{
+ mxWindow *wRender;
+ mxTab *tab;
+ mxChoice *cRenderMode;
+ mxChoice *cHighlightBone;
+
+ mxCheckBox *cbGround;
+ mxCheckBox *cbHitBoxes;
+ mxCheckBox *cbSequenceBoxes;
+ mxCheckBox *cbShadow;
+ mxCheckBox *cbMovement;
+ mxCheckBox *cbBackground;
+ mxCheckBox *cbSoftwareSkin;
+ mxCheckBox *cbOverbright2;
+ mxCheckBox *cbAttachments;
+ mxCheckBox *cbBones;
+ mxCheckBox *cbNormals;
+ mxCheckBox *cbNormalMap;
+// mxCheckBox *cbParallaxMap;
+ mxCheckBox *cbTangentFrame;
+ mxCheckBox *cbOverlayWireframe;
+ mxCheckBox *cbSpecular;
+ mxCheckBox *cbRunIK;
+ mxCheckBox *cbEnableHead;
+ mxCheckBox *cbIllumPosition;
+ mxCheckBox *cbPlaySounds;
+ mxCheckBox *cbShowOriginAxis;
+ mxSlider *leOriginAxisLength;
+
+ mxChoice *cLODChoice;
+ mxCheckBox *cbAutoLOD;
+ mxLineEdit *leLODSwitch;
+ mxLabel *lLODMetric;
+ mxChoice *cSequence[MAX_SEQUENCES];
+ mxSlider *slSequence[MAX_SEQUENCES];
+ int *iSelectionToSequence; // selection to sequence
+ int *iSequenceToSelection; // sequence to selection
+ mxLabel *laGroundSpeed;
+ mxSlider *slSpeedScale;
+ mxLabel *laFPS;
+ mxLabel *laBlendAmount;
+
+ mxChoice *cPoseParameter[NUM_POSEPARAMETERS];
+ mxSlider *slPoseParameter[NUM_POSEPARAMETERS];
+ mxLineEdit *lePoseParameter[NUM_POSEPARAMETERS];
+ mxLineEdit *leFOV;
+
+ mxSlider *slBlendTime;
+ mxLabel *laBlendTime;
+ mxChoice *cActivityModifiers;
+ mxSlider *slForceFrame;
+ mxLabel *lForcedFrame;
+ mxRadioButton *rbFrameSelection[MAX_SEQUENCES];
+ mxChoice *cBodypart, *cController, *cSubmodel;
+ mxSlider *slController;
+ mxChoice *cSkin;
+ mxChoice *cMaterials;
+ mxLabel *lModelInfo1, *lModelInfo2, *lModelInfo3, *lModelInfo4, *lModelInfo5;
+ //mxChoice *cTextures;
+ //mxCheckBox *cbChrome;
+ //mxLabel *lTexSize;
+ //mxLineEdit *leWidth, *leHeight;
+
+ mxLineEdit *leMeshScale, *leBoneScale;
+
+ MatSysWindow *d_MatSysWindow;
+ TextureWindow *d_textureWindow;
+
+ mxChoice *cFlex[NUM_FLEX_SLIDERS];
+ mxSlider *slFlexScale[NUM_FLEX_SLIDERS];
+
+ mxChoice *cPhysicsBone;
+ mxRadioButton *rbConstraintAxis[3];
+ mxSlider *slPhysicsFriction;
+ mxLabel *lPhysicsFriction;
+
+ mxSlider *slPhysicsConMin;
+ mxLabel *lPhysicsConMin;
+ mxCheckBox *cbLinked; // links min/max sliders
+
+ mxSlider *slPhysicsConMax;
+ mxLabel *lPhysicsConMax;
+ mxSlider *slPhysicsConTest;
+ mxLineEdit *leMass;
+
+ mxSlider *slPhysicsParamMassBias;
+ mxLabel *lPhysicsParamMassBias;
+ mxSlider *slPhysicsParamFriction;
+ mxLabel *lPhysicsParamFriction;
+ mxSlider *slPhysicsParamElasticity;
+ mxLabel *lPhysicsParamElasticity;
+ mxSlider *slPhysicsParamInertia;
+ mxLabel *lPhysicsParamInertia;
+ mxSlider *slPhysicsParamDamping;
+ mxLabel *lPhysicsParamDamping;
+ mxSlider *slPhysicsParamRotDamping;
+ mxLabel *lPhysicsParamRotDamping;
+ mxLabel *lPhysicsMaterial;
+
+ mxChoice *cIKChain;
+ mxChoice *cIKType;
+ mxLabel *lIKTouch;
+ mxChoice *cIKTouch;
+ mxLabel *lIKAttachment;
+ mxLineEdit *leIKAttachment;
+ mxCheckBox *cbIKRangeToggle;
+ mxLineEdit2 *leIKRangeStart;
+ mxLineEdit2 *leIKRangePeak;
+ mxLineEdit2 *leIKRangeTail;
+ mxLineEdit2 *leIKRangeEnd;
+ mxCheckBox *cbIKContactToggle;
+ mxLineEdit2 *leIKContactFrame;
+ mxChoice *cIKUsing;
+ mxLineEdit2 *leIKQCString;
+
+ mxLineEdit2 *leEventSoundFrame;
+ mxListBox *lbEventSoundName;
+ mxLineEdit2 *leEventQCString;
+
+ CBoneControlWindow* m_pBoneWindow;
+ CAttachmentsWindow* m_pAttachmentsWindow;
+ mxListBox *cMessageList;
+ mxListBox *cShaderUsed;
+
+public:
+ // CREATORS
+ ControlPanel (mxWindow *parent);
+ virtual ~ControlPanel ();
+
+ virtual void OnDelete();
+
+ // MANIPULATORS
+ int handleEvent (mxEvent *event);
+
+ int handlePhysicsEvent( mxEvent *event );
+ void UpdateConstraintSliders( int clamp );
+ void setupPhysics( void );
+ void setupPhysicsBone( int boneIndex );
+ void setupPhysicsAxis( int boneIndex, int axis );
+ int getPhysicsAxis( void );
+ void setPhysicsAxis( int axisIndex );
+ void writePhysicsData( void );
+ void handlePhysicsKey( mxEvent *event );
+// void readPhysicsMaterials( mxChoice *pList );
+
+ void dumpModelInfo ();
+ LoadModelResult_t loadModel(const char *filename);
+ LoadModelResult_t loadModel(const char *filename, int slot );
+ void OnLoadModel( void );
+
+ void resetControlPanel( void );
+ void setRenderMode (int mode);
+ void setShowGround (bool b);
+ void setShowMovement (bool b);
+ void setShowBackground (bool b);
+ void setShowNormals (bool b);
+ void setShowTangentFrame (bool b);
+ void setOverlayWireframe (bool b);
+ void setShowShadow (bool b);
+ void setShowHitBoxes (bool b);
+ void setShowBones (bool b);
+ void setShowAttachments (bool b);
+ void setHighlightBone( int index );
+ void setLOD( int index, bool setLODchoice, bool force );
+ void setAutoLOD( bool b );
+ void setSoftwareSkin( bool b );
+ void setOverbright( bool b );
+ void setLODMetric( float metric );
+ void setPolycount( int polycount );
+ void setModelInfo( int nVertCount, int nIndexCount, int nTriCount );
+ void setTransparent( bool isTransparent );
+ void updatePoseParameters( void );
+ void setFOV( float fov );
+ void setPlaySounds( bool b );
+ void setShowOriginAxis( bool b );
+ void setOriginAxisLength( float originAxisLength );
+
+ void initSequenceChoices();
+ void setSequence( int index );
+ void showActivityModifiers( int sequence );
+ void updateGroundSpeed( void );
+ void setOverlaySequence(int num, int index, float weight);
+ void updateTransitionAmount();
+ void startBlending( void );
+ void setSpeedScale ( float scale );
+ void updateSpeedScale( void );
+ void setBlend(int index, float value );
+
+ int getFrameSelection( void );
+ void setFrame( float frame );
+ void updateFrameSelection( void );
+ void updateFrameSlider( void );
+
+ void initBodypartChoices();
+ void setBodypart (int index);
+ void setSubmodel (int index);
+
+ void initBoneControllers ();
+ void setBoneController (int index);
+ void setBoneControllerValue (int index, float value);
+
+ void initSkinChoices();
+ void initMaterialChoices();
+
+ void setModelInfo ();
+
+ void initPhysicsBones();
+
+ void initLODs();
+
+ void centerView ();
+ void UpdateMaterialList ();
+ void viewmodelView();
+
+ void fullscreen ();
+
+ void setMatSysWindow (MatSysWindow *window) { d_MatSysWindow = window; }
+
+ void ConvertFlexData();
+ void initFlexes ();
+ void connectFlexes( CStudioHdr* hdr );
+
+ int GetCurrentHitboxSet( void );
+
+ void BuildIKRuleQCString();
+ void UpdateIKRuleWindow();
+
+ void BuildEventQCString();
+
+ void CreateSortedSequenceList( CStudioHdr* hdr, int *pSequence );
+ void SetFrameSlider( float flFrame );
+
+ void UnloadAllMergedModels();
+
+public:
+ // Sets up the main tabs
+ void SetupRenderWindow( mxTab* pTab );
+ void SetupSequenceWindow( mxTab* pTab );
+ void SetupBoneControlWindow( mxTab* pTab );
+ void SetupBodyWindow( mxTab* pTab );
+ void SetupFlexWindow( mxTab* pTab );
+ void SetupPhysicsWindow( mxTab* pTab );
+ void SetupAttachmentsWindow( mxTab *pTab );
+ void SetupIKRuleWindow( mxTab *pTab );
+ void SetupEventWindow( mxTab *pTab );
+ bool m_bVMTInfoLoaded;
+};
+
+
+extern ControlPanel *g_ControlPanel;
+
+
+
+#endif // INCLUDED_CONTROLPANEL \ No newline at end of file
diff --git a/utils/hlmv/debugdrawmodel.cpp b/utils/hlmv/debugdrawmodel.cpp
new file mode 100644
index 0000000..bb30836
--- /dev/null
+++ b/utils/hlmv/debugdrawmodel.cpp
@@ -0,0 +1,761 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+#include "istudiorender.h"
+#include "materialsystem/imesh.h"
+#include "mathlib/mathlib.h"
+#include "matsyswin.h"
+#include "viewersettings.h"
+#include "materialsystem/imaterialvar.h"
+
+extern IMaterialSystem *g_pMaterialSystem;
+
+#define NORMAL_LENGTH .5f
+#define NORMAL_OFFSET_FROM_MESH 0.1f
+
+int DebugDrawModel( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CMeshBuilder meshBuilder;
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ pRenderContext->Bind( materialBatch.m_pMaterial );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Normal3fv( &skinnedNormal.x );
+ meshBuilder.TexCoord2fv( 0, &vert.m_TexCoord.x );
+ meshBuilder.UserData( &skinnedTangentS.x );
+ meshBuilder.AdvanceVertex();
+ }
+
+ int i;
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( i = 0; i < materialBatch.m_TriListIndices.Count(); i++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[i] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelNormals( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ CMeshBuilder meshBuilder;
+ pRenderContext->Bind( g_materialVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh();
+ meshBuilder.Begin( pBuildMesh, MATERIAL_LINES, materialBatch.m_Verts.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ }
+
+// skinnedPos += skinnedNormal * NORMAL_OFFSET_FROM_MESH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+
+ skinnedPos += skinnedNormal * NORMAL_LENGTH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 0.0f, 0.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelTangentS( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ CMeshBuilder meshBuilder;
+ pRenderContext->Bind( g_materialVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh();
+ meshBuilder.Begin( pBuildMesh, MATERIAL_LINES, materialBatch.m_Verts.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+// skinnedPos += skinnedNormal * NORMAL_OFFSET_FROM_MESH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ skinnedPos += skinnedTangentS.AsVector3D() * NORMAL_LENGTH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelTangentT( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ CMeshBuilder meshBuilder;
+ pRenderContext->Bind( g_materialVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh();
+ meshBuilder.Begin( pBuildMesh, MATERIAL_LINES, materialBatch.m_Verts.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+ Vector skinnedTangentT = CrossProduct( skinnedNormal, skinnedTangentS.AsVector3D() ) * skinnedTangentS.w;
+
+// skinnedPos += skinnedNormal * NORMAL_OFFSET_FROM_MESH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+
+ skinnedPos += skinnedTangentT * NORMAL_LENGTH;
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+ }
+
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelBadVerts( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CMeshBuilder meshBuilder;
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ pRenderContext->Bind( g_materialVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Normal3fv( &skinnedNormal.x );
+ meshBuilder.TexCoord2fv( 0, &vert.m_TexCoord.x );
+ meshBuilder.UserData( &skinnedTangentS.x );
+
+ Vector color( 0.0f, 0.0f, 0.0f );
+ float len;
+
+ // check the length of the tangent S vector.
+ len = tangentS.AsVector3D().Length();
+ if( len < .9f || len > 1.1f )
+ {
+ color.Init( 1.0f, 0.0f, 0.0f );
+ }
+
+ // check the length of the normal.
+ len = normal.Length();
+ if( len < .9f || len > 1.1f )
+ {
+ color.Init( 1.0f, 0.0f, 0.0f );
+ }
+
+ // check the dot of tangent s and normal
+ float dot = DotProduct( tangentS.AsVector3D(), normal );
+ if( dot > .95 || dot < -.95 )
+ {
+ color.Init( 1.0f, 0.0f, 0.0f );
+ }
+
+ meshBuilder.Color3fv( color.Base() );
+
+ meshBuilder.AdvanceVertex();
+ }
+
+ int i;
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( i = 0; i < materialBatch.m_TriListIndices.Count(); i++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[i] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelWireframe( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, const Vector &color, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CMeshBuilder meshBuilder;
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ pRenderContext->Bind( g_materialWireframeVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Normal3fv( &skinnedNormal.x );
+ meshBuilder.TexCoord2fv( 0, &vert.m_TexCoord.x );
+ meshBuilder.UserData( &skinnedTangentS.x );
+ meshBuilder.Color3fv( color.Base() );
+ meshBuilder.AdvanceVertex();
+ }
+
+ int i;
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( i = 0; i < materialBatch.m_TriListIndices.Count(); i++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[i] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelBoneWeights( IStudioRender *pStudioRender, DrawModelInfo_t& info,
+ matrix3x4_t *pBoneToWorld, const Vector &modelOrigin, int flags )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CMeshBuilder meshBuilder;
+
+ int batchID;
+ for( batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+
+ pRenderContext->Bind( g_materialVertexColor );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+ Vector skinnedPos( 0.0f, 0.0f, 0.0f );
+ Vector skinnedNormal( 0.0f, 0.0f, 0.0f );
+ Vector4D skinnedTangentS( 0.0f, 0.0f, 0.0f, vert.m_TangentS[3] );
+ int k;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ const matrix3x4_t &poseToWorld = tris.m_PoseToWorld[ vert.m_BoneIndex[k] ];
+ Vector tmp;
+ VectorTransform( pos, poseToWorld, tmp );
+ skinnedPos += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( normal, poseToWorld, tmp );
+ skinnedNormal += vert.m_BoneWeight[k] * tmp;
+ VectorRotate( tangentS.AsVector3D(), poseToWorld, tmp );
+ skinnedTangentS.AsVector3D() += vert.m_BoneWeight[k] * tmp;
+ }
+
+ meshBuilder.Position3fv( &skinnedPos.x );
+ meshBuilder.Normal3fv( &skinnedNormal.x );
+ meshBuilder.TexCoord2fv( 0, &vert.m_TexCoord.x );
+ meshBuilder.UserData( &skinnedTangentS.x );
+
+ if (g_viewerSettings.highlightBone >= 0)
+ {
+ float v = 0.0;
+ for( k = 0; k < vert.m_NumBones; k++ )
+ {
+ if (vert.m_BoneIndex[k] == g_viewerSettings.highlightBone)
+ {
+ v = vert.m_BoneWeight[k];
+ }
+ }
+ v = clamp( v, 0.0f, 1.0f );
+ meshBuilder.Color4f( 1.0f - v, 1.0f, 1.0f - v, 0.5 );
+ }
+ else
+ {
+ switch( vert.m_NumBones )
+ {
+ case 0:
+ meshBuilder.Color3f( 0.0f, 0.0f, 0.0f );
+ break;
+ case 1:
+ meshBuilder.Color3f( 0.0f, 1.0f, 0.0f );
+ break;
+ case 2:
+ meshBuilder.Color3f( 1.0f, 1.0f, 0.0f );
+ break;
+ case 3:
+ meshBuilder.Color3f( 1.0f, 0.0f, 0.0f );
+ break;
+ default:
+ meshBuilder.Color3f( 1.0f, 1.0f, 1.0f );
+ break;
+ }
+ }
+ meshBuilder.AdvanceVertex();
+ }
+
+ int i;
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( i = 0; i < materialBatch.m_TriListIndices.Count(); i++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[i] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
+int DebugDrawModelTexCoord( IStudioRender *pStudioRender, const char *pMaterialName, const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, float w, float h )
+{
+ // Make static so that we aren't reallocating everything all the time.
+ // TODO: make sure that this actually keeps us from reallocating inside of GetTriangles.
+ static GetTriangles_Output_t tris;
+
+ pStudioRender->GetTriangles( info, pBoneToWorld, tris );
+
+ CUtlVector<int> batchList;
+ for( int batchID = 0; batchID < tris.m_MaterialBatches.Count(); batchID++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchID];
+ if ( !materialBatch.m_Verts.Count() || V_stricmp(materialBatch.m_pMaterial->GetName(), pMaterialName) )
+ continue;
+ batchList.AddToTail(batchID);
+ }
+ if ( !batchList.Count() )
+ return 0;
+
+ bool bFound = false;
+ IMaterialVar *pBaseVar = g_materialDebugCopyBaseTexture->FindVar( "$basetexture", &bFound, true );
+ if ( !bFound )
+ return 0;
+
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchList[0]];
+ IMaterialVar *pVar = materialBatch.m_pMaterial->FindVar( "$basetexture", &bFound, true );
+ if ( !bFound )
+ return 0;
+ pBaseVar->SetTextureValue( pVar->GetTextureValue() );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->OverrideDepthEnable( false, false );
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->Ortho( 0, h, w, 0, -1, 1 );
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ CMeshBuilder meshBuilder;
+
+ // now render a single quad with the base texture on it
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchList[0]];
+ //pRenderContext->Bind( materialBatch.m_pMaterial );
+ pRenderContext->Bind( g_materialDebugCopyBaseTexture );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, 4, 6 );
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[0];
+ //const Vector &pos = vert.m_Position;
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+
+ Vector uv0(0,0,0), uv1(1,0,0), uv2(1, 1, 0), uv3(0,1,0);
+ Vector *pUV[] = {&uv0, &uv1, &uv2, &uv3};
+
+ for ( int i = 0;i < 4; i++ )
+ {
+ Vector p = *pUV[i];
+ p.x *= w;
+ p.y *= h;
+ meshBuilder.Position3fv( &p.x );
+ meshBuilder.Normal3fv( &normal.x );
+ meshBuilder.TexCoord2fv( 0, pUV[i]->Base() );
+ meshBuilder.UserData( &tangentS.x );
+
+ meshBuilder.Color3f( 1.0f, 1.0f, 1.0f );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.FastIndex( 0 );
+ meshBuilder.FastIndex( 1 );
+ meshBuilder.FastIndex( 2 );
+
+ meshBuilder.FastIndex( 0 );
+ meshBuilder.FastIndex( 2 );
+ meshBuilder.FastIndex( 3 );
+
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+
+ // now draw coverage - show which UV space is used more than once
+#if 0
+ for( int i = 0; i < batchList.Count(); i++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchList[i]];
+ //pRenderContext->Bind( g_materialWireframeVertexColorNoCull );
+ pRenderContext->Bind( g_materialVertexColorAdditive );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ int vertID;
+ // Send the vertices down to the hardware.
+ for( vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+
+ Vector p;
+ p.x = vert.m_TexCoord.x * w;
+ p.y = vert.m_TexCoord.y * h;
+ p.z = 0;
+
+ meshBuilder.Position3fv( &p.x );
+ meshBuilder.Normal3fv( &normal.x );
+ meshBuilder.UserData( &tangentS.x );
+
+
+ meshBuilder.Color3f( 0.25f, 0.0f, 0.0f );
+ meshBuilder.AdvanceVertex();
+ }
+
+ int i;
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( i = 0; i < materialBatch.m_TriListIndices.Count(); i++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[i] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+#endif
+
+ const color32 batchColor = {0,255,255,0};
+ // now draw all batches with the matching material in wireframe over the render of the base texture
+ for( int i = 0; i < batchList.Count(); i++ )
+ {
+ GetTriangles_MaterialBatch_t &materialBatch = tris.m_MaterialBatches[batchList[i]];
+
+ pRenderContext->Bind( g_materialWireframeVertexColorNoCull );
+ IMesh *pBuildMesh = pRenderContext->GetDynamicMesh( false );
+ meshBuilder.Begin( pBuildMesh, MATERIAL_TRIANGLES, materialBatch.m_Verts.Count(),
+ materialBatch.m_TriListIndices.Count() );
+
+ // Send the vertices down to the hardware.
+ for( int vertID = 0; vertID < materialBatch.m_Verts.Count(); vertID++ )
+ {
+ GetTriangles_Vertex_t &vert = materialBatch.m_Verts[vertID];
+ const Vector &normal = vert.m_Normal;
+ const Vector4D &tangentS = vert.m_TangentS;
+
+ Vector p;
+ p.x = vert.m_TexCoord.x * w;
+ p.y = vert.m_TexCoord.y * h;
+ p.z = 0;
+
+ meshBuilder.Position3fv( &p.x );
+ meshBuilder.Normal3fv( &normal.x );
+ meshBuilder.UserData( &tangentS.x );
+
+
+ meshBuilder.Color4ubv( &batchColor.r );
+ meshBuilder.AdvanceVertex();
+ }
+
+ // Set the indices down to the hardware.
+ // Each triplet of indices is a triangle.
+ for( int j = 0; j < materialBatch.m_TriListIndices.Count(); j++ )
+ {
+ meshBuilder.FastIndex( materialBatch.m_TriListIndices[j] );
+ }
+ meshBuilder.End();
+ pBuildMesh->Draw();
+ }
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->PopMatrix();
+
+ pRenderContext->MatrixMode( MATERIAL_MODEL );
+ pRenderContext->PopMatrix();
+
+ return 0;
+}
+
diff --git a/utils/hlmv/debugdrawmodel.h b/utils/hlmv/debugdrawmodel.h
new file mode 100644
index 0000000..be82b45
--- /dev/null
+++ b/utils/hlmv/debugdrawmodel.h
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DEBUGDRAWMODEL_H
+#define DEBUGDRAWMODEL_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+int DebugDrawModel( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelNormals( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelTangentS( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelTangentT( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelBoneWeights( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelBadVerts( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelWireframe( IStudioRender *pStudioRender, DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, const Vector &modelOrigin,
+ const Vector &color, int flags = STUDIORENDER_DRAW_ENTIRE_MODEL );
+int DebugDrawModelTexCoord( IStudioRender *pStudioRender, const char *pMaterialName, const DrawModelInfo_t& info, matrix3x4_t *pBoneToWorld, float w, float h );
+
+
+#endif // DEBUGDRAWMODEL_H
diff --git a/utils/hlmv/fileassociation.cpp b/utils/hlmv/fileassociation.cpp
new file mode 100644
index 0000000..2ca7c43
--- /dev/null
+++ b/utils/hlmv/fileassociation.cpp
@@ -0,0 +1,288 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: FileAssociation.cpp
+// last modified: May 04 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include "FileAssociation.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+
+
+
+FileAssociation *g_FileAssociation = 0;
+
+
+
+FileAssociation::FileAssociation ()
+: mxWindow (0, 100, 100, 400, 210, "File Associations", mxWindow::Dialog)
+{
+ cExtension = new mxChoice (this, 5, 5, 220, 22, IDC_EXTENSION);
+
+ //new mxButton (this, 230, 5, 75, 22, "Add", IDC_ADD);
+ //new mxButton (this, 310, 5, 75, 22, "Remove", IDC_REMOVE);
+
+ new mxGroupBox (this, 5, 30, 380, 115, "Assocations");
+ rbAction[0] = new mxRadioButton (this, 10, 50, 120, 22, "program", IDC_ACTION1, true);
+ rbAction[1] = new mxRadioButton (this, 10, 72, 120, 22, "associated program", IDC_ACTION2);
+ rbAction[2] = new mxRadioButton (this, 10, 94, 120, 22, "HLMV default", IDC_ACTION3);
+ rbAction[3] = new mxRadioButton (this, 10, 116, 120, 22, "none", IDC_ACTION4);
+ leProgram = new mxLineEdit (this, 130, 50, 220, 22, "", IDC_PROGRAM);
+ leProgram->setEnabled (false);
+ bChooseProgram = new mxButton (this, 352, 50, 22, 22, ">>", IDC_CHOOSEPROGRAM);
+ bChooseProgram->setEnabled (false);
+
+ rbAction[0]->setChecked (false);
+ rbAction[1]->setChecked (true);
+
+ new mxButton (this, 110, 155, 75, 22, "Ok", IDC_OK);
+ new mxButton (this, 215, 155, 75, 22, "Cancel", IDC_CANCEL);
+
+ initAssociations ();
+}
+
+
+
+FileAssociation::~FileAssociation ()
+{
+}
+
+
+
+int
+FileAssociation::handleEvent (mxEvent *event)
+{
+ if (event->event != mxEvent::Action)
+ return 0;
+
+ switch (event->action)
+ {
+ case IDC_EXTENSION:
+ {
+ int index = cExtension->getSelectedIndex ();
+ if (index >= 0)
+ setAssociation (index);
+ }
+ break;
+
+ case IDC_ACTION1:
+ case IDC_ACTION2:
+ case IDC_ACTION3:
+ case IDC_ACTION4:
+ {
+ leProgram->setEnabled (rbAction[0]->isChecked ());
+ bChooseProgram->setEnabled (rbAction[0]->isChecked ());
+
+ int index = cExtension->getSelectedIndex ();
+ if (index >= 0)
+ d_associations[index].association = event->action - IDC_ACTION1;
+
+ }
+ break;
+
+ case IDC_PROGRAM:
+ {
+ int index = cExtension->getSelectedIndex ();
+ if (index >= 0)
+ strcpy (d_associations[index].program, leProgram->getLabel ());
+ }
+ break;
+
+ case IDC_CHOOSEPROGRAM:
+ {
+ const char *ptr = mxGetOpenFileName (this, 0, "*.exe");
+ if (ptr)
+ {
+ leProgram->setLabel (ptr);
+
+ int index = cExtension->getSelectedIndex ();
+ if (index >= 0)
+ strcpy (d_associations[index].program, leProgram->getLabel ());
+ }
+ }
+ break;
+
+ case IDC_OK:
+ saveAssociations ();
+
+ case IDC_CANCEL:
+ setVisible (false);
+ break;
+ }
+
+ return 1;
+}
+
+
+
+void
+FileAssociation::initAssociations ()
+{
+ int i;
+
+ cExtension->removeAll ();
+
+ for (i = 0; i < 16; i++)
+ d_associations[i].association = -1;
+
+ char path[256];
+ strcpy (path, mx::getApplicationPath ());
+ strcat (path, "/hlmv.fa");
+ FILE *file = fopen (path, "rt");
+ if (!file)
+ return;
+
+ i = 0;
+ char line[256];
+ while (i < 16 && fgets (line, 256, file))
+ {
+ int j = 0;
+ while (line[++j] != '\"');
+ line[j] = '\0';
+ strcpy (d_associations[i].extension, &line[1]);
+
+ while (line[++j] != '\"');
+ int k = j + 1;
+ while (line[++j] != '\"');
+ line[j] = '\0';
+ strcpy (d_associations[i].program, &line[k]);
+
+ d_associations[i].association = atoi (&line[++j]);
+
+ cExtension->add (d_associations[i].extension);
+ ++i;
+ }
+
+ fclose (file);
+
+ setAssociation (0);
+}
+
+
+
+void
+FileAssociation::setAssociation (int index)
+{
+ cExtension->select (index);
+ leProgram->setLabel (d_associations[index].program);
+
+ for (int i = 0; i < 4; i++)
+ rbAction[i]->setChecked (i == d_associations[index].association);
+
+ leProgram->setEnabled (d_associations[index].association == 0);
+ bChooseProgram->setEnabled (d_associations[index].association == 0);
+
+ // TODO: check for valid associtaion
+#ifdef WIN32__
+ char path[256];
+
+ strcpy (path, mx_gettemppath ());
+ strcat (path, "/hlmvtemp.");
+ strcat (path, d_associations[index].extension);
+
+ FILE *file = fopen (path, "wb");
+ if (file)
+ fclose (file);
+
+ int val = (int) ShellExecute ((HWND) getHandle (), "open", path, 0, 0, SW_HIDE);
+ char str[32];
+ sprintf (str, "%d", val);
+ setLabel (str);
+ rbAction[1]->setEnabled (val != 31);
+/*
+ WORD dw = 0;
+ HICON hIcon = ExtractAssociatedIcon ((HINSTANCE) GetWindowLong ((HWND) getHandle (), GWL_HINSTANCE), path, &dw);
+ SendMessage ((HWND) getHandle (), WM_SETICON, (WPARAM) ICON_SMALL, (LPARAM) hIcon);
+ char str[32];
+ sprintf (str, "%d", (int) hIcon);
+ setLabel (str);
+*/
+ DeleteFile (path);
+
+ //DestroyIcon (hIcon);
+#endif
+
+ rbAction[2]->setEnabled (
+ !mx_strcasecmp (d_associations[index].extension, "mdl") ||
+ !mx_strcasecmp (d_associations[index].extension, "tga") ||
+ !mx_strcasecmp (d_associations[index].extension, "wav")
+ );
+}
+
+
+
+void
+FileAssociation::saveAssociations ()
+{
+ char path[256];
+
+ strcpy (path, mx::getApplicationPath ());
+ strcat (path, "/hlmv.fa");
+
+ FILE *file = fopen (path, "wt");
+ if (!file)
+ return;
+
+ for (int i = 0; i < 16; i++)
+ {
+ if (d_associations[i].association == -1)
+ break;
+
+ fprintf (file, "\"%s\" \"%s\" %d\n",
+ d_associations[i].extension,
+ d_associations[i].program,
+ d_associations[i].association);
+ }
+
+ fclose (file);
+}
+
+
+
+int
+FileAssociation::getMode (char *extension)
+{
+ for (int i = 0; i < 16; i++)
+ {
+ //if (!strcmp (d_associations[i].extension, mx_strlower (extension)))
+ if (!strcmp (d_associations[i].extension, extension))
+ return d_associations[i].association;
+ }
+
+ return -1;
+}
+
+
+
+char *
+FileAssociation::getProgram (char *extension)
+{
+ for (int i = 0; i < 16; i++)
+ {
+ //if (!strcmp (d_associations[i].extension, mx_strlower (extension)))
+ if (!strcmp (d_associations[i].extension, extension))
+ return d_associations[i].program;
+ }
+
+ return 0;
+}
diff --git a/utils/hlmv/fileassociation.h b/utils/hlmv/fileassociation.h
new file mode 100644
index 0000000..7fe2d6b
--- /dev/null
+++ b/utils/hlmv/fileassociation.h
@@ -0,0 +1,98 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: FileAssociation.h
+// last modified: Apr 28 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#ifndef INCLUDED_FILEASSOCIATION
+#define INCLUDED_FILEASSOCIATION
+
+
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+
+
+
+#define IDC_EXTENSION 1001
+#define IDC_ADD 1002
+#define IDC_REMOVE 1003
+#define IDC_ACTION1 1004
+#define IDC_ACTION2 1005
+#define IDC_ACTION3 1006
+#define IDC_ACTION4 1007
+#define IDC_PROGRAM 1008
+#define IDC_CHOOSEPROGRAM 1009
+#define IDC_OK 1010
+#define IDC_CANCEL 1011
+
+
+
+typedef struct
+{
+ char extension[16];
+ char program[256];
+ int association;
+} association_t;
+
+
+
+
+class mxChoice;
+class mxRadioButton;
+class mxLineEdit;
+class mxButton;
+
+
+
+class FileAssociation : public mxWindow
+{
+ mxChoice *cExtension;
+ mxRadioButton *rbAction[4];
+ mxLineEdit *leProgram;
+ mxButton *bChooseProgram;
+ association_t d_associations[16];
+
+ void initAssociations ();
+ void saveAssociations ();
+
+public:
+ // CREATORS
+ FileAssociation ();
+ virtual ~FileAssociation ();
+
+ // MANIPULATORS
+ int handleEvent (mxEvent *event);
+ void setAssociation (int index);
+
+ // ACCESSORS
+ int getMode (char *extension);
+ char *getProgram (char *extension);
+};
+
+
+
+extern FileAssociation *g_FileAssociation;
+
+
+
+#endif // INCLUDED_FILEASSOCIATION \ No newline at end of file
diff --git a/utils/hlmv/hlmv.rc b/utils/hlmv/hlmv.rc
new file mode 100644
index 0000000..2ff73cf
--- /dev/null
+++ b/utils/hlmv/hlmv.rc
@@ -0,0 +1,84 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+MX_ICON ICON DISCARDABLE "icon1.ico"
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+/////////////////////////////////////////////////////////////////////////////
+// German (Switzerland) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DES)
+#ifdef _WIN32
+LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_SWISS
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // German (Switzerland) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/utils/hlmv/hlmv.vpc b/utils/hlmv/hlmv.vpc
new file mode 100644
index 0000000..df9f237
--- /dev/null
+++ b/utils/hlmv/hlmv.vpc
@@ -0,0 +1,204 @@
+//-----------------------------------------------------------------------------
+// HLMV.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,..\common"
+ $PreprocessorDefinitions "$BASE;VECTOR;PROTECTED_THINGS_DISABLE"
+ }
+
+ $Linker
+ {
+ $AdditionalDependencies "$BASE comctl32.lib winmm.lib"
+ $Version "1.1"
+ $EntryPoint "mainCRTStartup"
+ }
+}
+
+$Project "Hlmv"
+{
+ $Folder "Source Files"
+ {
+ $File "attachments_window.cpp"
+ $File "controlpanel.cpp"
+ $File "debugdrawmodel.cpp"
+ $File "fileassociation.cpp"
+ $File "matsyswin.cpp"
+ $File "mdlviewer.cpp"
+ $File "mxLineEdit2.cpp"
+ $File "pakviewer.cpp"
+ $File "physmesh.cpp"
+ $File "studio_flex.cpp"
+ $File "studio_render.cpp"
+ $File "studio_utils.cpp"
+ $File "sys_win.cpp"
+ $File "viewersettings.cpp"
+ }
+
+ $Folder "Header Files"
+ {
+ $File "anorms.h"
+ $File "attachments_window.h"
+ $File "controlpanel.h"
+ $File "debugdrawmodel.h"
+ $File "fileassociation.h"
+ $File "matsyswin.h"
+ $File "mdlviewer.h"
+ $File "mxLineEdit2.h"
+ $File "pakviewer.h"
+ $File "physmesh.h"
+ $File "resource.h"
+ $File "studio_render.h"
+ $File "studiomodel.h"
+ $File "sys.h"
+ $File "viewersettings.h"
+ }
+
+ $Folder "External Source Files"
+ {
+ $File "$SRCDIR\public\bone_setup.cpp"
+ $File "$SRCDIR\public\collisionutils.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\filesystem_init.cpp"
+ $File "$SRCDIR\public\jigglebones.cpp"
+ $File "$SRCDIR\public\studio.cpp"
+ }
+
+ $Folder "External Header Files"
+ {
+ $File "$SRCDIR\public\mathlib\amd3dx.h"
+ $File "$SRCDIR\public\basehandle.h"
+ $File "$SRCDIR\public\tier0\basetypes.h"
+ $File "$SRCDIR\public\bitvec.h"
+ $File "$SRCDIR\public\bone_accessor.h"
+ $File "$SRCDIR\public\bone_setup.h"
+ $File "$SRCDIR\public\bspflags.h"
+ $File "$SRCDIR\public\tier1\characterset.h"
+ $File "..\common\cmdlib.h"
+ $File "$SRCDIR\public\cmodel.h"
+ $File "$SRCDIR\public\CollisionUtils.h"
+ $File "$SRCDIR\public\tier0\commonmacros.h"
+ $File "$SRCDIR\public\mathlib\compressed_vector.h"
+ $File "$SRCDIR\public\const.h"
+ $File "$SRCDIR\public\vphysics\constraints.h"
+ $File "$SRCDIR\public\tier0\dbg.h"
+ $File "debugdrawmodel.h"
+ $File "$SRCDIR\public\tier0\fasttimer.h"
+ $File "$SRCDIR\public\filesystem.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ $File "$SRCDIR\public\tier1\fmtstr.h"
+ $File "$SRCDIR\public\gametrace.h"
+ $File "$SRCDIR\public\appframework\IAppSystem.h"
+ $File "$SRCDIR\public\tier0\icommandline.h"
+ $File "$SRCDIR\public\ihandleentity.h"
+ $File "$SRCDIR\public\materialsystem\imaterial.h"
+ $File "$SRCDIR\public\materialsystem\imaterialproxyfactory.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystem.h"
+ $File "$SRCDIR\public\materialsystem\imaterialsystemhardwareconfig.h"
+ $File "$SRCDIR\public\materialsystem\imaterialvar.h"
+ $File "$SRCDIR\public\materialsystem\imesh.h"
+ $File "$SRCDIR\public\tier1\interface.h"
+ $File "$SRCDIR\public\ISpatialPartition.h"
+ $File "$SRCDIR\public\istudiorender.h"
+ $File "$SRCDIR\public\materialsystem\itexture.h"
+ $File "$SRCDIR\public\jigglebones.h"
+ $File "$SRCDIR\public\materialsystem\materialsystem_config.h"
+ $File "$SRCDIR\public\mathlib\mathlib.h"
+ $File "$SRCDIR\public\tier0\memdbgoff.h"
+ $File "$SRCDIR\public\tier0\memdbgon.h"
+ $File "$SRCDIR\public\tier1\mempool.h"
+ $File "$SRCDIR\public\mouthinfo.h"
+ $File "$SRCDIR\public\phyfile.h"
+ $File "..\common\physdll.h"
+ $File "$SRCDIR\public\tier0\platform.h"
+ $File "$SRCDIR\public\tier0\protected_things.h"
+ $File "$SRCDIR\public\vstdlib\random.h"
+ $File "$SRCDIR\public\string_t.h"
+ $File "$SRCDIR\public\tier1\strtools.h"
+ $File "$SRCDIR\public\studio.h"
+ $File "$SRCDIR\public\texture_group_names.h"
+ $File "$SRCDIR\public\tier1\utlbuffer.h"
+ $File "$SRCDIR\public\tier1\utldict.h"
+ $File "$SRCDIR\public\tier1\utllinkedlist.h"
+ $File "$SRCDIR\public\tier1\utlmemory.h"
+ $File "$SRCDIR\public\tier1\utlrbtree.h"
+ $File "$SRCDIR\public\tier1\utlsymbol.h"
+ $File "$SRCDIR\public\tier1\utlvector.h"
+ $File "$SRCDIR\public\vcollide.h"
+ $File "$SRCDIR\public\vcollide_parse.h"
+ $File "$SRCDIR\public\mathlib\vector.h"
+ $File "$SRCDIR\public\mathlib\vector2d.h"
+ $File "$SRCDIR\public\mathlib\vector4d.h"
+ $File "$SRCDIR\public\mathlib\vmatrix.h"
+ $File "$SRCDIR\public\vphysics_interface.h"
+ $File "$SRCDIR\public\mathlib\vplane.h"
+ $File "$SRCDIR\public\tier0\vprof.h"
+ $File "$SRCDIR\public\vstdlib\vstdlib.h"
+
+ $Folder "mxtk"
+ {
+ $File "..\..\public\mxtk\mx.h"
+ $File "..\..\public\mxtk\mxBmp.h"
+ $File "..\..\public\mxtk\mxButton.h"
+ $File "..\..\public\mxtk\mxCheckBox.h"
+ $File "..\..\public\mxtk\mxChoice.h"
+ $File "..\..\public\mxtk\mxChooseColor.h"
+ $File "..\..\public\mxtk\mxEvent.h"
+ $File "..\..\public\mxtk\mxFileDialog.h"
+ $File "..\..\public\mxtk\mxGlWindow.h"
+ $File "..\..\public\mxtk\mxGroupBox.h"
+ $File "..\..\public\mxtk\mxImage.h"
+ $File "..\..\public\mxtk\mxInit.h"
+ $File "..\..\public\mxtk\mxLabel.h"
+ $File "..\..\public\mxtk\mxLineEdit.h"
+ $File "mxLineEdit2.h"
+ $File "..\..\public\mxtk\mxLinkedList.h"
+ $File "..\..\public\mxtk\mxListBox.h"
+ $File "..\..\public\mxtk\mxMatSysWindow.h"
+ $File "..\..\public\mxtk\mxMenu.h"
+ $File "..\..\public\mxtk\mxMenuBar.h"
+ $File "..\..\public\mxtk\mxMessageBox.h"
+ $File "..\..\public\mxtk\mxpath.h"
+ $File "..\..\public\mxtk\mxPcx.h"
+ $File "..\..\public\mxtk\mxPopupMenu.h"
+ $File "..\..\public\mxtk\mxProgressBar.h"
+ $File "..\..\public\mxtk\mxRadioButton.h"
+ $File "..\..\public\mxtk\mxScrollbar.h"
+ $File "..\..\public\mxtk\mxSlider.h"
+ $File "..\..\public\mxtk\mxstring.h"
+ $File "..\..\public\mxtk\mxTab.h"
+ $File "..\..\public\mxtk\mxTga.h"
+ $File "..\..\public\mxtk\mxToggleButton.h"
+ $File "..\..\public\mxtk\mxToolTip.h"
+ $File "..\..\public\mxtk\mxTreeView.h"
+ $File "..\..\public\mxtk\mxWidget.h"
+ $File "..\..\public\mxtk\mxWindow.h"
+ }
+ }
+
+ $Folder "Resources"
+ {
+ $File "hlmv.rc"
+ $File "icon1.ico"
+ $File "resource.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib tier2
+ $Lib appframework
+ $Lib bitmap
+ $Lib mathlib
+ $Lib $LIBCOMMON\mxtoolkitwin32
+ }
+}
diff --git a/utils/hlmv/icon1.ico b/utils/hlmv/icon1.ico
new file mode 100644
index 0000000..1d59980
--- /dev/null
+++ b/utils/hlmv/icon1.ico
Binary files differ
diff --git a/utils/hlmv/makefile b/utils/hlmv/makefile
new file mode 100644
index 0000000..fa1e4ff
--- /dev/null
+++ b/utils/hlmv/makefile
@@ -0,0 +1,59 @@
+# simple Makefile for Half-Life Model Viewer
+
+CC = g++
+CFLAGS = -O2
+IFLAGS =
+LFLAGS = -s
+LIBS = -lmxtk-qt -lqt -lqgl -lGL -lGLU -lXaw
+
+HLMV = ../bin/hlmv
+OBJS = \
+ ControlPanel.o \
+ FileAssociation.o \
+ GlWindow.o \
+ ViewerSettings.o \
+ mathlib.o \
+ mdlviewer.o \
+ pakviewer.o \
+ studio_render.o \
+ studio_utils.o \
+
+all: $(HLMV)
+
+$(HLMV): $(OBJS)
+ $(CC) $(LFLAGS) -o $(HLMV) $(OBJS) $(LIBS)
+
+ControlPanel.o: ControlPanel.cpp ControlPanel.h ViewerSettings.h StudioModel.h GlWindow.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) ControlPanel.cpp
+
+FileAssociation.o: FileAssociation.cpp FileAssociation.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) FileAssociation.cpp
+
+GlWindow.o: GlWindow.cpp GlWindow.h StudioModel.h ViewerSettings.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) GlWindow.cpp
+
+ViewerSettings.o: ViewerSettings.cpp ViewerSettings.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) ViewerSettings.cpp
+
+mathlib.o: mathlib.c mathlib.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) mathlib.c
+
+mdlviewer.o: mdlviewer.cpp mdlviewer.h GlWindow.h ControlPanel.h StudioModel.h pakviewer.h FileAssociation.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) mdlviewer.cpp
+
+pakviewer.o: pakviewer.cpp pakviewer.h mdlviewer.h GlWindow.h ControlPanel.h StudioModel.h FileAssociation.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) pakviewer.cpp
+
+
+studio_render.o: studio_render.cpp StudioModel.h ViewerSettings.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) studio_render.cpp
+
+studio_utils.o: studio_utils.cpp StudioModel.h
+ $(CC) -c $(CFLAGS) $(IFLAGS) studio_utils.cpp
+
+
+
+# clean
+
+clean:
+ rm *.o
diff --git a/utils/hlmv/matsyswin.cpp b/utils/hlmv/matsyswin.cpp
new file mode 100644
index 0000000..0eb32f1
--- /dev/null
+++ b/utils/hlmv/matsyswin.cpp
@@ -0,0 +1,1182 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/*
+
+ glapp.c - Simple OpenGL shell
+
+ There are several options allowed on the command line. They are:
+ -height : what window/screen height do you want to use?
+ -width : what window/screen width do you want to use?
+ -bpp : what color depth do you want to use?
+ -window : create a rendering window rather than full-screen
+ -fov : use a field of view other than 90 degrees
+*/
+
+
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: MatSysWindow.cpp
+// last modified: May 04 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include <mxtk/mx.h>
+#include <mxtk/mxMessageBox.h>
+#include <mxtk/mxTga.h>
+#include <mxtk/mxPcx.h>
+#include <mxtk/mxBmp.h>
+#include <mxtk/mxMatSysWindow.h>
+// #include "gl.h"
+// #include <GL/glu.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <string.h>
+#include "MatSysWin.h"
+#include "MDLViewer.h"
+#include "StudioModel.h"
+#include "ControlPanel.h"
+#include "ViewerSettings.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterialproxyfactory.h"
+#include "filesystem.h"
+#include <keyvalues.h>
+#include "materialsystem/imesh.h"
+#include "materialsystem/IMaterialSystemHardwareConfig.h"
+#include "materialsystem/itexture.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "tier0/dbg.h"
+#include "istudiorender.h"
+#include "tier0/icommandline.h"
+#include "mathlib/vmatrix.h"
+#include "studio_render.h"
+#include "vstdlib/cvar.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "soundsystem/isoundsystem.h"
+#include "soundchars.h"
+#include <optimize.h>
+#include "valve_ipc_win32.h"
+
+extern char g_appTitle[];
+extern bool g_bInError;
+extern int g_dxlevel;
+
+extern ISoundEmitterSystemBase *g_pSoundEmitterBase;
+extern ISoundSystem *g_pSoundSystem;
+
+extern CValveIpcClientUtl g_HlmvIpcClient;
+extern bool g_bHlmvMaster;
+
+
+void UpdateSounds()
+{
+ static double prev = 0;
+ double curr = (double) mx::getTickCount () / 1000.0;
+ if ( prev != 0 )
+ {
+ double dt = (curr - prev);
+ g_pSoundSystem->Update( dt * g_viewerSettings.speedScale );
+ }
+ prev = curr;
+}
+
+
+// FIXME: move all this to mxMatSysWin
+
+class DummyMaterialProxyFactory : public IMaterialProxyFactory
+{
+public:
+ virtual IMaterialProxy *CreateProxy( const char *proxyName ) {return NULL;}
+ virtual void DeleteProxy( IMaterialProxy *pProxy ) {}
+};
+DummyMaterialProxyFactory g_DummyMaterialProxyFactory;
+
+
+static void ReleaseMaterialSystemObjects()
+{
+ StudioModel::ReleaseStudioModel();
+}
+
+static void RestoreMaterialSystemObjects( int nChangeFlags )
+{
+ StudioModel::RestoreStudioModel();
+ g_ControlPanel->OnLoadModel();
+}
+
+void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig)
+{
+ if ( g_viewerSettings.enableNormalMapping )
+ {
+ pConfig->m_Flags &= ~MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP;
+ }
+ else
+ {
+ pConfig->m_Flags |= MATSYS_VIDCFG_FLAGS_DISABLE_BUMPMAP;
+ }
+
+ if ( g_viewerSettings.enableSpecular)
+ {
+ pConfig->m_Flags &= ~MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR;
+ }
+ else
+ {
+ pConfig->m_Flags |= MATSYS_VIDCFG_FLAGS_DISABLE_SPECULAR;
+ }
+
+ if ( g_viewerSettings.enableParallaxMapping )
+ {
+ pConfig->m_Flags |= MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING;
+ }
+ else
+ {
+ pConfig->m_Flags &= ~MATSYS_VIDCFG_FLAGS_ENABLE_PARALLAX_MAPPING;
+ }
+
+
+ // JasonM...did we foul this up?
+
+}
+
+MatSysWindow *g_MatSysWindow = 0;
+
+Vector g_vright( 50, 50, 0 ); // needs to be set to viewer's right in order for chrome to work
+
+IMaterial *g_materialBackground = NULL;
+IMaterial *g_materialWireframe = NULL;
+IMaterial *g_materialWireframeVertexColor = NULL;
+IMaterial *g_materialWireframeVertexColorNoCull = NULL;
+IMaterial *g_materialDebugCopyBaseTexture = NULL;
+IMaterial *g_materialFlatshaded = NULL;
+IMaterial *g_materialSmoothshaded = NULL;
+IMaterial *g_materialBones = NULL;
+IMaterial *g_materialLines = NULL;
+IMaterial *g_materialFloor = NULL;
+IMaterial *g_materialVertexColor = NULL;
+IMaterial *g_materialShadow = NULL;
+
+
+MatSysWindow::MatSysWindow (mxWindow *parent, int x, int y, int w, int h, const char *label, int style)
+: mxMatSysWindow (parent, x, y, w, h, label, style)
+{
+ g_pMaterialSystem->SetMaterialProxyFactory( &g_DummyMaterialProxyFactory );
+
+ m_pCubemapTexture = NULL;
+ m_hWnd = (HWND)getHandle();
+
+ MaterialSystem_Config_t config;
+ config = g_pMaterialSystem->GetCurrentConfigForVideoCard();
+ InitMaterialSystemConfig(&config);
+ if ( g_dxlevel != 0 )
+ {
+ config.dxSupportLevel = g_dxlevel;
+ }
+
+// config.m_VideoMode.m_Width = config.m_VideoMode.m_Height = 0;
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_WINDOWED, true );
+ config.SetFlag( MATSYS_VIDCFG_FLAGS_RESIZING, true );
+
+ if (!g_pMaterialSystem->SetMode( ( void * )m_hWnd, config ) )
+ {
+ return;
+ }
+
+ g_pMaterialSystem->OverrideConfig( config, false );
+
+ g_pMaterialSystem->AddReleaseFunc( ReleaseMaterialSystemObjects );
+ g_pMaterialSystem->AddRestoreFunc( RestoreMaterialSystemObjects );
+
+ m_pCubemapTexture = g_pMaterialSystem->FindTexture( "hlmv/cubemap", NULL, true );
+ m_pCubemapTexture->IncrementReferenceCount();
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->BindLocalCubemap( m_pCubemapTexture );
+
+ g_materialBackground = g_pMaterialSystem->FindMaterial("hlmv/background", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialBackground )
+ {
+ g_materialBackground->AddRef();
+ }
+ g_materialWireframe = g_pMaterialSystem->FindMaterial("debug/debugmrmwireframe", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialWireframe )
+ {
+ g_materialWireframe->AddRef();
+ }
+ g_materialWireframeVertexColor = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialWireframeVertexColor )
+ {
+ g_materialWireframeVertexColor->AddRef();
+ }
+ // test: create this from code - you need a vmt to make $nocull 1 happen, can't do it from the render context
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "Wireframe" );
+ pVMTKeyValues->SetInt("$ignorez", 1);
+ pVMTKeyValues->SetInt("$nocull", 1);
+ pVMTKeyValues->SetInt("$vertexcolor", 1);
+ pVMTKeyValues->SetInt("$decal", 1);
+ g_materialWireframeVertexColorNoCull = g_pMaterialSystem->CreateMaterial( "debug/wireframenocull", pVMTKeyValues );
+ if ( g_materialWireframeVertexColorNoCull )
+ {
+ g_materialWireframeVertexColorNoCull->AddRef();
+ }
+ }
+ {
+ KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" );
+ pVMTKeyValues->SetString("$basetexture", "vgui/white" );
+ g_materialDebugCopyBaseTexture = g_pMaterialSystem->CreateMaterial( "debug/copybasetexture", pVMTKeyValues );
+ if ( g_materialDebugCopyBaseTexture )
+ {
+ g_materialDebugCopyBaseTexture->AddRef();
+ }
+ }
+
+ g_materialFlatshaded = g_pMaterialSystem->FindMaterial("debug/debugdrawflatpolygons", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialFlatshaded )
+ {
+ g_materialFlatshaded->AddRef();
+ }
+ g_materialSmoothshaded = g_pMaterialSystem->FindMaterial("debug/debugmrmfullbright2", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialSmoothshaded )
+ {
+ g_materialSmoothshaded->AddRef();
+ }
+ g_materialBones = g_pMaterialSystem->FindMaterial("debug/debugskeleton", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialBones )
+ {
+ g_materialBones->AddRef();
+ }
+ g_materialLines = g_pMaterialSystem->FindMaterial("debug/debugwireframevertexcolor", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialLines )
+ {
+ g_materialLines->AddRef();
+ }
+ g_materialFloor = g_pMaterialSystem->FindMaterial("hlmv/floor", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialFloor )
+ {
+ g_materialFloor->AddRef();
+ }
+ g_materialVertexColor = g_pMaterialSystem->FindMaterial("debug/debugvertexcolor", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialVertexColor )
+ {
+ g_materialVertexColor->AddRef();
+ }
+ g_materialShadow = g_pMaterialSystem->FindMaterial("hlmv/shadow", TEXTURE_GROUP_OTHER, true);
+ if ( g_materialShadow )
+ {
+ g_materialShadow->AddRef();
+ }
+ if (!parent)
+ setVisible (true);
+ else
+ mx::setIdleWindow (this);
+}
+
+
+
+MatSysWindow::~MatSysWindow ()
+{
+ if (m_pCubemapTexture)
+ {
+ m_pCubemapTexture->DecrementReferenceCount();
+ }
+ mx::setIdleWindow (0);
+}
+
+
+
+int
+MatSysWindow::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ static float oldrx = 0, oldry = 0, oldtz = 50, oldtx = 0, oldty = 0;
+ static float oldlrx = 0, oldlry = 0;
+ static int oldx, oldy;
+
+ switch (event->event)
+ {
+
+ case mxEvent::Idle:
+ {
+ static double prev;
+ double curr = (double) mx::getTickCount () / 1000.0;
+ double dt = (curr - prev);
+
+ // clamp to 100fps
+ if (dt >= 0.0 && dt < 0.01)
+ {
+ Sleep( 10 - dt * 1000.0 );
+ return 1;
+ }
+
+ if ( prev != 0.0 )
+ {
+ // dt = 0.001;
+
+ g_pStudioModel->AdvanceFrame ( dt * g_viewerSettings.speedScale );
+ if ( g_viewerSettings.animateWeapons )
+ {
+ const char *pszMainSequenceName = g_pStudioModel->GetSequenceName( g_pStudioModel->GetSequence() );
+ if ( pszMainSequenceName )
+ {
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
+ {
+ if ( !g_pStudioExtraModel[ i ] )
+ continue;
+
+ // match weapon sequence and frame to marine
+ int iSequence = g_pStudioExtraModel[ i ]->LookupSequence( pszMainSequenceName );
+ if ( iSequence == -1 )
+ {
+ g_pStudioExtraModel[ i ]->SetFrame( 0 );
+ }
+ else
+ {
+ g_pStudioExtraModel[ i ]->SetSequence( iSequence );
+ g_pStudioExtraModel[ i ]->SetFrame( g_pStudioModel->GetCycle() * g_pStudioExtraModel[ i ]->GetMaxFrame() );
+ }
+ }
+ }
+ }
+ g_ControlPanel->updateFrameSlider( );
+ g_ControlPanel->updateGroundSpeed( );
+ }
+ prev = curr;
+
+ if (!g_viewerSettings.pause)
+ redraw ();
+
+ g_ControlPanel->updateTransitionAmount();
+
+ UpdateSounds();
+
+ return 1;
+ }
+ break;
+
+ case mxEvent::MouseUp:
+ {
+ g_viewerSettings.mousedown = false;
+ }
+ break;
+
+ case mxEvent::MouseDown:
+ {
+ g_viewerSettings.mousedown = true;
+
+ oldrx = g_pStudioModel->m_angles[0];
+ oldry = g_pStudioModel->m_angles[1];
+ oldtx = g_pStudioModel->m_origin[0];
+ oldty = g_pStudioModel->m_origin[1];
+ oldtz = g_pStudioModel->m_origin[2];
+ oldx = event->x;
+ oldy = event->y;
+ oldlrx = g_viewerSettings.lightrot[1];
+ oldlry = g_viewerSettings.lightrot[0];
+ g_viewerSettings.pause = false;
+
+ float r = 1.0/3.0 * min( w(), h() );
+
+ float d = sqrt( ( float )( (event->x - w()/2) * (event->x - w()/2) + (event->y - h()/2) * (event->y - h()/2) ) );
+
+ if (d < r)
+ g_viewerSettings.rotating = false;
+ else
+ g_viewerSettings.rotating = true;
+
+ return 1;
+ }
+ break;
+
+ case mxEvent::MouseDrag:
+ {
+ bool bSendModelTransform = true;
+
+ if ( event->buttons & mxEvent::MouseLeftButton )
+ {
+ if ( event->modifiers & mxEvent::KeyShift )
+ {
+ g_pStudioModel->m_origin[1] = oldty - (float)( event->x - oldx );
+ g_pStudioModel->m_origin[2] = oldtz + (float)( event->y - oldy );
+ }
+ else if ( event->modifiers & mxEvent::KeyCtrl )
+ {
+ float ry = (float)( event->y - oldy );
+ float rx = (float)( event->x - oldx );
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement = QAngle( ry, rx, 0 );
+
+ matrix3x4_t tmp1, tmp2, tmp3;
+ AngleMatrix( g_viewerSettings.lightrot, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, g_viewerSettings.lightrot );
+
+ // g_viewerSettings.lightrot[0] = oldlrx + (float) (event->y - oldy);
+ // g_viewerSettings.lightrot[1] = oldlry + (float) (event->x - oldx);
+
+ bSendModelTransform = false;
+ }
+ else
+ {
+ if ( !g_viewerSettings.rotating )
+ {
+ float ry = (float)( event->y - oldy );
+ float rx = (float)( event->x - oldx );
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement;
+ matrix3x4_t tmp1, tmp2, tmp3;
+
+ movement = QAngle( 0, rx, 0 );
+ AngleMatrix( g_pStudioModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp1, tmp2, tmp3 );
+ MatrixAngles( tmp3, g_pStudioModel->m_angles );
+
+ movement = QAngle( ry, 0, 0 );
+ AngleMatrix( g_pStudioModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, g_pStudioModel->m_angles );
+ }
+ else
+ {
+ float ang1 = ( 180 / 3.1415 ) * atan2( oldx - w() / 2.0, oldy - h() / 2.0 );
+ float ang2 = ( 180 / 3.1415 ) * atan2( event->x - w() / 2.0, event->y - h() / 2.0 );
+ oldx = event->x;
+ oldy = event->y;
+
+ QAngle movement = QAngle( 0, 0, ang2 - ang1 );
+
+ matrix3x4_t tmp1, tmp2, tmp3;
+ AngleMatrix( g_pStudioModel->m_angles, tmp1 );
+ AngleMatrix( movement, tmp2 );
+ ConcatTransforms( tmp2, tmp1, tmp3 );
+ MatrixAngles( tmp3, g_pStudioModel->m_angles );
+ }
+ }
+ }
+ else if ( event->buttons & mxEvent::MouseRightButton )
+ {
+ g_pStudioModel->m_origin[0] = oldtx + (float)( event->y - oldy );
+ }
+
+ if ( g_bHlmvMaster )
+ {
+ if ( bSendModelTransform )
+ {
+ g_MDLViewer->SendModelTransformToLinkedHlmv();
+ }
+ else
+ {
+ g_MDLViewer->SendLightRotToLinkedHlmv();
+ }
+ }
+
+ redraw();
+
+ return 1;
+ }
+ break;
+
+ case mxEvent::KeyDown:
+ {
+ switch (event->key)
+ {
+ case VK_F5: // F5
+ {
+ g_MDLViewer->Refresh();
+ break;
+ }
+ case 32:
+ {
+ int iSeq = g_pStudioModel->GetSequence ();
+ if (iSeq == g_pStudioModel->SetSequence (iSeq + 1))
+ {
+ g_pStudioModel->SetSequence (0);
+ }
+ }
+ break;
+ }
+ }
+ break;
+
+ } // switch (event->event)
+
+ return 1;
+}
+
+
+void DrawBackground()
+{
+ if (!g_viewerSettings.showBackground)
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind(g_materialBackground);
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ {
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ float dist=-15000.0f;
+ float tMin=0, tMax=1;
+
+ meshBuilder.Position3f(-dist, dist, dist);
+ meshBuilder.TexCoord2f( 0, tMin,tMax );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, dist, dist);
+ meshBuilder.TexCoord2f( 0, tMax,tMax );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist,-dist, dist);
+ meshBuilder.TexCoord2f( 0, tMax,tMin );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f(-dist,-dist, dist);
+ meshBuilder.TexCoord2f( 0, tMin,tMin );
+ meshBuilder.Color4ub( 255, 255, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void DrawHelpers()
+{
+ if (g_viewerSettings.mousedown)
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->LoadIdentity();
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 360 / 5 );
+
+ if (g_viewerSettings.rotating)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 0 );
+
+ for (int i = 0; i < 360; i += 5)
+ {
+ float a = i * (3.151492653/180.0f);
+
+ if (g_viewerSettings.rotating)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 0 );
+
+ meshBuilder.Position3f( sin( a ), cos( a ), -3.0f );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+
+void DrawGroundPlane()
+{
+ if (!g_viewerSettings.showGround)
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind(g_materialFloor);
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
+
+ pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
+
+ static Vector tMap( 0, 0, 0 );
+ static Vector dxMap( 1, 0, 0 );
+ static Vector dyMap( 0, 1, 0 );
+
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ g_pStudioModel->GetMovement( g_pStudioModel->m_prevGroundCycles, deltaPos, deltaAngles );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh();
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 );
+
+ float scale = 10.0;
+ float dist=-100.0f;
+
+ float dpdd = scale / dist;
+
+ tMap.x = tMap.x + dxMap.x * deltaPos.x * dpdd + dxMap.y * deltaPos.y * dpdd;
+ tMap.y = tMap.y + dyMap.x * deltaPos.x * dpdd + dyMap.y * deltaPos.y * dpdd;
+
+ while (tMap.x < 0.0) tMap.x += 1.0;
+ while (tMap.x > 1.0) tMap.x += -1.0;
+ while (tMap.y < 0.0) tMap.y += 1.0;
+ while (tMap.y > 1.0) tMap.y += -1.0;
+
+ VectorYawRotate( dxMap, -deltaAngles.y, dxMap );
+ VectorYawRotate( dyMap, -deltaAngles.y, dyMap );
+
+ // ARRGHHH, this is right but I don't know what I've done
+ meshBuilder.Position3f( -dist, dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (-dxMap.x - dyMap.x) * scale, tMap.y + (dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x - dyMap.x) * scale, tMap.y + (-dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, -dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x + dyMap.x) * scale, tMap.y + (-dxMap.y - dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( -dist, -dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x - (dxMap.x - dyMap.x) * scale, tMap.y - (-dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 255 );
+ meshBuilder.AdvanceVertex();
+
+ // draw underside
+ meshBuilder.Position3f( -dist, dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (-dxMap.x - dyMap.x) * scale, tMap.y + (dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 128 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( -dist, -dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x - (dxMap.x - dyMap.x) * scale, tMap.y - (-dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 128 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, -dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x + dyMap.x) * scale, tMap.y + (-dxMap.y - dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 128 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( dist, dist, 0 );
+ meshBuilder.TexCoord2f( 0, tMap.x + (dxMap.x - dyMap.x) * scale, tMap.y + (-dxMap.y + dyMap.y) * scale );
+ meshBuilder.Color4ub( 128, 128, 128, 128 );
+ meshBuilder.AdvanceVertex();
+
+
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PopMatrix();
+}
+
+
+
+
+void DrawMovementBoxes()
+{
+ if (!g_viewerSettings.showMovement)
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind(g_materialFloor);
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
+
+ pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
+
+ static matrix3x4_t mStart( 1, 0, 0, 0 , 0, 1, 0, 0 , 0, 0, 1, 0 );
+ matrix3x4_t mTemp;
+ static float prevframes[5];
+
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ g_pStudioModel->GetMovement( prevframes, deltaPos, deltaAngles );
+
+ AngleMatrix( deltaAngles, deltaPos, mTemp );
+ MatrixInvert( mTemp, mTemp );
+ ConcatTransforms( mTemp, mStart, mStart );
+
+ Vector bboxMin, bboxMax;
+ g_pStudioModel->ExtractBbox( bboxMin, bboxMax );
+
+ static float prevCycle = 0.0;
+
+ if (fabs( g_pStudioModel->GetFrame( 0 ) - prevCycle) > 0.5)
+ {
+ SetIdentityMatrix( mStart );
+ }
+ prevCycle = g_pStudioModel->GetFrame( 0 );
+
+ // starting position
+ {
+ float color[] = { 0.7, 1, 0, 0.5 };
+ float wirecolor[] = { 1, 1, 0, 1.0 };
+ g_pStudioModel->drawTransparentBox( bboxMin, bboxMax, mStart, color, wirecolor );
+ }
+
+ // current position
+ {
+ float color[] = { 1, 0.7, 0, 0.5 };
+ float wirecolor[] = { 1, 0, 0, 1.0 };
+ SetIdentityMatrix( mTemp );
+ g_pStudioModel->drawTransparentBox( bboxMin, bboxMax, mTemp, color, wirecolor );
+ }
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PopMatrix();
+}
+
+
+
+char const *HLMV_TranslateSoundName( char const *soundname, StudioModel *model )
+{
+ if ( Q_stristr( soundname, ".wav" ) )
+ return PSkipSoundChars( soundname );
+
+ if ( model )
+ {
+ return PSkipSoundChars( g_pSoundEmitterBase->GetWavFileForSound( soundname, model->GetFileName() ) );
+ }
+
+ return PSkipSoundChars( g_pSoundEmitterBase->GetWavFileForSound( soundname, NULL ) );
+}
+
+
+void PlaySound( const char *pSoundName, StudioModel *pStudioModel )
+{
+ // Play Sound
+ if (!g_viewerSettings.playSounds)
+ return;
+
+ if ( pSoundName == NULL || pSoundName[ 0 ] == '\0' )
+ return;
+
+ const char *pSoundFileName = HLMV_TranslateSoundName( pSoundName, pStudioModel );
+
+ char filename[ 256 ];
+ sprintf( filename, "sound/%s", pSoundFileName );
+ CAudioSource *pAudioSource = g_pSoundSystem->FindOrAddSound( filename );
+ if ( pAudioSource == NULL )
+ return;
+
+ float volume = VOL_NORM;
+ gender_t gender = GENDER_NONE;
+ if ( pStudioModel )
+ {
+ gender = g_pSoundEmitterBase->GetActorGender( pStudioModel->GetFileName() );
+ }
+
+ CSoundParameters params;
+ if ( !Q_stristr( pSoundName, ".wav" ) &&
+ g_pSoundEmitterBase->GetParametersForSound( pSoundName, params, gender ) )
+ {
+ volume = params.volume;
+ }
+
+ g_pSoundSystem->PlaySound( pAudioSource, volume, NULL );
+}
+
+
+// copied from baseentity.cpp
+// HACK: This must match the #define in cl_animevent.h in the client .dll code!!!
+#define CL_EVENT_SOUND 5004
+#define CL_EVENT_FOOTSTEP_LEFT 6004
+#define CL_EVENT_FOOTSTEP_RIGHT 6005
+#define CL_EVENT_MFOOTSTEP_LEFT 6006
+#define CL_EVENT_MFOOTSTEP_RIGHT 6007
+
+// copied from scriptevent.h
+#define SCRIPT_EVENT_SOUND 1004 // Play named wave file (on CHAN_BODY)
+#define SCRIPT_EVENT_SOUND_VOICE 1008 // Play named wave file (on CHAN_VOICE)
+
+
+void PlaySounds( StudioModel *pStudioModel )
+{
+ if ( pStudioModel == NULL )
+ return;
+
+ int iLayer = g_ControlPanel->getFrameSelection();
+ float flFrame = pStudioModel->GetFrame( iLayer );
+ float flTime = flFrame / 30.0f; // pStudioModel->GetSequenceTime()
+
+ float prevtime = flTime - pStudioModel->GetTimeDelta();
+ float currtime = flTime;
+
+ float duration = pStudioModel->GetDuration();
+
+ prevtime = fmod( prevtime, duration );
+ currtime = fmod( currtime, duration );
+
+ float prevcycle = prevtime / duration;
+ float currcycle = currtime / duration;
+
+ CStudioHdr *pStudioHdr = pStudioModel->GetStudioHdr();
+ if ( pStudioHdr == NULL )
+ return;
+
+ int seq = pStudioModel->GetSequence();
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( seq );
+
+ for ( int i = 0; i < (int)seqdesc.numevents; ++i )
+ {
+ mstudioevent_t *pEvent = seqdesc.pEvent( i );
+#if defined( _DEBUG )
+ const char *pEventName = pEvent->pszEventName();
+ NOTE_UNUSED( pEventName );
+#endif
+ if ( pEvent->cycle <= prevcycle || pEvent->cycle > currcycle )
+ continue;
+
+ // largely copied from BuildAnimationEventSoundList in baseentity.cpp
+ switch ( pEvent->event )
+ {
+ case 0:
+ if ( Q_strcmp( pEvent->pszEventName(), "AE_CL_PLAYSOUND" ) == 0 )
+ {
+ PlaySound( pEvent->pszOptions(), pStudioModel );
+ continue;
+ }
+ break;
+
+ case CL_EVENT_SOUND: // Old-style client .dll animation event
+ // fall-through intentional
+ case SCRIPT_EVENT_SOUND:
+ // fall-through intentional
+ case SCRIPT_EVENT_SOUND_VOICE:
+ PlaySound( pEvent->pszOptions(), pStudioModel );
+ break;
+
+ case CL_EVENT_FOOTSTEP_LEFT:
+ case CL_EVENT_FOOTSTEP_RIGHT:
+ {
+ char soundname[256];
+ char const *options = pEvent->pszOptions();
+ if ( !options || !options[0] )
+ {
+ options = "NPC_CombineS";
+ }
+
+ Q_snprintf( soundname, 256, "%s.RunFootstepLeft", options );
+ PlaySound( soundname, pStudioModel );
+ Q_snprintf( soundname, 256, "%s.RunFootstepRight", options );
+ PlaySound( soundname, pStudioModel );
+ Q_snprintf( soundname, 256, "%s.FootstepLeft", options );
+ PlaySound( soundname, pStudioModel );
+ Q_snprintf( soundname, 256, "%s.FootstepRight", options );
+ PlaySound( soundname, pStudioModel );
+ }
+ break;
+/*
+ case AE_CL_PLAYSOUND:
+ if ( !( pEvent->type & AE_TYPE_CLIENT ) )
+ break;
+
+ if ( pEvent->options[0] )
+ {
+ PlaySound( pEvent->options, pStudioModel );
+ }
+ else
+ {
+ Warning( "-- Error --: empty soundname, .qc error on AE_CL_PLAYSOUND in model %s, sequence %s, animevent # %i\n",
+ pStudioHdr->name(), seqdesc.pszLabel(), i + 1 );
+ }
+ break;
+*/
+ default:
+ break;
+ }
+ }
+}
+
+
+void
+MatSysWindow::draw ()
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( g_bInError || !g_pStudioModel->GetStudioRender() )
+ return;
+
+ static bool bInDraw = false;
+ if (bInDraw)
+ return;
+
+ bInDraw = true;
+
+ UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ g_pMaterialSystem->BeginFrame( 0 );
+ g_pStudioModel->GetStudioRender()->BeginFrame();
+
+ pRenderContext->ClearColor3ub(g_viewerSettings.bgColor[0] * 255, g_viewerSettings.bgColor[1] * 255, g_viewerSettings.bgColor[2] * 255);
+ // pRenderContext->ClearColor3ub(0, 0, 0 );
+ pRenderContext->ClearBuffers(true, true);
+
+ pRenderContext->Viewport( 0, 0, w(), h() );
+
+ pRenderContext->MatrixMode( MATERIAL_PROJECTION );
+ pRenderContext->LoadIdentity( );
+ pRenderContext->PerspectiveX(g_viewerSettings.fov, (float)w() / (float)h(), 1.0f, 20000.0f);
+
+ DrawBackground();
+ DrawGroundPlane();
+ DrawMovementBoxes();
+ DrawHelpers();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+ // FIXME: why is this needed? Doesn't SetView() override this?
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ g_pStudioModel->ClearLookTargets();
+ g_pStudioModel->AddLookTarget( Vector( 0, 0, 0 ), g_pStudioModel->GetSolveHeadTurn() ? 1.0f : 0.0f );
+ int polycount = g_pStudioModel->DrawModel ();
+
+ g_pStudioModel->GetStudioRender()->EndFrame();
+
+ UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
+
+ g_ControlPanel->setModelInfo();
+
+ int lod;
+ float metric;
+ metric = g_pStudioModel->GetLodMetric();
+ lod = g_pStudioModel->GetLodUsed();
+ g_ControlPanel->setLOD( lod, true, false );
+ g_ControlPanel->setLODMetric( metric );
+
+ g_ControlPanel->setPolycount( polycount );
+
+ int nVertCount = 0;
+ int nIndexCount = 0;
+ int nTriCount = 0;
+
+ CStudioHdr *pStudioHdr = g_pStudioModel->GetStudioHdr();
+ if ( pStudioHdr != NULL )
+ {
+ studiohwdata_t *pHardwareData = g_pStudioModel->GetHardwareData();
+ if ( pHardwareData != NULL )
+ {
+ studioloddata_t *pLODData = &pHardwareData->m_pLODs[ pHardwareData->m_RootLOD ];
+ for ( int meshID = 0; meshID < pHardwareData->m_NumStudioMeshes; meshID++ )
+ {
+ studiomeshdata_t *pMesh = &pLODData->m_pMeshData[meshID];
+ for ( int groupID = 0; groupID < pMesh->m_NumGroup; groupID++ )
+ {
+ studiomeshgroup_t *pMeshGroup = &pMesh->m_pMeshGroup[ groupID ];
+
+ for( int j = 0; j < pMeshGroup->m_NumStrips; j++ )
+ {
+ nIndexCount += pMeshGroup->m_pStripData[ j ].numIndices;
+ nVertCount += pMeshGroup->m_pStripData[ j ].numVerts;
+ nTriCount += ( pMeshGroup->m_pStripData[ j ].numIndices / 3 );
+ }
+ }
+ }
+ }
+ }
+
+ g_ControlPanel->setModelInfo( nVertCount, nIndexCount, nTriCount );
+
+ g_ControlPanel->setTransparent( g_pStudioModel->m_bIsTransparent );
+
+ g_ControlPanel->updatePoseParameters( );
+
+ // draw what ever else is loaded
+ int i;
+ for (i = 0; i < HLMV_MAX_MERGED_MODELS; i++)
+ {
+ if (g_pStudioExtraModel[i] != NULL)
+ {
+ g_pStudioModel->GetStudioRender()->BeginFrame();
+ g_pStudioExtraModel[i]->DrawModel( true );
+ g_pStudioModel->GetStudioRender()->EndFrame();
+ }
+ }
+
+ g_pStudioModel->IncrementFramecounter();
+
+ PlaySounds( g_pStudioModel );
+ UpdateSounds(); // need to call this multiple times per frame to avoid audio stuttering
+
+ g_pMaterialSystem->SwapBuffers();
+
+ g_pMaterialSystem->EndFrame();
+
+ bInDraw = false;
+}
+
+
+
+/*
+int
+MatSysWindow::loadTexture (const char *filename, int name)
+{
+ if (!filename || !strlen (filename))
+ {
+ if (d_textureNames[name])
+ {
+ glDeleteTextures (1, (const GLuint *) &d_textureNames[name]);
+ d_textureNames[name] = 0;
+
+ if (name == 0)
+ strcpy (g_viewerSettings.backgroundTexFile, "");
+ else
+ strcpy (g_viewerSettings.groundTexFile, "");
+ }
+
+ return 0;
+ }
+
+ mxImage *image = 0;
+
+ char ext[16];
+ strcpy (ext, mx_getextension (filename));
+
+ if (!mx_strcasecmp (ext, ".tga"))
+ image = mxTgaRead (filename);
+ else if (!mx_strcasecmp (ext, ".pcx"))
+ image = mxPcxRead (filename);
+ else if (!mx_strcasecmp (ext, ".bmp"))
+ image = mxBmpRead (filename);
+
+ if (image)
+ {
+ if (name == 0)
+ strcpy (g_viewerSettings.backgroundTexFile, filename);
+ else
+ strcpy (g_viewerSettings.groundTexFile, filename);
+
+ d_textureNames[name] = name + 1;
+
+ if (image->bpp == 8)
+ {
+ mstudiotexture_t texture;
+ texture.width = image->width;
+ texture.height = image->height;
+
+ g_pStudioModel->UploadTexture (&texture, (byte *) image->data, (byte *) image->palette, name + 1);
+ }
+ else
+ {
+ glBindTexture (GL_TEXTURE_2D, d_textureNames[name]);
+ glTexImage2D (GL_TEXTURE_2D, 0, 3, image->width, image->height, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data);
+ glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+
+ delete image;
+
+ return name + 1;
+ }
+
+ return 0;
+}
+*/
+
+
+void
+MatSysWindow::dumpViewport (const char *filename)
+{
+ redraw ();
+ int w = w2 ();
+ int h = h2 ();
+
+ mxImage *image = new mxImage ();
+ if (image->create (w, h, 24))
+ {
+#if 0
+ glReadBuffer (GL_FRONT);
+ glReadPixels (0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, image->data);
+#else
+ HDC hdc = GetDC ((HWND) getHandle ());
+ byte *data = (byte *) image->data;
+ int i = 0;
+ for (int y = 0; y < h; y++)
+ {
+ for (int x = 0; x < w; x++)
+ {
+ COLORREF cref = GetPixel (hdc, x, y);
+ data[i++] = (byte) ((cref >> 0)& 0xff);
+ data[i++] = (byte) ((cref >> 8) & 0xff);
+ data[i++] = (byte) ((cref >> 16) & 0xff);
+ }
+ }
+ ReleaseDC ((HWND) getHandle (), hdc);
+#endif
+ if (!mxTgaWrite (filename, image))
+ mxMessageBox (this, "Error writing screenshot.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+
+ delete image;
+ }
+}
+
diff --git a/utils/hlmv/matsyswin.h b/utils/hlmv/matsyswin.h
new file mode 100644
index 0000000..ed7c60f
--- /dev/null
+++ b/utils/hlmv/matsyswin.h
@@ -0,0 +1,179 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef MATSYSWIN_H
+#define MATSYSWIN_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include <mxtk/mxMatSysWindow.h>
+#include "materialsystem/imaterialsystem.h"
+#include "interface.h"
+
+class ITexture;
+class MatSysWindow : public mxMatSysWindow
+{
+public:
+
+ // CREATORS
+ MatSysWindow( mxWindow *parent, int x, int y, int w, int h, const char *label, int style );
+ ~MatSysWindow( );
+
+ // MANIPULATORS
+ void dumpViewport (const char *filename);
+ virtual int handleEvent( mxEvent *event );
+ virtual void draw( );
+
+ void *m_hWnd;
+ // void *m_hDC;
+
+ CSysModule *m_hMaterialSystemInst;
+ ITexture *m_pCubemapTexture;
+
+};
+
+
+extern MatSysWindow *g_MatSysWindow;
+extern IMaterial *g_materialBackground;
+extern IMaterial *g_materialWireframe;
+extern IMaterial *g_materialWireframeVertexColor;
+extern IMaterial *g_materialWireframeVertexColorNoCull;
+extern IMaterial *g_materialDebugCopyBaseTexture;
+extern IMaterial *g_materialFlatshaded;
+extern IMaterial *g_materialSmoothshaded;
+extern IMaterial *g_materialBones;
+extern IMaterial *g_materialLines;
+extern IMaterial *g_materialFloor;
+extern IMaterial *g_materialVertexColor;
+extern IMaterial *g_materialShadow;
+
+#if 0
+
+typedef struct
+{
+ int width;
+ int height;
+ int bpp;
+ int flags;
+ int frequency;
+} screen_res_t;
+
+
+
+typedef struct
+{
+ int width;
+ int height;
+ int bpp;
+} devinfo_t;
+
+
+
+class MaterialSystemApp
+{
+public:
+
+ MaterialSystemApp();
+ ~MaterialSystemApp();
+
+ void Term();
+
+ // Post a message to shutdown the app.
+ void AppShutdown();
+
+ int WinMain(void *hInstance, void *hPrevInstance, char *szCmdLine, int iCmdShow);
+ long WndProc(void *hwnd, long iMsg, long wParam, long lParam);
+
+ int FindNumParameter(const char *s, int defaultVal=-1);
+ bool FindParameter(const char *s);
+ const char* FindParameterArg(const char *s);
+
+ void SetTitleText(PRINTF_FORMAT_STRING const char *fmt, ...);
+
+
+private:
+
+ bool InitMaterialSystem();
+ void Clear();
+
+ bool CreateMainWindow(int width, int height, int bpp, bool fullscreen);
+
+ void RenderScene();
+
+ void MouseCapture();
+ void MouseRelease();
+
+ void GetParameters();
+
+
+public:
+ IMaterialSystem *m_pMaterialSystem;
+ void *m_hMaterialSystemInst;
+
+ devinfo_t m_DevInfo;
+
+ void *m_hInstance;
+ int m_iCmdShow;
+ void *m_hWnd;
+ void *m_hDC;
+ bool m_bActive;
+ bool m_bFullScreen;
+ int m_width;
+ int m_height;
+ int m_centerx; // for mouse offset calculations
+ int m_centery;
+ int m_bpp;
+ BOOL m_bChangeBPP;
+ BOOL m_bAllowSoft;
+ BOOL m_bPaused;
+ int m_glnWidth;
+ int m_glnHeight;
+ float m_gldAspect;
+ float m_NearClip;
+ float m_FarClip;
+ float m_fov;
+
+ screen_res_t *m_pResolutions;
+ int m_iResCount;
+
+ int m_iVidMode;
+};
+
+
+// ---------------------------------------------------------------------------------------- //
+// Global functions
+// ---------------------------------------------------------------------------------------- //
+
+// Show an error dialog and quit.
+bool Sys_Error(PRINTF_FORMAT_STRING const char *pMsg, ...);
+
+// Print to the trace window.
+void con_Printf(PRINTF_FORMAT_STRING const char *pMsg, ...);
+
+// Returns true if the key is down.
+bool IsKeyDown(char key);
+
+
+
+extern MaterialSystemApp g_MaterialSystemApp;
+
+
+extern unsigned int g_Time;
+
+
+#endif
+
+#endif // GLAPP_H
diff --git a/utils/hlmv/mdlviewer.cpp b/utils/hlmv/mdlviewer.cpp
new file mode 100644
index 0000000..aae97d6
--- /dev/null
+++ b/utils/hlmv/mdlviewer.cpp
@@ -0,0 +1,1631 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//===========================================================================//
+//
+// 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include <mxtk/mxTga.h>
+#include <mxtk/mxEvent.h>
+#include "mdlviewer.h"
+#include "ViewerSettings.h"
+#include "MatSysWin.h"
+#include "ControlPanel.h"
+#include "StudioModel.h"
+#include "FileAssociation.h"
+#include "tier1/strtools.h"
+#include "tier0/icommandline.h"
+#include "filesystem.h"
+#include "ifilesystemopendialog.h"
+#include "appframework/appframework.h"
+#include "istudiorender.h"
+#include "materialsystem/imaterialsystem.h"
+#include "vphysics_interface.h"
+#include "Datacache/imdlcache.h"
+#include "datacache/idatacache.h"
+#include "filesystem_init.h"
+#include "materialsystem/imaterialsystemhardwareconfig.h"
+#include "SoundEmitterSystem/isoundemittersystembase.h"
+#include "soundsystem/isoundsystem.h"
+#include "tier1/tier1.h"
+#include "valve_ipc_win32.h"
+#include "threadtools.h"
+#include "vstdlib/IKeyValuesSystem.h"
+
+bool g_bOldFileDialogs = false;
+
+MDLViewer *g_MDLViewer = 0;
+char g_appTitle[] = "Half-Life Model Viewer v1.22";
+static char recentFiles[8][256] = { "", "", "", "", "", "", "", "" };
+extern int g_dxlevel;
+bool g_bInError = false;
+
+
+//-----------------------------------------------------------------------------
+// Singleton interfaces
+//-----------------------------------------------------------------------------
+IStudioRender *g_pStudioRender;
+IMDLCache *g_pMDLCache;
+IPhysicsSurfaceProps *physprop;
+IPhysicsCollision *physcollision;
+IFileSystem *g_pFileSystem;
+IMaterialSystem *g_pMaterialSystem;
+IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig;
+IStudioDataCache *g_pStudioDataCache;
+IDataCache *g_pDataCache;
+ISoundEmitterSystemBase *g_pSoundEmitterBase;
+ISoundSystem *g_pSoundSystem;
+CreateInterfaceFn g_Factory;
+
+// Filesystem dialog module wrappers.
+CSysModule *g_pFSDialogModule = 0;
+CreateInterfaceFn g_FSDialogFactory = 0;
+
+
+class CHlmvIpcServer : public CValveIpcServerUtl
+{
+public:
+ CHlmvIpcServer() : CValveIpcServerUtl( "HLMV_IPC_SERVER" ) {}
+ ~CHlmvIpcServer();
+
+public:
+ bool HasCommands();
+ void AppendCommand( char *pszCommand );
+ char *GetCommand();
+ void PopCommand();
+
+protected:
+ virtual BOOL ExecuteCommand( CUtlBuffer &cmd, CUtlBuffer &res );
+
+protected:
+ CThreadFastMutex m_mtx;
+ CUtlVector< char * > m_lstCommands;
+}
+g_HlmvIpcServer;
+
+CValveIpcClientUtl g_HlmvIpcClient( "HLMV_IPC_SERVER" );
+bool g_bHlmvMaster = false; // This hlmv is controlling a controlled hlmv instance
+bool g_bHlmvControlled = false; // This hlmv is being controlled by a master hlmv instance
+
+void LoadFileSystemDialogModule()
+{
+ Assert( !g_pFSDialogModule );
+
+ // Load the module with the file system open dialog.
+ const char *pDLLName = "FileSystemOpenDialog.dll";
+ g_pFSDialogModule = Sys_LoadModule( pDLLName );
+ if ( g_pFSDialogModule )
+ {
+ g_FSDialogFactory = Sys_GetFactory( g_pFSDialogModule );
+ }
+
+ if ( !g_pFSDialogModule || !g_FSDialogFactory )
+ {
+ if ( g_pFSDialogModule )
+ {
+ Sys_UnloadModule( g_pFSDialogModule );
+ g_pFSDialogModule = NULL;
+ }
+ }
+}
+
+void UnloadFileSystemDialogModule()
+{
+ if ( g_pFSDialogModule )
+ {
+ Sys_UnloadModule( g_pFSDialogModule );
+ g_pFSDialogModule = 0;
+ }
+}
+
+
+
+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);
+ }
+}
+
+struct AccelTableEntry_t
+{
+ unsigned short key;
+ unsigned short command;
+ unsigned char flags;
+};
+
+AccelTableEntry_t accelTable[] = {{VK_F1, IDC_FLUSH_SHADERS, mx::ACCEL_VIRTKEY},
+ {VK_F5, IDC_FILE_REFRESH, mx::ACCEL_VIRTKEY},
+ {'u', IDC_FILE_UNLOADALLMERGEDMODELS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'U', IDC_FILE_UNLOADALLMERGEDMODELS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'w', IDC_ACCEL_WIREFRAME, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'W', IDC_ACCEL_WIREFRAME, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'a', IDC_ACCEL_ATTACHMENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'A', IDC_ACCEL_ATTACHMENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'g', IDC_ACCEL_GROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'G', IDC_ACCEL_GROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'h', IDC_ACCEL_HITBOXES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'H', IDC_ACCEL_HITBOXES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'o', IDC_ACCEL_BONES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'O', IDC_ACCEL_BONES, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'b', IDC_ACCEL_BACKGROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'B', IDC_ACCEL_BACKGROUND, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'m', IDC_ACCEL_MOVEMENT, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'M', IDC_ACCEL_MOVEMENT, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'n', IDC_ACCEL_NORMALS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'N', IDC_ACCEL_NORMALS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'t', IDC_ACCEL_TANGENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'T', IDC_ACCEL_TANGENTS, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'s', IDC_ACCEL_SHADOW, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY},
+ {'S', IDC_ACCEL_SHADOW, mx::ACCEL_CONTROL | mx::ACCEL_VIRTKEY}};
+#define NUM_ACCELERATORS ARRAYSIZE( accelTable )
+
+
+MDLViewer::MDLViewer ()
+: mxWindow (0, 0, 0, 0, 0, g_appTitle, mxWindow::Normal)
+{
+ d_MatSysWindow = 0;
+ d_cpl = 0;
+
+ // create menu stuff
+ mb = new mxMenuBar (this);
+ mxMenu *menuFile = new mxMenu ();
+ menuOptions = new mxMenu ();
+ menuView = new mxMenu ();
+ mxMenu *menuHelp = new mxMenu ();
+
+ mb->addMenu ("File", menuFile);
+ mb->addMenu ("Options", menuOptions);
+ mb->addMenu ("View", menuView);
+ mb->addMenu ("Help", menuHelp);
+
+ mxMenu *menuRecentModels = new mxMenu ();
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS1);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS2);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS3);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS4);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS5);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS6);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS7);
+ menuRecentModels->add ("(empty)", IDC_FILE_RECENTMODELS8);
+
+ if ( g_bOldFileDialogs )
+ {
+ menuFile->add ("Load Model...", IDC_FILE_LOADMODEL);
+ menuFile->add ("(Steam) Load Model...", IDC_FILE_LOADMODEL_STEAM);
+ }
+ else
+ {
+ menuFile->add ("Load Model...", IDC_FILE_LOADMODEL_STEAM);
+ }
+
+ menuFile->add( "Refresh (F5)", IDC_FILE_REFRESH );
+ menuFile->addSeparator ();
+
+ if ( g_bOldFileDialogs )
+ {
+ menuFile->add ("Load Weapon...", IDC_FILE_LOADMERGEDMODEL);
+ menuFile->add ("(Steam) Load Weapon...", IDC_FILE_LOADMERGEDMODEL_STEAM);
+ }
+ else
+ {
+ menuFile->add ("Load Weapon...", IDC_FILE_LOADMERGEDMODEL_STEAM);
+ }
+
+ mxMenu *menuUnloadWeapon = new mxMenu ();
+ menuUnloadWeapon->add ("Unload All Merged Models (Ctrl-U)", IDC_FILE_UNLOADALLMERGEDMODELS);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL1);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL2);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL3);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL4);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL5);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL6);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL7);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL8);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL9);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL10);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL11);
+ menuUnloadWeapon->add ("(empty)", IDC_FILE_UNLOADMERGEDMODEL12);
+ for ( int i = IDC_FILE_UNLOADMERGEDMODEL1; i <= IDC_FILE_UNLOADMERGEDMODEL12; i++ )
+ {
+ menuUnloadWeapon->setEnabled( i, false );
+ }
+ menuFile->addMenu ("Unload Weapon", menuUnloadWeapon);
+
+ menuFile->addSeparator ();
+ menuFile->add ("Load Background Texture...", IDC_FILE_LOADBACKGROUNDTEX);
+ menuFile->add ("Load Ground Texture...", IDC_FILE_LOADGROUNDTEX);
+ menuFile->addSeparator ();
+ menuFile->add ("Unload Ground Texture", IDC_FILE_UNLOADGROUNDTEX);
+ menuFile->addSeparator ();
+ menuFile->addMenu ("Recent Models", menuRecentModels);
+ menuFile->addSeparator ();
+ menuFile->add ("Exit", IDC_FILE_EXIT);
+
+ menuFile->setEnabled(IDC_FILE_LOADBACKGROUNDTEX, false);
+ menuFile->setEnabled(IDC_FILE_LOADGROUNDTEX, false);
+ menuFile->setEnabled(IDC_FILE_UNLOADGROUNDTEX, false);
+
+ menuOptions->add ("Background Color...", IDC_OPTIONS_COLORBACKGROUND);
+ menuOptions->add ("Ground Color...", IDC_OPTIONS_COLORGROUND);
+ menuOptions->add ("Light Color...", IDC_OPTIONS_COLORLIGHT);
+ menuOptions->add ("Ambient Color...", IDC_OPTIONS_COLORAMBIENT);
+ menuOptions->addSeparator ();
+ menuOptions->add ("Center View", IDC_OPTIONS_CENTERVIEW);
+ menuOptions->add ("Viewmodel Mode", IDC_OPTIONS_VIEWMODEL);
+#ifdef WIN32
+ menuOptions->addSeparator ();
+ menuOptions->add ("Make Screenshot...", IDC_OPTIONS_MAKESCREENSHOT);
+ //menuOptions->add ("Dump Model Info", IDC_OPTIONS_DUMP);
+#endif
+
+ menuView->add ("File Associations...", IDC_VIEW_FILEASSOCIATIONS);
+ menuView->setEnabled( IDC_VIEW_FILEASSOCIATIONS, false );
+
+ menuView->addSeparator ();
+ menuView->add ("Show Activities", IDC_VIEW_ACTIVITIES);
+ menuView->add ("Show hidden", IDC_VIEW_HIDDEN );
+
+#ifdef WIN32
+ menuHelp->add ("Goto Homepage...", IDC_HELP_GOTOHOMEPAGE);
+ menuHelp->addSeparator ();
+#endif
+ menuHelp->add ("About...", IDC_HELP_ABOUT);
+
+
+ d_MatSysWindow = new MatSysWindow (this, 0, 0, 100, 100, "", mxWindow::Normal);
+#ifdef WIN32
+ // SetWindowLong ((HWND) d_MatSysWindow->getHandle (), GWL_EXSTYLE, WS_EX_CLIENTEDGE);
+#endif
+
+ d_cpl = new ControlPanel (this);
+ d_cpl->setMatSysWindow (d_MatSysWindow);
+ g_MatSysWindow = d_MatSysWindow;
+
+ g_FileAssociation = new FileAssociation ();
+
+ loadRecentFiles ();
+ initRecentFiles ();
+
+ LoadViewerRootSettings( );
+
+ // FIXME: where do I actually find the domain size of the viewport, especially for multi-monitor
+ // try to catch weird initialization error
+ if (g_viewerSettings.xpos < -16384)
+ g_viewerSettings.xpos = 20;
+ if (g_viewerSettings.ypos < -16384)
+ g_viewerSettings.ypos = 20;
+ g_viewerSettings.ypos = max( 0, g_viewerSettings.ypos );
+ g_viewerSettings.width = max( 640, g_viewerSettings.width );
+ g_viewerSettings.height = max( 700, g_viewerSettings.height );
+
+ setBounds( g_viewerSettings.xpos, g_viewerSettings.ypos, g_viewerSettings.width, g_viewerSettings.height );
+ setVisible (true);
+ setTimer( 200 );
+
+ CUtlVector< mx::Accel_t > accelerators;
+ mx::Accel_t accel;
+
+ for (int i=0; i < NUM_ACCELERATORS; i++)
+ {
+ accel.flags = accelTable[i].flags ;
+ accel.key = accelTable[i].key;
+ accel.command = accelTable[i].command;
+ accelerators.AddToTail( accel );
+ }
+
+ mx::createAccleratorTable( accelerators.Count(), accelerators.Base() );
+
+ g_HlmvIpcServer.EnsureRegisteredAndRunning();
+
+ if ( !g_HlmvIpcServer.IsRunning() )
+ {
+ menuOptions->addSeparator();
+ menuOptions->add ( "Link HLMV", IDC_OPTIONS_LINKHLMV );
+ menuOptions->add ( "Unlink HLMV", IDC_OPTIONS_UNLINKHLMV );
+ menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, true );
+ menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
+ }
+}
+
+
+
+MDLViewer::~MDLViewer ()
+{
+ g_HlmvIpcServer.EnsureStoppedAndUnregistered();
+
+ saveRecentFiles ();
+ SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel );
+ SaveViewerRootSettings( );
+
+#ifdef WIN32
+ DeleteFile ("hlmv.cfg");
+ DeleteFile ("midump.txt");
+#endif
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Reloads the currently loaded model file.
+//-----------------------------------------------------------------------------
+void MDLViewer::Refresh( void )
+{
+ KeyValuesSystem()->InvalidateCache();
+
+ g_pStudioModel->ReleaseStudioModel();
+ g_pMDLCache->Flush();
+ if ( recentFiles[0][0] != '\0' )
+ {
+ char szFile[MAX_PATH];
+ strcpy( szFile, recentFiles[0] );
+ g_pMaterialSystem->ReloadMaterials();
+ d_cpl->loadModel( szFile );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Loads the file and updates the MRU list.
+// Input : pszFile - File to load.
+//-----------------------------------------------------------------------------
+void MDLViewer::LoadModelFile( const char *pszFile, int slot )
+{
+ // copy off name, pszFile may be point into recentFiles array
+ char filename[1024];
+ strcpy( filename, pszFile );
+
+ LoadModelResult_t eLoaded = d_cpl->loadModel( filename, slot );
+
+ if ( eLoaded != LoadModel_Success )
+ {
+ switch (eLoaded)
+ {
+ case LoadModel_LoadFail:
+ {
+ mxMessageBox (this, "Error loading model.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
+ break;
+ }
+
+ case LoadModel_PostLoadFail:
+ {
+ mxMessageBox (this, "Error post-loading model.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
+ break;
+ }
+
+ case LoadModel_NoModel:
+ {
+ mxMessageBox (this, "Error loading model. The model has no vertices.", g_appTitle, MX_MB_ERROR | MX_MB_OK);
+ break;
+ }
+ }
+
+ return;
+ }
+
+ if (slot == -1)
+ {
+ int i;
+ for (i = 0; i < 8; i++)
+ {
+ if (!mx_strcasecmp( recentFiles[i], filename ))
+ break;
+ }
+
+ // shift down existing recent files
+ for (i = ((i > 7) ? 7 : i); i > 0; i--)
+ {
+ strcpy (recentFiles[i], recentFiles[i-1]);
+ }
+
+ strcpy( recentFiles[0], filename );
+
+ initRecentFiles ();
+
+ setLabel( "%s", filename );
+ }
+ else
+ {
+ mb->modify (IDC_FILE_UNLOADMERGEDMODEL1 + slot, IDC_FILE_UNLOADMERGEDMODEL1 + slot, pszFile);
+ mb->setEnabled (IDC_FILE_UNLOADMERGEDMODEL1 + slot, true);
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Takes a TGA screenshot of the given filename and exits.
+// Input : pszFile - File to load.
+//-----------------------------------------------------------------------------
+void MDLViewer::SaveScreenShot( const char *pszFile )
+{
+ char filename[1024];
+ strcpy( filename, pszFile );
+ LoadModelResult_t eLoaded = d_cpl->loadModel( filename );
+
+ //
+ // Screenshot mode. Write a screenshot file and exit.
+ //
+ if ( eLoaded == LoadModel_Success )
+ {
+ g_viewerSettings.bgColor[0] = 117.0f / 255.0f;
+ g_viewerSettings.bgColor[1] = 196.0f / 255.0f;
+ g_viewerSettings.bgColor[2] = 219.0f / 255.0f;
+
+ // Build the name of the TGA to write.
+ char szScreenShot[256];
+ strcpy(szScreenShot, filename);
+ char *pchDot = strrchr(szScreenShot, '.');
+ if (pchDot)
+ {
+ strcpy(pchDot, ".tga");
+ }
+ else
+ {
+ strcat(szScreenShot, ".tga");
+ }
+
+ // Center the view and write the TGA.
+ d_cpl->centerView();
+ d_MatSysWindow->dumpViewport(szScreenShot);
+ }
+
+ // Shut down.
+ mx::quit();
+ return;
+}
+
+
+void MDLViewer::DumpText( const char *pszFile )
+{
+ char filename[1024];
+ strcpy( filename, pszFile );
+ LoadModelResult_t eLoaded = d_cpl->loadModel( filename );
+
+ //
+ // Screenshot mode. Write a screenshot file and exit.
+ //
+ if ( eLoaded == LoadModel_Success )
+ {
+ if ( g_pStudioModel->m_bIsTransparent )
+ {
+ Msg("%s is transparent\n", filename );
+ }
+ if ( g_pStudioModel->m_bHasProxy )
+ {
+ Msg("%s has material proxies\n", filename );
+ }
+ }
+
+ // Shut down.
+ mx::quit();
+}
+
+
+const char* MDLViewer::SteamGetOpenFilename()
+{
+ if ( !g_FSDialogFactory )
+ return NULL;
+
+ static char filename[MAX_PATH];
+
+ IFileSystemOpenDialog *pDlg;
+ pDlg = (IFileSystemOpenDialog*)g_FSDialogFactory( FILESYSTEMOPENDIALOG_VERSION, NULL );
+ if ( !pDlg )
+ {
+ char str[512];
+ Q_snprintf( str, sizeof( str ), "Can't create %s interface.", FILESYSTEMOPENDIALOG_VERSION );
+ MessageBox( NULL, str, "Error", MB_OK );
+ return NULL;
+ }
+ pDlg->Init( g_Factory, NULL );
+ pDlg->AddFileMask( "*.jpg" );
+ pDlg->AddFileMask( "*.mdl" );
+ pDlg->SetInitialDir( "models", "game" );
+ pDlg->SetFilterMdlAndJpgFiles( true );
+
+ if (pDlg->DoModal() == IDOK)
+ {
+ pDlg->GetFilename( filename, sizeof( filename ) );
+ pDlg->Release();
+ return filename;
+ }
+ else
+ {
+ pDlg->Release();
+ return NULL;
+ }
+}
+
+
+int
+MDLViewer::handleEvent (mxEvent *event)
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ switch (event->event)
+ {
+ case mxEvent::Action:
+ {
+ switch (event->action)
+ {
+ case IDC_FILE_LOADMODEL:
+ {
+ const char *ptr = mxGetOpenFileName (this, 0, "*.mdl");
+ if (ptr)
+ {
+ LoadModelFile( ptr );
+ }
+ }
+ break;
+
+ case IDC_FILE_LOADMODEL_STEAM:
+ {
+ const char *pFilename = SteamGetOpenFilename();
+ if ( pFilename )
+ {
+ LoadModelFile( pFilename );
+ }
+ }
+ break;
+
+ case IDC_FILE_LOADMERGEDMODEL:
+ {
+ const char *ptr = mxGetOpenFileName (this, 0, "*.mdl");
+ if (ptr)
+ {
+ // find the first free slot
+ int iChosenSlot = 0;
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
+ {
+ if ( g_viewerSettings.mergeModelFile[i][0] == 0 )
+ {
+ iChosenSlot = i;
+ break;
+ }
+ }
+ strcpy( g_viewerSettings.mergeModelFile[iChosenSlot], ptr );
+ LoadModelFile( ptr, iChosenSlot );
+ }
+ }
+ break;
+
+ case IDC_FILE_LOADMERGEDMODEL_STEAM:
+ {
+ const char *pFilename = SteamGetOpenFilename();
+ if ( pFilename )
+ {
+ // find the first free slot
+ int iChosenSlot = 0;
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
+ {
+ if ( g_viewerSettings.mergeModelFile[i][0] == 0 )
+ {
+ iChosenSlot = i;
+ break;
+ }
+ }
+ strcpy( g_viewerSettings.mergeModelFile[iChosenSlot], pFilename );
+ LoadModelFile( pFilename, iChosenSlot );
+ }
+ }
+ break;
+
+
+ case IDC_FILE_UNLOADMERGEDMODEL1:
+ case IDC_FILE_UNLOADMERGEDMODEL2:
+ case IDC_FILE_UNLOADMERGEDMODEL3:
+ case IDC_FILE_UNLOADMERGEDMODEL4:
+ case IDC_FILE_UNLOADMERGEDMODEL5:
+ case IDC_FILE_UNLOADMERGEDMODEL6:
+ case IDC_FILE_UNLOADMERGEDMODEL7:
+ case IDC_FILE_UNLOADMERGEDMODEL8:
+ case IDC_FILE_UNLOADMERGEDMODEL9:
+ case IDC_FILE_UNLOADMERGEDMODEL10:
+ case IDC_FILE_UNLOADMERGEDMODEL11:
+ case IDC_FILE_UNLOADMERGEDMODEL12:
+ {
+ int i = event->action - IDC_FILE_UNLOADMERGEDMODEL1;
+ // FIXME: move to d_cpl
+ if (g_pStudioExtraModel[i])
+ {
+ V_strcpy_safe( g_viewerSettings.mergeModelFile[i], "" );
+ g_pStudioExtraModel[i]->FreeModel( false );
+ delete g_pStudioExtraModel[i];
+ g_pStudioExtraModel[i] = NULL;
+
+ mb->modify (IDC_FILE_UNLOADMERGEDMODEL1 + i, IDC_FILE_UNLOADMERGEDMODEL1 + i, "(empty)");
+ mb->setEnabled (IDC_FILE_UNLOADMERGEDMODEL1 + i, false);
+ }
+ }
+ break;
+
+ case IDC_FILE_UNLOADALLMERGEDMODELS:
+ d_cpl->UnloadAllMergedModels();
+ break;
+
+ case IDC_FILE_REFRESH:
+ {
+ Refresh();
+ break;
+ }
+
+ case IDC_FLUSH_SHADERS:
+ {
+ CCommand args;
+ args.Tokenize( "mat_flushshaders" );
+
+ ConCommandBase *pCommandBase = g_pCVar->FindCommandBase( args[0] );
+ if ( !pCommandBase )
+ {
+ ConWarning( "Unknown command or convar '%s'!\n", args[0] );
+ break;
+ }
+
+ if ( pCommandBase->IsCommand() )
+ {
+ ConCommand *pCommand = static_cast<ConCommand*>( pCommandBase );
+ pCommand->Dispatch( args );
+ }
+ }
+ break;
+
+ case IDC_FILE_LOADBACKGROUNDTEX:
+ case IDC_FILE_LOADGROUNDTEX:
+ {
+ const char *ptr = mxGetOpenFileName (this, 0, "*.*");
+ if (ptr)
+ {
+ if (0 /* d_MatSysWindow->loadTexture (ptr, event->action - IDC_FILE_LOADBACKGROUNDTEX) */)
+ {
+ if (event->action == IDC_FILE_LOADBACKGROUNDTEX)
+ d_cpl->setShowBackground (true);
+ else
+ d_cpl->setShowGround (true);
+
+ }
+ else
+ mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ }
+ }
+ break;
+
+ case IDC_FILE_UNLOADGROUNDTEX:
+ {
+ // d_MatSysWindow->loadTexture (0, 1);
+ d_cpl->setShowGround (false);
+ }
+ break;
+
+ case IDC_FILE_RECENTMODELS1:
+ case IDC_FILE_RECENTMODELS2:
+ case IDC_FILE_RECENTMODELS3:
+ case IDC_FILE_RECENTMODELS4:
+ case IDC_FILE_RECENTMODELS5:
+ case IDC_FILE_RECENTMODELS6:
+ case IDC_FILE_RECENTMODELS7:
+ case IDC_FILE_RECENTMODELS8:
+ {
+ int i = event->action - IDC_FILE_RECENTMODELS1;
+ LoadModelFile( recentFiles[i] );
+ }
+ break;
+
+ case IDC_FILE_EXIT:
+ {
+ redraw ();
+ mx::quit ();
+ }
+ break;
+
+ case IDC_OPTIONS_COLORBACKGROUND:
+ case IDC_OPTIONS_COLORGROUND:
+ case IDC_OPTIONS_COLORLIGHT:
+ case IDC_OPTIONS_COLORAMBIENT:
+ {
+ float *cols[4] = { g_viewerSettings.bgColor, g_viewerSettings.gColor, g_viewerSettings.lColor, g_viewerSettings.aColor };
+ float *col = cols[event->action - IDC_OPTIONS_COLORBACKGROUND];
+ int r = (int) (col[0] * 255.0f);
+ int g = (int) (col[1] * 255.0f);
+ int b = (int) (col[2] * 255.0f);
+ if (mxChooseColor (this, &r, &g, &b))
+ {
+ col[0] = (float) r / 255.0f;
+ col[1] = (float) g / 255.0f;
+ col[2] = (float) b / 255.0f;
+ }
+ }
+ break;
+
+ case IDC_OPTIONS_CENTERVIEW:
+ d_cpl->centerView ();
+ if ( g_bHlmvMaster )
+ {
+ SendModelTransformToLinkedHlmv();
+ }
+ break;
+ case IDC_OPTIONS_CENTERVERTS:
+ //d_cpl->centerVerts( );
+ if ( g_bHlmvMaster )
+ {
+ SendModelTransformToLinkedHlmv();
+ }
+ break;
+ case IDC_OPTIONS_VIEWMODEL:
+ {
+ d_cpl->viewmodelView();
+ if ( g_bHlmvMaster )
+ {
+ SendModelTransformToLinkedHlmv();
+ }
+ }
+ break;
+
+ case IDC_OPTIONS_MAKESCREENSHOT:
+ {
+ char *ptr = (char *) mxGetSaveFileName (this, "", "*.tga");
+ if (ptr)
+ {
+ if (!strstr (ptr, ".tga"))
+ strcat (ptr, ".tga");
+ d_MatSysWindow->dumpViewport (ptr);
+ }
+ }
+ break;
+
+ case IDC_OPTIONS_DUMP:
+ d_cpl->dumpModelInfo ();
+ break;
+
+ case IDC_OPTIONS_SYNCHLMVCAMERA:
+ SendModelTransformToLinkedHlmv();
+
+ break;
+
+ case IDC_OPTIONS_LINKHLMV:
+ if ( !g_bHlmvMaster && !g_HlmvIpcServer.IsRunning() && g_HlmvIpcClient.Connect() )
+ {
+ CUtlBuffer cmd;
+ CUtlBuffer res;
+
+ // Make connection to other hlmv
+ cmd.PutString( "hlmvLink" );
+ cmd.PutChar( '\0' );
+
+ if ( g_HlmvIpcClient.ExecuteCommand( cmd, res ) )
+ {
+ g_bHlmvMaster = true;
+ }
+
+ g_HlmvIpcClient.Disconnect();
+
+ SendModelTransformToLinkedHlmv();
+ SendLightRotToLinkedHlmv();
+
+ menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, true );
+ menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, false );
+
+ menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, false );
+ menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, true );
+ }
+ break;
+
+ case IDC_OPTIONS_UNLINKHLMV:
+ if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
+ {
+ CUtlBuffer cmd;
+ CUtlBuffer res;
+
+ // Break connection to linked hlmv
+
+ cmd.PutString( "hlmvUnlink" );
+ cmd.PutChar( '\0' );
+
+ g_HlmvIpcClient.ExecuteCommand( cmd, res );
+ g_bHlmvMaster = false;
+
+ g_HlmvIpcClient.Disconnect();
+
+ menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
+ menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, true );
+
+ menuOptions->setChecked( IDC_OPTIONS_UNLINKHLMV, true );
+ menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
+ }
+ break;
+
+ case IDC_VIEW_FILEASSOCIATIONS:
+ g_FileAssociation->setAssociation (0);
+ g_FileAssociation->setVisible (true);
+ break;
+
+ case IDC_VIEW_ACTIVITIES:
+ g_viewerSettings.showActivities = !g_viewerSettings.showActivities;
+ menuView->setChecked( event->action, g_viewerSettings.showActivities );
+ d_cpl->initSequenceChoices();
+ d_cpl->resetControlPanel();
+ break;
+
+ case IDC_VIEW_HIDDEN:
+ g_viewerSettings.showHidden = !g_viewerSettings.showHidden;
+ menuView->setChecked( event->action, g_viewerSettings.showHidden );
+ d_cpl->initSequenceChoices();
+ d_cpl->resetControlPanel();
+ break;
+
+#ifdef WIN32
+ case IDC_HELP_GOTOHOMEPAGE:
+ ShellExecute (0, "open", "http://www.swissquake.ch/chumbalum-soft/index.html", 0, 0, SW_SHOW);
+ break;
+#endif
+
+ case IDC_HELP_ABOUT:
+ mxMessageBox (this,
+ "Half-Life Model Viewer v2.0 (c) 2004 Valve Corp.\n"
+ "Portions (c) 1999 by Mete Ciragan\n\n"
+ "Left-drag inside circle to spin.\n"
+ "Left-drag outside circle to rotate.\n"
+ "Right-drag to zoom.\n"
+ "Shift-left-drag to x-y-pan.\n"
+ "Shift-right-drag to z-pan.\n"
+ "Ctrl-left-drag to move light.\n\n"
+ "Build:\t" __DATE__ ".\n"
+ "Email:\[email protected]\n"
+ "Web:\thttp://www.swissquake.ch/chumbalum-soft/", "About Half-Life Model Viewer",
+ MX_MB_OK | MX_MB_INFORMATION);
+ break;
+
+ case IDC_ACCEL_WIREFRAME:
+ d_cpl->setOverlayWireframe( !g_viewerSettings.overlayWireframe );
+ break;
+
+ case IDC_ACCEL_ATTACHMENTS:
+ d_cpl->setShowAttachments( !g_viewerSettings.showAttachments );
+ break;
+
+ case IDC_ACCEL_GROUND:
+ d_cpl->setShowGround( !g_viewerSettings.showGround );
+ break;
+
+ case IDC_ACCEL_HITBOXES:
+ d_cpl->setShowHitBoxes( !g_viewerSettings.showHitBoxes );
+ break;
+
+ case IDC_ACCEL_BONES:
+ d_cpl->setShowBones( !g_viewerSettings.showBones );
+ break;
+
+ case IDC_ACCEL_BACKGROUND:
+ d_cpl->setShowBackground( !g_viewerSettings.showBackground );
+ break;
+
+ case IDC_ACCEL_MOVEMENT:
+ d_cpl->setShowMovement( !g_viewerSettings.showMovement );
+ break;
+
+ case IDC_ACCEL_NORMALS:
+ d_cpl->setShowNormals( !g_viewerSettings.showNormals );
+ break;
+
+ case IDC_ACCEL_TANGENTS:
+ d_cpl->setShowTangentFrame( !g_viewerSettings.showTangentFrame );
+ break;
+
+ case IDC_ACCEL_SHADOW:
+ d_cpl->setShowShadow( !g_viewerSettings.showShadow );
+ break;
+
+ } //switch (event->action)
+
+ } // mxEvent::Action
+ break;
+
+ case mxEvent::Size:
+ {
+ g_viewerSettings.xpos = x();
+ g_viewerSettings.ypos = y();
+ g_viewerSettings.width = w();
+ g_viewerSettings.height = h();
+
+ int w = event->width;
+ int h = event->height;
+ int y = mb->getHeight ();
+#ifdef WIN32
+#define HEIGHT 240
+#else
+#define HEIGHT 140
+ h -= 40;
+#endif
+
+ d_MatSysWindow->setBounds (0, y, w, h - HEIGHT); // !!
+ d_cpl->setBounds (0, y + h - HEIGHT, w, HEIGHT);
+ }
+ break;
+
+
+ case mxEvent::PosChanged:
+ {
+ g_viewerSettings.xpos = x();
+ g_viewerSettings.ypos = y();
+ }
+ break;
+
+ case KeyDown:
+ d_MatSysWindow->handleEvent(event);
+ d_cpl->handleEvent(event);
+ break;
+
+ case mxEvent::Activate:
+ {
+ if (event->action)
+ {
+ mx::setIdleWindow( getMatSysWindow() );
+ }
+ else
+ {
+ mx::setIdleWindow( 0 );
+ }
+ }
+ break;
+
+ case mxEvent::Timer:
+ {
+ if ( g_HlmvIpcServer.HasCommands() )
+ {
+ // Execute next command at next msg pump, ~ 1/60th s (60Hz) if controlled, 0.1s if not
+ if ( g_bHlmvControlled )
+ {
+ // Clear up to 10 pending commands if controlled
+ for ( int nCmdCount = 0; nCmdCount < 10 && g_HlmvIpcServer.HasCommands(); ++nCmdCount )
+ {
+ handleIpcCommand( g_HlmvIpcServer.GetCommand() );
+ g_HlmvIpcServer.PopCommand();
+ }
+
+ setTimer( 17 );
+ }
+ else
+ {
+ handleIpcCommand( g_HlmvIpcServer.GetCommand() );
+ g_HlmvIpcServer.PopCommand();
+
+ setTimer( 100 );
+ }
+ }
+ else if ( !g_HlmvIpcServer.IsRunning() )
+ {
+ // Keep trying to establish our server slot if another instance quits
+ BOOL bIsRunning = g_HlmvIpcServer.IsRunning();
+ g_HlmvIpcServer.EnsureRegisteredAndRunning();
+ if ( !bIsRunning && g_HlmvIpcServer.IsRunning() )
+ {
+ g_bHlmvMaster = false;
+ menuOptions->setEnabled( IDC_OPTIONS_LINKHLMV, false );
+ menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
+ menuOptions->setEnabled( IDC_OPTIONS_UNLINKHLMV, false );
+ menuOptions->setChecked( IDC_OPTIONS_LINKHLMV, false );
+ }
+
+ // Attempt every 1.0 s
+ setTimer( 1000 );
+ }
+ else
+ {
+ // Idling, poll command queue @ ~60Hz if controlled, 0.5s ( 2Hz ) otherwise
+ if ( g_bHlmvControlled )
+ {
+ setTimer( 17 );
+ }
+ else
+ {
+ setTimer( 500 );
+ }
+ }
+ }
+ break;
+ } // event->event
+
+ return 1;
+}
+
+
+
+void TranslateMayaToHLMVCoordinates( const Vector &vMayaPos, const QAngle &vMayaRot, Vector &vHLMVPos, QAngle &vHLMVAngles )
+{
+ vHLMVPos.Init( vMayaPos.z, vMayaPos.x, vMayaPos.y );
+
+ vHLMVAngles[PITCH] = -vMayaRot[0];
+ vHLMVAngles[YAW] = vMayaRot[1] + 180;
+ vHLMVAngles[ROLL] = -vMayaRot[2];
+}
+
+
+void MDLViewer::handleIpcCommand( char *szCommand )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if ( !strcmp( "reload", szCommand ) )
+ {
+ Refresh();
+
+ if ( HWND hWnd = (HWND) getHandle() )
+ {
+ if ( ::IsIconic( hWnd ) )
+ ::ShowWindow( hWnd, SW_RESTORE );
+
+ ::BringWindowToTop( hWnd );
+ ::SetForegroundWindow( hWnd );
+ ::SetFocus( hWnd );
+ }
+ }
+ else if ( V_strncasecmp( "cameraTo", szCommand, 8 ) == 0 )
+ {
+ // Read the camera position and angles from Maya.
+ Vector vMayaPos;
+ QAngle vMayaRot;
+
+ char szFirstPart[32];
+ sscanf( szCommand, "%s %f %f %f %f %f %f",
+ szFirstPart,
+ &vMayaPos.x, &vMayaPos.y, &vMayaPos.z,
+ &vMayaRot.x, &vMayaRot.y, &vMayaRot.z );
+
+
+ //
+ // Obviously, this could all be simplified, but it's nice to make it easy to see what it's doing here.
+ //
+
+
+ // Translate the camera position/angles from Maya space to model space.
+ // In model space, +X=forward, +Y=left, and +Z=up
+ // (i.e. the model faces forward along +X)
+ Vector vCameraPos;
+ QAngle vCameraAngles;
+ TranslateMayaToHLMVCoordinates( vMayaPos, vMayaRot, vCameraPos, vCameraAngles );
+
+
+ // Now, build a matrix from model space to camera space. The way we're defining camera space,
+ // the axes point the same way as in model space (+X=forward, +Y=left, +Z=up).
+ // This is a standard put-stuff-in-camera-space matrix (backtranslate and then backrotate).
+ matrix3x4_t mModelToCameraRot, mModelToCameraTrans, mModelToCameraFull;
+
+ // Backtranslate..
+ SetIdentityMatrix( mModelToCameraTrans );
+ MatrixSetColumn( -vCameraPos, 3, mModelToCameraTrans );
+
+ // Backrotate..
+ AngleMatrix( vCameraAngles, mModelToCameraRot );
+ MatrixTranspose( mModelToCameraRot );
+
+ // Concatenate.
+ MatrixMultiply( mModelToCameraRot, mModelToCameraTrans, mModelToCameraFull );
+
+
+ // Now we need to convert the camera space from above to HLMV's specific camera space. This just means negating
+ // the X and Y axes because HLMV's camera space is (+X=back, +Y=left, +Z=up).
+ matrix3x4_t mCameraToWorld(
+ -1, 0, 0, 0,
+ 0, -1, 0, 0,
+ 0, 0, 1, 0 );
+
+ // Blat all these matrices together.
+ matrix3x4_t mFinal;
+ MatrixMultiply( mCameraToWorld, mModelToCameraFull, mFinal );
+
+ // Tell HLMV our new fancy transform to use, and then we'll see the model from the same place Maya did.
+ g_pStudioModel->SetModelTransform( mFinal );
+
+ // Redraw.
+ d_MatSysWindow->redraw();
+ }
+ else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvModelTransform" ) )
+ {
+ matrix3x4_t m;
+
+ sscanf( szCommand, "%*s %f %f %f %f %f %f %f %f %f %f %f %f",
+ &m.m_flMatVal[0][0], &m.m_flMatVal[0][1], &m.m_flMatVal[0][2], &m.m_flMatVal[0][3],
+ &m.m_flMatVal[1][0], &m.m_flMatVal[1][1], &m.m_flMatVal[1][2], &m.m_flMatVal[1][3],
+ &m.m_flMatVal[2][0], &m.m_flMatVal[2][1], &m.m_flMatVal[2][2], &m.m_flMatVal[2][3] );
+
+ // Tell HLMV our new fancy transform to use, and then we'll see the model from the same place Maya did.
+ g_pStudioModel->SetModelTransform( m );
+
+ // Redraw.
+ d_MatSysWindow->redraw();
+ }
+ else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvLightRot" ) )
+ {
+ sscanf( szCommand, "%*s %f %f %f",
+ &g_viewerSettings.lightrot[0], &g_viewerSettings.lightrot[1], &g_viewerSettings.lightrot[2] );
+
+ // Redraw.
+ d_MatSysWindow->redraw();
+ }
+ else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvForceFrame" ) )
+ {
+ float flFrame = 0.0f;
+ sscanf( szCommand, "%*s %f", &flFrame );
+
+ d_cpl->SetFrameSlider( flFrame );
+ d_cpl->setFrame( flFrame );
+ d_cpl->setSpeedScale( 0 );
+
+ // Redraw.
+ d_MatSysWindow->redraw();
+ }
+ else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvLink" ) )
+ {
+ if ( !g_bHlmvControlled )
+ {
+ g_bHlmvControlled = true;
+
+ CUtlString label( "LINKED: " );
+ label += getLabel();
+ setLabel( label.Get() );
+ }
+ }
+ else if ( StringHasPrefixCaseSensitive( szCommand, "hlmvUnlink" ) )
+ {
+ g_bHlmvControlled = false;
+
+ const char *pszLabel = getLabel();
+ if ( StringHasPrefixCaseSensitive( pszLabel, "LINKED: " ) )
+ {
+ setLabel( pszLabel + 8 ); // Skip past "LINKED: "
+ }
+ }
+}
+
+
+void
+MDLViewer::redraw ()
+{
+ /*
+ mxEvent event;
+ event.event = mxEvent::Size;
+ event.width = w2 ();
+ event.height = h2 ();
+ handleEvent (&event);
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int MDLViewer::GetCurrentHitboxSet( void )
+{
+ return d_cpl ? d_cpl->GetCurrentHitboxSet() : 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Sends the model transform to the controlled hlmv instance
+//-----------------------------------------------------------------------------
+void MDLViewer::SendModelTransformToLinkedHlmv()
+{
+ if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
+ {
+ matrix3x4_t m;
+ g_pStudioModel->GetModelTransform( m );
+
+ CUtlBuffer cmd;
+ CUtlBuffer res;
+
+ cmd.Printf( "%s %f %f %f %f %f %f %f %f %f %f %f %f",
+ "hlmvModelTransform",
+ m.m_flMatVal[0][0], m.m_flMatVal[0][1], m.m_flMatVal[0][2], m.m_flMatVal[0][3],
+ m.m_flMatVal[1][0], m.m_flMatVal[1][1], m.m_flMatVal[1][2], m.m_flMatVal[1][3],
+ m.m_flMatVal[2][0], m.m_flMatVal[2][1], m.m_flMatVal[2][2], m.m_flMatVal[2][3] );
+
+ g_HlmvIpcClient.ExecuteCommand( cmd, res );
+
+ g_HlmvIpcClient.Disconnect();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Sends the light rotation to the controlled hlmv instance
+//-----------------------------------------------------------------------------
+void MDLViewer::SendLightRotToLinkedHlmv()
+{
+ if ( g_bHlmvMaster && g_HlmvIpcClient.Connect() )
+ {
+ CUtlBuffer cmdLightRot;
+ CUtlBuffer resLightRot;
+
+ cmdLightRot.Printf( "%s %f %f %f",
+ "hlmvLightRot",
+ g_viewerSettings.lightrot[0], g_viewerSettings.lightrot[1], g_viewerSettings.lightrot[2] );
+
+ g_HlmvIpcClient.ExecuteCommand( cmdLightRot, resLightRot );
+
+ g_HlmvIpcClient.Disconnect();
+ }
+}
+
+
+SpewRetval_t HLMVSpewFunc( SpewType_t spewType, char const *pMsg )
+{
+ g_bInError = true;
+ switch (spewType)
+ {
+ case SPEW_ERROR:
+ MessageBox(NULL, pMsg, "FATAL ERROR", MB_OK);
+ g_bInError = false;
+ return SPEW_ABORT;
+
+ default:
+ OutputDebugString(pMsg);
+ g_bInError = false;
+#ifdef _DEBUG
+ return spewType == SPEW_ASSERT ? SPEW_DEBUGGER : SPEW_CONTINUE;
+#else
+ return SPEW_CONTINUE;
+#endif
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CHLModelViewerApp : public CSteamAppSystemGroup
+{
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit();
+ virtual int Main();
+ virtual void PostShutdown();
+ virtual void Destroy();
+};
+
+
+//-----------------------------------------------------------------------------
+// Create all singleton systems
+//-----------------------------------------------------------------------------
+bool CHLModelViewerApp::Create()
+{
+ SpewOutputFunc( HLMVSpewFunc );
+
+ g_dxlevel = CommandLine()->ParmValue( "-dx", 0 );
+ g_bOldFileDialogs = ( CommandLine()->FindParm( "-olddialogs" ) != 0 );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
+ { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
+ { "vphysics.dll", VPHYSICS_INTERFACE_VERSION },
+ { "datacache.dll", DATACACHE_INTERFACE_VERSION },
+ { "datacache.dll", MDLCACHE_INTERFACE_VERSION },
+ { "datacache.dll", STUDIO_DATA_CACHE_INTERFACE_VERSION },
+ { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
+ { "soundsystem.dll", SOUNDSYSTEM_INTERFACE_VERSION },
+
+ { "", "" } // Required to terminate the list
+ };
+
+ if ( !AddSystems( appSystems ) )
+ return false;
+
+ g_pFileSystem = (IFileSystem*)FindSystem( FILESYSTEM_INTERFACE_VERSION );
+ g_pMaterialSystem = (IMaterialSystem*)FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION );
+ g_pMaterialSystemHardwareConfig = (IMaterialSystemHardwareConfig*)FindSystem( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION );
+ g_pStudioRender = (IStudioRender*)FindSystem( STUDIO_RENDER_INTERFACE_VERSION );
+ g_pDataCache = (IDataCache*)FindSystem( DATACACHE_INTERFACE_VERSION );
+ g_pMDLCache = (IMDLCache*)FindSystem( MDLCACHE_INTERFACE_VERSION );
+ g_pStudioDataCache = (IStudioDataCache*)FindSystem( STUDIO_DATA_CACHE_INTERFACE_VERSION );
+ physcollision = (IPhysicsCollision *)FindSystem( VPHYSICS_COLLISION_INTERFACE_VERSION );
+ physprop = (IPhysicsSurfaceProps *)FindSystem( VPHYSICS_SURFACEPROPS_INTERFACE_VERSION );
+ g_pSoundEmitterBase = (ISoundEmitterSystemBase *)FindSystem( SOUNDEMITTERSYSTEM_INTERFACE_VERSION );
+ g_pSoundSystem = (ISoundSystem *)FindSystem( SOUNDSYSTEM_INTERFACE_VERSION );
+
+ if ( !g_pFileSystem || !physprop || !physcollision || !g_pMaterialSystem || !g_pStudioRender || !g_pMDLCache || !g_pDataCache )
+ {
+ Error("Unable to load required library interface!\n");
+ }
+
+ const char *pShaderDLL = CommandLine()->ParmValue("-shaderdll");
+ const char *pArg;
+ if ( CommandLine()->CheckParm( "-shaderapi", &pArg ))
+ {
+ pShaderDLL = pArg;
+ }
+
+ if(!pShaderDLL)
+ {
+ pShaderDLL = "shaderapidx9.dll";
+ }
+
+ g_pMaterialSystem->SetShaderAPI( pShaderDLL );
+
+ g_Factory = GetFactory();
+
+ return true;
+}
+
+
+void CHLModelViewerApp::Destroy()
+{
+ g_pFileSystem = NULL;
+ g_pMaterialSystem = NULL;
+ g_pMaterialSystemHardwareConfig = NULL;
+ g_pStudioRender = NULL;
+ g_pDataCache = NULL;
+ g_pMDLCache = NULL;
+ g_pStudioDataCache = NULL;
+ physcollision = NULL;
+ physprop = NULL;
+}
+
+
+//-----------------------------------------------------------------------------
+// Init, shutdown
+//-----------------------------------------------------------------------------
+bool CHLModelViewerApp::PreInit( )
+{
+ CreateInterfaceFn factory = GetFactory();
+ ConnectTier1Libraries( &factory, 1 );
+ ConVar_Register( 0 );
+
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+
+ // Add paths...
+ if ( !SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ // Get the adapter from the command line....
+ const char *pAdapterString;
+ int nAdapter = 0;
+ if (CommandLine()->CheckParm( "-adapter", &pAdapterString ))
+ {
+ nAdapter = atoi( pAdapterString );
+ }
+
+ int nAdapterFlags = 0;
+ if ( CommandLine()->CheckParm( "-ref" ) )
+ {
+ nAdapterFlags |= MATERIAL_INIT_REFERENCE_RASTERIZER;
+ }
+
+ g_pMaterialSystem->SetAdapter( nAdapter, nAdapterFlags );
+
+ g_bOldFileDialogs = true;
+ if ( CommandLine()->FindParm( "-NoSteamdDialog" ) )
+ g_bOldFileDialogs = false;
+
+ LoadFileSystemDialogModule();
+
+ return true;
+}
+
+void CHLModelViewerApp::PostShutdown()
+{
+ UnloadFileSystemDialogModule();
+ DisconnectTier1Libraries();
+}
+
+
+//-----------------------------------------------------------------------------
+// main application
+//-----------------------------------------------------------------------------
+int CHLModelViewerApp::Main()
+{
+ g_pMaterialSystem->ModInit();
+ g_pSoundEmitterBase->ModInit();
+
+ g_pDataCache->SetSize( 64 * 1024 * 1024 );
+
+ //mx::setDisplayMode (0, 0, 0);
+ g_MDLViewer = new MDLViewer ();
+ g_MDLViewer->setMenuBar (g_MDLViewer->getMenuBar ());
+
+ g_pStudioModel->Init();
+ g_pStudioModel->ModelInit();
+ g_pStudioModel->ClearLookTargets( );
+
+ // Load up the initial model
+ const char *pMdlName = NULL;
+ int nParmCount = CommandLine()->ParmCount();
+ if ( nParmCount > 1 )
+ {
+ pMdlName = CommandLine()->GetParm( nParmCount - 1 );
+ }
+
+ if ( pMdlName && Q_stristr( pMdlName, ".mdl" ) )
+ {
+ char absPath[MAX_PATH];
+ Q_MakeAbsolutePath( absPath, sizeof( absPath ), pMdlName );
+
+ if ( CommandLine()->FindParm( "-screenshot" ) )
+ {
+ g_MDLViewer->SaveScreenShot( absPath );
+ }
+ else if ( CommandLine()->FindParm( "-dump" ) )
+ {
+ g_MDLViewer->DumpText( absPath );
+ }
+ else
+ {
+ g_MDLViewer->LoadModelFile( absPath );
+ }
+ }
+
+ int nRetVal = mx::run ();
+
+ g_pStudioModel->Shutdown();
+ g_pMaterialSystem->ModShutdown();
+
+ return nRetVal;
+}
+
+static bool CHLModelViewerApp_SuggestGameInfoDirFn( CFSSteamSetupInfo const *pFsSteamSetupInfo, char *pchPathBuffer, int nBufferLength, bool *pbBubbleDirectories )
+{
+ const char *pMdlName = NULL;
+ int nParmCount = CommandLine()->ParmCount();
+ if ( nParmCount > 1 )
+ {
+ pMdlName = CommandLine()->GetParm( nParmCount - 1 );
+ }
+
+ if ( pMdlName && Q_stristr( pMdlName, ".mdl" ) )
+ {
+ Q_MakeAbsolutePath( pchPathBuffer, nBufferLength, pMdlName );
+
+ if ( pbBubbleDirectories )
+ *pbBubbleDirectories = true;
+
+ return true;
+ }
+
+ return false;
+}
+
+
+//-----------------------------------------------------------------------------
+// Main entry point
+//-----------------------------------------------------------------------------
+int main (int argc, char *argv[])
+{
+ CommandLine()->CreateCmdLine( argc, argv );
+ mx::init( argc, argv );
+
+ // make sure we start in the right directory
+ char szName[256];
+ strcpy( szName, mx::getApplicationPath() );
+ // mx_setcwd (szName);
+
+ // Set game info directory suggestion callback
+ SetSuggestGameInfoDirFn( CHLModelViewerApp_SuggestGameInfoDirFn );
+
+ CHLModelViewerApp hlmodelviewerApp;
+ CSteamApplication steamApplication( &hlmodelviewerApp );
+ return steamApplication.Run();
+}
+//
+// Implementation of IPC server
+//
+
+
+CHlmvIpcServer::~CHlmvIpcServer()
+{
+ for ( int k = 0; k < m_lstCommands.Count(); ++ k )
+ {
+ delete [] m_lstCommands[k];
+ }
+ m_lstCommands.Purge();
+}
+
+bool CHlmvIpcServer::HasCommands()
+{
+ AUTO_LOCK( m_mtx );
+ return m_lstCommands.Count() > 0;
+}
+
+void CHlmvIpcServer::AppendCommand( char *pszCommand )
+{
+ AUTO_LOCK( m_mtx );
+ m_lstCommands.AddToTail( pszCommand );
+}
+
+char * CHlmvIpcServer::GetCommand()
+{
+ AUTO_LOCK( m_mtx );
+ return m_lstCommands.Count() ? m_lstCommands[0] : "";
+}
+
+void CHlmvIpcServer::PopCommand()
+{
+ AUTO_LOCK( m_mtx );
+ if ( m_lstCommands.Count() )
+ {
+ delete [] m_lstCommands[0];
+ m_lstCommands.Remove( 0 );
+ }
+}
+
+BOOL CHlmvIpcServer::ExecuteCommand(CUtlBuffer &cmd, CUtlBuffer &res)
+{
+ char *szCommand = ( char * ) cmd.Base();
+ int nLen = strlen( szCommand );
+ while ( nLen > 0 && V_isspace( szCommand[ nLen - 1 ] ) )
+ -- nLen;
+
+ if ( nLen <= 0 )
+ return FALSE;
+
+ char *pchCopy = new char[ nLen + 1 ];
+ memcpy( pchCopy, szCommand, nLen );
+ pchCopy[ nLen ] = 0;
+
+ AppendCommand( pchCopy );
+
+ res.PutInt( 0 );
+ return TRUE;
+}
+
diff --git a/utils/hlmv/mdlviewer.h b/utils/hlmv/mdlviewer.h
new file mode 100644
index 0000000..d13b5a6
--- /dev/null
+++ b/utils/hlmv/mdlviewer.h
@@ -0,0 +1,163 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: mdlviewer.h
+// last modified: Apr 28 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#ifndef INCLUDED_MDLVIEWER
+#define INCLUDED_MDLVIEWER
+
+
+
+#ifndef INCLUDED_MXWINDOW
+#include "mxWindow.h"
+#endif
+
+
+
+#define IDC_FILE_LOADMODEL 1001
+#define IDC_FILE_LOADBACKGROUNDTEX 1002
+#define IDC_FILE_LOADGROUNDTEX 1003
+#define IDC_FILE_UNLOADGROUNDTEX 1004
+#define IDC_FILE_CLOSEPAKFILE 1007
+#define IDC_FILE_RECENTMODELS1 1008
+#define IDC_FILE_RECENTMODELS2 1009
+#define IDC_FILE_RECENTMODELS3 1010
+#define IDC_FILE_RECENTMODELS4 1011
+#define IDC_FILE_RECENTMODELS5 1012
+#define IDC_FILE_RECENTMODELS6 1013
+#define IDC_FILE_RECENTMODELS7 1014
+#define IDC_FILE_RECENTMODELS8 1015
+#define IDC_FILE_EXIT 1016
+#define IDC_FILE_REFRESH 1017
+#define IDC_FILE_LOADMERGEDMODEL 1018
+#define IDC_FILE_UNLOADMERGEDMODEL 1019
+#define IDC_FILE_LOADMODEL_STEAM 1020
+#define IDC_FILE_LOADMERGEDMODEL_STEAM 1021
+#define IDC_FLUSH_SHADERS 1022
+
+#define IDC_OPTIONS_COLORBACKGROUND 1101
+#define IDC_OPTIONS_COLORGROUND 1102
+#define IDC_OPTIONS_COLORLIGHT 1103
+#define IDC_OPTIONS_COLORAMBIENT 1104
+#define IDC_OPTIONS_CENTERVIEW 1105
+#define IDC_OPTIONS_CENTERVERTS 1106
+#define IDC_OPTIONS_MAKESCREENSHOT 1107
+#define IDC_OPTIONS_DUMP 1108
+#define IDC_OPTIONS_VIEWMODEL 1109
+#define IDC_OPTIONS_SYNCHLMVCAMERA 1110
+#define IDC_OPTIONS_LINKHLMV 1111
+#define IDC_OPTIONS_UNLINKHLMV 1112
+
+#define IDC_VIEW_FILEASSOCIATIONS 1201
+#define IDC_VIEW_ACTIVITIES 1202
+#define IDC_VIEW_HIDDEN 1203
+
+#define IDC_HELP_GOTOHOMEPAGE 1301
+#define IDC_HELP_ABOUT 1302
+
+// Keyboard accelerators (not items on any menu...checkboxes on Render tab)
+#define IDC_ACCEL_WIREFRAME 1401
+#define IDC_ACCEL_ATTACHMENTS 1402
+#define IDC_ACCEL_GROUND 1403
+#define IDC_ACCEL_HITBOXES 1404
+#define IDC_ACCEL_BONES 1405
+#define IDC_ACCEL_BACKGROUND 1406
+#define IDC_ACCEL_MOVEMENT 1407
+#define IDC_ACCEL_NORMALS 1408
+#define IDC_ACCEL_TANGENTS 1409
+#define IDC_ACCEL_SHADOW 1410
+
+#define IDC_FILE_UNLOADMERGEDMODEL1 1414
+#define IDC_FILE_UNLOADMERGEDMODEL2 1415
+#define IDC_FILE_UNLOADMERGEDMODEL3 1416
+#define IDC_FILE_UNLOADMERGEDMODEL4 1417
+#define IDC_FILE_UNLOADMERGEDMODEL5 1418
+#define IDC_FILE_UNLOADMERGEDMODEL6 1419
+#define IDC_FILE_UNLOADMERGEDMODEL7 1420
+#define IDC_FILE_UNLOADMERGEDMODEL8 1421
+#define IDC_FILE_UNLOADMERGEDMODEL9 1422
+#define IDC_FILE_UNLOADMERGEDMODEL10 1423
+#define IDC_FILE_UNLOADMERGEDMODEL11 1424
+#define IDC_FILE_UNLOADMERGEDMODEL12 1425
+
+#define IDC_FILE_UNLOADALLMERGEDMODELS 1430
+
+class mxMenuBar;
+class MatSysWindow;
+class ControlPanel;
+class mxMenu;
+
+enum { Action, Size, Timer, Idle, Show, Hide,
+ MouseUp, MouseDown, MouseMove, MouseDrag,
+ KeyUp, KeyDown
+ };
+
+class MDLViewer : public mxWindow
+{
+ mxMenuBar *mb;
+ MatSysWindow *d_MatSysWindow;
+ ControlPanel *d_cpl;
+ mxMenu *menuOptions;
+ mxMenu *menuView;
+
+ void loadRecentFiles ();
+ void saveRecentFiles ();
+ void initRecentFiles ();
+
+public:
+ // CREATORS
+ MDLViewer ();
+ ~MDLViewer ();
+
+ // MANIPULATORS
+ virtual int handleEvent (mxEvent *event);
+
+ void SendModelTransformToLinkedHlmv();
+ void SendLightRotToLinkedHlmv();
+
+ void redraw ();
+
+ void handleIpcCommand( char *szCommand );
+
+ void Refresh( void );
+ void LoadModelFile( const char *pszFile, int slot = -1 );
+ void SaveScreenShot( const char *pszFile );
+ void DumpText( const char *pszFile );
+
+ // ACCESSORS
+ mxMenuBar *getMenuBar () const { return mb; }
+ MatSysWindow *getMatSysWindow () const { return d_MatSysWindow; }
+
+ int GetCurrentHitboxSet( void );
+
+private:
+ const char* SteamGetOpenFilename();
+};
+
+
+
+extern MDLViewer *g_MDLViewer;
+extern char g_appTitle[];
+
+
+
+#endif // INCLUDED_MDLVIEWER \ No newline at end of file
diff --git a/utils/hlmv/mxLineEdit2.cpp b/utils/hlmv/mxLineEdit2.cpp
new file mode 100644
index 0000000..17c9090
--- /dev/null
+++ b/utils/hlmv/mxLineEdit2.cpp
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include <windows.h>
+#include <richedit.h>
+#include "mxLineEdit2.h"
+
+
+
+mxLineEdit2::mxLineEdit2( mxWindow *parent, int x, int y, int w, int h, const char *label, int id, int style )
+ : mxLineEdit( parent, x, y, w, h, label, id, style )
+{
+}
+
+
+void mxLineEdit2::getText( char *pOut, int len )
+{
+ GetWindowText( (HWND) getHandle (), pOut, len );
+ pOut[len-1] = 0;
+}
+
+
+void mxLineEdit2::setText( const char *pText )
+{
+ setLabel( "%s", pText );
+}
+
+
diff --git a/utils/hlmv/mxLineEdit2.h b/utils/hlmv/mxLineEdit2.h
new file mode 100644
index 0000000..bddb875
--- /dev/null
+++ b/utils/hlmv/mxLineEdit2.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef MXLINEEDIT2_H
+#define MXLINEEDIT2_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#ifndef INCLUDED_MXWINDOW
+#include <mxtk/mxWindow.h>
+#endif
+#include <mxtk/mxLineEdit.h>
+
+
+// Extends the (mostly unimplemented) mxLineEdit control.
+class mxLineEdit2 : public mxLineEdit
+{
+public:
+ mxLineEdit2( mxWindow *parent, int x, int y, int w, int h, const char *label = 0, int id = 0, int style = 0 );
+
+ void getText( char *pOut, int len );
+ void setText( const char *pText );
+};
+
+
+#endif // MXLINEEDIT2_H
diff --git a/utils/hlmv/pakviewer.cpp b/utils/hlmv/pakviewer.cpp
new file mode 100644
index 0000000..edd53c4
--- /dev/null
+++ b/utils/hlmv/pakviewer.cpp
@@ -0,0 +1,516 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: pakviewer.cpp
+// last modified: May 04 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <mxtk/mx.h>
+#include "pakviewer.h"
+#include "mdlviewer.h"
+// #include "GlWindow.h"
+#include "StudioModel.h"
+#include "ControlPanel.h"
+#include "FileAssociation.h"
+
+
+
+int
+pak_ExtractFile (const char *pakFile, const char *lumpName, char *outFile)
+{
+ FILE *file = fopen (pakFile, "rb");
+ if (!file)
+ return 0;
+
+ int ident, dirofs, dirlen;
+
+ fread (&ident, sizeof (int), 1, file);
+ if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
+ {
+ fclose (file);
+ return 0;
+ }
+
+ fread (&dirofs, sizeof (int), 1, file);
+ fread (&dirlen, sizeof (int), 1, file);
+
+ fseek (file, dirofs, SEEK_SET);
+ int numLumps = dirlen / 64;
+
+ for (int i = 0; i < numLumps; i++)
+ {
+ char name[56];
+ int filepos, filelen;
+
+ fread (name, 56, 1, file);
+ fread (&filepos, sizeof (int), 1, file);
+ fread (&filelen, sizeof (int), 1, file);
+
+ if (!mx_strcasecmp (name, lumpName))
+ {
+ FILE *out = fopen (outFile, "wb");
+ if (!out)
+ {
+ fclose (file);
+ return 0;
+ }
+
+ fseek (file, filepos, SEEK_SET);
+
+ while (filelen--)
+ fputc (fgetc (file), out);
+
+ fclose (out);
+ fclose (file);
+
+ return 1;
+ }
+ }
+
+ fclose (file);
+
+ return 0;
+}
+
+
+
+PAKViewer::PAKViewer (mxWindow *window)
+: mxWindow (window, 0, 0, 0, 0, "", mxWindow::Normal)
+{
+ strcpy (d_pakFile, "");
+ strcpy (d_currLumpName, "");
+
+ tvPAK = new mxTreeView (this, 0, 0, 0, 0, IDC_PAKVIEWER);
+ pmMenu = new mxPopupMenu ();
+ pmMenu->add ("Load Model", 1);
+ pmMenu->addSeparator ();
+ pmMenu->add ("Load Background", 2);
+ pmMenu->add ("Load Ground", 3);
+ pmMenu->addSeparator ();
+ pmMenu->add ("Play Sound", 4);
+ pmMenu->addSeparator ();
+ pmMenu->add ("Extract File...", 5);
+ setLoadEntirePAK (true);
+
+ setVisible (false);
+}
+
+
+
+PAKViewer::~PAKViewer ()
+{
+ //tvPAK->remove (0);
+ //tvPAK->remove();
+ closePAKFile ();
+}
+
+
+
+void
+_makeTempFileName (char *str, const char *suffix)
+{
+ strcpy (str, mx_gettemppath ());
+
+ strcat (str, "/hltempmodel");
+ strcat (str, suffix);
+}
+
+
+
+int
+PAKViewer::handleEvent (mxEvent *event)
+{
+ switch (event->event)
+ {
+ case mxEvent::Action:
+ {
+ switch (event->action)
+ {
+ case IDC_PAKVIEWER: // tvPAK
+ if (event->flags & mxEvent::RightClicked)
+ {
+ pmMenu->setEnabled (1, strstr (d_currLumpName, ".mdl") != 0);
+ pmMenu->setEnabled (2, strstr (d_currLumpName, ".tga") != 0);
+ pmMenu->setEnabled (3, strstr (d_currLumpName, ".tga") != 0);
+ pmMenu->setEnabled (4, strstr (d_currLumpName, ".wav") != 0);
+ int ret = pmMenu->popup (tvPAK, event->x, event->y);
+ switch (ret)
+ {
+ case 1:
+ OnLoadModel ();
+ break;
+
+ case 2:
+ OnLoadTexture (0);
+ break;
+
+ case 3:
+ OnLoadTexture (1);
+ break;
+
+ case 4:
+ OnPlaySound ();
+ break;
+
+ case 5:
+ OnExtract ();
+ break;
+ }
+ }
+ else if (event->flags & mxEvent::DoubleClicked)
+ {
+ OnPAKViewer ();
+ char e[16];
+
+ strncpy (e, mx_getextension (d_currLumpName), 16);
+ int mode = g_FileAssociation->getMode (&e[1]);
+ if (mode == -1)
+ return 1;
+
+ char *program = g_FileAssociation->getProgram (&e[1]);
+
+#ifdef WIN32
+ if (mode == 0)
+ {
+ char str[256];
+ _makeTempFileName (str, e);
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ else
+ {
+ if (program)
+ {
+ char path[256];
+ strcpy (path, program);
+ strcat (path, " ");
+ strcat (path, str);
+ if ((int) WinExec (path, SW_SHOW) <= 32)
+ mxMessageBox (this, "Error executing specified program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ }
+ }
+ }
+
+ // associated program
+ else if (mode == 1)
+ {
+ char str[256];
+ _makeTempFileName (str, e);
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, str))
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ else
+ if ((int) ShellExecute ((HWND) getHandle (), "open", str, 0, 0, SW_SHOW) <= 32)
+ mxMessageBox (this, "Error executing document with associated program.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ }
+
+ // HLMV default
+ else
+#endif
+ if (mode == 2)
+ {
+ if (!strcmp (e, ".mdl"))
+ OnLoadModel ();
+
+ else if (!strcmp (e, ".tga"))
+ OnLoadTexture (0);
+
+ else if (!strcmp (e, ".wav"))
+ OnPlaySound ();
+
+ return 1;
+ }
+ }
+
+ return OnPAKViewer ();
+ } // event->action
+ } // mxEvent::Action
+ break;
+
+ case mxEvent::Size:
+ {
+ tvPAK->setBounds (0, 0, event->width, event->height);
+ } // mxEvent::Size
+ break;
+
+ } // event->event
+
+ return 1;
+}
+
+
+
+int
+PAKViewer::OnPAKViewer ()
+{
+ mxTreeViewItem *tvi = tvPAK->getSelectedItem ();
+ if (tvi)
+ {
+ strcpy (d_currLumpName, tvPAK->getLabel (tvi));
+
+ // find the full lump name
+ mxTreeViewItem *tviParent = tvPAK->getParent (tvi);
+ char tmp[128];
+ while (tviParent)
+ {
+ strcpy (tmp, d_currLumpName);
+ strcpy (d_currLumpName, tvPAK->getLabel (tviParent));
+ strcat (d_currLumpName, "/");
+ strcat (d_currLumpName, tmp);
+ tviParent = tvPAK->getParent (tviParent);
+ }
+
+ if (!d_loadEntirePAK)
+ {
+ // finally insert "models/"
+ strcpy (tmp, d_currLumpName);
+ strcpy (d_currLumpName, "models/");
+ strcat (d_currLumpName, tmp);
+ }
+ }
+
+ return 1;
+}
+
+
+
+int PAKViewer::OnLoadModel ()
+{
+ static char str2[256];
+ char suffix[16];
+
+ strcpy (suffix, ".mdl");
+ _makeTempFileName (str2, suffix);
+
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
+ {
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ return 1;
+ }
+
+ g_pStudioModel->FreeModel ( false );
+ if( !g_pStudioModel->LoadModel (str2) )
+ {
+ mxMessageBox (this, "Error reading model header.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ return 1;
+ }
+
+ return 1;
+}
+
+
+
+int PAKViewer::OnLoadTexture (int pos)
+{
+ static char str2[256];
+ char suffix[16] = "";
+
+ if (strstr (d_currLumpName, ".tga"))
+ sprintf (suffix, "%d%s", pos, ".tga");
+
+ _makeTempFileName (str2, suffix);
+
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
+ {
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ return 1;
+ }
+
+ if (0 /* g_MDLViewer->getGlWindow ()->loadTexture (str2, pos) */)
+ {
+ if (pos == 0)
+ g_ControlPanel->setShowBackground (true);
+ else
+ g_ControlPanel->setShowGround (true);
+ }
+ else
+ mxMessageBox (this, "Error loading texture.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+
+ return 1;
+}
+
+
+
+int
+PAKViewer::OnPlaySound ()
+{
+#ifdef WIN32
+ static char str2[256];
+ char suffix[16] = "";
+
+ // stop any playing sound
+ PlaySound (0, 0, SND_FILENAME | SND_ASYNC);
+
+ if (strstr (d_currLumpName, ".wav"))
+ sprintf (suffix, "%d%s", 44, ".wav");
+
+ _makeTempFileName (str2, suffix);
+
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, str2))
+ {
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ return 1;
+ }
+
+ PlaySound (str2, 0, SND_FILENAME | SND_ASYNC);
+
+#endif
+ return 1;
+}
+
+
+
+int
+PAKViewer::OnExtract ()
+{
+ char *ptr = (char *) mxGetSaveFileName (this, "", "*.*");
+ if (ptr)
+ {
+ if (!pak_ExtractFile (d_pakFile, d_currLumpName, ptr))
+ mxMessageBox (this, "Error extracting from PAK file.", g_appTitle, MX_MB_OK | MX_MB_ERROR);
+ }
+
+ return 1;
+}
+
+
+
+int
+_compare(const void *arg1, const void *arg2)
+{
+ if (strchr ((char *) arg1, '/') && !strchr ((char *) arg2, '/'))
+ return -1;
+
+ else if (!strchr ((char *) arg1, '/') && strchr ((char *) arg2, '/'))
+ return 1;
+
+ else
+ return strcmp ((char *) arg1, (char *) arg2);
+}
+
+
+
+bool
+PAKViewer::openPAKFile (const char *pakFile)
+{
+ FILE *file = fopen (pakFile, "rb");
+ if (!file)
+ return false;
+
+ int ident, dirofs, dirlen;
+
+ // check for id
+ fread (&ident, sizeof (int), 1, file);
+ if (ident != (int) (('K' << 24) + ('C' << 16) + ('A' << 8) + 'P'))
+ {
+ fclose (file);
+ return false;
+ }
+
+ // load lumps
+ fread (&dirofs, sizeof (int), 1, file);
+ fread (&dirlen, sizeof (int), 1, file);
+ int numLumps = dirlen / 64;
+
+ fseek (file, dirofs, SEEK_SET);
+ lump_t *lumps = new lump_t[numLumps];
+ if (!lumps)
+ {
+ fclose (file);
+ return false;
+ }
+
+ fread (lumps, sizeof (lump_t), numLumps, file);
+ fclose (file);
+
+ qsort (lumps, numLumps, sizeof (lump_t), _compare);
+
+ // save pakFile for later
+ strcpy (d_pakFile, pakFile);
+
+ tvPAK->remove (0);
+
+ char namestack[32][32];
+ mxTreeViewItem *tvistack[32];
+ for (int k = 0; k < 32; k++)
+ {
+ strcpy (namestack[k], "");
+ tvistack[k] = 0;
+ }
+
+ for (int i = 0; i < numLumps; i++)
+ {
+ if (d_loadEntirePAK || !strncmp (lumps[i].name, "models", 6))
+ {
+ char *tok;
+ if (d_loadEntirePAK)
+ tok = &lumps[i].name[0];
+ else
+ tok = &lumps[i].name[7];
+
+ int i = 1;
+ while (tok)
+ {
+ char *end = strchr (tok, '/');
+ if (end)
+ *end = '\0';
+
+ if (strcmp (namestack[i], tok))
+ {
+ strcpy (namestack[i], tok);
+/*
+ if (i == 0)
+ tvistack[i] = tvPAK->add (0, tok);
+ else*/
+ tvistack[i] = tvPAK->add (tvistack[i - 1], tok);
+
+ for (int j = i + 1; j < 32; j++)
+ {
+ strcpy (namestack[j], "");
+ tvistack[j] = 0;
+ }
+ }
+
+ ++i;
+
+ if (end)
+ tok = end + 1;
+ else
+ tok = 0;
+ }
+ }
+ }
+
+ delete[] lumps;
+
+ setVisible (true);
+
+ return true;
+}
+
+
+
+void
+PAKViewer::closePAKFile ()
+{
+ strcpy (d_pakFile, "");
+ setVisible (false);
+}
diff --git a/utils/hlmv/pakviewer.h b/utils/hlmv/pakviewer.h
new file mode 100644
index 0000000..c5c4acf
--- /dev/null
+++ b/utils/hlmv/pakviewer.h
@@ -0,0 +1,99 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: pakviewer.h
+// last modified: Apr 28 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#ifndef INCLUDED_PAKVIEWER
+#define INCLUDED_PAKVIEWER
+
+
+
+#ifndef INCLUDED_MXWINDOW
+#include "mxWindow.h"
+#endif
+
+
+
+#define IDC_PAKVIEWER 1001
+
+
+
+typedef struct
+{
+ char name[56];
+ int filepos;
+ int filelen;
+} lump_t;
+
+
+
+#ifdef __cpluspus
+extern "C" {
+#endif
+
+int pak_ExtractFile (const char *pakFile, const char *lumpName, char *outFile);
+
+#ifdef __cpluspus
+}
+#endif
+
+
+
+class mxTreeView;
+class mxButton;
+class mxPopupMenu;
+// class GlWindow;
+
+
+
+class PAKViewer : public mxWindow
+{
+ char d_pakFile[256];
+ char d_currLumpName[256];
+ bool d_loadEntirePAK;
+ mxTreeView *tvPAK;
+ mxPopupMenu *pmMenu;
+
+public:
+ // CREATORS
+ PAKViewer (mxWindow *window);
+ ~PAKViewer ();
+
+ // MANIPULATORS
+ virtual int handleEvent (mxEvent *event);
+ int OnPAKViewer ();
+ int OnLoadModel ();
+ int OnLoadTexture (int pos);
+ int OnPlaySound ();
+ int OnExtract ();
+
+ bool openPAKFile (const char *pakFile);
+ void closePAKFile ();
+ void setLoadEntirePAK (bool b) { d_loadEntirePAK = b; }
+
+ // ACCESSORS
+ bool getLoadEntirePAK () const { return d_loadEntirePAK; }
+};
+
+
+
+#endif // INCLUDED_PAKVIEWER \ No newline at end of file
diff --git a/utils/hlmv/physmesh.cpp b/utils/hlmv/physmesh.cpp
new file mode 100644
index 0000000..957bd8f
--- /dev/null
+++ b/utils/hlmv/physmesh.cpp
@@ -0,0 +1,619 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/***
+*
+* Copyright (c) 1998, Valve LLC. All rights reserved.
+*
+* This product contains software technology licensed from Id
+* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
+* All Rights Reserved.
+*
+****/
+
+#include "filesystem.h"
+#include "vphysics/constraints.h"
+#include "phyfile.h"
+#include "physdll.h"
+#include "physmesh.h"
+#include "mathlib/mathlib.h"
+#include <stddef.h>
+#include "utlvector.h"
+#include "commonmacros.h"
+#include "studiomodel.h"
+#include "tier1/strtools.h"
+#include "bone_setup.h"
+#include "fmtstr.h"
+#include "vcollide_parse.h"
+
+int FindPhysprop( const char *pPropname );
+
+bool LoadPhysicsProperties( void );
+extern int FindBoneIndex( CStudioHdr *pstudiohdr, const char *pName );
+
+
+struct collisionpair_t
+{
+ int object0;
+ int object1;
+ collisionpair_t *pNext;
+};
+
+class CStudioPhysics : public IStudioPhysics
+{
+public:
+ CStudioPhysics( void )
+ {
+ m_pList = NULL;
+ m_listCount = 0;
+ m_mass = 0;
+ m_noselfCollisions = false;
+ m_pCollisionPairs = NULL;
+ memset( &m_edit, 0, sizeof(editparams_t) );
+ }
+
+ ~CStudioPhysics( void )
+ {
+ if ( physcollision )
+ {
+ for ( int i = 0; i < m_listCount; i++ )
+ {
+ physcollision->DestroyDebugMesh( m_pList[i].m_vertCount, m_pList[i].m_pVerts );
+ physcollision->DestroyQueryModel( m_pList[i].m_pCollisionModel );
+ }
+ }
+ delete[] m_pList;
+ }
+
+ int Count( void )
+ {
+ return m_listCount;
+ }
+
+ CPhysmesh *GetMesh( int index )
+ {
+ if ( index < m_listCount )
+ return m_pList + index;
+
+ return NULL;
+ }
+
+ float GetMass( void ) { return m_mass; }
+
+ void AddCollisionPair( int index0, int index1 )
+ {
+ collisionpair_t *pPair = new collisionpair_t;
+ pPair->object0 = index0;
+ pPair->object1 = index1;
+ pPair->pNext = m_pCollisionPairs;
+ m_pCollisionPairs = pPair;
+
+ }
+
+ void Load( MDLHandle_t handle );
+ char *DumpQC( void );
+ void ParseKeydata( void );
+
+ vcollide_t *GetVCollide()
+ {
+ return g_pMDLCache->GetVCollide( m_MDLHandle );
+ }
+
+ CPhysmesh *m_pList;
+ MDLHandle_t m_MDLHandle;
+ int m_listCount;
+
+ float m_mass;
+ editparams_t m_edit;
+ bool m_noselfCollisions;
+ collisionpair_t *m_pCollisionPairs;
+};
+
+
+void CPhysmesh::Clear( void )
+{
+ memset( this, 0, sizeof(*this) );
+ memset( &m_constraint, 0, sizeof(m_constraint) );
+ m_constraint.parentIndex = -1;
+ m_constraint.childIndex = -1;
+}
+
+
+IStudioPhysics *LoadPhysics( MDLHandle_t mdlHandle )
+{
+ CStudioPhysics *pPhysics = new CStudioPhysics;
+ pPhysics->Load( mdlHandle );
+ return pPhysics;
+}
+
+void DestroyPhysics( IStudioPhysics *pStudioPhysics )
+{
+ CStudioPhysics *pPhysics = static_cast<CStudioPhysics*>( pStudioPhysics );
+ if ( pPhysics )
+ {
+ delete pPhysics;
+ }
+}
+
+void CStudioPhysics::Load( MDLHandle_t mdlHandle )
+{
+ m_MDLHandle = mdlHandle;
+
+ LoadPhysicsProperties();
+
+ vcollide_t *pVCollide = GetVCollide( );
+ if ( !pVCollide )
+ {
+ m_pList = NULL;
+ m_listCount = 0;
+ return;
+ }
+
+ m_pList = new CPhysmesh[pVCollide->solidCount];
+ m_listCount = pVCollide->solidCount;
+
+ int i;
+
+ for ( i = 0; i < pVCollide->solidCount; i++ )
+ {
+ m_pList[i].Clear();
+ m_pList[i].m_vertCount = physcollision->CreateDebugMesh( pVCollide->solids[i], &m_pList[i].m_pVerts );
+ m_pList[i].m_pCollisionModel = physcollision->CreateQueryModel( pVCollide->solids[i] );
+ }
+
+ ParseKeydata();
+
+ CStudioHdr studioHdr( g_pMDLCache->GetStudioHdr( mdlHandle ), g_pMDLCache );
+ for ( i = 0; i < pVCollide->solidCount; i++ )
+ {
+ CPhysmesh *pmesh = m_pList + i;
+ int boneIndex = FindBoneIndex( &studioHdr, pmesh->m_boneName );
+ if ( boneIndex < 0 )
+ continue;
+
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ CPhysmesh *pparent = m_pList + pmesh->m_constraint.parentIndex;
+ int parentIndex = FindBoneIndex( &studioHdr, pparent->m_boneName );
+ Studio_CalcBoneToBoneTransform( &studioHdr, boneIndex, parentIndex, pmesh->m_matrix );
+ }
+ else
+ {
+ MatrixInvert( studioHdr.pBone(boneIndex)->poseToBone, pmesh->m_matrix );
+ }
+ }
+
+ // doesn't have a root bone? Make it the first bone
+ if ( !m_edit.rootName[0] )
+ {
+ strcpy( m_edit.rootName, m_pList[0].m_boneName );
+ }
+}
+
+
+class CEditParse : public IVPhysicsKeyHandler
+{
+public:
+ virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
+ {
+ editparams_t *pEdit = (editparams_t *)pCustom;
+ if ( !strcmpi( pKey, "rootname" ) )
+ {
+ strncpy( pEdit->rootName, pValue, sizeof(pEdit->rootName) );
+ }
+ else if ( !strcmpi( pKey, "totalmass" ) )
+ {
+ pEdit->totalMass = atof( pValue );
+ }
+ else if ( !strcmpi( pKey, "concave" ) )
+ {
+ pEdit->concave = atoi( pValue );
+ }
+ else if ( !strcmpi( pKey, "jointmerge" ) )
+ {
+ char tmp[1024];
+ char parentName[512], childName[512];
+ Q_strncpy( tmp, pValue, 1024 );
+ char *pWord = strtok( tmp, "," );
+ Q_strncpy( parentName, pWord, sizeof(parentName) );
+ pWord = strtok( NULL, "," );
+ Q_strncpy( childName, pWord, sizeof(childName) );
+ if ( pEdit->mergeCount < ARRAYSIZE(pEdit->mergeList) )
+ {
+ merge_t *pMerge = &pEdit->mergeList[pEdit->mergeCount];
+ pEdit->mergeCount++;
+ pMerge->parent = g_pStudioModel->FindBone(parentName);
+ pMerge->child = g_pStudioModel->FindBone(childName);
+ }
+ }
+ }
+ virtual void SetDefaults( void *pCustom )
+ {
+ editparams_t *pEdit = (editparams_t *)pCustom;
+ memset( pEdit, 0, sizeof(*pEdit) );
+ }
+};
+
+class CRagdollCollisionRulesParse : public IVPhysicsKeyHandler
+{
+public:
+ CRagdollCollisionRulesParse( CStudioPhysics *pStudio ) : m_pStudio(pStudio)
+ {
+ pStudio->m_noselfCollisions = false;
+ }
+
+ virtual void ParseKeyValue( void *pData, const char *pKey, const char *pValue )
+ {
+ if ( !strcmpi( pKey, "selfcollisions" ) )
+ {
+ // keys disabled by default
+ Assert( atoi(pValue) == 0 );
+ m_pStudio->m_noselfCollisions = true;
+ }
+ else if ( !strcmpi( pKey, "collisionpair" ) )
+ {
+ if ( !m_pStudio->m_noselfCollisions )
+ {
+ char tmp[1024];
+ Q_strncpy( tmp, pValue, 1024 );
+ char *pWord = strtok( tmp, "," );
+ int index0 = atoi(pWord);
+ pWord = strtok( NULL, "," );
+ int index1 = atoi(pWord);
+ m_pStudio->AddCollisionPair( index0, index1 );
+ }
+ else
+ {
+ Assert(0);
+ }
+ }
+ }
+ virtual void SetDefaults( void *pData ) {}
+
+private:
+ CStudioPhysics *m_pStudio;
+};
+
+class CSolidParse : public IVPhysicsKeyHandler
+{
+public:
+ virtual void ParseKeyValue( void *pCustom, const char *pKey, const char *pValue )
+ {
+ hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
+ if ( !strcmpi( pKey, "massbias" ) )
+ {
+ pSolid->massBias = atof( pValue );
+ }
+ else
+ {
+ printf("Bad key %s!!\n", pKey);
+ }
+ }
+ virtual void SetDefaults( void *pCustom )
+ {
+ hlmvsolid_t *pSolid = (hlmvsolid_t *)pCustom;
+ pSolid->massBias = 1.0;
+ }
+};
+
+void CStudioPhysics::ParseKeydata( void )
+{
+ IVPhysicsKeyParser *pParser = physcollision->VPhysicsKeyParserCreate( GetVCollide()->pKeyValues );
+
+ while ( !pParser->Finished() )
+ {
+ const char *pBlock = pParser->GetCurrentBlockName();
+ if ( !stricmp( pBlock, "solid" ) )
+ {
+ hlmvsolid_t solid;
+ CSolidParse solidParse;
+
+ pParser->ParseSolid( &solid, &solidParse );
+ solid.surfacePropIndex = FindPhysprop( solid.surfaceprop );
+
+ if ( solid.index >= 0 && solid.index < m_listCount )
+ {
+ strcpy( m_pList[solid.index].m_boneName, solid.name );
+ memcpy( &m_pList[solid.index].m_solid, &solid, sizeof(solid) );
+ }
+ }
+ else if ( !stricmp( pBlock, "ragdollconstraint" ) )
+ {
+ constraint_ragdollparams_t constraint;
+ pParser->ParseRagdollConstraint( &constraint, NULL );
+ if ( constraint.childIndex >= 0 && constraint.childIndex < m_listCount )
+ {
+ // In the editor / qc these show up as 5X so that 1.0 is the default
+ constraint.axes[0].torque *= 5;
+ constraint.axes[1].torque *= 5;
+ constraint.axes[2].torque *= 5;
+ m_pList[constraint.childIndex].m_constraint = constraint;
+ }
+ }
+ else if ( !stricmp( pBlock, "editparams" ) )
+ {
+ CEditParse editParse;
+ pParser->ParseCustom( &m_edit, &editParse );
+ m_mass = m_edit.totalMass;
+ }
+ else if ( !strcmpi( pBlock, "collisionrules" ) )
+ {
+ CRagdollCollisionRulesParse rules(this);
+ pParser->ParseCustom( NULL, &rules );
+ }
+ else
+ {
+ pParser->SkipBlock();
+ }
+ }
+ physcollision->VPhysicsKeyParserDestroy( pParser );
+}
+
+
+int FindPhysprop( const char *pPropname )
+{
+ if ( physprop )
+ {
+ int count = physprop->SurfacePropCount();
+ for ( int i = 0; i < count; i++ )
+ {
+ if ( !strcmpi( pPropname, physprop->GetPropName(i) ) )
+ return i;
+ }
+ }
+ return 0;
+}
+
+
+
+class CTextBuffer
+{
+public:
+ CTextBuffer( void ) {}
+ ~CTextBuffer( void ) {}
+
+ inline int GetSize( void ) { return m_buffer.Size(); }
+ inline char *GetData( void ) { return m_buffer.Base(); }
+
+ void WriteText( const char *pText )
+ {
+ int len = strlen( pText );
+ CopyData( pText, len );
+ }
+
+ void Terminate( void ) { CopyData( "\0", 1 ); }
+
+ void CopyData( const char *pData, int len )
+ {
+ int offset = m_buffer.AddMultipleToTail( len );
+ memcpy( m_buffer.Base() + offset, pData, len );
+ }
+
+private:
+ CUtlVector<char> m_buffer;
+};
+
+
+struct physdefaults_t
+{
+ int surfacePropIndex;
+ float inertia;
+ float damping;
+ float rotdamping;
+};
+
+//-----------------------------------------------------------------------------
+// Purpose: Nasty little routine (that was easy to code) to find the most common
+// value in an array of structs containing that as a member
+// Input : *pStructArray - pointer to head of struct array
+// arrayCount - number of elements in the array
+// structSize - size of each element
+// fieldOffset - offset to the float we're finding
+// Output : static T - most common value
+//-----------------------------------------------------------------------------
+template< class T >
+static T FindCommonValue( void *pStructArray, int arrayCount, int structSize, int fieldOffset )
+{
+ int maxCount = 0;
+ T maxVal = 0;
+
+ // BUGBUG: This is O(n^2), but n is really small
+ for ( int i = 0; i < arrayCount; i++ )
+ {
+ // current = struct[i].offset
+ T current = *(T *)((char *)pStructArray + (i*structSize) + fieldOffset);
+ int currentCount = 0;
+
+ // if everything is set to the default, this is almost O(n)
+ if ( current == maxVal )
+ continue;
+
+ for ( int j = 0; j < arrayCount; j++ )
+ {
+ // value = struct[j].offset
+ T value = *(T *)((char *)pStructArray + (j*structSize) + fieldOffset);
+ if ( value == current )
+ currentCount++;
+ }
+
+ if ( currentCount > maxCount )
+ {
+ maxVal = current;
+ maxCount = currentCount;
+ }
+ }
+
+ return maxVal;
+}
+
+static void CalcDefaultProperties( CPhysmesh *pList, int listCount, physdefaults_t &defs )
+{
+ defs.surfacePropIndex = FindCommonValue<int>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.surfacePropIndex) );
+ defs.inertia = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.inertia) );
+ defs.damping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.damping) );
+ defs.rotdamping = FindCommonValue<float>( pList, listCount, sizeof(CPhysmesh), offsetof(CPhysmesh, m_solid.params.rotdamping) );
+}
+
+static void DumpModelProperties( CTextBuffer &out, float mass, physdefaults_t &defs )
+{
+ char tmpbuf[1024];
+ sprintf( tmpbuf, "\t$mass %.1f\r\n", mass );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$inertia %.2f\r\n", defs.inertia );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$damping %.2f\r\n", defs.damping );
+ out.WriteText( tmpbuf );
+ sprintf( tmpbuf, "\t$rotdamping %.2f\r\n", defs.rotdamping );
+ out.WriteText( tmpbuf );
+}
+
+char *CStudioPhysics::DumpQC( void )
+{
+ if ( !m_listCount )
+ return NULL;
+
+ CTextBuffer out;
+ physdefaults_t defs;
+
+ CalcDefaultProperties( m_pList, m_listCount, defs );
+
+ if ( m_listCount == 1 )
+ {
+ out.WriteText( "$collisionmodel ragdoll {\r\n\r\n" );
+ if ( m_edit.concave )
+ {
+ out.WriteText( "\t$concave\r\n" );
+ }
+ DumpModelProperties( out, m_mass, defs );
+ }
+ else
+ {
+ int i;
+
+ out.WriteText( "$collisionjoints ragdoll {\r\n\r\n" );
+ DumpModelProperties( out, m_mass, defs );
+
+ // write out the root bone
+ if ( m_edit.rootName[0] )
+ {
+ char tmp[128];
+ sprintf( tmp, "\t$rootbone \"%s\"\r\n", m_edit.rootName );
+ out.WriteText( tmp );
+ }
+
+ for ( i = 0; i < m_edit.mergeCount; i++ )
+ {
+ char tmp[1024];
+ if ( m_edit.mergeList[i].parent >= 0 && m_edit.mergeList[i].child >= 0 )
+ {
+ char const *pParentName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].parent)->pszName();
+ char const *pChildName = g_pStudioModel->GetStudioHdr()->pBone(m_edit.mergeList[i].child)->pszName();
+ Q_snprintf( tmp, sizeof(tmp), "\t$jointmerge \"%s\" \"%s\"\r\n", pParentName, pChildName );
+ out.WriteText( tmp );
+ }
+ }
+ char tmpbuf[1024];
+ for ( i = 0; i < m_listCount; i++ )
+ {
+ CPhysmesh *pmesh = m_pList + i;
+ char jointname[256];
+ sprintf( jointname, "\"%s\"", pmesh->m_boneName );
+ if ( pmesh->m_solid.massBias != 1.0 )
+ {
+ sprintf( tmpbuf, "\t$jointmassbias %s %.2f\r\n", jointname, pmesh->m_solid.massBias );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.inertia != defs.inertia )
+ {
+ sprintf( tmpbuf, "\t$jointinertia %s %.2f\r\n", jointname, pmesh->m_solid.params.inertia );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.damping != defs.damping )
+ {
+ sprintf( tmpbuf, "\t$jointdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.damping );
+ out.WriteText( tmpbuf );
+ }
+ if ( pmesh->m_solid.params.rotdamping != defs.rotdamping )
+ {
+ sprintf( tmpbuf, "\t$jointrotdamping %s %.2f\r\n", jointname, pmesh->m_solid.params.rotdamping );
+ out.WriteText( tmpbuf );
+ }
+
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ for ( int j = 0; j < 3; j++ )
+ {
+ char *pAxis[] = { "x", "y", "z" };
+ sprintf( tmpbuf, "\t$jointconstrain %s %s limit %.2f %.2f %.2f\r\n", jointname, pAxis[j], pmesh->m_constraint.axes[j].minRotation, pmesh->m_constraint.axes[j].maxRotation, pmesh->m_constraint.axes[j].torque );
+ out.WriteText( tmpbuf );
+ }
+ }
+ if ( i != m_listCount-1 )
+ {
+ out.WriteText( "\r\n" );
+ }
+ }
+ }
+
+ if ( m_noselfCollisions )
+ {
+ out.WriteText( "\t$noselfcollisions\r\n" );
+ }
+ else if ( m_pCollisionPairs )
+ {
+ collisionpair_t *pPair = m_pCollisionPairs;
+ out.WriteText("\r\n");
+ while ( pPair )
+ {
+ out.WriteText( CFmtStr( "\t$jointcollide %s %s\r\n", m_pList[pPair->object0].m_boneName, m_pList[pPair->object1].m_boneName ) );
+ pPair = pPair->pNext;
+ }
+ }
+ out.WriteText( "}\r\n" );
+
+ // only need the pose for ragdolls
+ if ( m_listCount != 1 )
+ {
+ out.WriteText( "$sequence ragdoll \t\t\"ragdoll_pose\" \t\tFPS 30 \t\tactivity ACT_DIERAGDOLL 1\r\n" );
+ }
+
+ out.Terminate();
+
+ if ( out.GetSize() )
+ {
+ char *pOutput = new char[out.GetSize()];
+ memcpy( pOutput, out.GetData(), out.GetSize() );
+ return pOutput;
+ }
+
+ return NULL;
+}
+
+static const char *pMaterialFilename = "scripts/surfaceproperties.txt";
+
+bool LoadPhysicsProperties( void )
+{
+ // already loaded
+ if ( physprop->SurfacePropCount() )
+ return false;
+
+ FileHandle_t fp = g_pFileSystem->Open( pMaterialFilename, "rb" );
+ if ( fp == FILESYSTEM_INVALID_HANDLE )
+ return false;
+
+ int len = g_pFileSystem->Size( fp );
+
+ char *pText = new char[len+1];
+ g_pFileSystem->Read( pText, len, fp );
+ g_pFileSystem->Close( fp );
+ pText[len]=0;
+
+ physprop->ParseSurfaceData( pMaterialFilename, pText );
+
+ delete[] pText;
+ return true;
+}
diff --git a/utils/hlmv/physmesh.h b/utils/hlmv/physmesh.h
new file mode 100644
index 0000000..1695ed6
--- /dev/null
+++ b/utils/hlmv/physmesh.h
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef PHYSMESH_H
+#define PHYSMESH_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "datacache/imdlcache.h"
+#include "vphysics_interface.h"
+#include "vcollide_parse.h"
+
+
+struct studiohdr_t;
+
+struct hlmvsolid_t : public solid_t
+{
+ float massBias;
+ int surfacePropIndex;
+};
+
+struct merge_t
+{
+ int parent;
+ int child;
+};
+
+struct editparams_t
+{
+ float totalMass;
+ char rootName[128];
+ int concave;
+ int mergeCount;
+ merge_t mergeList[32]; // there are never very many of these, so don't bother doing anything dynamic
+};
+
+class CPhysmesh
+{
+public:
+ void Clear( void );
+
+ char m_boneName[64];
+ int m_vertCount;
+ Vector *m_pVerts;
+ matrix3x4_t m_matrix;
+
+ hlmvsolid_t m_solid;
+ constraint_ragdollparams_t m_constraint;
+ ICollisionQuery *m_pCollisionModel;
+};
+
+class IStudioPhysics
+{
+public:
+ virtual ~IStudioPhysics( void ) {}
+
+ virtual char *DumpQC( void ) = 0;
+ virtual int Count( void ) = 0;
+ virtual CPhysmesh *GetMesh( int index ) = 0;
+ virtual float GetMass( void ) = 0;
+};
+
+IStudioPhysics *LoadPhysics( MDLHandle_t mdlHandle );
+void DestroyPhysics( IStudioPhysics *pStudioPhysics );
+
+#endif // PHYSMESH_H
diff --git a/utils/hlmv/resource.h b/utils/hlmv/resource.h
new file mode 100644
index 0000000..21224da
--- /dev/null
+++ b/utils/hlmv/resource.h
@@ -0,0 +1,23 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by hlmviewer.rc
+//
+#define IDI_ICON1 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 103
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1000
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/utils/hlmv/studio_flex.cpp b/utils/hlmv/studio_flex.cpp
new file mode 100644
index 0000000..9c0b28d
--- /dev/null
+++ b/utils/hlmv/studio_flex.cpp
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include <string.h>
+#include <assert.h>
+
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "bitmap/TGALoader.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "materialsystem/itexture.h"
+#include "matsyswin.h"
+#include "istudiorender.h"
+
+#include "studio_render.h"
+
+Vector g_viewtarget( 0, 0, 0 );
+float g_flexdescweight[MAXSTUDIOFLEXDESC];
+float g_flexdescweight2[MAXSTUDIOFLEXDESC];
+Vector vright( 1, 0, 0 );
+Vector vup( 0, 1, 0 );
+Vector r_origin( 0, 0, 0 );
+
+
+// hack! Should probably use the gamma stuff in mathlib since we already linking it.
+int LocalLinearToTexture( float v )
+{
+ return pow( v, 1.0f / 2.2f ) * 255;
+}
+
+// hack! Should probably use the gamma stuff in mathlib since we already linking it.
+float LocalTextureToLinear( int c )
+{
+ return pow( c / 255.0, 2.2 );
+}
+
+
+
+#define sign( a ) (((a) < 0) ? -1 : (((a) > 0) ? 1 : 0 ))
+
+
+void StudioModel::RunFlexRules( )
+{
+ StudioModel *pSrcModel = g_pStudioModel;
+
+ // only the root model has control over flex rules
+ CStudioHdr *pSrcStudioHdr = pSrcModel->GetStudioHdr();
+ CStudioHdr *pDstStudioHdr = GetStudioHdr();
+
+ if ( !pSrcStudioHdr )
+ {
+ pSrcModel = this;
+ pSrcStudioHdr = GetStudioHdr();
+ }
+
+ float src[MAXSTUDIOFLEXCTRL*4];
+
+ for (LocalFlexController_t i = LocalFlexController_t(0); i < pSrcStudioHdr->numflexcontrollers(); i++)
+ {
+ mstudioflexcontroller_t *pflex = pSrcStudioHdr->pFlexcontroller( i );
+ int j = pSrcStudioHdr->pFlexcontroller( i )->localToGlobal;
+ // remap m_flexweights to full dynamic range, global flexcontroller indexes
+ if (j >= 0 && j < MAXSTUDIOFLEXCTRL*4)
+ {
+ src[j] = pSrcModel->m_flexweight[i] * (pflex->max - pflex->min) + pflex->min;
+ }
+ }
+
+ pDstStudioHdr->RunFlexRules( src, g_flexdescweight );
+}
+
diff --git a/utils/hlmv/studio_render.cpp b/utils/hlmv/studio_render.cpp
new file mode 100644
index 0000000..340ec67
--- /dev/null
+++ b/utils/hlmv/studio_render.cpp
@@ -0,0 +1,2368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// studio_render.cpp: routines for drawing Half-Life 3DStudio models
+// updates:
+// 1-4-99 fixed AdvanceFrame wraping bug
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <windows.h> // for OutputDebugString. . has to be a better way!
+
+
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "vphysics/constraints.h"
+#include "physmesh.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "matsyswin.h"
+#include "istudiorender.h"
+#include "utldict.h"
+#include "filesystem.h"
+#include "studio_render.h"
+#include "materialsystem/imesh.h"
+#include "bone_setup.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "MDLViewer.h"
+#include "bone_accessor.h"
+#include "jigglebones.h"
+#include "debugdrawmodel.h"
+
+// FIXME:
+extern ViewerSettings g_viewerSettings;
+int g_dxlevel = 0;
+
+#pragma warning( disable : 4244 ) // double to float
+
+////////////////////////////////////////////////////////////////////////
+
+CStudioHdr *g_pCacheHdr = NULL;
+
+Vector g_flexedverts[MAXSTUDIOVERTS];
+Vector g_flexednorms[MAXSTUDIOVERTS];
+int g_flexages[MAXSTUDIOVERTS];
+
+Vector *g_pflexedverts;
+Vector *g_pflexednorms;
+int *g_pflexages;
+
+int g_smodels_total; // cookie
+
+matrix3x4_t g_viewtransform; // view transformation
+//matrix3x4_t g_posetoworld[MAXSTUDIOBONES]; // bone transformation matrix
+matrix3x4_t g_mCachedViewTransform; // copy of view transform for boneMerge passes
+
+static int maxNumVertices;
+static int first = 1;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+mstudioseqdesc_t &StudioModel::GetSeqDesc( int seq )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return pStudioHdr->pSeqdesc( seq );
+}
+
+mstudioanimdesc_t &StudioModel::GetAnimDesc( int anim )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return pStudioHdr->pAnimdesc( anim );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Keeps a global clock to autoplay sequences to run from
+// Also deals with speedScale changes
+//-----------------------------------------------------------------------------
+float GetAutoPlayTime( void )
+{
+ static int g_prevTicks;
+ static float g_time;
+
+ int ticks = GetTickCount();
+ // limit delta so that float time doesn't overflow
+ if (g_prevTicks == 0)
+ g_prevTicks = ticks;
+
+ g_time += ( (ticks - g_prevTicks) / 1000.0f ) * g_viewerSettings.speedScale;
+ g_prevTicks = ticks;
+
+ return g_time;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Keeps a global clock for "realtime" overlays to run from
+//-----------------------------------------------------------------------------
+float GetRealtimeTime( void )
+{
+ // renamed static's so debugger doesn't get confused and show the wrong one
+ static int g_prevTicksRT;
+ static float g_timeRT;
+
+ int ticks = GetTickCount();
+ // limit delta so that float time doesn't overflow
+ if (g_prevTicksRT == 0)
+ g_prevTicksRT = ticks;
+
+ g_timeRT += ( (ticks - g_prevTicksRT) / 1000.0f );
+ g_prevTicksRT = ticks;
+
+ return g_timeRT;
+}
+
+void StudioModel::AdvanceFrame( float dt )
+{
+ if (dt > 0.1)
+ dt = 0.1f;
+
+ m_dt = dt;
+
+ float t = GetDuration( );
+
+ if (t > 0)
+ {
+ if (dt > 0)
+ {
+ m_cycle += dt / t;
+ m_sequencetime += dt;
+
+ // wrap
+ m_cycle -= (int)(m_cycle);
+ }
+ }
+ else
+ {
+ m_cycle = 0;
+ }
+
+
+ for (int i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ t = GetDuration( m_Layer[i].m_sequence );
+ if (t > 0)
+ {
+ if (dt > 0)
+ {
+ m_Layer[i].m_cycle += (dt / t) * m_Layer[i].m_playbackrate;
+ m_Layer[i].m_cycle -= (int)(m_Layer[i].m_cycle);
+ }
+ }
+ else
+ {
+ m_Layer[i].m_cycle = 0;
+ }
+ }
+}
+
+float StudioModel::GetInterval( void )
+{
+ return m_dt;
+}
+
+float StudioModel::GetCycle( void )
+{
+ return m_cycle;
+}
+
+float StudioModel::GetFrame( void )
+{
+ return GetCycle() * GetMaxFrame();
+}
+
+int StudioModel::GetMaxFrame( void )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return Studio_MaxFrame( pStudioHdr, m_sequence, m_poseparameter );
+}
+
+int StudioModel::SetFrame( int frame )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( frame <= 0 )
+ frame = 0;
+
+ int maxFrame = GetMaxFrame();
+ if ( frame >= maxFrame )
+ {
+ frame = maxFrame;
+ m_cycle = 0.99999;
+ return frame;
+ }
+
+ m_cycle = frame / (float)maxFrame;
+ return frame;
+}
+
+
+float StudioModel::GetCycle( int iLayer )
+{
+ if (iLayer == 0)
+ {
+ return m_cycle;
+ }
+ else if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ return m_Layer[index].m_cycle;
+ }
+ return 0;
+}
+
+
+float StudioModel::GetFrame( int iLayer )
+{
+ return GetCycle( iLayer ) * GetMaxFrame( iLayer );
+}
+
+
+int StudioModel::GetMaxFrame( int iLayer )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( pStudioHdr )
+ {
+ if (iLayer == 0)
+ return Studio_MaxFrame( pStudioHdr, m_sequence, m_poseparameter );
+
+ if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ return Studio_MaxFrame( pStudioHdr, m_Layer[index].m_sequence, m_poseparameter );
+ }
+ }
+
+ return 0;
+}
+
+
+int StudioModel::SetFrame( int iLayer, int frame )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( frame <= 0 )
+ frame = 0;
+
+ int maxFrame = GetMaxFrame( iLayer );
+ float cycle = 0;
+ if (maxFrame)
+ {
+ if ( frame >= maxFrame )
+ {
+ frame = maxFrame;
+ cycle = 0.99999;
+ }
+ cycle = frame / (float)maxFrame;
+ }
+
+ if (iLayer == 0)
+ {
+ m_cycle = cycle;
+ }
+ else if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ m_Layer[index].m_cycle = cycle;
+ }
+
+ return frame;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Maps from local axis (X,Y,Z) to Half-Life (PITCH,YAW,ROLL) axis/rotation mappings
+//-----------------------------------------------------------------------------
+static int RemapAxis( int axis )
+{
+ switch( axis )
+ {
+ case 0:
+ return 2;
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ }
+
+ return 0;
+}
+
+void StudioModel::Physics_SetPreview( int previewBone, int axis, float t )
+{
+ m_physPreviewBone = previewBone;
+ m_physPreviewAxis = axis;
+ m_physPreviewParam = t;
+}
+
+
+void StudioModel::OverrideBones( bool *override )
+{
+ matrix3x4_t basematrix;
+ matrix3x4_t bonematrix;
+
+ QAngle tmp;
+ // offset for the base pose to world transform of 90 degrees around up axis
+ tmp[0] = 0; tmp[1] = 90; tmp[2] = 0;
+ AngleMatrix( tmp, bonematrix );
+ ConcatTransforms( g_viewtransform, bonematrix, basematrix );
+
+ for ( int i = 0; i < m_pPhysics->Count(); i++ )
+ {
+ CPhysmesh *pmesh = m_pPhysics->GetMesh( i );
+ // BUGBUG: Cache this if you care about performance!
+ int boneIndex = FindBone(pmesh->m_boneName);
+
+ // bone is not constrained, don't override rotations
+ if ( pmesh->m_constraint.parentIndex == 0 && pmesh->m_constraint.childIndex == 0 )
+ {
+ boneIndex = -1;
+ }
+
+ if ( boneIndex >= 0 )
+ {
+ matrix3x4_t *parentMatrix = &basematrix;
+ override[boneIndex] = true;
+ int parentBone = -1;
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ parentBone = FindBone( m_pPhysics->GetMesh(pmesh->m_constraint.parentIndex)->m_boneName );
+ }
+ if ( parentBone >= 0 )
+ {
+ parentMatrix = &m_pBoneToWorld[ parentBone ];
+ }
+
+ if ( m_physPreviewBone == i )
+ {
+ matrix3x4_t tmpmatrix;
+ QAngle rot;
+ constraint_axislimit_t *axis = pmesh->m_constraint.axes + m_physPreviewAxis;
+
+ int hlAxis = RemapAxis( m_physPreviewAxis );
+ rot.Init();
+ rot[hlAxis] = axis->minRotation + (axis->maxRotation - axis->minRotation) * m_physPreviewParam;
+ AngleMatrix( rot, tmpmatrix );
+ ConcatTransforms( pmesh->m_matrix, tmpmatrix, bonematrix );
+ }
+ else
+ {
+ MatrixCopy( pmesh->m_matrix, bonematrix );
+ }
+
+ ConcatTransforms( *parentMatrix, bonematrix, m_pBoneToWorld[ boneIndex ] );
+ }
+ }
+}
+
+
+int StudioModel::BoneMask( void )
+{
+ int lod = g_viewerSettings.autoLOD ? 0 : g_viewerSettings.lod;
+
+ int mask = BONE_USED_BY_VERTEX_AT_LOD(lod);
+ if (g_viewerSettings.showAttachments || g_viewerSettings.m_iEditAttachment != -1 || m_nSolveHeadTurn != 0 || LookupAttachment( "eyes" ) != -1)
+ {
+ mask |= BONE_USED_BY_ATTACHMENT;
+ }
+
+ if (g_viewerSettings.showHitBoxes)
+ {
+ mask |= BONE_USED_BY_HITBOX;
+ }
+
+ mask |= BONE_USED_BY_BONE_MERGE;
+
+ return mask;
+ // return BONE_USED_BY_ANYTHING_AT_LOD( lod );
+
+ // return BONE_USED_BY_ANYTHING;
+}
+
+void StudioModel::SetUpBones( bool mergeBones )
+{
+ int i, j;
+
+ mstudiobone_t *pbones;
+
+ static Vector pos[MAXSTUDIOBONES];
+ matrix3x4_t bonematrix;
+ static Quaternion q[MAXSTUDIOBONES];
+ bool override[MAXSTUDIOBONES];
+
+ static matrix3x4_t boneCache[MAXSTUDIOBONES];
+
+ // For blended transitions
+ static Vector pos2[MAXSTUDIOBONES];
+ static Quaternion q2[MAXSTUDIOBONES];
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( m_sequence );
+
+ QAngle a1;
+ Vector p1;
+ MatrixAngles( g_viewtransform, a1, p1 );
+ CIKContext *pIK = NULL;
+ m_ik.Init( pStudioHdr, a1, p1, GetRealtimeTime(), m_iFramecounter, BoneMask( ) );
+ if ( g_viewerSettings.enableIK )
+ {
+ pIK = &m_ik;
+ }
+
+ IBoneSetup boneSetup( pStudioHdr, BoneMask(), m_poseparameter );
+
+ boneSetup.InitPose( pos, q );
+
+ boneSetup.AccumulatePose( pos, q, m_sequence, m_cycle, 1.0, GetRealtimeTime(), pIK );
+
+ if ( g_viewerSettings.blendSequenceChanges &&
+ m_sequencetime < m_blendtime &&
+ m_prevsequence != m_sequence &&
+ m_prevsequence < pStudioHdr->GetNumSeq() &&
+ !(seqdesc.flags & STUDIO_SNAP) )
+ {
+ // Make sure frame is valid
+ if ( m_prevcycle >= 1.0 )
+ {
+ m_prevcycle = 0.0f;
+ }
+
+ float s = 1.0 - ( m_sequencetime / m_blendtime );
+ s = 3 * s * s - 2 * s * s * s;
+
+ boneSetup.AccumulatePose( pos, q, m_prevsequence, m_prevcycle, s, GetRealtimeTime(), NULL );
+ // Con_DPrintf("%d %f : %d %f : %f\n", pev->sequence, f, pev->prevsequence, pev->prevframe, s );
+ }
+ else
+ {
+ m_prevcycle = m_cycle;
+ }
+
+ int iMaxPriority = 0;
+ for (i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ if (m_Layer[i].m_weight > 0)
+ {
+ iMaxPriority = max( m_Layer[i].m_priority, iMaxPriority );
+ }
+ }
+
+ for (j = 0; j <= iMaxPriority; j++)
+ {
+ for (i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ if (m_Layer[i].m_priority == j && m_Layer[i].m_weight > 0)
+ {
+ boneSetup.AccumulatePose( pos, q, m_Layer[i].m_sequence, m_Layer[i].m_cycle, m_Layer[i].m_weight, GetRealtimeTime(), pIK );
+ }
+ }
+ }
+
+ if (m_nSolveHeadTurn != 0)
+ {
+ GetBodyPoseParametersFromFlex( );
+ }
+
+ CalcHeadRotation( pos, q );
+
+ CIKContext auto_ik;
+ auto_ik.Init( pStudioHdr, a1, p1, 0.0, 0, BoneMask( ) );
+ boneSetup.CalcAutoplaySequences( pos, q, GetAutoPlayTime(), &auto_ik );
+
+ boneSetup.CalcBoneAdj( pos, q, m_controller );
+
+ CBoneBitList boneComputed;
+ if (pIK)
+ {
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ GetMovement( m_prevIKCycles, deltaPos, deltaAngles );
+
+ Vector tmp;
+ VectorRotate( deltaPos, g_viewtransform, tmp );
+ deltaPos = tmp;
+
+ pIK->UpdateTargets( pos, q, m_pBoneToWorld, boneComputed );
+
+ // FIXME: check number of slots?
+ for (int i = 0; i < pIK->m_target.Count(); i++)
+ {
+ trace_t tr;
+ CIKTarget *pTarget = &pIK->m_target[i];
+
+ switch( pTarget->type )
+ {
+ case IK_GROUND:
+ {
+ // drawLine( pTarget->est.pos, pTarget->est.pos + pTarget->offset.pos, 0, 255, 0 );
+
+ // hack in movement
+ pTarget->est.pos -= deltaPos;
+
+ matrix3x4_t invViewTransform;
+ MatrixInvert( g_viewtransform, invViewTransform );
+ Vector tmp;
+ VectorTransform( pTarget->est.pos, invViewTransform, tmp );
+ tmp.z = pTarget->est.floor;
+ VectorTransform( tmp, g_viewtransform, pTarget->est.pos );
+ Vector p1;
+ Quaternion q1;
+ MatrixAngles( g_viewtransform, q1, p1 );
+ pTarget->est.q = q1;
+
+ float color[4] = { 0, 0, 0, 0 };
+ float wirecolor[4] = { 1, 1, 0, 1 };
+ if (pTarget->est.latched > 0.0)
+ {
+ wirecolor[1] = 1.0 - pTarget->est.flWeight;
+ }
+ else
+ {
+ wirecolor[0] = 1.0 - pTarget->est.flWeight;
+ }
+
+ float r = max(pTarget->est.radius,1.f);
+ Vector p0 = tmp + Vector( -r, -r, 0 );
+ Vector p2 = tmp + Vector( r, r, 0 );
+ drawTransparentBox( p0, p2, g_viewtransform, color, wirecolor );
+
+
+ if (!g_viewerSettings.enableTargetIK)
+ {
+ pTarget->est.flWeight = 0.0;
+ }
+ }
+ break;
+ case IK_ATTACHMENT:
+ {
+ matrix3x4_t m;
+
+ QuaternionMatrix( pTarget->est.q, pTarget->est.pos, m );
+
+ drawTransform( m, 4 );
+ }
+ break;
+ }
+
+ // drawLine( pTarget->est.pos, pTarget->latched.pos, 255, 0, 0 );
+ }
+
+ pIK->SolveDependencies( pos, q, m_pBoneToWorld, boneComputed );
+ }
+
+ pbones = pStudioHdr->pBone( 0 );
+
+ memset( override, 0, sizeof(bool)*pStudioHdr->numbones() );
+
+ if ( g_viewerSettings.showPhysicsPreview )
+ {
+ OverrideBones( override );
+ }
+
+ for (i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if ( !(pStudioHdr->pBone( i )->flags & BoneMask()))
+ {
+ int j, k;
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ m_pBoneToWorld[i][j][k] = VEC_T_NAN;
+ }
+ }
+ continue;
+ }
+
+ if ( override[i] )
+ {
+ continue;
+ }
+ else if (boneComputed.IsBoneMarked(i))
+ {
+ // already calculated
+ }
+ else if (CalcProceduralBone( pStudioHdr, i, CBoneAccessor( m_pBoneToWorld ) ))
+ {
+ continue;
+ }
+ else
+ {
+ QuaternionMatrix( q[i], bonematrix );
+
+ bonematrix[0][3] = pos[i][0];
+ bonematrix[1][3] = pos[i][1];
+ bonematrix[2][3] = pos[i][2];
+
+ if ( (pStudioHdr->pBone( 0 )[i].flags & BONE_ALWAYS_PROCEDURAL) &&
+ (pStudioHdr->pBone( 0 )[i].proctype & STUDIO_PROC_JIGGLE) )
+ {
+ //
+ // Physics-based "jiggle" bone
+ // Bone is assumed to be along the Z axis
+ // Pitch around X, yaw around Y
+ //
+
+ // compute desired bone orientation
+ matrix3x4_t goalMX;
+
+ if (pbones[i].parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, goalMX );
+ }
+ else
+ {
+ ConcatTransforms( m_pBoneToWorld[ pbones[i].parent ], bonematrix, goalMX );
+ }
+
+ // get jiggle properties from QC data
+ mstudiojigglebone_t *jiggleInfo = (mstudiojigglebone_t *)pStudioHdr->pBone( 0 )[i].pProcedure( );
+
+ if (!m_pJiggleBones)
+ {
+ m_pJiggleBones = new CJiggleBones;
+ }
+
+ // do jiggle physics
+ m_pJiggleBones->BuildJiggleTransformations( i, GetRealtimeTime(), jiggleInfo, goalMX, m_pBoneToWorld[ i ] );
+ }
+ else if (pbones[i].parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, m_pBoneToWorld[ i ] );
+ // MatrixCopy(bonematrix, g_bonetoworld[i]);
+ }
+ else
+ {
+ ConcatTransforms( m_pBoneToWorld[ pbones[i].parent ], bonematrix, m_pBoneToWorld[ i ] );
+ }
+ }
+
+ if (!mergeBones)
+ {
+ g_pCacheHdr = pStudioHdr;
+ MatrixCopy( m_pBoneToWorld[ i ], boneCache[i] );
+ }
+ else if (g_pCacheHdr)
+ {
+ for (j = 0; j < g_pCacheHdr->numbones(); j++)
+ {
+ if ( Q_stricmp( pStudioHdr->pBone( i )->pszName(), g_pCacheHdr->pBone( j )->pszName() ) == 0 )
+ break;
+ }
+ if (j < g_pCacheHdr->numbones())
+ {
+ MatrixCopy( boneCache[j], m_pBoneToWorld[ i ] );
+ }
+ }
+ }
+
+ if ( mergeBones )
+ {
+ Studio_RunBoneFlexDrivers( m_flexweight, pStudioHdr, pos, m_pBoneToWorld, g_mCachedViewTransform );
+ }
+ else
+ {
+ MatrixCopy( g_viewtransform, g_mCachedViewTransform );
+ Studio_RunBoneFlexDrivers( m_flexweight, pStudioHdr, pos, m_pBoneToWorld, g_viewtransform );
+ }
+
+ if (g_viewerSettings.showAttachments)
+ {
+ // drawTransform( m_pBoneToWorld[ 0 ] );
+ }
+}
+
+
+
+/*
+================
+StudioModel::SetupLighting
+ set some global variables based on entity position
+inputs:
+outputs:
+================
+*/
+void StudioModel::SetupLighting ( )
+{
+ LightDesc_t light[2];
+
+ light[0].m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ light[0].m_Attenuation0 = 1.0f;
+ light[0].m_Attenuation1 = 0.0;
+ light[0].m_Attenuation2 = 0.0;
+ light[0].m_Color[0] = g_viewerSettings.lColor[0];
+ light[0].m_Color[1] = g_viewerSettings.lColor[1];
+ light[0].m_Color[2] = g_viewerSettings.lColor[2];
+ light[0].m_Range = 2000;
+
+ // DEBUG: Spin the light around the head for debugging
+// g_viewerSettings.lightrot = QAngle( 0, 0, 0 );
+// g_viewerSettings.lightrot.y = fmod( (90 * GetTickCount( ) / 1000.0), 360.0);
+
+ AngleVectors( g_viewerSettings.lightrot, &light[0].m_Direction, NULL, NULL );
+ g_pStudioRender->SetLocalLights( 1, light );
+
+#if 0
+ light[1].m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ light[1].m_Attenuation0 = 1.0f;
+ light[1].m_Attenuation1 = 0.0;
+ light[1].m_Attenuation2 = 0.0;
+ light[1].m_Range = 2000;
+ light[1].m_Color[0] = g_viewerSettings.lColor[2];
+ light[1].m_Color[1] = g_viewerSettings.lColor[1];
+ light[1].m_Color[2] = g_viewerSettings.lColor[0];
+ light[1].m_Direction.x = -light[0].m_Direction.y;
+ light[1].m_Direction.y = light[0].m_Direction.x;
+ light[1].m_Direction.z = light[0].m_Direction.z;
+ g_pStudioRender->SetLocalLights( 2, light );
+#endif
+
+ int i;
+ for( i = 0; i < g_pStudioRender->GetNumAmbientLightSamples(); i++ )
+ {
+ m_AmbientLightColors[i][0] = g_viewerSettings.aColor[0];
+ m_AmbientLightColors[i][1] = g_viewerSettings.aColor[1];
+ m_AmbientLightColors[i][2] = g_viewerSettings.aColor[2];
+ }
+
+ //m_AmbientLightColors[0][0] = 1.0;
+ //m_AmbientLightColors[0][1] = 1.0;
+ //m_AmbientLightColors[0][2] = 1.0;
+
+ g_pStudioRender->SetAmbientLightColors( m_AmbientLightColors );
+}
+
+
+int FindBoneIndex( CStudioHdr *pstudiohdr, const char *pName )
+{
+ mstudiobone_t *pbones = pstudiohdr->pBone( 0 );
+ for (int i = 0; i < pstudiohdr->numbones(); i++)
+ {
+ if ( !strcmpi( pName, pbones[i].pszName() ) )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the named bone index, -1 if not found
+// Input : *pName - bone name
+//-----------------------------------------------------------------------------
+int StudioModel::FindBone( const char *pName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return FindBoneIndex( pStudioHdr, pName );
+}
+
+
+int StudioModel::Physics_GetBoneIndex( const char *pName )
+{
+ for (int i = 0; i < m_pPhysics->Count(); i++)
+ {
+ CPhysmesh *pmesh = m_pPhysics->GetMesh(i);
+ if ( !strcmpi( pName, pmesh[i].m_boneName ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*
+=================
+StudioModel::SetupModel
+ based on the body part, figure out which mesh it should be using.
+inputs:
+ currententity
+outputs:
+ pstudiomesh
+ pmdl
+=================
+*/
+
+void StudioModel::SetupModel ( int bodypart )
+{
+ int index;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (bodypart > pStudioHdr->numbodyparts())
+ {
+ // Con_DPrintf ("StudioModel::SetupModel: no such bodypart %d\n", bodypart);
+ bodypart = 0;
+ }
+
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( bodypart );
+
+ index = m_bodynum / pbodypart->base;
+ index = index % pbodypart->nummodels;
+
+ m_pmodel = pbodypart->pModel( index );
+
+ if(first){
+ maxNumVertices = m_pmodel->numvertices;
+ first = 0;
+ }
+}
+
+
+static IMaterial *g_pAlpha;
+
+
+//-----------------------------------------------------------------------------
+// Draws a box, not wireframed
+//-----------------------------------------------------------------------------
+
+void StudioModel::drawBox (Vector const *v, float const * color )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+
+ // The four sides
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 * 4 );
+ for (int i = 0; i < 10; i++)
+ {
+ meshBuilder.Position3fv (v[i & 7].Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+
+ // top and bottom
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[0].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[4].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[2].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[3].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[5].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Draws a wireframed box
+//-----------------------------------------------------------------------------
+
+void StudioModel::drawWireframeBox (Vector const *v, float const* color )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+
+ // The four sides
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 );
+ for (int i = 0; i < 10; i++)
+ {
+ meshBuilder.Position3fv (v[i & 7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+
+ // top and bottom
+ meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, 4 );
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[0].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[2].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[4].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, 4 );
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[5].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[3].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Draws the position and axies of a transformation matrix, x=red,y=green,z=blue
+//-----------------------------------------------------------------------------
+void StudioModel::drawTransform( matrix3x4_t& m, float flLength )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ for (int k = 0; k < 3; k++)
+ {
+ static unsigned char color[3][3] =
+ {
+ { 255, 0, 0 },
+ { 0, 255, 0 },
+ { 0, 0, 255 }
+ };
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ meshBuilder.Color3ubv( color[k] );
+ meshBuilder.Position3f( m[0][3], m[1][3], m[2][3]);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3ubv( color[k] );
+ meshBuilder.Position3f( m[0][3] + m[0][k] * flLength, m[1][3] + m[1][k] * flLength, m[2][3] + m[2][k] * flLength);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void drawLine( Vector const &p1, Vector const &p2, int r, int g, int b, bool noDepthTest, float duration )
+{
+ g_pStudioModel->drawLine( p1, p2, r, g, b );
+}
+
+
+void StudioModel::drawLine( Vector const &p1, Vector const &p2, int r, int g, int b )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialVertexColor );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ meshBuilder.Color3ub( r, g, b );
+ meshBuilder.Position3f( p1.x, p1.y, p1.z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3ub( r, g, b );
+ meshBuilder.Position3f( p2.x, p2.y, p2.z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a transparent box with a wireframe outline
+//-----------------------------------------------------------------------------
+void StudioModel::drawTransparentBox( Vector const &bbmin, Vector const &bbmax,
+ const matrix3x4_t& m, float const *color, float const *wirecolor )
+{
+ Vector v[8], v2[8];
+
+ v[0][0] = bbmin[0];
+ v[0][1] = bbmax[1];
+ v[0][2] = bbmin[2];
+
+ v[1][0] = bbmin[0];
+ v[1][1] = bbmin[1];
+ v[1][2] = bbmin[2];
+
+ v[2][0] = bbmax[0];
+ v[2][1] = bbmax[1];
+ v[2][2] = bbmin[2];
+
+ v[3][0] = bbmax[0];
+ v[3][1] = bbmin[1];
+ v[3][2] = bbmin[2];
+
+ v[4][0] = bbmax[0];
+ v[4][1] = bbmax[1];
+ v[4][2] = bbmax[2];
+
+ v[5][0] = bbmax[0];
+ v[5][1] = bbmin[1];
+ v[5][2] = bbmax[2];
+
+ v[6][0] = bbmin[0];
+ v[6][1] = bbmax[1];
+ v[6][2] = bbmax[2];
+
+ v[7][0] = bbmin[0];
+ v[7][1] = bbmin[1];
+ v[7][2] = bbmax[2];
+
+ VectorTransform (v[0], m, v2[0]);
+ VectorTransform (v[1], m, v2[1]);
+ VectorTransform (v[2], m, v2[2]);
+ VectorTransform (v[3], m, v2[3]);
+ VectorTransform (v[4], m, v2[4]);
+ VectorTransform (v[5], m, v2[5]);
+ VectorTransform (v[6], m, v2[6]);
+ VectorTransform (v[7], m, v2[7]);
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_pAlpha );
+ drawBox( v2, color );
+
+ pRenderContext->Bind( g_materialBones );
+ drawWireframeBox( v2, wirecolor );
+}
+
+
+
+void StudioModel::UpdateStudioRenderConfig( bool bWireframe, bool bZBufferWireframe, bool bNormals, bool bTangentFrame )
+{
+ StudioRenderConfig_t config;
+ memset( &config, 0, sizeof( config ) );
+ config.fEyeShiftX = 0.0f;
+ config.fEyeShiftY = 0.0f;
+ config.fEyeShiftZ = 0.0f;
+ config.fEyeSize = 0;
+ config.drawEntities = 1;
+ config.skin = 0;
+ config.fullbright = 0;
+ config.bEyeMove = true;
+ config.bWireframe = bWireframe;
+
+ if ( g_viewerSettings.renderMode == RM_WIREFRAME || g_viewerSettings.softwareSkin || config.bWireframe || bNormals || bTangentFrame )
+ {
+ config.bSoftwareSkin = true;
+ }
+ else
+ {
+ config.bSoftwareSkin = false;
+ }
+
+ config.bSoftwareLighting = false;
+ config.bNoHardware = false;
+ config.bNoSoftware = false;
+ config.bTeeth = true;
+ config.bEyes = true;
+ config.bFlex = true;
+ config.bDrawNormals = bNormals;
+ config.bDrawTangentFrame = bTangentFrame;
+ config.bDrawZBufferedWireframe = bZBufferWireframe;
+ config.bShowEnvCubemapOnly = false;
+ g_pStudioRender->UpdateConfig( config );
+
+ MaterialSystem_Config_t matSysConfig = g_pMaterialSystem->GetCurrentConfigForVideoCard();
+ extern void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig);
+ InitMaterialSystemConfig( &matSysConfig );
+ matSysConfig.nFullbright = 0;
+ if( g_viewerSettings.renderMode == RM_SMOOTHSHADED )
+ {
+ matSysConfig.nFullbright = 2;
+ }
+
+ if ( g_dxlevel != 0 )
+ {
+ matSysConfig.dxSupportLevel = g_dxlevel;
+ }
+ g_pMaterialSystem->OverrideConfig( matSysConfig, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the skeleton
+//-----------------------------------------------------------------------------
+void StudioModel::DrawBones( )
+{
+ // draw bones
+ if (!g_viewerSettings.showBones && (g_viewerSettings.highlightBone < 0))
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ bool drawRed = (g_viewerSettings.highlightBone >= 0);
+
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if ( !(pStudioHdr->pBone( i )->flags & BoneMask()))
+ continue;
+
+ if ( pbones[i].parent >= 0 )
+ {
+ int j = pbones[i].parent;
+ if ( (g_viewerSettings.highlightBone < 0 ) || (j == g_viewerSettings.highlightBone) )
+ {
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ if (drawRed)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( m_pBoneToWorld[j][0][3], m_pBoneToWorld[j][1][3], m_pBoneToWorld[j][2][3]);
+ meshBuilder.AdvanceVertex();
+
+ if (drawRed)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( m_pBoneToWorld[i][0][3], m_pBoneToWorld[i][1][3], m_pBoneToWorld[i][2][3]);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+
+ if (g_viewerSettings.highlightBone >= 0)
+ {
+ if (i != g_viewerSettings.highlightBone)
+ continue;
+ }
+
+ drawTransform( m_pBoneToWorld[i] );
+ }
+
+ // manadatory to access correct verts
+ SetCurrentModel();
+
+ // highlight used vertices with point
+ /*
+ if (g_viewerSettings.highlightBone >= 0)
+ {
+ int k, j, n;
+ for (i = 0; i < pStudioHdr->numbodyparts; i++)
+ {
+ for (j = 0; j < pStudioHdr->pBodypart( i )->nummodels; j++)
+ {
+ mstudiomodel_t *pModel = pStudioHdr->pBodypart( i )->pModel( j );
+
+ const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ meshBuilder.Begin( pMesh, MATERIAL_POINTS, 1 );
+
+ for (k = 0; k < pModel->numvertices; k++)
+ {
+ for (n = 0; n < vertData->BoneWeights( k )->numbones; n++)
+ {
+ if (vertData->BoneWeights( k )->bone[n] == g_viewerSettings.highlightBone)
+ {
+ Vector tmp;
+ Transform( *vertData->Position( k ), vertData->BoneWeights( k ), tmp );
+
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( tmp.x, tmp.y, tmp.z );
+ meshBuilder.AdvanceVertex();
+ break;
+ }
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+ }
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws attachments
+//-----------------------------------------------------------------------------
+void StudioModel::DrawAttachments( )
+{
+ if ( !g_viewerSettings.showAttachments )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++)
+ {
+ mstudioattachment_t &pattachments = (mstudioattachment_t &)pStudioHdr->pAttachment( i );
+
+ matrix3x4_t world;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( i ) ], pattachments.local, world );
+
+ drawTransform( world );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws Axis
+//-----------------------------------------------------------------------------
+void StudioModel::DrawOriginAxis( )
+{
+ if ( !g_viewerSettings.showOriginAxis )
+ return;
+
+ const float fAxisLength = g_viewerSettings.originAxisLength;
+ if ( fAxisLength <= 0.0f )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
+
+ pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( fAxisLength, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, fAxisLength, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, fAxisLength );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PopMatrix();
+}
+
+
+void StudioModel::DrawEditAttachment()
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ int iEditAttachment = g_viewerSettings.m_iEditAttachment;
+ if ( iEditAttachment >= 0 && iEditAttachment < pStudioHdr->GetNumAttachments() )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pStudioHdr->pAttachment( iEditAttachment );
+
+ matrix3x4_t world;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( iEditAttachment ) ], pAttachment.local, world );
+
+ drawTransform( world );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws hitboxes
+//-----------------------------------------------------------------------------
+
+
+static float hullcolor[8][4] =
+{
+ { 1.0, 1.0, 1.0, 1.0 },
+ { 1.0, 0.5, 0.5, 1.0 },
+ { 0.5, 1.0, 0.5, 1.0 },
+ { 1.0, 1.0, 0.5, 1.0 },
+ { 0.5, 0.5, 1.0, 1.0 },
+ { 1.0, 0.5, 1.0, 1.0 },
+ { 0.5, 1.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 }
+};
+
+
+void StudioModel::DrawHitboxes( )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!g_pAlpha)
+ {
+ g_pAlpha = g_pMaterialSystem->FindMaterial("debug/debughitbox", TEXTURE_GROUP_OTHER, false);
+ if ( g_pAlpha )
+ {
+ g_pAlpha->AddRef();
+ }
+ }
+
+ if (g_viewerSettings.showHitBoxes || (g_viewerSettings.highlightHitbox >= 0))
+ {
+ int hitboxset = g_MDLViewer->GetCurrentHitboxSet();
+
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ hitboxset ].m_Hitboxes;
+ for (unsigned short j = list.Head(); j != list.InvalidIndex(); j = list.Next(j) )
+ {
+ // Only draw one hitbox if we've selected it.
+ if ((g_viewerSettings.highlightHitbox >= 0) &&
+ (g_viewerSettings.highlightHitbox != j))
+ continue;
+
+ mstudiobbox_t *pBBox = &list[j].m_BBox;
+
+ float interiorcolor[4];
+ int c = pBBox->group % 8;
+ interiorcolor[0] = hullcolor[c][0] * 0.7;
+ interiorcolor[1] = hullcolor[c][1] * 0.7;
+ interiorcolor[2] = hullcolor[c][2] * 0.7;
+ interiorcolor[3] = hullcolor[c][3] * 0.4;
+
+ drawTransparentBox( pBBox->bbmin, pBBox->bbmax, m_pBoneToWorld[ pBBox->bone ], interiorcolor, hullcolor[ c ] );
+ }
+ }
+
+ /*
+ float color2[] = { 0, 0.7, 1, 0.6 };
+ float wirecolor2[] = { 0, 1, 1, 1.0 };
+ drawTransparentBox( pStudioHdr->min, pStudioHdr->max, g_viewtransform, color2, wirecolor2 );
+ */
+
+ if (g_viewerSettings.showSequenceBoxes)
+ {
+ float color[] = { 0.7, 1, 0, 0.6 };
+ float wirecolor[] = { 1, 1, 0, 1.0 };
+
+ drawTransparentBox( pStudioHdr->pSeqdesc( m_sequence ).bbmin, pStudioHdr->pSeqdesc( m_sequence ).bbmax, g_viewtransform, color, wirecolor );
+ }
+}
+
+void StudioModel::DrawIllumPosition( )
+{
+ if( !g_viewerSettings.showIllumPosition )
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+
+ Vector modelPt0;
+ Vector modelPt1;
+ Vector worldPt0;
+ Vector worldPt1;
+
+ // draw axis through illum position
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.x -= 4;
+ modelPt1.x += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 255, 0, 0 );
+
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.y -= 4;
+ modelPt1.y += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 0, 255, 0 );
+
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.z -= 4;
+ modelPt1.z += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 0, 0, 255 );
+
+}
+
+//-----------------------------------------------------------------------------
+// Draws the physics model
+//-----------------------------------------------------------------------------
+
+void StudioModel::DrawPhysicsModel( )
+{
+ if (!g_viewerSettings.showPhysicsModel)
+ return;
+
+ if ( g_viewerSettings.renderMode == RM_WIREFRAME && m_pPhysics->Count() == 1 )
+ {
+ // show the convex pieces in solid
+ DrawPhysConvex( m_pPhysics->GetMesh(0), g_materialFlatshaded );
+ }
+ else
+ {
+ for (int i = 0; i < m_pPhysics->Count(); i++)
+ {
+ float red[] = { 1.0, 0, 0, 0.25 };
+ float yellow[] = { 1.0, 1.0, 0, 0.5 };
+
+ CPhysmesh *pmesh = m_pPhysics->GetMesh(i);
+ int boneIndex = FindBone(pmesh->m_boneName);
+
+ if ( boneIndex >= 0 )
+ {
+ if ( (i+1) == g_viewerSettings.highlightPhysicsBone )
+ {
+ DrawPhysmesh( pmesh, boneIndex, g_materialBones, red );
+ }
+ else
+ {
+ if ( g_viewerSettings.highlightPhysicsBone < 1 )
+ {
+ // yellow for most
+ DrawPhysmesh( pmesh, boneIndex, g_materialBones, yellow );
+ }
+ }
+ }
+ else
+ {
+ DrawPhysmesh( pmesh, -1, g_materialBones, red );
+ }
+ }
+ }
+}
+
+
+
+
+void StudioModel::SetViewTarget( void )
+{
+ // only valid if the attachment bones are used
+ if ((BoneMask() & BONE_USED_BY_ATTACHMENT) == 0)
+ {
+ return;
+ }
+
+ int iEyeAttachment = LookupAttachment( "eyes" );
+
+ if (iEyeAttachment == -1)
+ return;
+
+ Vector local;
+ Vector tmp;
+
+ // look forward
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioattachment_t &patt = (mstudioattachment_t &)pStudioHdr->pAttachment( iEyeAttachment );
+ matrix3x4_t attToWorld;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( iEyeAttachment ) ], patt.local, attToWorld );
+ local = Vector( 32, 0, 0 );
+ Vector vEyes;
+ MatrixPosition( attToWorld, vEyes );
+
+ // aim the eyes if there's a target
+ if (m_vecHeadTargets.Count() > 0 && !m_vecHeadTargets.Tail().m_bSelf)
+ {
+ VectorITransform( m_vecHeadTargets.Tail().m_vecPosition - vEyes, attToWorld, local );
+ }
+
+ float flDist = local.Length();
+
+ VectorNormalize( local );
+
+ // calculate animated eye deflection
+ Vector eyeDeflect;
+ QAngle eyeAng( GetFlexController("eyes_updown"), GetFlexController("eyes_rightleft"), 0 );
+
+ // debugoverlay->AddTextOverlay( m_vecOrigin + Vector( 0, 0, 64 ), 0, 0, "%.2f %.2f", eyeAng.x, eyeAng.y );
+
+ AngleVectors( eyeAng, &eyeDeflect );
+ eyeDeflect.x = 0;
+
+ // reduce deflection the more the eye is off center
+ // FIXME: this angles make no damn sense
+ eyeDeflect = eyeDeflect * (local.x * local.x);
+ local = local + eyeDeflect;
+ VectorNormalize( local );
+
+ // check to see if the eye is aiming outside the max eye deflection
+ float flMaxEyeDeflection = pStudioHdr->MaxEyeDeflection();
+ if ( local.x < flMaxEyeDeflection )
+ {
+ // if so, clamp it to 30 degrees offset
+ // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 1, 0, "%5.3f %5.3f %5.3f", local.x, local.y, local.z );
+ local.x = 0;
+ float d = local.LengthSqr();
+ if ( d > 0.0f )
+ {
+ d = sqrtf( ( 1.0f - flMaxEyeDeflection * flMaxEyeDeflection ) / ( local.y*local.y + local.z*local.z ) );
+ local.x = flMaxEyeDeflection;
+ local.y = local.y * d;
+ local.z = local.z * d;
+ }
+ else
+ {
+ local.x = 1.0;
+ }
+ }
+ local = local * flDist;
+ VectorTransform( local, attToWorld, tmp );
+
+ g_pStudioRender->SetEyeViewTarget( pStudioHdr->GetRenderHdr(), m_bodynum, tmp );
+}
+
+
+float UTIL_VecToYaw( const matrix3x4_t& matrix, const Vector &vec )
+{
+ Vector tmp = vec;
+ VectorNormalize( tmp );
+
+ float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
+ float y = matrix[0][1] * tmp.x + matrix[1][1] * tmp.y + matrix[2][1] * tmp.z;
+
+ if (x == 0.0f && y == 0.0f)
+ return 0.0f;
+
+ float yaw = atan2( -y, x );
+
+ yaw = RAD2DEG(yaw);
+
+ if (yaw < 0)
+ yaw += 360;
+
+ return yaw;
+}
+
+
+float UTIL_VecToPitch( const matrix3x4_t& matrix, const Vector &vec )
+{
+ Vector tmp = vec;
+ VectorNormalize( tmp );
+
+ float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
+ float z = matrix[0][2] * tmp.x + matrix[1][2] * tmp.y + matrix[2][2] * tmp.z;
+
+ if (x == 0.0f && z == 0.0f)
+ return 0.0f;
+
+ float pitch = atan2( z, x );
+
+ pitch = RAD2DEG(pitch);
+
+ if (pitch < 0)
+ pitch += 360;
+
+ return pitch;
+}
+
+
+float UTIL_AngleDiff( float destAngle, float srcAngle )
+{
+ float delta;
+
+ delta = destAngle - srcAngle;
+ if ( destAngle > srcAngle )
+ {
+ while ( delta >= 180 )
+ delta -= 360;
+ }
+ else
+ {
+ while ( delta <= -180 )
+ delta += 360;
+ }
+ return delta;
+}
+
+
+void StudioModel::UpdateBoneChain(
+ Vector pos[],
+ Quaternion q[],
+ int iBone,
+ matrix3x4_t *pBoneToWorld )
+{
+ matrix3x4_t bonematrix;
+
+ QuaternionMatrix( q[iBone], pos[iBone], bonematrix );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ int parent = pStudioHdr->pBone( iBone )->parent;
+ if (parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, pBoneToWorld[iBone] );
+ }
+ else
+ {
+ // evil recursive!!!
+ UpdateBoneChain( pos, q, parent, pBoneToWorld );
+ ConcatTransforms( pBoneToWorld[parent], bonematrix, pBoneToWorld[iBone] );
+ }
+}
+
+
+
+
+void StudioModel::GetBodyPoseParametersFromFlex( )
+{
+ float flGoal;
+
+ flGoal = GetFlexController( "move_rightleft" );
+ SetPoseParameter( "body_trans_Y", flGoal );
+
+ flGoal = GetFlexController( "move_forwardback" );
+ SetPoseParameter( "body_trans_X", flGoal );
+
+ flGoal = GetFlexController( "move_updown" );
+ SetPoseParameter( "body_lift", flGoal );
+
+ flGoal = GetFlexController( "body_rightleft" ) + GetBodyYaw();
+ SetPoseParameter( "body_yaw", flGoal );
+
+ flGoal = GetFlexController( "body_updown" );
+ SetPoseParameter( "body_pitch", flGoal );
+
+ flGoal = GetFlexController( "body_tilt" );
+ SetPoseParameter( "body_roll", flGoal );
+
+ flGoal = GetFlexController( "chest_rightleft" ) + GetSpineYaw();
+ SetPoseParameter( "spine_yaw", flGoal );
+
+ flGoal = GetFlexController( "chest_updown" );
+ SetPoseParameter( "spine_pitch", flGoal );
+
+ flGoal = GetFlexController( "chest_tilt" );
+ SetPoseParameter( "spine_roll", flGoal );
+
+ flGoal = GetFlexController( "head_forwardback" );
+ SetPoseParameter( "neck_trans", flGoal );
+
+ flGoal = GetFlexController( "gesture_updown" );
+ SetPoseParameter( "gesture_height", flGoal );
+
+ flGoal = GetFlexController( "gesture_rightleft" );
+ SetPoseParameter( "gesture_width", flGoal );
+}
+
+
+
+
+void StudioModel::CalcHeadRotation( Vector pos[], Quaternion q[] )
+{
+ static Vector pos2[MAXSTUDIOBONES];
+ static Quaternion q2[MAXSTUDIOBONES];
+
+ if (m_nSolveHeadTurn == 0)
+ return;
+
+ if (m_dt == 0.0f)
+ {
+ m_dt = 0.1;
+ }
+
+ // GetAttachment( "eyes", vEyePosition, vEyeAngles );
+ int iForwardAttachment = LookupAttachment( "forward" );
+ if (iForwardAttachment == -1)
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioattachment_t &patt = (mstudioattachment_t &)pStudioHdr->pAttachment( iForwardAttachment );
+
+ matrix3x4_t attToWorld;
+ int iBone = pStudioHdr->GetAttachmentBone( iForwardAttachment );
+ BuildBoneChain( pStudioHdr, g_viewtransform, pos, q, iBone, m_pBoneToWorld );
+ ConcatTransforms( m_pBoneToWorld[ iBone ], patt.local, attToWorld );
+
+ Vector vForward;
+ VectorRotate( Vector( 1, 0, 0 ), attToWorld, vForward );
+
+ float dt = m_dt;
+ if (m_nSolveHeadTurn == 2)
+ {
+ dt = 0.1;
+ }
+
+ Vector vEyes;
+ MatrixPosition( attToWorld, vEyes );
+
+ Vector vHead = vForward;
+ float flHeadInfluence = 0.0;
+ int i;
+ for (i = 0; i < m_vecHeadTargets.Count(); i++)
+ {
+ Vector dir;
+
+ if (m_vecHeadTargets[i].m_bSelf)
+ {
+ dir = vForward;
+ }
+ else
+ {
+ dir = m_vecHeadTargets[i].m_vecPosition - vEyes;
+ }
+ VectorNormalize( dir );
+ float flInterest = m_vecHeadTargets[i].m_flWeight;
+ if (flInterest > 0.0)
+ {
+ if (flHeadInfluence == 0.0)
+ {
+ vHead = dir;
+ flHeadInfluence = flInterest;
+ }
+ else
+ {
+ flHeadInfluence = flHeadInfluence * (1 - flInterest) + flInterest;
+ float w = flInterest / flHeadInfluence;
+ vHead = vHead * (1 - w) + dir * w;
+ }
+ }
+ }
+
+ Vector vTargetDir = Vector( 0, 0, 0 );
+ vTargetDir = vForward * (1.0 - flHeadInfluence) + vHead * flHeadInfluence;
+ VectorNormalize( vTargetDir );
+
+ SetPoseParameter( "head_pitch", 0.0 );
+ SetPoseParameter( "head_yaw", 0.0 );
+ SetPoseParameter( "head_roll", 0.0 );
+ SetHeadPosition( attToWorld, vTargetDir, dt );
+
+ // Msg( "yaw %f pitch %f\n", vEyeAngles.y, vEyeAngles.x );
+}
+
+
+
+float StudioModel::SetHeadPosition( matrix3x4_t& attToWorld, Vector const &vTargetPos, float dt )
+{
+ float flDiff;
+ int iPose;
+ QAngle vEyeAngles;
+ float flMoved = 0.0f;
+ matrix3x4_t targetXform, invAttToWorld;
+ matrix3x4_t headXform;
+
+ // align current "forward direction" to target direction
+ targetXform = attToWorld;
+ Studio_AlignIKMatrix( targetXform, vTargetPos );
+
+ // calc head movement needed
+ MatrixInvert( attToWorld, invAttToWorld );
+ ConcatTransforms( invAttToWorld, targetXform, headXform );
+
+ MatrixAngles( headXform, vEyeAngles );
+
+ // FIXME: add chest compression
+
+ // Msg( "yaw %f pitch %f\n", vEyeAngles.y, vEyeAngles.x );
+
+ float flMin, flMax;
+
+#if 1
+ //--------------------------------------
+ // Set head yaw
+ //--------------------------------------
+ // flDiff = vEyeAngles.y + GetFlexController( "head_rightleft" );
+ iPose = LookupPoseParameter( "head_yaw" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.y + GetFlexController( "head_rightleft" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+#if 1
+ //--------------------------------------
+ // Set head pitch
+ //--------------------------------------
+ iPose = LookupPoseParameter( "head_pitch" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.x + GetFlexController( "head_updown" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+#if 1
+ //--------------------------------------
+ // Set head roll
+ //--------------------------------------
+ iPose = LookupPoseParameter( "head_roll" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.z + GetFlexController( "head_tilt" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+ return flMoved;
+}
+
+
+DrawModelInfo_t g_DrawModelInfo;
+DrawModelResults_t g_DrawModelResults;
+bool g_bDrawModelInfoValid = false;
+
+
+void StudioModel::GetModelTransform( matrix3x4_t &mat )
+{
+ AngleMatrix( m_angles, mat );
+
+ Vector vecModelOrigin;
+ VectorMultiply( m_origin, -1.0f, vecModelOrigin );
+ MatrixSetColumn( vecModelOrigin, 3, mat );
+}
+
+void StudioModel::SetModelTransform( const matrix3x4_t &mat )
+{
+ m_origin.x = -mat.m_flMatVal[0][3];
+ m_origin.y = -mat.m_flMatVal[1][3];
+ m_origin.z = -mat.m_flMatVal[2][3];
+
+ MatrixAngles( mat, m_angles );
+}
+
+
+/*
+================
+StudioModel::DrawModel
+inputs:
+ currententity
+ r_entorigin
+================
+*/
+int StudioModel::DrawModel( bool mergeBones )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ g_smodels_total++; // render data cache cookie
+
+ // JasonM & garymcthack - should really only do this once a frame and at init time.
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+
+ // NOTE: UpdateStudioRenderConfig can delete the studio hdr
+ pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr || pStudioHdr->numbodyparts() == 0)
+ return 0;
+
+ // Construct a transform to apply to the model. The camera is stuck in a fixed position
+ static Vector vecModelOrigin;
+ if ( !mergeBones )
+ {
+ AngleMatrix( m_angles, g_viewtransform );
+ VectorMultiply( m_origin, -1.0f, vecModelOrigin );
+ MatrixSetColumn( vecModelOrigin, 3, g_viewtransform );
+ }
+
+ // These values HAVE to be sent down for LOD to work correctly.
+ Vector viewOrigin, viewRight, viewUp, viewPlaneNormal;
+ g_pStudioRender->SetViewState( vec3_origin, Vector(0, 1, 0), Vector(0, 0, 1), Vector( 1, 0, 0 ) );
+
+ // g_pStudioRender->SetEyeViewTarget( viewOrigin );
+
+ SetUpBones( mergeBones );
+
+ SetupLighting( );
+
+ SetViewTarget( );
+
+
+ extern float g_flexdescweight[MAXSTUDIOFLEXDESC]; // garymcthack
+ extern float g_flexdescweight2[MAXSTUDIOFLEXDESC]; // garymcthack
+
+ int i;
+ for (i = 0; i < pStudioHdr->numflexdesc(); i++)
+ {
+ g_flexdescweight[i] = 0.0;
+ }
+
+ RunFlexRules( );
+
+ float d = 0.8;
+
+ if (m_dt != 0)
+ {
+ d = ExponentialDecay( 0.8, 0.033, m_dt );
+ }
+
+ float *pFlexWeights, *pFlexDelayedWeights;
+ g_pStudioRender->LockFlexWeights( pStudioHdr->numflexdesc(), &pFlexWeights, &pFlexDelayedWeights );
+
+ for (i = 0; i < pStudioHdr->numflexdesc(); i++)
+ {
+ g_flexdescweight2[i] = g_flexdescweight2[i] * d + g_flexdescweight[i] * (1 - d);
+
+ pFlexWeights[i] = g_flexdescweight[i];
+ pFlexDelayedWeights[i] = g_flexdescweight2[i];
+ }
+
+ g_pStudioRender->UnlockFlexWeights( );
+
+
+ // draw
+
+ g_pStudioRender->SetAlphaModulation( 1.0f );
+
+ g_bDrawModelInfoValid = true;
+ memset( &g_DrawModelInfo, 0, sizeof( g_DrawModelInfo ) );
+ g_DrawModelInfo.m_pStudioHdr = (studiohdr_t *)pStudioHdr->GetRenderHdr();
+ g_DrawModelInfo.m_pHardwareData = GetHardwareData();
+ if ( !g_DrawModelInfo.m_pHardwareData )
+ return 0;
+ g_DrawModelInfo.m_Decals = STUDIORENDER_DECAL_INVALID;
+ g_DrawModelInfo.m_Skin = m_skinnum;
+ g_DrawModelInfo.m_Body = m_bodynum;
+ g_DrawModelInfo.m_HitboxSet = g_MDLViewer->GetCurrentHitboxSet();
+ g_DrawModelInfo.m_pClientEntity = NULL;
+ g_DrawModelInfo.m_Lod = g_viewerSettings.autoLOD ? -1 : g_viewerSettings.lod;
+ g_DrawModelInfo.m_pColorMeshes = NULL;
+
+
+ if( g_viewerSettings.renderMode == RM_SHOWBADVERTEXDATA )
+ {
+ DebugDrawModelBadVerts( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin );
+
+ DebugDrawModelWireframe( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin, Vector( 0.2f, 0.2f, 0.2f ) );
+
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+
+#if 0
+ // overlay wireframe
+
+ // Set the state to trigger wireframe rendering
+ UpdateStudioRenderConfig( true, true, false, false );
+
+ // Draw wireframe
+ count = g_pStudioRender->DrawModel( &g_DrawModelResults, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL );
+ m_LodUsed = g_DrawModelResults.m_nLODUsed;
+ m_LodMetric = g_DrawModelResults.m_flLodMetric;
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+#endif
+ }
+ else if( g_viewerSettings.renderMode == RM_BONEWEIGHTS )
+ {
+ g_DrawModelInfo.m_Lod = 0;
+ DebugDrawModelBoneWeights( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin );
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+ }
+ else if( g_viewerSettings.renderMode == RM_TEXCOORDS )
+ {
+ const char *pMatName = "";
+ if ( g_DrawModelInfo.m_pHardwareData->m_pLODs && g_viewerSettings.materialIndex < g_DrawModelInfo.m_pHardwareData->m_pLODs[0].numMaterials )
+ {
+ pMatName = g_DrawModelInfo.m_pHardwareData->m_pLODs[0].ppMaterials[g_viewerSettings.materialIndex]->GetName();
+ }
+ DebugDrawModelTexCoord( g_pStudioRender, pMatName, g_DrawModelInfo, m_pBoneToWorld, g_viewerSettings.width, g_viewerSettings.height );
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+ m_LodUsed = g_DrawModelInfo.m_Lod;
+ }
+ else
+ {
+ // Draw the model normally (may include normal and/or tangent line segments)
+ g_pStudioRender->DrawModel( &g_DrawModelResults, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+ m_LodUsed = g_DrawModelResults.m_nLODUsed;
+ m_LodMetric = g_DrawModelResults.m_flLODMetric;
+
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+
+ // Optionally overlay wireframe...
+ if ( g_viewerSettings.overlayWireframe && !(g_viewerSettings.renderMode == RM_WIREFRAME) )
+ {
+ // Set the state to trigger wireframe rendering
+ UpdateStudioRenderConfig( true, true, false, false );
+
+ // Draw the wireframe over top of the model
+ g_pStudioRender->DrawModel( NULL, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+ }
+ }
+
+ int nCount = g_DrawModelResults.m_ActualTriCount;
+
+ DrawBones();
+ DrawAttachments();
+ DrawOriginAxis();
+ DrawEditAttachment();
+ DrawHitboxes();
+ DrawPhysicsModel();
+ DrawIllumPosition();
+
+ // Only draw the shadow if the ground is also drawn
+ if ( g_viewerSettings.showShadow && g_viewerSettings.showGround )
+ {
+ matrix3x4_t invViewTransform;
+
+ MatrixInvert( g_viewtransform, invViewTransform );
+
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ matrix3x4_t *pMatrix = &m_pBoneToWorld[ i ];
+
+ matrix3x4_t tmp1;
+
+ ConcatTransforms( invViewTransform, *pMatrix, tmp1 );
+ tmp1[2][0] = 0.0;
+ tmp1[2][1] = 0.0;
+ tmp1[2][2] = 0.0;
+ tmp1[2][3] = 0.05;
+ ConcatTransforms( g_viewtransform, tmp1, *pMatrix );
+ }
+ g_DrawModelInfo.m_Lod = GetHardwareData()->m_NumLODs - 1;
+
+ float zero[4] = { 0, 0, 0, 0 };
+ g_pStudioRender->SetColorModulation( zero );
+ g_pStudioRender->ForcedMaterialOverride( g_materialShadow );
+
+ // Turn off any wireframe, normals or tangent frame display for the drop shadow
+ UpdateStudioRenderConfig( false, false, false, false );
+
+ g_pStudioRender->DrawModel( NULL, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+
+ g_pStudioRender->ForcedMaterialOverride( NULL );
+ float one[4] = { 1, 1, 1, 1 };
+ g_pStudioRender->SetColorModulation( one );
+ }
+
+ return nCount;
+}
+
+
+void StudioModel::DrawPhysmesh( CPhysmesh *pMesh, int boneIndex, IMaterial* pMaterial, float* color )
+{
+ matrix3x4_t *pMatrix;
+ if ( boneIndex >= 0 )
+ {
+ pMatrix = &m_pBoneToWorld[ boneIndex ];
+ }
+ else
+ {
+ pMatrix = &g_viewtransform;
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+ IMesh* pMatMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMatMesh, MATERIAL_TRIANGLES, pMesh->m_vertCount/3 );
+
+ int vertIndex = 0;
+ for ( int i = 0; i < pMesh->m_vertCount; i+=3 )
+ {
+ Vector v;
+
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ }
+ meshBuilder.End();
+ pMatMesh->Draw();
+}
+
+
+void RandomColor( float *color, int key )
+{
+ static bool first = true;
+ static colorVec colors[256];
+
+ if ( first )
+ {
+ int r, g, b;
+ first = false;
+ for ( int i = 0; i < 256; i++ )
+ {
+ do
+ {
+ r = rand()&255;
+ g = rand()&255;
+ b = rand()&255;
+ } while ( (r+g+b)<256 );
+ colors[i].r = r;
+ colors[i].g = g;
+ colors[i].b = b;
+ colors[i].a = 255;
+ }
+ }
+
+ int index = key & 255;
+ color[0] = colors[index].r * (1.f / 255.f);
+ color[1] = colors[index].g * (1.f / 255.f);
+ color[2] = colors[index].b * (1.f / 255.f);
+ color[3] = colors[index].a * (1.f / 255.f);
+}
+
+void StudioModel::DrawPhysConvex( CPhysmesh *pMesh, IMaterial* pMaterial )
+{
+ matrix3x4_t &matrix = g_viewtransform;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+
+ for ( int i = 0; i < pMesh->m_pCollisionModel->ConvexCount(); i++ )
+ {
+ float color[4];
+ RandomColor( color, i );
+ IMesh* pMatMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ int triCount = pMesh->m_pCollisionModel->TriangleCount( i );
+ meshBuilder.Begin( pMatMesh, MATERIAL_TRIANGLES, triCount );
+
+ for ( int j = 0; j < triCount; j++ )
+ {
+ Vector objectSpaceVerts[3];
+ pMesh->m_pCollisionModel->GetTriangleVerts( i, j, objectSpaceVerts );
+
+ for ( int k = 0; k < 3; k++ )
+ {
+ Vector v;
+
+ VectorTransform (objectSpaceVerts[k], matrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+ meshBuilder.End();
+ pMatMesh->Draw();
+ }
+}
+
+
+
+/*
+================
+
+================
+*/
+
+
+int StudioModel::GetLodUsed( void )
+{
+ return m_LodUsed;
+}
+
+float StudioModel::GetLodMetric( void )
+{
+ return m_LodMetric;
+}
+
+
+const char *StudioModel::GetKeyValueText( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return Studio_GetKeyValueText( pStudioHdr, iSequence );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : solve -
+//-----------------------------------------------------------------------------
+void StudioModel::SetSolveHeadTurn( int solve )
+{
+ m_nSolveHeadTurn = solve;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int StudioModel::GetSolveHeadTurn() const
+{
+ return m_nSolveHeadTurn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : target -
+//-----------------------------------------------------------------------------
+void StudioModel::ClearLookTargets( void )
+{
+ m_vecHeadTargets.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : target -
+//-----------------------------------------------------------------------------
+void StudioModel::AddLookTarget( const Vector& vecPosition, float flWeight )
+{
+ if (m_vecHeadTargets.Count() > 8)
+ return;
+
+ StudioLookTarget tmp;
+
+ tmp.m_flWeight = flWeight;
+ tmp.m_vecPosition = vecPosition;
+ tmp.m_bSelf = false;
+
+ m_vecHeadTargets.AddToTail( tmp );
+}
+
+
+void StudioModel::AddLookTargetSelf( float flWeight )
+{
+ if (m_vecHeadTargets.Count() > 8)
+ return;
+
+ StudioLookTarget tmp;
+
+ tmp.m_flWeight = flWeight;
+ tmp.m_vecPosition = Vector(0,0,0);
+ tmp.m_bSelf = true;
+
+ m_vecHeadTargets.AddToTail( tmp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+void StudioModel::SetModelYaw( float flYaw )
+{
+ m_flModelYaw = flYaw;
+}
+
+float StudioModel::GetModelYaw( void ) const
+{
+ return m_flModelYaw;
+}
+
+void StudioModel::SetBodyYaw( float flYaw )
+{
+ m_flBodyYaw = flYaw;
+}
+
+float StudioModel::GetBodyYaw( void ) const
+{
+ return m_flBodyYaw;
+}
+
+void StudioModel::SetSpineYaw( float flYaw )
+{
+ m_flSpineYaw = flYaw;
+}
+
+float StudioModel::GetSpineYaw( void ) const
+{
+ return m_flSpineYaw;
+}
+
diff --git a/utils/hlmv/studio_render.h b/utils/hlmv/studio_render.h
new file mode 100644
index 0000000..5e262fb
--- /dev/null
+++ b/utils/hlmv/studio_render.h
@@ -0,0 +1,26 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//===========================================================================//
+
+#ifndef STUDIO_RENDER_H
+#define STUDIO_RENDER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+extern Vector g_viewtarget;
+
+extern Vector g_flexedverts[];
+extern Vector g_flexednorms[];
+extern int g_flexages[];
+
+extern DrawModelInfo_t g_DrawModelInfo;
+extern DrawModelResults_t g_DrawModelResults;
+extern bool g_bDrawModelInfoValid;
+
+
+#endif // STUDIO_RENDER_H
diff --git a/utils/hlmv/studio_utils.cpp b/utils/hlmv/studio_utils.cpp
new file mode 100644
index 0000000..d37f87b
--- /dev/null
+++ b/utils/hlmv/studio_utils.cpp
@@ -0,0 +1,1224 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// updates:
+// 1-4-99 fixed file texture load and file read bug
+
+////////////////////////////////////////////////////////////////////////
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <malloc.h>
+#include "StudioModel.h"
+#include "vphysics/constraints.h"
+#include "physmesh.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "ViewerSettings.h"
+#include "bone_setup.h"
+#include "UtlMemory.h"
+#include "mxtk/mx.h"
+#include "filesystem.h"
+#include "IStudioRender.h"
+#include "materialsystem/IMaterialSystemHardwareConfig.h"
+#include "MDLViewer.h"
+#include "optimize.h"
+
+extern char g_appTitle[];
+Vector *StudioModel::m_AmbientLightColors;
+
+#pragma warning( disable : 4244 ) // double to float
+
+
+static StudioModel g_studioModel;
+static StudioModel *g_pActiveModel;
+
+// Expose it to the rest of the app
+StudioModel *g_pStudioModel = &g_studioModel;
+StudioModel *g_pStudioExtraModel[HLMV_MAX_MERGED_MODELS];
+
+StudioModel::StudioModel()
+{
+ m_MDLHandle = MDLHANDLE_INVALID;
+ ClearLookTargets();
+}
+
+void StudioModel::Init()
+{
+ m_AmbientLightColors = new Vector[g_pStudioRender->GetNumAmbientLightSamples()];
+
+ // JasonM & garymcthack - should really only do this once a frame and at init time.
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+}
+
+void StudioModel::Shutdown( void )
+{
+ g_pStudioModel->FreeModel( false );
+ delete [] m_AmbientLightColors;
+}
+
+void StudioModel::SetCurrentModel()
+{
+ // track the correct model
+ g_pActiveModel = this;
+}
+
+void StudioModel::ReleaseStudioModel()
+{
+ SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel );
+ g_pStudioModel->FreeModel( true );
+}
+
+void StudioModel::RestoreStudioModel()
+{
+ // should view settings be loaded before the model is loaded?
+ if ( g_pStudioModel->LoadModel( g_pStudioModel->m_pModelName ) )
+ {
+ g_pStudioModel->PostLoadModel( g_pStudioModel->m_pModelName );
+ }
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Frees the model data and releases textures from OpenGL.
+//-----------------------------------------------------------------------------
+void StudioModel::FreeModel( bool bReleasing )
+{
+ if ( m_pStudioHdr )
+ {
+ delete m_pStudioHdr;
+ m_pStudioHdr = NULL;
+ }
+
+ if ( m_MDLHandle != MDLHANDLE_INVALID )
+ {
+ g_pMDLCache->Release( m_MDLHandle );
+ m_MDLHandle = MDLHANDLE_INVALID;
+ }
+
+ if ( !bReleasing )
+ {
+ if (m_pModelName)
+ {
+ delete[] m_pModelName;
+ m_pModelName = NULL;
+ }
+ }
+
+ m_SurfaceProps.Purge();
+
+ DestroyPhysics( m_pPhysics );
+ m_pPhysics = NULL;
+}
+
+void *StudioModel::operator new( size_t stAllocateBlock )
+{
+ // call into engine to get memory
+ Assert( stAllocateBlock != 0 );
+ return calloc( 1, stAllocateBlock );
+}
+
+void StudioModel::operator delete( void *pMem )
+{
+#ifdef _DEBUG
+ // set the memory to a known value
+ int size = _msize( pMem );
+ memset( pMem, 0xcd, size );
+#endif
+
+ // get the engine to free the memory
+ free( pMem );
+}
+
+void *StudioModel::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine )
+{
+ // call into engine to get memory
+ Assert( stAllocateBlock != 0 );
+ return calloc( 1, stAllocateBlock );
+}
+
+void StudioModel::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine )
+{
+#ifdef _DEBUG
+ // set the memory to a known value
+ int size = _msize( pMem );
+ memset( pMem, 0xcd, size );
+#endif
+ // get the engine to free the memory
+ free( pMem );
+}
+
+bool StudioModel::LoadModel( const char *pModelName )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ if (!pModelName)
+ return 0;
+
+ // In the case of restore, m_pModelName == modelname
+ if (m_pModelName != pModelName)
+ {
+ // Copy over the model name; we'll need it later...
+ if (m_pModelName)
+ {
+ delete[] m_pModelName;
+ }
+ m_pModelName = new char[Q_strlen(pModelName) + 1];
+ strcpy( m_pModelName, pModelName );
+ }
+
+ m_MDLHandle = g_pMDLCache->FindMDL( pModelName );
+
+ // allocate a pool for a studiohdr cache
+ if (m_pStudioHdr != NULL)
+ {
+ delete m_pStudioHdr;
+ }
+ m_pStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache );
+
+ // manadatory to access correct verts
+ SetCurrentModel();
+
+ m_pPhysics = LoadPhysics( m_MDLHandle );
+
+ // Copy over all of the hitboxes; we may add and remove elements
+ m_HitboxSets.RemoveAll();
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+
+ int i;
+ for ( int s = 0; s < pStudioHdr->numhitboxsets(); s++ )
+ {
+ mstudiohitboxset_t *pSrcSet = pStudioHdr->pHitboxSet( s );
+ if ( !pSrcSet )
+ continue;
+
+ int j = m_HitboxSets.AddToTail();
+ HitboxSet_t &set = m_HitboxSets[j];
+ set.m_Name = pSrcSet->pszName();
+
+ for ( i = 0; i < pSrcSet->numhitboxes; ++i )
+ {
+ mstudiobbox_t *pHit = pSrcSet->pHitbox(i);
+ int nIndex = set.m_Hitboxes.AddToTail( );
+ HitboxInfo_t &hitbox = set.m_Hitboxes[nIndex];
+
+ hitbox.m_Name = pHit->pszHitboxName();
+ hitbox.m_BBox = *pHit;
+
+ // Blat out bbox name index so we don't use it by mistake...
+ hitbox.m_BBox.szhitboxnameindex = 0;
+ }
+ }
+
+ // Copy over all of the surface props; we may change them...
+ for ( i = 0; i < pStudioHdr->numbones(); ++i )
+ {
+ mstudiobone_t* pBone = pStudioHdr->pBone(i);
+
+ CUtlSymbol prop( pBone->pszSurfaceProp() );
+ m_SurfaceProps.AddToTail( prop );
+ }
+
+ m_physPreviewBone = -1;
+
+ bool forceOpaque = (pStudioHdr->flags() & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0;
+ bool vertexLit = false;
+ m_bIsTransparent = false;
+ m_bHasProxy = false;
+
+ studiohwdata_t *pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle );
+ if ( !pHardwareData )
+ {
+ Assert( 0 );
+ return false;
+ }
+
+ for( int lodID = pHardwareData->m_RootLOD; lodID < pHardwareData->m_NumLODs; lodID++ )
+ {
+ studioloddata_t *pLODData = &pHardwareData->m_pLODs[lodID];
+ for ( i = 0; i < pLODData->numMaterials; ++i )
+ {
+ if (pLODData->ppMaterials[i]->IsVertexLit())
+ {
+ vertexLit = true;
+ }
+ if ((!forceOpaque) && pLODData->ppMaterials[i]->IsTranslucent())
+ {
+ m_bIsTransparent = true;
+ //Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pStudioHdr->name );
+ }
+ if (pLODData->ppMaterials[i]->HasProxy())
+ {
+ m_bHasProxy = true;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+
+bool StudioModel::PostLoadModel( const char *modelname )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (pStudioHdr == NULL)
+ return false;
+
+ SetSequence (0);
+ SetController (0, 0.0f);
+ SetController (1, 0.0f);
+ SetController (2, 0.0f);
+ SetController (3, 0.0f);
+ SetBlendTime( DEFAULT_BLEND_TIME );
+ // SetHeadTurn( 1.0f ); // FIXME:!!!
+
+ int n;
+ for (n = 0; n < pStudioHdr->numbodyparts(); n++)
+ {
+ SetBodygroup (n, 0);
+ }
+
+ SetSkin (0);
+
+/*
+ Vector mins, maxs;
+ ExtractBbox (mins, maxs);
+ if (mins[2] < 5.0f)
+ m_origin[2] = -mins[2];
+*/
+ return true;
+}
+
+
+//------------------------------------------------------------------------------
+// Returns true if the model has at least one body part with model data, false if not.
+//------------------------------------------------------------------------------
+bool StudioModel::HasModel()
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ for ( int i = 0; i < pStudioHdr->numbodyparts(); i++ )
+ {
+ if ( pStudioHdr->pBodypart(i)->nummodels )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+////////////////////////////////////////////////////////////////////////
+
+
+int StudioModel::GetSequence( )
+{
+ return m_sequence;
+}
+
+int StudioModel::SetSequence( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if (iSequence < 0)
+ return 0;
+
+ if (iSequence > pStudioHdr->GetNumSeq())
+ return m_sequence;
+
+ m_prevsequence = m_sequence;
+ m_sequence = iSequence;
+ m_cycle = 0;
+ m_sequencetime = 0.0;
+
+ return m_sequence;
+}
+
+const char* StudioModel::GetSequenceName( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return NULL;
+
+ if (iSequence < 0)
+ return NULL;
+
+ if (iSequence > pStudioHdr->GetNumSeq())
+ return NULL;
+
+ return pStudioHdr->pSeqdesc( iSequence ).pszLabel();
+}
+
+void StudioModel::ClearOverlaysSequences( void )
+{
+ ClearAnimationLayers( );
+ memset( m_Layer, 0, sizeof( m_Layer ) );
+}
+
+void StudioModel::ClearAnimationLayers( void )
+{
+ m_iActiveLayers = 0;
+}
+
+int StudioModel::GetNewAnimationLayer( int iPriority )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( m_iActiveLayers >= MAXSTUDIOANIMLAYERS )
+ {
+ Assert( 0 );
+ return MAXSTUDIOANIMLAYERS - 1;
+ }
+
+ m_Layer[m_iActiveLayers].m_priority = iPriority;
+
+ return m_iActiveLayers++;
+}
+
+int StudioModel::SetOverlaySequence( int iLayer, int iSequence, float flWeight )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if (iSequence < 0)
+ return 0;
+
+ if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS)
+ {
+ Assert(0);
+ return 0;
+ }
+
+ if (iSequence > pStudioHdr->GetNumSeq())
+ return m_Layer[iLayer].m_sequence;
+
+ m_Layer[iLayer].m_sequence = iSequence;
+ m_Layer[iLayer].m_weight = flWeight;
+ m_Layer[iLayer].m_playbackrate = 1.0;
+
+ return iSequence;
+}
+
+float StudioModel::SetOverlayRate( int iLayer, float flCycle, float flPlaybackRate )
+{
+ if (iLayer >= 0 && iLayer < MAXSTUDIOANIMLAYERS)
+ {
+ m_Layer[iLayer].m_cycle = flCycle;
+ m_Layer[iLayer].m_playbackrate = flPlaybackRate;
+ }
+ return flCycle;
+}
+
+
+int StudioModel::GetOverlaySequence( int iLayer )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return -1;
+
+ if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS)
+ {
+ Assert(0);
+ return 0;
+ }
+
+ return m_Layer[iLayer].m_sequence;
+}
+
+
+float StudioModel::GetOverlaySequenceWeight( int iLayer )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return -1;
+
+ if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS)
+ {
+ Assert(0);
+ return 0;
+ }
+
+ return m_Layer[iLayer].m_weight;
+}
+
+
+int StudioModel::LookupSequence( const char *szSequence )
+{
+ int i;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return -1;
+
+ for (i = 0; i < pStudioHdr->GetNumSeq(); i++)
+ {
+ if (!stricmp( szSequence, pStudioHdr->pSeqdesc( i ).pszLabel() ))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int StudioModel::LookupActivity( const char *szActivity )
+{
+ int i;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return -1;
+
+ for (i = 0; i < pStudioHdr->GetNumSeq(); i++)
+ {
+ if (!stricmp( szActivity, pStudioHdr->pSeqdesc( i ).pszActivityName() ))
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int StudioModel::SetSequence( const char *szSequence )
+{
+ return SetSequence( LookupSequence( szSequence ) );
+}
+
+void StudioModel::StartBlending( void )
+{
+ // Switch back to old sequence ( this will oscillate between this one and the last one )
+ SetSequence( m_prevsequence );
+}
+
+void StudioModel::SetBlendTime( float blendtime )
+{
+ if ( blendtime > 0.0f )
+ {
+ m_blendtime = blendtime;
+ }
+}
+
+float StudioModel::GetTransitionAmount( void )
+{
+ if ( g_viewerSettings.blendSequenceChanges &&
+ m_sequencetime < m_blendtime && m_prevsequence != m_sequence )
+ {
+ float s;
+ s = ( m_sequencetime / m_blendtime );
+ return s;
+ }
+
+ return 0.0f;
+}
+
+LocalFlexController_t StudioModel::LookupFlexController( char *szName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return LocalFlexController_t(0);
+
+ for (LocalFlexController_t iFlex = LocalFlexController_t(0); iFlex < pStudioHdr->numflexcontrollers(); iFlex++)
+ {
+ if (stricmp( szName, pStudioHdr->pFlexcontroller( iFlex )->pszName() ) == 0)
+ {
+ return iFlex;
+ }
+ }
+ return LocalFlexController_t(-1);
+}
+
+
+void StudioModel::SetFlexController( char *szName, float flValue )
+{
+ SetFlexController( LookupFlexController( szName ), flValue );
+}
+
+void StudioModel::SetFlexController( LocalFlexController_t iFlex, float flValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return;
+
+ if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers())
+ {
+ mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex);
+
+ if (pflex->min != pflex->max)
+ {
+ flValue = (flValue - pflex->min) / (pflex->max - pflex->min);
+ }
+ m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f );
+ }
+}
+
+
+void StudioModel::SetFlexControllerRaw( LocalFlexController_t iFlex, float flValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return;
+
+ if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers())
+ {
+// mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex);
+ m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f );
+ }
+}
+
+float StudioModel::GetFlexController( char *szName )
+{
+ return GetFlexController( LookupFlexController( szName ) );
+}
+
+float StudioModel::GetFlexController( LocalFlexController_t iFlex )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0.0f;
+
+ if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers())
+ {
+ mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex);
+
+ float flValue = m_flexweight[iFlex];
+
+ if (pflex->min != pflex->max)
+ {
+ flValue = flValue * (pflex->max - pflex->min) + pflex->min;
+ }
+ return flValue;
+ }
+ return 0.0;
+}
+
+
+float StudioModel::GetFlexControllerRaw( LocalFlexController_t iFlex )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0.0f;
+
+ if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers())
+ {
+// mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex);
+ return m_flexweight[iFlex];
+ }
+ return 0.0;
+}
+
+int StudioModel::GetNumLODs() const
+{
+ return g_pStudioRender->GetNumLODs( *GetHardwareData() );
+}
+
+float StudioModel::GetLODSwitchValue( int lod ) const
+{
+ return g_pStudioRender->GetLODSwitchValue( *GetHardwareData(), lod );
+}
+
+void StudioModel::SetLODSwitchValue( int lod, float switchValue )
+{
+ g_pStudioRender->SetLODSwitchValue( *GetHardwareData(), lod, switchValue );
+}
+
+void StudioModel::ExtractBbox( Vector &mins, Vector &maxs )
+{
+ studiohdr_t *pStudioHdr = GetStudioRenderHdr();
+ if ( !pStudioHdr )
+ return;
+
+ // look for hull
+ if ( ((Vector)pStudioHdr->hull_min).Length() != 0 )
+ {
+ mins = pStudioHdr->hull_min;
+ maxs = pStudioHdr->hull_max;
+ }
+ // look for view clip
+ else if (((Vector)pStudioHdr->view_bbmin).Length() != 0)
+ {
+ mins = pStudioHdr->view_bbmin;
+ maxs = pStudioHdr->view_bbmax;
+ }
+ else
+ {
+ mstudioseqdesc_t &pseqdesc = pStudioHdr->pSeqdesc( m_sequence );
+
+ mins = pseqdesc.bbmin;
+ maxs = pseqdesc.bbmax;
+ }
+}
+
+
+
+void StudioModel::GetSequenceInfo( int iSequence, float *pflFrameRate, float *pflGroundSpeed )
+{
+ float t = GetDuration( iSequence );
+
+ if (t > 0)
+ {
+ *pflFrameRate = 1.0 / t;
+ }
+ else
+ {
+ *pflFrameRate = 1.0;
+ }
+ *pflGroundSpeed = GetGroundSpeed( iSequence );
+}
+
+void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed )
+{
+ GetSequenceInfo( m_sequence, pflFrameRate, pflGroundSpeed );
+}
+
+float StudioModel::GetFPS( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0.0f;
+
+ return Studio_FPS( pStudioHdr, iSequence, m_poseparameter );
+}
+
+float StudioModel::GetFPS( void )
+{
+ return GetFPS( m_sequence );
+}
+
+float StudioModel::GetDuration( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0.0f;
+
+ return Studio_Duration( pStudioHdr, iSequence, m_poseparameter );
+}
+
+
+int StudioModel::GetNumFrames( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr || iSequence < 0 || iSequence >= pStudioHdr->GetNumSeq() )
+ {
+ return 1;
+ }
+
+ return Studio_MaxFrame( pStudioHdr, iSequence, m_poseparameter );
+}
+
+static int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence )
+{
+ if ( !pstudiohdr ||
+ sequence < 0 ||
+ sequence >= pstudiohdr->GetNumSeq() )
+ {
+ return 0;
+ }
+
+ mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence );
+
+ return seqdesc.flags;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : iSequence -
+// Output : Returns true on success, false on failure.
+//-----------------------------------------------------------------------------
+bool StudioModel::GetSequenceLoops( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return false;
+
+ int flags = GetSequenceFlags( pStudioHdr, iSequence );
+ bool looping = flags & STUDIO_LOOPING ? true : false;
+ return looping;
+}
+
+float StudioModel::GetDuration( )
+{
+ return GetDuration( m_sequence );
+}
+
+
+void StudioModel::GetMovement( float prevcycle[5], Vector &vecPos, QAngle &vecAngles )
+{
+ vecPos.Init();
+ vecAngles.Init();
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return;
+
+ // assume that changes < -0.5 are loops....
+ if (m_cycle - prevcycle[0] < -0.5)
+ {
+ prevcycle[0] = prevcycle[0] - 1.0;
+ }
+
+ Studio_SeqMovement( pStudioHdr, m_sequence, prevcycle[0], m_cycle, m_poseparameter, vecPos, vecAngles );
+ prevcycle[0] = m_cycle;
+
+ int i;
+ for (i = 0; i < 4; i++)
+ {
+ Vector vecTmp;
+ QAngle angTmp;
+
+ if (m_Layer[i].m_cycle - prevcycle[i+1] < -0.5)
+ {
+ prevcycle[i+1] = prevcycle[i+1] - 1.0;
+ }
+
+ if (m_Layer[i].m_weight > 0.0)
+ {
+ vecTmp.Init();
+ angTmp.Init();
+ if (Studio_SeqMovement( pStudioHdr, m_Layer[i].m_sequence, prevcycle[i+1], m_Layer[i].m_cycle, m_poseparameter, vecTmp, angTmp ))
+ {
+ vecPos = vecPos * ( 1.0 - m_Layer[i].m_weight ) + vecTmp * m_Layer[i].m_weight;
+ }
+ }
+ prevcycle[i+1] = m_Layer[i].m_cycle;
+ }
+
+ return;
+}
+
+
+void StudioModel::GetMovement( int iSequence, float prevCycle, float nextCycle, Vector &vecPos, QAngle &vecAngles )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ {
+ vecPos.Init();
+ vecAngles.Init();
+ return;
+ }
+
+ // FIXME: this doesn't consider layers
+ Studio_SeqMovement( pStudioHdr, iSequence, prevCycle, nextCycle, m_poseparameter, vecPos, vecAngles );
+
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ground speed of the specifed sequence.
+//-----------------------------------------------------------------------------
+float StudioModel::GetGroundSpeed( int iSequence )
+{
+ Vector vecMove;
+ QAngle vecAngles;
+ GetMovement( iSequence, 0, 1, vecMove, vecAngles );
+
+ float t = GetDuration( iSequence );
+
+ float flGroundSpeed = 0;
+ if (t > 0)
+ {
+ flGroundSpeed = vecMove.Length() / t;
+ }
+
+ return flGroundSpeed;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ground speed of the current sequence.
+//-----------------------------------------------------------------------------
+float StudioModel::GetGroundSpeed( void )
+{
+ return GetGroundSpeed( m_sequence );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the ground speed of the current sequence.
+//-----------------------------------------------------------------------------
+float StudioModel::GetCurrentVelocity( void )
+{
+ Vector vecVelocity;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (pStudioHdr && Studio_SeqVelocity( pStudioHdr, m_sequence, m_cycle, m_poseparameter, vecVelocity ))
+ {
+ return vecVelocity.Length();
+ }
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns the the sequence should be hidden or not
+//-----------------------------------------------------------------------------
+bool StudioModel::IsHidden( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (pStudioHdr->pSeqdesc( iSequence ).flags & STUDIO_HIDDEN)
+ return true;
+
+ return false;
+}
+
+
+
+void StudioModel::GetSeqAnims( int iSequence, mstudioanimdesc_t *panim[4], float *weight )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return;
+
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence );
+ Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, m_poseparameter, panim, weight );
+}
+
+void StudioModel::GetSeqAnims( mstudioanimdesc_t *panim[4], float *weight )
+{
+ GetSeqAnims( m_sequence, panim, weight );
+}
+
+
+float StudioModel::SetController( int iController, float flValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0.0f;
+
+ return Studio_SetController( pStudioHdr, iController, flValue, m_controller[iController] );
+}
+
+
+
+int StudioModel::LookupPoseParameter( char const *szName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return false;
+
+ for (int iParameter = 0; iParameter < pStudioHdr->GetNumPoseParameters(); iParameter++)
+ {
+ if (stricmp( szName, pStudioHdr->pPoseParameter( iParameter ).pszName() ) == 0)
+ {
+ return iParameter;
+ }
+ }
+ return -1;
+}
+
+float StudioModel::SetPoseParameter( char const *szName, float flValue )
+{
+ return SetPoseParameter( LookupPoseParameter( szName ), flValue );
+}
+
+float StudioModel::SetPoseParameter( int iParameter, float flValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0.0f;
+
+ return Studio_SetPoseParameter( pStudioHdr, iParameter, flValue, m_poseparameter[iParameter] );
+}
+
+float StudioModel::GetPoseParameter( char const *szName )
+{
+ return GetPoseParameter( LookupPoseParameter( szName ) );
+}
+
+float* StudioModel::GetPoseParameters()
+{
+ return m_poseparameter;
+}
+
+float StudioModel::GetPoseParameter( int iParameter )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0.0f;
+
+ return Studio_GetPoseParameter( pStudioHdr, iParameter, m_poseparameter[iParameter] );
+}
+
+bool StudioModel::GetPoseParameterRange( int iParameter, float *pflMin, float *pflMax )
+{
+ *pflMin = 0;
+ *pflMax = 0;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return false;
+
+ if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters())
+ return false;
+
+ const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iParameter );
+
+ *pflMin = Pose.start;
+ *pflMax = Pose.end;
+
+ return true;
+}
+
+int StudioModel::LookupAttachment( char const *szName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return -1;
+
+ for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++)
+ {
+ if (stricmp( pStudioHdr->pAttachment( i ).pszName(), szName ) == 0)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+int StudioModel::SetBodygroup( int iGroup, int iValue /*= -1*/ )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ if (iGroup > pStudioHdr->numbodyparts())
+ return -1;
+
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( iGroup );
+
+ int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels;
+
+ // if the submodel index is not specified or out of range, just use the current value
+ if ( iValue < 0 || iValue >= pbodypart->nummodels )
+ return iCurrent;
+
+ m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base));
+
+ return iValue;
+}
+
+
+int StudioModel::SetSkin( int iValue )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ if (iValue >= pStudioHdr->numskinfamilies())
+ {
+ return m_skinnum;
+ }
+
+ m_skinnum = iValue;
+
+ return iValue;
+}
+
+
+
+void StudioModel::scaleMeshes (float scale)
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return;
+
+ int i, j, k;
+
+ // manadatory to access correct verts
+ SetCurrentModel();
+
+ // scale verts
+ int tmp = m_bodynum;
+ for (i = 0; i < pStudioHdr->numbodyparts(); i++)
+ {
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( i );
+ for (j = 0; j < pbodypart->nummodels; j++)
+ {
+ SetBodygroup (i, j);
+ SetupModel (i);
+
+ const mstudio_modelvertexdata_t *vertData = m_pmodel->GetVertexData();
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ for (k = 0; k < m_pmodel->numvertices; k++)
+ {
+ *vertData->Position(k) *= scale;
+ }
+ }
+ }
+
+ m_bodynum = tmp;
+
+ // scale complex hitboxes
+ int hitboxset = g_MDLViewer->GetCurrentHitboxSet();
+
+ mstudiobbox_t *pbboxes = pStudioHdr->pHitbox( 0, hitboxset );
+ for (i = 0; i < pStudioHdr->iHitboxCount( hitboxset ); i++)
+ {
+ VectorScale (pbboxes[i].bbmin, scale, pbboxes[i].bbmin);
+ VectorScale (pbboxes[i].bbmax, scale, pbboxes[i].bbmax);
+ }
+
+ // scale bounding boxes
+ for (i = 0; i < pStudioHdr->GetNumSeq(); i++)
+ {
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( i );
+ Vector tmp;
+
+ tmp = seqdesc.bbmin;
+ VectorScale( tmp, scale, tmp );
+ seqdesc.bbmin = tmp;
+
+ tmp = seqdesc.bbmax;
+ VectorScale( tmp, scale, tmp );
+ seqdesc.bbmax = tmp;
+
+ }
+
+ // maybe scale exeposition, pivots, attachments
+}
+
+
+
+void StudioModel::scaleBones (float scale)
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return;
+
+ mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ pbones[i].pos *= scale;
+ pbones[i].posscale *= scale;
+ }
+}
+
+int StudioModel::Physics_GetBoneCount( void )
+{
+ return m_pPhysics->Count();
+}
+
+
+const char *StudioModel::Physics_GetBoneName( int index )
+{
+ CPhysmesh *pmesh = m_pPhysics->GetMesh( index );
+
+ if ( !pmesh )
+ return NULL;
+
+ return pmesh->m_boneName;
+}
+
+
+void StudioModel::Physics_GetData( int boneIndex, hlmvsolid_t *psolid, constraint_ragdollparams_t *pConstraint ) const
+{
+ CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex );
+
+ if ( !pMesh )
+ return;
+
+ if ( psolid )
+ {
+ memcpy( psolid, &pMesh->m_solid, sizeof(*psolid) );
+ }
+
+ if ( pConstraint )
+ {
+ *pConstraint = pMesh->m_constraint;
+ }
+}
+
+void StudioModel::Physics_SetData( int boneIndex, const hlmvsolid_t *psolid, const constraint_ragdollparams_t *pConstraint )
+{
+ CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex );
+
+ if ( !pMesh )
+ return;
+
+ if ( psolid )
+ {
+ memcpy( &pMesh->m_solid, psolid, sizeof(*psolid) );
+ }
+
+ if ( pConstraint )
+ {
+ pMesh->m_constraint = *pConstraint;
+ }
+}
+
+
+float StudioModel::Physics_GetMass( void )
+{
+ return m_pPhysics->GetMass();
+}
+
+void StudioModel::Physics_SetMass( float mass )
+{
+ m_physMass = mass;
+}
+
+
+char *StudioModel::Physics_DumpQC( void )
+{
+ return m_pPhysics->DumpQC();
+}
+
+const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData )
+{
+ Assert( pModelData == NULL );
+ Assert( g_pActiveModel );
+
+ return g_pStudioDataCache->CacheVertexData( g_pActiveModel->GetStudioRenderHdr() );
+}
+
+
+//-----------------------------------------------------------------------------
+// FIXME: This trashy glue code is really not acceptable. Figure out a way of making it unnecessary.
+//-----------------------------------------------------------------------------
+const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const
+{
+ MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName );
+ *cache = (void*)handle;
+ return g_pMDLCache->GetStudioHdr( handle );
+}
+
+virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const
+{
+ return g_pMDLCache->GetVirtualModel( (MDLHandle_t)virtualModel );
+}
+
+byte *studiohdr_t::GetAnimBlock( int i ) const
+{
+ return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i );
+}
+
+int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const
+{
+ return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut );
+}
+
+const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache );
+}
diff --git a/utils/hlmv/studiomodel.h b/utils/hlmv/studiomodel.h
new file mode 100644
index 0000000..c431b48
--- /dev/null
+++ b/utils/hlmv/studiomodel.h
@@ -0,0 +1,483 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#ifndef INCLUDED_STUDIOMODEL
+#define INCLUDED_STUDIOMODEL
+
+#include "mathlib/mathlib.h"
+#include "studio.h"
+#include "mouthinfo.h"
+#include "UtlLinkedList.h"
+#include "utlsymbol.h"
+#include "bone_setup.h"
+#include "datacache/imdlcache.h"
+#include "viewersettings.h"
+#include "tier1/utlstring.h"
+
+#define DEFAULT_BLEND_TIME 0.2
+
+//-----------------------------------------------------------------------------
+// Forward declarations
+//-----------------------------------------------------------------------------
+typedef struct IFACE_TAG IFACE;
+typedef struct IMESH_TAG IMESH;
+typedef struct ResolutionUpdateTag ResolutionUpdate;
+typedef struct FaceUpdateTag FaceUpdate;
+class IMaterial;
+class IDataCache;
+class IStudioPhysics;
+class IMaterialSystem;
+class IMDLCache;
+class CPhysmesh;
+struct hlmvsolid_t;
+struct constraint_ragdollparams_t;
+class IStudioRender;
+class IPhysicsSurfaceProps;
+class IPhysicsCollision;
+class IStudioDataCache;
+class IDataCache;
+class IFileSystem;
+class IMaterialSystemHardwareConfig;
+class CJiggleBones;
+
+//-----------------------------------------------------------------------------
+// Singleton interfaces
+//-----------------------------------------------------------------------------
+extern IStudioRender *g_pStudioRender;
+extern IMDLCache *g_pMDLCache;
+extern IPhysicsSurfaceProps *physprop;
+extern IPhysicsCollision *physcollision;
+extern IStudioDataCache *g_pStudioDataCache;
+extern IDataCache *g_pDataCache;
+extern IFileSystem *g_pFileSystem;
+extern IMaterialSystem *g_pMaterialSystem;
+extern IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig;
+
+
+class AnimationLayer
+{
+public:
+ float m_cycle; // 0 to 1 animation playback index
+ int m_sequence; // sequence index
+ float m_weight;
+ float m_playbackrate;
+ int m_priority; // lower priorities get layered first
+};
+
+struct StudioLookTarget
+{
+ float m_flWeight;
+ Vector m_vecPosition;
+ bool m_bSelf;
+};
+
+struct HitboxInfo_t
+{
+ CUtlString m_Name;
+ mstudiobbox_t m_BBox;
+};
+
+// I'm saving this as internal data because we may add or remove hitboxes
+// I'm using a utllinkedlist so hitbox IDs remain constant on add + remove
+typedef CUtlLinkedList< HitboxInfo_t, unsigned short > HitboxList_t;
+
+
+struct HitboxSet_t
+{
+ CUtlString m_Name;
+ HitboxList_t m_Hitboxes;
+};
+
+
+class StudioModel
+{
+public:
+ StudioModel();
+
+ // memory handling, uses calloc so members are zero'd out on instantiation
+ static void *operator new( size_t nSize );
+ static void* operator new( size_t size, int nBlockUse, const char *pFileName, int nLine );
+
+ static void operator delete( void *pData );
+ static void operator delete( void* p, int nBlockUse, const char *pFileName, int nLine );
+
+
+ static void Init( void );
+ static void Shutdown( void ); // garymcthack - need to call this.
+ static void UpdateViewState( const Vector& viewOrigin,
+ const Vector& viewRight,
+ const Vector& viewUp,
+ const Vector& viewPlaneNormal );
+
+ static void ReleaseStudioModel( void );
+ static void RestoreStudioModel( void );
+
+ static void UnloadGroupFiles();
+
+ char const *GetFileName( void );
+
+ IStudioRender *GetStudioRender();
+
+ static void UpdateStudioRenderConfig( bool bWireframe, bool bZBufferWireframe, bool bNormals, bool bTangentFrame );
+ studiohdr_t *getAnimHeader (int i) const;
+
+ virtual void ModelInit( void ) { }
+
+ bool IsModelLoaded() const;
+
+ void FreeModel( bool bReleasing );
+ bool LoadModel( const char *modelname );
+ virtual bool PostLoadModel ( const char *modelname );
+ bool HasModel();
+
+ virtual int DrawModel( bool mergeBones = false );
+
+ virtual void AdvanceFrame( float dt );
+ float GetInterval( void );
+ float GetCycle( void );
+ float GetFrame( void );
+ int GetMaxFrame( void );
+ int SetFrame( int frame );
+ float GetCycle( int iLayer );
+ float GetFrame( int iLayer );
+ int GetMaxFrame( int iLayer );
+ int SetFrame( int iLayer, int frame );
+
+ void ExtractBbox( Vector &mins, Vector &maxs );
+
+ void SetBlendTime( float blendtime );
+ int LookupSequence( const char *szSequence );
+ int LookupActivity( const char *szActivity );
+ int SetSequence( int iSequence );
+ int SetSequence( const char *szSequence );
+ const char* GetSequenceName( int iSequence );
+ void ClearOverlaysSequences( void );
+ void ClearAnimationLayers( void );
+ int GetNewAnimationLayer( int iPriority = 0 );
+
+ int SetOverlaySequence( int iLayer, int iSequence, float flWeight );
+ float SetOverlayRate( int iLayer, float flCycle, float flFrameRate );
+ int GetOverlaySequence( int iLayer );
+ float GetOverlaySequenceWeight( int iLayer );
+ void StartBlending( void );
+
+ float GetTransitionAmount( void );
+ int GetSequence( void );
+ void GetSequenceInfo( int iSequence, float *pflFrameRate, float *pflGroundSpeed );
+ void GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed );
+ float GetFPS( int iSequence );
+ float GetFPS( );
+ float GetDuration( int iSequence );
+ float GetDuration( );
+ int GetNumFrames( int iSequence );
+ bool GetSequenceLoops( int iSequence );
+ void GetMovement( float prevCycle[5], Vector &vecPos, QAngle &vecAngles );
+ void GetMovement( int iSequence, float prevCycle, float currCycle, Vector &vecPos, QAngle &vecAngles );
+ void GetSeqAnims( int iSequence, mstudioanimdesc_t *panim[4], float *pweights );
+ void GetSeqAnims( mstudioanimdesc_t *panim[4], float *pweights );
+ float GetGroundSpeed( int iSequence );
+ float GetGroundSpeed( void );
+ float GetCurrentVelocity( void );
+ bool IsHidden( int iSequence );
+
+ float SetController( int iController, float flValue );
+
+ int LookupPoseParameter( char const *szName );
+ float SetPoseParameter( int iParameter, float flValue );
+ float SetPoseParameter( char const *szName, float flValue );
+ float GetPoseParameter( char const *szName );
+ float GetPoseParameter( int iParameter );
+ bool GetPoseParameterRange( int iParameter, float *pflMin, float *pflMax );
+ float* GetPoseParameters();
+
+ int LookupAttachment( char const *szName );
+
+ int SetBodygroup( int iGroup, int iValue = -1 );
+ int SetSkin( int iValue );
+ int FindBone( const char *pName );
+
+ LocalFlexController_t LookupFlexController( char *szName );
+ void SetFlexController( char *szName, float flValue );
+ void SetFlexController( LocalFlexController_t iFlex, float flValue );
+ float GetFlexController( char *szName );
+ float GetFlexController( LocalFlexController_t iFlex );
+ void SetFlexControllerRaw( LocalFlexController_t iFlex, float flValue );
+ float GetFlexControllerRaw( LocalFlexController_t iFlex );
+
+ // void CalcBoneTransform( int iBone, Vector pos[], Quaternion q[], matrix3x4_t& bonematrix );
+
+ void UpdateBoneChain( Vector pos[], Quaternion q[], int iBone, matrix3x4_t *pBoneToWorld );
+ void SetViewTarget( void ); // ???
+ void GetBodyPoseParametersFromFlex( void );
+ void CalcHeadRotation( Vector pos[], Quaternion q[] );
+ float SetHeadPosition( matrix3x4_t& attToWorld, Vector const &vTargetPos, float dt );
+
+ int GetNumLODs() const;
+ float GetLODSwitchValue( int lod ) const;
+ void SetLODSwitchValue( int lod, float switchValue );
+
+ void scaleMeshes( float scale );
+ void scaleBones( float scale );
+
+ // Physics
+ void OverrideBones( bool *override );
+ int Physics_GetBoneCount( void );
+ const char * Physics_GetBoneName( int index );
+ int Physics_GetBoneIndex( const char *pName );
+ void Physics_GetData( int boneIndex, hlmvsolid_t *psolid, constraint_ragdollparams_t *pConstraint ) const;
+ void Physics_SetData( int boneIndex, const hlmvsolid_t *psolid, constraint_ragdollparams_t const *pConstraint );
+ void Physics_SetPreview( int previewBone, int axis, float t );
+ float Physics_GetMass( void );
+ void Physics_SetMass( float mass );
+ char *Physics_DumpQC( void );
+
+ float GetSequenceTime() const { return m_sequencetime; }
+ float GetTimeDelta() const { return m_dt; }
+
+ CStudioHdr *m_pStudioHdr;
+ CStudioHdr *GetStudioHdr() const;
+ studiohdr_t *GetStudioRenderHdr() const;
+ studiohwdata_t *GetHardwareData( void ) const;
+
+ // Get and set the model transform (i.e. what m_origin and m_angles are used to generate).
+ void GetModelTransform( matrix3x4_t &mat );
+ void SetModelTransform( const matrix3x4_t &mat );
+
+public:
+ // entity settings
+ QAngle m_angles; // rot
+ Vector m_origin; // trans
+
+protected:
+ int m_bodynum; // bodypart selection
+ int m_skinnum; // skin group selection
+ float m_controller[4]; // bone controllers
+
+public:
+ CMouthInfo m_mouth;
+
+protected:
+ char *m_pModelName; // model file name
+
+ // bool m_owntexmodel; // do we have a modelT.mdl ?
+
+ // Previouse sequence data
+ float m_blendtime;
+ float m_sequencetime;
+ int m_prevsequence;
+ float m_prevcycle;
+
+ float m_dt;
+
+ // Blending info
+
+ // Gesture,Sequence layering state
+#define MAXSTUDIOANIMLAYERS 8
+ AnimationLayer m_Layer[MAXSTUDIOANIMLAYERS];
+ int m_iActiveLayers;
+
+public:
+ float m_cycle; // 0 to 1 animation playback index
+protected:
+ int m_sequence; // sequence index
+ float m_poseparameter[MAXSTUDIOPOSEPARAM]; // intra-sequence blending
+ float m_weight;
+
+ // internal data
+ MDLHandle_t m_MDLHandle;
+ mstudiomodel_t *m_pmodel;
+
+public:
+ CUtlVector< HitboxSet_t > m_HitboxSets;
+ CUtlVector< CUtlSymbol > m_SurfaceProps;
+
+protected:
+ // class data
+ static Vector *m_AmbientLightColors;
+
+ // Added data
+ // IMESH *m_pimesh;
+ // VertexUpdate *m_pvertupdate;
+ // FaceUpdate *m_pfaceupdate;
+ IFACE *m_pface;
+
+ // studiohdr_t *m_ptexturehdr;
+
+ Vector4D m_adj; // FIX: non persistant, make static
+
+public:
+ IStudioPhysics *m_pPhysics;
+private:
+ int m_physPreviewBone;
+ int m_physPreviewAxis;
+ float m_physPreviewParam;
+ float m_physMass;
+
+public:
+ mstudioseqdesc_t &GetSeqDesc( int seq );
+ const matrix3x4_t* BoneToWorld( int nBoneIndex ) const;
+
+private:
+ mstudioanimdesc_t &GetAnimDesc( int anim );
+ mstudioanim_t *GetAnim( int anim );
+
+ void DrawPhysmesh( CPhysmesh *pMesh, int boneIndex, IMaterial *pMaterial, float *color );
+ void DrawPhysConvex( CPhysmesh *pMesh, IMaterial *pMaterial );
+
+ void SetupLighting( void );
+
+ virtual void SetupModel( int bodypart );
+
+private:
+ float m_flexweight[MAXSTUDIOFLEXCTRL];
+ matrix3x4_t m_pBoneToWorld[MAXSTUDIOBONES];
+
+public:
+ virtual void RunFlexRules( void );
+ virtual int BoneMask( void );
+ virtual void SetUpBones( bool mergeBones );
+
+ int GetLodUsed( void );
+ float GetLodMetric( void );
+
+ const char *GetKeyValueText( int iSequence );
+
+private:
+ // Drawing helper methods
+ void DrawBones( );
+ void DrawAttachments( );
+ void DrawEditAttachment();
+ void DrawHitboxes();
+ void DrawPhysicsModel( );
+ void DrawIllumPosition( );
+ void DrawOriginAxis( );
+
+public:
+ // generic interface to rendering?
+ void drawBox (Vector const *v, float const * color );
+ void drawWireframeBox (Vector const *v, float const* color );
+ void drawTransform( matrix3x4_t& m, float flLength = 4 );
+ void drawLine( Vector const &p1, Vector const &p2, int r = 0, int g = 0, int b = 255 );
+ void drawTransparentBox( Vector const &bbmin, Vector const &bbmax, const matrix3x4_t& m, float const *color, float const *wirecolor );
+
+private:
+ int m_LodUsed;
+ float m_LodMetric;
+
+public:
+
+ void SetSolveHeadTurn( int solve );
+ int GetSolveHeadTurn() const;
+
+ void ClearLookTargets( void );
+ void AddLookTarget( const Vector& vecPosition, float flWeight );
+ void AddLookTargetSelf( float flWeight );
+
+ void SetModelYaw( float yaw );
+ float GetModelYaw( void ) const;
+ void SetBodyYaw( float yaw );
+ float GetBodyYaw( void ) const;
+ void SetSpineYaw( float yaw );
+ float GetSpineYaw( void ) const;
+
+private:
+
+ // 0 == no, 1 == based on dt, 2 == completely.
+ int m_nSolveHeadTurn;
+ CUtlVector < StudioLookTarget > m_vecHeadTargets;
+
+ float m_flModelYaw;
+ float m_flBodyYaw;
+ float m_flSpineYaw;
+
+public:
+ bool m_bIsTransparent;
+ bool m_bHasProxy;
+
+ // necessary for accessing correct vertexes
+ void SetCurrentModel();
+
+public:
+ CIKContext m_ik;
+ float m_prevGroundCycles[5];
+ float m_prevIKCycles[5];
+
+public:
+ void IncrementFramecounter( void ) { m_iFramecounter++; };
+private:
+ int m_iFramecounter;
+
+private:
+ CJiggleBones *m_pJiggleBones;
+};
+
+
+//-----------------------------------------------------------------------------
+// Inline methods
+//-----------------------------------------------------------------------------
+inline CStudioHdr *StudioModel::GetStudioHdr( void ) const
+{
+ if (!m_pStudioHdr || m_pStudioHdr->IsReadyForAccess())
+ return m_pStudioHdr;
+
+ studiohdr_t *hdr = g_pMDLCache->GetStudioHdr( m_MDLHandle );
+
+ m_pStudioHdr->Init( hdr );
+
+ if (m_pStudioHdr->IsReadyForAccess())
+ return m_pStudioHdr;
+
+ return NULL;
+}
+
+inline studiohdr_t *StudioModel::GetStudioRenderHdr( void ) const
+{
+ return g_pMDLCache->GetStudioHdr( m_MDLHandle );
+}
+
+inline studiohwdata_t *StudioModel::GetHardwareData( void ) const
+{
+ return g_pMDLCache->GetHardwareData( m_MDLHandle );
+}
+
+inline studiohdr_t *StudioModel::getAnimHeader( int i ) const
+{
+// return g_pMDLCache->GetStudioHdr( m_AnimHandle[i] );
+// return m_panimhdr[i];
+}
+
+inline char const *StudioModel::GetFileName( void )
+{
+ return m_pModelName;
+}
+
+inline IStudioRender *StudioModel::GetStudioRender()
+{
+ return g_pStudioRender;
+}
+
+inline bool StudioModel::IsModelLoaded() const
+{
+ return m_MDLHandle != MDLHANDLE_INVALID;
+}
+
+inline const matrix3x4_t* StudioModel::BoneToWorld( int nBoneIndex ) const
+{
+ return &m_pBoneToWorld[nBoneIndex];
+}
+
+
+//-----------------------------------------------------------------------------
+// Globals
+//-----------------------------------------------------------------------------
+extern Vector g_vright; // needs to be set to viewer's right in order for chrome to work
+extern StudioModel *g_pStudioModel;
+extern StudioModel *g_pStudioExtraModel[HLMV_MAX_MERGED_MODELS];
+
+
+#endif // INCLUDED_STUDIOMODEL
diff --git a/utils/hlmv/sys.h b/utils/hlmv/sys.h
new file mode 100644
index 0000000..e3ea6f7
--- /dev/null
+++ b/utils/hlmv/sys.h
@@ -0,0 +1,14 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef SYS_H
+#define SYS_H
+#pragma once
+
+extern void Sys_CopyStringToClipboard( const char *pOut );
+
+#endif // SYS_H
diff --git a/utils/hlmv/sys_win.cpp b/utils/hlmv/sys_win.cpp
new file mode 100644
index 0000000..aa7510f
--- /dev/null
+++ b/utils/hlmv/sys_win.cpp
@@ -0,0 +1,37 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: abstract system dependent functions
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "sys.h"
+#include <windows.h>
+#include "tier1/strtools.h"
+
+
+void Sys_CopyStringToClipboard( const char *pOut )
+{
+ if ( !pOut || !OpenClipboard( NULL ) )
+ {
+ return;
+ }
+ // Remove the current Clipboard contents
+ if( !EmptyClipboard() )
+ {
+ return;
+ }
+ HGLOBAL clipbuffer;
+ char *buffer;
+ EmptyClipboard();
+
+ int len = Q_strlen(pOut)+1;
+ clipbuffer = GlobalAlloc(GMEM_DDESHARE, len );
+ buffer = (char*)GlobalLock( clipbuffer );
+ Q_strncpy( buffer, pOut, len );
+ GlobalUnlock( clipbuffer );
+
+ SetClipboardData( CF_TEXT,clipbuffer );
+
+ CloseClipboard();
+}
+
diff --git a/utils/hlmv/viewersettings.cpp b/utils/hlmv/viewersettings.cpp
new file mode 100644
index 0000000..acb5913
--- /dev/null
+++ b/utils/hlmv/viewersettings.cpp
@@ -0,0 +1,704 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: ViewerSettings.cpp
+// last modified: May 29 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#include "ViewerSettings.h"
+#include "studiomodel.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "windows.h"
+
+
+ViewerSettings g_viewerSettings;
+
+
+ViewerSettings::ViewerSettings()
+{
+ Q_memset( this, 0, sizeof( *this ) );
+}
+
+void InitViewerSettings ( const char *subkey )
+{
+ ViewerSettings save = g_viewerSettings;
+
+ memset (&g_viewerSettings, 0, sizeof (ViewerSettings));
+
+ // Some values should survive. This is a crappy way to do settings in general. Sigh.
+ {
+ g_viewerSettings.faceposerToolsDriveMouth = save.faceposerToolsDriveMouth;
+ }
+
+ strcpy( g_viewerSettings.registrysubkey, subkey );
+
+ g_pStudioModel->m_angles.Init( -90.0f, 0.0f, 0.0f );
+ g_pStudioModel->m_origin.Init( 0.0f, 0.0f, 50.0f );
+
+ g_viewerSettings.renderMode = RM_TEXTURED;
+ g_viewerSettings.fov = 65.0f;
+ g_viewerSettings.enableNormalMapping = true;
+ g_viewerSettings.enableParallaxMapping = false;
+ g_viewerSettings.showNormals = false;
+ g_viewerSettings.showTangentFrame = false;
+ g_viewerSettings.overlayWireframe = false;
+ g_viewerSettings.enableSpecular = true;
+ g_viewerSettings.playSounds = true;
+
+ g_viewerSettings.bgColor[0] = 0.25f;
+ g_viewerSettings.bgColor[1] = 0.25f;
+ g_viewerSettings.bgColor[2] = 0.25f;
+
+ g_viewerSettings.gColor[0] = 0.85f;
+ g_viewerSettings.gColor[1] = 0.85f;
+ g_viewerSettings.gColor[2] = 0.69f;
+
+ g_viewerSettings.lColor[0] = 1.0f;
+ g_viewerSettings.lColor[1] = 1.0f;
+ g_viewerSettings.lColor[2] = 1.0f;
+
+ g_viewerSettings.aColor[0] = 0.3f;
+ g_viewerSettings.aColor[1] = 0.3f;
+ g_viewerSettings.aColor[2] = 0.3f;
+
+ g_viewerSettings.lightrot[0] = 0.0f;
+ g_viewerSettings.lightrot[1] = 180.0f;
+ g_viewerSettings.lightrot[2] = 0.0f;
+
+ g_viewerSettings.speedScale = 1.0f;
+
+ g_viewerSettings.application_mode = 0;
+
+ g_viewerSettings.thumbnailsize = 128;
+ g_viewerSettings.thumbnailsizeanim = 128;
+
+ g_viewerSettings.m_iEditAttachment = -1;
+
+ g_viewerSettings.highlightHitbox = -1;
+ g_viewerSettings.highlightBone = -1;
+
+ g_viewerSettings.speechapiindex = 0;
+ g_viewerSettings.cclanguageid = 0;
+
+ // default hlmv settings
+ g_viewerSettings.xpos = 20;
+ g_viewerSettings.ypos = 20;
+ g_viewerSettings.width = 640;
+ g_viewerSettings.height = 700;
+
+ g_viewerSettings.originAxisLength = 10.0f;
+}
+
+
+bool RegReadVector( HKEY hKey, const char *szSubKey, Vector& value )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( szBuff );
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (sscanf( szBuff, "(%f %f %f)", &value[0], &value[1], &value[2] ) != 3)
+ return false;
+
+ return true;
+}
+
+bool RegReadQAngle( HKEY hKey, const char *szSubKey, QAngle& value )
+{
+ Vector tmp;
+ if (RegReadVector( hKey, szSubKey, tmp ))
+ {
+ value.Init( tmp.x, tmp.y, tmp.z );
+ return true;
+ }
+ return false;
+}
+
+
+bool RegReadColor( HKEY hKey, const char *szSubKey, float value[4] )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( szBuff );
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (sscanf( szBuff, "(%f %f %f %f)", &value[0], &value[1], &value[2], &value[3] ) != 4)
+ return false;
+
+ return true;
+}
+
+
+bool RegWriteVector( HKEY hKey, const char *szSubKey, Vector& value )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwSize; // Size of element data
+
+ sprintf( szBuff, "(%f %f %f)", value[0], value[1], value[2] );
+ dwSize = strlen( szBuff );
+
+ lResult = RegSetValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_SZ, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+bool RegWriteQAngle( HKEY hKey, const char *szSubKey, QAngle& value )
+{
+ Vector tmp;
+ tmp.Init( value.x, value.y, value.z );
+ return RegWriteVector( hKey, szSubKey, tmp );
+}
+
+
+bool RegWriteColor( HKEY hKey, const char *szSubKey, float value[4] )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwSize; // Size of element data
+
+ sprintf( szBuff, "(%f %f %f %f)", value[0], value[1], value[2], value[3] );
+ dwSize = strlen( szBuff );
+
+ lResult = RegSetValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_SZ, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+bool RegReadBool( HKEY hKey, const char *szSubKey, bool *value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ DWORD dwTemp;
+
+ dwSize = sizeof( dwTemp );
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)&dwTemp, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (dwType != REG_DWORD)
+ return false;
+
+ *value = (dwTemp != 0);
+
+ return true;
+}
+
+bool RegReadInt( HKEY hKey, const char *szSubKey, int *value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( DWORD );
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)value, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (dwType != REG_DWORD)
+ return false;
+
+ return true;
+}
+
+
+bool RegWriteInt( HKEY hKey, const char *szSubKey, int value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( DWORD );
+
+ lResult = RegSetValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_DWORD, // type buffer
+ (LPBYTE)&value, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+
+
+
+bool RegReadFloat( HKEY hKey, const char *szSubKey, float *value )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = sizeof( szBuff );
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ *value = atof( szBuff );
+
+ return true;
+}
+
+
+bool RegWriteFloat( HKEY hKey, const char *szSubKey, float value )
+{
+ LONG lResult; // Registry function result code
+ char szBuff[128]; // Temp. buffer
+ DWORD dwSize; // Size of element data
+
+ sprintf( szBuff, "%f", value );
+ dwSize = strlen( szBuff );
+
+ lResult = RegSetValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_SZ, // type buffer
+ (LPBYTE)szBuff, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+
+
+bool RegReadString( HKEY hKey, const char *szSubKey, char *string, int size )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwType; // Type of key
+ DWORD dwSize; // Size of element data
+
+ dwSize = size;
+
+ lResult = RegQueryValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ &dwType, // type buffer
+ (LPBYTE)string, // data buffer
+ &dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ if (dwType != REG_SZ)
+ return false;
+
+ return true;
+}
+
+
+bool RegWriteString( HKEY hKey, const char *szSubKey, char *string )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwSize; // Size of element data
+
+ dwSize = strlen( string );
+
+ lResult = RegSetValueEx(
+ hKey, // handle to key
+ szSubKey, // value name
+ 0, // reserved
+ REG_SZ, // type buffer
+ (LPBYTE)string, // data buffer
+ dwSize ); // size of data buffer
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ return true;
+}
+
+
+
+
+
+
+
+
+
+LONG RegViewerSettingsKey( const char *filename, PHKEY phKey, LPDWORD lpdwDisposition )
+{
+ if (strlen( filename ) == 0)
+ return ERROR_KEY_DELETED;
+
+ char szFileName[1024];
+
+ strcpy( szFileName, filename );
+
+ // strip out bogus characters
+ for (char *cp = szFileName; *cp; cp++)
+ {
+ if (*cp == '\\' || *cp == '/' || *cp == ':')
+ *cp = '.';
+ }
+
+ char szModelKey[1024];
+
+ sprintf( szModelKey, "Software\\Valve\\%s\\%s", g_viewerSettings.registrysubkey, szFileName );
+
+ return RegCreateKeyEx(
+ HKEY_CURRENT_USER, // handle of open key
+ szModelKey, // address of name of subkey to open
+ 0, // DWORD ulOptions, // reserved
+ NULL, // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ phKey, // Key we are creating
+ lpdwDisposition); // Type of creation
+}
+
+
+LONG RegViewerRootKey( PHKEY phKey, LPDWORD lpdwDisposition )
+{
+ char szRootKey[1024];
+
+ sprintf( szRootKey, "Software\\Valve\\%s", g_viewerSettings.registrysubkey );
+
+ return RegCreateKeyEx(
+ HKEY_CURRENT_USER, // handle of open key
+ szRootKey, // address of name of subkey to open
+ 0, // DWORD ulOptions, // reserved
+ NULL, // Type of value
+ REG_OPTION_NON_VOLATILE, // Store permanently in reg.
+ KEY_ALL_ACCESS, // REGSAM samDesired, // security access mask
+ NULL,
+ phKey, // Key we are creating
+ lpdwDisposition); // Type of creation
+}
+
+
+bool LoadViewerSettingsInt( char const *keyname, int *value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ HKEY hModelKey;
+
+ lResult = RegViewerSettingsKey( "hlfaceposer", &hModelKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ // First time, just set to Valve default
+ if (dwDisposition == REG_CREATED_NEW_KEY)
+ {
+ return false;
+ }
+
+ *value = 0;
+ RegReadInt( hModelKey, keyname, value );
+ return true;
+}
+
+bool SaveViewerSettingsInt ( const char *keyname, int value )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ HKEY hModelKey;
+ lResult = RegViewerSettingsKey( "hlfaceposer", &hModelKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ RegWriteInt( hModelKey, keyname, value );
+ return true;
+}
+
+bool LoadViewerSettings (const char *filename, StudioModel *pModel )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ HKEY hModelKey;
+
+ if (filename == NULL || pModel == NULL)
+ return false;
+
+ lResult = RegViewerSettingsKey( filename, &hModelKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ // First time, just set to Valve default
+ if (dwDisposition == REG_CREATED_NEW_KEY)
+ {
+ return false;
+ }
+
+ RegReadQAngle( hModelKey, "Rot", pModel->m_angles );
+ RegReadVector( hModelKey, "Trans", pModel->m_origin );
+ RegReadColor( hModelKey, "bgColor", g_viewerSettings.bgColor );
+ RegReadColor( hModelKey, "gColor", g_viewerSettings.gColor );
+ RegReadColor( hModelKey, "lColor", g_viewerSettings.lColor );
+ RegReadColor( hModelKey, "aColor", g_viewerSettings.aColor );
+ RegReadQAngle( hModelKey, "lightrot", g_viewerSettings.lightrot );
+
+ int iTemp;
+ float flTemp;
+ char szTemp[256];
+
+ RegReadString( hModelKey, "sequence", szTemp, sizeof( szTemp ) );
+ iTemp = pModel->LookupSequence( szTemp );
+ pModel->SetSequence( iTemp );
+ RegReadString( hModelKey, "overlaySequence0", szTemp, sizeof( szTemp ) );
+ iTemp = pModel->LookupSequence( szTemp );
+ RegReadFloat( hModelKey, "overlayWeight0", &flTemp );
+ pModel->SetOverlaySequence( 0, iTemp, flTemp );
+ RegReadString( hModelKey, "overlaySequence1", szTemp, sizeof( szTemp ) );
+ iTemp = pModel->LookupSequence( szTemp );
+ RegReadFloat( hModelKey, "overlayWeight1", &flTemp );
+ pModel->SetOverlaySequence( 1, iTemp, flTemp );
+ RegReadString( hModelKey, "overlaySequence2", szTemp, sizeof( szTemp ) );
+ iTemp = pModel->LookupSequence( szTemp );
+ RegReadFloat( hModelKey, "overlayWeight2", &flTemp );
+ pModel->SetOverlaySequence( 2, iTemp, flTemp );
+ RegReadString( hModelKey, "overlaySequence3", szTemp, sizeof( szTemp ) );
+ iTemp = pModel->LookupSequence( szTemp );
+ RegReadFloat( hModelKey, "overlayWeight3",&flTemp );
+ pModel->SetOverlaySequence( 3, iTemp, flTemp );
+
+ RegReadFloat( hModelKey, "speedscale", &g_viewerSettings.speedScale );
+ if (g_viewerSettings.speedScale > 1.0)
+ g_viewerSettings.speedScale = 1.0;
+
+ RegReadInt( hModelKey, "viewermode", &g_viewerSettings.application_mode );
+ RegReadInt( hModelKey, "thumbnailsize", &g_viewerSettings.thumbnailsize );
+ RegReadInt( hModelKey, "thumbnailsizeanim", &g_viewerSettings.thumbnailsizeanim );
+
+ if ( g_viewerSettings.thumbnailsize == 0 )
+ {
+ g_viewerSettings.thumbnailsize = 128;
+ }
+ if ( g_viewerSettings.thumbnailsizeanim == 0 )
+ {
+ g_viewerSettings.thumbnailsizeanim = 128;
+ }
+
+ RegReadInt( hModelKey, "speechapiindex", &g_viewerSettings.speechapiindex );
+ RegReadInt( hModelKey, "cclanguageid", &g_viewerSettings.cclanguageid );
+
+ RegReadBool( hModelKey, "showground", &g_viewerSettings.showGround );
+ RegReadBool( hModelKey, "showbackground", &g_viewerSettings.showBackground );
+ RegReadBool( hModelKey, "showshadow", &g_viewerSettings.showShadow );
+ RegReadBool( hModelKey, "showillumpos", &g_viewerSettings.showIllumPosition );
+ RegReadBool( hModelKey, "enablenormalmapping", &g_viewerSettings.enableNormalMapping );
+ RegReadBool( hModelKey, "playsounds", &g_viewerSettings.playSounds );
+ RegReadBool( hModelKey, "showoriginaxis", &g_viewerSettings.showOriginAxis );
+ RegReadFloat( hModelKey, "originaxislength", &g_viewerSettings.originAxisLength );
+
+ char merge_buffer[32];
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
+ {
+ Q_snprintf( merge_buffer, sizeof( merge_buffer ), "merge%d", i + 1 );
+ RegReadString( hModelKey, merge_buffer, g_viewerSettings.mergeModelFile[i], sizeof( g_viewerSettings.mergeModelFile[i] ) );
+ }
+
+ return true;
+}
+
+
+
+
+
+bool LoadViewerRootSettings( void )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ HKEY hRootKey;
+ lResult = RegViewerRootKey( &hRootKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ RegReadInt( hRootKey, "renderxpos", &g_viewerSettings.xpos );
+ RegReadInt( hRootKey, "renderypos", &g_viewerSettings.ypos );
+ RegReadInt( hRootKey, "renderwidth", &g_viewerSettings.width );
+ RegReadInt( hRootKey, "renderheight", &g_viewerSettings.height );
+
+ RegReadBool( hRootKey, "faceposerToolsDriveMouth", &g_viewerSettings.faceposerToolsDriveMouth );
+
+ return true;
+}
+
+
+
+bool SaveViewerSettings (const char *filename, StudioModel *pModel )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ if (filename == NULL || pModel == NULL)
+ return false;
+
+ HKEY hModelKey;
+ lResult = RegViewerSettingsKey( filename, &hModelKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+ CStudioHdr *hdr = pModel->GetStudioHdr();
+ if ( !hdr )
+ return false;
+
+ RegWriteQAngle( hModelKey, "Rot", pModel->m_angles );
+ RegWriteVector( hModelKey, "Trans", pModel->m_origin );
+ RegWriteColor( hModelKey, "bgColor", g_viewerSettings.bgColor );
+ RegWriteColor( hModelKey, "gColor", g_viewerSettings.gColor );
+ RegWriteColor( hModelKey, "lColor", g_viewerSettings.lColor );
+ RegWriteColor( hModelKey, "aColor", g_viewerSettings.aColor );
+ RegWriteQAngle( hModelKey, "lightrot", g_viewerSettings.lightrot );
+ RegWriteString( hModelKey, "sequence", hdr->pSeqdesc( pModel->GetSequence() ).pszLabel() );
+ RegWriteString( hModelKey, "overlaySequence0", hdr->pSeqdesc( pModel->GetOverlaySequence( 0 ) ).pszLabel() );
+ RegWriteFloat( hModelKey, "overlayWeight0", pModel->GetOverlaySequenceWeight( 0 ) );
+ RegWriteString( hModelKey, "overlaySequence1", hdr->pSeqdesc( pModel->GetOverlaySequence( 1 ) ).pszLabel() );
+ RegWriteFloat( hModelKey, "overlayWeight1", pModel->GetOverlaySequenceWeight( 1 ) );
+ RegWriteString( hModelKey, "overlaySequence2", hdr->pSeqdesc( pModel->GetOverlaySequence( 2 ) ).pszLabel() );
+ RegWriteFloat( hModelKey, "overlayWeight2", pModel->GetOverlaySequenceWeight( 2 ) );
+ RegWriteString( hModelKey, "overlaySequence3", hdr->pSeqdesc( pModel->GetOverlaySequence( 3 ) ).pszLabel() );
+ RegWriteFloat( hModelKey, "overlayWeight3", pModel->GetOverlaySequenceWeight( 3 ) );
+ RegWriteFloat( hModelKey, "speedscale", g_viewerSettings.speedScale );
+ RegWriteInt( hModelKey, "viewermode", g_viewerSettings.application_mode );
+ RegWriteInt( hModelKey, "thumbnailsize", g_viewerSettings.thumbnailsize );
+ RegWriteInt( hModelKey, "thumbnailsizeanim", g_viewerSettings.thumbnailsizeanim );
+ RegWriteInt( hModelKey, "speechapiindex", g_viewerSettings.speechapiindex );
+ RegWriteInt( hModelKey, "cclanguageid", g_viewerSettings.cclanguageid );
+ RegWriteInt( hModelKey, "showground", g_viewerSettings.showGround );
+ RegWriteInt( hModelKey, "showbackground", g_viewerSettings.showBackground );
+ RegWriteInt( hModelKey, "showshadow", g_viewerSettings.showShadow );
+ RegWriteInt( hModelKey, "showillumpos", g_viewerSettings.showIllumPosition );
+ RegWriteInt( hModelKey, "enablenormalmapping", g_viewerSettings.enableNormalMapping );
+ RegWriteInt( hModelKey, "playsounds", g_viewerSettings.playSounds );
+ RegWriteInt( hModelKey, "showoriginaxis", g_viewerSettings.showOriginAxis );
+ RegWriteFloat( hModelKey, "originaxislength", g_viewerSettings.originAxisLength );
+
+ char merge_buffer[32];
+ for ( int i = 0; i < HLMV_MAX_MERGED_MODELS; i++ )
+ {
+ Q_snprintf( merge_buffer, sizeof( merge_buffer ), "merge%d", i + 1 );
+ RegWriteString( hModelKey, merge_buffer, g_viewerSettings.mergeModelFile[i] );
+ }
+
+ return true;
+}
+
+bool SaveViewerRootSettings( void )
+{
+ LONG lResult; // Registry function result code
+ DWORD dwDisposition; // Type of key opening event
+
+ HKEY hRootKey;
+ lResult = RegViewerRootKey( &hRootKey, &dwDisposition);
+
+ if (lResult != ERROR_SUCCESS) // Failure
+ return false;
+
+ RegWriteInt( hRootKey, "renderxpos", g_viewerSettings.xpos );
+ RegWriteInt( hRootKey, "renderypos", g_viewerSettings.ypos );
+ RegWriteInt( hRootKey, "renderwidth", g_viewerSettings.width );
+ RegWriteInt( hRootKey, "renderheight", g_viewerSettings.height );
+
+ RegWriteInt( hRootKey, "faceposerToolsDriveMouth", g_viewerSettings.faceposerToolsDriveMouth ? 1 : 0 );
+
+ return true;
+}
diff --git a/utils/hlmv/viewersettings.h b/utils/hlmv/viewersettings.h
new file mode 100644
index 0000000..9fd8a34
--- /dev/null
+++ b/utils/hlmv/viewersettings.h
@@ -0,0 +1,157 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+//
+// Half-Life Model Viewer (c) 1999 by Mete Ciragan
+//
+// file: ViewerSettings.h
+// last modified: May 29 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
+//
+// web: http://www.swissquake.ch/chumbalum-soft/
+//
+#ifndef INCLUDED_VIEWERSETTINGS
+#define INCLUDED_VIEWERSETTINGS
+
+#include "mathlib/vector.h"
+
+enum // render modes
+{
+ RM_WIREFRAME = 0,
+// RM_FLATSHADED,
+ RM_SMOOTHSHADED,
+ RM_TEXTURED,
+ RM_BONEWEIGHTS,
+ RM_SHOWBADVERTEXDATA,
+ RM_TEXCOORDS,
+};
+
+#define HLMV_MAX_MERGED_MODELS 12
+
+struct ViewerSettings
+{
+ char registrysubkey[ 64 ];
+ int application_mode; // 0 expression, 1 choreo
+
+ bool showHitBoxes;
+ bool showBones;
+ bool showAttachments;
+ bool showPhysicsModel;
+ bool showPhysicsPreview;
+ bool showSequenceBoxes;
+ bool enableIK;
+ bool enableTargetIK;
+ bool showNormals;
+ bool showTangentFrame;
+ bool overlayWireframe;
+ bool enableNormalMapping;
+ bool enableParallaxMapping;
+ bool enableSpecular;
+ bool showIllumPosition;
+ bool playSounds;
+
+ // Current attachment we're editing. -1 if none.
+ int m_iEditAttachment;
+ bool showLightingCenter;
+ int highlightPhysicsBone;
+ int highlightHitbox;
+ int highlightBone;
+ QAngle lightrot; // light rotation
+ float lColor[4]; // directional color
+ float aColor[4]; // ambient color
+
+ // external
+
+ // model
+ float fov; // horizontal field of view
+
+ // render
+ int renderMode;
+ bool showBackground;
+ bool showGround;
+ bool showTexture;
+ bool showMovement;
+ bool showShadow;
+ int texture;
+ int skin;
+ int materialIndex;
+ bool showOriginAxis;
+ float originAxisLength;
+
+ // animation
+ float speedScale;
+ bool blendSequenceChanges;
+ bool animateWeapons;
+
+ // bodyparts and bonecontrollers
+ //int submodels[32];
+ //float controllers[8];
+
+ // fullscreen
+ int xpos, ypos;
+ int width, height;
+ bool cds;
+
+ // colors
+ float bgColor[4]; // background color
+ float gColor[4];
+
+ // misc
+ bool pause;
+ bool rotating;
+ bool mousedown;
+
+ // only used for fullscreen mode
+ // char modelFile[256];
+ //char backgroundTexFile[256];
+ //char groundTexFile[256];
+
+ int lod;
+ bool autoLOD;
+ bool softwareSkin;
+ bool overbright;
+
+ int thumbnailsize;
+ int thumbnailsizeanim;
+
+ int speechapiindex;
+ int cclanguageid; // Close captioning language id (see sentence.h enum)
+
+ bool showHidden;
+ bool showActivities;
+
+ bool faceposerToolsDriveMouth;
+
+ char mergeModelFile[HLMV_MAX_MERGED_MODELS][256];
+
+ ViewerSettings();
+
+};
+
+extern ViewerSettings g_viewerSettings;
+class StudioModel;
+
+void InitViewerSettings ( const char *subkey );
+bool LoadViewerSettings (const char *filename, StudioModel *pModel );
+bool SaveViewerSettings (const char *filename, StudioModel *pModel );
+bool LoadViewerRootSettings( void );
+bool SaveViewerRootSettings( void );
+
+// For saving/loading "global" settings
+bool LoadViewerSettingsInt( char const *keyname, int *value );
+bool SaveViewerSettingsInt ( const char *keyname, int value );
+
+
+#endif // INCLUDED_VIEWERSETTINGS \ No newline at end of file