//========= Copyright 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" #include "bone_setup.h" using namespace vgui; extern float GetAutoPlayTime( void ); DECLARE_BUILD_FACTORY( CBaseModelPanel ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseModelPanel::CBaseModelPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) , m_nActiveSequence( ACT_INVALID ) , m_flActiveSequenceDuration( 0.f ) { m_bForcePos = false; m_bMousePressed = false; m_bAllowRotation = false; m_bAllowPitch = false; m_bAllowFullManipulation = false; m_bApplyManipulators = false; m_bForcedCameraPosition = false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseModelPanel::~CBaseModelPanel() { } //----------------------------------------------------------------------------- // Purpose: Load in the model portion of the panel's resource file. //----------------------------------------------------------------------------- void CBaseModelPanel::ApplySettings( KeyValues *inResourceData ) { BaseClass::ApplySettings( inResourceData ); // Set whether we render to texture m_bRenderToTexture = inResourceData->GetBool( "render_texture", true ); m_bUseParticle = inResourceData->GetBool( "use_particle", false ); // 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->GetBool( "allow_rot", false ); m_bAllowPitch = inResourceData->GetBool( "allow_pitch", false ); // Do we allow full manipulation on these panels. m_bAllowFullManipulation = inResourceData->GetBool( "allow_manip", false ); // 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 ); } } SetMouseInputEnabled( m_bAllowFullManipulation || m_bAllowRotation || m_bAllowPitch ); } //----------------------------------------------------------------------------- // 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 ); } } SetupModelDefaults(); } //----------------------------------------------------------------------------- // 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->GetBool( "default" ); 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 ) { // Set the move_x parameter so the run activity works SetPoseParameterByName( "move_x", 1.0f ); // 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; if ( !pszName ) return iIndex; 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 ( !V_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; 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 ); // Do we have an activity or a sequence? int iSequence = ACT_INVALID; if ( m_BMPResData.m_aAnimations[iAnim].m_pszActivity && m_BMPResData.m_aAnimations[iAnim].m_pszActivity[0] ) { iSequence = FindSequenceFromActivity( &studioHdr, m_BMPResData.m_aAnimations[iAnim].m_pszActivity ); } else if ( m_BMPResData.m_aAnimations[iAnim].m_pszSequence && m_BMPResData.m_aAnimations[iAnim].m_pszSequence[0] ) { iSequence = LookupSequence( &studioHdr, m_BMPResData.m_aAnimations[iAnim].m_pszSequence ); } if ( iSequence != ACT_INVALID ) { SetSequence( iSequence, true ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetMDL( MDLHandle_t handle, void *pProxyData ) { MDLCACHE_CRITICAL_SECTION(); studiohdr_t *pHdr = g_pMDLCache->GetStudioHdr( handle ); if ( pHdr ) { // SetMDL will cause the base CMdl code to set our localtoglobal indices if they aren't set. // We set them up here so that they're left alone by that code. CStudioHdr studioHdr( pHdr, g_pMDLCache ); if (studioHdr.numflexcontrollers() > 0 && studioHdr.pFlexcontroller( LocalFlexController_t(0) )->localToGlobal == -1) { for (LocalFlexController_t i = LocalFlexController_t(0); i < studioHdr.numflexcontrollers(); i++) { int j = C_BaseFlex::AddGlobalFlexController( studioHdr.pFlexcontroller( i )->pszName() ); studioHdr.pFlexcontroller( i )->localToGlobal = j; } } } else { handle = MDLHANDLE_INVALID; } // Clear our current sequence SetSequence( ACT_IDLE ); BaseClass::SetMDL( handle, pProxyData ); SetupModelDefaults(); // Need to invalidate the layout so the panel will adjust is LookAt for the new model. InvalidateLayout(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ) { BaseClass::SetModelAnglesAndPosition( angRot, vecPos ); // Cache m_vecPlayerPos = vecPos; m_angPlayer = angRot; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::SetMDL( const char *pMDLName, void *pProxyData ) { BaseClass::SetMDL( pMDLName, pProxyData ); // 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_bForcedCameraPosition ) { return; } if ( m_bAllowFullManipulation ) { // Set this to true if you want to keep the current rotation when changing models or poses const bool bPreserveManipulation = false; // Need to look at the target so we can rotate around it const Vector kVecFocalPoint( 0.0f, 0.0f, 60.0f ); ResetCameraPivot(); SetCameraOffset( -(m_vecPlayerPos + kVecFocalPoint) ); SetCameraPositionAndAngles( kVecFocalPoint, vec3_angle, !bPreserveManipulation ); // We want to move the player to the origin and facing the correct way, // but don't clobber m_angPlayer and m_vecPlayerPos, so use BaseClass. BaseClass::SetModelAnglesAndPosition( m_angPlayer, vec3_origin ); // Once a manual transform has been done we want to apply it if ( m_bApplyManipulators ) { ApplyManipulation(); } else { SyncManipulation(); } return; } 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::OnTick() { // Cycle stuff gets handled in mdlpanel::OnTick, so we want to fix up // what our sequence is before it gets called. // Check if we have a active sequence, and if it's expired and we need // to run our default if ( m_nActiveSequence != ACT_INVALID ) { float flElapsedTime = GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime; if ( flElapsedTime >= m_flActiveSequenceDuration ) { m_nActiveSequence = ACT_INVALID; m_flActiveSequenceDuration = 0.f; SetupModelDefaults(); } } BaseClass::OnTick(); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnKeyCodePressed ( vgui::KeyCode code ) { if ( m_bAllowFullManipulation ) { BaseClass::OnKeyCodePressed( code ); return; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnKeyCodeReleased( vgui::KeyCode code ) { if ( m_bAllowFullManipulation ) { BaseClass::OnKeyCodeReleased( code ); return; } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnMousePressed ( vgui::MouseCode code ) { if ( m_bAllowFullManipulation ) { BaseClass::OnMousePressed( code ); return; } if ( !m_bAllowRotation && !m_bAllowPitch ) return; RequestFocus(); EnableMouseCapture( true, code ); // Save where they clicked input()->GetCursorPosition( m_nClickStartX, m_nClickStartY ); // 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_bAllowFullManipulation ) { BaseClass::OnMouseReleased( code ); return; } if ( !m_bAllowRotation && !m_bAllowPitch ) return; EnableMouseCapture( false ); m_bMousePressed = false; // Restore the cursor to where the clicked input()->SetCursorPos( m_nClickStartX, m_nClickStartY ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnCursorMoved( int x, int y ) { if ( m_bAllowFullManipulation ) { if ( m_pCurrentManip ) { m_bApplyManipulators = true; } BaseClass::OnCursorMoved( x, y ); return; } if ( !m_bAllowRotation && !m_bAllowPitch ) return; if ( m_bMousePressed ) { WarpMouse( x, y ); int xpos, ypos; input()->GetCursorPos( xpos, ypos ); if ( m_bAllowRotation ) { // Only want the x delta. float flDelta = xpos - m_nManipStartX; // Apply the delta and rotate the player. RotateYaw( flDelta ); } if ( m_bAllowPitch ) { // Only want the y delta. float flDelta = ypos - m_nManipStartY; // Apply the delta and rotate the player. RotatePitch( flDelta ); } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::RotateYaw( float flDelta ) { 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::RotatePitch( float flDelta ) { m_angPlayer.x += flDelta; if ( m_angPlayer.x > m_flMaxPitch ) { m_angPlayer.x = m_flMaxPitch; } else if ( m_angPlayer.x < -m_flMaxPitch ) { m_angPlayer.x = -m_flMaxPitch; } SetModelAnglesAndPosition( m_angPlayer, m_vecPlayerPos ); } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- Vector CBaseModelPanel::GetPlayerPos() const { return m_vecPlayerPos; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- QAngle CBaseModelPanel::GetPlayerAngles() const { return m_angPlayer; } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::PlaySequence( const char *pszSequenceName ) { CStudioHdr studioHDR( GetStudioHdr(), g_pMDLCache ); int iSeq = ::LookupSequence( &studioHDR, pszSequenceName ); if ( iSeq != ACT_INVALID ) { m_nActiveSequence = iSeq; m_flActiveSequenceDuration = Studio_Duration( &studioHDR, iSeq, NULL ); SetSequence( m_nActiveSequence, true ); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CBaseModelPanel::OnMouseWheeled( int delta ) { if ( m_bAllowFullManipulation ) { BaseClass::OnMouseWheeled( 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 ); m_vecPlayerPos = 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(); if (m_bAllowRotation || m_bAllowPitch ) { vecCameraOffset.x = 0.0f; } SetCameraOffset( Vector( 0.0f, -vecCameraOffset.x, -vecCameraOffset.y ) ); UpdateCameraTransform(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CBaseModelPanel::particle_data_t::~particle_data_t() { if ( m_pParticleSystem ) { delete m_pParticleSystem; m_pParticleSystem = NULL; } } //----------------------------------------------------------------------------- // Purpose: Allocate particle data //----------------------------------------------------------------------------- void CBaseModelPanel::particle_data_t::UpdateControlPoints( CStudioHdr *pStudioHdr, matrix3x4_t *pWorldMatrix, const CUtlVector< int >& vecAttachments, int iDefaultBone /*= 0*/, const Vector& vecParticleOffset /*= vec3_origin*/ ) { if ( m_pParticleSystem ) { // Update control points which is updating the position of the particles matrix3x4_t matAttachToWorld; Vector vecPosition, vecForward, vecRight, vecUp; if ( vecAttachments.Count() ) { for ( int i = 0; i < vecAttachments.Count(); ++i ) { const mstudioattachment_t& attach = pStudioHdr->pAttachment( vecAttachments[i] ); MatrixMultiply( pWorldMatrix[ attach.localbone ], attach.local, matAttachToWorld ); MatrixVectors( matAttachToWorld, &vecForward, &vecRight, &vecUp ); MatrixPosition( matAttachToWorld, vecPosition ); m_pParticleSystem->SetControlPointOrientation( i, vecForward, vecRight, vecUp ); m_pParticleSystem->SetControlPoint( i, vecPosition + vecParticleOffset ); } } else { matAttachToWorld = pWorldMatrix[iDefaultBone]; MatrixVectors( matAttachToWorld, &vecForward, &vecRight, &vecUp ); MatrixPosition( matAttachToWorld, vecPosition ); m_pParticleSystem->SetControlPointOrientation( 0, vecForward, vecRight, vecUp ); m_pParticleSystem->SetControlPoint( 0, vecPosition + vecParticleOffset ); } } m_bIsUpdateToDate = true; } //----------------------------------------------------------------------------- // Purpose: Allocate particle data //----------------------------------------------------------------------------- CBaseModelPanel::particle_data_t *CBaseModelPanel::CreateParticleData( const char *pszParticleName ) { Assert( m_bUseParticle ); if ( !m_bUseParticle ) return NULL; CParticleCollection *pParticle = g_pParticleSystemMgr->CreateParticleCollection( pszParticleName ); if ( !pParticle ) return NULL; particle_data_t *pData = new particle_data_t; pData->m_bIsUpdateToDate = false; pData->m_pParticleSystem = pParticle; m_particleList.AddToTail( pData ); return pData; } //----------------------------------------------------------------------------- // Purpose: remove and delete particle data //----------------------------------------------------------------------------- bool CBaseModelPanel::SafeDeleteParticleData( particle_data_t **pData ) { if ( !m_bUseParticle ) return false; if ( *pData ) { FOR_EACH_VEC( m_particleList, i ) { if ( *pData == m_particleList[i] ) { delete *pData; *pData = NULL; m_particleList.FastRemove( i ); return true; } } } return false; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::PrePaint3D( IMatRenderContext *pRenderContext ) { if ( !m_bUseParticle ) return; // mark all effects need to be updated FOR_EACH_VEC( m_particleList, i ) { m_particleList[i]->m_bIsUpdateToDate = false; } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CBaseModelPanel::PostPaint3D( IMatRenderContext *pRenderContext ) { if ( !m_bUseParticle ) return; // This needs calling to reset various counters. g_pParticleSystemMgr->SetLastSimulationTime( gpGlobals->curtime ); // Render Particles pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PushMatrix(); pRenderContext->LoadIdentity( ); FOR_EACH_VEC( m_particleList, i ) { if ( m_particleList[i]->m_bIsUpdateToDate ) { m_particleList[i]->m_pParticleSystem->Simulate( gpGlobals->frametime, false ); m_particleList[i]->m_pParticleSystem->Render( pRenderContext ); m_particleList[i]->m_bIsUpdateToDate = false; } } pRenderContext->MatrixMode( MATERIAL_MODEL ); pRenderContext->PopMatrix(); }