//====== Copyright © 1996-2008, Valve Corporation, All rights reserved. ======= // // Purpose: // //============================================================================= #include "cbase.h" #include "basemodel_panel.h" #include "activitylist.h" #include "animation.h" #include "vgui/iinput.h" #include "matsys_controls/manipulator.h" using namespace vgui; DECLARE_BUILD_FACTORY( CBaseModelPanel ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseModelPanel::CBaseModelPanel( vgui::Panel *pParent, const char *pName ): BaseClass( pParent, pName ) { m_bForcePos = false; m_bMousePressed = false; m_bAllowRotation = false; vgui::SETUP_PANEL( this ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseModelPanel::~CBaseModelPanel() { } //----------------------------------------------------------------------------- // Purpose: Load in the model portion of the panel's resource file. //----------------------------------------------------------------------------- void CBaseModelPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); // Grab and set the camera FOV. float flFOV = GetCameraFOV(); m_BMPResData.m_flFOV = inResourceData->GetInt( "fov", flFOV ); SetCameraFOV( m_BMPResData.m_flFOV ); // Do we allow rotation on these panels. m_bAllowRotation = ( inResourceData->GetInt( "allow_rot", 0 ) == 1 ); // Parse our resource file and apply all necessary updates to the MDL. for ( KeyValues *pData = inResourceData->GetFirstSubKey() ; pData != NULL ; pData = pData->GetNextKey() ) { if ( !Q_stricmp( pData->GetName(), "model" ) ) { ParseModelResInfo( pData ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::ParseModelResInfo( KeyValues *inResourceData ) { m_bForcePos = ( inResourceData->GetInt( "force_pos", 0 ) == 1 ); m_BMPResData.m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); m_BMPResData.m_pszModelName_HWM = ReadAndAllocStringValue( inResourceData, "modelname_hwm" ); m_BMPResData.m_pszVCD = ReadAndAllocStringValue( inResourceData, "vcd" ); m_BMPResData.m_angModelPoseRot.Init( inResourceData->GetFloat( "angles_x", 0.0f ), inResourceData->GetFloat( "angles_y", 0.0f ), inResourceData->GetFloat( "angles_z", 0.0f ) ); m_BMPResData.m_vecOriginOffset.Init( inResourceData->GetFloat( "origin_x", 110.0 ), inResourceData->GetFloat( "origin_y", 5.0 ), inResourceData->GetFloat( "origin_z", 5.0 ) ); m_BMPResData.m_vecFramedOriginOffset.Init( inResourceData->GetFloat( "frame_origin_x", 110.0 ), inResourceData->GetFloat( "frame_origin_y", 5.0 ), inResourceData->GetFloat( "frame_origin_z", 5.0 ) ); m_BMPResData.m_vecViewportOffset.Init(); m_BMPResData.m_nSkin = inResourceData->GetInt( "skin", -1 ); m_BMPResData.m_bUseSpotlight = ( inResourceData->GetInt( "spotlight", 0 ) == 1 ); m_angPlayer = m_BMPResData.m_angModelPoseRot; m_vecPlayerPos = m_BMPResData.m_vecOriginOffset; for ( KeyValues *pData = inResourceData->GetFirstSubKey(); pData != NULL; pData = pData->GetNextKey() ) { if ( !Q_stricmp( pData->GetName(), "animation" ) ) { ParseModelAnimInfo( pData ); } else if ( !Q_stricmp( pData->GetName(), "attached_model" ) ) { ParseModelAttachInfo( pData ); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::ParseModelAnimInfo( KeyValues *inResourceData ) { if ( !inResourceData ) return; int iAnim = m_BMPResData.m_aAnimations.AddToTail(); if ( iAnim == m_BMPResData.m_aAnimations.InvalidIndex() ) return; m_BMPResData.m_aAnimations[iAnim].m_pszName = ReadAndAllocStringValue( inResourceData, "name" ); m_BMPResData.m_aAnimations[iAnim].m_pszSequence = ReadAndAllocStringValue( inResourceData, "sequence" ); m_BMPResData.m_aAnimations[iAnim].m_pszActivity = ReadAndAllocStringValue( inResourceData, "activity" ); m_BMPResData.m_aAnimations[iAnim].m_bDefault = ( inResourceData->GetInt( "default", 0 ) == 1 ); for ( KeyValues *pAnimData = inResourceData->GetFirstSubKey(); pAnimData != NULL; pAnimData = pAnimData->GetNextKey() ) { if ( !Q_stricmp( pAnimData->GetName(), "pose_parameters" ) ) { m_BMPResData.m_aAnimations[iAnim].m_pPoseParameters = pAnimData->MakeCopy(); } } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::ParseModelAttachInfo( KeyValues *inResourceData ) { if ( !inResourceData ) return; int iAttach = m_BMPResData.m_aAttachModels.AddToTail(); if ( iAttach == m_BMPResData.m_aAttachModels.InvalidIndex() ) return; m_BMPResData.m_aAttachModels[iAttach].m_pszModelName = ReadAndAllocStringValue( inResourceData, "modelname" ); m_BMPResData.m_aAttachModels[iAttach].m_nSkin = inResourceData->GetInt( "skin", -1 ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetupModelDefaults( void ) { SetupModelAnimDefaults(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetupModelAnimDefaults( void ) { // Verify that we have animations for this model. int nAnimCount = m_BMPResData.m_aAnimations.Count(); if ( nAnimCount == 0 ) return; // Find the default animation if one exists. int iIndex = FindDefaultAnim(); if ( iIndex == -1 ) return; SetModelAnim( iIndex ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseModelPanel::FindDefaultAnim( void ) { int iIndex = -1; int nAnimCount = m_BMPResData.m_aAnimations.Count(); for ( int iAnim = 0; iAnim < nAnimCount; ++iAnim ) { if ( m_BMPResData.m_aAnimations[iAnim].m_bDefault ) return iAnim; } return iIndex; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseModelPanel::FindAnimByName( const char *pszName ) { int iIndex = -1; int nAnimCount = m_BMPResData.m_aAnimations.Count(); for ( int iAnim = 0; iAnim < nAnimCount; ++iAnim ) { if ( !Q_stricmp( m_BMPResData.m_aAnimations[iAnim].m_pszName, pszName ) ) return iAnim; } return iIndex; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- int CBaseModelPanel::FindSequenceFromActivity( CStudioHdr *pStudioHdr, const char *pszActivity ) { if ( !pStudioHdr ) return -1; for ( int iSeq = 0; iSeq < pStudioHdr->GetNumSeq(); ++iSeq ) { mstudioseqdesc_t &seqDesc = pStudioHdr->pSeqdesc( iSeq ); if ( !stricmp( seqDesc.pszActivityName(), pszActivity ) ) { return iSeq; } } return -1; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetModelAnim( int iAnim ) { int nAnimCount = m_BMPResData.m_aAnimations.Count(); if ( nAnimCount == 0 || !m_BMPResData.m_aAnimations.IsValidIndex( iAnim ) ) return; // Do we have an activity or a sequence? if ( m_BMPResData.m_aAnimations[iAnim].m_pszActivity && m_BMPResData.m_aAnimations[iAnim].m_pszActivity[0] ) { SetModelAnim( m_BMPResData.m_aAnimations[iAnim].m_pszActivity ); } else if ( m_BMPResData.m_aAnimations[iAnim].m_pszSequence && m_BMPResData.m_aAnimations[iAnim].m_pszSequence[0] ) { SetModelAnim( m_BMPResData.m_aAnimations[iAnim].m_pszSequence ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetModelAnim( const char *pszName ) { MDLCACHE_CRITICAL_SECTION(); // Get the studio header of the root model. studiohdr_t *pStudioHdr = m_RootMDL.m_MDL.GetStudioHdr(); if ( !pStudioHdr ) return; CStudioHdr studioHdr( pStudioHdr, g_pMDLCache ); int iSequence = ACT_INVALID; iSequence = FindSequenceFromActivity( &studioHdr, pszName ); if ( iSequence == ACT_INVALID ) { iSequence = LookupSequence( &studioHdr, pszName ); } if ( iSequence != ACT_INVALID ) { SetSequence( iSequence ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetMDL( MDLHandle_t handle ) { BaseClass::SetMDL( handle ); SetupModelDefaults(); // Need to invalidate the layout so the panel will adjust is LookAt for the new model. InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetMDL( const char *pMDLName ) { SetSequence( 0 ); BaseClass::SetMDL( pMDLName ); // Need to invalidate the layout so the panel will adjust is LookAt for the new model. // InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::PerformLayout() { BaseClass::PerformLayout(); if ( m_bForcePos ) { ResetCameraPivot(); SetCameraOffset( Vector( 0.0f, 0.0f, 0.0f ) ); SetCameraPositionAndAngles( vec3_origin, vec3_angle ); SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); } // Center and fill the frame with the model? if ( m_bStartFramed ) { Vector vecBoundsMin, vecBoundsMax; if ( GetBoundingBox( vecBoundsMin, vecBoundsMax ) ) { LookAtBounds( vecBoundsMin, vecBoundsMax ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnKeyCodePressed ( vgui::KeyCode code ) { return; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnKeyCodeReleased( vgui::KeyCode code ) { return; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnMousePressed ( vgui::MouseCode code ) { if ( !m_bAllowRotation ) return; RequestFocus(); EnableMouseCapture( true, code ); // Warp the mouse to the center of the screen int width, height; GetSize( width, height ); int x = width / 2; int y = height / 2; int xpos = x; int ypos = y; LocalToScreen( xpos, ypos ); input()->SetCursorPos( xpos, ypos ); m_nManipStartX = xpos; m_nManipStartY = ypos; m_bMousePressed = true; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnMouseReleased( vgui::MouseCode code ) { if ( !m_bAllowRotation ) return; EnableMouseCapture( false ); m_bMousePressed = false; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnCursorMoved( int x, int y ) { if ( !m_bAllowRotation ) return; if ( m_bMousePressed ) { WarpMouse( x, y ); int xpos, ypos; input()->GetCursorPos( xpos, ypos ); // Only want the x delta. float flDelta = xpos - m_nManipStartX; // Apply the delta and rotate the player. m_angPlayer.y += flDelta; if ( m_angPlayer.y > 360.0f ) { m_angPlayer.y = m_angPlayer.y - 360.0f; } else if ( m_angPlayer.y < -360.0f ) { m_angPlayer.y = m_angPlayer.y + 360.0f; } SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnMouseWheeled( int delta ) { return; } //----------------------------------------------------------------------------- // Purpose: Set the camera to a distance that allows the object to fill the model panel. //----------------------------------------------------------------------------- void CBaseModelPanel::LookAtBounds( const Vector &vecBoundsMin, const Vector &vecBoundsMax ) { // Get the model space render bounds. Vector vecMin = vecBoundsMin; Vector vecMax = vecBoundsMax; Vector vecCenter = ( vecMax + vecMin ) * 0.5f; vecMin -= vecCenter; vecMax -= vecCenter; // Get the bounds points and transform them by the desired model panel rotation. Vector aBoundsPoints[8]; aBoundsPoints[0].Init( vecMax.x, vecMax.y, vecMax.z ); aBoundsPoints[1].Init( vecMin.x, vecMax.y, vecMax.z ); aBoundsPoints[2].Init( vecMax.x, vecMin.y, vecMax.z ); aBoundsPoints[3].Init( vecMin.x, vecMin.y, vecMax.z ); aBoundsPoints[4].Init( vecMax.x, vecMax.y, vecMin.z ); aBoundsPoints[5].Init( vecMin.x, vecMax.y, vecMin.z ); aBoundsPoints[6].Init( vecMax.x, vecMin.y, vecMin.z ); aBoundsPoints[7].Init( vecMin.x, vecMin.y, vecMin.z ); // Translated center point (offset from camera center). Vector vecTranslateCenter = -vecCenter; // Build the rotation matrix. matrix3x4_t matRotation; AngleMatrix( m_BMPResData.m_angModelPoseRot, matRotation ); Vector aXFormPoints[8]; for ( int iPoint = 0; iPoint < 8; ++iPoint ) { VectorTransform( aBoundsPoints[iPoint], matRotation, aXFormPoints[iPoint] ); } Vector vecXFormCenter; VectorTransform( -vecTranslateCenter, matRotation, vecXFormCenter ); int w, h; GetSize( w, h ); float flW = (float)w; float flH = (float)h; float flFOVx = DEG2RAD( m_BMPResData.m_flFOV * 0.5f ); float flFOVy = CalcFovY( ( m_BMPResData.m_flFOV * 0.5f ), flW/flH ); flFOVy = DEG2RAD( flFOVy ); float flTanFOVx = tan( flFOVx ); float flTanFOVy = tan( flFOVy ); // Find the max value of x, y, or z Vector2D dist[8]; float flDist = 0.0f; for ( int iPoint = 0; iPoint < 8; ++iPoint ) { float flDistY = fabs( aXFormPoints[iPoint].y / flTanFOVx ) - aXFormPoints[iPoint].x; float flDistZ = fabs( aXFormPoints[iPoint].z / flTanFOVy ) - aXFormPoints[iPoint].x; dist[iPoint].x = flDistY; dist[iPoint].y = flDistZ; float flTestDist = MAX( flDistZ, flDistY ); flDist = MAX( flDist, flTestDist ); } // Screen space points. Vector2D aScreenPoints[8]; Vector aCameraPoints[8]; for ( int iPoint = 0; iPoint < 8; ++iPoint ) { aCameraPoints[iPoint] = aXFormPoints[iPoint]; aCameraPoints[iPoint].x += flDist; aScreenPoints[iPoint].x = aCameraPoints[iPoint].y / ( flTanFOVx * aCameraPoints[iPoint].x ); aScreenPoints[iPoint].y = aCameraPoints[iPoint].z / ( flTanFOVy * aCameraPoints[iPoint].x ); aScreenPoints[iPoint].x = ( aScreenPoints[iPoint].x * 0.5f + 0.5f ) * flW; aScreenPoints[iPoint].y = ( aScreenPoints[iPoint].y * 0.5f + 0.5f ) * flH; } // Find the min/max and center of the 2D bounding box of the object. Vector2D vecScreenMin( 99999.0f, 99999.0f ), vecScreenMax( -99999.0f, -99999.0f ); for ( int iPoint = 0; iPoint < 8; ++iPoint ) { vecScreenMin.x = MIN( vecScreenMin.x, aScreenPoints[iPoint].x ); vecScreenMin.y = MIN( vecScreenMin.y, aScreenPoints[iPoint].y ); vecScreenMax.x = MAX( vecScreenMax.x, aScreenPoints[iPoint].x ); vecScreenMax.y = MAX( vecScreenMax.y, aScreenPoints[iPoint].y ); } // Offset the model to the be the correct distance away from the camera. Vector vecModelPos; vecModelPos.x = flDist - vecXFormCenter.x; vecModelPos.y = -vecXFormCenter.y; vecModelPos.z = -vecXFormCenter.z; SetModelAnglesAndPosition( m_BMPResData.m_angModelPoseRot, vecModelPos ); // Back project to figure out the camera offset to center the model. Vector2D vecPanelCenter( ( flW * 0.5f ), ( flH * 0.5f ) ); Vector2D vecScreenCenter = ( vecScreenMax + vecScreenMin ) * 0.5f; Vector2D vecPanelCenterCamera, vecScreenCenterCamera; vecPanelCenterCamera.x = ( ( vecPanelCenter.x / flW ) * 2.0f ) - 0.5f; vecPanelCenterCamera.y = ( ( vecPanelCenter.y / flH ) * 2.0f ) - 0.5f; vecPanelCenterCamera.x *= ( flTanFOVx * flDist ); vecPanelCenterCamera.y *= ( flTanFOVy * flDist ); vecScreenCenterCamera.x = ( ( vecScreenCenter.x / flW ) * 2.0f ) - 0.5f; vecScreenCenterCamera.y = ( ( vecScreenCenter.y / flH ) * 2.0f ) - 0.5f; vecScreenCenterCamera.x *= ( flTanFOVx * flDist ); vecScreenCenterCamera.y *= ( flTanFOVy * flDist ); Vector2D vecCameraOffset( 0.0f, 0.0f ); vecCameraOffset.x = vecPanelCenterCamera.x - vecScreenCenterCamera.x; vecCameraOffset.y = vecPanelCenterCamera.y - vecScreenCenterCamera.y; // Clear the camera pivot and set position matrix. ResetCameraPivot(); SetCameraOffset( Vector( 0.0f, -vecCameraOffset.x, -vecCameraOffset.y ) ); UpdateCameraTransform(); }