diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlmv | |
| download | archived-source-engine-2018-hl2-src-master.tar.xz archived-source-engine-2018-hl2-src-master.zip | |
Diffstat (limited to 'utils/hlmv')
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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 Binary files differnew file mode 100644 index 0000000..1d59980 --- /dev/null +++ b/utils/hlmv/icon1.ico 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 +// +// email: [email protected] +// 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 |