//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //=============================================================================// #include "ControlPanel.h" #include "ViewerSettings.h" #include "StudioModel.h" #include "IStudioRender.h" #include "MatSysWin.h" #include #include #include #include #include #include "FlexPanel.h" #include "mxExpressionTray.h" #include "PhonemeEditor.h" #include "hlfaceposer.h" #include "expclass.h" #include "mxExpressionTab.h" #include "ExpressionTool.h" #include "MDLViewer.h" #include "choreowidgetdrawhelper.h" #include "faceposer_models.h" #include "ifaceposerworkspace.h" #include "choreoview.h" #include "GestureTool.h" #include "RampTool.h" #include "SceneRampTool.h" #include "phonemeextractor/PhonemeExtractor.h" #include "tier1/KeyValues.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern char g_appTitle[]; ControlPanel *g_pControlPanel = 0; //----------------------------------------------------------------------------- // Purpose: A simple subclass so we can paint the window background //----------------------------------------------------------------------------- class CControlPanelTabWindow : public mxWindow { public: CControlPanelTabWindow( mxWindow *parent, int x, int y, int w, int h ) : mxWindow( parent, x, y, w, h ) { FacePoser_AddWindowStyle( this, WS_CLIPSIBLINGS | WS_CLIPCHILDREN ); }; virtual bool PaintBackground( void ) { CChoreoWidgetDrawHelper drawHelper( this ); RECT rc; drawHelper.GetClientRect( rc ); drawHelper.DrawFilledRect( GetSysColor( COLOR_BTNFACE ), rc ); return false; } }; ControlPanel::ControlPanel (mxWindow *parent) : IFacePoserToolWindow( "ControlPanel", "Control Panel" ), mxWindow( parent, 0, 0, 0, 0 ), tab( 0 ) { // create tabcontrol with subdialog windows tab = new mxTab (this, 0, 0, 0, 0, IDC_TAB); CControlPanelTabWindow *wRender = new CControlPanelTabWindow (this, 0, 0, 0, 0); tab->add (wRender, "Render"); cRenderMode = new mxChoice (wRender, 5, 5, 100, 22, IDC_RENDERMODE); cRenderMode->add ("Wireframe"); cRenderMode->add ("Flatshaded"); cRenderMode->add ("Smoothshaded"); cRenderMode->add ("Textured"); cRenderMode->select (3); mxToolTip::add (cRenderMode, "Select Render Mode"); slModelGap = new mxSlider( wRender, 220, 5, 140, 20, IDC_MODELSPACING ); slModelGap->setRange( 0.0f, 64.0f, 256 ); slModelGap->setValue( 16 ); mxToolTip::add (slModelGap, "Select Model Spacing" ); new mxLabel (wRender, 220, 25, 140, 20, "Model Spacing"); cbAllWindowsDriveSpeech = new mxCheckBox( wRender, 220, 45, 140, 20, "All tools drive mouth", IDC_TOOLSDRIVEMOUTH ); cbAllWindowsDriveSpeech->setChecked( g_viewerSettings.faceposerToolsDriveMouth ); cbGround = new mxCheckBox (wRender, 110, 5, 100, 20, "Ground", IDC_GROUND); cbGround->setEnabled( true ); cbMovement = new mxCheckBox (wRender, 110, 25, 100, 20, "Movement", IDC_MOVEMENT); cbMovement->setEnabled( false ); cbBackground = new mxCheckBox (wRender, 110, 45, 100, 20, "Background", IDC_BACKGROUND); cbBackground->setEnabled( false ); new mxCheckBox (wRender, 110, 65, 100, 20, "Hit Boxes", IDC_HITBOXES); new mxCheckBox (wRender, 5, 65, 100, 20, "Bones", IDC_BONES); mxCheckBox *cbAttachments = new mxCheckBox (wRender, 5, 45, 100, 20, "Attachments", IDC_ATTACHMENTS); cbAttachments->setEnabled( false ); CControlPanelTabWindow *wSequence = new CControlPanelTabWindow (this, 0, 0, 0, 0); tab->add (wSequence, "Sequence"); cSequence = new mxChoice (wSequence, 5, 5, 200, 22, IDC_SEQUENCE); mxToolTip::add (cSequence, "Select Sequence"); slSpeedScale = new mxSlider (wSequence, 5, 32, 200, 18, IDC_SPEEDSCALE); slSpeedScale->setRange (0.0, 5.0 ); slSpeedScale->setValue (0.0); mxToolTip::add (slSpeedScale, "Speed Scale"); lSpeedScale = new mxLabel( wSequence, 5, 50, 200, 18 ); lSpeedScale->setLabel( "Speed scale" ); CControlPanelTabWindow *wBody = new CControlPanelTabWindow (this, 0, 0, 0, 0); tab->add (wBody, "Body"); 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, 45); mxToolTip::add (slController, "Change current bone controller value"); lModelInfo1 = new mxLabel (wBody, 220, 5, 120, 100, "No Model."); lModelInfo2 = new mxLabel (wBody, 340, 5, 120, 100, ""); cSkin = new mxChoice (wBody, 5, 55, 100, 22, IDC_SKINS); mxToolTip::add (cSkin, "Choose a skin family"); } ControlPanel::~ControlPanel() { } bool ControlPanel::CanClose( void ) { workspacefiles->StartStoringFiles( IWorkspaceFiles::EXPRESSION ); for ( int i = 0 ; i < expressions->GetNumClasses(); i++ ) { CExpClass *cl = expressions->GetClass( i ); if ( cl ) { workspacefiles->StoreFile( IWorkspaceFiles::EXPRESSION, cl->GetFileName() ); } } workspacefiles->FinishStoringFiles( IWorkspaceFiles::EXPRESSION ); // Now close them all, or abort exit if user doesn't want to close any that have changed return Closeall(); } void ControlPanel::OnDelete() { } void ControlPanel::PositionControls( int width, int height ) { if ( tab ) { tab->setBounds( 0, GetCaptionHeight(), width, height ); } } void ControlPanel::redraw() { if ( !ToolCanDraw() ) return; CChoreoWidgetDrawHelper helper( this, GetSysColor( COLOR_BTNFACE ) ); HandleToolRedraw( helper ); BaseClass::redraw(); } int ControlPanel::handleEvent (mxEvent *event) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); int iret = 0; if ( HandleToolEvent( event ) ) { return iret; } switch ( event->event ) { case mxEvent::Size: { PositionControls( event->width, event->height ); iret = 1; } break; case mxEvent::Action: { iret = 1; switch (event->action) { case IDC_TOOLSDRIVEMOUTH: { g_viewerSettings.faceposerToolsDriveMouth = ((mxCheckBox *)event->widget)->isChecked(); } break; case IDC_TAB: { g_viewerSettings.showTexture = (tab->getSelectedIndex() == 3); } break; case IDC_RENDERMODE: { int index = cRenderMode->getSelectedIndex(); if (index >= 0) { setRenderMode (index); } } 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_PHYSICSMODEL: g_viewerSettings.showPhysicsModel = ((mxCheckBox *) event->widget)->isChecked(); break; case IDC_BONES: g_viewerSettings.showBones = ((mxCheckBox *) event->widget)->isChecked(); break; case IDC_ATTACHMENTS: g_viewerSettings.showAttachments = ((mxCheckBox *) event->widget)->isChecked(); break; case IDC_SEQUENCE: { int index = cSequence->getSelectedIndex(); if (index >= 0) { setSequence ( index ); } } break; case IDC_SPEEDSCALE: { g_viewerSettings.speedScale = ((mxSlider *) event->widget)->getValue(); lSpeedScale->setLabel( va( "Speed scale %.2f", g_viewerSettings.speedScale ) ); } break; case IDC_PRIMARYBLEND: { setBlend( 0, ((mxSlider *) event->widget)->getValue() ); } break; case IDC_SECONDARYBLEND: { setBlend( 1, ((mxSlider *) event->widget)->getValue() ); } break; case IDC_BODYPART: { int index = cBodypart->getSelectedIndex(); if (index >= 0) { 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) setBoneControllerValue (index, slController->getValue()); } break; case IDC_SKINS: { int index = cSkin->getSelectedIndex(); if (index >= 0) { models->GetActiveStudioModel()->SetSkin (index); g_viewerSettings.skin = index; g_pMatSysWindow->redraw(); } } break; default: iret = 0; break; } } } return iret; } void ControlPanel::dumpModelInfo() { } void ControlPanel::ChangeModel( const char *filename ) { HCURSOR hPrevCursor = SetCursor( LoadCursor( NULL, IDC_WAIT ) ); // init all the selection tabs based on the current model initSequenceChoices(); initBodypartChoices(); initBoneControllerChoices(); initSkinChoices(); setModelInfo(); SetCloseCaptionLanguageId( g_viewerSettings.cclanguageid, true ); g_viewerSettings.m_iEditAttachment = -1; g_viewerSettings.enableIK = true; g_viewerSettings.enableTargetIK = false; setSequence( models->GetActiveStudioModel()->GetSequence() ); setSpeed( g_viewerSettings.speedScale ); mx_setcwd (mx_getpath (filename)); g_pFlexPanel->initFlexes(); // centerView(); // CenterOnFace(); IFacePoserToolWindow::ModelChanged(); CExpClass *cl = expressions->GetActiveClass(); if ( cl ) { cl->SelectExpression( cl->GetSelectedExpression() ); } SetSuffix( va( " - %s.mdl", models->GetActiveModelName() ) ); redraw(); SetCursor( hPrevCursor ); } void ControlPanel::setRenderMode (int mode) { g_viewerSettings.renderMode = mode; g_pMatSysWindow->redraw(); } void ControlPanel::setHighlightBone( int index ) { g_viewerSettings.highlightPhysicsBone = index; } void ControlPanel::setShowGround (bool b) { g_viewerSettings.showGround = b; cbGround->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::initSequenceChoices() { CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); if (hdr) { cSequence->removeAll(); for (int i = 0; i < hdr->GetNumSeq(); i++) { cSequence->add (hdr->pSeqdesc(i).pszLabel()); } cSequence->select (0); } } void ControlPanel::setSequence (int index) { cSequence->select (index); models->GetActiveStudioModel()->SetSequence(index); initPoseParameters( ); } void ControlPanel::setSpeed( float value ) { g_viewerSettings.speedScale = value; slSpeedScale->setValue( value ); } void ControlPanel::setBlend(int index, float value ) { models->GetActiveStudioModel()->SetPoseParameter( index, value ); } void ControlPanel::initBodypartChoices() { CStudioHdr *hdr = models->GetActiveStudioModel()->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 + 1); cSubmodel->add (str); } cSubmodel->select (0); } } } void ControlPanel::setBodypart (int index) { CStudioHdr *hdr = models->GetActiveStudioModel()->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 + 1); cSubmodel->add (str); } cSubmodel->select (0); //models->GetActiveStudioModel()->SetBodygroup (index, 0); } } } void ControlPanel::setSubmodel (int index) { models->GetActiveStudioModel()->SetBodygroup (cBodypart->getSelectedIndex(), index); } void ControlPanel::initBoneControllerChoices() { CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); if (hdr) { cController->setEnabled (hdr->numbonecontrollers() > 0); slController->setEnabled (hdr->numbonecontrollers() > 0); cController->removeAll(); for (int i = 0; i < hdr->numbonecontrollers(); i++) { mstudiobonecontroller_t *pbonecontroller = hdr->pBonecontroller(i); char str[32]; sprintf (str, "Controller %d", pbonecontroller->inputfield); cController->add (str); } if (hdr->numbonecontrollers() > 0) { mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(0); cController->select (0); slController->setRange (pbonecontrollers->start, pbonecontrollers->end); slController->setValue (0); } } } void ControlPanel::setBoneController (int index) { CStudioHdr *hdr = models->GetActiveStudioModel()->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 = models->GetActiveStudioModel()->GetStudioHdr(); if (hdr) { mstudiobonecontroller_t *pbonecontrollers = hdr->pBonecontroller(index); models->GetActiveStudioModel()->SetController (pbonecontrollers->inputfield, value); } } void ControlPanel::initPoseParameters() { CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); if (hdr) { for (int i = 0; i < hdr->GetNumPoseParameters(); i++) { setBlend( i, 0.0 ); } } } void ControlPanel::initSkinChoices() { CStudioHdr *hdr = models->GetActiveStudioModel()->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 + 1); cSkin->add (str); } cSkin->select (0); models->GetActiveStudioModel()->SetSkin (0); g_viewerSettings.skin = 0; } } void ControlPanel::setModelInfo() { static char str[2048]; CStudioHdr *hdr = models->GetActiveStudioModel()->GetStudioHdr(); if (!hdr) return; int hbcount = 0; for ( int s = 0; s < hdr->numhitboxsets(); s++ ) { hbcount += hdr->iHitboxCount( s ); } sprintf (str, "Bones: %d\n" "Bone Controllers: %d\n" "Hit Boxes: %d in %d sets\n" "Sequences: %d\n", hdr->numbones(), hdr->numbonecontrollers(), hbcount, hdr->numhitboxsets(), hdr->GetNumSeq() ); lModelInfo1->setLabel (str); sprintf (str, "Textures: %d\n" "Skin Families: %d\n" "Bodyparts: %d\n" "Attachments: %d\n", hdr->numtextures(), hdr->numskinfamilies(), hdr->numbodyparts(), hdr->GetNumAttachments()); lModelInfo2->setLabel (str); } void ControlPanel::CenterOnFace( void ) { if ( !models->GetActiveStudioModel() ) return; StudioModel *mdl = models->GetActiveStudioModel(); if ( !mdl ) return; CStudioHdr *hdr = mdl->GetStudioHdr(); if ( !hdr ) return; setSpeed( 1.0f ); int oldSeq = models->GetActiveStudioModel()->GetSequence(); int seq = models->GetActiveStudioModel()->LookupSequence( "idle_suble" ); if ( seq == -1 ) seq = 0; if ( seq != oldSeq ) { Con_Printf( "Centering changed model sequence # to %d\n", seq ); } setSequence( seq ); initPoseParameters( ); mdl->m_angles.Init(); mdl->m_origin.Init(); Vector size; VectorSubtract( hdr->hull_max(), hdr->hull_min(), size ); float eyeheight = hdr->hull_min().z + 0.9 * size.z; if ( hdr->GetNumAttachments() > 0 ) { for (int i = 0; i < hdr->GetNumAttachments(); i++) { const mstudioattachment_t &attachment = hdr->pAttachment( i ); int iBone = hdr->GetAttachmentBone( i ); if ( Q_stricmp( attachment.pszName(), "eyes" ) ) continue; mstudiobone_t *bone = hdr->pBone( iBone ); if ( !bone ) continue; matrix3x4_t boneToPose; MatrixInvert( bone->poseToBone, boneToPose ); matrix3x4_t attachmentPoseToLocal; ConcatTransforms( boneToPose, attachment.local, attachmentPoseToLocal ); Vector localSpaceEyePosition; VectorITransform( vec3_origin, attachmentPoseToLocal, localSpaceEyePosition ); // Not sure why this must be negative? eyeheight = -localSpaceEyePosition.z + hdr->hull_min().z; break; } } KeyValues *seqKeyValues = new KeyValues(""); if ( seqKeyValues->LoadFromBuffer( mdl->GetFileName( ), mdl->GetKeyValueText( seq ) ) ) { // Do we have a build point section? KeyValues *pkvAllFaceposer = seqKeyValues->FindKey("faceposer"); if ( pkvAllFaceposer ) { float flEyeheight = pkvAllFaceposer->GetFloat( "eye_height", -9999.0f ); if ( flEyeheight != -9999.0f ) { eyeheight = flEyeheight; } } } seqKeyValues->deleteThis(); mdl->m_origin.x = size.z * .65f; mdl->m_origin.z += eyeheight; CUtlVector< StudioModel * > modellist; modellist.AddToTail( models->GetActiveStudioModel() ); int i; if ( models->CountVisibleModels() > 0 ) { modellist.RemoveAll(); for ( i = 0; i < models->Count(); i++ ) { if ( models->IsModelShownIn3DView( i ) ) { modellist.AddToTail( models->GetStudioModel( i ) ); } } } int modelcount = modellist.Count(); int countover2 = modelcount / 2; int ydelta = GetModelGap(); int yoffset = -countover2 * ydelta; for ( i = 0 ; i < modelcount; i++ ) { if ( models->GetStudioHeader( i ) == hdr ) { mdl->m_origin.y = -yoffset; } yoffset += ydelta; } g_pMatSysWindow->redraw(); } //----------------------------------------------------------------------------- // Purpose: // Output : float //----------------------------------------------------------------------------- float ControlPanel::GetModelGap( void ) { return slModelGap->getValue(); } void ControlPanel::centerView() { StudioModel *pModel = models->GetActiveStudioModel(); if ( !pModel ) return; Vector min, max; models->GetActiveStudioModel()->ExtractBbox (min, max); float dx = max[0] - min[0]; float dy = max[1] - min[1]; float dz = max[2] - min[2]; float d = dx; if (dy > d) d = dy; if (dz > d) d = dz; pModel->m_origin[0] = d * 1.0f; pModel->m_origin[1] = 0; pModel->m_origin[2] = min[2] + dz / 2; pModel->m_angles[0] = 0.0f; pModel->m_angles[1] = 0.0f; pModel->m_angles[2] = 0.0f; g_viewerSettings.lightrot.x = 0.f; g_viewerSettings.lightrot.y = -180.0f; g_viewerSettings.lightrot.z = 0.0f; g_pMatSysWindow->redraw(); } bool ControlPanel::Close() { int index = g_pExpressionClass->getSelectedIndex(); CExpClass *cl = expressions->GetClass( index ); if ( !cl ) return true; return expressions->CloseClass( cl ); } bool ControlPanel::Closeall() { bool retval = true; while ( expressions->GetNumClasses() > 0 ) { CExpClass *cl = expressions->GetClass( 0 ); if ( !cl ) break; if ( !expressions->CloseClass( cl ) ) { return false; } } return retval; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::Copy( void ) { g_pFlexPanel->CopyControllerSettings(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::Paste( void ) { g_pFlexPanel->PasteControllerSettings(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::Undo( void ) { CExpClass *active = expressions->GetActiveClass(); if ( !active ) return; int index = active->GetSelectedExpression(); if ( index != -1 ) { UndoExpression( index ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::Redo( void ) { CExpClass *active = expressions->GetActiveClass(); if ( !active ) return; int index = active->GetSelectedExpression(); if ( index != -1 ) { RedoExpression( index ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::UndoExpression( int index ) { if ( index == -1 ) return; CExpClass *active = expressions->GetActiveClass(); if ( !active ) return; CExpression *exp = active->GetExpression( index ); if ( exp ) { exp->Undo(); // Show the updated data active->SelectExpression( index ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void ControlPanel::RedoExpression( int index ) { if ( index == -1 ) return; CExpClass *active = expressions->GetActiveClass(); if ( !active ) return; CExpression *exp = active->GetExpression( index ); if ( exp ) { exp->Redo(); // Show the updated data active->SelectExpression( index ); } } void ControlPanel::DeleteExpression( int index ) { CExpClass *active = expressions->GetActiveClass(); if ( !active ) return; CExpression *exp = active->GetExpression( index ); if ( exp ) { Con_Printf( "Deleting expression %s : %s\n", exp->name, exp->description ); g_pFlexPanel->DeleteExpression( index ); active->SelectExpression( max( 0, index - 1 ) ); } } //----------------------------------------------------------------------------- // Purpose: // Input : dt - //----------------------------------------------------------------------------- void ControlPanel::Think( float dt ) { } //----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool ControlPanel::AllToolsDriveSpeech( void ) { return cbAllWindowsDriveSpeech->isChecked(); }