diff --git a/src/game/client/c_point_commentary_node.cpp b/src/game/client/c_point_commentary_node.cpp index 527d5ca0d..19131424c 100644 --- a/src/game/client/c_point_commentary_node.cpp +++ b/src/game/client/c_point_commentary_node.cpp @@ -441,10 +441,10 @@ void CHudCommentary::Paint() // Draw the speaker names // Get our scheme and font information vgui::HScheme scheme = vgui::scheme()->GetScheme( "ClientScheme" ); - vgui::HFont hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault" ); + vgui::HFont hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "CommentaryDefault", true ); if ( !hFont ) { - hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default" ); + hFont = vgui::scheme()->GetIScheme(scheme)->GetFont( "Default", true ); } vgui::surface()->DrawSetTextFont( hFont ); vgui::surface()->DrawSetTextColor( clr ); diff --git a/src/game/client/clientmode_shared.cpp b/src/game/client/clientmode_shared.cpp index 7fd78629a..54933d249 100644 --- a/src/game/client/clientmode_shared.cpp +++ b/src/game/client/clientmode_shared.cpp @@ -837,23 +837,26 @@ void ClientModeShared::StartMessageMode( int iMessageModeType ) } #if defined( TF_CLIENT_DLL ) - bool bSuspensionInMatch = GTFGCClientSystem() && GTFGCClientSystem()->BHaveChatSuspensionInCurrentMatch(); - if ( !cl_enable_text_chat.GetBool() || bSuspensionInMatch ) + if ( iMessageModeType == MM_SAY || iMessageModeType == MM_SAY_TEAM ) { - CBaseHudChat *pHUDChat = ( CBaseHudChat * ) GET_HUDELEMENT( CHudChat ); - if ( pHUDChat ) + bool bSuspensionInMatch = GTFGCClientSystem() && GTFGCClientSystem()->BHaveChatSuspensionInCurrentMatch(); + if ( !cl_enable_text_chat.GetBool() || bSuspensionInMatch ) { - const char *pszReason = "#TF_Chat_Disabled"; - if ( bSuspensionInMatch ) + CBaseHudChat *pHUDChat = ( CBaseHudChat * ) GET_HUDELEMENT( CHudChat ); + if ( pHUDChat ) { - pszReason = "#TF_Chat_Unavailable"; - } + const char *pszReason = "#TF_Chat_Disabled"; + if ( bSuspensionInMatch ) + { + pszReason = "#TF_Chat_Unavailable"; + } - char szLocalized[100]; - g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find( pszReason ), szLocalized, sizeof( szLocalized ) ); - pHUDChat->ChatPrintf( 0, CHAT_FILTER_NONE, "%s ", szLocalized ); + char szLocalized[100]; + g_pVGuiLocalize->ConvertUnicodeToANSI( g_pVGuiLocalize->Find( pszReason ), szLocalized, sizeof( szLocalized ) ); + pHUDChat->ChatPrintf( 0, CHAT_FILTER_NONE, "%s ", szLocalized ); + } + return; } - return; } #endif // TF_CLIENT_DLL diff --git a/src/game/client/hud_closecaption.cpp b/src/game/client/hud_closecaption.cpp index 2d9ca9da4..311cd4c02 100644 --- a/src/game/client/hud_closecaption.cpp +++ b/src/game/client/hud_closecaption.cpp @@ -1596,7 +1596,7 @@ void CHudCloseCaption::CreateFonts( void ) { vgui::IScheme *pScheme = vgui::scheme()->GetIScheme( GetScheme() ); - m_hFonts[CCFONT_NORMAL] = pScheme->GetFont( "CloseCaption_Normal", true ); + m_hFonts[CCFONT_NORMAL] = pScheme->GetFont( "CloseCaption", true ); if ( IsPC() ) { diff --git a/src/game/client/tf/tf_hud_mann_vs_machine_status.cpp b/src/game/client/tf/tf_hud_mann_vs_machine_status.cpp index d8fa9e011..c43ee87c7 100644 --- a/src/game/client/tf/tf_hud_mann_vs_machine_status.cpp +++ b/src/game/client/tf/tf_hud_mann_vs_machine_status.cpp @@ -851,7 +851,14 @@ void CWaveStatusPanel::UpdateEnemyCounts( void ) if ( pPanel->m_pEnemyCountImageBG ) { - pPanel->m_pEnemyCountImageBG->SetBgColor( m_clrNormal ); + if ( support[i].iFlags & MVM_CLASS_FLAG_MINIBOSS ) + { + pPanel->m_pEnemyCountImageBG->SetBgColor( m_clrMiniBoss ); + } + else + { + pPanel->m_pEnemyCountImageBG->SetBgColor( m_clrNormal ); + } } if ( pPanel->m_pEnemyCountCritBG ) diff --git a/src/game/client/tf/tf_hud_scope.cpp b/src/game/client/tf/tf_hud_scope.cpp index fb0a464e0..4ba0957ca 100644 --- a/src/game/client/tf/tf_hud_scope.cpp +++ b/src/game/client/tf/tf_hud_scope.cpp @@ -525,4 +525,4 @@ void CHudScope::Paint( void ) if( bDisableClipping ) g_pMatSystemSurface->DisableClipping( false ); -} +} \ No newline at end of file diff --git a/src/game/client/voice_menu.cpp b/src/game/client/voice_menu.cpp index 0a6d37862..c58363496 100644 --- a/src/game/client/voice_menu.cpp +++ b/src/game/client/voice_menu.cpp @@ -17,6 +17,10 @@ static int g_ActiveVoiceMenu = 0; +#if defined( TF_CLIENT_DLL ) +extern ConVar tf_voice_command_suspension_mode; +#endif + void OpenVoiceMenu( int index ) { // do not show the menu if the player is dead or is an observer @@ -28,7 +32,7 @@ void OpenVoiceMenu( int index ) return; #if defined ( TF_CLIENT_DLL ) - if ( GTFGCClientSystem() && GTFGCClientSystem()->BHaveChatSuspensionInCurrentMatch() ) + if ( GTFGCClientSystem() && GTFGCClientSystem()->BHaveChatSuspensionInCurrentMatch() && tf_voice_command_suspension_mode.GetInt() == 1 ) { CBaseHudChat *pHUDChat = ( CBaseHudChat * ) GET_HUDELEMENT( CHudChat ); if ( pHUDChat ) diff --git a/src/game/server/tf/bot/tf_bot.cpp b/src/game/server/tf/bot/tf_bot.cpp index d4b6810c3..ae235092c 100644 --- a/src/game/server/tf/bot/tf_bot.cpp +++ b/src/game/server/tf/bot/tf_bot.cpp @@ -704,9 +704,9 @@ DEFINE_SCRIPTFUNC( IsOnAnyMission, "Return true if this bot has a current missio DEFINE_SCRIPTFUNC_WRAPPED( SetMissionTarget, "Set this bot's mission target to the given entity" ) DEFINE_SCRIPTFUNC_WRAPPED( GetMissionTarget, "Get this bot's current mission target" ) -DEFINE_SCRIPTFUNC( SetBehaviorFlag, "Set the given behavior flag(s) for this bot" ) -DEFINE_SCRIPTFUNC( ClearBehaviorFlag, "Clear the given behavior flag(s) for this bot" ) -DEFINE_SCRIPTFUNC( IsBehaviorFlagSet, "Return true if the given behavior flag(s) are set for this bot" ) +DEFINE_SCRIPTFUNC_WRAPPED( SetBehaviorFlag, "Set the given behavior flag(s) for this bot" ) +DEFINE_SCRIPTFUNC_WRAPPED( ClearBehaviorFlag, "Clear the given behavior flag(s) for this bot" ) +DEFINE_SCRIPTFUNC_WRAPPED( IsBehaviorFlagSet, "Return true if the given behavior flag(s) are set for this bot" ) DEFINE_SCRIPTFUNC_WRAPPED( SetActionPoint, "Set the given action point for this bot" ) DEFINE_SCRIPTFUNC_WRAPPED( GetActionPoint, "Get the given action point for this bot" ) diff --git a/src/game/server/tf/bot/tf_bot.h b/src/game/server/tf/bot/tf_bot.h index 2d79db6d1..cce41e3a4 100644 --- a/src/game/server/tf/bot/tf_bot.h +++ b/src/game/server/tf/bot/tf_bot.h @@ -331,6 +331,9 @@ public: void SetBehaviorFlag( unsigned int flags ); void ClearBehaviorFlag( unsigned int flags ); bool IsBehaviorFlagSet( unsigned int flags ) const; + void ScriptSetBehaviorFlag( int flags ) { this->SetBehaviorFlag( (unsigned int)flags ); } + void ScriptClearBehaviorFlag( int flags ) { this->ClearBehaviorFlag( (unsigned int)flags ); } + bool ScriptIsBehaviorFlagSet( int flags ) const { return this->IsBehaviorFlagSet( (unsigned int)flags ); } bool FindSplashTarget( CBaseEntity *target, float maxSplashRadius, Vector *splashTarget ) const; @@ -358,11 +361,11 @@ public: CBaseEntity *GetMissionTarget( void ) const; void SetMissionString( CUtlString string ); CUtlString *GetMissionString( void ); - void ScriptSetMission( unsigned int mission, bool resetBehaviorSystem = true ) { this->SetMission( (MissionType)mission, resetBehaviorSystem ); } - void ScriptSetPrevMission( unsigned int mission ) { this->SetPrevMission( (MissionType)mission ); } - unsigned int ScriptGetMission( void ) const { return (unsigned int)this->GetMission(); } - unsigned int ScriptGetPrevMission( void ) const { return (unsigned int)this->GetPrevMission(); } - bool ScriptHasMission( unsigned int mission ) const { return this->HasMission( (MissionType)mission ); } + void ScriptSetMission( int mission, bool resetBehaviorSystem = true ) { this->SetMission( (MissionType)mission, resetBehaviorSystem ); } + void ScriptSetPrevMission( int mission ) { this->SetPrevMission( (MissionType)mission ); } + int ScriptGetMission( void ) const { return (int)this->GetMission(); } + int ScriptGetPrevMission( void ) const { return (int)this->GetPrevMission(); } + bool ScriptHasMission( int mission ) const { return this->HasMission( (MissionType)mission ); } void ScriptSetMissionTarget( HSCRIPT hTarget ) { this->SetMissionTarget( ToEnt( hTarget ) ); } HSCRIPT ScriptGetMissionTarget( void ) const { return ToHScript( this->GetMissionTarget() ); } diff --git a/src/game/server/tf/entity_currencypack.cpp b/src/game/server/tf/entity_currencypack.cpp index f9a18b51a..480f9d503 100644 --- a/src/game/server/tf/entity_currencypack.cpp +++ b/src/game/server/tf/entity_currencypack.cpp @@ -210,15 +210,17 @@ void CCurrencyPack::ComeToRest( void ) } } } + // Or a func_respawnroom (robots can drop money in their own spawn) for ( int i = 0; i < IFuncRespawnRoomAutoList::AutoList().Count(); i++ ) { - CFuncRespawnRoom *pRespawnRoom = static_cast( IFuncRespawnRoomAutoList::AutoList()[i] ); + CFuncRespawnRoom *pRespawnRoom = static_cast( IFuncRespawnRoomAutoList::AutoList()[ i ] ); Vector vecMins, vecMaxs; pRespawnRoom->GetCollideable()->WorldSpaceSurroundingBounds( &vecMins, &vecMaxs ); if ( IsPointInBox( GetCollideable()->GetCollisionOrigin(), vecMins, vecMaxs ) ) { TFGameRules()->DistributeCurrencyAmount( m_nAmount ); + m_bTouched = true; UTIL_Remove( this ); } diff --git a/src/game/server/tf/tf_player.cpp b/src/game/server/tf/tf_player.cpp index b83535ce5..8e288e041 100644 --- a/src/game/server/tf/tf_player.cpp +++ b/src/game/server/tf/tf_player.cpp @@ -277,6 +277,7 @@ extern ConVar sv_vote_allow_spectators; ConVar sv_vote_late_join_time( "sv_vote_late_join_time", "90", FCVAR_NONE, "Grace period after the match starts before players who join the match receive a vote-creation cooldown" ); ConVar sv_vote_late_join_cooldown( "sv_vote_late_join_cooldown", "300", FCVAR_NONE, "Length of the vote-creation cooldown when joining the server after the grace period has expired" ); +extern ConVar tf_voice_command_suspension_mode; extern ConVar tf_feign_death_duration; extern ConVar spec_freeze_time; extern ConVar spec_freeze_traveltime; @@ -19993,7 +19994,20 @@ bool CTFPlayer::ShouldShowVoiceSubtitleToEnemy( void ) //----------------------------------------------------------------------------- bool CTFPlayer::CanSpeakVoiceCommand( void ) { - return ( gpGlobals->curtime > m_flNextVoiceCommandTime ); + if ( tf_voice_command_suspension_mode.GetInt() == 1 ) + return false; + + if ( gpGlobals->curtime <= m_flNextVoiceCommandTime ) + return false; + + // misyl: New F2P voice command rate-limiting path. + if ( BHaveChatSuspensionInCurrentMatch() && tf_voice_command_suspension_mode.GetInt() == 2 ) + { + if ( !m_RateLimitedVoiceCommandTokenBucket.BTakeToken( gpGlobals->curtime ) ) + return false; + } + + return true; } //----------------------------------------------------------------------------- diff --git a/src/game/server/tf/tf_player.h b/src/game/server/tf/tf_player.h index 87620ffed..224ddbeb0 100644 --- a/src/game/server/tf/tf_player.h +++ b/src/game/server/tf/tf_player.h @@ -66,6 +66,46 @@ enum EAmmoSource kAmmoSource_ResourceMeter, // it regenerated after a cooldown }; +extern ConVar tf_voice_command_suspension_rate_limit_bucket_count; +extern ConVar tf_voice_command_suspension_rate_limit_bucket_refill_rate; + +class CVoiceCommandBucketSizer +{ +public: + int GetBucketSize() const { return tf_voice_command_suspension_rate_limit_bucket_count.GetInt(); } + float GetBucketRefillRate() const { return tf_voice_command_suspension_rate_limit_bucket_refill_rate.GetFloat(); } +}; + +template +class CRateLimitingTokenBucket : public TBucketSizer +{ +public: + CRateLimitingTokenBucket() + : m_nBucket( this->GetBucketSize() ) + { + } + + bool BTakeToken( float flNow ) + { + // misyl: This token bucket doesn't go negative, so you don't ever dig yourself into a hole by spamming. + // You might want that if you use this class, feel free to add something to the BucketSizer. + + int nNewBucket = MIN( m_nBucket + ( flNow - m_flLastTokenTaken ) / this->GetBucketRefillRate(), this->GetBucketSize() ) - 1; + if ( nNewBucket <= 0 ) + { + return false; + } + + m_nBucket = nNewBucket; + m_flLastTokenTaken = flNow; + + return true; + } +private: + float m_flLastTokenTaken = 0.0f; + int m_nBucket = 0; +}; + //============================================================================= // // TF Player @@ -483,6 +523,8 @@ public: float m_flNextVoiceCommandTime; int m_iVoiceSpamCounter; + CRateLimitingTokenBucket m_RateLimitedVoiceCommandTokenBucket; + float m_flNextSpeakWeaponFire; virtual int CalculateTeamBalanceScore( void ); diff --git a/src/game/shared/saverestore.cpp b/src/game/shared/saverestore.cpp index 72a9eb516..b78ca6144 100644 --- a/src/game/shared/saverestore.cpp +++ b/src/game/shared/saverestore.cpp @@ -333,6 +333,7 @@ void CSave::Log( const char *pName, fieldtype_t fieldType, void *value, int coun byte *pColor = &pValue[iCount*4]; Q_snprintf( szTempBuf, sizeof( szTempBuf ), "(%d %d %d %d)", ( int )pColor[0], ( int )pColor[1], ( int )pColor[2], ( int )pColor[3] ); Q_strncat( szBuf, szTempBuf, sizeof( szTempBuf ), COPY_ALL_CHARACTERS ); + break; } case FIELD_EMBEDDED: case FIELD_CUSTOM: diff --git a/src/game/shared/tf/tf_gamemovement.cpp b/src/game/shared/tf/tf_gamemovement.cpp index 91e1c0c81..476601634 100644 --- a/src/game/shared/tf/tf_gamemovement.cpp +++ b/src/game/shared/tf/tf_gamemovement.cpp @@ -1377,7 +1377,7 @@ int CTFGameMovement::CheckStuck( void ) m_pTFPlayer->GetTeam()->GetName(), m_pTFPlayer->GetAbsOrigin().x, m_pTFPlayer->GetAbsOrigin().y, m_pTFPlayer->GetAbsOrigin().z ); - m_pTFPlayer->CommitSuicide(false, true); + m_pTFPlayer->CommitSuicide( false, true ); } else { diff --git a/src/game/shared/tf/tf_gamerules.cpp b/src/game/shared/tf/tf_gamerules.cpp index 76dd4fcae..fd43efd91 100644 --- a/src/game/shared/tf/tf_gamerules.cpp +++ b/src/game/shared/tf/tf_gamerules.cpp @@ -1107,7 +1107,13 @@ ConVar tf_competitive_required_late_join_confirm_timeout( "tf_competitive_requir ConVar tf_gamemode_community ( "tf_gamemode_community", "0", FCVAR_REPLICATED | FCVAR_NOTIFY | FCVAR_DEVELOPMENTONLY ); +ConVar tf_voice_command_suspension_mode( "tf_voice_command_suspension_mode", "2", FCVAR_REPLICATED, "0 = None | 1 = No Voice Commands | 2 = Rate Limited" ); + #ifdef GAME_DLL + +ConVar tf_voice_command_suspension_rate_limit_bucket_count( "tf_voice_command_suspension_rate_limit_bucket_count", "5" ); // Bucket size of 5. +ConVar tf_voice_command_suspension_rate_limit_bucket_refill_rate( "tf_voice_command_suspension_rate_limit_bucket_refill_rate", "6" ); // 6s + void cc_powerup_mode( IConVar *pConVar, const char *pOldString, float flOldValue ) { ConVarRef var( pConVar ); @@ -10127,7 +10133,12 @@ VoiceCommandMenuItem_t *CTFGameRules::VoiceCommand( CBaseMultiplayerPlayer *pPla if ( pTFPlayer ) { if ( pTFPlayer->BHaveChatSuspensionInCurrentMatch() ) - return NULL; + { + if ( tf_voice_command_suspension_mode.GetInt() == 1 ) + { + return NULL; + } + } if ( pTFPlayer->m_Shared.InCond( TF_COND_HALLOWEEN_GHOST_MODE ) ) { diff --git a/src/game/shared/tf/tf_item_powerup_bottle.cpp b/src/game/shared/tf/tf_item_powerup_bottle.cpp index 936a7741a..9402131ad 100644 --- a/src/game/shared/tf/tf_item_powerup_bottle.cpp +++ b/src/game/shared/tf/tf_item_powerup_bottle.cpp @@ -279,7 +279,7 @@ void CTFPowerupBottle::ReapplyProvision( void ) // Refill weapon clips for ( int i = 0; i < MAX_WEAPONS; i++ ) { - CTFWeaponBase *pWeapon = dynamic_cast(pTFPlayer->GetWeapon(i)); + CTFWeaponBase *pWeapon = dynamic_cast( pTFPlayer->GetWeapon( i ) ); if ( !pWeapon ) continue; @@ -287,8 +287,8 @@ void CTFPowerupBottle::ReapplyProvision( void ) if ( TFGameRules() && TFGameRules()->IsMannVsMachineMode() ) { if ( ( pWeapon->UsesPrimaryAmmo() && !pWeapon->HasPrimaryAmmo() ) || - ( pWeapon->UsesSecondaryAmmo() && !pWeapon->HasSecondaryAmmo() ) || - ( pWeapon->IsEnergyWeapon() && !pWeapon->Energy_HasEnergy() ) ) + ( pWeapon->UsesSecondaryAmmo() && !pWeapon->HasSecondaryAmmo() ) || + ( pWeapon->IsEnergyWeapon() && !pWeapon->Energy_HasEnergy() ) ) { pTFPlayer->AwardAchievement( ACHIEVEMENT_TF_MVM_USE_AMMO_BOTTLE ); } @@ -298,7 +298,7 @@ void CTFPowerupBottle::ReapplyProvision( void ) if ( iShareBottle && pHealTarget ) { - CTFWeaponBase *pPatientWeapon = dynamic_cast(pHealTarget->GetWeapon(i)); + CTFWeaponBase *pPatientWeapon = dynamic_cast( pHealTarget->GetWeapon( i ) ); if ( !pPatientWeapon ) continue; diff --git a/src/game/shared/tf/tf_player_shared.cpp b/src/game/shared/tf/tf_player_shared.cpp index 2486e4ae7..abe67cf9c 100644 --- a/src/game/shared/tf/tf_player_shared.cpp +++ b/src/game/shared/tf/tf_player_shared.cpp @@ -9698,7 +9698,9 @@ void CTFPlayerShared::StunPlayer( float flTime, float flReductionAmount, int iSt float flRemapAmount = RemapValClamped( flReductionAmount, 0.0, 1.0, 0, 255 ); +#ifdef GAME_DLL int iOldStunFlags = GetStunFlags(); +#endif // Already stunned bool bStomp = false; diff --git a/src/game/shared/tf/tf_weapon_flamethrower.cpp b/src/game/shared/tf/tf_weapon_flamethrower.cpp index 28e515553..65810b222 100644 --- a/src/game/shared/tf/tf_weapon_flamethrower.cpp +++ b/src/game/shared/tf/tf_weapon_flamethrower.cpp @@ -2110,7 +2110,7 @@ bool CTFFlameThrower::EffectMeterShouldFlash( void ) //----------------------------------------------------------------------------- Vector CTFFlameThrower::GetMuzzlePosHelper( bool bVisualPos ) { - Vector vecMuzzlePos; + Vector vecMuzzlePos = vec3_origin; CTFPlayer *pOwner = GetTFPlayerOwner(); if ( pOwner ) { diff --git a/src/game/shared/tf/tf_weaponbase.cpp b/src/game/shared/tf/tf_weaponbase.cpp index 4b7dd3f4c..d6ca76332 100644 --- a/src/game/shared/tf/tf_weaponbase.cpp +++ b/src/game/shared/tf/tf_weaponbase.cpp @@ -424,11 +424,14 @@ void CTFWeaponBase::Activate( void ) GiveDefaultAmmo(); } -void CTFWeaponBase::GiveDefaultAmmo(void) +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTFWeaponBase::GiveDefaultAmmo( void ) { BaseClass::GiveDefaultAmmo(); - if (IsEnergyWeapon()) + if ( IsEnergyWeapon() ) { m_flEnergy = Energy_GetMaxEnergy(); } diff --git a/src/lib/common/x64/2015/release/bzip2.lib b/src/lib/common/x64/2015/release/bzip2.lib index f4dae722c..8c23813fb 100644 Binary files a/src/lib/common/x64/2015/release/bzip2.lib and b/src/lib/common/x64/2015/release/bzip2.lib differ diff --git a/src/lib/common/x64/2015/release/jpeglib.lib b/src/lib/common/x64/2015/release/jpeglib.lib index 90565dc7d..f904fba85 100644 Binary files a/src/lib/common/x64/2015/release/jpeglib.lib and b/src/lib/common/x64/2015/release/jpeglib.lib differ diff --git a/src/lib/common/x64/2015/release/libspng.lib b/src/lib/common/x64/2015/release/libspng.lib index 5c0c09201..a3bd8b319 100644 Binary files a/src/lib/common/x64/2015/release/libspng.lib and b/src/lib/common/x64/2015/release/libspng.lib differ diff --git a/src/lib/common/x64/2015/release/lzma.lib b/src/lib/common/x64/2015/release/lzma.lib index 85fb39b63..c18cabc9d 100644 Binary files a/src/lib/common/x64/2015/release/lzma.lib and b/src/lib/common/x64/2015/release/lzma.lib differ diff --git a/src/lib/common/x64/2015/release/replay_common.lib b/src/lib/common/x64/2015/release/replay_common.lib index 7e7bf2176..dfe9800ca 100644 Binary files a/src/lib/common/x64/2015/release/replay_common.lib and b/src/lib/common/x64/2015/release/replay_common.lib differ diff --git a/src/lib/public/linux64/appframework.a b/src/lib/public/linux64/appframework.a index 43bcf5c84..e62b04a23 100644 Binary files a/src/lib/public/linux64/appframework.a and b/src/lib/public/linux64/appframework.a differ diff --git a/src/lib/public/linux64/gcsdk_sdk.a b/src/lib/public/linux64/gcsdk_sdk.a index 5db1cf044..21219f1a0 100644 Binary files a/src/lib/public/linux64/gcsdk_sdk.a and b/src/lib/public/linux64/gcsdk_sdk.a differ diff --git a/src/lib/public/linux64/matsys_controls.a b/src/lib/public/linux64/matsys_controls.a deleted file mode 100644 index 6f397f1b3..000000000 Binary files a/src/lib/public/linux64/matsys_controls.a and /dev/null differ diff --git a/src/lib/public/x64/appframework.lib b/src/lib/public/x64/appframework.lib index 3f21cbd3b..f734b3209 100644 Binary files a/src/lib/public/x64/appframework.lib and b/src/lib/public/x64/appframework.lib differ diff --git a/src/lib/public/x64/bitmap.lib b/src/lib/public/x64/bitmap.lib index 1534065c4..e89ea18a6 100644 Binary files a/src/lib/public/x64/bitmap.lib and b/src/lib/public/x64/bitmap.lib differ diff --git a/src/lib/public/x64/choreoobjects.lib b/src/lib/public/x64/choreoobjects.lib index 44a2cb42b..eb962a537 100644 Binary files a/src/lib/public/x64/choreoobjects.lib and b/src/lib/public/x64/choreoobjects.lib differ diff --git a/src/lib/public/x64/datamodel.lib b/src/lib/public/x64/datamodel.lib index 17f4105cb..1ce43bfc2 100644 Binary files a/src/lib/public/x64/datamodel.lib and b/src/lib/public/x64/datamodel.lib differ diff --git a/src/lib/public/x64/dmxloader.lib b/src/lib/public/x64/dmxloader.lib index 71c0c7142..0071b274f 100644 Binary files a/src/lib/public/x64/dmxloader.lib and b/src/lib/public/x64/dmxloader.lib differ diff --git a/src/lib/public/x64/gcsdk_sdk.lib b/src/lib/public/x64/gcsdk_sdk.lib index d886d0e84..91c873511 100644 Binary files a/src/lib/public/x64/gcsdk_sdk.lib and b/src/lib/public/x64/gcsdk_sdk.lib differ diff --git a/src/lib/public/x64/libz.lib b/src/lib/public/x64/libz.lib index 126668a39..26327f81f 100644 Binary files a/src/lib/public/x64/libz.lib and b/src/lib/public/x64/libz.lib differ diff --git a/src/lib/public/x64/matsys_controls.lib b/src/lib/public/x64/matsys_controls.lib deleted file mode 100644 index 99e35f82b..000000000 Binary files a/src/lib/public/x64/matsys_controls.lib and /dev/null differ diff --git a/src/lib/public/x64/particles.lib b/src/lib/public/x64/particles.lib index e85d3fca7..62d9179a3 100644 Binary files a/src/lib/public/x64/particles.lib and b/src/lib/public/x64/particles.lib differ diff --git a/src/lib/public/x64/shaderlib.lib b/src/lib/public/x64/shaderlib.lib index 11c8edb22..ec434bb1f 100644 Binary files a/src/lib/public/x64/shaderlib.lib and b/src/lib/public/x64/shaderlib.lib differ diff --git a/src/lib/public/x64/tier2.lib b/src/lib/public/x64/tier2.lib index ca099d394..ee8405637 100644 Binary files a/src/lib/public/x64/tier2.lib and b/src/lib/public/x64/tier2.lib differ diff --git a/src/lib/public/x64/tier3.lib b/src/lib/public/x64/tier3.lib index 5ead18781..82c84d251 100644 Binary files a/src/lib/public/x64/tier3.lib and b/src/lib/public/x64/tier3.lib differ diff --git a/src/lib/public/x64/vpklib.lib b/src/lib/public/x64/vpklib.lib index 7eeff88e8..471e7c258 100644 Binary files a/src/lib/public/x64/vpklib.lib and b/src/lib/public/x64/vpklib.lib differ diff --git a/src/lib/public/x64/vtf.lib b/src/lib/public/x64/vtf.lib index b797e77b0..1230fb33a 100644 Binary files a/src/lib/public/x64/vtf.lib and b/src/lib/public/x64/vtf.lib differ diff --git a/src/public/const.h b/src/public/const.h index d66e8ea0d..84f28527d 100644 --- a/src/public/const.h +++ b/src/public/const.h @@ -121,7 +121,7 @@ #define FL_INWATER (1<<9) // In water // NOTE if you move things up, make sure to change this value -#define PLAYER_FLAG_BITS 10 +#define PLAYER_FLAG_BITS 32 #define FL_FLY (1<<10) // Changes the SV_Movestep() behavior to not need to be on ground #define FL_SWIM (1<<11) // Changes the SV_Movestep() behavior to not need to be on ground (but stay in water) diff --git a/src/public/gcsdk/msgprotobuf.h b/src/public/gcsdk/msgprotobuf.h index 06a806993..21b8526b5 100644 --- a/src/public/gcsdk/msgprotobuf.h +++ b/src/public/gcsdk/msgprotobuf.h @@ -101,6 +101,7 @@ public: virtual ~CProtoBufMsgBase(); + bool BIsInitialized() const; bool InitFromPacket( IMsgNetPacket * pNetPacket ); bool BAsyncSend( IProtoBufSendHandler & pSender ) const; bool BAsyncSendWithPreSerializedBody( IProtoBufSendHandler & pSender, const byte *pubBody, uint32 cubBody ) const; diff --git a/src/vgui2/matsys_controls/QCGenerator.cpp b/src/vgui2/matsys_controls/QCGenerator.cpp new file mode 100644 index 000000000..5a7d2be46 --- /dev/null +++ b/src/vgui2/matsys_controls/QCGenerator.cpp @@ -0,0 +1,727 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#if defined(WIN32) && !defined( _X360 ) +#include +#endif +#include "filesystem.h" +#include "filesystem_init.h" +#include "appframework/IAppSystemGroup.h" +#include "appframework/IAppSystem.h" +#include "appframework/AppFramework.h" +#include "filesystem_helpers.h" + +#include "matsys_controls/QCGenerator.h" +#include "tier1/KeyValues.h" +#include "tier2/vconfig.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/FileOpenDialog.h" +#include "vgui_controls/DirectorySelectDialog.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/MessageBox.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "vgui/Cursor.h" +#include "vgui_controls/KeyBoardEditorDialog.h" + +#if defined( _X360 ) +#include "xbox/xbox_win32stubs.h" +#endif + +using namespace vgui; + +#define MAX_KEYVALUE 1024 + + +//----------------------------------------------------------------------------- +// Purpose: returns a pointer to the 'count' occurence of a character from the end of a string +// returns 0 of there aren't 'count' number of the character in the string +//----------------------------------------------------------------------------- +char *strrchrcount(char *string, int character, int count ) +{ + int j = count; + int numChars = V_strlen( string ); + for( int i = numChars; i > 0; i-- ) + { + if( string[i-1] == character ) + { + j--; + } + if( j == 0 ) + { + return string + i-1; + } + } + return 0; +} + + + +class CModalPreserveMessageBox : public vgui::MessageBox +{ +public: + CModalPreserveMessageBox(const char *title, const char *text, vgui::Panel *parent) + : vgui::MessageBox( title, text, parent ) + { + m_PrevAppFocusPanel = vgui::input()->GetAppModalSurface(); + } + + ~CModalPreserveMessageBox() + { + vgui::input()->SetAppModalSurface( m_PrevAppFocusPanel ); + } + + +public: + vgui::VPANEL m_PrevAppFocusPanel; +}; + +void VGUIMessageBox( vgui::Panel *pParent, const char *pTitle, const char *pMsg, ... ) +{ + char msg[4096]; + va_list marker; + va_start( marker, pMsg ); + Q_vsnprintf( msg, sizeof( msg ), pMsg, marker ); + va_end( marker ); + + vgui::MessageBox *dlg = new CModalPreserveMessageBox( pTitle, msg, pParent ); + dlg->DoModal(); + dlg->Activate(); + dlg->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Places all the info from the vgui controls into the QCInfo struct +//----------------------------------------------------------------------------- +void QCInfo::SyncFromControls() +{ + char tempText[MAX_PATH]; + + vgui::Panel *pTargetField = pQCGenerator->FindChildByName( "staticPropCheck" ); + bStaticProp = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "mostlyOpaqueCheck" ); + bMostlyOpaque = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "disableCollisionsCheck" ); + bDisableCollision = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "referencePhysicsCheck" ); + bReferenceAsPhys = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "concaveCheck" ); + bConcave = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "automassCheck" ); + bAutomass = ((CheckButton *)pTargetField)->IsSelected(); + + pTargetField = pQCGenerator->FindChildByName( "massField" ); + ((TextEntry *)pTargetField)->GetText(tempText, MAX_PATH); + fMass = atof(tempText); + + pTargetField = pQCGenerator->FindChildByName( "scaleField" ); + ((TextEntry *)pTargetField)->GetText(tempText, MAX_PATH); + fScale = atof(tempText); + + pTargetField = pQCGenerator->FindChildByName( "collisionSMDField" ); + ((TextEntry *)pTargetField)->GetText( tempText, MAX_PATH ); + V_strcpy_safe( pszCollisionPath, tempText ); + + pTargetField = pQCGenerator->FindChildByName( "surfacePropertyDropDown" ); + ((ComboBox *)pTargetField)->GetText( tempText, MAX_PATH ); + V_strcpy_safe( pszSurfaceProperty, tempText ); + + pTargetField = pQCGenerator->FindChildByName( "materialsField" ); + ((TextEntry *)pTargetField)->GetText( tempText, MAX_PATH ); + V_strcpy_safe( pszMaterialPath, tempText ); + + LODs.RemoveAll(); + pTargetField = pQCGenerator->FindChildByName( "LODList" ); + int numLOD = ((ListPanel *)pTargetField)->GetItemCount(); + for ( int i = 0; i < numLOD; i++ ) + { + KeyValues *key = ((ListPanel *)pTargetField)->GetItem( i ); + LODInfo newLOD; + + V_strcpy_safe( newLOD.pszFilename, key->GetString( "SMD" ) ); + newLOD.iLOD = key->GetInt( "LOD" ); + LODs.AddToTail( newLOD ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: Called during intialization to setup the initial state of the VGUI controls +//----------------------------------------------------------------------------- +void QCInfo::SyncToControls() +{ + char tempText[MAX_PATH]; + + vgui::Panel *pTargetField = pQCGenerator->FindChildByName( "staticPropCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bStaticProp ); + + pTargetField = pQCGenerator->FindChildByName( "mostlyOpaqueCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bMostlyOpaque ); + + pTargetField = pQCGenerator->FindChildByName( "disableCollisionsCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bDisableCollision ); + + pTargetField = pQCGenerator->FindChildByName( "referencePhysicsCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bReferenceAsPhys ); + + pTargetField = pQCGenerator->FindChildByName( "concaveCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bConcave ); + + pTargetField = pQCGenerator->FindChildByName( "automassCheck" ); + ((CheckButton *)pTargetField)->SetSelected( bAutomass ); + + Q_snprintf( tempText, 10, "%d", (int)fMass ); + pTargetField = pQCGenerator->FindChildByName( "massField" ); + ((TextEntry *)pTargetField)->SetText( tempText ); + + Q_snprintf( tempText, 10, "%d", (int)fScale ); + pTargetField = pQCGenerator->FindChildByName( "scaleField" ); + ((TextEntry *)pTargetField)->SetText( tempText ); + + pTargetField = pQCGenerator->FindChildByName( "collisionSMDField" ); + ((TextEntry *)pTargetField)->SetText( pszCollisionPath ); + + pTargetField = pQCGenerator->FindChildByName( "materialsField" ); + ((TextEntry *)pTargetField)->SetText( pszMaterialPath ); + + pTargetField = pQCGenerator->FindChildByName( "surfacePropertyDropDown" ); + int numItems = ((ComboBox *)pTargetField)->GetItemCount(); + for( int i = 0; i < numItems; i++ ) + { + ((ComboBox *)pTargetField)->GetItemText( i, tempText, MAX_PATH ); + if ( !Q_strcmp( tempText, pszSurfaceProperty ) ) + { + ((ComboBox *)pTargetField)->SetItemEnabled( i, true ); + ((ComboBox *)pTargetField)->SetText( tempText ); + break; + } + } +} + +CBrowseButton::CBrowseButton( vgui::Panel *pParent ) : BaseClass( pParent, "Browse Button", "...", pParent, "browse" ) +{ + SetParent( pParent ); + pszStartingDirectory = NULL; + pszFileFilter = NULL; + pszTargetField = NULL; +} + +CBrowseButton::~CBrowseButton() +{ +} + +void CBrowseButton::SetCharVar( char **pVar, const char *pszNewText ) +{ + if ( *pVar && pszNewText && !Q_strcmp( *pVar, pszNewText ) ) + { + return; + } + + if ( *pVar ) + { + delete [] *pVar; + *pVar = NULL; + } + + if ( pszNewText ) + { + int len = Q_strlen( pszNewText ) + 1; + *pVar = new char[ len ]; + Q_strncpy( *pVar, pszNewText, len ); + } +} + +void CBrowseButton::InitBrowseInfo( int x, int y, const char *pszName, const char *pszDir, const char *pszFilter, const char *pszField ) +{ + SetSize( 24, 24 ); + SetPos( x, y ); + SetName( pszName ); + SetCharVar( GetStartingDirectory(), pszDir ); + SetCharVar( GetFileFilter(), pszFilter ); + SetCharVar( GetTargetField(), pszField ); + SetActionMessage(); +} + +void CBrowseButton::SetActionMessage() +{ + KeyValues *newActionMessage = new KeyValues( "browse", "directory", pszStartingDirectory, "filter", pszFileFilter); + newActionMessage->SetString( "targetField", pszTargetField ); + SetCommand( newActionMessage ); +} + +const char *ParseKeyvalue( const char *pBuffer, char *key, char *value ) +{ + char com_token[1024]; + + pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL ); + if ( Q_strlen( com_token ) < MAX_KEYVALUE ) + { + Q_strncpy( key, com_token, MAX_KEYVALUE ); + Q_strlower( key ); + } + // no value on a close brace + if ( !Q_strcmp( key, "}" ) ) + { + value[0] = 0; + return pBuffer; + } + + pBuffer = (const char *)ParseFile( pBuffer, com_token, NULL ); + if ( Q_strlen( com_token ) < MAX_KEYVALUE ) + { + Q_strncpy( value, com_token, MAX_KEYVALUE ); + Q_strlower( value ); + } + + return pBuffer; +} + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CQCGenerator::CQCGenerator( vgui::Panel *pParent, const char *pszPath, const char *pszScene ) : BaseClass( pParent, "QCGenerator" ) +{ + m_szTargetField[0] = 0; + m_nSelectedSequence = 0; + m_nSelectedColumn = 0; + + m_QCInfo_t.Init( this ); + + SetMinimumSize(846, 770); + + m_pLODPanel = new ListPanel(this, "LODList"); + m_pLODPanel->SetSelectIndividualCells( true ); + m_pLODPanel->AddColumnHeader(0, "SMD", "LOD SMD", 450, 0); + m_pLODPanel->AddColumnHeader(1, "LOD", "LOD Distance", 50, 0); + m_pLODPanel->AddActionSignalTarget( this ); + m_pLODPanel->SetMouseInputEnabled( true ); + + LoadControlSettings( "QCGenerator.res" ); + + m_pCollisionBrowseButton = new CBrowseButton( this ); + m_pCollisionBrowseButton->InitBrowseInfo( 808, 158, "collisionBrowseButton", pszPath, "*.smd", "collisionSMDField" ); + + char szTerminatedPath[1024] = "\0"; + sprintf( szTerminatedPath, "%s\\", pszPath ); + + InitializeSMDPaths( szTerminatedPath, pszScene ); + + char *pszMaterialsStart = strrchrcount( szTerminatedPath, '\\', 3 ) + 1; + char *pszMaterialsEnd = strrchr( szTerminatedPath, '\\'); + Q_strncpy( m_QCInfo_t.pszMaterialPath, pszMaterialsStart, pszMaterialsEnd - pszMaterialsStart + 1 ); + + SetParent( pParent ); + + char szGamePath[1024] = "\0"; + char szSearchPath[1024] = "\0"; + + // Get the currently set game configuration + GetVConfigRegistrySetting( GAMEDIR_TOKEN, szGamePath, sizeof( szGamePath ) ); + static const char *pSurfacePropFilename = "\\scripts\\surfaceproperties.txt"; + + sprintf( szSearchPath, "%s%s", szGamePath, pSurfacePropFilename ); + + FileHandle_t fp = g_pFullFileSystem->Open( szSearchPath, "rb" ); + + if ( !fp ) + { + //the set game configuration didn't have a surfaceproperties file; we are grabbing it from hl2 + //TODO: This only works if they are in a subdirectory that is a peer to an hl2 directory + // that contains the file. It potentially needs to search the entire drive or prompt for the location + char *pszEndGamePath = Q_strrchr( szGamePath, '\\' ); + pszEndGamePath[0] = 0; + V_strcat_safe( szGamePath, "\\hl2" ); + sprintf( szSearchPath, "%s%s", szGamePath, pSurfacePropFilename ); + fp = g_pFullFileSystem->Open( szSearchPath, "rb" ); + } + + int len = g_pFullFileSystem->Size( fp ); + + const char *szSurfacePropContents = new char[len+1]; + g_pFullFileSystem->Read( (void *)szSurfacePropContents, len, fp ); + + char key[MAX_KEYVALUE], value[MAX_KEYVALUE]; + + vgui::Panel *pSurfacePropDropDown = FindChildByName( "surfacePropertyDropDown" ); + + //filling up the surface property dropdown + while ( szSurfacePropContents ) + { + szSurfacePropContents = ParseKeyvalue( szSurfacePropContents, key, value ); + ((ComboBox *)pSurfacePropDropDown)->AddItem( key, NULL ); + while ( szSurfacePropContents ) + { + szSurfacePropContents = ParseKeyvalue( szSurfacePropContents, key, value ); + if (!stricmp( key, "}" ) ) + { + break; + } + } + } + m_QCInfo_t.SyncToControls(); + + m_pLODEdit = 0; +} + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CQCGenerator::~CQCGenerator() +{ +} + +void CQCGenerator::OnCommand( const char *command ) +{ + if ( Q_stricmp( command, "createQC" ) == 0 ) + { + m_QCInfo_t.SyncFromControls(); + GenerateQCFile(); + } + if ( Q_stricmp( command, "deleteSeq" ) == 0 ) + { + //delete it + DeleteLOD(); + } + if ( Q_stricmp( command, "editSeq" ) == 0 ) + { + //edit + EditLOD(); + } + + BaseClass::OnCommand( command ); +} + + +void CQCGenerator::OnKeyCodeTyped( KeyCode code ) +{ + switch ( code ) + { + case KEY_ENTER: + EditLOD(); + } +} + + +void CQCGenerator::OnBrowse( KeyValues *data ) +{ + V_strcpy_safe( m_szTargetField, data->GetString( "targetField" ) ); + const char *filter = data->GetString( "filter" ); + + if ( Q_strlen( filter ) == 0 ) + { +// BrowseDirectory( data ); + } + else + { + BrowseFile( data ); + } +} + +/* +//This function is no longer used in the current version of the program. +void CQCGenerator::BrowseDirectory( KeyValues *data ) +{ + DirectorySelectDialog *pDialog = new DirectorySelectDialog( this, "Select Directory" ); + pDialog->AddActionSignalTarget( this ); + pDialog->DoModal(); + pDialog->SetStartDirectory( data->GetString( "directory" ) ); +} +*/ + +void CQCGenerator::BrowseFile( KeyValues *data ) +{ + const char *filter = data->GetString( "filter" ); + + FileOpenDialog *pDialog = new FileOpenDialog( this, "Select File", true ); + pDialog->AddFilter( filter, filter, true ); + pDialog->AddActionSignalTarget(this); + pDialog->SetStartDirectory( data->GetString( "directory" ) ); + pDialog->DoModal( true ); +} + +void CQCGenerator::OnFileSelected( KeyValues *data ) +{ + if ( *m_szTargetField ) + { + vgui::Panel *pTargetField = FindChildByName( m_szTargetField ); + ((TextEntry *)pTargetField)->SetText( data->GetString( "fullpath" ) ); + Repaint(); + } +} + +void CQCGenerator::OnDirectorySelected( KeyValues *data ) +{ + if ( *m_szTargetField ) + { + vgui::Panel *pTargetField = FindChildByName( m_szTargetField ); + ((TextEntry *)pTargetField)->SetText( data->GetString( "dir" ) ); + Repaint(); + } +} + +bool CQCGenerator::GenerateQCFile() +{ + //TODO: clean this up. Consider creating a datatype that includes the string to write out when the QC file is created + char *nameBegin = strrchr( m_QCInfo_t.pszSMDPath, '\\' ); + + char szPath[MAX_PATH]; + char szName[MAX_PATH]; + Q_strncpy( szPath, m_QCInfo_t.pszSMDPath, nameBegin - m_QCInfo_t.pszSMDPath + 2 ); + V_strcpy_safe( szName, szPath); + V_strcat_safe( szName, m_QCInfo_t.pszSceneName); + V_strcat_safe( szName, ".qc" ); + FileHandle_t pSaveFile = g_pFullFileSystem->Open( szName, "wt" ); + if (!pSaveFile) + { + char szSaveError[1024] = ""; + Q_snprintf( szSaveError, 1024, "Save failed: invalid file name '%s'\n\nDirectory '%s' must exist.", szName, szPath ); + VGUIMessageBox( this, "QC Generator error", szSaveError ); + return 0; + } + + //write qc header + g_pFullFileSystem->FPrintf( pSaveFile, "//\n// .qc file version 1.0\n\n"); + //write out modelname info + char szModelName[MAX_PATH]; + char *modelStart = strrchrcount( szName, '\\', 2) + 1; + char *modelEnd = strrchr( szName, '.' ); + Q_strncpy( szModelName, modelStart, modelEnd - modelStart + 1 ); + V_strcat_safe( szModelName, ".mdl" ); + g_pFullFileSystem->FPrintf( pSaveFile, "$modelname %s\n\n", szModelName ); + //write out scale info + g_pFullFileSystem->FPrintf( pSaveFile, "$scale %f\n", m_QCInfo_t.fScale ); + //write out body info + g_pFullFileSystem->FPrintf( pSaveFile, "$body \"Body\" \"%s\"\n", strrchr( m_QCInfo_t.pszSMDPath, '\\' ) + 1 ); + + if ( m_QCInfo_t.bStaticProp == true ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "$staticprop\n" ); + } + + if ( m_QCInfo_t.bMostlyOpaque == true ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "$mostlyopaque\n" ); + } + + //write out surfaceprop info + g_pFullFileSystem->FPrintf( pSaveFile, "$surfaceprop \"%s\"\n\n", m_QCInfo_t.pszSurfaceProperty ); + //write materials + + g_pFullFileSystem->FPrintf( pSaveFile, "$cdmaterials %s\n\n", m_QCInfo_t.pszMaterialPath); + + if ( m_QCInfo_t.bStaticProp || m_QCInfo_t.bNoAnimation ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "// --------- Animation sequences -------\n"); + g_pFullFileSystem->FPrintf( pSaveFile, "$sequence \"idle\" \"%s\" fps 30\n\n", strrchr(m_QCInfo_t.pszSMDPath, '\\')+1); + } + + //write out lod info + for( int i = 0; i < m_QCInfo_t.LODs.Count(); i++ ) + { + LODInfo thisLOD = m_QCInfo_t.LODs.Element( i ); + g_pFullFileSystem->FPrintf( pSaveFile, "$lod %d\n{\n\treplacemodel \"%s\" \"%s\"\n}\n\n", thisLOD.iLOD, strrchr(m_QCInfo_t.pszSMDPath, '\\')+1, thisLOD.pszFilename ); + } + + if ( m_QCInfo_t.bDisableCollision != true ) + { + //write out collision header + g_pFullFileSystem->FPrintf( pSaveFile, "\n" ); + //write out collision info + if ( m_QCInfo_t.bReferenceAsPhys == true ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "$collisionmodel \"%s\"", strrchr( m_QCInfo_t.pszSMDPath, '\\' ) + 1 ); + } + else + { + if( Q_strcmp( m_QCInfo_t.pszCollisionPath, "" ) ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "$collisionmodel \"%s\"", strrchr( m_QCInfo_t.pszCollisionPath, '\\' ) + 1 ); + } + } + + g_pFullFileSystem->FPrintf( pSaveFile, " {\n\t// Mass in kilograms\n "); + + if ( m_QCInfo_t.bAutomass == true ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "\t$automass\n" ); + } + else + { + g_pFullFileSystem->FPrintf( pSaveFile, "\t$mass %f\n", m_QCInfo_t.fMass ); + } + + if ( m_QCInfo_t.bConcave == true ) + { + g_pFullFileSystem->FPrintf( pSaveFile, "\t$concave\n" ); + } + g_pFullFileSystem->FPrintf( pSaveFile, "}\n\n"); + } + + g_pFullFileSystem->Close( pSaveFile ); + + char szCommand[MAX_PATH]; + char szGamePath[MAX_PATH]; + + char studiomdlPath[512]; + g_pFullFileSystem->RelativePathToFullPath( "studiomdl.bat", NULL, studiomdlPath, sizeof( studiomdlPath )); + + GetVConfigRegistrySetting( GAMEDIR_TOKEN, szGamePath, sizeof( szGamePath ) ); + +#ifdef WIN32 + STARTUPINFO startup; + PROCESS_INFORMATION process; + + + memset(&startup, 0, sizeof(startup)); + startup.cb = sizeof(startup); + + + sprintf( szCommand, "%s -game %s %s", studiomdlPath, szGamePath, szName); + bool bReturn = CreateProcess( NULL, szCommand, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &startup, &process); +#else + Assert( !"Implement me, why aren't we using a thread tool abstraction?" ); + bool bReturn = false; +#endif + return bReturn; +} + +void CQCGenerator::InitializeSMDPaths( const char *pszPath, const char *pszScene ) +{ + V_strcpy_safe( m_QCInfo_t.pszSceneName, pszScene ); + + FileFindHandle_t *pFileHandle = new FileFindHandle_t(); + + g_pFullFileSystem->AddSearchPath( pszPath, "SMD_DIR" ); + + const char *filename = g_pFullFileSystem->FindFirst( "*.smd", pFileHandle ); + + bool bFoundReference = false; + bool bFoundCollision = false; + bool bFoundLOD = false; + + //iterate through .smd files + const char *startName = pszScene; + + int nSearchLength = Q_strlen( pszScene ); + + int currentLOD = 1; + + while( filename ) + { + if ( !strncmp( startName, filename, nSearchLength ) ) + { + const char *filenameEnd = filename + nSearchLength; + if ( !strncmp( filenameEnd, "_ref", 4 ) || !strncmp( filenameEnd, ".smd", 4 ) ) + { + bFoundReference = true; + //we have found the reference smd. + V_strcpy_safe( m_QCInfo_t.pszSMDPath, pszPath ); + V_strcat_safe( m_QCInfo_t.pszSMDPath, filename ); + } + if ( !strncmp( filenameEnd, "_phy", 4) || !strncmp( filenameEnd, "_col", 4 ) ) + { + bFoundCollision = true; + //we have found the collision smd. + V_strcpy_safe( m_QCInfo_t.pszCollisionPath, pszPath ); + V_strcat_safe( m_QCInfo_t.pszCollisionPath, filename ); + } + if ( !strncmp( filenameEnd, "_lod", 4) ) + { + bFoundLOD = true; + //we found an LOD smd. + char lodName[255]; + Q_snprintf( lodName, Q_strlen( lodName ), "lod%d", currentLOD ); + //we found an LOD + KeyValues *newKv = new KeyValues( lodName, "SMD", filename, "LOD", "10" ); + m_pLODPanel->AddItem( newKv, currentLOD, false, false ); + currentLOD++; + } + } + filename = g_pFullFileSystem->FindNext( *pFileHandle ); + } + char pszMessage[2048] = ""; + char pszRefMessage[1024] = ""; + char pszColMessage[1024] = ""; + if (!bFoundReference ) + { + V_strcat_safe( m_QCInfo_t.pszSMDPath, pszPath ); + V_strcat_safe( m_QCInfo_t.pszSMDPath, pszScene ); + V_strcat_safe( m_QCInfo_t.pszSMDPath, ".smd" ); + Q_snprintf( pszRefMessage, 1024, "Reference SMD not found.\n\nValid default reference SMDs are %s%s_ref*.smd and %s%s.smd\nUsing default of %s. Model will not compile.\n\n", pszPath, pszScene, pszPath, pszScene, m_QCInfo_t.pszSMDPath ); + } + if ( !bFoundCollision ) + { + Q_snprintf( pszColMessage, 1024, "Collision SMD not found.\n\nThe valid default collision SMD is %s%s_phy*.smd.\nUsing reference SMD as default.\n", pszPath, pszScene ); + V_strcpy_safe( m_QCInfo_t.pszCollisionPath, m_QCInfo_t.pszSMDPath ); + m_QCInfo_t.bReferenceAsPhys = true; + } + if ( !bFoundReference || !bFoundCollision) + { + V_strcpy_safe( pszMessage, pszRefMessage ); + V_strcat_safe( pszMessage, pszColMessage ); + VGUIMessageBox( this, "Error Initializing Paths", pszMessage ); + } +} + + +void CQCGenerator::DeleteLOD() +{ + int numSelected = m_pLODPanel->GetSelectedItemsCount(); + int selected; + for ( int i = numSelected-1; i >= 0; i-- ) + { + selected = m_pLODPanel->GetSelectedItem( i ); + m_pLODPanel->RemoveItem( selected ); + } +} + +void CQCGenerator::EditLOD() +{ + int numSelected = m_pLODPanel->GetSelectedItemsCount(); + if ( numSelected == 1 && !m_pLODPanel->IsInEditMode() ) + { + if ( m_pLODEdit ) + { + m_pLODEdit->MarkForDeletion(); + m_pLODEdit = NULL; + } + m_pLODEdit = new vgui::TextEntry( this, "Edit" ); + m_pLODEdit->SendNewLine( true ); + m_nSelectedSequence = m_pLODPanel->GetSelectedItem( 0 ); + m_nSelectedColumn = m_pLODPanel->GetSelectedColumn(); + m_pLODPanel->EnterEditMode( m_nSelectedSequence, m_nSelectedColumn, m_pLODEdit ); + } +} + +void CQCGenerator::OnNewLODText() +{ + KeyValues *pEditItem = m_pLODPanel->GetItem( m_nSelectedSequence ); + KeyValues *pListItem = pEditItem; + wchar_t szEditText[MAX_PATH]; + + pEditItem = pEditItem->GetFirstValue(); + const char *name = pEditItem->GetName(); + for( int i = 0; i < m_nSelectedColumn; i++ ) + { + pEditItem = pEditItem->GetNextValue(); + name = pEditItem->GetName(); + } + m_pLODEdit->GetText( szEditText, MAX_PATH ); + + pListItem->SetWString( name, szEditText ); + + m_pLODPanel->LeaveEditMode(); + m_pLODPanel->InvalidateLayout(); + return; +} \ No newline at end of file diff --git a/src/vgui2/matsys_controls/assetpicker.cpp b/src/vgui2/matsys_controls/assetpicker.cpp new file mode 100644 index 000000000..4d30b7f52 --- /dev/null +++ b/src/vgui2/matsys_controls/assetpicker.cpp @@ -0,0 +1,47 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/assetpicker.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Asset Picker with no preview +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CAssetPicker::CAssetPicker( vgui::Panel *pParent, const char *pAssetType, + const char *pExt, const char *pSubDir, const char *pTextType ) : + BaseClass( pParent, pAssetType, pExt, pSubDir, pTextType ) +{ + CreateStandardControls( this ); + LoadControlSettingsAndUserConfig( "resource/assetpicker.res" ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CAssetPickerFrame::CAssetPickerFrame( vgui::Panel *pParent, const char *pTitle, + const char *pAssetType, const char *pExt, const char *pSubDir, const char *pTextType ) : + BaseClass( pParent ) +{ + SetAssetPicker( new CAssetPicker( this, pAssetType, pExt, pSubDir, pTextType ) ); + LoadControlSettingsAndUserConfig( "resource/assetpickerframe.res" ); + SetTitle( pTitle, false ); +} + + + diff --git a/src/vgui2/matsys_controls/baseassetpicker.cpp b/src/vgui2/matsys_controls/baseassetpicker.cpp new file mode 100644 index 000000000..30144f2b3 --- /dev/null +++ b/src/vgui2/matsys_controls/baseassetpicker.cpp @@ -0,0 +1,1625 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "filesystem.h" +#include "matsys_controls/baseassetpicker.h" +#include "tier1/KeyValues.h" +#include "tier1/utlntree.h" +#include "tier1/utlrbtree.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/TreeView.h" +#include "vgui_controls/ImageList.h" +#include "vgui_controls/CheckButton.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "vgui/IVGui.h" +#include "vgui/Cursor.h" + + +using namespace vgui; + + +#define ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME 0.25f +#define ASSET_LIST_DIRECTORY_SEARCH_TIME 0.025f + + +//----------------------------------------------------------------------------- +// sorting function, should return true if node1 should be displayed before node2 +//----------------------------------------------------------------------------- +bool AssetTreeViewSortFunc( KeyValues *node1, KeyValues *node2 ) +{ + const char *pDir1 = node1->GetString( "text", NULL ); + const char *pDir2 = node2->GetString( "text", NULL ); + return Q_stricmp( pDir1, pDir2 ) < 0; +} + + +//----------------------------------------------------------------------------- +// +// Tree view for assets +// +//----------------------------------------------------------------------------- +class CAssetTreeView : public vgui::TreeView +{ + DECLARE_CLASS_SIMPLE( CAssetTreeView, vgui::TreeView ); + +public: + CAssetTreeView( vgui::Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir ); + + // Inherited from base classes + virtual void GenerateChildrenOfNode( int itemIndex ); + virtual void ApplySchemeSettings( vgui::IScheme *pScheme ); + + // Opens and selects the root folder + void OpenRoot(); + + // Purpose: Refreshes the active file list + void RefreshFileList(); + + // Adds a subdirectory + DirHandle_t GetRootDirectory( ); + DirHandle_t AddSubDirectory( DirHandle_t hParent, const char *pDirName ); + void ClearDirectories(); + + // Selects a folder + void SelectFolder( const char *pSubDir, const char *pPath ); + +private: + // Allocates the root node + void AllocateRootNode( ); + + // Purpose: Refreshes the active file list + DirHandle_t RefreshTreeViewItem( int nItemIndex ); + + // Sets an item to be colored as if its a menu + void SetItemColorForDirectories( int nItemID ); + + // Add a directory into the treeview + void AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath ); + + // Selects an item in the tree + bool SelectFolder_R( int nItemID, const char *pPath ); + + CUtlString m_RootFolderName; + CUtlString m_RootDirectory; + vgui::ImageList m_Images; + CUtlNTree< CUtlString, DirHandle_t > m_DirectoryStructure; +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAssetTreeView::CAssetTreeView( Panel *pParent, const char *pName, const char *pRootFolderName, const char *pRootDir ) : BaseClass(pParent, pName), m_Images( false ) +{ + SetSortFunc( AssetTreeViewSortFunc ); + + m_RootFolderName = pRootFolderName; + m_RootDirectory = pRootDir; + AllocateRootNode(); + + // build our list of images + m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); + SetImageList( &m_Images, false ); + + SETUP_PANEL( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Refreshes the active file list +//----------------------------------------------------------------------------- +void CAssetTreeView::OpenRoot() +{ + RemoveAll(); + + // add the base node + const char *pRootDir = m_DirectoryStructure[ m_DirectoryStructure.Root() ]; + KeyValues *pkv = new KeyValues( "root" ); + pkv->SetString( "text", m_RootFolderName.Get() ); + pkv->SetInt( "root", 1 ); + pkv->SetInt( "expand", 1 ); + pkv->SetInt( "dirHandle", m_DirectoryStructure.Root() ); + pkv->SetString( "path", pRootDir ); + int iRoot = AddItem( pkv, GetRootItemIndex() ); + pkv->deleteThis(); + ExpandItem( iRoot, true ); +} + + +//----------------------------------------------------------------------------- +// Allocates the root node +//----------------------------------------------------------------------------- +void CAssetTreeView::AllocateRootNode( ) +{ + DirHandle_t hRoot = m_DirectoryStructure.Alloc(); + m_DirectoryStructure.SetRoot( hRoot ); + m_DirectoryStructure[hRoot] = m_RootDirectory; +} + + +//----------------------------------------------------------------------------- +// Adds a subdirectory (maintains sorted order) +//----------------------------------------------------------------------------- +DirHandle_t CAssetTreeView::GetRootDirectory( ) +{ + return m_DirectoryStructure.Root(); +} + +DirHandle_t CAssetTreeView::AddSubDirectory( DirHandle_t hParent, const char *pDirName ) +{ + DirHandle_t hSubdir = m_DirectoryStructure.Alloc(); + m_DirectoryStructure[hSubdir] = pDirName; + m_DirectoryStructure[hSubdir].ToLower(); + + DirHandle_t hChild = m_DirectoryStructure.FirstChild( hParent ); + m_DirectoryStructure.LinkChildBefore( hParent, hChild, hSubdir ); + + return hSubdir; +} + +void CAssetTreeView::ClearDirectories() +{ + m_DirectoryStructure.RemoveAll(); + AllocateRootNode(); +} + + +//----------------------------------------------------------------------------- +// Sets an item to be colored as if its a menu +//----------------------------------------------------------------------------- +void CAssetTreeView::SetItemColorForDirectories( int nItemID ) +{ + // mark directories in orange + SetItemFgColor( nItemID, Color(224, 192, 0, 255) ); +} + + +//----------------------------------------------------------------------------- +// Add a directory into the treeview +//----------------------------------------------------------------------------- +void CAssetTreeView::AddDirectoryToTreeView( int nParentItemIndex, const char *pFullParentPath, DirHandle_t hPath ) +{ + const char *pDirName = m_DirectoryStructure[hPath].Get(); + KeyValues *kv = new KeyValues( "node", "text", pDirName ); + + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, sizeof( pFullPath ), "%s/%s", pFullParentPath, pDirName ); + Q_FixSlashes( pFullPath ); + Q_strlower( pFullPath ); + bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex(); + kv->SetString( "path", pFullPath ); + kv->SetInt( "expand", bHasSubdirectories ); + kv->SetInt( "image", 0 ); + kv->SetInt( "dirHandle", hPath ); + + int nItemID = AddItem( kv, nParentItemIndex ); + kv->deleteThis(); + + // mark directories in orange + SetItemColorForDirectories( nItemID ); +} + + +//----------------------------------------------------------------------------- +// override to incremental request and show p4 directories +//----------------------------------------------------------------------------- +void CAssetTreeView::GenerateChildrenOfNode( int nItemIndex ) +{ + KeyValues *pkv = GetItemData( nItemIndex ); + + const char *pFullParentPath = pkv->GetString( "path", NULL ); + if ( !pFullParentPath ) + return; + + DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() ); + if ( hPath == m_DirectoryStructure.InvalidIndex() ) + return; + + DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath ); + while ( hChild != m_DirectoryStructure.InvalidIndex() ) + { + AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild ); + hChild = m_DirectoryStructure.NextSibling( hChild ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Refreshes the active file list +//----------------------------------------------------------------------------- +DirHandle_t CAssetTreeView::RefreshTreeViewItem( int nItemIndex ) +{ + if ( nItemIndex < 0 ) + return m_DirectoryStructure.InvalidIndex(); + + // Make sure the expand icons are set correctly + KeyValues *pkv = GetItemData( nItemIndex ); + DirHandle_t hPath = (DirHandle_t)pkv->GetInt( "dirHandle", m_DirectoryStructure.InvalidIndex() ); + const char *pFullParentPath = pkv->GetString( "path", NULL ); + bool bHasSubdirectories = m_DirectoryStructure.FirstChild( hPath ) != m_DirectoryStructure.InvalidIndex(); + if ( bHasSubdirectories != ( pkv->GetInt( "expand" ) != 0 ) ) + { + pkv->SetInt( "expand", bHasSubdirectories ); + ModifyItem( nItemIndex, pkv ); + } + bool bIsExpanded = IsItemExpanded( nItemIndex ); + if ( !bIsExpanded ) + return hPath; + + // Check all children + build a list of children we've already got + int nChildCount = GetNumChildren( nItemIndex ); + DirHandle_t *pFoundHandles = (DirHandle_t*)_alloca( nChildCount * sizeof(DirHandle_t) ); + memset( pFoundHandles, 0xFF, nChildCount * sizeof(DirHandle_t) ); + for ( int i = 0; i < nChildCount; ++i ) + { + int nChildItemIndex = GetChild( nItemIndex, i ); + pFoundHandles[i] = RefreshTreeViewItem( nChildItemIndex ); + } + + // Check directory structure to see if other directories were added + DirHandle_t hChild = m_DirectoryStructure.FirstChild( hPath ); + for ( ; hChild != m_DirectoryStructure.InvalidIndex(); hChild = m_DirectoryStructure.NextSibling( hChild ) ) + { + // Search for existence of this child already + bool bFound = false; + for ( int j = 0; j < nChildCount; ++j ) + { + if ( pFoundHandles[j] == hChild ) + { + pFoundHandles[j] = pFoundHandles[nChildCount-1]; + --nChildCount; + bFound = true; + break; + } + } + + if ( bFound ) + continue; + + // Child is new, add it + AddDirectoryToTreeView( nItemIndex, pFullParentPath, hChild ); + } + + return hPath; +} + +void CAssetTreeView::RefreshFileList() +{ + // Make sure the expand icons are set correctly + RefreshTreeViewItem( GetRootItemIndex() ); + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Selects a folder +//----------------------------------------------------------------------------- +bool CAssetTreeView::SelectFolder_R( int nItemID, const char *pPath ) +{ + if ( nItemID < 0 ) + return false; + + KeyValues *kv = GetItemData( nItemID ); + const char *pTestPath = kv->GetString( "path" ); + if ( !Q_stricmp( pTestPath, pPath ) ) + { + AddSelectedItem( nItemID, true, false, true ); + return true; + } + + // Substring match.. + CUtlString str = pTestPath; + str += '\\'; + if ( Q_strnicmp( str, pPath, str.Length() ) ) + return false; + + ExpandItem( nItemID, true ); + + int nChildCount = GetNumChildren( nItemID ); + for ( int i = 0; i < nChildCount; ++i ) + { + int nChildItemID = GetChild( nItemID, i ); + if ( SelectFolder_R( nChildItemID, pPath ) ) + return true; + } + return false; +} + +void CAssetTreeView::SelectFolder( const char *pSubDir, const char *pPath ) +{ + char pTemp[MAX_PATH]; + Q_snprintf( pTemp, sizeof(pTemp), "%s\\%s", pSubDir, pPath ); + Q_StripTrailingSlash( pTemp ); + + int nItem = GetRootItemIndex(); + SelectFolder_R( nItem, pTemp ); +} + + + +//----------------------------------------------------------------------------- +// setup a smaller font +//----------------------------------------------------------------------------- +void CAssetTreeView::ApplySchemeSettings( IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetFont( pScheme->GetFont("DefaultSmall") ); + SetFgColor( Color(216, 222, 211, 255) ); +} + + +//----------------------------------------------------------------------------- +// +// Cache of asset data so we don't need to rebuild all the time +// +//----------------------------------------------------------------------------- +DECLARE_POINTER_HANDLE( AssetList_t ); +#define ASSET_LIST_INVALID ((AssetList_t)(0xFFFF)) + + +class CAssetCache +{ +public: + struct CachedAssetInfo_t + { + CUtlString m_AssetName; + int m_nModIndex; + }; + + struct ModInfo_t + { + CUtlString m_ModName; + CUtlString m_Path; + }; + + CAssetCache(); + + // Mod iteration + int ModCount() const; + const ModInfo_t& ModInfo( int nIndex ) const; + + // Building the mod list + void BuildModList(); + + AssetList_t FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt ); + bool BeginAssetScan( AssetList_t hList, bool bForceRescan = false ); + CAssetTreeView* GetFileTree( AssetList_t hList ); + int GetAssetCount( AssetList_t hList ) const; + const CachedAssetInfo_t& GetAsset( AssetList_t hList, int nIndex ) const; + + void AddAsset( AssetList_t hList, const CachedAssetInfo_t& info ); + + bool ContinueSearchForAssets( AssetList_t hList, float flDuration ); + +private: + struct DirToCheck_t + { + CUtlString m_DirName; + DirHandle_t m_hDirHandle; + }; + + struct CachedAssetList_t + { + CachedAssetList_t() {} + CachedAssetList_t( const char *pSearchSubDir, int nExtCount, const char **ppSearchExt ) : + m_pSubDir( pSearchSubDir, Q_strlen( pSearchSubDir ) + 1 ) + { + m_Ext.AddMultipleToTail( nExtCount, ppSearchExt ); + } + CachedAssetList_t( const CachedAssetList_t& ) + { + // Only used during insertion; do nothing + } + + CUtlVector< CachedAssetInfo_t > m_AssetList; + CAssetTreeView *m_pFileTree; + + CUtlString m_pSubDir; + CUtlVector< const char * > m_Ext; + + CUtlLinkedList< DirToCheck_t > m_DirectoriesToCheck; + FileFindHandle_t m_hFind; + bool m_bAssetScanComplete; + }; + +private: + bool AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hDirHandle, float flStartTime, float flDuration ); + bool DoesExtensionMatch( CachedAssetList_t& list, const char *pFileName ); + void AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex ); + +private: + // List of known mods + CUtlVector< ModInfo_t > m_ModList; + + // List of cached assets + CUtlRBTree< CachedAssetList_t > m_CachedAssets; + + // Have we built the mod list? + bool m_bBuiltModList; + + static bool CachedAssetLessFunc( const CachedAssetList_t& src1, const CachedAssetList_t& src2 ); +}; + + +//----------------------------------------------------------------------------- +// Static instance of the asset cache +//----------------------------------------------------------------------------- +static CAssetCache s_AssetCache; + + +//----------------------------------------------------------------------------- +// Map sort func +//----------------------------------------------------------------------------- +bool CAssetCache::CachedAssetLessFunc( const CAssetCache::CachedAssetList_t& src1, const CAssetCache::CachedAssetList_t& src2 ) +{ + int nRetVal = Q_stricmp( src1.m_pSubDir, src2.m_pSubDir ) > 0; + if ( nRetVal != 0 ) + return nRetVal > 0; + + int nCount = src1.m_Ext.Count(); + int nDiff = nCount - src2.m_Ext.Count(); + if ( nDiff != 0 ) + return nDiff > 0; + + for ( int i = 0; i < nCount; ++i ) + { + nRetVal = Q_stricmp( src1.m_Ext[i], src2.m_Ext[i] ); + if ( nRetVal != 0 ) + return nRetVal > 0; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CAssetCache::CAssetCache() : m_CachedAssets( 0, 0, CachedAssetLessFunc ) +{ + m_bBuiltModList = false; +} + + +//----------------------------------------------------------------------------- +// Mod iteration +//----------------------------------------------------------------------------- +int CAssetCache::ModCount() const +{ + return m_ModList.Count(); +} + +const CAssetCache::ModInfo_t& CAssetCache::ModInfo( int nIndex ) const +{ + return m_ModList[nIndex]; +} + + +//----------------------------------------------------------------------------- +// Building the mod list +//----------------------------------------------------------------------------- +void CAssetCache::BuildModList() +{ + if ( m_bBuiltModList ) + return; + + m_bBuiltModList = true; + + m_ModList.RemoveAll(); + + // Add all mods + int nLen = g_pFullFileSystem->GetSearchPath( "GAME", false, NULL, 0 ); + char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); + g_pFullFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen ); + char *pPath = pSearchPath; + while( pPath ) + { + char *pSemiColon = strchr( pPath, ';' ); + if ( pSemiColon ) + { + *pSemiColon = 0; + } + + Q_StripTrailingSlash( pPath ); + Q_FixSlashes( pPath ); + + char pModName[ MAX_PATH ]; + Q_FileBase( pPath, pModName, sizeof( pModName ) ); + + // Always start in an asset-specific directory +// char pAssetPath[MAX_PATH]; +// Q_snprintf( pAssetPath, MAX_PATH, "%s\\%s", pPath, m_pAssetSubDir ); +// Q_FixSlashes( pPath ); + + int i = m_ModList.AddToTail( ); + m_ModList[i].m_ModName.Set( pModName ); + m_ModList[i].m_Path.Set( pPath ); + + pPath = pSemiColon ? pSemiColon + 1 : NULL; + } +} + + +//----------------------------------------------------------------------------- +// Adds an asset to the list of assets of this type +//----------------------------------------------------------------------------- +void CAssetCache::AddAssetToList( CachedAssetList_t& list, const char *pAssetName, int nModIndex ) +{ + int i = list.m_AssetList.AddToTail( ); + CachedAssetInfo_t& info = list.m_AssetList[i]; + info.m_AssetName.Set( pAssetName ); + info.m_nModIndex = nModIndex; +} + + +//----------------------------------------------------------------------------- +// Extension matches? +//----------------------------------------------------------------------------- +bool CAssetCache::DoesExtensionMatch( CachedAssetList_t& info, const char *pFileName ) +{ + char pChildExt[MAX_PATH]; + Q_ExtractFileExtension( pFileName, pChildExt, sizeof(pChildExt) ); + + // Check the extension matches + int nCount = info.m_Ext.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( !Q_stricmp( info.m_Ext[i], pChildExt ) ) + return true; + } + + return false; +} + + +//----------------------------------------------------------------------------- +// Recursively add all files matching the wildcard under this directory +//----------------------------------------------------------------------------- +bool CAssetCache::AddFilesInDirectory( CachedAssetList_t& list, const char *pStartingFile, const char *pFilePath, DirHandle_t hCurrentDir, float flStartTime, float flDuration ) +{ + // Indicates no files found + if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE ) + return true; + + // generate children + // add all the items + int nModCount = m_ModList.Count(); + int nSubDirLen = list.m_pSubDir ? Q_strlen(list.m_pSubDir) : 0; + const char *pszFileName = pStartingFile; + while ( pszFileName ) + { + char pRelativeChildPath[MAX_PATH]; + Q_snprintf( pRelativeChildPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName ); + + if ( g_pFullFileSystem->FindIsDirectory( list.m_hFind ) ) + { + // If .svn is in the name, don't add this directory!! + if ( strstr (pszFileName, ".svn") ) + { + pszFileName = g_pFullFileSystem->FindNext( list.m_hFind ); + continue; + } + + if ( Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) ) + { + DirHandle_t hDirHandle = list.m_pFileTree->AddSubDirectory( hCurrentDir, pszFileName ); + int i = list.m_DirectoriesToCheck.AddToTail(); + list.m_DirectoriesToCheck[i].m_DirName = pRelativeChildPath; + list.m_DirectoriesToCheck[i].m_hDirHandle = hDirHandle; + } + } + else + { + // Check the extension matches + if ( DoesExtensionMatch( list, pszFileName ) ) + { + char pFullAssetPath[MAX_PATH]; + g_pFullFileSystem->RelativePathToFullPath( pRelativeChildPath, "GAME", pFullAssetPath, sizeof(pFullAssetPath) ); + + int nModIndex = -1; + for ( int i = 0; i < nModCount; ++i ) + { + if ( !Q_strnicmp( pFullAssetPath, m_ModList[i].m_Path, m_ModList[i].m_Path.Length() ) ) + { + nModIndex = i; + break; + } + } + + if ( nModIndex >= 0 ) + { + // Strip 'subdir/' prefix + char *pAssetName = pRelativeChildPath; + if ( list.m_pSubDir ) + { + if ( !Q_strnicmp( list.m_pSubDir, pAssetName, nSubDirLen ) ) + { + if ( pAssetName[nSubDirLen] == '\\' ) + { + pAssetName += nSubDirLen + 1; + } + } + } + strlwr( pAssetName ); + + AddAssetToList( list, pAssetName, nModIndex ); + } + } + } + + // Don't let the search go for too long at a time + if ( Plat_FloatTime() - flStartTime >= flDuration ) + return false; + + pszFileName = g_pFullFileSystem->FindNext( list.m_hFind ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Recursively add all files matching the wildcard under this directory +//----------------------------------------------------------------------------- +bool CAssetCache::ContinueSearchForAssets( AssetList_t hList, float flDuration ) +{ + CachedAssetList_t& list = m_CachedAssets[ (intp)hList ]; + + float flStartTime = Plat_FloatTime(); + while ( list.m_DirectoriesToCheck.Count() ) + { + const char *pFilePath = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_DirName; + DirHandle_t hCurrentDir = list.m_DirectoriesToCheck[ list.m_DirectoriesToCheck.Head() ].m_hDirHandle; + + const char *pStartingFile; + if ( list.m_hFind == FILESYSTEM_INVALID_FIND_HANDLE ) + { + char pSearchString[MAX_PATH]; + Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath ); + + // get the list of files + pStartingFile = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &list.m_hFind ); + } + else + { + pStartingFile = g_pFullFileSystem->FindNext( list.m_hFind ); + } + + if ( !AddFilesInDirectory( list, pStartingFile, pFilePath, hCurrentDir, flStartTime, flDuration ) ) + return false; + + g_pFullFileSystem->FindClose( list.m_hFind ); + list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; + list.m_DirectoriesToCheck.Remove( list.m_DirectoriesToCheck.Head() ); + } + list.m_bAssetScanComplete = true; + return true; +} + + +//----------------------------------------------------------------------------- +// Asset cache iteration +//----------------------------------------------------------------------------- +bool CAssetCache::BeginAssetScan( AssetList_t hList, bool bForceRescan ) +{ + CachedAssetList_t& list = m_CachedAssets[ (intp)hList ]; + if ( bForceRescan ) + { + list.m_bAssetScanComplete = false; + if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE ) + { + g_pFullFileSystem->FindClose( list.m_hFind ); + list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; + } + list.m_DirectoriesToCheck.RemoveAll(); + } + + if ( list.m_bAssetScanComplete ) + return false; + + // This case occurs if we stopped the picker previously while in the middle of a scan + if ( list.m_hFind != FILESYSTEM_INVALID_FIND_HANDLE ) + return true; + + list.m_AssetList.RemoveAll(); + list.m_pFileTree->ClearDirectories(); + + // Add all files, determine which mod they are in. + int i = list.m_DirectoriesToCheck.AddToTail(); + list.m_DirectoriesToCheck[i].m_DirName = list.m_pSubDir; + list.m_DirectoriesToCheck[i].m_hDirHandle = list.m_pFileTree->GetRootDirectory(); + return true; +} + + +//----------------------------------------------------------------------------- +// Asset cache iteration +//----------------------------------------------------------------------------- +AssetList_t CAssetCache::FindAssetList( const char *pAssetType, const char *pSubDir, int nExtCount, const char **ppExt ) +{ + CachedAssetList_t search( pSubDir, nExtCount, ppExt ); + int nIndex = m_CachedAssets.Find( search ); + if ( nIndex == m_CachedAssets.InvalidIndex() ) + { + nIndex = m_CachedAssets.Insert( search ); + CachedAssetList_t &list = m_CachedAssets[nIndex]; + list.m_pSubDir = pSubDir; + list.m_Ext.AddMultipleToTail( nExtCount, ppExt ); + list.m_hFind = FILESYSTEM_INVALID_FIND_HANDLE; + list.m_bAssetScanComplete = false; + list.m_pFileTree = new CAssetTreeView( NULL, "FolderFilter", pAssetType, pSubDir ); + } + + return (AssetList_t)(intp)nIndex; +} + +CAssetTreeView* CAssetCache::GetFileTree( AssetList_t hList ) +{ + if ( hList == ASSET_LIST_INVALID ) + return NULL; + return m_CachedAssets[ (intp)hList ].m_pFileTree; +} + +int CAssetCache::GetAssetCount( AssetList_t hList ) const +{ + if ( hList == ASSET_LIST_INVALID ) + return 0; + return m_CachedAssets[ (intp)hList ].m_AssetList.Count(); +} + +const CAssetCache::CachedAssetInfo_t& CAssetCache::GetAsset( AssetList_t hList, int nIndex ) const +{ + Assert( nIndex < GetAssetCount(hList) ); + return m_CachedAssets[ (intp)hList ].m_AssetList[ nIndex ]; +} + + + +//----------------------------------------------------------------------------- +// +// Base asset Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by asset name +//----------------------------------------------------------------------------- +static int __cdecl AssetBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + bool bRoot1 = item1.kv->GetInt("root") != 0; + bool bRoot2 = item2.kv->GetInt("root") != 0; + if ( bRoot1 != bRoot2 ) + return bRoot1 ? -1 : 1; + const char *pString1 = item1.kv->GetString("asset"); + const char *pString2 = item2.kv->GetString("asset"); + return Q_stricmp( pString1, pString2 ); +} + +static int __cdecl AssetBrowserModSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + int nMod1 = item1.kv->GetInt("modIndex", -1); + int nMod2 = item2.kv->GetInt("modIndex", -1); + if ( nMod1 != nMod2 ) + return nMod1 - nMod2; + return AssetBrowserSortFunc( pPanel, item1, item2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CBaseAssetPicker::CBaseAssetPicker( vgui::Panel *pParent, const char *pAssetType, + const char *pExt, const char *pSubDir, const char *pTextType ) : + BaseClass( pParent, "AssetPicker" ) +{ + m_bBuiltAssetList = false; + m_pAssetType = pAssetType; + m_pAssetTextType = pTextType; + m_pAssetExt = pExt; + m_pAssetSubDir = pSubDir; + m_bFinishedAssetListScan = false; + m_bFirstAssetScan = false; + m_nMatchingAssets = 0; + m_bSubDirCheck = true; + m_hAssetList = ASSET_LIST_INVALID; +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CBaseAssetPicker::~CBaseAssetPicker() +{ + SaveUserConfig(); + + // Detach! + m_pFileTree->RemoveActionSignalTarget( this ); + m_pFileTree->SetParent( (Panel*)NULL ); + m_pFileTree = NULL; +} + + +//----------------------------------------------------------------------------- +// Creates standard controls +//----------------------------------------------------------------------------- +void CBaseAssetPicker::CreateStandardControls( vgui::Panel *pParent, bool bAllowMultiselect ) +{ + int nExtCount = 1 + m_ExtraAssetExt.Count(); + const char **ppExt = (const char **)_alloca( nExtCount * sizeof(const char *) ); + ppExt[0] = m_pAssetExt; + if ( nExtCount > 1 ) + { + memcpy( ppExt + 1, m_ExtraAssetExt.Base(), nExtCount - 1 ); + } + + m_hAssetList = s_AssetCache.FindAssetList( m_pAssetType, m_pAssetSubDir, nExtCount, ppExt ); + + m_pAssetSplitter = new vgui::Splitter( pParent, "AssetSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + vgui::Panel *pSplitterTopSide = m_pAssetSplitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_pAssetSplitter->GetChild( 1 ); + + // Combo box for mods + m_pModSelector = new ComboBox( pSplitterTopSide, "ModFilter", 5, false ); + m_pModSelector->AddActionSignalTarget( this ); + + // Rescan button + m_pRescanButton = new Button( pSplitterTopSide, "RescanButton", "Rescan", this, "AssetRescan" ); + + // file browser tree controls + m_pFileTree = s_AssetCache.GetFileTree( m_hAssetList ); + m_pFileTree->SetParent( pSplitterTopSide ); + m_pFileTree->AddActionSignalTarget( this ); + + m_pSubDirCheck = new CheckButton( pSplitterTopSide, "SubDirCheck", "Check subfolders for files?" ); + m_pSubDirCheck->SetSelected( true ); + m_pSubDirCheck->SetEnabled( false ); + m_pSubDirCheck->SetVisible( false ); + m_pSubDirCheck->AddActionSignalTarget( this ); + + char pTemp[512]; + Q_snprintf( pTemp, sizeof(pTemp), "No .%s files", m_pAssetExt ); + m_pAssetBrowser = new vgui::ListPanel( pSplitterBottomSide, "AssetBrowser" ); + m_pAssetBrowser->AddColumnHeader( 0, "mod", "Mod", 52, 0 ); + m_pAssetBrowser->AddColumnHeader( 1, "asset", m_pAssetType, 128, ListPanel::COLUMN_RESIZEWITHWINDOW ); + m_pAssetBrowser->SetSelectIndividualCells( false ); + m_pAssetBrowser->SetMultiselectEnabled( bAllowMultiselect ); + m_pAssetBrowser->SetEmptyListText( pTemp ); + m_pAssetBrowser->SetDragEnabled( true ); + m_pAssetBrowser->AddActionSignalTarget( this ); + m_pAssetBrowser->SetSortFunc( 0, AssetBrowserModSortFunc ); + m_pAssetBrowser->SetSortFunc( 1, AssetBrowserSortFunc ); + m_pAssetBrowser->SetSortColumn( 1 ); + + // filter selection + m_pFilter = new TextEntry( pSplitterBottomSide, "FilterList" ); + m_pFilter->AddActionSignalTarget( this ); + + // full path + m_pFullPath = new TextEntry( pSplitterBottomSide, "FullPath" ); + m_pFullPath->SetEnabled( false ); + m_pFullPath->SetEditable( false ); + + m_nCurrentModFilter = -1; +} + + +//----------------------------------------------------------------------------- +// Reads user config settings +//----------------------------------------------------------------------------- +void CBaseAssetPicker::ApplyUserConfigSettings( KeyValues *pUserConfig ) +{ + BaseClass::ApplyUserConfigSettings( pUserConfig ); + + // Populates the mod list names + RefreshAssetList(); + + const char *pFilter = pUserConfig->GetString( "filter", "" ); + m_FolderFilter = pUserConfig->GetString( "folderfilter", "" ); + const char *pMod = pUserConfig->GetString( "mod", "" ); + SetFilter( pFilter ); + m_nCurrentModFilter = -1; + if ( pMod && pMod[0] ) + { + int nCount = s_AssetCache.ModCount(); + for ( int i = 0; i < nCount; ++i ) + { + const CAssetCache::ModInfo_t& modInfo = s_AssetCache.ModInfo( i ); + if ( Q_stricmp( pMod, modInfo.m_ModName ) ) + continue; + + int nItemCount = m_pModSelector->GetItemCount(); + for ( int j = 0; j < nItemCount; ++j ) + { + int nItemID = m_pModSelector->GetItemIDFromRow( j ); + KeyValues *kv = m_pModSelector->GetItemUserData( nItemID ); + int nModIndex = kv->GetInt( "mod" ); + if ( nModIndex == i ) + { + m_nCurrentModFilter = i; + m_pModSelector->ActivateItem( nItemID ); + break; + } + } + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: returns user config settings for this control +//----------------------------------------------------------------------------- +void CBaseAssetPicker::GetUserConfigSettings( KeyValues *pUserConfig ) +{ + BaseClass::GetUserConfigSettings( pUserConfig ); + pUserConfig->SetString( "filter", m_Filter ); + pUserConfig->SetString( "folderfilter", m_FolderFilter ); + pUserConfig->SetString( "mod", ( m_nCurrentModFilter >= 0 ) ? + s_AssetCache.ModInfo( m_nCurrentModFilter ).m_ModName : "" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: optimization, return true if this control has any user config settings +//----------------------------------------------------------------------------- +bool CBaseAssetPicker::HasUserConfigSettings() +{ + return true; +} + + +//----------------------------------------------------------------------------- +// Allows the picker to browse multiple asset types +//----------------------------------------------------------------------------- +void CBaseAssetPicker::AddExtension( const char *pExtension ) +{ + m_ExtraAssetExt.AddToTail( pExtension ); +} + + +//----------------------------------------------------------------------------- +// Is multiselect enabled? +//----------------------------------------------------------------------------- +bool CBaseAssetPicker::IsMultiselectEnabled() const +{ + return m_pAssetBrowser->IsMultiselectEnabled(); +} + + +//----------------------------------------------------------------------------- +// Sets the initial selected asset +//----------------------------------------------------------------------------- +void CBaseAssetPicker::SetInitialSelection( const char *pAssetName ) +{ + // This makes it so the background list filling code will automatically select this asset when it gets to it. + m_SelectedAsset = pAssetName; + + if ( pAssetName ) + { + // Sometimes we've already refreshed our list with a bunch of cached resources and the item is already in the list, + // so in that case just select it here. + int cnt = m_pAssetBrowser->GetItemCount(); + for ( int i=0; i < cnt; i++ ) + { + KeyValues *kv = m_pAssetBrowser->GetItem( i ); + if ( !kv ) + continue; + + const char *pTestAssetName = kv->GetString( "asset" ); + if ( !pTestAssetName ) + continue; + + if ( Q_stricmp( pTestAssetName, pAssetName ) == 0 ) + { + m_pAssetBrowser->SetSelectedCell( i, 0 ); + break; + } + } + } +} + + +//----------------------------------------------------------------------------- +// Set/get the filter +//----------------------------------------------------------------------------- +void CBaseAssetPicker::SetFilter( const char *pFilter ) +{ + m_Filter = pFilter; + m_pFilter->SetText( pFilter ); +} + +const char *CBaseAssetPicker::GetFilter() +{ + return m_Filter; +} + + +//----------------------------------------------------------------------------- +// Purpose: called to open +//----------------------------------------------------------------------------- +void CBaseAssetPicker::Activate() +{ + RefreshAssetList(); + RequestFilterFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnKeyCodePressed( KeyCode code ) +{ + if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN )) + { + KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code); + vgui::ipanel()->SendMessage( m_pAssetBrowser->GetVPanel(), pMsg, GetVPanel()); + pMsg->deleteThis(); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + +//----------------------------------------------------------------------------- +// Is a particular asset visible? +//----------------------------------------------------------------------------- +bool CBaseAssetPicker::IsAssetVisible( int nAssetIndex ) +{ + const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex ); + + // Filter based on active mod + int nModIndex = info.m_nModIndex; + if ( ( m_nCurrentModFilter >= 0 ) && ( m_nCurrentModFilter != nModIndex ) ) + return false; + + // Filter based on name + const char *pAssetName = info.m_AssetName; + if ( !Q_strcmp( pAssetName, m_SelectedAsset ) ) + return true; + + if ( m_Filter.Length() && !Q_stristr( pAssetName, m_Filter.Get() ) ) + return false; + + // Filter based on folder + if ( m_FolderFilter.Length() && Q_strnicmp( pAssetName, m_FolderFilter.Get(), m_FolderFilter.Length() ) ) + return false; + + // Filter based on subdirectory check + if ( !m_bSubDirCheck && strchr( pAssetName + m_FolderFilter.Length(), '\\' ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Adds an asset from the cache to the list +//----------------------------------------------------------------------------- +void CBaseAssetPicker::AddAssetToList( int nAssetIndex ) +{ + const CAssetCache::CachedAssetInfo_t& info = s_AssetCache.GetAsset( m_hAssetList, nAssetIndex ); + + bool bInRootDir = !strchr( info.m_AssetName, '\\' ) && !strchr( info.m_AssetName, '/' ); + + KeyValues *kv = new KeyValues( "node", "asset", info.m_AssetName ); + kv->SetString( "mod", s_AssetCache.ModInfo( info.m_nModIndex ).m_ModName ); + kv->SetInt( "modIndex", info.m_nModIndex ); + kv->SetInt( "root", bInRootDir ); + int nItemID = m_pAssetBrowser->AddItem( kv, 0, false, false ); + kv->deleteThis(); + + if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 && !Q_strcmp( m_SelectedAsset, info.m_AssetName ) ) + { + m_pAssetBrowser->SetSelectedCell( nItemID, 0 ); + } + + KeyValues *pDrag = new KeyValues( "drag", "text", info.m_AssetName ); + if ( m_pAssetTextType ) + { + pDrag->SetString( "texttype", m_pAssetTextType ); + } + m_pAssetBrowser->SetItemDragData( nItemID, pDrag ); + + int i = m_AssetList.AddToTail( ); + m_AssetList[i].m_nAssetIndex = nAssetIndex; + m_AssetList[i].m_nItemId = nItemID; + + bool bIsVisible = IsAssetVisible( i ); + m_pAssetBrowser->SetItemVisible( nItemID, bIsVisible ); + if ( bIsVisible ) + { + ++m_nMatchingAssets; + } +} + + +//----------------------------------------------------------------------------- +// Continues to build the asset list +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnTick() +{ + BaseClass::OnTick(); + + int nPreAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); + + // Stop getting called back once all assets have been found + float flTime = m_bFirstAssetScan ? ASSET_LIST_DIRECTORY_INITIAL_SEARCH_TIME : ASSET_LIST_DIRECTORY_SEARCH_TIME; + bool bFinished = s_AssetCache.ContinueSearchForAssets( m_hAssetList, flTime ); + + if ( m_bFirstAssetScan ) + { + m_pFileTree->OpenRoot(); + } + m_bFirstAssetScan = false; + + int nPostAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); + for ( int i = nPreAssetCount; i < nPostAssetCount; ++i ) + { + AddAssetToList( i ); + } + + if ( bFinished ) + { + vgui::ivgui()->RemoveTickSignal( GetVPanel() ); + m_bFinishedAssetListScan = true; + + // Copy the current folder filter.. this is necessary + // to finally select the folder loaded from the user config settings + // in the free view (since it's finally populated at this point) + // NOTE: if a user has changed the folder filter between startup + // and this point, this should still work since m_FolderFilter should be updated + m_pFileTree->SelectFolder( m_pAssetSubDir, m_FolderFilter ); + RefreshAssetList( ); + return; + } + + UpdateAssetColumnHeader(); +} + + +//----------------------------------------------------------------------------- +// Builds the Bsp name list +//----------------------------------------------------------------------------- +void CBaseAssetPicker::BuildAssetNameList( ) +{ + if ( m_bBuiltAssetList ) + return; + + m_bBuiltAssetList = true; + m_nMatchingAssets = 0; + m_nCurrentModFilter = -1; + + // Build the list of known mods if we haven't + s_AssetCache.BuildModList(); + + m_pModSelector->RemoveAll(); + m_pModSelector->AddItem( "All Mods", new KeyValues( "Mod", "mod", -1 ) ); + int nModCount = s_AssetCache.ModCount(); + for ( int i = 0; i < nModCount; ++i ) + { + const char *pModName = s_AssetCache.ModInfo( i ).m_ModName; + m_pModSelector->AddItem( pModName, new KeyValues( "Mod", "mod", i ) ); + } + m_pModSelector->ActivateItemByRow( 0 ); + + // If we've already read in + if ( s_AssetCache.BeginAssetScan( m_hAssetList ) ) + { + m_bFirstAssetScan = true; + m_bFinishedAssetListScan = false; + vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); + } + else + { + m_bFirstAssetScan = false; + m_bFinishedAssetListScan = true; + } + + int nAssetCount = s_AssetCache.GetAssetCount( m_hAssetList ); + for ( int i = 0; i < nAssetCount; ++i ) + { + AddAssetToList( i ); + } +} + + +//----------------------------------------------------------------------------- +// Rescan assets +//----------------------------------------------------------------------------- +void CBaseAssetPicker::RescanAssets() +{ + m_pAssetBrowser->RemoveAll(); + m_AssetList.RemoveAll(); + s_AssetCache.BeginAssetScan( m_hAssetList, true ); + m_bFirstAssetScan = true; + m_nMatchingAssets = 0; + + if ( m_bFinishedAssetListScan ) + { + m_bFinishedAssetListScan = false; + vgui::ivgui()->AddTickSignal( GetVPanel(), 10 ); + } +} + + +//----------------------------------------------------------------------------- +// Returns the mod path to the item index +//----------------------------------------------------------------------------- +const char *CBaseAssetPicker::GetModPath( int nModIndex ) +{ + return s_AssetCache.ModInfo( nModIndex ).m_Path.Get(); +} + + +//----------------------------------------------------------------------------- +// Command handler +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "AssetRescan" ) ) + { + RescanAssets(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Update column headers +//----------------------------------------------------------------------------- +void CBaseAssetPicker::UpdateAssetColumnHeader( ) +{ + char pColumnTitle[512]; + Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)%s", + m_pAssetType, m_nMatchingAssets, m_AssetList.Count(), m_bFinishedAssetListScan ? "" : " ..." ); + m_pAssetBrowser->SetColumnHeaderText( 1, pColumnTitle ); +} + + +//----------------------------------------------------------------------------- +// Request focus of the filter box +//----------------------------------------------------------------------------- +void CBaseAssetPicker::RequestFilterFocus() +{ + if ( m_Filter.Length() ) + { + m_pFilter->SelectAllOnFirstFocus( true ); + } + m_pFilter->RequestFocus(); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the asset list +//----------------------------------------------------------------------------- +void CBaseAssetPicker::RefreshAssetList( ) +{ + BuildAssetNameList(); + + // Check the filter matches + int nCount = m_AssetList.Count(); + m_nMatchingAssets = 0; + for ( int i = 0; i < nCount; ++i ) + { + // Filter based on active mod + bool bIsVisible = IsAssetVisible( i ); + m_pAssetBrowser->SetItemVisible( m_AssetList[i].m_nItemId, bIsVisible ); + if ( bIsVisible ) + { + ++m_nMatchingAssets; + } + } + + UpdateAssetColumnHeader(); + m_pAssetBrowser->SortList(); + + if ( ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) && ( m_pAssetBrowser->GetItemCount() > 0 ) ) + { + // Invoke a callback if the next selection will be a 'default' selection + OnNextSelectionIsDefault(); + int nItemID = m_pAssetBrowser->GetItemIDFromRow( 0 ); + m_pAssetBrowser->SetSelectedCell( nItemID, 0 ); + } + + m_pFileTree->RefreshFileList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on file folder changing +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnFileSelected() +{ + // update list + const char *pFolderFilter = ""; + int iItem = m_pFileTree->GetFirstSelectedItem(); + if ( iItem >= 0 ) + { + KeyValues *pkv = m_pFileTree->GetItemData( iItem ); + pFolderFilter = pkv->GetString( "path" ); + + // The first keys are always the subdir + pFolderFilter += Q_strlen( m_pAssetSubDir ); + if ( *pFolderFilter ) + { + ++pFolderFilter; + } + } + + if ( Q_stricmp( pFolderFilter, m_FolderFilter.Get() ) ) + { + int nLen = Q_strlen( pFolderFilter ); + m_FolderFilter = pFolderFilter; + if ( nLen > 0 ) + { + m_FolderFilter += '\\'; + } + RefreshAssetList(); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnTextChanged( KeyValues *pKeyValues ) +{ + vgui::Panel *pSource = (vgui::Panel*)pKeyValues->GetPtr( "panel" ); + if ( pSource == m_pFilter ) + { + int nLength = m_pFilter->GetTextLength(); + char *pNewFilter = (char*)_alloca( (nLength+1) * sizeof(char) ); + if ( nLength > 0 ) + { + m_pFilter->GetText( pNewFilter, nLength+1 ); + } + else + { + pNewFilter[0] = 0; + } + if ( Q_stricmp( pNewFilter, m_Filter.Get() ) ) + { + m_Filter.SetLength( nLength ); + m_Filter = pNewFilter; + RefreshAssetList(); + } + return; + } + + if ( pSource == m_pModSelector ) + { + KeyValues *pKeyValuesActive = m_pModSelector->GetActiveItemUserData(); + if ( pKeyValuesActive ) + { + m_nCurrentModFilter = pKeyValuesActive->GetInt( "mod", -1 ); + RefreshAssetList(); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates preview when an item is selected +//----------------------------------------------------------------------------- +void CBaseAssetPicker::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr( "panel", NULL ); + if ( pPanel == m_pAssetBrowser ) + { + int nCount = GetSelectedAssetCount(); + Assert( nCount > 0 ); + const char *pSelectedAsset = GetSelectedAsset( nCount - 1 ); + + // Fill in the full path + int nModIndex = GetSelectedAssetModIndex(); + char pBuf[MAX_PATH]; + Q_snprintf( pBuf, sizeof(pBuf), "%s\\%s\\%s", + s_AssetCache.ModInfo( nModIndex ).m_Path.Get(), m_pAssetSubDir, pSelectedAsset ); + Q_FixSlashes( pBuf ); + m_pFullPath->SetText( pBuf ); + + surface()->SetCursor( dc_waitarrow ); + OnSelectedAssetPicked( pSelectedAsset ); + return; + } +} + +void CBaseAssetPicker::OnCheckButtonChecked( KeyValues *kv ) +{ + vgui::Panel *pSource = (vgui::Panel*)kv->GetPtr( "panel" ); + if ( pSource == m_pSubDirCheck ) + { + m_bSubDirCheck = m_pSubDirCheck->IsSelected(); + RefreshAssetList(); + } +} + + +//----------------------------------------------------------------------------- +// Returns the selceted asset count +//----------------------------------------------------------------------------- +int CBaseAssetPicker::GetSelectedAssetCount() +{ + return m_pAssetBrowser->GetSelectedItemsCount(); +} + + +//----------------------------------------------------------------------------- +// Returns the selceted asset name +//----------------------------------------------------------------------------- +const char *CBaseAssetPicker::GetSelectedAsset( int nAssetIndex ) +{ + int nSelectedAssetCount = m_pAssetBrowser->GetSelectedItemsCount(); + if ( nAssetIndex < 0 ) + { + nAssetIndex = nSelectedAssetCount - 1; + } + if ( nSelectedAssetCount <= nAssetIndex || nAssetIndex < 0 ) + return NULL; + + int nIndex = m_pAssetBrowser->GetSelectedItem( nAssetIndex ); + KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex ); + return pItemKeyValues->GetString( "asset" ); +} + + +//----------------------------------------------------------------------------- +// Returns the selceted asset mod index +//----------------------------------------------------------------------------- +int CBaseAssetPicker::GetSelectedAssetModIndex( ) +{ + if ( m_pAssetBrowser->GetSelectedItemsCount() == 0 ) + return 0; + + int nIndex = m_pAssetBrowser->GetSelectedItem( 0 ); + KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( nIndex ); + return pItemKeyValues->GetInt( "modIndex" ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CBaseAssetPickerFrame::CBaseAssetPickerFrame( vgui::Panel *pParent ) : + BaseClass( pParent, "AssetPickerFrame" ) +{ + m_pContextKeyValues = NULL; + SetDeleteSelfOnClose( true ); + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); +} + +CBaseAssetPickerFrame::~CBaseAssetPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Allows the derived class to create the picker +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::SetAssetPicker( CBaseAssetPicker* pPicker ) +{ + m_pPicker = pPicker; + m_pPicker->AddActionSignalTarget( this ); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Sets the initial selected asset +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::SetInitialSelection( const char *pAssetName ) +{ + m_pPicker->SetInitialSelection( pAssetName ); +} + + +//----------------------------------------------------------------------------- +// Set/get the filter +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::SetFilter( const char *pFilter ) +{ + m_pPicker->SetFilter( pFilter ); +} + +const char *CBaseAssetPickerFrame::GetFilter() +{ + return m_pPicker->GetFilter( ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::DoModal( KeyValues *pKeyValues ) +{ + BaseClass::DoModal(); + CleanUpMessage(); + m_pContextKeyValues = pKeyValues; + m_pPicker->Activate(); +} + + +//----------------------------------------------------------------------------- +// Posts a message (passing the key values) +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::PostMessageAndClose( KeyValues *pKeyValues ) +{ + if ( m_pContextKeyValues ) + { + pKeyValues->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + CloseModal(); + PostActionSignal( pKeyValues ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CBaseAssetPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Open" ) ) + { + KeyValues *pActionKeys = new KeyValues( "AssetSelected" ); + if ( !m_pPicker->IsMultiselectEnabled() ) + { + const char *pAssetName = m_pPicker->GetSelectedAsset( ); + pActionKeys->SetString( "asset", pAssetName ); + } + else + { + char pBuf[512]; + KeyValues *pAssetKeys = pActionKeys->FindKey( "assets", true ); + int nCount = m_pPicker->GetSelectedAssetCount(); + for ( int i = 0; i < nCount; ++i ) + { + Q_snprintf( pBuf, sizeof(pBuf), "asset%d", i ); + pAssetKeys->SetString( pBuf, m_pPicker->GetSelectedAsset( i ) ); + } + } + PostMessageAndClose( pActionKeys ); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + diff --git a/src/vgui2/matsys_controls/colorpickerpanel.cpp b/src/vgui2/matsys_controls/colorpickerpanel.cpp new file mode 100644 index 000000000..848f78a57 --- /dev/null +++ b/src/vgui2/matsys_controls/colorpickerpanel.cpp @@ -0,0 +1,1255 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/colorpickerpanel.h" +#include "matsys_controls/matsyscontrols.h" +#include "matsys_controls/proceduraltexturepanel.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/itexture.h" +#include "pixelwriter.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/RadioButton.h" +#include "vgui/IInput.h" +#include "tier1/KeyValues.h" +#include "bitmap/imageformat.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Color picker +//----------------------------------------------------------------------------- +enum ColorType_t +{ + COLOR_TYPE_RGB = 0, + COLOR_TYPE_HSV, +}; + +enum ColorChannel_t +{ + CHANNEL_RED = 0, + CHANNEL_GREEN, + CHANNEL_BLUE, + + CHANNEL_HUE = 0, + CHANNEL_SATURATION, + CHANNEL_VALUE, +}; + + +//----------------------------------------------------------------------------- +// Converts RGB to normalized +//----------------------------------------------------------------------------- +static void RGB888ToVector( RGB888_t inColor, Vector *pOutVector ) +{ + pOutVector->Init( inColor.r / 255.0f, inColor.g / 255.0f, inColor.b / 255.0f ); +} + +static void VectorToRGB888( const Vector &inVector, RGB888_t &outColor ) +{ + int r = (int)((inVector.x * 255.0f) + 0.5f); + int g = (int)((inVector.y * 255.0f) + 0.5f); + int b = (int)((inVector.z * 255.0f) + 0.5f); + outColor.r = clamp( r, 0, 255 ); + outColor.g = clamp( g, 0, 255 ); + outColor.b = clamp( b, 0, 255 ); +} + + +//----------------------------------------------------------------------------- +// Convert RGB to HSV +//----------------------------------------------------------------------------- +static inline void RGBtoHSV( const RGB888_t &rgb, Vector &hsv ) +{ + Vector vecRGB; + RGB888ToVector( rgb, &vecRGB ); + RGBtoHSV( vecRGB, hsv ); +} + + +//----------------------------------------------------------------------------- +// Convert HSV to RGB +//----------------------------------------------------------------------------- +static inline void HSVtoRGB( const Vector &hsv, RGB888_t &rgb ) +{ + Vector vecRGB; + HSVtoRGB( hsv, vecRGB ); + VectorToRGB888( vecRGB, rgb ); +} + + +//----------------------------------------------------------------------------- +// This previews the 'xy' color +//----------------------------------------------------------------------------- +class CColorXYPreview : public CProceduralTexturePanel +{ + DECLARE_CLASS_SIMPLE( CColorXYPreview, CProceduralTexturePanel ); + +public: + // constructor + CColorXYPreview( vgui::Panel *pParent, const char *pName ); + + virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ); + virtual void Paint( void ); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + virtual void OnCursorMoved( int x, int y ); + + void SetMode( ColorType_t type, ColorChannel_t channel ); + void SetColor( const RGB888_t &color, const Vector &hsvColor ); + +private: + // Computes a color given a particular x,y value + void ComputeColorForPoint( int x, int y, RGB888_t &color ); + void ComputeHSVColorForPoint( int x, int y, Vector &vscHSV ); + + // Updates the color based on the mouse position + void UpdateColorFromMouse( int x, int y ); + + static ColorChannel_t s_pHSVRemapX[3]; + static ColorChannel_t s_pHSVRemapY[3]; + static ColorChannel_t s_pRGBRemapX[3]; + static ColorChannel_t s_pRGBRemapY[3]; + + ColorType_t m_Type; + ColorChannel_t m_Channel; + RGB888_t m_CurrentColor; + Vector m_CurrentHSVColor; + vgui::HCursor m_hPickerCursor; + bool m_bDraggingMouse; +}; + + +ColorChannel_t CColorXYPreview::s_pHSVRemapX[3] = +{ + CHANNEL_SATURATION, CHANNEL_HUE, CHANNEL_HUE +}; + +ColorChannel_t CColorXYPreview::s_pHSVRemapY[3] = +{ + CHANNEL_VALUE, CHANNEL_VALUE, CHANNEL_SATURATION +}; + +ColorChannel_t CColorXYPreview::s_pRGBRemapX[3] = +{ + CHANNEL_BLUE, CHANNEL_BLUE, CHANNEL_RED +}; + +ColorChannel_t CColorXYPreview::s_pRGBRemapY[3] = +{ + CHANNEL_GREEN, CHANNEL_RED, CHANNEL_GREEN +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CColorXYPreview::CColorXYPreview( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + Init( 256, 256, false ); + m_CurrentColor.r = m_CurrentColor.g = m_CurrentColor.b = 255; + SetMode( COLOR_TYPE_HSV, CHANNEL_HUE ); + SetMouseInputEnabled( true ); + m_hPickerCursor = surface()->CreateCursorFromFile( "resource/colorpicker.cur" ); + SetCursor( m_hPickerCursor ); + m_bDraggingMouse = false; +} + + +//----------------------------------------------------------------------------- +// Sets the mode for the preview +//----------------------------------------------------------------------------- +void CColorXYPreview::SetMode( ColorType_t type, ColorChannel_t channel ) +{ + if ( m_Type != type || m_Channel != channel ) + { + m_Type = type; + m_Channel = channel; + DownloadTexture(); + } +} + +void CColorXYPreview::SetColor( const RGB888_t &color, const Vector &hsvColor ) +{ + if ( color != m_CurrentColor || m_CurrentHSVColor != hsvColor ) + { + m_CurrentColor = color; + m_CurrentHSVColor = hsvColor; + DownloadTexture(); + } +} + + +//----------------------------------------------------------------------------- +// Computes a color given a particular x,y value +//----------------------------------------------------------------------------- +void CColorXYPreview::ComputeColorForPoint( int x, int y, RGB888_t &color ) +{ + color = m_CurrentColor; + ((unsigned char*)&color)[ s_pRGBRemapX[m_Channel] ] = x; + ((unsigned char*)&color)[ s_pRGBRemapY[m_Channel] ] = GetImageHeight() - y - 1; +} + +void CColorXYPreview::ComputeHSVColorForPoint( int x, int y, Vector &vscHSV ) +{ + vscHSV = m_CurrentHSVColor; + vscHSV[ s_pHSVRemapX[m_Channel] ] = (float)x / 255.0f; + vscHSV[ s_pHSVRemapY[m_Channel] ] = (float)(GetImageHeight() - y - 1) / 255.0f; + if ( vscHSV.y == 0.0f ) + { + vscHSV.x = -1.0f; + } + + if ( m_Channel != CHANNEL_HUE ) + { + if ( vscHSV.x != -1.0f ) + { + vscHSV.x *= 360.0f; + } + } +} + + +//----------------------------------------------------------------------------- +// Fills the texture w/ the image buffer +//----------------------------------------------------------------------------- +void CColorXYPreview::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) +{ + Assert( pVTFTexture->FrameCount() == 1 ); + Assert( pVTFTexture->FaceCount() == 1 ); + Assert( !pTexture->IsMipmapped() ); + + int nWidth, nHeight, nDepth; + pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth ); + Assert( nDepth == 1 ); + Assert( nWidth == m_nWidth && nHeight == m_nHeight ); + + CPixelWriter pixelWriter; + pixelWriter.SetPixelMemory( pVTFTexture->Format(), + pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) ); + + for ( int y = 0; y < nHeight; ++y ) + { + pixelWriter.Seek( 0, y ); + + for ( int x = 0; x < nWidth; ++x ) + { + RGB888_t color; + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSV; + ComputeHSVColorForPoint( x, y, vecHSV ); + HSVtoRGB( vecHSV, color ); + } + else + { + ComputeColorForPoint( x, y, color ); + } + + pixelWriter.WritePixel( color.r, color.g, color.b, 255 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Paints a circle over the currently selected color +//----------------------------------------------------------------------------- +void CColorXYPreview::Paint( void ) +{ + BaseClass::Paint(); + + int x, y; + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSVNormalized = m_CurrentHSVColor; + if ( vecHSVNormalized.x != -1.0f ) + { + vecHSVNormalized.x *= 255.0f / 360.0f; + } + vecHSVNormalized.y *= 255.0f; + vecHSVNormalized.z *= 255.0f; + + x = (int)( vecHSVNormalized[ s_pHSVRemapX[m_Channel] ] + 0.5f); + y = GetImageHeight() - 1 - (int)( vecHSVNormalized[ s_pHSVRemapY[m_Channel] ] + 0.5f ); + } + else + { + x = ((unsigned char*)&m_CurrentColor)[ s_pRGBRemapX[m_Channel] ]; + y = GetImageHeight() - 1 - ((unsigned char*)&m_CurrentColor)[ s_pRGBRemapY[m_Channel] ]; + } + + // Renormalize x, y to actual size + int w, h; + GetSize( w, h ); + x = (int)( w * (float)x / 255.0f + 0.5f ); + y = (int)( h * (float)y / 255.0f + 0.5f ); + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + vgui::surface()->DrawOutlinedCircle( x, y, 5, 8 ); + vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawOutlinedCircle( x, y, 6, 8 ); +} + + +//----------------------------------------------------------------------------- +// Updates the color based on the mouse position +//----------------------------------------------------------------------------- +void CColorXYPreview::UpdateColorFromMouse( int x, int y ) +{ + int w, h; + GetSize( w, h ); + + float flNormalizedX = (float)x / (w-1); + float flNormalizedY = (float)y / (h-1); + flNormalizedX = clamp( flNormalizedX, 0.0f, 1.0f ); + flNormalizedY = clamp( flNormalizedY, 0.0f, 1.0f ); + + int tx = (int)( (GetImageWidth()-1) * flNormalizedX + 0.5f ); + int ty = (int)( (GetImageHeight()-1) * flNormalizedY + 0.5f ); + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSV; + ComputeHSVColorForPoint( tx, ty, vecHSV ); + + KeyValues *pKeyValues = new KeyValues( "HSVSelected" ); + pKeyValues->SetFloat( "hue", vecHSV.x ); + pKeyValues->SetFloat( "saturation", vecHSV.y ); + pKeyValues->SetFloat( "value", vecHSV.z ); + PostActionSignal( pKeyValues ); + + // This prevents a 1-frame lag in the current color position + RGB888_t color; + HSVtoRGB( vecHSV, color ); + SetColor( color, vecHSV ); + } + else + { + RGB888_t color; + ComputeColorForPoint( tx, ty, color ); + + Color c( color.r, color.g, color.b, 255 ); + KeyValues *pKeyValues = new KeyValues( "ColorSelected" ); + pKeyValues->SetColor( "color", c ); + PostActionSignal( pKeyValues ); + + // This prevents a 1-frame lag in the current color position + Vector vecHSV; + RGBtoHSV( color, vecHSV ); + SetColor( color, vecHSV ); + } +} + + +//----------------------------------------------------------------------------- +// Handle input +//----------------------------------------------------------------------------- +void CColorXYPreview::OnMousePressed( vgui::MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + + if ( code == MOUSE_LEFT ) + { + if ( !m_bDraggingMouse ) + { + m_bDraggingMouse = true; + input()->SetMouseCapture(GetVPanel()); + + int x, y; + input()->GetCursorPos( x, y ); + ScreenToLocal( x, y ); + + UpdateColorFromMouse( x, y ); + } + } +} + +void CColorXYPreview::OnMouseReleased( vgui::MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + + if ( code == MOUSE_LEFT ) + { + if ( m_bDraggingMouse ) + { + m_bDraggingMouse = false; + input()->SetMouseCapture( (VPANEL)0 ); + } + } +} + +void CColorXYPreview::OnCursorMoved( int x, int y ) +{ + BaseClass::OnCursorMoved( x, y ); + + if ( m_bDraggingMouse ) + { + UpdateColorFromMouse( x, y ); + } +} + + +//----------------------------------------------------------------------------- +// This previews the 'z' color +//----------------------------------------------------------------------------- +class CColorZPreview : public CProceduralTexturePanel +{ + DECLARE_CLASS_SIMPLE( CColorZPreview, CProceduralTexturePanel ); + +public: + // constructor + CColorZPreview( vgui::Panel *pParent, const char *pName ); + + virtual void RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ); + virtual void PerformLayout(); + virtual void Paint( void ); + virtual void OnCursorMoved( int x,int y ); + virtual void OnMousePressed( vgui::MouseCode code ); + virtual void OnMouseReleased( vgui::MouseCode code ); + + void SetMode( ColorType_t type, ColorChannel_t channel ); + void SetColor( const RGB888_t &color, const Vector &hsvColor ); + + // Computes a color given a particular x,y value + void ComputeColorForPoint( int y, RGB888_t &color ); + void ComputeHSVColorForPoint( int y, bool bProceduralTexture, Vector &vecHSV ); + +private: + // Updates the color based on the mouse position + void UpdateColorFromMouse( int x, int y ); + + ColorType_t m_Type; + ColorChannel_t m_Channel; + RGB888_t m_CurrentColor; + Vector m_CurrentHSVColor; + bool m_bDraggingMouse; +}; + +#define MARKER_WIDTH 6 + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CColorZPreview::CColorZPreview( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + Init( 8, 256, false ); + m_CurrentColor.r = m_CurrentColor.g = m_CurrentColor.b = 255; + + Vector vecRGB; + RGB888ToVector( m_CurrentColor, &vecRGB ); + RGBtoHSV( vecRGB, m_CurrentHSVColor ); + m_bDraggingMouse = false; + + SetMouseInputEnabled( true ); + SetMode( COLOR_TYPE_HSV, CHANNEL_HUE ); +} + + +//----------------------------------------------------------------------------- +// Sets the mode for the preview +//----------------------------------------------------------------------------- +void CColorZPreview::SetMode( ColorType_t type, ColorChannel_t channel ) +{ + if ( m_Type != type || m_Channel != channel ) + { + m_Type = type; + m_Channel = channel; + DownloadTexture(); + } +} + +void CColorZPreview::SetColor( const RGB888_t &color, const Vector &hsvColor ) +{ + if ( color != m_CurrentColor || m_CurrentHSVColor != hsvColor ) + { + m_CurrentColor = color; + m_CurrentHSVColor = hsvColor; + DownloadTexture(); + } +} + + +//----------------------------------------------------------------------------- +// Lays out the panel +//----------------------------------------------------------------------------- +void CColorZPreview::PerformLayout() +{ + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + Rect_t r; + r.x = MARKER_WIDTH; + r.y = MARKER_WIDTH; + r.width = w - (MARKER_WIDTH*2); + r.height = h - (MARKER_WIDTH*2); + + SetPaintRect( &r ); +} + + +//----------------------------------------------------------------------------- +// Computes a color given a particular x,y value +//----------------------------------------------------------------------------- +void CColorZPreview::ComputeColorForPoint( int y, RGB888_t &color ) +{ + color = m_CurrentColor; + ((unsigned char*)&color)[ m_Channel ] = GetImageHeight() - y - 1; +} + +void CColorZPreview::ComputeHSVColorForPoint( int y, bool bProceduralTexture, Vector &vecHSV ) +{ + vecHSV = m_CurrentHSVColor; + vecHSV[ m_Channel ] = (float)(GetImageHeight() - y - 1) / 255.0f; + if ( m_Channel == CHANNEL_HUE ) + { + if ( vecHSV.x != -1.0f ) + { + vecHSV.x *= 360.0f; + } + + if ( bProceduralTexture ) + { + vecHSV.y = 1.0f; + vecHSV.z = 1.0f; + } + } +} + + +//----------------------------------------------------------------------------- +// Fills the texture w/ the image buffer +//----------------------------------------------------------------------------- +void CColorZPreview::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) +{ + Assert( pVTFTexture->FrameCount() == 1 ); + Assert( pVTFTexture->FaceCount() == 1 ); + Assert( !pTexture->IsMipmapped() ); + + int nWidth, nHeight, nDepth; + pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth ); + Assert( nDepth == 1 ); + Assert( nWidth == m_nWidth && nHeight == m_nHeight ); + + CPixelWriter pixelWriter; + pixelWriter.SetPixelMemory( pVTFTexture->Format(), + pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) ); + + for ( int y = 0; y < nHeight; ++y ) + { + pixelWriter.Seek( 0, y ); + + RGB888_t color; + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSV; + ComputeHSVColorForPoint( y, true, vecHSV ); + HSVtoRGB( vecHSV, color ); + } + else + { + ComputeColorForPoint( y, color ); + } + + for ( int x = 0; x < nWidth; ++x ) + { + pixelWriter.WritePixel( color.r, color.g, color.b, 255 ); + } + } +} + + +//----------------------------------------------------------------------------- +// Updates the color based on the mouse position +//----------------------------------------------------------------------------- +void CColorZPreview::UpdateColorFromMouse( int x, int y ) +{ + int w, h; + GetSize( w, h ); + h -= 2 * MARKER_WIDTH; + + float flNormalizedY = (float)( y - MARKER_WIDTH ) / (h-1); + flNormalizedY = clamp( flNormalizedY, 0.0f, 1.0f ); + + int ty = (int)( (GetImageHeight() - 1) * flNormalizedY + 0.5f ); + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSV; + ComputeHSVColorForPoint( ty, false, vecHSV ); + + KeyValues *pKeyValues = new KeyValues( "HSVSelected" ); + pKeyValues->SetFloat( "hue", vecHSV.x ); + pKeyValues->SetFloat( "saturation", vecHSV.y ); + pKeyValues->SetFloat( "value", vecHSV.z ); + PostActionSignal( pKeyValues ); + + // This prevents a 1-frame lag in the current color position + RGB888_t color; + HSVtoRGB( vecHSV, color ); + SetColor( color, vecHSV ); + } + else + { + RGB888_t color; + ComputeColorForPoint( ty, color ); + + Color c( color.r, color.g, color.b, 255 ); + KeyValues *pKeyValues = new KeyValues( "ColorSelected" ); + pKeyValues->SetColor( "color", c ); + PostActionSignal( pKeyValues ); + + // This prevents a 1-frame lag in the current color position + Vector vecHSV; + RGBtoHSV( color, vecHSV ); + SetColor( color, vecHSV ); + } +} + + +//----------------------------------------------------------------------------- +// Handle input +//----------------------------------------------------------------------------- +void CColorZPreview::OnMousePressed( vgui::MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + + if ( code == MOUSE_LEFT ) + { + if ( !m_bDraggingMouse ) + { + m_bDraggingMouse = true; + input()->SetMouseCapture(GetVPanel()); + + int x, y; + input()->GetCursorPos( x, y ); + ScreenToLocal( x, y ); + + UpdateColorFromMouse( x, y ); + } + } +} + +void CColorZPreview::OnMouseReleased( vgui::MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + + if ( code == MOUSE_LEFT ) + { + if ( m_bDraggingMouse ) + { + m_bDraggingMouse = false; + input()->SetMouseCapture( (VPANEL)0 ); + } + } +} + +void CColorZPreview::OnCursorMoved( int x, int y ) +{ + BaseClass::OnCursorMoved( x, y ); + + if ( m_bDraggingMouse ) + { + UpdateColorFromMouse( x, y ); + } +} + + +//----------------------------------------------------------------------------- +// Paints the panel (the two arrows, specifically) +//----------------------------------------------------------------------------- +void CColorZPreview::Paint( void ) +{ + BaseClass::Paint(); + + int y; + if ( m_Type != COLOR_TYPE_RGB ) + { + Vector vecHSVNormalized = m_CurrentHSVColor; + if ( vecHSVNormalized.x != -1.0f ) + { + vecHSVNormalized.x *= 255.0f / 360.0f; + } + vecHSVNormalized.y *= 255.0f; + vecHSVNormalized.z *= 255.0f; + + y = GetImageHeight() - 1 - (int)( vecHSVNormalized[ m_Channel ] + 0.5f ); + } + else + { + y = GetImageHeight() - 1 - ((unsigned char*)&m_CurrentColor)[ m_Channel ]; + } + + // Renormalize y to actual size + int w, h; + GetSize( w, h ); + h -= 2 * MARKER_WIDTH; + y = (int)( h * (float)y / 255.0f + 0.5f ); + + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + + int px[3] = { 0, 0, MARKER_WIDTH }; + int py[3] = { MARKER_WIDTH + y - MARKER_WIDTH, MARKER_WIDTH + y + MARKER_WIDTH, MARKER_WIDTH + y }; + vgui::surface()->DrawPolyLine( px, py, 3 ); + + px[0] = px[1] = w-1; + px[2] = w - 1 - MARKER_WIDTH; + vgui::surface()->DrawPolyLine( px, py, 3 ); +} + + +//----------------------------------------------------------------------------- +// +// Color picker panel +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CColorPickerPanel::CColorPickerPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + m_pColorXYPreview = new CColorXYPreview( this, "ColorXYPreview" ); + m_pColorZPreview = new CColorZPreview( this, "ColorZPreview" ); + m_pColorXYPreview->AddActionSignalTarget( this ); + + m_pHueRadio = new RadioButton( this, "HueRadio", "H" ); + m_pSaturationRadio = new RadioButton( this, "SaturationRadio", "S" ); + m_pValueRadio = new RadioButton( this, "ValueRadio", "V" ); + m_pRedRadio = new RadioButton( this, "RedRadio", "R" ); + m_pGreenRadio = new RadioButton( this, "GreenRadio", "G" ); + m_pBlueRadio = new RadioButton( this, "BlueRadio", "B" ); + + m_pHueText = new TextEntry( this, "HueText" ); + m_pSaturationText = new TextEntry( this, "SaturationText"); + m_pValueText = new TextEntry( this, "ValueText" ); + m_pRedText = new TextEntry( this, "RedText" ); + m_pGreenText = new TextEntry( this, "GreenText" ); + m_pBlueText = new TextEntry( this, "BlueText" ); + m_pAlphaText= new TextEntry( this, "AlphaText" ); + + m_pInitialColor = new Panel( this, "InitialColor" ); + m_pCurrentColor = new Panel( this, "CurrentColor" ); + m_pInitialColor->SetVisible( true ); + m_pCurrentColor->SetVisible( true ); + m_pInitialColor->SetPaintBackgroundEnabled( true ); + m_pCurrentColor->SetPaintBackgroundEnabled( true ); + + m_pInitialColor->SetMouseInputEnabled( false ); + SetMouseInputEnabled( true ); + + Color c( 255, 255, 255, 255 ); + SetInitialColor( c ); + + LoadControlSettings( "resource/colorpicker.res" ); +} + + +//----------------------------------------------------------------------------- +// Sets the initial color +//----------------------------------------------------------------------------- +void CColorPickerPanel::SetInitialColor( Color initialColor ) +{ + m_InitialColor.r = initialColor.r(); + m_InitialColor.g = initialColor.g(); + m_InitialColor.b = initialColor.b(); + + m_CurrentAlpha = initialColor.a(); + m_InitialAlpha = m_CurrentAlpha; + m_CurrentColor = m_InitialColor; + + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + if ( m_CurrentHSVColor.x == -1 ) + { + m_CurrentHSVColor.x = 0; + } + OnColorChanged(); +} + + +//----------------------------------------------------------------------------- +// Handle input +//----------------------------------------------------------------------------- +void CColorPickerPanel::OnMousePressed( vgui::MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + + if ( code == MOUSE_LEFT ) + { + // Clicking inside the initial color window + // resets the current color to the initial color + int x, y; + input()->GetCursorPos( x, y ); + ScreenToLocal( x, y ); + + int cx, cy, cw, ch; + m_pInitialColor->GetBounds( cx, cy, cw, ch ); + if ( ( cx <= x ) && ( cx+cw > x ) && ( cy <= y ) && ( cy+ch > y ) ) + { + m_CurrentColor = m_InitialColor; + m_CurrentAlpha = m_InitialAlpha; + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + if ( m_CurrentHSVColor.x == -1 ) + { + m_CurrentHSVColor.x = 0; + } + OnColorChanged(); + } + } +} + + +//----------------------------------------------------------------------------- +// Gets the current/initial color +//----------------------------------------------------------------------------- +void CColorPickerPanel::GetCurrentColor( Color *pColor ) +{ + pColor->SetColor( m_CurrentColor.r, m_CurrentColor.g, m_CurrentColor.b, m_CurrentAlpha ); +} + +void CColorPickerPanel::GetInitialColor( Color *pColor ) +{ + pColor->SetColor( m_InitialColor.r, m_InitialColor.g, m_InitialColor.b, m_InitialAlpha ); +} + + +//----------------------------------------------------------------------------- +// Updates the preview colors +//----------------------------------------------------------------------------- +void CColorPickerPanel::UpdatePreviewColors() +{ + Color c; + c.SetColor( m_InitialColor.r, m_InitialColor.g, m_InitialColor.b, 255 ); + m_pInitialColor->SetBgColor( c ); + c.SetColor( m_CurrentColor.r, m_CurrentColor.g, m_CurrentColor.b, 255 ); + m_pCurrentColor->SetBgColor( c ); +} + + +//----------------------------------------------------------------------------- +// Used to make sure we win over the scheme settings +//----------------------------------------------------------------------------- +void CColorPickerPanel::ApplySchemeSettings(IScheme *pScheme) +{ + // Need to override the scheme settings for this button + BaseClass::ApplySchemeSettings( pScheme ); + UpdatePreviewColors(); +} + + +//----------------------------------------------------------------------------- +// Callbacks from the color preview dialogs +//----------------------------------------------------------------------------- +void CColorPickerPanel::OnHSVSelected( KeyValues *data ) +{ + m_CurrentHSVColor.x = data->GetFloat( "hue" ); + m_CurrentHSVColor.y = data->GetFloat( "saturation" ); + m_CurrentHSVColor.z = data->GetFloat( "value" ); + HSVtoRGB( m_CurrentHSVColor, m_CurrentColor ); + OnColorChanged(); +} + +void CColorPickerPanel::OnColorSelected( KeyValues *data ) +{ + Color c = data->GetColor( "color" ); + m_CurrentColor.r = c.r(); + m_CurrentColor.g = c.g(); + m_CurrentColor.b = c.b(); + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + OnColorChanged(); +} + + +//----------------------------------------------------------------------------- +// Radio buttons +//----------------------------------------------------------------------------- +void CColorPickerPanel::OnRadioButtonChecked( KeyValues *pKeyValues ) +{ + // NOTE: The radio button command strings are defined in the colorpicker.res file + // in game/platform/resource. + vgui::Panel *pPanel = (vgui::Panel *)pKeyValues->GetPtr( "panel" ); + if ( pPanel == m_pRedRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_RED ); + m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_RED ); + } + else if ( pPanel == m_pGreenRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_GREEN ); + m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_GREEN ); + } + else if ( pPanel == m_pBlueRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_BLUE ); + m_pColorZPreview->SetMode( COLOR_TYPE_RGB, CHANNEL_BLUE ); + } + else if ( pPanel == m_pHueRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_HUE ); + m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_HUE ); + } + else if ( pPanel == m_pSaturationRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_SATURATION ); + m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_SATURATION ); + } + else if ( pPanel == m_pValueRadio ) + { + m_pColorXYPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_VALUE ); + m_pColorZPreview->SetMode( COLOR_TYPE_HSV, CHANNEL_VALUE ); + } +} + + +//----------------------------------------------------------------------------- +// Called when the color changes +//----------------------------------------------------------------------------- +void CColorPickerPanel::OnColorChanged( vgui::TextEntry *pChanged ) +{ + char temp[256]; + + if ( pChanged != m_pRedText ) + { + Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.r ); + m_pRedText->SetText( temp ); + } + if ( pChanged != m_pGreenText ) + { + Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.g ); + m_pGreenText->SetText( temp ); + } + if ( pChanged != m_pBlueText ) + { + Q_snprintf( temp, sizeof(temp), "%d", m_CurrentColor.b ); + m_pBlueText->SetText( temp ); + } + if ( pChanged != m_pAlphaText ) + { + Q_snprintf( temp, sizeof( temp ), "%d", m_CurrentAlpha ); + m_pAlphaText->SetText( temp ); + } + + if ( pChanged != m_pHueText ) + { + Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.x + 0.5f) ); + m_pHueText->SetText( temp ); + } + if ( pChanged != m_pSaturationText ) + { + Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.y * 100 + 0.5f) ); + m_pSaturationText->SetText( temp ); + } + if ( pChanged != m_pValueText ) + { + Q_snprintf( temp, sizeof(temp), "%d", (int)(m_CurrentHSVColor.z * 100 + 0.5f) ); + m_pValueText->SetText( temp ); + } + + m_pColorXYPreview->SetColor( m_CurrentColor, m_CurrentHSVColor ); + m_pColorZPreview->SetColor( m_CurrentColor, m_CurrentHSVColor ); + UpdatePreviewColors(); + PostActionSignal( new KeyValues( "command", "command", "preview" ) ); +} + + +//----------------------------------------------------------------------------- +// Called when the color text entry panels change +//----------------------------------------------------------------------------- +void CColorPickerPanel::OnTextChanged( KeyValues *data ) +{ + Panel *pPanel = (Panel *)data->GetPtr( "panel", NULL ); + + float flHue = m_CurrentHSVColor.x; + + char buf[256]; + if ( pPanel == m_pRedText ) + { + m_pRedText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + m_CurrentColor.r = clamp( val, 0, 255 ); + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + } + else if ( pPanel == m_pGreenText ) + { + m_pGreenText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + m_CurrentColor.g = clamp( val, 0, 255 ); + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + } + else if ( pPanel == m_pBlueText ) + { + m_pBlueText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + m_CurrentColor.b = clamp( val, 0, 255 ); + RGBtoHSV( m_CurrentColor, m_CurrentHSVColor ); + } + else if ( pPanel == m_pAlphaText ) + { + m_pAlphaText->GetText( buf, sizeof( buf ) ); + int val = atoi( buf ); + m_CurrentAlpha = clamp( val, 0, 255 ); + } + else if ( pPanel == m_pHueText ) + { + m_pHueText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + m_CurrentHSVColor.x = clamp( val, 0, 360 ); + HSVtoRGB( m_CurrentHSVColor, m_CurrentColor ); + } + else if ( pPanel == m_pSaturationText ) + { + m_pSaturationText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + val = clamp( val, 0, 100 ); + m_CurrentHSVColor.y = (float)val / 100.0f; + HSVtoRGB( m_CurrentHSVColor, m_CurrentColor ); + } + else if ( pPanel == m_pValueText ) + { + m_pValueText->GetText( buf, sizeof(buf) ); + int val = atoi( buf ); + val = clamp( val, 0, 100 ); + m_CurrentHSVColor.z = (float)val / 100.0f; + HSVtoRGB( m_CurrentHSVColor, m_CurrentColor ); + } + + // Preserve hue + if ( m_CurrentHSVColor.x == -1 ) + { + m_CurrentHSVColor.x = flHue; + } + OnColorChanged( static_cast(pPanel) ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CColorPickerFrame::CColorPickerFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent, "ColorPickerFrame" ) +{ + m_pContextKeys = NULL; + SetDeleteSelfOnClose( true ); + m_pPicker = new CColorPickerPanel( this, "ColorPicker" ); + m_pPicker->AddActionSignalTarget( this ); + m_pOpenButton = new Button( this, "OkButton", "Ok", this, "Ok" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettings( "resource/colorpickerframe.res" ); + + int w, h; + GetSize( w, h ); + SetMinimumSize( w, h ); + + SetTitle( pTitle, false ); +} + +CColorPickerFrame::~CColorPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CColorPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeys ) + { + m_pContextKeys->deleteThis(); + m_pContextKeys = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CColorPickerFrame::DoModal( Color initialColor, KeyValues *pContextKeys ) +{ + CleanUpMessage(); + m_pPicker->SetInitialColor( initialColor ); + m_pContextKeys = pContextKeys; + + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// Gets the initial color +//----------------------------------------------------------------------------- +void CColorPickerFrame::GetInitialColor( Color *pColor ) +{ + m_pPicker->GetInitialColor( pColor ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CColorPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Ok" ) ) + { + Color c; + m_pPicker->GetCurrentColor( &c ); + + KeyValues *pActionKeys = new KeyValues( "ColorPickerPicked" ); + pActionKeys->SetColor( "color", c ); + if ( m_pContextKeys ) + { + pActionKeys->AddSubKey( m_pContextKeys ); + m_pContextKeys = NULL; + } + CloseModal(); + PostActionSignal( pActionKeys ); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + vgui::input()->ReleaseAppModalSurface(); + KeyValues *pActionKeys = new KeyValues( "ColorPickerCancel" ); + if ( m_pContextKeys ) + { + pActionKeys->AddSubKey( m_pContextKeys ); + m_pContextKeys = NULL; + } + CloseModal(); + PostActionSignal( pActionKeys ); + return; + } + + if ( !Q_stricmp( pCommand, "Preview" ) ) + { + Color c; + m_pPicker->GetCurrentColor( &c ); + + KeyValues *pActionKeys = new KeyValues( "ColorPickerPreview" ); + pActionKeys->SetColor( "color", c ); + if ( m_pContextKeys ) + { + pActionKeys->AddSubKey( m_pContextKeys->MakeCopy() ); + } + PostActionSignal( pActionKeys ); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: A button which brings up the color picker +// +//----------------------------------------------------------------------------- +CColorPickerButton::CColorPickerButton( vgui::Panel *pParent, const char *pName, vgui::Panel *pActionSignalTarget ) : + BaseClass( pParent, pName, "" ) +{ + m_CurrentColor.SetColor( 255, 255, 255, 255 ); + if ( pActionSignalTarget ) + { + AddActionSignalTarget( pActionSignalTarget ); + } +} + +CColorPickerButton::~CColorPickerButton() +{ +} + +void CColorPickerButton::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + UpdateButtonColor(); +} + + +//----------------------------------------------------------------------------- +// Called when the picker gets a new color +//----------------------------------------------------------------------------- +void CColorPickerButton::OnPicked( KeyValues *data ) +{ + SetColor( data->GetColor( "color" ) ); + + // Fire normal action signal messages + KeyValues *pMessage = new KeyValues( "ColorPickerPicked" ); + pMessage->SetColor( "color", m_CurrentColor ); + PostActionSignal( pMessage ); + PlayButtonReleasedSound(); + SetSelected( false ); +} + + +//----------------------------------------------------------------------------- +// Called when a color is previewed +//----------------------------------------------------------------------------- +void CColorPickerButton::OnPreview( KeyValues *data ) +{ + KeyValues *pMessage = new KeyValues( "ColorPickerPreview" ); + pMessage->SetColor( "color", data->GetColor( "color" ) ); + PostActionSignal( pMessage ); +} + + +//----------------------------------------------------------------------------- +// Called when cancel is hit in the picker +//----------------------------------------------------------------------------- +void CColorPickerButton::OnCancelled( ) +{ + SetSelected( false ); + + KeyValues *pMessage = new KeyValues( "ColorPickerCancel" ); + pMessage->SetColor( "startingColor", m_CurrentColor ); + PostActionSignal( pMessage ); +} + + +//----------------------------------------------------------------------------- +// Perform the click +//----------------------------------------------------------------------------- +void CColorPickerButton::DoClick() +{ + SetSelected( true ); + + CColorPickerFrame *pColorPickerDialog = new CColorPickerFrame( this, "Select Color" ); + pColorPickerDialog->AddActionSignalTarget( this ); + pColorPickerDialog->DoModal( m_CurrentColor ); +} + + +//----------------------------------------------------------------------------- +// Set current color +//----------------------------------------------------------------------------- +void CColorPickerButton::SetColor( const Color& clr ) +{ + m_CurrentColor = clr; + UpdateButtonColor(); +} + +void CColorPickerButton::SetColor( int r, int g, int b, int a ) +{ + m_CurrentColor.SetColor( r, g, b, a ); + UpdateButtonColor(); +} + + +//----------------------------------------------------------------------------- +// Update button color +//----------------------------------------------------------------------------- +void CColorPickerButton::UpdateButtonColor() +{ + SetDefaultColor( m_CurrentColor, m_CurrentColor ); + SetArmedColor( m_CurrentColor, m_CurrentColor ); + SetDepressedColor( m_CurrentColor, m_CurrentColor ); +} diff --git a/src/vgui2/matsys_controls/curveeditorpanel.cpp b/src/vgui2/matsys_controls/curveeditorpanel.cpp new file mode 100644 index 000000000..186444d99 --- /dev/null +++ b/src/vgui2/matsys_controls/curveeditorpanel.cpp @@ -0,0 +1,200 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/curveeditorpanel.h" +#include "matsys_controls/matsyscontrols.h" +#include "tier1/KeyValues.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "vgui/MouseCode.h" + +using namespace vgui; + + + +//----------------------------------------------------------------------------- +// constructor, destructor +//----------------------------------------------------------------------------- +CCurveEditorPanel::CCurveEditorPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_nSelectedPoint = -1; + SetMouseInputEnabled( true ); + SetKeyBoardInputEnabled( true ); + m_nHighlightedPoint = -1; +} + +CCurveEditorPanel::~CCurveEditorPanel() +{ +} + + +//----------------------------------------------------------------------------- +// Converts screen location to normalized values +//----------------------------------------------------------------------------- +void CCurveEditorPanel::ScreenToValue( int x, int y, float *pIn, float *pOut ) +{ + int w, h; + GetSize( w, h ); + + *pIn = (float)x / (w-1); + *pOut = 1.0f - ((float)y / (h-1)); +} + +void CCurveEditorPanel::ValueToScreen( float flIn, float flOut, int *x, int *y ) +{ + int w, h; + GetSize( w, h ); + + *x = (int)(flIn * (w-1) + 0.5f); + *y = (h-1) - (int)(flOut * (h-1) + 0.5f); +} + + +//----------------------------------------------------------------------------- +// Handle input +//----------------------------------------------------------------------------- +void CCurveEditorPanel::OnMousePressed( vgui::MouseCode code ) +{ + BaseClass::OnMousePressed( code ); + + int x, y; + input()->GetCursorPos( x, y ); + ScreenToLocal( x, y ); + + if ( code == MOUSE_LEFT ) + { + int w, h; + GetSize( w, h ); + + float flIn, flOut; + ScreenToValue( x, y, &flIn, &flOut ); + float flTolerance = 5.0f / (w-1); // +/- 3 pixels to select the point + m_nSelectedPoint = FindOrAddControlPoint( flIn, flTolerance, flOut ); + } +} + +void CCurveEditorPanel::OnMouseReleased( vgui::MouseCode code ) +{ + BaseClass::OnMouseReleased( code ); + + if ( code == MOUSE_LEFT ) + { + m_nSelectedPoint = -1; + } +} + +void CCurveEditorPanel::OnCursorMoved( int x, int y ) +{ + BaseClass::OnCursorMoved( x, y ); + + float flIn, flOut; + ScreenToValue( x, y, &flIn, &flOut ); + + int w, h; + GetSize( w, h ); + float flTolerance = 5.0f / (w-1); // +/- 3 pixels to select the point + m_nHighlightedPoint = FindControlPoint( flIn, flTolerance ); + + if ( m_nSelectedPoint < 0 ) + return; + m_nSelectedPoint = ModifyControlPoint( m_nSelectedPoint, flIn, flOut ); +} + + +//----------------------------------------------------------------------------- +// Handles keypresses +//----------------------------------------------------------------------------- +void CCurveEditorPanel::OnKeyCodePressed( vgui::KeyCode code ) +{ + BaseClass::OnKeyCodePressed( code ); + + if ( m_nSelectedPoint >= 0 ) + { + RemoveControlPoint( m_nSelectedPoint ); + m_nSelectedPoint = -1; + } +} + + +//----------------------------------------------------------------------------- +// This paints the grid behind the curves +//----------------------------------------------------------------------------- +void CCurveEditorPanel::PaintBackground( void ) +{ + int w, h; + GetSize( w, h ); + + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + vgui::surface()->DrawFilledRect( 0, 0, w, h ); + + vgui::surface()->DrawSetColor( 128, 128, 128, 255 ); + vgui::surface()->DrawLine( 0, h/4, w, h/4 ); + vgui::surface()->DrawLine( 0, h/2, w, h/2 ); + vgui::surface()->DrawLine( 0, 3*h/4, w, 3*h/4 ); + + vgui::surface()->DrawLine( w/4, 0, w/4, h ); + vgui::surface()->DrawLine( w/2, 0, w/2, h ); + vgui::surface()->DrawLine( 3*w/4, 0, 3*w/4, h ); + + vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawLine( 0, 0, w, 0 ); + vgui::surface()->DrawLine( w, 0, w, h ); + vgui::surface()->DrawLine( w, h, 0, h ); + vgui::surface()->DrawLine( 0, h, 0, 0 ); +} + + +//----------------------------------------------------------------------------- +// Sets the color curves operation to edit +//----------------------------------------------------------------------------- +void CCurveEditorPanel::Paint( void ) +{ + int w, h; + GetSize( w, h ); + + int x0 = 0, y0 = 0; + + // FIXME: Add method to draw multiple lines DrawPolyLine connects the 1st and last points... bleah + vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + for ( int i = 0; i < w; ++i ) + { + float flIn = (float)i / (w-1); + float flOut = GetValue( flIn ); + int x = i; + int y = (h-1) - (int)(flOut * (h-1) + 0.5f); + y = clamp( y, 0, h-1 ); + + if ( i != 0 ) + { + vgui::surface()->DrawLine( x0, y0, x, y ); + } + + x0 = x; y0 = y; + } + + + // This will paint the control points + // The currently selected one will be painted red and filled. + for ( int i = ControlPointCount(); --i >= 0; ) + { + float flIn, flOut; + GetControlPoint( i, &flIn, &flOut ); + + int cx, cy; + ValueToScreen( flIn, flOut, &cx, &cy ); + + if ( (i == m_nSelectedPoint) || ((m_nSelectedPoint == -1) && (i == m_nHighlightedPoint)) ) + { + vgui::surface()->DrawSetColor( 255, 0, 0, 255 ); + vgui::surface()->DrawFilledRect( cx-3, cy-3, cx+3, cy+3 ); + } + else + { + vgui::surface()->DrawSetColor( 0, 0, 0, 255 ); + vgui::surface()->DrawOutlinedRect( cx-3, cy-3, cx+3, cy+3 ); + } + } +} \ No newline at end of file diff --git a/src/vgui2/matsys_controls/gamefiletreeview.cpp b/src/vgui2/matsys_controls/gamefiletreeview.cpp new file mode 100644 index 000000000..712d54f23 --- /dev/null +++ b/src/vgui2/matsys_controls/gamefiletreeview.cpp @@ -0,0 +1,301 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#if defined(WIN32) && !defined( _X360 ) +#include +#endif +#undef PropertySheet + +#include "matsys_controls/gamefiletreeview.h" +#include "filesystem.h" +#include "tier1/KeyValues.h" +#include "vgui/ISurface.h" +#include "vgui/Cursor.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// list of all tree view icons +//----------------------------------------------------------------------------- +enum +{ + IMAGE_FOLDER = 1, + IMAGE_OPENFOLDER, + IMAGE_FILE, +}; + + +//----------------------------------------------------------------------------- +// Constructor +//----------------------------------------------------------------------------- +CGameFileTreeView::CGameFileTreeView( Panel *parent, const char *name, const char *pRootFolderName, const char *pRootDir, const char *pExtension ) : BaseClass(parent, name), m_Images( false ) +{ + m_RootDir = pRootDir; + + m_Ext = pExtension; + m_bUseExt = ( pExtension != NULL ); + + m_RootFolderName = pRootFolderName; + + // build our list of images + m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); + m_Images.AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) ); + m_Images.AddImage( scheme()->GetImage( "resource/icon_file", false ) ); + SetImageList( &m_Images, false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Refreshes the active file list +//----------------------------------------------------------------------------- +void CGameFileTreeView::RefreshFileList() +{ + RemoveAll(); + SetFgColor(Color(216, 222, 211, 255)); + + // add the base node + KeyValues *pkv = new KeyValues( "root" ); + pkv->SetString( "text", m_RootFolderName ); + pkv->SetInt( "root", 1 ); + pkv->SetInt( "expand", 1 ); + int iRoot = AddItem( pkv, GetRootItemIndex() ); + pkv->deleteThis(); + ExpandItem( iRoot, true ); +} + + +//----------------------------------------------------------------------------- +// Selects the root folder +//----------------------------------------------------------------------------- +void CGameFileTreeView::SelectRoot() +{ + AddSelectedItem( GetRootItemIndex(), true ); +} + + +//----------------------------------------------------------------------------- +// Gets the number of root directories +//----------------------------------------------------------------------------- +int CGameFileTreeView::GetRootDirectoryCount() +{ + return GetNumChildren( GetRootItemIndex() ); +} + + +//----------------------------------------------------------------------------- +// Gets the ith root directory +//----------------------------------------------------------------------------- +const char *CGameFileTreeView::GetRootDirectory( int nIndex ) +{ + int nItemIndex = GetChild( GetRootItemIndex(), nIndex ); + KeyValues *kv = GetItemData( nItemIndex ); + if ( !kv ) + return NULL; + return kv->GetString( "path", NULL ); +} + + +//----------------------------------------------------------------------------- +// Populate the root node (necessary since tree view can't have multiple roots) +//----------------------------------------------------------------------------- +void CGameFileTreeView::PopulateRootNode( int itemIndex ) +{ + AddDirectoriesOfNode( itemIndex, m_RootDir ); + + if ( m_bUseExt ) + { + AddFilesOfNode( itemIndex, m_RootDir, m_Ext ); + } +} + + +//----------------------------------------------------------------------------- +// Populate the root node with directories +//----------------------------------------------------------------------------- +bool CGameFileTreeView::DoesDirectoryHaveSubdirectories( const char *pFilePath ) +{ + char pSearchString[MAX_PATH]; + Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath ); + + // get the list of files + FileFindHandle_t findHandle; + + // generate children + // add all the items + const char *pszFileName = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &findHandle ); + while ( pszFileName ) + { + bool bIsDirectory = g_pFullFileSystem->FindIsDirectory( findHandle ); + if ( bIsDirectory && Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) ) + return true; + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + + g_pFullFileSystem->FindClose( findHandle ); + return false; +} + + +//----------------------------------------------------------------------------- +// Populate the root node with directories +//----------------------------------------------------------------------------- +void CGameFileTreeView::AddDirectoriesOfNode( int itemIndex, const char *pFilePath ) +{ + char pSearchString[MAX_PATH]; + Q_snprintf( pSearchString, MAX_PATH, "%s\\*", pFilePath ); + + // get the list of files + FileFindHandle_t findHandle; + + // generate children + // add all the items + const char *pszFileName = g_pFullFileSystem->FindFirstEx( pSearchString, "GAME", &findHandle ); + while ( pszFileName ) + { + bool bIsDirectory = g_pFullFileSystem->FindIsDirectory( findHandle ); + if ( bIsDirectory && Q_strnicmp( pszFileName, ".", 2 ) && Q_strnicmp( pszFileName, "..", 3 ) ) + { + KeyValues *kv = new KeyValues( "node", "text", pszFileName ); + + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, sizeof(pFullPath), "%s/%s", pFilePath, pszFileName ); + Q_FixSlashes( pFullPath ); + Q_strlower( pFullPath ); + bool bHasSubdirectories = DoesDirectoryHaveSubdirectories( pFullPath ); + kv->SetString( "path", pFullPath ); + + kv->SetInt( "expand", bHasSubdirectories ); + kv->SetInt( "dir", 1 ); + kv->SetInt( "image", IMAGE_FOLDER ); + + int itemID = AddItem(kv, itemIndex); + kv->deleteThis(); + + // mark directories in orange + SetItemColorForDirectories( itemID ); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + + g_pFullFileSystem->FindClose( findHandle ); +} + + +//----------------------------------------------------------------------------- +// Populate the root node with files +//----------------------------------------------------------------------------- +void CGameFileTreeView::AddFilesOfNode( int itemIndex, const char *pFilePath, const char *pExt ) +{ + char pSearchString[MAX_PATH]; + Q_snprintf( pSearchString, MAX_PATH, "%s\\*.%s", pFilePath, pExt ); + + // get the list of files + FileFindHandle_t findHandle; + + // generate children + // add all the items + const char *pszFileName = g_pFullFileSystem->FindFirst( pSearchString, &findHandle ); + while ( pszFileName ) + { + if ( !g_pFullFileSystem->FindIsDirectory( findHandle ) ) + { + KeyValues *kv = new KeyValues( "node", "text", pszFileName ); + + char pFullPath[MAX_PATH]; + Q_snprintf( pFullPath, MAX_PATH, "%s\\%s", pFilePath, pszFileName ); + kv->SetString( "path", pFullPath ); + kv->SetInt( "image", IMAGE_FILE ); + + AddItem(kv, itemIndex); + kv->deleteThis(); + } + + pszFileName = g_pFullFileSystem->FindNext( findHandle ); + } + + g_pFullFileSystem->FindClose( findHandle ); +} + + +//----------------------------------------------------------------------------- +// override to incremental request and show p4 directories +//----------------------------------------------------------------------------- +void CGameFileTreeView::GenerateChildrenOfNode(int itemIndex) +{ + KeyValues *pkv = GetItemData(itemIndex); + if ( pkv->GetInt("root") ) + { + PopulateRootNode( itemIndex ); + return; + } + + if (!pkv->GetInt("dir")) + return; + + const char *pFilePath = pkv->GetString("path", ""); + if (!pFilePath[0]) + return; + + surface()->SetCursor(dc_waitarrow); + + AddDirectoriesOfNode( itemIndex, pFilePath ); + + if ( m_bUseExt ) + { + AddFilesOfNode( itemIndex, pFilePath, m_Ext ); + } +} + + +//----------------------------------------------------------------------------- +// setup a context menu whenever a directory is clicked on +//----------------------------------------------------------------------------- +void CGameFileTreeView::GenerateContextMenu( int itemIndex, int x, int y ) +{ + return; + + /* + KeyValues *pkv = GetItemData(itemIndex); + const char *pFilePath = pkv->GetString("path", ""); + if (!pFilePath[0]) + return; + + Menu *pContext = new Menu(this, "FileContext"); + pContext->AddMenuItem("Cloak folder", new KeyValues("CloakFolder", "item", itemIndex), GetParent(), NULL); + + // show the context menu + pContext->SetPos(x, y); + pContext->SetVisible(true); + */ +} + + +//----------------------------------------------------------------------------- +// Sets an item to be colored as if its a menu +//----------------------------------------------------------------------------- +void CGameFileTreeView::SetItemColorForDirectories( int itemID ) +{ + // mark directories in orange + SetItemFgColor( itemID, Color(224, 192, 0, 255) ); +} + + +//----------------------------------------------------------------------------- +// setup a smaller font +//----------------------------------------------------------------------------- +void CGameFileTreeView::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + SetFont( pScheme->GetFont("DefaultSmall") ); +} + diff --git a/src/vgui2/matsys_controls/manipulator.cpp b/src/vgui2/matsys_controls/manipulator.cpp new file mode 100644 index 000000000..b582b11f4 --- /dev/null +++ b/src/vgui2/matsys_controls/manipulator.cpp @@ -0,0 +1,201 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//============================================================================= + +#include "matsys_controls/manipulator.h" + +#include "materialsystem/imaterialsystem.h" + +#include "vgui/IVGui.h" +#include "vgui/IInput.h" +#include "vgui/ISystem.h" +#include "vgui/MouseCode.h" + +#include "mathlib/vector.h" +#include "mathlib/vmatrix.h" +#include "mathlib/mathlib.h" + +#include + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// local helper functions +//----------------------------------------------------------------------------- +static float UpdateTime( float &flLastTime ) +{ + float flTime = vgui::system()->GetFrameTime(); + float dt = flTime - flLastTime; + flLastTime = flTime; + return dt; +} + + +//----------------------------------------------------------------------------- +// Base class for manipulators which operate on transforms +//----------------------------------------------------------------------------- +CTransformManipulator::CTransformManipulator( matrix3x4_t *pTransform ) : + m_pTransform( pTransform ) +{ +} + +void CTransformManipulator::SetTransform( matrix3x4_t *pTransform ) +{ + m_pTransform = pTransform; +} + +matrix3x4_t *CTransformManipulator::GetTransform( void ) +{ + return m_pTransform; +} + + +//----------------------------------------------------------------------------- +// CPotteryWheelManip - nendo-style camera manipulator +//----------------------------------------------------------------------------- +CPotteryWheelManip::CPotteryWheelManip( matrix3x4_t *pTransform ) : + CTransformManipulator( pTransform ), + m_lastx( -1 ), m_lasty( -1 ), + m_zoom( 100.0f ), m_altitude( 0.0f ), m_azimuth( 0.0f ), + m_prevZoom( 100.0f ), m_prevAltitude( 0.0f ), m_prevAzimuth( 0.0f ), + m_flLastMouseTime( 0.0f ), m_flLastTickTime( 0.0f ), + m_flSpin( 0.0f ), m_bSpin( false ) +{ +} + +void CPotteryWheelManip::OnBeginManipulation( void ) +{ + m_prevZoom = m_zoom; + m_prevAltitude = m_altitude; + m_prevAzimuth = m_azimuth; + m_flLastMouseTime = m_flLastTickTime = vgui::system()->GetFrameTime(); + m_flSpin = 0.0f; + m_bSpin = false; +} + +// Sets the zoom level +void CPotteryWheelManip::SetZoom( float flZoom ) +{ + m_prevZoom = m_zoom = flZoom; +} + + +void CPotteryWheelManip::OnAcceptManipulation( void ) +{ + m_flSpin = 0.0f; + m_bSpin = false; +} + +void CPotteryWheelManip::OnCancelManipulation( void ) +{ + m_zoom = m_prevZoom; + m_altitude = m_prevAltitude; + m_azimuth = m_prevAzimuth; + m_flSpin = 0.0f; + m_bSpin = false; + UpdateTransform(); +} + + +void CPotteryWheelManip::OnTick( void ) +{ + float dt = UpdateTime( m_flLastTickTime ); + + if ( m_bSpin ) + { + m_azimuth += dt * m_flSpin; + UpdateTransform(); + } +} + +void CPotteryWheelManip::OnCursorMoved( int x, int y ) +{ + float dt = UpdateTime( m_flLastMouseTime ); + + if ( m_bSpin ) + { + m_lastx = x; + m_lasty = y; + return; + } + + if ( input()->IsMouseDown( MOUSE_MIDDLE ) ) + { + int dy = y - m_lasty; + int dx = x - m_lastx; + + if ( abs( dx ) < 2 * abs( dy ) ) + { + UpdateZoom( 0.2f * dy ); + } + else + { + m_flSpin = (dt != 0.0f) ? 0.002f * dx / dt : 0.0f; + m_azimuth += 0.002f * dx; + } + } + else + { + m_azimuth += 0.002f * ( x - m_lastx ); + m_altitude -= 0.002f * ( y - m_lasty ); + m_altitude = max( (float)-M_PI/2, min( (float)M_PI/2, m_altitude ) ); + } + m_lastx = x; + m_lasty = y; + + UpdateTransform(); +} + +void CPotteryWheelManip::OnMousePressed( vgui::MouseCode code, int x, int y ) +{ + UpdateTime( m_flLastMouseTime ); + m_lastx = x; + m_lasty = y; + m_bSpin = false; + m_flSpin = 0.0f; +} + +void CPotteryWheelManip::OnMouseReleased( vgui::MouseCode code, int x, int y ) +{ + UpdateTime( m_flLastMouseTime ); + + if ( code == MOUSE_MIDDLE ) + { + m_bSpin = ( fabs( m_flSpin ) > 1.0f ); + } + + m_lastx = x; + m_lasty = y; +} + +void CPotteryWheelManip::OnMouseWheeled( int delta ) +{ + UpdateTime( m_flLastMouseTime ); + + UpdateZoom( -10.0f * delta ); + UpdateTransform(); +} + +void CPotteryWheelManip::UpdateTransform() +{ + if ( !m_pTransform ) + return; + + float y = m_zoom * sin( m_altitude ); + float xz = m_zoom * cos( m_altitude ); + float x = xz * sin( m_azimuth ); + float z = xz * cos( m_azimuth ); + + Vector position(x, y, z); + AngleMatrix( RadianEuler( -m_altitude, m_azimuth, 0 ), position, *m_pTransform ); +} + + +void CPotteryWheelManip::UpdateZoom( float delta ) +{ + m_zoom *= pow( 1.01f, delta ); +} diff --git a/src/vgui2/matsys_controls/matsys_controls.vpc b/src/vgui2/matsys_controls/matsys_controls.vpc new file mode 100644 index 000000000..c4b1bc8ce --- /dev/null +++ b/src/vgui2/matsys_controls/matsys_controls.vpc @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// MATSYS_CONTROLS.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$macro SRCDIR "..\.." + +$include "$SRCDIR\vpc_scripts\source_lib_base.vpc" + +$Project "matsys_controls" +{ + $Folder "Source Files" + { + $File "assetpicker.cpp" + $File "baseassetpicker.cpp" + $File "colorpickerpanel.cpp" + $File "curveeditorpanel.cpp" + $File "gamefiletreeview.cpp" + $File "manipulator.cpp" + $File "matsyscontrols.cpp" + $File "mdlpanel.cpp" + $File "mdlpicker.cpp" + $File "mdlsequencepicker.cpp" + $File "picker.cpp" + $File "potterywheelpanel.cpp" + $File "proceduraltexturepanel.cpp" + $File "QCGenerator.cpp" + $File "sequencepicker.cpp" + $File "tgapreviewpanel.cpp" + $File "vmtpicker.cpp" + $File "vmtpreviewpanel.cpp" + $File "vtfpicker.cpp" + $File "vtfpreviewpanel.cpp" + $File "vmtpanel.cpp" + } + + $Folder "Header Files" + { + $File "$SRCDIR\public\matsys_controls\assetpicker.h" + $File "$SRCDIR\public\matsys_controls\baseassetpicker.h" + $File "$SRCDIR\public\matsys_controls\colorpickerpanel.h" + $File "$SRCDIR\public\matsys_controls\gamefiletreeview.h" + $File "$SRCDIR\public\matsys_controls\manipulator.h" + $File "$SRCDIR\public\matsys_controls\matsyscontrols.h" + $File "$SRCDIR\public\matsys_controls\mdlpanel.h" + $File "$SRCDIR\public\matsys_controls\mdlpicker.h" + $File "$SRCDIR\public\matsys_controls\mdlsequencepicker.h" + $File "$SRCDIR\public\matsys_controls\picker.h" + $File "$SRCDIR\public\matsys_controls\potterywheelpanel.h" + $File "$SRCDIR\public\matsys_controls\proceduraltexturepanel.h" + $File "$SRCDIR\public\matsys_controls\QCGenerator.h" + $File "$SRCDIR\public\matsys_controls\sequencepicker.h" + $File "$SRCDIR\public\matsys_controls\tgapreviewpanel.h" + $File "$SRCDIR\public\matsys_controls\vmtpicker.h" + $File "$SRCDIR\public\matsys_controls\vmtpreviewpanel.h" + $File "$SRCDIR\public\matsys_controls\vtfpicker.h" + $File "$SRCDIR\public\matsys_controls\vtfpreviewpanel.h" + $File "$SRCDIR\public\matsys_controls\vmtpanel.h" + } + + $Folder "Link Libraries" + { + $Lib bitmap [$WIN32] + } +} diff --git a/src/vgui2/matsys_controls/matsyscontrols.cpp b/src/vgui2/matsys_controls/matsyscontrols.cpp new file mode 100644 index 000000000..11c415f23 --- /dev/null +++ b/src/vgui2/matsys_controls/matsyscontrols.cpp @@ -0,0 +1,100 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "matsys_controls/matsyscontrols.h" +#include +#include +#include +#include +#include +#include "vgui_controls/Controls.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +namespace vgui +{ + +IMaterialSystem *g_pMaterialSystem = NULL; +IMaterialSystem *MaterialSystem() +{ + return g_pMaterialSystem; +} + +IMaterialSystemHardwareConfig *g_pMaterialSystemHardwareConfig = NULL; +IMaterialSystemHardwareConfig *MaterialSystemHardwareConfig() +{ + return g_pMaterialSystemHardwareConfig; +} + +IMDLCache *g_pMDLCache = NULL; +IMDLCache *MDLCache() +{ + return g_pMDLCache; +} + +IMatSystemSurface *g_pMatSystemSurface = NULL; +IMatSystemSurface *MatSystemSurface() +{ + return g_pMatSystemSurface; +} + +IStudioRender *g_pStudioRender = NULL; +IStudioRender *StudioRender() +{ + return g_pStudioRender; +} + + +//----------------------------------------------------------------------------- +// Purpose: finds a particular interface in the factory set +//----------------------------------------------------------------------------- +static void *InitializeInterface( char const *interfaceName, CreateInterfaceFn *factoryList, int numFactories ) +{ + void *retval; + + for ( int i = 0; i < numFactories; i++ ) + { + CreateInterfaceFn factory = factoryList[ i ]; + if ( !factory ) + continue; + + retval = factory( interfaceName, NULL ); + if ( retval ) + return retval; + } + + // No provider for requested interface!!! + // Assert( !"No provider for requested interface!!!" ); + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Purpose: Initializes the controls +//----------------------------------------------------------------------------- +bool VGui_InitMatSysInterfacesList( const char *moduleName, CreateInterfaceFn *factoryList, int numFactories ) +{ + if ( !vgui::VGui_InitInterfacesList( moduleName, factoryList, numFactories ) ) + return false; + + g_pMaterialSystem = (IMaterialSystem *)InitializeInterface( MATERIAL_SYSTEM_INTERFACE_VERSION, factoryList, numFactories ); + g_pMatSystemSurface = (IMatSystemSurface *)InitializeInterface( MAT_SYSTEM_SURFACE_INTERFACE_VERSION, factoryList, numFactories ); + g_pMDLCache = (IMDLCache *)InitializeInterface( MDLCACHE_INTERFACE_VERSION, factoryList, numFactories ); + g_pStudioRender = (IStudioRender *)InitializeInterface( STUDIO_RENDER_INTERFACE_VERSION, factoryList, numFactories ); + g_pMaterialSystemHardwareConfig = (IMaterialSystemHardwareConfig *)InitializeInterface( MATERIALSYSTEM_HARDWARECONFIG_INTERFACE_VERSION, factoryList, numFactories ); + + // MDL cache + studiorender are optional + return ( g_pMaterialSystem && g_pMatSystemSurface && g_pMaterialSystemHardwareConfig ); +} + + +} // namespace vgui + + + diff --git a/src/vgui2/matsys_controls/mdlpanel.cpp b/src/vgui2/matsys_controls/mdlpanel.cpp new file mode 100644 index 000000000..5e671971e --- /dev/null +++ b/src/vgui2/matsys_controls/mdlpanel.cpp @@ -0,0 +1,981 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/mdlpanel.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "materialsystem/imesh.h" +#include "vgui/IVGui.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/Frame.h" +#include "tier1/convar.h" +#include "tier0/dbg.h" +#include "tier1/fmtstr.h" +#include "istudiorender.h" +#include "matsys_controls/matsyscontrols.h" +#include "vcollide.h" +#include "vcollide_parse.h" +#include "bone_setup.h" +#include "vphysics_interface.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + +using namespace vgui; + +DECLARE_BUILD_FACTORY( CMDLPanel ); + +static const int THUMBNAIL_SAFE_ZONE_SIZE = 512; +static const int THUMBNAIL_SAFE_ZONE_HEIGHT = 92; +static const float THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE = (float)THUMBNAIL_SAFE_ZONE_HEIGHT / THUMBNAIL_SAFE_ZONE_SIZE; + +//----------------------------------------------------------------------------- +// 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 = Plat_MSTime(); + + // limit delta so that float time doesn't overflow + if (g_prevTicks == 0) + { + g_prevTicks = ticks; + } + + g_time += ( ticks - g_prevTicks ) / 1000.0f; + g_prevTicks = ticks; + + return g_time; +} + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CMDLPanel::CMDLPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + SetVisible( true ); + + // Used to poll input + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + // Deal with the default cubemap + ITexture *pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap", NULL, true ); + m_DefaultEnvCubemap.Init( pCubemapTexture ); + pCubemapTexture = vgui::MaterialSystem()->FindTexture( "editor/cubemap.hdr", NULL, true ); + m_DefaultHDREnvCubemap.Init( pCubemapTexture ); + + SetIdentityMatrix( m_RootMDL.m_MDLToWorld ); + m_RootMDL.m_pStudioHdr = NULL; + m_RootMDL.m_unMdlCacheSerial = 0; + m_bDrawCollisionModel = false; + m_bWireFrame = false; + m_bGroundGrid = false; + m_bLockView = false; + m_bLookAtCamera = true; + m_bThumbnailSafeZone = false; + m_nNumSequenceLayers = 0; + ResetAnimationEventState( &m_EventState ); +} + +CMDLPanel::~CMDLPanel() +{ + m_aMergeMDLs.Purge(); + m_DefaultEnvCubemap.Shutdown( ); + m_DefaultHDREnvCubemap.Shutdown(); + if ( m_RootMDL.m_pStudioHdr ) + { + delete m_RootMDL.m_pStudioHdr; + m_RootMDL.m_pStudioHdr = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Scheme settings +//----------------------------------------------------------------------------- +void CMDLPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetBackgroundColor( GetBgColor() ); + SetBorder( pScheme->GetBorder( "MenuBorder") ); +} + + +//----------------------------------------------------------------------------- +// Rendering options +//----------------------------------------------------------------------------- +void CMDLPanel::SetCollsionModel( bool bVisible ) +{ + m_bDrawCollisionModel = bVisible; +} + +void CMDLPanel::SetGroundGrid( bool bVisible ) +{ + m_bGroundGrid = bVisible; +} + +void CMDLPanel::SetWireFrame( bool bVisible ) +{ + m_bWireFrame = bVisible; +} + +void CMDLPanel::SetLockView( bool bLocked ) +{ + m_bLockView = bLocked; +} + +void CMDLPanel::SetLookAtCamera( bool bLookAtCamera ) +{ + m_bLookAtCamera = bLookAtCamera; +} + +void CMDLPanel::SetIgnoreDoubleClick( bool bState ) +{ + m_bIgnoreDoubleClick = bState; +} + +void CMDLPanel::SetThumbnailSafeZone( bool bVisible ) +{ + m_bThumbnailSafeZone = bVisible; +} + +//----------------------------------------------------------------------------- +// Stores the clip +//----------------------------------------------------------------------------- +void CMDLPanel::SetMDL( MDLHandle_t handle, void *pProxyData ) +{ + m_RootMDL.m_MDL.SetMDL( handle ); + if ( m_RootMDL.m_pStudioHdr ) + { + delete m_RootMDL.m_pStudioHdr; + } + m_RootMDL.m_pStudioHdr = new CStudioHdr( m_RootMDL.m_MDL.GetStudioHdr(), g_pMDLCache ); + m_RootMDL.m_MDL.m_pProxyData = pProxyData; + + Vector vecMins, vecMaxs; + GetMDLBoundingBox( &vecMins, &vecMaxs, handle, m_RootMDL.m_MDL.m_nSequence ); + + m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = false; + m_RootMDL.m_MDL.m_vecViewTarget.Init( 100.0f, 0.0f, vecMaxs.z ); + + m_RootMDL.m_flCycleStartTime = 0.f; + + // Set the pose parameters to the default for the mdl + SetPoseParameters( NULL, 0 ); + + // Clear any sequence layers + SetSequenceLayers( NULL, 0 ); + + ResetAnimationEventState( &m_EventState ); +} + +//----------------------------------------------------------------------------- +// An MDL was selected +//----------------------------------------------------------------------------- +void CMDLPanel::SetMDL( const char *pMDLName, void *pProxyData ) +{ + MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName ); + MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID; + if ( vgui::MDLCache()->IsErrorModel( hMDL ) ) + { + hMDL = MDLHANDLE_INVALID; + } + + SetMDL( hMDL, pProxyData ); + + // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference. + int nRef = vgui::MDLCache()->Release( hMDLFindResult ); + (void)nRef; // Avoid unreferenced variable warning + AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMDL referenced a model that has a zero ref count." ); +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a model bounding box. +//----------------------------------------------------------------------------- +bool CMDLPanel::GetBoundingBox( Vector &vecBoundsMin, Vector &vecBoundsMax ) +{ + // Check to see if we have a valid model to look at. + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + return false; + + GetMDLBoundingBox( &vecBoundsMin, &vecBoundsMax, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: Returns a more accurate bounding sphere +//----------------------------------------------------------------------------- +bool CMDLPanel::GetBoundingSphere( Vector &vecCenter, float &flRadius ) +{ + // Check to see if we have a valid model to look at. + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + return false; + + Vector vecEngineCenter; + GetMDLBoundingSphere( &vecEngineCenter, &flRadius, m_RootMDL.m_MDL.GetMDL(), m_RootMDL.m_MDL.m_nSequence ); + VectorTransform( vecEngineCenter, m_RootMDL.m_MDLToWorld, vecCenter ); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::SetModelAnglesAndPosition( const QAngle &angRot, const Vector &vecPos ) +{ + SetIdentityMatrix( m_RootMDL.m_MDLToWorld ); + AngleMatrix( angRot, vecPos, m_RootMDL.m_MDLToWorld ); +} + +//----------------------------------------------------------------------------- +// Sets the camera to look at the model +//----------------------------------------------------------------------------- +void CMDLPanel::LookAtMDL() +{ + // Check to see if we have a valid model to look at. + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + return; + + if ( m_bLockView ) + return; + + float flRadius; + Vector vecCenter; + GetBoundingSphere( vecCenter, flRadius ); + LookAt( vecCenter, flRadius ); +} + + +//----------------------------------------------------------------------------- +// FIXME: This should be moved into studiorender +//----------------------------------------------------------------------------- +static ConVar r_showenvcubemap( "r_showenvcubemap", "0", FCVAR_CHEAT ); +static ConVar r_eyegloss ( "r_eyegloss", "1", FCVAR_ARCHIVE ); // wet eyes +static ConVar r_eyemove ( "r_eyemove", "1", FCVAR_ARCHIVE ); // look around +static ConVar r_eyeshift_x ( "r_eyeshift_x", "0", FCVAR_ARCHIVE ); // eye X position +static ConVar r_eyeshift_y ( "r_eyeshift_y", "0", FCVAR_ARCHIVE ); // eye Y position +static ConVar r_eyeshift_z ( "r_eyeshift_z", "0", FCVAR_ARCHIVE ); // eye Z position +static ConVar r_eyesize ( "r_eyesize", "0", FCVAR_ARCHIVE ); // adjustment to iris textures +static ConVar mat_softwareskin( "mat_softwareskin", "0", FCVAR_CHEAT ); +static ConVar r_nohw ( "r_nohw", "0", FCVAR_CHEAT ); +static ConVar r_nosw ( "r_nosw", "0", FCVAR_CHEAT ); +static ConVar r_teeth ( "r_teeth", "1" ); +static ConVar r_drawentities ( "r_drawentities", "1", FCVAR_CHEAT ); +static ConVar r_flex ( "r_flex", "1" ); +static ConVar r_eyes ( "r_eyes", "1" ); +static ConVar r_skin ( "r_skin","0", FCVAR_CHEAT ); +static ConVar r_maxmodeldecal ( "r_maxmodeldecal", "50" ); +static ConVar r_modelwireframedecal ( "r_modelwireframedecal", "0", FCVAR_CHEAT ); +static ConVar mat_normals ( "mat_normals", "0", FCVAR_CHEAT ); +static ConVar r_eyeglintlodpixels ( "r_eyeglintlodpixels", "0", FCVAR_CHEAT ); +static ConVar r_rootlod ( "r_rootlod", "0" ); + +static StudioRenderConfig_t s_StudioRenderConfig; + +void CMDLPanel::UpdateStudioRenderConfig( void ) +{ + memset( &s_StudioRenderConfig, 0, sizeof(s_StudioRenderConfig) ); + + s_StudioRenderConfig.bEyeMove = !!r_eyemove.GetInt(); + s_StudioRenderConfig.fEyeShiftX = r_eyeshift_x.GetFloat(); + s_StudioRenderConfig.fEyeShiftY = r_eyeshift_y.GetFloat(); + s_StudioRenderConfig.fEyeShiftZ = r_eyeshift_z.GetFloat(); + s_StudioRenderConfig.fEyeSize = r_eyesize.GetFloat(); + if( mat_softwareskin.GetInt() || m_bWireFrame ) + { + s_StudioRenderConfig.bSoftwareSkin = true; + } + else + { + s_StudioRenderConfig.bSoftwareSkin = false; + } + s_StudioRenderConfig.bNoHardware = !!r_nohw.GetInt(); + s_StudioRenderConfig.bNoSoftware = !!r_nosw.GetInt(); + s_StudioRenderConfig.bTeeth = !!r_teeth.GetInt(); + s_StudioRenderConfig.drawEntities = r_drawentities.GetInt(); + s_StudioRenderConfig.bFlex = !!r_flex.GetInt(); + s_StudioRenderConfig.bEyes = !!r_eyes.GetInt(); + s_StudioRenderConfig.bWireframe = m_bWireFrame; + s_StudioRenderConfig.bDrawNormals = mat_normals.GetBool(); + s_StudioRenderConfig.skin = r_skin.GetInt(); + s_StudioRenderConfig.maxDecalsPerModel = r_maxmodeldecal.GetInt(); + s_StudioRenderConfig.bWireframeDecals = r_modelwireframedecal.GetInt() != 0; + + s_StudioRenderConfig.fullbright = false; + s_StudioRenderConfig.bSoftwareLighting = false; + + s_StudioRenderConfig.bShowEnvCubemapOnly = r_showenvcubemap.GetInt() ? true : false; + s_StudioRenderConfig.fEyeGlintPixelWidthLODThreshold = r_eyeglintlodpixels.GetFloat(); + + StudioRender()->UpdateConfig( s_StudioRenderConfig ); +} + +void CMDLPanel::DrawCollisionModel() +{ + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + { + return; + } + + vcollide_t *pCollide = MDLCache()->GetVCollide( m_RootMDL.m_MDL.GetMDL() ); + + if ( !pCollide || pCollide->solidCount <= 0 ) + return; + + static color32 color = {255,0,0,0}; + + IVPhysicsKeyParser *pParser = g_pPhysicsCollision->VPhysicsKeyParserCreate( pCollide->pKeyValues ); + CStudioHdr &studioHdr = *m_RootMDL.m_pStudioHdr; + + matrix3x4_t pBoneToWorld[MAXSTUDIOBONES]; + m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, MAXSTUDIOBONES, pBoneToWorld ); + + // PERFORMANCE: Just parse the script each frame. It's fast enough for tools. If you need + // this to go faster then cache off the bone index mapping in an array like HLMV does + while ( !pParser->Finished() ) + { + const char *pBlock = pParser->GetCurrentBlockName(); + if ( !stricmp( pBlock, "solid" ) ) + { + solid_t solid; + + pParser->ParseSolid( &solid, NULL ); + int boneIndex = Studio_BoneIndexByName( &studioHdr, solid.name ); + Vector *outVerts; + int vertCount = g_pPhysicsCollision->CreateDebugMesh( pCollide->solids[solid.index], &outVerts ); + + if ( vertCount ) + { + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); + // NOTE: assumes these have been set up already by the model render code + // So this is a little bit of a back door to a cache of the bones + // this code wouldn't work unless you draw the model this frame before calling + // this routine. CMDLPanel always does this, but it's worth noting. + // A better solution would be to move the ragdoll visulization into the CDmeMdl + // and either draw it there or make it queryable and query/draw here. + matrix3x4_t xform; + SetIdentityMatrix( xform ); + if ( boneIndex >= 0 ) + { + MatrixCopy( pBoneToWorld[ boneIndex ], xform ); + } + IMesh *pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_Wireframe ); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLES, vertCount/3 ); + + for ( int j = 0; j < vertCount; j++ ) + { + Vector out; + VectorTransform( outVerts[j].Base(), xform, out.Base() ); + meshBuilder.Position3fv( out.Base() ); + meshBuilder.Color4ub( color.r, color.g, color.b, color.a ); + meshBuilder.TexCoord2f( 0, 0, 0 ); + meshBuilder.AdvanceVertex(); + } + meshBuilder.End(); + pMesh->Draw(); + } + + g_pPhysicsCollision->DestroyDebugMesh( vertCount, outVerts ); + } + else + { + pParser->SkipBlock(); + } + } + g_pPhysicsCollision->VPhysicsKeyParserDestroy( pParser ); +} + +void CMDLPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight ) +{ + int iWidth = nDisplayWidth; + int iHeight = nDisplayHeight; + if ( m_bThumbnailSafeZone ) + { + iWidth = THUMBNAIL_SAFE_ZONE_SIZE; + iHeight = THUMBNAIL_SAFE_ZONE_SIZE; + } + BaseClass::SetupRenderState( iWidth, iHeight ); +} + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CMDLPanel::OnPaint3D() +{ + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + return; + + // FIXME: Move this call into DrawModel in StudioRender + StudioRenderConfig_t oldStudioRenderConfig; + StudioRender()->GetCurrentConfig( oldStudioRenderConfig ); + + UpdateStudioRenderConfig(); + + CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() ); + if ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE ) + { + ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( false ) : m_DefaultEnvCubemap; + pRenderContext->BindLocalCubemap( pMyCube ); + } + else + { + ITexture *pMyCube = HasLightProbe() ? GetLightProbeCubemap( true ) : m_DefaultHDREnvCubemap; + pRenderContext->BindLocalCubemap( pMyCube ); + } + + PrePaint3D( pRenderContext ); + + if ( m_bGroundGrid ) + { + DrawGrid(); + } + + if ( m_bLookAtCamera ) + { + matrix3x4_t worldToCamera; + ComputeCameraTransform( &worldToCamera ); + + Vector vecPosition; + MatrixGetColumn( worldToCamera, 3, vecPosition ); + m_RootMDL.m_MDL.m_bWorldSpaceViewTarget = true; + m_RootMDL.m_MDL.m_vecViewTarget = vecPosition; + } + + // Draw the MDL + CStudioHdr &studioHdr = *m_RootMDL.m_pStudioHdr; + + SetupFlexWeights(); + + matrix3x4_t *pBoneToWorld = g_pStudioRender->LockBoneMatrices( studioHdr.numbones() ); + m_RootMDL.m_MDL.SetUpBones( m_RootMDL.m_MDLToWorld, studioHdr.numbones(), pBoneToWorld, m_PoseParameters, m_SequenceLayers, m_nNumSequenceLayers ); + g_pStudioRender->UnlockBoneMatrices(); + + IMaterial* pOverrideMaterial = GetOverrideMaterial( m_RootMDL.m_MDL.GetMDL() ); + if ( pOverrideMaterial != NULL ) + g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial ); + + m_RootMDL.m_MDL.Draw( m_RootMDL.m_MDLToWorld, pBoneToWorld ); + + if ( pOverrideMaterial != NULL ) + g_pStudioRender->ForcedMaterialOverride( NULL ); + + pOverrideMaterial = NULL; + + // Draw the merge MDLs. + matrix3x4_t matMergeBoneToWorld[MAXSTUDIOBONES]; + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[iMerge].m_bDisabled ) + continue; + + // Get the merge studio header. + CStudioHdr *pMergeHdr = m_aMergeMDLs[iMerge].m_pStudioHdr; + matrix3x4_t *pMergeBoneToWorld = &matMergeBoneToWorld[0]; + + // If we have a valid mesh, bonemerge it. If we have an invalid mesh we can't bonemerge because + // it'll crash trying to pull data from the missing header. + if ( pMergeHdr != NULL ) + { + CStudioHdr &mergeHdr = *pMergeHdr; + m_aMergeMDLs[iMerge].m_MDL.SetupBonesWithBoneMerge( &mergeHdr, pMergeBoneToWorld, &studioHdr, pBoneToWorld, m_RootMDL.m_MDLToWorld ); + + pOverrideMaterial = GetOverrideMaterial( m_aMergeMDLs[iMerge].m_MDL.GetMDL() ); + if ( pOverrideMaterial != NULL ) + g_pStudioRender->ForcedMaterialOverride( pOverrideMaterial ); + + m_aMergeMDLs[iMerge].m_MDL.Draw( m_aMergeMDLs[iMerge].m_MDLToWorld, pMergeBoneToWorld ); + + if ( pOverrideMaterial != NULL ) + g_pStudioRender->ForcedMaterialOverride( NULL ); + + // Notify of model render + RenderingMergedModel( pRenderContext, &mergeHdr, m_aMergeMDLs[iMerge].m_MDL.GetMDL(), pMergeBoneToWorld ); + } + } + + RenderingRootModel( pRenderContext, &studioHdr, m_RootMDL.m_MDL.GetMDL(), pBoneToWorld ); + + PostPaint3D( pRenderContext ); + + if ( m_bDrawCollisionModel ) + { + DrawCollisionModel(); + } + + pRenderContext->Flush(); + StudioRender()->UpdateConfig( oldStudioRenderConfig ); +} + + +//----------------------------------------------------------------------------- +// Sets the current LOD +//----------------------------------------------------------------------------- +void CMDLPanel::SetLOD( int nLOD ) +{ + m_RootMDL.m_MDL.m_nLOD = nLOD; + + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + m_aMergeMDLs[iMerge].m_MDL.m_nLOD = nLOD; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current sequence +//----------------------------------------------------------------------------- +void CMDLPanel::SetSequence( int nSequence, bool bResetSequence ) +{ + m_RootMDL.m_MDL.m_nSequence = nSequence; + + if ( bResetSequence ) + { + m_RootMDL.m_flCycleStartTime = GetAutoPlayTime(); + } +} + + +//----------------------------------------------------------------------------- +// Set the current pose parameters. If NULL the pose parameters will be reset +// to the default values. +//----------------------------------------------------------------------------- +void CMDLPanel::SetPoseParameters( const float *pPoseParameters, int nCount ) +{ + if ( pPoseParameters ) + { + int nParameters = MIN( MAXSTUDIOPOSEPARAM, nCount ); + for ( int iParam = 0; iParam < nParameters; ++iParam ) + { + m_PoseParameters[ iParam ] = pPoseParameters[ iParam ]; + } + } + else if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID ) + { + CStudioHdr &studioHdr = *m_RootMDL.m_pStudioHdr; + Studio_CalcDefaultPoseParameters( &studioHdr, m_PoseParameters, MAXSTUDIOPOSEPARAM ); + } +} + + +//----------------------------------------------------------------------------- +// Set a pose parameter by name +//----------------------------------------------------------------------------- +bool CMDLPanel::SetPoseParameterByName( const char *pszName, float fValue ) +{ + if ( !m_RootMDL.m_pStudioHdr ) + return false; + CStudioHdr &studioHdr = *m_RootMDL.m_pStudioHdr; + int nPoseCount = studioHdr.GetNumPoseParameters(); + + for ( int i = 0; i < nPoseCount; ++i ) + { + const mstudioposeparamdesc_t &Pose = studioHdr.pPoseParameter( i ); + if ( V_strcasecmp( pszName, Pose.pszName() ) == 0 ) + { + m_PoseParameters[ i ] = fValue; + return true; + } + } + return false; +} + + +//----------------------------------------------------------------------------- +// Set the overlay sequence layers +//----------------------------------------------------------------------------- +void CMDLPanel::SetSequenceLayers( const MDLSquenceLayer_t *pSequenceLayers, int nCount ) +{ + if ( pSequenceLayers ) + { + m_nNumSequenceLayers = MIN( MAX_SEQUENCE_LAYERS, nCount ); + for ( int iLayer = 0; iLayer < m_nNumSequenceLayers; ++iLayer ) + { + m_SequenceLayers[ iLayer ] = pSequenceLayers[ iLayer ]; + ResetAnimationEventState( &m_SequenceLayerEventState[ iLayer ] ); + } + } + else + { + m_nNumSequenceLayers = 0; + V_memset( m_SequenceLayers, 0, sizeof( m_SequenceLayers ) ); + } +} + +//----------------------------------------------------------------------------- +// Set the current skin +//----------------------------------------------------------------------------- +void CMDLPanel::SetSkin( int nSkin ) +{ + m_RootMDL.m_MDL.m_nSkin = nSkin; +} + +//----------------------------------------------------------------------------- +// called when we're ticked... +//----------------------------------------------------------------------------- +void CMDLPanel::OnTick() +{ + BaseClass::OnTick(); + if ( m_RootMDL.m_MDL.GetMDL() != MDLHANDLE_INVALID ) + { + ValidateMDLs(); + + m_RootMDL.m_MDL.m_flTime = ( GetAutoPlayTime() - m_RootMDL.m_flCycleStartTime ); + + DoAnimationEvents(); + } +} + +void CMDLPanel::Paint() +{ + BaseClass::Paint(); + + if ( m_bThumbnailSafeZone ) + { + int iWidth, iHeight; + GetSize( iWidth, iHeight ); + + CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() ); + + IMaterial *safezone = materials->FindMaterial( "vgui/thumbnails_safezone", TEXTURE_GROUP_VGUI, true ); + if ( safezone ) + { + safezone->IncrementReferenceCount(); + } + + int screenposx = 0; + int screenposy = 0; + LocalToScreen( screenposx, screenposy ); + + int iScaledHeight = THUMBNAIL_SAFE_ZONE_HEIGHT_SCALE * iHeight; + int iRemappedHeight = RemapVal( iScaledHeight, 0, THUMBNAIL_SAFE_ZONE_HEIGHT, 0, iScaledHeight ); + + pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight, + 0, 0, + THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT, + THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE ); + + screenposx = 0; + screenposy = iHeight - iRemappedHeight; + LocalToScreen( screenposx, screenposy ); + pRenderContext->DrawScreenSpaceRectangle( safezone, screenposx, screenposy, iWidth, iRemappedHeight, + 0, 0, + THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_HEIGHT, + THUMBNAIL_SAFE_ZONE_SIZE, THUMBNAIL_SAFE_ZONE_SIZE ); + + if ( safezone ) + { + safezone->DecrementReferenceCount(); + } + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::DoAnimationEvents() +{ + if ( !m_RootMDL.m_pStudioHdr ) + return; + CStudioHdr &studioHdr = *m_RootMDL.m_pStudioHdr; + + // If we don't have any sequences, don't do anything + if ( studioHdr.GetNumSeq() < 1 ) + { + Assert( studioHdr.GetNumSeq() >= 1 ); + return; + } + + // we're holding onto model data that can be forced reloaded when importing workshop items + // detect the vmodel change here and force an update if so, until we fix the real issue of the studioHdr not being updated itself + if ( studioHdr.GetVirtualModel() != nullptr && studioHdr.GetVirtualModel() != studioHdr.GetRenderHdr()->GetVirtualModel() ) + { + studioHdr.ResetVModel( studioHdr.GetRenderHdr()->GetVirtualModel() ); + } + + DoAnimationEvents( &studioHdr, m_RootMDL.m_MDL.m_nSequence, m_RootMDL.m_MDL.m_flTime, false, &m_EventState ); + + for ( int i = 0; i < m_nNumSequenceLayers; ++i ) + { + float flTime = m_RootMDL.m_MDL.m_flTime - m_SequenceLayers[ i ].m_flCycleBeganAt; + //Plat_DebugString( CFmtStr("Animation: time = %f, started = %f, delta = %f\n",m_RootMDL.m_MDL.m_flTime,m_SequenceLayers[ i ].m_flCycleBeganAt,flTime ) ); + DoAnimationEvents( &studioHdr, m_SequenceLayers[ i ].m_nSequenceIndex, flTime, m_SequenceLayers[ i ].m_bNoLoop, &m_SequenceLayerEventState[ i ] ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::DoAnimationEvents( CStudioHdr *pStudioHdr, int nSeqNum, float flTime, bool bNoLoop, MDLAnimEventState_t *pEventState ) +{ + if ( nSeqNum < 0 || nSeqNum >= pStudioHdr->GetNumSeq() ) + { + return; + } + + mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( nSeqNum ); + if ( seqdesc.numevents == 0 ) + { + return; + } + + mstudioevent_t *pevent = seqdesc.pEvent( 0 ); + + int nFrameCount = Studio_MaxFrame( pStudioHdr, nSeqNum, m_PoseParameters ); + if ( nFrameCount == 0 ) + { + nFrameCount = 1; + } + float flEventCycle = ( flTime * m_RootMDL.m_MDL.m_flPlaybackRate ) / nFrameCount; + //Plat_DebugString( CFmtStr("Event cycle: %f, playback rate: %f, frame count: %d\n", flEventCycle, m_RootMDL.m_MDL.m_flPlaybackRate, nFrameCount ) ); + if ( bNoLoop ) + { + flEventCycle = MIN(flEventCycle, 1.0f); + } + else + { + flEventCycle -= (int)(flEventCycle); + } + + if ( pEventState->m_nEventSequence != nSeqNum ) + { + pEventState->m_nEventSequence = nSeqNum; + flEventCycle = 0.0f; + pEventState->m_flPrevEventCycle = -0.01f; // back up to get 0'th frame animations + } + + if ( flEventCycle == pEventState->m_flPrevEventCycle ) + { + return; + } + + // check for looping + BOOL bLooped = (flEventCycle < pEventState->m_flPrevEventCycle); + + // This makes sure events that occur at the end of a sequence occur are + // sent before events that occur at the beginning of a sequence. + if (bLooped) + { + for (int i = 0; i < (int)seqdesc.numevents; i++) + { + if ( pevent[i].cycle <= pEventState->m_flPrevEventCycle ) + continue; + + FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() ); + } + + // Necessary to get the next loop working + pEventState->m_flPrevEventCycle = -0.01f; + } + + for (int i = 0; i < (int)seqdesc.numevents; i++) + { + if ( (pevent[i].cycle > pEventState->m_flPrevEventCycle && pevent[i].cycle <= flEventCycle) ) + { + FireEvent( pevent[ i ].pszEventName(), pevent[ i ].pszOptions() ); + } + } + + pEventState->m_flPrevEventCycle = flEventCycle; +} + +void CMDLPanel::FireEvent( const char *pszEventName, const char *pszEventOptions ) +{ + KeyValues* pKVEvent = new KeyValues( "AnimEvent" ); + pKVEvent->SetString( "name", pszEventName ); + pKVEvent->SetString( "options", pszEventOptions ); + PostActionSignal( pKVEvent ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::ResetAnimationEventState( MDLAnimEventState_t *pEventState ) +{ + pEventState->m_nEventSequence = -1; + pEventState->m_flPrevEventCycle = -0.01f; +} + +//----------------------------------------------------------------------------- +// input +//----------------------------------------------------------------------------- +void CMDLPanel::OnMouseDoublePressed( vgui::MouseCode code ) +{ + if ( m_bIgnoreDoubleClick ) + return; + + float flRadius; + Vector vecCenter; + GetBoundingSphere( vecCenter, flRadius ); + LookAt( vecCenter, flRadius ); + + BaseClass::OnMouseDoublePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::SetMergeMDL( MDLHandle_t handle, void *pProxyData, int nSkin /*= -1 */ ) +{ + // Verify that we have a root model to merge to. + if ( m_RootMDL.m_MDL.GetMDL() == MDLHANDLE_INVALID ) + return; + + int iIndex = m_aMergeMDLs.AddToTail(); + if ( !m_aMergeMDLs.IsValidIndex( iIndex ) ) + return; + + m_aMergeMDLs[iIndex].m_MDL.SetMDL( handle ); + + if ( nSkin != -1 ) + { + m_aMergeMDLs[iIndex].m_MDL.m_nSkin = nSkin; + } + + m_aMergeMDLs[iIndex].m_MDL.m_nLOD = m_RootMDL.m_MDL.m_nLOD; + + m_aMergeMDLs[iIndex].m_MDL.m_pProxyData = pProxyData; + SetIdentityMatrix( m_aMergeMDLs[iIndex].m_MDLToWorld ); + + m_aMergeMDLs[iIndex].m_bDisabled = false; + + m_aMergeMDLs[iIndex].m_pStudioHdr = new CStudioHdr( m_aMergeMDLs[iIndex].m_MDL.GetStudioHdr(), g_pMDLCache ); + + // Need to invalidate the layout so the panel will adjust is LookAt for the new model. + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +MDLHandle_t CMDLPanel::SetMergeMDL( const char *pMDLName, void *pProxyData, int nSkin /*= -1 */ ) +{ + MDLHandle_t hMDLFindResult = vgui::MDLCache()->FindMDL( pMDLName ); + MDLHandle_t hMDL = pMDLName ? hMDLFindResult : MDLHANDLE_INVALID; + if ( vgui::MDLCache()->IsErrorModel( hMDL ) ) + { + hMDL = MDLHANDLE_INVALID; + } + + SetMergeMDL( hMDL, pProxyData, nSkin ); + + // FindMDL takes a reference and the the CMDL will also hold a reference for as long as it sticks around. Release the FindMDL reference. + int nRef = vgui::MDLCache()->Release( hMDLFindResult ); + (void)nRef; // Avoid unreferenced variable warning + AssertMsg( hMDL == MDLHANDLE_INVALID || nRef > 0, "CMDLPanel::SetMergeMDL referenced a model that has a zero ref count." ); + + return hMDL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMDLPanel::GetMergeMDLIndex( void *pProxyData ) +{ + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[iMerge].m_MDL.m_pProxyData == pProxyData ) + return iMerge; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CMDLPanel::GetMergeMDLIndex( MDLHandle_t handle ) +{ + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle ) + return iMerge; + } + + return -1; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CMDL *CMDLPanel::GetMergeMDL( MDLHandle_t handle ) +{ + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[iMerge].m_MDL.GetMDL() == handle ) + return (&m_aMergeMDLs[iMerge].m_MDL); + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +CStudioHdr *CMDLPanel::GetMergeMDLStudioHdr( MDLHandle_t handle ) +{ + int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[ iMerge ].m_MDL.GetMDL() == handle ) + return ( m_aMergeMDLs[ iMerge ].m_pStudioHdr ); + } + + return NULL; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CMDLPanel::ClearMergeMDLs( void ) +{ + const int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( !m_aMergeMDLs[iMerge].m_pStudioHdr ) + { + continue; + } + delete m_aMergeMDLs[iMerge].m_pStudioHdr; + m_aMergeMDLs[iMerge].m_pStudioHdr = NULL; + } + + m_aMergeMDLs.Purge(); +} + +void CMDLPanel::ValidateMDLs() +{ + uint32 uMdlCacheSerial = vgui::ivgui()->GetMdlCacheSerial(); + + if ( m_RootMDL.m_pStudioHdr && m_RootMDL.m_unMdlCacheSerial != uMdlCacheSerial ) + { + m_RootMDL.m_pStudioHdr = new CStudioHdr( m_RootMDL.m_MDL.GetStudioHdr(), g_pMDLCache ); + m_RootMDL.m_unMdlCacheSerial = uMdlCacheSerial; + } + + const int nMergeCount = m_aMergeMDLs.Count(); + for ( int iMerge = 0; iMerge < nMergeCount; ++iMerge ) + { + if ( m_aMergeMDLs[iMerge].m_pStudioHdr && m_aMergeMDLs[iMerge].m_unMdlCacheSerial != uMdlCacheSerial ) + { + m_aMergeMDLs[iMerge].m_pStudioHdr = new CStudioHdr( m_aMergeMDLs[iMerge].m_MDL.GetStudioHdr(), g_pMDLCache ); + m_aMergeMDLs[iMerge].m_unMdlCacheSerial = uMdlCacheSerial; + } + } +} diff --git a/src/vgui2/matsys_controls/mdlpicker.cpp b/src/vgui2/matsys_controls/mdlpicker.cpp new file mode 100644 index 000000000..17c5b562d --- /dev/null +++ b/src/vgui2/matsys_controls/mdlpicker.cpp @@ -0,0 +1,1742 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/mdlpicker.h" +#include "tier1/KeyValues.h" +#include "tier1/utldict.h" +#include "filesystem.h" +#include "studio.h" +#include "matsys_controls/matsyscontrols.h" +#include "matsys_controls/mdlpanel.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/Button.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/MessageBox.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/CheckButton.h" +#include "vgui_controls/DirectorySelectDialog.h" +#include "vgui/IVGui.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "vgui/Cursor.h" +#include "matsys_controls/assetpicker.h" +#include "matsys_controls/colorpickerpanel.h" +#include "dmxloader/dmxloader.h" +#include "tier1/utlbuffer.h" +#include "bitmap/tgawriter.h" +#include "tier3/tier3.h" +#include "istudiorender.h" +#include "../vgui2/src/VPanel.h" +#include "tier2/p4helpers.h" +#include "ivtex.h" +#include "bitmap/tgaloader.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + +static bool SaveTgaAndAddToP4( unsigned char *pImage, ImageFormat imageFormat, int Width, int Height, const char *szDestFilename ) +{ + + // allocate a buffer to write the tga into + int iMaxTGASize = 1024 + (Width * Height * 4); + void *pTGA = malloc( iMaxTGASize ); + CUtlBuffer buffer( pTGA, iMaxTGASize ); + + if( !TGAWriter::WriteToBuffer( pImage, buffer, Width, Height, imageFormat, IMAGE_FORMAT_BGRA8888 ) ) + { + Error( "Couldn't write bitmap data snapshot.\n" ); + return false; + } + + CP4AutoEditAddFile autop4( szDestFilename ); + + // async write to disk (this will take ownership of the memory) + char szDirName[ _MAX_PATH ]; + strcpy( szDirName, szDestFilename ); + V_StripFilename( szDirName ); + g_pFullFileSystem->CreateDirHierarchy( szDirName, "" ); + g_pFullFileSystem->AsyncWrite( szDestFilename, buffer.Base(), buffer.TellPut(), true ); + + return true; +} + +//----------------------------------------------------------------------------- +// +// MDL Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by MDL name +//----------------------------------------------------------------------------- +static int __cdecl MDLBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("mdl"); + const char *string2 = item2.kv->GetString("mdl"); + return stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMDLPicker::CMDLPicker( vgui::Panel *pParent, int nFlags ) : + BaseClass( pParent, "MDL Files", "mdl", "models", "mdlName" ) +{ + for( int i = 0; i < MAX_SELECTED_MODELS; i++ ) + { + m_hSelectedMDL[ i ] = MDLHANDLE_INVALID; + } + + m_nFlags = nFlags; // remember what we show and what not + + m_pRenderPage = NULL; + m_pSequencesPage = NULL; + m_pActivitiesPage = NULL; + m_pSkinsPage = NULL; + m_pInfoPage = NULL; + m_pScreenCapsPage = NULL; + + m_pSequencesList = NULL; + m_pActivitiesList = NULL; + + m_hDirectorySelectDialog = NULL; + + // Horizontal splitter for mdls + m_pFileBrowserSplitter = new Splitter( this, "FileBrowserSplitter", SPLITTER_MODE_VERTICAL, 1 ); + + float flFractions[] = { 0.33f, 0.67f }; + + m_pFileBrowserSplitter->RespaceSplitters( flFractions ); + + vgui::Panel *pSplitterLeftSide = m_pFileBrowserSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pFileBrowserSplitter->GetChild( 1 ); + + // Standard browser controls + pSplitterLeftSide->RequestFocus(); + CreateStandardControls( pSplitterLeftSide, false ); + + // property sheet - revisions, changes, etc. + m_pPreviewSplitter = new Splitter( pSplitterRightSide, "PreviewSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + + vgui::Panel *pSplitterTopSide = m_pPreviewSplitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_pPreviewSplitter->GetChild( 1 ); + + // MDL preview + m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" ); + SetSkipChildDuringPainting( m_pMDLPreview ); + + m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" ); + m_pViewsSheet->AddActionSignalTarget( this ); + + // now add wanted features + if ( nFlags & PAGE_RENDER ) + { + m_pRenderPage = new vgui::PropertyPage( m_pViewsSheet, "RenderPage" ); + + m_pRenderPage->AddActionSignalTarget( this ); + + m_pRenderPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerrender.res" ); + + RefreshRenderSettings(); + + // ground + Button *pSelectProbe = (Button*)m_pRenderPage->FindChildByName( "ChooseLightProbe" ); + pSelectProbe->AddActionSignalTarget( this ); + } + + if ( nFlags & PAGE_SEQUENCES ) + { + m_pSequencesPage = new vgui::PropertyPage( m_pViewsSheet, "SequencesPage" ); + + m_pSequencesList = new vgui::ListPanel( m_pSequencesPage, "SequencesList" ); + m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 ); + m_pSequencesList->AddActionSignalTarget( this ); + m_pSequencesList->SetSelectIndividualCells( true ); + m_pSequencesList->SetEmptyListText("No .MDL file currently selected."); + m_pSequencesList->SetDragEnabled( true ); + m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 ); + } + + if ( nFlags & PAGE_ACTIVITIES ) + { + m_pActivitiesPage = new vgui::PropertyPage( m_pViewsSheet, "ActivitiesPage" ); + + m_pActivitiesList = new vgui::ListPanel( m_pActivitiesPage, "ActivitiesList" ); + m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 ); + m_pActivitiesList->AddActionSignalTarget( this ); + m_pActivitiesList->SetSelectIndividualCells( true ); + m_pActivitiesList->SetEmptyListText( "No .MDL file currently selected." ); + m_pActivitiesList->SetDragEnabled( true ); + m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 ); + } + + if ( nFlags & PAGE_SKINS ) + { + m_pSkinsPage = new vgui::PropertyPage( m_pViewsSheet, "SkinsPage" ); + + m_pSkinsList = new vgui::ListPanel( m_pSkinsPage, "SkinsList" ); + m_pSkinsList->AddColumnHeader( 0, "skin", "skin", 52, 0 ); + m_pSkinsList->AddActionSignalTarget( this ); + m_pSkinsList->SetSelectIndividualCells( true ); + m_pSkinsList->SetEmptyListText( "No .MDL file currently selected." ); + m_pSkinsList->SetDragEnabled( true ); + m_pSkinsList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 6, -6, -6 ); + } + + if ( nFlags & PAGE_INFO ) + { + m_pInfoPage = new vgui::PropertyPage( m_pViewsSheet, "InfoPage" ); + + m_pInfoPage->AddActionSignalTarget( this ); + + m_pInfoPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerinfo.res" ); + + CheckButton * pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "PhysicsObject" ); + pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor()); + pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor()); + pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "StaticObject" ); + pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor()); + pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor()); + pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName( "DynamicObject" ); + pTempCheck->SetDisabledFgColor1( pTempCheck->GetFgColor()); + pTempCheck->SetDisabledFgColor2( pTempCheck->GetFgColor()); + + m_pPropDataList = new vgui::ListPanel( m_pInfoPage, "PropData" ); + m_pPropDataList->AddColumnHeader( 0, "key", "key", 250, ListPanel::COLUMN_FIXEDSIZE ); + m_pPropDataList->AddColumnHeader( 1, "value", "value", 52, 0 ); + m_pPropDataList->AddActionSignalTarget( this ); + m_pPropDataList->SetSelectIndividualCells( false ); + m_pPropDataList->SetEmptyListText( "No prop_data available." ); + m_pPropDataList->SetDragEnabled( true ); + m_pPropDataList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 6, 72, -6, -6 ); + + RefreshRenderSettings(); + } + + if ( nFlags & PAGE_SCREEN_CAPS ) + { + m_pScreenCapsPage = new vgui::PropertyPage( m_pViewsSheet, "ScreenCapsPage" ); + +// not sure why we have to do this for the color picker + CColorPickerButton *m_pBackgroundColor; + m_pBackgroundColor = new CColorPickerButton( m_pScreenCapsPage, "BackgroundColor", this ); + + m_pScreenCapsPage->LoadControlSettingsAndUserConfig( "resource/mdlpickerscreencaps.res" ); + + TextEntry *pTempValue; + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" ); + pTempValue->SetText( "256" ); + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" ); + pTempValue->SetText( "256" ); + +// not sure why this doesn't work +// m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" ); + m_pBackgroundColor->SetColor( 255, 0, 0, 255 ); + + Label *m_pOutputDirectory; + m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->SetText( "c:\\" ); + + Button *pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "Capture" ); + pSelectProbe->AddActionSignalTarget( this ); + pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "OutputDirectorySelect" ); + pSelectProbe->AddActionSignalTarget( this ); + pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "SaveCaps" ); + pSelectProbe->AddActionSignalTarget( this ); + pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "RestoreCaps" ); + pSelectProbe->AddActionSignalTarget( this ); + pSelectProbe = ( Button * )m_pScreenCapsPage->FindChildByName( "GenerateBackpackIcons" ); + pSelectProbe->AddActionSignalTarget( this ); + } + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/mdlpicker.res" ); + + // Pages must be added after control settings are set up + if ( m_pRenderPage ) + { + m_pViewsSheet->AddPage( m_pRenderPage, "Render" ); + } + if ( m_pSequencesPage ) + { + m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" ); + } + if ( m_pActivitiesPage ) + { + m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" ); + } + if ( m_pSkinsPage ) + { + m_pViewsSheet->AddPage( m_pSkinsPage, "Skins" ); + } + if ( m_pInfoPage ) + { + m_pViewsSheet->AddPage( m_pInfoPage, "Info" ); + } + if ( m_pScreenCapsPage ) + { + m_pViewsSheet->AddPage( m_pScreenCapsPage, "Screen Caps" ); + } +} + +void CMDLPicker::RefreshRenderSettings() +{ + vgui::CheckButton *pToggle; + + if ( !m_pRenderPage ) + return; + + // ground + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("NoGround"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetGroundGrid( !pToggle->IsSelected() ); + + // collision + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("Collision"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetCollsionModel( pToggle->IsSelected() ); + + // wireframe + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("Wireframe"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetWireFrame( pToggle->IsSelected() ); + + // lockview + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("LockView"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetLockView( pToggle->IsSelected() ); + + // look at camera + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("LookAtCamera"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetLookAtCamera( pToggle->IsSelected() ); + + // thumbnail safe zone + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName("ThumbnailSafeZone"); + pToggle->AddActionSignalTarget( this ); + m_pMDLPreview->SetThumbnailSafeZone( pToggle->IsSelected() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMDLPicker::~CMDLPicker() +{ +} + + +//----------------------------------------------------------------------------- +// Performs layout +//----------------------------------------------------------------------------- +void CMDLPicker::PerformLayout() +{ + // NOTE: This call should cause auto-resize to occur + // which should fix up the width of the panels + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + // Layout the mdl splitter + m_pFileBrowserSplitter->SetBounds( 0, 0, w, h ); +} + + +//----------------------------------------------------------------------------- +// Buttons on various pages +//----------------------------------------------------------------------------- +void CMDLPicker::OnAssetSelected( KeyValues *pParams ) +{ + const char *pAsset = pParams->GetString( "asset" ); + + char pProbeBuf[MAX_PATH]; + Q_snprintf( pProbeBuf, sizeof(pProbeBuf), "materials/lightprobes/%s", pAsset ); + + BeginDMXContext(); + CDmxElement *pLightProbe = NULL; + bool bOk = UnserializeDMX( pProbeBuf, "GAME", true, &pLightProbe ); + if ( !pLightProbe || !bOk ) + { + char pBuf[1024]; + Q_snprintf( pBuf, sizeof(pBuf), "Error loading lightprobe file '%s'!\n", pProbeBuf ); + vgui::MessageBox *pMessageBox = new vgui::MessageBox( "Error Loading File!\n", pBuf, GetParent() ); + pMessageBox->DoModal( ); + + EndDMXContext( true ); + return; + } + + m_pMDLPreview->SetLightProbe( pLightProbe ); + EndDMXContext( true ); +} + + +//----------------------------------------------------------------------------- +// Buttons on various pages +//----------------------------------------------------------------------------- +void CMDLPicker::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "ChooseLightProbe" ) ) + { + CAssetPickerFrame *pPicker = new CAssetPickerFrame( this, "Select Light Probe (.prb) File", + "Light Probe", "prb", "materials/lightprobes", "lightprobe" ); + pPicker->DoModal(); + return; + } + else if ( !Q_stricmp( pCommand, "OutputDirectorySelect" ) ) + { + if ( !m_hDirectorySelectDialog.Get() ) + { + m_hDirectorySelectDialog = new DirectorySelectDialog( this, "Choose Screen Caps output folder" ); + } + + Label *m_pOutputDirectory; + char temp[ MAX_PATH ]; + m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->GetText( temp, sizeof( temp ) ); + + m_hDirectorySelectDialog->MakeReadyForUse(); + m_hDirectorySelectDialog->SetStartDirectory( temp ); + m_hDirectorySelectDialog->DoModal(); + return; + } + else if ( !Q_stricmp( pCommand, "Capture" ) ) + { + CaptureScreenCaps(); + return; + } + else if ( !Q_stricmp( pCommand, "GenerateBackpackIcons" ) ) + { + // shut off the ground grid + vgui::CheckButton *pGroundToggle = (vgui::CheckButton *)m_pRenderPage->FindChildByName( "NoGround" ); + bool bOriginalGridState = pGroundToggle->IsSelected(); + + vgui::CheckButton *pSafeZoneToggle = (vgui::CheckButton *)m_pRenderPage->FindChildByName( "ThumbnailSafeZone" ); + bool bOriginalSafeZoneState = pSafeZoneToggle->IsSelected(); + + m_pMDLPreview->SetGroundGrid( false ); + m_pMDLPreview->SetThumbnailSafeZone( false ); + + // make the icons + GenerateBackpackIcons(); + + // return the ground grid to its original state + m_pMDLPreview->SetGroundGrid( !bOriginalGridState ); + m_pMDLPreview->SetThumbnailSafeZone( bOriginalSafeZoneState ); + + return; + } + else if ( !Q_stricmp( pCommand, "SaveCaps" ) ) + { + SaveCaps( NULL ); + return; + } + else if ( !Q_stricmp( pCommand, "RestoreCaps" ) ) + { + if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) ) + { + int nCount = m_AssetList.Count(); + for ( int i = 0; i < nCount; ++i ) + { + if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ i ].m_nItemId ) && + m_pAssetBrowser->IsItemSelected( m_AssetList[ i ].m_nItemId ) ) + { + KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ i ].m_nItemId ); + const char *pSelectedAsset = pItemKeyValues->GetString( "asset" ); + + char szBathPath[ _MAX_PATH ]; + Label *m_pOutputDirectory; + + m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->GetText( szBathPath, sizeof( szBathPath ) ); + + char szPathedFileName[ _MAX_PATH ]; + V_sprintf_safe( szPathedFileName, "%sbackpack\\%s", szBathPath, pSelectedAsset ); + + Label *m_pResults = ( Label * )m_pScreenCapsPage->FindChildByName( "CaptureResults" ); + if ( RestoreCaps( szPathedFileName ) ) + { + m_pResults->SetText( "Prefs Restored" ); + } + else + { + m_pResults->SetText( "Prefs NOT FOUND" ); + } + break; + } + } + } + else + { + RestoreCaps( NULL ); + } + return; + } + + BaseClass::OnCommand( pCommand ); +} + + +//----------------------------------------------------------------------------- +// Handles the directory selection for screen caps +//----------------------------------------------------------------------------- +void CMDLPicker::OnDirectorySelected( char const *dir ) +{ + if ( m_hDirectorySelectDialog != 0 ) + { + m_hDirectorySelectDialog->MarkForDeletion(); + } + + Label *m_pOutputDirectory; + m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->SetText( dir ); +} + + +//----------------------------------------------------------------------------- +// Screen captures the specific model and writes out a .tga. Assumes the MDLPreview +// panel has been properly adjusted to 0,0 in screen space and that width / height +// have been set. +//----------------------------------------------------------------------------- +const char *CMDLPicker::CaptureModel( int nModIndex, const char *AssetName, const char *OutputPath, int Width, int Height, Color BackgroundColor, bool bSelectedOnly ) +{ + char pBuf[ MAX_PATH ]; + Q_snprintf( pBuf, sizeof( pBuf ), "%s\\%s\\%s", GetModPath( nModIndex ), m_pAssetSubDir, AssetName ); + Q_FixSlashes( pBuf ); + + if ( !bSelectedOnly ) + { + SelectMDL( pBuf, false, -1 ); + } + + CMatRenderContextPtr pRenderContext( materials ); + + g_pMaterialSystem->BeginFrame( 0 ); + g_pStudioRender->BeginFrame(); + +// pRenderContext->ClearColor4ub( 0, 0, 0, 0 ); +// pRenderContext->ClearBuffers( true, true ); + + Color NewPanelColor; + + NewPanelColor.SetColor( 0, 0, 0, 0 ); + m_pMDLPreview->SetBackgroundColor( NewPanelColor ); + + g_pVGuiSurface->PaintTraverseEx( m_pMDLPreview->GetVPanel(), false ); + + g_pStudioRender->EndFrame(); + g_pMaterialSystem->EndFrame( ); + + // get the data from the backbuffer and save to disk + // bitmap bits + unsigned char *pImageBlack = ( unsigned char * )malloc( Width * 4 * Height ); + + // Get Bits from the material system + pRenderContext->ReadPixels( 0, 0, Width, Height, pImageBlack, IMAGE_FORMAT_BGRA8888 ); + + + g_pMaterialSystem->BeginFrame( 0 ); + g_pStudioRender->BeginFrame(); + +// pRenderContext->ClearColor4ub( 255, 255, 255, 0 ); +// pRenderContext->ClearBuffers( true, true ); + + NewPanelColor.SetColor( 255, 255, 255, 0 ); + m_pMDLPreview->SetBackgroundColor( NewPanelColor ); + + g_pVGuiSurface->PaintTraverseEx( m_pMDLPreview->GetVPanel(), false ); + + g_pStudioRender->EndFrame(); + g_pMaterialSystem->EndFrame( ); + + // get the data from the backbuffer and save to disk + // bitmap bits + unsigned char *pImageWhite = ( unsigned char * )malloc( Width * 4 * Height ); + + // Get Bits from the material system + pRenderContext->ReadPixels( 0, 0, Width, Height, pImageWhite, IMAGE_FORMAT_BGRA8888 ); + + unsigned char *pBlackPos = pImageBlack; + unsigned char *pWhitePos = pImageWhite; + for( int y = 0; y < Height; y++ ) + { + for( int x = 0; x < Width; x++, pBlackPos += 4, pWhitePos += 4 ) + { + if ( ( *( pBlackPos + 0 ) ) != ( *( pWhitePos + 0 ) ) || // blue + ( *( pBlackPos + 1 ) ) != ( *( pWhitePos + 1 ) ) || // green + ( *( pBlackPos + 2 ) ) != ( *( pWhitePos + 2 ) ) ) // red + { + unsigned char nBlueDiff = ( *( pBlackPos + 0 ) ); + unsigned char nGreenDiff = ( *( pBlackPos + 1 ) ); + unsigned char nRedDiff = ( *( pBlackPos + 2 ) ); + + unsigned char nMax = nBlueDiff; + if ( nGreenDiff > nMax ) + { + nMax = nGreenDiff; + } + if ( nRedDiff > nMax ) + { + nMax = nRedDiff; + } + + *( pBlackPos + 3 ) = nMax; + } + else + { + *( pBlackPos + 3 ) = 0xff; + } + } + } + + static char szPathedFileName[ _MAX_PATH ]; + V_sprintf_safe( szPathedFileName, "%s%s", OutputPath, AssetName ); + V_SetExtension( szPathedFileName, ".tga", sizeof( szPathedFileName ) ); + + bool bResult = SaveTgaAndAddToP4( pImageBlack, IMAGE_FORMAT_BGRA8888, Width, Height, szPathedFileName ); + + free( pImageBlack ); + free( pImageWhite ); + + if ( !bResult) return NULL; + + if ( bSelectedOnly ) + { + SaveCaps( szPathedFileName ); + } + + return szPathedFileName; +} + + +//----------------------------------------------------------------------------- +// Will go through the asset browser and capture each visible item. +//----------------------------------------------------------------------------- +void CMDLPicker::CaptureScreenCaps( void ) +{ + char temp[ 256 ]; + TextEntry *pTempValue; + int width; + int height; + char szBathPath[ _MAX_PATH ]; + Label *m_pOutputDirectory; + + m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->GetText( szBathPath, sizeof( szBathPath ) ); + + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" ); + pTempValue->GetText( temp, sizeof( temp ) ); + width = atoi( temp ); + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" ); + pTempValue->GetText( temp, sizeof( temp ) ); + height = atoi( temp ); + + int PanelX, PanelY, PanelWidth, PanelHeight; + Color PanelColor; + + Panel *pParent = m_pMDLPreview->GetParent(); + m_pMDLPreview->GetPos( PanelX, PanelY ); + m_pMDLPreview->GetSize( PanelWidth, PanelHeight ); + PanelColor = m_pMDLPreview->GetBackgroundColor(); + + m_pMDLPreview->SetParent( ( vgui::Panel * )NULL ); + m_pMDLPreview->SetPos( 0, 0 ); + m_pMDLPreview->SetSize( width, height ); + + CColorPickerButton *m_pBackgroundColor; + m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" ); + + Color NewPanelColor = m_pBackgroundColor->GetColor(); + NewPanelColor[3] = 0; + m_pMDLPreview->SetBackgroundColor( NewPanelColor ); + ((VPanel *)m_pMDLPreview->GetVPanel())->Solve(); + + bool bSelectedOnly = false; + if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) ) + { + bSelectedOnly = true; + } + + int nCount = m_AssetList.Count(); + int nNumItems = 0; + for ( int i = 0; i < nCount; ++i ) + { + if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ i ].m_nItemId ) && + ( !bSelectedOnly || m_pAssetBrowser->IsItemSelected( m_AssetList[ i ].m_nItemId ) ) ) + { + KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ i ].m_nItemId ); + const char *pSelectedAsset = pItemKeyValues->GetString( "asset" ); + int nModIndex = pItemKeyValues->GetInt( "modIndex" ); + + CaptureModel( nModIndex, pSelectedAsset, szBathPath, width, height, NewPanelColor, bSelectedOnly ); + nNumItems++; + } + } + + m_pMDLPreview->SetParent( pParent ); + m_pMDLPreview->SetPos( PanelX, PanelY ); + m_pMDLPreview->SetSize( PanelWidth, PanelHeight ); + m_pMDLPreview->SetBackgroundColor( PanelColor ); + ((VPanel *)m_pMDLPreview->GetVPanel())->Solve(); + + Label *m_pResults; + + V_sprintf_safe( temp, "Captured %d items", nNumItems ); + m_pResults = ( Label * )m_pScreenCapsPage->FindChildByName( "CaptureResults" ); + m_pResults->SetText( temp ); +} + + +//----------------------------------------------------------------------------- +// Stub for XBox360 compiles +//----------------------------------------------------------------------------- +#if defined( _X360 ) +const char *getenv( const char *varname ) +{ + return NULL; +} +#endif + + +//----------------------------------------------------------------------------- +// Writes two very simple .vmt file, one for the passed in asset, +// and the other for _large. +//----------------------------------------------------------------------------- +void CMDLPicker::WriteBackbackVMTFiles( const char *pAssetName ) +{ + const char *pVProject = getenv( "VPROJECT" ); + if ( !pVProject ) + return; + + char pStrippedAssetName[ MAX_PATH ]; + V_StripExtension( pAssetName, pStrippedAssetName, sizeof( pStrippedAssetName ) ); + V_strcat_safe( pStrippedAssetName, GetOutputFileSuffix().Get() ); + + char pVMTFilename[ MAX_PATH ]; + Q_snprintf( pVMTFilename, sizeof( pVMTFilename ), "%s\\materials\\backpack\\%s.vmt", pVProject, pStrippedAssetName ); + Q_FixSlashes( pVMTFilename ); + + char pBaseTextureName[ MAX_PATH ]; + Q_snprintf( pBaseTextureName, sizeof( pBaseTextureName ), "backpack\\%s", pStrippedAssetName ); + Q_FixSlashes( pBaseTextureName ); + + { + CP4AutoEditAddFile autop4( pVMTFilename ); + FileHandle_t fileHandle = g_pFullFileSystem->Open( pVMTFilename, "w" ); + if ( fileHandle ) + { + + g_pFullFileSystem->FPrintf( fileHandle, "\"UnlitGeneric\"\n" ); + g_pFullFileSystem->FPrintf( fileHandle, "{\n" ); + g_pFullFileSystem->FPrintf( fileHandle, " \"$baseTexture\" \"%s\"\n", pBaseTextureName ); + g_pFullFileSystem->FPrintf( fileHandle, " $translucent 1\n" ); + g_pFullFileSystem->FPrintf( fileHandle, " $vertexcolor 1\n" ); + g_pFullFileSystem->FPrintf( fileHandle, "}\n" ); + + g_pFullFileSystem->Close( fileHandle ); + } + } + + // now write the _large version + Q_snprintf( pVMTFilename, sizeof( pVMTFilename ), "%s\\materials\\backpack\\%s_large", pVProject, pStrippedAssetName ); + V_SetExtension( pVMTFilename, ".vmt", sizeof( pVMTFilename ) ); + Q_FixSlashes( pVMTFilename ); + + Q_snprintf( pBaseTextureName, sizeof( pBaseTextureName ), "backpack\\%s_large", pStrippedAssetName ); + Q_FixSlashes( pBaseTextureName ); + + { + CP4AutoEditAddFile autop4( pVMTFilename ); + FileHandle_t fileHandle = g_pFullFileSystem->Open( pVMTFilename, "w" ); + if ( fileHandle ) + { + g_pFullFileSystem->FPrintf( fileHandle, "\"UnlitGeneric\"\n" ); + g_pFullFileSystem->FPrintf( fileHandle, "{\n" ); + g_pFullFileSystem->FPrintf( fileHandle, " \"$baseTexture\" \"%s\"\n", pBaseTextureName ); + g_pFullFileSystem->FPrintf( fileHandle, " $translucent 1\n" ); + g_pFullFileSystem->FPrintf( fileHandle, "}\n" ); + + g_pFullFileSystem->Close( fileHandle ); + } + } +} + + +//----------------------------------------------------------------------------- +void *VTexFilesystemFactory( const char *pName, int *pReturnCode ) +{ + return g_pFullFileSystem; +} + + +void* MdlPickerFSFactory( const char *pName, int *pReturnCode ) +{ + if ( IsX360() ) + return NULL; + + if ( Q_stricmp( pName, FILESYSTEM_INTERFACE_VERSION ) == 0 ) + return g_pFullFileSystem; + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Creates the two required icons for the TF2 store/backpack system +//----------------------------------------------------------------------------- +void CMDLPicker::GenerateBackpackIcons( void ) +{ + if ( !g_pVTex ) + return; + + int width; + int height; + + // find the index of the item we are currently viewing + int selectedItemIndex; + for ( selectedItemIndex = 0; selectedItemIndex < m_AssetList.Count(); ++selectedItemIndex ) + { + if ( m_pAssetBrowser->IsItemVisible( m_AssetList[ selectedItemIndex ].m_nItemId ) && + m_pAssetBrowser->IsItemSelected( m_AssetList[ selectedItemIndex ].m_nItemId ) ) + { + break; + } + } + + if ( selectedItemIndex >= m_AssetList.Count() ) + return; + + // + // Fetch and check environment variables + // + const char *pVContent = getenv( "VCONTENT" ); + if ( !pVContent ) + { + Error( "VCONTENT environment variable not set" ); + return; + } + + const char *pVMod = getenv( "VMOD" ); + if ( !pVMod ) + { + Error( "VMOD environment variable not set" ); + return; + } + + const char *pVProject = getenv( "VPROJECT" ); + if ( !pVProject ) + { + Error( "VPROJECT environment variable not set" ); + return; + } + + // extract the filename of the model + KeyValues *pItemKeyValues = m_pAssetBrowser->GetItem( m_AssetList[ selectedItemIndex ].m_nItemId ); + const char *pSelectedAsset = pItemKeyValues->GetString( "asset" ); + + // set the P4 changelist label to refer to this set of icons + char pChangelistLabel[ MAX_PATH ]; + V_strcpy_safe( pChangelistLabel, pSelectedAsset ); + V_FileBase( pChangelistLabel, pChangelistLabel, sizeof( pChangelistLabel ) ); + V_strcat_safe( pChangelistLabel, " Auto Checkout", sizeof( pChangelistLabel ) ); + g_p4factory->SetOpenFileChangeList( pChangelistLabel ); + + // generate .VMT files for normal and _large icons + WriteBackbackVMTFiles( pSelectedAsset ); + + // store original state of model preview panel + int PanelX, PanelY, PanelWidth, PanelHeight; + Color PanelColor; + + Panel *pParent = m_pMDLPreview->GetParent(); + m_pMDLPreview->GetPos( PanelX, PanelY ); + m_pMDLPreview->GetSize( PanelWidth, PanelHeight ); + PanelColor = m_pMDLPreview->GetBackgroundColor(); + + // slam preview panel to desired TGA size for large icon, and write it out + width = 512; + height = 512; + m_pMDLPreview->SetParent( ( vgui::Panel * )NULL ); + m_pMDLPreview->SetPos( 0, 0 ); + m_pMDLPreview->SetSize( width, height ); + + CColorPickerButton *m_pBackgroundColor; + m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" ); + + Color NewPanelColor = m_pBackgroundColor->GetColor(); + NewPanelColor[3] = 0; + m_pMDLPreview->SetBackgroundColor( NewPanelColor ); + ((VPanel *)m_pMDLPreview->GetVPanel())->Solve(); + + char pLargeAssetName[ MAX_PATH ]; + V_strcpy_safe( pLargeAssetName, pSelectedAsset ); + V_StripExtension( pLargeAssetName, pLargeAssetName, ARRAYSIZE( pLargeAssetName ) ); + + CUtlString strExtention = GetOutputFileSuffix(); + strExtention += "_large.mdl"; + + V_strcat_safe( pLargeAssetName, strExtention.String() ); + + char pOutputPath[ MAX_PATH ]; + Q_snprintf( pOutputPath, sizeof( pOutputPath ), "%s\\%s\\materialsrc\\backpack\\", pVContent, pVMod ); + Q_FixSlashes( pOutputPath ); + + int nModIndex = pItemKeyValues->GetInt( "modIndex" ); + const char *pLargeTGAName = CaptureModel( nModIndex, pLargeAssetName, pOutputPath, width, height, NewPanelColor, true ); + if ( !pLargeTGAName ) + return; + + // write corresponding .txt file with vtex options + char pVTexOptionsFileName[ MAX_PATH ]; + V_strcpy_safe( pVTexOptionsFileName, pLargeTGAName ); + V_SetExtension( pVTexOptionsFileName, ".txt", sizeof( pVTexOptionsFileName ) ); + + { + CP4AutoEditAddFile autop4( pVTexOptionsFileName ); + FileHandle_t hVTexOptionsFile = g_pFullFileSystem->Open( pVTexOptionsFileName, "w" ); + if ( hVTexOptionsFile ) + { + g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nomip 1\n" ); + g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nolod 1\n" ); + g_pFullFileSystem->Close( hVTexOptionsFile ); + } + } + + // !KLUDGE! Everybody I've talked to says that vtex is *supposed* to + // use VPROJECT. But it doesn't. I don't think I can safely change vtex + // without breaking tons of stuff. So just force the output directory. + // Determine the proper output directory based on VPROJECT + char pOutputPathGame[ MAX_PATH ]; + Q_strncpy( pOutputPathGame, pVProject, sizeof( pOutputPathGame ) ); + Q_StripTrailingSlash( pOutputPathGame ); + Q_strncat( pOutputPathGame, "/materials/", sizeof( pOutputPathGame ) ); + const char *pBackpack = Q_stristr( pLargeTGAName, "backpack" ); + if ( pBackpack ) + { + Q_strncat( pOutputPathGame, pBackpack, sizeof( pOutputPathGame ) ); + Q_StripFilename( pOutputPathGame ); + } + Q_FixSlashes( pOutputPathGame ); + + // run vtex on the TGA and .txt file to create .VTF and add it to our Perforce changelist + char *vTexArgv[64]; + int vTexArgc = 0; + vTexArgv[ vTexArgc++ ] = ""; + vTexArgv[ vTexArgc++ ] = "-quiet"; + vTexArgv[ vTexArgc++ ] = "-UseStandardError"; + vTexArgv[ vTexArgc++ ] = "-WarningsAsErrors"; + vTexArgv[ vTexArgc++ ] = "-p4skip"; + vTexArgv[ vTexArgc++ ] = "-outdir"; + vTexArgv[ vTexArgc++ ] = pOutputPathGame; + vTexArgv[ vTexArgc++ ] = (char *)pLargeTGAName; + + g_pVTex->VTex( MdlPickerFSFactory, pOutputPathGame, vTexArgc, vTexArgv ); + + // Generale small TGA name, by removing the "large" part + char pSmallTGAName[ MAX_PATH ]; + strcpy( pSmallTGAName, pLargeTGAName ); + char *_large = Q_stristr( pSmallTGAName, "_large"); + Assert(_large); + strcpy(_large, _large+6); + + // Load up the large icon + int nCheckWidth, nCheckHeight; + ImageFormat largeFmt; + float gamma; + CUtlBuffer largeTGAFileData; + CUtlMemory largeTGAImageData; + + if ( !g_pFullFileSystem->ReadFile( pLargeTGAName, NULL, largeTGAFileData ) + || !TGALoader::GetInfo( largeTGAFileData, &nCheckWidth, &nCheckHeight, &largeFmt, &gamma ) + || nCheckWidth != width || nCheckHeight != height ) + { + Error( "Failed to reload image header %s", pLargeTGAName ); + Assert( false ); + return; + } + + largeTGAFileData.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + if ( !TGALoader::LoadRGBA8888( largeTGAFileData, largeTGAImageData, nCheckWidth, nCheckHeight ) + || nCheckWidth != width || nCheckHeight != height ) + { + Error( "Failed to reload image data %s", pLargeTGAName ); + Assert( false ); + return; + } + + // + // Perform a downsample. This is better than just re-rendering at the smaller size, + // which essentially just point-samples the image. + // + CUtlMemory smallTGAImageData; + const int kSmallSize = 128; + smallTGAImageData.EnsureCapacity(kSmallSize*kSmallSize*4); + ImageLoader::ResampleInfo_t resampleInfo; + resampleInfo.m_nSrcWidth = width; + resampleInfo.m_nSrcHeight = height; + resampleInfo.m_flSrcGamma = gamma; + resampleInfo.m_pSrc = (unsigned char *)largeTGAImageData.Base(); + + resampleInfo.m_nDestWidth = kSmallSize; + resampleInfo.m_nDestHeight = kSmallSize; + resampleInfo.m_flDestGamma = gamma; + resampleInfo.m_pDest = (unsigned char *)smallTGAImageData.Base(); + + resampleInfo.m_nFlags = ImageLoader::RESAMPLE_CLAMPS | ImageLoader::RESAMPLE_CLAMPT; + //resampleInfo.m_nFlags |= ImageLoader::RESAMPLE_NICE_FILTER; // Turn this off. It has some sort of edge enhancement or something. Causes edges to ring. + + if ( !ImageLoader::ResampleRGBA8888( resampleInfo ) ) + { + Error( "Failed to resample %s", pLargeTGAName ); + Assert( false ); + return; + } + + // Save it + if ( !SaveTgaAndAddToP4( resampleInfo.m_pDest, IMAGE_FORMAT_RGBA8888, resampleInfo.m_nDestWidth, resampleInfo.m_nDestHeight, pSmallTGAName ) ) + { + return; + } + + // Save the .cfg file. + SaveCaps( pSmallTGAName ); + + // write corresponding .txt file with vtex options + V_strcpy_safe( pVTexOptionsFileName, pSmallTGAName ); + V_SetExtension( pVTexOptionsFileName, ".txt", sizeof( pVTexOptionsFileName ) ); + + { + CP4AutoEditAddFile autop4( pVTexOptionsFileName ); + FileHandle_t hVTexOptionsFile = g_pFullFileSystem->Open( pVTexOptionsFileName, "w" ); + if ( hVTexOptionsFile ) + { + g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nomip 1\n" ); + g_pFullFileSystem->FPrintf( hVTexOptionsFile, "nolod 1\n" ); + g_pFullFileSystem->Close( hVTexOptionsFile ); + } + } + + // run vtex on the TGA and .txt file to create .VTF and add it to our Perforce changelist + vTexArgc = 0; + vTexArgv[ vTexArgc++ ] = ""; + vTexArgv[ vTexArgc++ ] = "-quiet"; + vTexArgv[ vTexArgc++ ] = "-UseStandardError"; + vTexArgv[ vTexArgc++ ] = "-WarningsAsErrors"; + vTexArgv[ vTexArgc++ ] = "-p4skip"; + vTexArgv[ vTexArgc++ ] = "-outdir"; + vTexArgv[ vTexArgc++ ] = pOutputPathGame; + vTexArgv[ vTexArgc++ ] = (char *)pSmallTGAName; + g_pVTex->VTex( MdlPickerFSFactory, pOutputPathGame, vTexArgc, vTexArgv ); + + + // restore the preview panel to its original state + m_pMDLPreview->SetParent( pParent ); + m_pMDLPreview->SetPos( PanelX, PanelY ); + m_pMDLPreview->SetSize( PanelWidth, PanelHeight ); + m_pMDLPreview->SetBackgroundColor( PanelColor ); + ((VPanel *)m_pMDLPreview->GetVPanel())->Solve(); +} + + +CUtlString CMDLPicker::GetOutputFileSuffix() +{ + char temp[256]; + TextEntry *pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "SuffixText" ); + if ( pTempValue ) + { + pTempValue->GetText( temp, sizeof( temp ) ); + } + return temp; +} + + +//----------------------------------------------------------------------------- +// Saves the screen cap information and camera position +//----------------------------------------------------------------------------- +void CMDLPicker::SaveCaps( const char *szFileName ) +{ + char temp[ _MAX_PATH ]; + + KeyValues *CaptureData = new KeyValues( "ScreenCaps" ); + + Vector vecPos; + QAngle angDir; + m_pMDLPreview->GetCameraPositionAndAngles( vecPos, angDir ); + V_sprintf_safe( temp, "%g %g %g", vecPos.x, vecPos.y, vecPos.z ); + CaptureData->SetString( "CameraPosition", temp ); + V_sprintf_safe( temp, "%g %g %g", angDir.x, angDir.y, angDir.z ); + CaptureData->SetString( "CameraAngles", temp ); + + Vector vecOffset; + m_pMDLPreview->GetCameraOffset( vecOffset ); + V_sprintf_safe( temp, "%g %g %g", vecOffset.x, vecOffset.y, vecOffset.z ); + CaptureData->SetString( "CameraOffset", temp ); + + CColorPickerButton *m_pBackgroundColor; + m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" ); + Color color = m_pBackgroundColor->GetColor(); + + V_sprintf_safe( temp, "%d %d %d %d", color.r(), color.g(), color.b(), color.a() ); + CaptureData->SetString( "BackgroundColor", temp ); + + TextEntry *pTempValue; + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" ); + pTempValue->GetText( temp, sizeof( temp ) ); + CaptureData->SetString( "Width", temp ); + + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" ); + pTempValue->GetText( temp, sizeof( temp ) ); + CaptureData->SetString( "Height", temp ); + + vgui::CheckButton *pToggle; + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "NoGround" ); + CaptureData->SetInt( "NoGround", pToggle->IsSelected() ? 1 : 0 ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Collision" ); + CaptureData->SetInt( "Collision", pToggle->IsSelected() ? 1 : 0 ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Wireframe" ); + CaptureData->SetInt( "Wifeframe", pToggle->IsSelected() ? 1 : 0 ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LockView" ); + CaptureData->SetInt( "LockView", pToggle->IsSelected() ? 1 : 0 ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LookAtCamera" ); + CaptureData->SetInt( "LookAtCamera", pToggle->IsSelected() ? 1 : 0 ); + + for( int i = 1; i < MAX_SELECTED_MODELS; i++ ) + { + if ( m_hSelectedMDL[ i ] != MDLHANDLE_INVALID ) + { + const char *MergedModelName = vgui::MDLCache()->GetModelName( m_hSelectedMDL[ i ] ); + V_sprintf_safe( temp, "Merged_%d", i ); + CaptureData->SetString( temp, MergedModelName ); + } + } + + if ( szFileName != NULL ) + { + strcpy( temp, szFileName ); + V_SetExtension( temp, ".cfg", sizeof( temp ) ); + } + else + { + Label *m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->GetText( temp, sizeof( temp ) ); + + strcat( temp, "ScreenCaps.cfg" ); + } + + CaptureData->SaveToFile( g_pFullFileSystem, temp ); + CP4AutoAddFile autop4( temp ); +} + + +//----------------------------------------------------------------------------- +// Restores the screen cap information and camera position +//----------------------------------------------------------------------------- +bool CMDLPicker::RestoreCaps( const char *szFileName ) +{ + char temp[ _MAX_PATH ]; + + if ( szFileName != NULL ) + { + strcpy( temp, szFileName ); + V_SetExtension( temp, ".cfg", sizeof( temp ) ); + } + else + { + Label *m_pOutputDirectory = ( Label * )m_pScreenCapsPage->FindChildByName( "OutputDirectory" ); + m_pOutputDirectory->GetText( temp, sizeof( temp ) ); + strcat( temp, "ScreenCaps.cfg" ); + } + + KeyValues *CaptureData = new KeyValues( "ScreenCaps" ); + + if ( !CaptureData->LoadFromFile( g_pFullFileSystem, temp ) ) + { + return false; + } + + Vector vecPos; + QAngle angDir; + Vector vecOffset; + sscanf( CaptureData->GetString( "CameraPosition" ), "%g %g %g", &vecPos.x, &vecPos.y, &vecPos.z ); + sscanf( CaptureData->GetString( "CameraAngles" ), "%g %g %g", &angDir.x, &angDir.y, &angDir.z ); + sscanf( CaptureData->GetString( "CameraOffset" ), "%g %g %g", &vecOffset.x, &vecOffset.y, &vecOffset.z ); + + m_pMDLPreview->SetCameraOffset( vecOffset ); + m_pMDLPreview->SetCameraPositionAndAngles( vecPos, angDir ); + + CColorPickerButton *m_pBackgroundColor; + int r, g, b, a; + m_pBackgroundColor = ( CColorPickerButton * )m_pScreenCapsPage->FindChildByName( "BackgroundColor" ); + sscanf( CaptureData->GetString( "BackgroundColor" ), "%d %d %d %d", &r, &g, &b, &a ); + m_pBackgroundColor->SetColor( r, g, b, a ); + + TextEntry *pTempValue; + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "WidthText" ); + pTempValue->SetText( CaptureData->GetString( "Width" ) ); + + pTempValue = ( TextEntry * )m_pScreenCapsPage->FindChildByName( "HeightText" ); + pTempValue->SetText( CaptureData->GetString( "Height" ) ); + + + vgui::CheckButton *pToggle; + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "NoGround" ); + pToggle->SetSelected( ( CaptureData->GetInt( "NoGround" ) == 1 ) ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Collision" ); + pToggle->SetSelected( ( CaptureData->GetInt( "Collision" ) == 1 ) ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "Wireframe" ); + pToggle->SetSelected( ( CaptureData->GetInt( "Wireframe" ) == 1 ) ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LockView" ); + pToggle->SetSelected( ( CaptureData->GetInt( "LockView" ) == 1 ) ); + + pToggle = (vgui::CheckButton*)m_pRenderPage->FindChildByName( "LookAtCamera" ); + pToggle->SetSelected( ( CaptureData->GetInt( "LookAtCamera" ) == 1 ) ); + + for( int i = 1; i < MAX_SELECTED_MODELS; i++ ) + { + V_sprintf_safe( temp, "Merged_%d", i ); + const char *MergedModelName = CaptureData->GetString( temp, NULL ); + if ( MergedModelName ) + { + SelectMDL( MergedModelName, false, i ); + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Purpose: rebuilds the list of activities +//----------------------------------------------------------------------------- +void CMDLPicker::RefreshActivitiesAndSequencesList() +{ + m_pActivitiesList->RemoveAll(); + m_pSequencesList->RemoveAll(); + m_pMDLPreview->SetSequence( 0 ); + + if ( m_hSelectedMDL[ 0 ] == MDLHANDLE_INVALID ) + { + m_pActivitiesList->SetEmptyListText("No .MDL file currently selected"); + m_pSequencesList->SetEmptyListText("No .MDL file currently selected"); + return; + } + + m_pActivitiesList->SetEmptyListText(".MDL file contains no activities"); + m_pSequencesList->SetEmptyListText(".MDL file contains no sequences"); + + studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] ); + + CUtlDict activityNames( true, 0, hdr->GetNumSeq() ); + + for (int j = 0; j < hdr->GetNumSeq(); j++) + { + if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN)) + { + const char *pActivityName = hdr->pSeqdesc(j).pszActivityName(); + if ( pActivityName && pActivityName[0] ) + { + // Multiple sequences can have the same activity name; only add unique activity names + if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() ) + { + KeyValues *pkv = new KeyValues("node", "activity", pActivityName ); + int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName ); + pDrag->SetString( "texttype", "activityName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL[ 0 ] ) ); + m_pActivitiesList->SetItemDragData( nItemID, pDrag ); + + activityNames.Insert( pActivityName, j ); + } + } + + const char *pSequenceName = hdr->pSeqdesc(j).pszLabel(); + if ( pSequenceName && pSequenceName[0] ) + { + KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName); + int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName ); + pDrag->SetString( "texttype", "sequenceName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL[ 0 ] ) ); + m_pSequencesList->SetItemDragData( nItemID, pDrag ); + } + } + } +} + +//----------------------------------------------------------------------------- +// A MDL was selected +//----------------------------------------------------------------------------- +void CMDLPicker::OnSelectedAssetPicked( const char *pMDLName ) +{ + char pRelativePath[MAX_PATH]; + + int nSelectSecondary = -1; + if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) ) + { + nSelectSecondary = 0; + } + else if ( input()->IsMouseDown(MOUSE_RIGHT) ) + { + nSelectSecondary = 1; + } + + if ( pMDLName ) + { + Q_snprintf( pRelativePath, sizeof(pRelativePath), "models\\%s", pMDLName ); + SelectMDL( pRelativePath, true, nSelectSecondary ); + } + else + { + SelectMDL( NULL, true, nSelectSecondary ); + } +} + + +//----------------------------------------------------------------------------- +// Allows external apps to select a MDL +//----------------------------------------------------------------------------- +void CMDLPicker::SelectMDL( const char *pRelativePath, bool bDoLookAt, int nSelectSecondary ) +{ + MDLHandle_t hSelectedMDL = pRelativePath ? vgui::MDLCache()->FindMDL( pRelativePath ) : MDLHANDLE_INVALID; + int index = ( nSelectSecondary > 0 ? nSelectSecondary : 0 ); + + // We didn't change models after all... + if ( hSelectedMDL == m_hSelectedMDL[ index ] ) + { + // vgui::MDLCache()->FindMDL adds a reference by default we don't use, release it again + if ( hSelectedMDL != MDLHANDLE_INVALID ) + { + vgui::MDLCache()->Release( hSelectedMDL ); + } + return; + } + + m_hSelectedMDL[ index ] = hSelectedMDL; + + if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL[ index ] ) ) + { + m_hSelectedMDL[ index ] = MDLHANDLE_INVALID; + } + if ( nSelectSecondary != -1 ) + { + m_pMDLPreview->ClearMergeMDLs(); + for( int i = 1; i < MAX_SELECTED_MODELS; i++ ) + { + if ( i != index ) + { + m_hSelectedMDL[ i ] = MDLHANDLE_INVALID; + } + } + } + + if ( index > 0 ) + { + m_pMDLPreview->SetMergeMDL( m_hSelectedMDL[ index ] ); + } + else + { + m_pMDLPreview->SetMDL( m_hSelectedMDL[ index ] ); + + if ( bDoLookAt ) + { + m_pMDLPreview->LookAtMDL(); + } + + if ( m_nFlags & ( PAGE_SKINS ) ) + { + UpdateSkinsList(); + } + + if ( m_nFlags & ( PAGE_INFO ) ) + { + UpdateInfoTab(); + } + + if ( m_nFlags & (PAGE_ACTIVITIES|PAGE_SEQUENCES) ) + { + RefreshActivitiesAndSequencesList(); + } + } + + // vgui::MDLCache()->FindMDL adds a reference by default we don't use, release it again + if ( hSelectedMDL != MDLHANDLE_INVALID ) + { + vgui::MDLCache()->Release( hSelectedMDL ); + } + + PostActionSignal( new KeyValues( "MDLPreviewChanged", "mdl", pRelativePath ? pRelativePath : "" ) ); +} + + +//----------------------------------------------------------------------------- +// Purpose: updates revision view on a file being selected +//----------------------------------------------------------------------------- +void CMDLPicker::OnCheckButtonChecked(KeyValues *kv) +{ +// RefreshMDLList(); + BaseClass::OnCheckButtonChecked( kv ); + RefreshRenderSettings(); +} + + +void CMDLPicker::GetSelectedMDLName( char *pBuffer, int nMaxLen ) +{ + Assert( nMaxLen > 0 ); + if ( GetSelectedAssetCount() > 0 ) + { + Q_snprintf( pBuffer, nMaxLen, "models\\%s", GetSelectedAsset( ) ); + } + else + { + pBuffer[0] = 0; + } +} + +//----------------------------------------------------------------------------- +// Gets the selected activity/sequence +//----------------------------------------------------------------------------- +int CMDLPicker::GetSelectedPage( ) +{ + if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) ) + return PAGE_SEQUENCES; + + if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) ) + return PAGE_ACTIVITIES; + + return PAGE_NONE; +} + +const char *CMDLPicker::GetSelectedSequenceName() +{ + if ( !m_pSequencesPage ) + return NULL; + + int nIndex = m_pSequencesList->GetSelectedItem( 0 ); + if ( nIndex >= 0 ) + { + KeyValues *pkv = m_pSequencesList->GetItem( nIndex ); + return pkv->GetString( "sequence", NULL ); + } + + return NULL; +} + +const char *CMDLPicker::GetSelectedActivityName() +{ + if ( !m_pActivitiesPage ) + return NULL; + + int nIndex = m_pActivitiesList->GetSelectedItem( 0 ); + if ( nIndex >= 0 ) + { + KeyValues *pkv = m_pActivitiesList->GetItem( nIndex ); + return pkv->GetString( "activity", NULL ); + } + return NULL; +} + +int CMDLPicker::GetSelectedSkin() +{ + if ( !m_pSkinsPage ) + return 0; + + int nIndex = m_pSkinsList->GetSelectedItem( 0 ); + if ( nIndex >= 0 ) + { + return nIndex; + } + return 0; +} + +//----------------------------------------------------------------------------- +// Plays the selected activity +//----------------------------------------------------------------------------- +void CMDLPicker::SelectActivity( const char *pActivityName ) +{ + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] ); + for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ ) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 ) + { + // FIXME: Add weighted sequence selection logic? + m_pMDLPreview->SetSequence( i ); + break; + } + } + + PostActionSignal( new KeyValues( "SequenceSelectionChanged", "activity", pActivityName ) ); +} + + +//----------------------------------------------------------------------------- +// Plays the selected sequence +//----------------------------------------------------------------------------- +void CMDLPicker::SelectSequence( const char *pSequenceName ) +{ + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] ); + for (int i = 0; i < pstudiohdr->GetNumSeq(); i++) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) ) + { + m_pMDLPreview->SetSequence( i ); + break; + } + } + + PostActionSignal( new KeyValues( "SequenceSelectionChanged", "sequence", pSequenceName ) ); +} + +void CMDLPicker::SelectSkin( int nSkin ) +{ + m_pMDLPreview->SetSkin( nSkin ); + PostActionSignal( new KeyValues( "SkinSelectionChanged", "skin", nSkin)); +} + + +//----------------------------------------------------------------------------- +// Purpose: Updates preview when an item is selected +//----------------------------------------------------------------------------- +void CMDLPicker::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL); + if ( m_pSequencesList && (pPanel == m_pSequencesList ) ) + { + const char *pSequenceName = GetSelectedSequenceName(); + if ( pSequenceName ) + { + SelectSequence( pSequenceName ); + } + return; + } + + if ( m_pActivitiesList && ( pPanel == m_pActivitiesList ) ) + { + const char *pActivityName = GetSelectedActivityName(); + if ( pActivityName ) + { + SelectActivity( pActivityName ); + } + return; + } + + if ( m_pSkinsList && ( pPanel == m_pSkinsList ) ) + { + int nSelectedSkin = GetSelectedSkin(); + SelectSkin( nSelectedSkin ); + + return; + } + + BaseClass::OnItemSelected( kv ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CMDLPicker::OnPageChanged( ) +{ + if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) ) + { + m_pSequencesList->RequestFocus(); + + const char *pSequenceName = GetSelectedSequenceName(); + + if ( pSequenceName ) + { + SelectSequence( pSequenceName ); + } + return; + } + + if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) ) + { + m_pActivitiesList->RequestFocus(); + + const char *pActivityName = GetSelectedActivityName(); + + if ( pActivityName ) + { + SelectActivity( pActivityName ); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CMDLPickerFrame::CMDLPickerFrame( vgui::Panel *pParent, const char *pTitle, int nFlags ) : + BaseClass( pParent ) +{ + SetAssetPicker( new CMDLPicker( this, nFlags ) ); + LoadControlSettingsAndUserConfig( "resource/mdlpickerframe.res" ); + SetTitle( pTitle, false ); +} + +CMDLPickerFrame::~CMDLPickerFrame() +{ +} + + +//----------------------------------------------------------------------------- +// Allows external apps to select a MDL +//----------------------------------------------------------------------------- +void CMDLPickerFrame::SelectMDL( const char *pRelativePath ) +{ + static_cast( GetAssetPicker() )->SelectMDL( pRelativePath ); +} + +int CMDLPicker::UpdateSkinsList() +{ + int nNumSkins = 0; + + if ( m_pSkinsList ) + { + m_pSkinsList->RemoveAll(); + + studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] ); + if ( hdr ) + { + nNumSkins = hdr->numskinfamilies; + for ( int i = 0; i < nNumSkins; i++ ) + { + char skinText[25] = ""; + V_sprintf_safe( skinText, "skin%i", i ); + KeyValues *pkv = new KeyValues("node", "skin", skinText ); + m_pSkinsList->AddItem( pkv, 0, false, false ); + } + } + } + + return nNumSkins; +} + +void CMDLPicker::UpdateInfoTab() +{ + studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL[ 0 ] ); + if ( !hdr ) + return; + + int nMass = hdr->mass; + Panel *pTempPanel = m_pInfoPage->FindChildByName("MassValue"); + char massBuff[10]; + Q_snprintf( massBuff, 10, "%d", nMass ); + ((vgui::Label *)pTempPanel)->SetText( massBuff ); + bool bIsStatic = hdr->flags & STUDIOHDR_FLAGS_STATIC_PROP; + bool bIsPhysics = false; + const char* buf = hdr->KeyValueText(); + Label * pTempLabel = (Label *)m_pInfoPage->FindChildByName("StaticText"); + pTempLabel->SetVisible( false ); + if( buf ) + { + buf = Q_strstr( buf, "prop_data" ); + if ( buf ) + { + int iPropDataCount = UpdatePropDataList( buf, bIsStatic ); + if( iPropDataCount ) + { + bIsPhysics = true; + } + } + else + { + m_pPropDataList->RemoveAll(); + } + } + else + { + m_pPropDataList->RemoveAll(); + } + + CheckButton * pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("StaticObject"); + pTempCheck->SetCheckButtonCheckable( true ); + pTempCheck->SetSelected( bIsStatic ); + pTempCheck->SetCheckButtonCheckable( false ); + pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("PhysicsObject"); + pTempCheck->SetCheckButtonCheckable( true ); + pTempCheck->SetSelected( bIsPhysics ); + pTempCheck->SetCheckButtonCheckable( false ); + pTempCheck = (CheckButton *)m_pInfoPage->FindChildByName("DynamicObject"); + pTempCheck->SetCheckButtonCheckable( true ); + pTempCheck->SetSelected( !bIsPhysics ); + pTempCheck->SetCheckButtonCheckable( false ); + + +} + +int CMDLPicker::UpdatePropDataList( const char* pszPropData, bool &bIsStatic ) +{ + int iCount = 0; + + if ( m_pPropDataList ) + { + m_pPropDataList->RemoveAll(); + + const char * endPropData = strchr( pszPropData, '}' ); + char keyText[255] = ""; + char valueText[255] = ""; + const char *beginChunk = strchr( pszPropData, '\"' ); + if ( !beginChunk ) + { + return 0; + } + beginChunk++; + const char *endChunk = strchr( beginChunk, '\"' ); + while( endChunk ) + { + Q_memcpy( keyText, beginChunk, endChunk - beginChunk ); + beginChunk = endChunk + 1; + beginChunk = strchr( beginChunk, '\"' ) + 1; + endChunk = strchr( beginChunk, '\"' ); + Q_memcpy( valueText, beginChunk, endChunk - beginChunk ); + if( !Q_strcmp( keyText, "allowstatic" ) && !Q_strcmp( valueText , "1" ) ) + { + if ( !bIsStatic ) + { + Label * pTempLabel = (Label *)m_pInfoPage->FindChildByName("StaticText"); + pTempLabel->SetVisible( true ); + } + bIsStatic &= true; + } + KeyValues *pkv = new KeyValues("node", "key", keyText, "value", valueText ); + m_pPropDataList->AddItem( pkv, 0, false, false ); + Q_memset( keyText, 0, 255 ); + Q_memset( valueText, 0, 255 ); + iCount++; + beginChunk = endChunk + 1; + beginChunk = strchr( beginChunk, '\"' ); + if ( !beginChunk || beginChunk > endPropData ) + { + return iCount; + } + beginChunk++; + endChunk = strchr( beginChunk, '\"' ); + } + } + return iCount; +} diff --git a/src/vgui2/matsys_controls/mdlsequencepicker.cpp b/src/vgui2/matsys_controls/mdlsequencepicker.cpp new file mode 100644 index 000000000..8e4443b03 --- /dev/null +++ b/src/vgui2/matsys_controls/mdlsequencepicker.cpp @@ -0,0 +1,539 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/mdlsequencepicker.h" +#include "tier1/KeyValues.h" +#include "tier1/utldict.h" +#include "datacache/imdlcache.h" +#include "filesystem.h" +#include "studio.h" +#include "vgui/IVGui.h" +#include "vgui/Cursor.h" +#include "vgui/ISurface.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/ComboBox.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/ToolWindow.h" +#include "vgui_controls/Button.h" +#include "matsys_controls/gamefiletreeview.h" +#include "matsys_controls/matsyscontrols.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// MDL Sequence Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CMDLSequencePicker::CMDLSequencePicker( vgui::Panel *pParent ) : BaseClass(pParent, "MDLSequencePicker"), m_Images(false) +{ + vgui::ivgui()->AddTickSignal( GetVPanel() ); + + m_hSelectedMDL = MDLHANDLE_INVALID; + + // Horizontal splitter for mdls + m_pMDLSplitter = new Splitter( this, "MDLSplitter", SPLITTER_MODE_VERTICAL, 1 ); + + vgui::Panel *pSplitterLeftSide = m_pMDLSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pMDLSplitter->GetChild( 1 ); + + // filter selection + m_pFilterList = new ComboBox( pSplitterLeftSide, "FilterList", 16, true ); + m_pFilterList->AddActionSignalTarget( this ); + + // file browser tree controls + m_pFileTree = new CGameFileTreeView( pSplitterLeftSide, "FileTree", "All .MDLs", "models", "mdl" ); + + // build our list of images + m_Images.AddImage( scheme()->GetImage( "resource/icon_folder", false ) ); + m_Images.AddImage( scheme()->GetImage( "resource/icon_folder_selected", false ) ); + m_Images.AddImage( scheme()->GetImage( "resource/icon_file", false ) ); + m_pFileTree->SetImageList( &m_Images, false ); + m_pFileTree->AddActionSignalTarget( this ); + + // property sheet - revisions, changes, etc. + m_pSequenceSplitter = new Splitter( pSplitterRightSide, "SequenceSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + + vgui::Panel *pSplitterTopSide = m_pSequenceSplitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_pSequenceSplitter->GetChild( 1 ); + + // MDL preview + m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" ); + SetSkipChildDuringPainting( m_pMDLPreview ); + + m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" ); + m_pViewsSheet->AddActionSignalTarget( this ); + + // sequences + m_pSequencesPage = new PropertyPage( m_pViewsSheet, "SequencesPage" ); + m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" ); + m_pSequencesList = new ListPanel( m_pSequencesPage, "SequencesList" ); + m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 ); + m_pSequencesList->AddActionSignalTarget( this ); + m_pSequencesList->SetSelectIndividualCells( true ); + m_pSequencesList->SetEmptyListText("No .MDL file currently selected."); + m_pSequencesList->SetDragEnabled( true ); + m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + + // Activities + m_pActivitiesPage = new PropertyPage( m_pViewsSheet, "ActivitiesPage" ); + m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" ); + m_pActivitiesList = new ListPanel( m_pActivitiesPage, "ActivitiesList" ); + m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 ); + m_pActivitiesList->AddActionSignalTarget( this ); + m_pActivitiesList->SetSelectIndividualCells( true ); + m_pActivitiesList->SetEmptyListText( "No .MDL file currently selected." ); + m_pActivitiesList->SetDragEnabled( true ); + m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/mdlsequencepicker.res" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CMDLSequencePicker::~CMDLSequencePicker() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: This is a bit of a hack to make sure that the ToolWindow containing this picker punches +// a hold for the rendering viewport, too +// Input : - +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnTick() +{ + BaseClass::OnTick(); + if ( GetParent() ) + { + ToolWindow *tw = dynamic_cast< ToolWindow * >( GetParent()->GetParent() ); + + if ( tw ) + { + tw->SetSkipChildDuringPainting( IsVisible() ? m_pMDLPreview : NULL ); + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: stops app on close +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnClose() +{ + BaseClass::OnClose(); +} + +//----------------------------------------------------------------------------- +// Purpose: called to open +//----------------------------------------------------------------------------- +void CMDLSequencePicker::Activate() +{ + RefreshFileList(); + RefreshActivitiesAndSequencesList(); +} + + +//----------------------------------------------------------------------------- +// Performs layout +//----------------------------------------------------------------------------- +void CMDLSequencePicker::PerformLayout() +{ + // NOTE: This call should cause auto-resize to occur + // which should fix up the width of the panels + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + // Layout the mdl splitter + m_pMDLSplitter->SetBounds( 0, 0, w, h ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Refreshes the active file list +//----------------------------------------------------------------------------- +void CMDLSequencePicker::RefreshFileList() +{ + m_pFileTree->RefreshFileList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: rebuilds the list of activities +//----------------------------------------------------------------------------- +void CMDLSequencePicker::RefreshActivitiesAndSequencesList() +{ + m_pActivitiesList->RemoveAll(); + m_pSequencesList->RemoveAll(); + m_pMDLPreview->SetSequence( 0 ); + + if ( m_hSelectedMDL == MDLHANDLE_INVALID ) + { + m_pActivitiesList->SetEmptyListText("No .MDL file currently selected"); + m_pSequencesList->SetEmptyListText("No .MDL file currently selected"); + return; + } + + m_pActivitiesList->SetEmptyListText(".MDL file contains no activities"); + m_pSequencesList->SetEmptyListText(".MDL file contains no sequences"); + + studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + + CUtlDict activityNames( true, 0, hdr->GetNumSeq() ); + + for (int j = 0; j < hdr->GetNumSeq(); j++) + { + if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN)) + { + const char *pActivityName = hdr->pSeqdesc(j).pszActivityName(); + if ( pActivityName && pActivityName[0] ) + { + // Multiple sequences can have the same activity name; only add unique activity names + if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() ) + { + KeyValues *pkv = new KeyValues("node", "activity", pActivityName ); + int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName ); + pDrag->SetString( "texttype", "activityName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) ); + m_pActivitiesList->SetItemDragData( nItemID, pDrag ); + + activityNames.Insert( pActivityName, j ); + } + } + + const char *pSequenceName = hdr->pSeqdesc(j).pszLabel(); + if ( pSequenceName && pSequenceName[0] ) + { + KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName); + pkv->SetInt( "seqindex", j ); + + int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName ); + pDrag->SetString( "texttype", "sequenceName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) ); + m_pSequencesList->SetItemDragData( nItemID, pDrag ); + } + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnTextChanged( vgui::Panel *pPanel, const char *pText ) +{ +// m_pFileTree->SetFilter( pText ); + RefreshFileList(); +} + + +/* +//----------------------------------------------------------------------------- +// Purpose: Selects an sequence based on an activity +//----------------------------------------------------------------------------- +int SelectWeightedSequence( studiohdr_t *pstudiohdr, int activity, int curSequence ) +{ + if (! pstudiohdr) + return 0; + + VerifySequenceIndex( pstudiohdr ); + + int weighttotal = 0; + int seq = ACTIVITY_NOT_AVAILABLE; + int weight = 0; + for (int i = 0; i < pstudiohdr->GetNumSeq(); i++) + { + int curActivity = GetSequenceActivity( pstudiohdr, i, &weight ); + if (curActivity == activity) + { + if ( curSequence == i && weight < 0 ) + { + seq = i; + break; + } + weighttotal += iabs(weight); + + int randomValue; + if ( IsInPrediction() ) + randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i ); + else + randomValue = RandomInt( 0, weighttotal - 1 ); + + if (!weighttotal || randomValue < iabs(weight)) + seq = i; + } + } + + return seq; +} +*/ + +//----------------------------------------------------------------------------- +// Plays the selected activity +//----------------------------------------------------------------------------- +void CMDLSequencePicker::PlaySelectedActivity( ) +{ + int nIndex = m_pActivitiesList->GetSelectedItem( 0 ); + if ( nIndex < 0 ) + return; + + KeyValues *pkv = m_pActivitiesList->GetItem( nIndex ); + const char *pActivityName = pkv->GetString( "activity", NULL ); + if ( !pActivityName ) + return; + + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ ) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 ) + { + // FIXME: Add weighted sequence selection logic? + m_pMDLPreview->SetSequence( i ); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Plays the selected sequence +//----------------------------------------------------------------------------- +void CMDLSequencePicker::PlaySelectedSequence( ) +{ + int nIndex = m_pSequencesList->GetSelectedItem( 0 ); + if ( nIndex < 0 ) + return; + + KeyValues *pkv = m_pSequencesList->GetItem( nIndex ); + const char *pSequenceName = pkv->GetString( "sequence", NULL ); + if ( !pSequenceName ) + return; + + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + for (int i = 0; i < pstudiohdr->GetNumSeq(); i++) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) ) + { + m_pMDLPreview->SetSequence( i ); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnPageChanged( ) +{ + if ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) + { + PlaySelectedSequence(); + return; + } + + if ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) + { + PlaySelectedActivity(); + return; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL); + if ( pPanel == m_pSequencesList ) + { + PlaySelectedSequence(); + return; + } + + if ( pPanel == m_pActivitiesList ) + { + PlaySelectedActivity(); + return; + } +} + + +//----------------------------------------------------------------------------- +// An MDL was selected +//----------------------------------------------------------------------------- +void CMDLSequencePicker::SelectMDL( const char *pMDLName ) +{ + m_hSelectedMDL = pMDLName ? vgui::MDLCache()->FindMDL( pMDLName ) : MDLHANDLE_INVALID; + if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL ) ) + { + m_hSelectedMDL = MDLHANDLE_INVALID; + } + m_pMDLPreview->SetMDL( m_hSelectedMDL ); + m_pMDLPreview->LookAtMDL(); + RefreshActivitiesAndSequencesList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: updates revision view on a file being selected +//----------------------------------------------------------------------------- +void CMDLSequencePicker::OnFileSelected() +{ + // update list + int iItem = m_pFileTree->GetFirstSelectedItem(); + if ( iItem < 0 ) + return; + + // Don't bother to change if a directory was selected + KeyValues *pkv = m_pFileTree->GetItemData(iItem); + if ( pkv->GetInt("dir") || pkv->GetInt("root") ) + return; + + surface()->SetCursor(dc_waitarrow); + + const char *pFullPathName = pkv->GetString( "path" ); + + char pRelativePathName[MAX_PATH]; + g_pFullFileSystem->FullPathToRelativePath( pFullPathName, pRelativePathName, sizeof(pRelativePathName) ); + + // FIXME: Check that we're not actually opening the wrong file!! + SelectMDL( pRelativePathName ); +} + +char const *CMDLSequencePicker::GetModelName() +{ + if ( MDLHANDLE_INVALID == m_hSelectedMDL ) + { + return ""; + } + + return vgui::MDLCache()->GetModelName( m_hSelectedMDL ); +} + +char const *CMDLSequencePicker::GetSequenceName() +{ + int nIndex = m_pSequencesList->GetSelectedItem( 0 ); + if ( nIndex < 0 ) + return ""; + + KeyValues *pkv = m_pSequencesList->GetItem( nIndex ); + const char *pSequenceName = pkv->GetString( "sequence", NULL ); + if ( !pSequenceName ) + return ""; + + return pSequenceName; +} + +int CMDLSequencePicker::GetSequenceNumber() +{ + int nIndex = m_pSequencesList->GetSelectedItem( 0 ); + if ( nIndex < 0 ) + return -1; + KeyValues *pkv = m_pSequencesList->GetItem( nIndex ); + return pkv->GetInt( "seqindex", -1 ); +} + +//----------------------------------------------------------------------------- +// Sequence picker frame +//----------------------------------------------------------------------------- +CMDLSequencePickerFrame::CMDLSequencePickerFrame( vgui::Panel *parent, char const *title ) : + BaseClass( parent, "MDLSequencePickerFrame" ) +{ + m_pMDLSequencePicker = new CMDLSequencePicker( this ); + SetTitle( title, true ); + SetSizeable( false ); + SetCloseButtonVisible( false ); + SetMoveable( true ); + SetMinimumSize( 640, 480 ); + Activate(); + m_pMDLSequencePicker->Activate(); + + m_pOK = new Button( this, "OK", "#vgui_ok", this ); + m_pOK->SetCommand( new KeyValues( "OnOK" ) ); + m_pCancel= new Button( this, "Cancel", "#vgui_cancel", this ); + m_pOK->SetCommand( new KeyValues( "OnCancel" ) ); + m_pOK->SetEnabled( false ); + + vgui::ivgui()->AddTickSignal( GetVPanel(), 0 ); +} + +CMDLSequencePickerFrame::~CMDLSequencePickerFrame() +{ +} + +void CMDLSequencePickerFrame::OnTick() +{ + BaseClass::OnTick(); + + bool bHasModel = m_pMDLSequencePicker->GetModelName()[ 0 ] != 0 ? true : false; + bool bHasSequence = m_pMDLSequencePicker->GetSequenceNumber() != -1 ? true : false; + + m_pOK->SetEnabled( bHasModel && bHasSequence ); +} + +void CMDLSequencePickerFrame::PerformLayout() +{ + BaseClass::PerformLayout(); + + int x, y, w, h; + GetClientArea( x, y, w, h ); + h -= 24; + m_pMDLSequencePicker->SetBounds( x, y, w, h ); + + h += 5; + + + int bw = 120; + int bwwithGap = 2 * bw + 10; + + x = ( w - bwwithGap ) / 2; + m_pOK->SetBounds( x, y + h, bw, 16 ); + x += bw + 10; + m_pCancel->SetBounds( x, y + h, bw, 16 ); +} + +void CMDLSequencePickerFrame::OnCancel() +{ + KeyValues *pActionKeys = new KeyValues( "AssetSelected" ); + pActionKeys->SetString( "ModelName", m_pMDLSequencePicker->GetModelName() ); + pActionKeys->SetString( "SequenceName", m_pMDLSequencePicker->GetSequenceName() ); + pActionKeys->SetInt( "SequenceNumber", m_pMDLSequencePicker->GetSequenceNumber() ); + + PostActionSignal( pActionKeys ); + + CloseModal(); +} + +void CMDLSequencePickerFrame::OnOK() +{ + CloseModal(); +} + + diff --git a/src/vgui2/matsys_controls/picker.cpp b/src/vgui2/matsys_controls/picker.cpp new file mode 100644 index 000000000..63622134a --- /dev/null +++ b/src/vgui2/matsys_controls/picker.cpp @@ -0,0 +1,318 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "filesystem.h" +#include "matsys_controls/picker.h" +#include "tier1/KeyValues.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/TextEntry.h" +#include "vgui_controls/Button.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Base asset Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by asset name +//----------------------------------------------------------------------------- +static int __cdecl PickerBrowserSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("choice"); + const char *string2 = item2.kv->GetString("choice"); + return stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CPicker::CPicker( vgui::Panel *pParent, const char *pColumnHeader, const char *pTextType ) : + BaseClass( pParent, "Picker" ) +{ + m_pPickerType = pColumnHeader; + m_pPickerTextType = pTextType; + + // FIXME: Make this an image browser + m_pPickerBrowser = new vgui::ListPanel( this, "Browser" ); + m_pPickerBrowser->AddColumnHeader( 0, "choice", m_pPickerType, 52, 0 ); + m_pPickerBrowser->SetSelectIndividualCells( true ); + m_pPickerBrowser->SetEmptyListText( "Nothing to pick" ); + m_pPickerBrowser->SetDragEnabled( true ); + m_pPickerBrowser->AddActionSignalTarget( this ); + m_pPickerBrowser->SetSortFunc( 0, PickerBrowserSortFunc ); + m_pPickerBrowser->SetSortColumn( 0 ); + + // filter selection + m_pFilterList = new TextEntry( this, "FilterList" ); + m_pFilterList->AddActionSignalTarget( this ); + m_pFilterList->RequestFocus(); + + LoadControlSettingsAndUserConfig( "resource/picker.res" ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CPicker::~CPicker() +{ +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPicker::OnKeyCodePressed( KeyCode code ) +{ + if (( code == KEY_UP ) || ( code == KEY_DOWN ) || ( code == KEY_PAGEUP ) || ( code == KEY_PAGEDOWN )) + { + KeyValues *pMsg = new KeyValues("KeyCodePressed", "code", code); + vgui::ipanel()->SendMessage( m_pPickerBrowser->GetVPanel(), pMsg, GetVPanel()); + pMsg->deleteThis(); + } + else + { + BaseClass::OnKeyCodePressed( code ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the asset list +//----------------------------------------------------------------------------- +void CPicker::SetStringList( const PickerList_t &list ) +{ + m_Type = list.m_Type; + m_pPickerBrowser->RemoveAll(); + + int nCount = list.Count(); + for ( int i = 0; i < nCount; ++i ) + { + const char *pPickerName = list[i].m_pChoiceString; + KeyValues *kv = new KeyValues( "node", "choice", pPickerName ); + if ( m_Type == PICKER_CHOICE_STRING ) + { + kv->SetString( "value", list[i].m_pChoiceValue ); + } + else + { + kv->SetPtr( "value", list[i].m_pChoiceValuePtr ); + } + int nItemID = m_pPickerBrowser->AddItem( kv, 0, false, false ); + + if ( m_Type == PICKER_CHOICE_STRING ) + { + KeyValues *pDrag = new KeyValues( "drag", "text", list[i].m_pChoiceValue ); + if ( m_pPickerTextType ) + { + pDrag->SetString( "texttype", m_pPickerTextType ); + } + m_pPickerBrowser->SetItemDragData( nItemID, pDrag ); + } + } + RefreshChoiceList(); +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes the choice list +//----------------------------------------------------------------------------- +void CPicker::RefreshChoiceList( ) +{ + // Check the filter matches + int nMatchingCount = 0; + int nTotalCount = 0; + for ( int nItemID = m_pPickerBrowser->FirstItem(); nItemID != m_pPickerBrowser->InvalidItemID(); nItemID = m_pPickerBrowser->NextItem( nItemID ) ) + { + KeyValues *kv = m_pPickerBrowser->GetItem( nItemID ); + const char *pPickerName = kv->GetString( "choice" ); + bool bVisible = !m_Filter.Length() || Q_stristr( pPickerName, m_Filter.Get() ); + m_pPickerBrowser->SetItemVisible( nItemID, bVisible ); + if ( bVisible ) + { + ++nMatchingCount; + } + ++nTotalCount; + } + + char pColumnTitle[512]; + Q_snprintf( pColumnTitle, sizeof(pColumnTitle), "%s (%d/%d)", + m_pPickerType, nMatchingCount, nTotalCount ); + m_pPickerBrowser->SetColumnHeaderText( 0, pColumnTitle ); + + m_pPickerBrowser->SortList(); + if ( ( m_pPickerBrowser->GetSelectedItemsCount() == 0 ) && ( m_pPickerBrowser->GetItemCount() > 0 ) ) + { + int nItemID = m_pPickerBrowser->GetItemIDFromRow( 0 ); + m_pPickerBrowser->SetSelectedCell( nItemID, 0 ); + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CPicker::OnTextChanged( ) +{ + int nLength = m_pFilterList->GetTextLength(); + m_Filter.SetLength( nLength ); + if ( nLength > 0 ) + { + m_pFilterList->GetText( m_Filter.GetForModify(), nLength+1 ); + } + RefreshChoiceList(); +} + + +//----------------------------------------------------------------------------- +// Returns the selected string +//----------------------------------------------------------------------------- +PickerChoiceType_t CPicker::GetSelectionType() const +{ + return m_Type; +} + +const char *CPicker::GetSelectedString( ) const +{ + if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 ) + return NULL; + + if ( m_Type != PICKER_CHOICE_STRING ) + return NULL; + + int nIndex = m_pPickerBrowser->GetSelectedItem( 0 ); + KeyValues *pItemKeyValues = m_pPickerBrowser->GetItem( nIndex ); + return pItemKeyValues->GetString( "value" ); +} + +void *CPicker::GetSelectedPtr( ) const +{ + if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 ) + return NULL; + + if ( m_Type != PICKER_CHOICE_PTR ) + return NULL; + + int nIndex = m_pPickerBrowser->GetSelectedItem( 0 ); + KeyValues *pItemKeyValues = m_pPickerBrowser->GetItem( nIndex ); + return pItemKeyValues->GetPtr( "value" ); +} + + +//----------------------------------------------------------------------------- +// Returns the index of the selected string +//----------------------------------------------------------------------------- +int CPicker::GetSelectedIndex() +{ + if ( m_pPickerBrowser->GetSelectedItemsCount() == 0 ) + return -1; + + return m_pPickerBrowser->GetSelectedItem( 0 ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CPickerFrame::CPickerFrame( vgui::Panel *pParent, const char *pTitle, const char *pPickerType, const char *pTextType ) : + BaseClass( pParent, "PickerFrame" ) +{ + m_pContextKeyValues = NULL; + SetDeleteSelfOnClose( true ); + m_pPicker = new CPicker( this, pPickerType, pTextType ); + m_pPicker->AddActionSignalTarget( this ); + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/pickerframe.res" ); + + SetTitle( pTitle, false ); +} + +CPickerFrame::~CPickerFrame() +{ + CleanUpMessage(); +} + + +//----------------------------------------------------------------------------- +// Deletes the message +//----------------------------------------------------------------------------- +void CPickerFrame::CleanUpMessage() +{ + if ( m_pContextKeyValues ) + { + m_pContextKeyValues->deleteThis(); + m_pContextKeyValues = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CPickerFrame::DoModal( const PickerList_t &list, KeyValues *pContextKeyValues ) +{ + CleanUpMessage(); + m_pContextKeyValues = pContextKeyValues; + m_pPicker->SetStringList( list ); + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CPickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Open" ) ) + { + KeyValues *pActionKeys = new KeyValues( "Picked" ); + pActionKeys->SetInt( "choiceIndex", m_pPicker->GetSelectedIndex( ) ); + + if ( m_pPicker->GetSelectionType() == PICKER_CHOICE_STRING ) + { + const char *pPickerName = m_pPicker->GetSelectedString( ); + pActionKeys->SetString( "choice", pPickerName ); + } + else + { + void *pPickerPtr = m_pPicker->GetSelectedPtr( ); + pActionKeys->SetPtr( "choice", pPickerPtr ); + } + + if ( m_pContextKeyValues ) + { + pActionKeys->AddSubKey( m_pContextKeyValues ); + m_pContextKeyValues = NULL; + } + PostActionSignal( pActionKeys ); + CloseModal(); + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + diff --git a/src/vgui2/matsys_controls/potterywheelpanel.cpp b/src/vgui2/matsys_controls/potterywheelpanel.cpp new file mode 100644 index 000000000..250fb64c1 --- /dev/null +++ b/src/vgui2/matsys_controls/potterywheelpanel.cpp @@ -0,0 +1,1231 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/potterywheelpanel.h" +#include "matsys_controls/manipulator.h" +#include "vgui/ISystem.h" +#include "vgui/Cursor.h" +#include "vgui/IVGui.h" +#include "vgui/ISurface.h" +#include "vgui/IInput.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "dmxloader/dmxelement.h" +#include "vgui_controls/Frame.h" +#include "convar.h" +#include "tier0/dbg.h" +#include "matsys_controls/matsyscontrols.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/imaterialsystem.h" +#include "istudiorender.h" +#include "materialsystem/imaterialsystemhardwareconfig.h" +#include "tier2/renderutils.h" +#include "tier1/KeyValues.h" +#include "materialsystem/imesh.h" + +#include "inputsystem/iinputsystem.h" + +#include "renderparm.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// Translation manipulator +//----------------------------------------------------------------------------- +class CTranslationManipulator : public CTransformManipulator +{ +public: + CTranslationManipulator( matrix3x4_t *pTransform ); + + // Methods of IManipulator + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ); + virtual void OnCursorMoved( int x, int y ); + +protected: + int m_lastx, m_lasty; +}; + + +CTranslationManipulator::CTranslationManipulator( matrix3x4_t *pTransform ) : CTransformManipulator( pTransform ) +{ + m_lastx = m_lasty = 0; +} + +void CTranslationManipulator::OnMousePressed( vgui::MouseCode code, int x, int y ) +{ + m_lasty = y; + m_lastx = x; +} + +void CTranslationManipulator::OnCursorMoved( int x, int y ) +{ + if ( !m_pTransform ) + return; + + Vector vPosition; + QAngle quakeEuler; + MatrixAngles( *m_pTransform, quakeEuler, vPosition ); + + Vector forward, right, up; + AngleVectors( quakeEuler, &forward, &right, &up ); + + int dy = y - m_lasty; + int dx = x - m_lastx; + + right *= -0.2f * dx; + up *= 0.2f * dy; + vPosition += up + right; + + m_lastx = x; + m_lasty = y; + + PositionMatrix( vPosition, *m_pTransform ); +} + + +//----------------------------------------------------------------------------- +// Zoom manipulator +//----------------------------------------------------------------------------- +class CZoomManipulator : public CBaseManipulator +{ +public: + CZoomManipulator( float *pDistance ); + + // Methods of IManipulator + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ); + virtual void OnCursorMoved( int x, int y ); + +protected: + int m_lasty; + float *m_pDistance; +}; + +CZoomManipulator::CZoomManipulator( float *pDistance ) +{ + m_lasty = 0; + m_pDistance = pDistance; +} + +void CZoomManipulator::OnMousePressed( vgui::MouseCode code, int x, int y ) +{ + m_lasty = y; +} + +void CZoomManipulator::OnCursorMoved( int x, int y ) +{ + float delta = 0.2f * ( y - m_lasty ); + m_lasty = y; + *m_pDistance *= pow( 1.01f, delta ); +} + + +//----------------------------------------------------------------------------- +// Rotation manipulator +//----------------------------------------------------------------------------- +class CRotationManipulator : public CTransformManipulator +{ +public: + CRotationManipulator( matrix3x4_t *pTransform ); + + // Inherited from IManipulator + virtual void OnMousePressed( vgui::MouseCode code, int x, int y ); + virtual void OnCursorMoved( int x, int y ); + virtual void UpdateTransform(); + + void UpdateFromMatrix( void ); + +private: + int m_lastx, m_lasty; + float m_altitude, m_azimuth, m_roll; + bool m_bDoRoll; +}; + + +CRotationManipulator::CRotationManipulator( matrix3x4_t *pTransform ) : CTransformManipulator( pTransform ) +{ + m_lastx = m_lasty = 0; + m_altitude = M_PI/6; + m_azimuth = -3*M_PI/4; + m_roll = 0.0f; + m_bDoRoll = false; + UpdateTransform(); +} + +void CRotationManipulator::OnMousePressed( vgui::MouseCode code, int x, int y ) +{ + if ( input()->IsKeyDown( KEY_LALT ) || input()->IsKeyDown( KEY_RALT ) ) + { + m_bDoRoll = true; + } + else + { + m_bDoRoll = false; + } + m_lasty = y; + m_lastx = x; +} + +void CRotationManipulator::OnCursorMoved( int x, int y ) +{ + if ( m_bDoRoll ) + { + m_roll += 0.002f * ( m_lastx - x ); + } + else + { + m_azimuth += 0.002f * ( m_lastx - x ); + m_altitude -= 0.002f * ( m_lasty - y ); + m_altitude = max( (float)-M_PI/2, min( (float)M_PI/2, m_altitude ) ); + } + + m_lastx = x; + m_lasty = y; + + UpdateTransform(); +} + +void CRotationManipulator::UpdateTransform() +{ + if ( !m_pTransform ) + return; + + QAngle angles( RAD2DEG( m_altitude ), RAD2DEG( m_azimuth ), RAD2DEG( m_roll ) ); + Vector vecPosition; + MatrixGetColumn( *m_pTransform, 3, vecPosition ); + AngleMatrix( angles, vecPosition, *m_pTransform ); +} + +void CRotationManipulator::UpdateFromMatrix( void ) +{ + if ( !m_pTransform ) + return; + + QAngle angDir; + Vector vecPos; + + MatrixAngles( *m_pTransform, angDir, vecPos ); + + m_altitude = DEG2RAD( angDir.x ); + m_azimuth = DEG2RAD( angDir.y ); + m_roll = DEG2RAD( angDir.z ); +} + + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CPotteryWheelPanel::CPotteryWheelPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ), + m_pCameraRotate( NULL ), + m_pCameraTranslate( NULL ), + m_pCameraZoom( NULL ), + m_pLightManip( NULL ), + m_pCurrentManip( NULL ), + m_nCaptureMouseCode( vgui::MouseCode( -1 ) ), + m_xoffset( 0 ), m_yoffset( 0 ), + m_bRenderToTexture( true ) +{ + m_bHasLightProbe = false; + + SetPaintBackgroundEnabled( false ); + SetPaintBorderEnabled( false ); + m_ClearColor.SetColor( 76, 88, 68, 255 ); + + SetIdentityMatrix( m_CameraPivot ); + + CreateDefaultLights(); + + m_nManipStartX = m_nManipStartY = 0; + + m_vecCameraOffset.Init( 100.0f, 0.0f, 0.0f ); + + m_Camera.m_flZNear = 3.0f; + m_Camera.m_flZFar = 16384.0f * 1.73205080757f; + m_Camera.m_flFOV = 30.0f; + + m_pCameraRotate = new CRotationManipulator( &m_CameraPivot ); + m_pCameraTranslate = new CTranslationManipulator( &m_CameraPivot ); + m_pCameraZoom = new CZoomManipulator( &m_vecCameraOffset.x ); + + KeyValues *pMaterialKeys = new KeyValues( "Wireframe", "$model", "1" ); + + pMaterialKeys->SetString( "$vertexcolor", "1" ); + m_Wireframe.Init( "potterywheelpanelwireframe", pMaterialKeys ); + + SetKeyBoardInputEnabled( true ); + UpdateCameraTransform(); +} + +void CPotteryWheelPanel::ApplySettings( KeyValues *inResourceData ) +{ + BaseClass::ApplySettings( inResourceData ); + + KeyValues *pLights = inResourceData->FindKey( "lights" ); + if ( pLights ) + { + ParseLightsFromKV( pLights ); + } +} + +void CPotteryWheelPanel::Init( int x, int y, int wide, int tall ) +{ + BaseClass::Init( x, y, wide, tall ); + + // Used to poll input + vgui::ivgui()->AddTickSignal( GetVPanel() ); +} + + +CPotteryWheelPanel::~CPotteryWheelPanel() +{ + m_Wireframe.Shutdown(); + m_LightProbeBackground.Shutdown(); + m_LightProbeHDRBackground.Shutdown(); + m_LightProbeCubemap.Shutdown(); + m_LightProbeHDRCubemap.Shutdown(); + + if ( m_pCameraRotate ) + { + delete m_pCameraRotate; + m_pCameraRotate = NULL; + } + + if ( m_pCameraZoom ) + { + delete m_pCameraZoom; + m_pCameraZoom = NULL; + } + + if ( m_pCameraTranslate ) + { + delete m_pCameraTranslate; + m_pCameraTranslate = NULL; + } + + DestroyLights(); +} + +void CPotteryWheelPanel::CreateDefaultLights() +{ + for ( int i = 0; i < 6; ++i ) + { + m_vecAmbientCube[i].Init( 0.4f, 0.4f, 0.4f, 1.0f ); + } + + memset( &m_Lights[0].m_Desc, 0, sizeof(LightDesc_t) ); + SetIdentityMatrix( m_Lights[0].m_LightToWorld ); + m_Lights[0].m_Desc.m_Type = MATERIAL_LIGHT_DIRECTIONAL; + m_Lights[0].m_Desc.m_Color.Init( 1.0f, 1.0f, 1.0f ); + m_Lights[0].m_Desc.m_Direction.Init( 0.0f, 0.0f, -1.0f ); + m_Lights[0].m_Desc.m_Range=0.0; + m_Lights[0].m_Desc.m_Attenuation0 = 1.0; + m_Lights[0].m_Desc.m_Attenuation1 = 0; + m_Lights[0].m_Desc.m_Attenuation2 = 0; + m_Lights[0].m_Desc.RecalculateDerivedValues(); + m_nLightCount = 1; + + m_pLightManip = new CPotteryWheelManip( &m_Lights[0].m_LightToWorld ); +} + + +void CPotteryWheelPanel::DestroyLights() +{ + if ( m_pLightManip ) + { + delete m_pLightManip; + m_pLightManip = NULL; + } + + m_nLightCount = 0; +} + + +void StringToFloatArray( float *pVector, int count, const char *pString ) +{ + char *pstr, *pfront, tempString[128]; + int j; + + Q_strncpy( tempString, pString, sizeof(tempString) ); + pstr = pfront = tempString; + + for ( j = 0; j < count; j++ ) // lifted from pr_edict.c + { + pVector[j] = atof( pfront ); + + // skip any leading whitespace + while ( *pstr && *pstr <= ' ' ) + pstr++; + + // skip to next whitespace + while ( *pstr && *pstr > ' ' ) + pstr++; + + if (!*pstr) + break; + + pstr++; + pfront = pstr; + } + for ( j++; j < count; j++ ) + { + pVector[j] = 0; + } +} + +void StringToVector( float *pVector, const char *pString ) +{ + StringToFloatArray( pVector, 3, pString ); +} + + +//----------------------------------------------------------------------------- +// Sets initialize lights from KeyValues +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::ParseLightsFromKV( KeyValues *pLightsKV ) +{ + int nLightCount = 0; + FOR_EACH_SUBKEY( pLightsKV, pLocalLight ) + { + Assert( nLightCount < MAX_LIGHT_COUNT ); + if ( nLightCount >= MAX_LIGHT_COUNT ) + break; + + LightDesc_t *pDesc = &m_Lights[nLightCount].m_Desc; + const char *pType = pLocalLight->GetString( "name" ); + Vector vecColor; + StringToVector( vecColor.Base(), pLocalLight->GetString( "color" ) ); + + if ( !Q_stricmp( pType, "directional" ) ) + { + Vector vecDirection; + StringToVector( vecDirection.Base(), pLocalLight->GetString( "direction" ) ); + pDesc->InitDirectional( vecDirection.Normalized(), vecColor ); + ++nLightCount; + continue; + } + + if ( !Q_stricmp( pType, "point" ) ) + { + Vector vecAtten; + StringToVector( vecAtten.Base(), pLocalLight->GetString( "attenuation" ) ); + Vector vecOrigin; + StringToVector( vecOrigin.Base(), pLocalLight->GetString( "origin" ) ); + pDesc->InitPoint( vecOrigin, vecColor ); + pDesc->m_Attenuation0 = vecAtten.x; + pDesc->m_Attenuation1 = vecAtten.y; + pDesc->m_Attenuation2 = vecAtten.z; + pDesc->m_Range = pLocalLight->GetFloat( "maxDistance" ); + pDesc->RecalculateDerivedValues(); + ++nLightCount; + continue; + } + + if ( !Q_stricmp( pType, "spot" ) ) + { + Vector vecAtten; + StringToVector( vecAtten.Base(), pLocalLight->GetString( "attenuation" ) ); + Vector vecOrigin; + StringToVector( vecOrigin.Base(), pLocalLight->GetString( "origin" ) ); + pDesc->InitSpot( vecOrigin, vecColor, vec3_origin, + pLocalLight->GetFloat( "inner_cone_angle" ), + pLocalLight->GetFloat( "outer_cone_angle" ) ); + + Vector vecDirection; + StringToVector( vecDirection.Base(), pLocalLight->GetString( "direction" ) ); + pDesc->m_Direction = vecDirection.Normalized(); + pDesc->m_Attenuation0 = vecAtten.x; + pDesc->m_Attenuation1 = vecAtten.y; + pDesc->m_Attenuation2 = vecAtten.z; + pDesc->m_Range = pLocalLight->GetFloat( "maxDistance" ); + pDesc->m_Falloff = pLocalLight->GetFloat( "exponent" ); + pDesc->RecalculateDerivedValues(); + ++nLightCount; + continue; + } + + AssertMsg1( 0, "Failed to initialize light with type '%s'", pType ); + } + + AssertMsg( nLightCount > 0, "Must specify at least one valid light" ); + + m_nLightCount = nLightCount; +} + + +//----------------------------------------------------------------------------- +// Sets the background color +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetBackgroundColor( int r, int g, int b ) +{ + m_ClearColor.SetColor( r, g, b, 255 ); +} + +void CPotteryWheelPanel::SetBackgroundColor( const Color& c ) +{ + m_ClearColor = c; +} + +const Color& CPotteryWheelPanel::GetBackgroundColor() const +{ + return m_ClearColor; +} + + +//----------------------------------------------------------------------------- +// Light probe +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetLightProbe( CDmxElement *pLightProbe ) +{ + m_LightProbeBackground.Shutdown(); + m_LightProbeHDRBackground.Shutdown(); + m_LightProbeCubemap.Shutdown(); + m_LightProbeHDRCubemap.Shutdown(); + + DestroyLights(); + + m_bHasLightProbe = ( pLightProbe != NULL ); + if ( !m_bHasLightProbe ) + { + CreateDefaultLights(); + return; + } + + const char *pCubemap = pLightProbe->GetValueString( "cubemap" ); + m_LightProbeCubemap.Init( pCubemap, TEXTURE_GROUP_OTHER ); + + const char *pCubemapHDR = pLightProbe->HasAttribute( "cubemapHdr" ) ? pLightProbe->GetValueString( "cubemapHdr" ) : pCubemap; + m_LightProbeHDRCubemap.Init( pCubemapHDR, TEXTURE_GROUP_OTHER ); + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetInt( "$ignorez", 1 ); + pVMTKeyValues->SetString( "$envmap", pCubemap ); + pVMTKeyValues->SetInt( "$no_fullbright", 1 ); + pVMTKeyValues->SetInt( "$nocull", 1 ); + m_LightProbeBackground.Init( "SPWP_LightProbeBackground", pVMTKeyValues ); + m_LightProbeBackground->Refresh(); + + pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetInt( "$ignorez", 1 ); + pVMTKeyValues->SetString( "$envmap", pCubemapHDR ); + pVMTKeyValues->SetInt( "$no_fullbright", 1 ); + pVMTKeyValues->SetInt( "$nocull", 1 ); + m_LightProbeHDRBackground.Init( "SPWP_LightProbeBackground_HDR", pVMTKeyValues ); + m_LightProbeHDRBackground->Refresh(); + + const CUtlVector< Vector >& ambientCube = pLightProbe->GetArray( "ambientCube" ); + if ( ambientCube.Count() == 6 ) + { + for ( int i = 0; i < 6; ++i ) + { + m_vecAmbientCube[i].Init( ambientCube[i].x, ambientCube[i].y, ambientCube[i].z, 0.0f ); + } + } + + const CUtlVector< CDmxElement* >& localLights = pLightProbe->GetArray< CDmxElement* >( "localLights" ); + int nLightCount = localLights.Count(); + for ( int i = 0; i < nLightCount; ++i ) + { + if ( m_nLightCount == MAX_LIGHT_COUNT ) + break; + + LightDesc_t *pDesc = &m_Lights[m_nLightCount].m_Desc; + CDmxElement *pLocalLight = localLights[ i ]; + const char *pType = pLocalLight->GetValueString( "name" ); + const Vector& vecColor = pLocalLight->GetValue( "color" ); + + if ( !Q_stricmp( pType, "directional" ) ) + { + pDesc->InitDirectional( pLocalLight->GetValue( "direction" ), vecColor ); + ++m_nLightCount; + continue; + } + + if ( !Q_stricmp( pType, "point" ) ) + { + const Vector& vecAtten = pLocalLight->GetValue( "attenuation" ); + pDesc->InitPoint( pLocalLight->GetValue( "origin" ), vecColor ); + pDesc->m_Attenuation0 = vecAtten.x; + pDesc->m_Attenuation1 = vecAtten.y; + pDesc->m_Attenuation2 = vecAtten.z; + pDesc->m_Range = pLocalLight->GetValue( "maxDistance" ); + pDesc->RecalculateDerivedValues(); + ++m_nLightCount; + continue; + } + + if ( !Q_stricmp( pType, "spot" ) ) + { + const Vector& vecAtten = pLocalLight->GetValue( "attenuation" ); + pDesc->InitSpot( pLocalLight->GetValue( "origin" ), vecColor, vec3_origin, + RAD2DEG ( pLocalLight->GetValue( "theta" ) ), + RAD2DEG ( pLocalLight->GetValue( "phi" ) ) ); + + pDesc->m_Direction = pLocalLight->GetValue( "direction" ); + pDesc->m_Attenuation0 = vecAtten.x; + pDesc->m_Attenuation1 = vecAtten.y; + pDesc->m_Attenuation2 = vecAtten.z; + pDesc->m_Range = pLocalLight->GetValue( "maxDistance" ); + pDesc->m_Falloff = pLocalLight->GetValue( "exponent" ); + pDesc->RecalculateDerivedValues(); + ++m_nLightCount; + continue; + } + } + + if ( nLightCount > 0 ) + { + m_pLightManip = new CPotteryWheelManip( &m_Lights[0].m_LightToWorld ); + } +} + +bool CPotteryWheelPanel::HasLightProbe() const +{ + return m_bHasLightProbe; +} + +ITexture *CPotteryWheelPanel::GetLightProbeCubemap( bool bHDR ) +{ + if ( !m_bHasLightProbe ) + return NULL; + + return bHDR ? m_LightProbeHDRCubemap : m_LightProbeCubemap; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int CPotteryWheelPanel::GetCameraFOV( void ) +{ + return m_Camera.m_flFOV; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetCameraFOV( float flFOV ) +{ + m_Camera.m_flFOV = flFOV; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetCameraOffset( const Vector &vecOffset ) +{ + m_vecCameraOffset = vecOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::GetCameraOffset( Vector &vecOffset ) +{ + vecOffset = m_vecCameraOffset; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetCameraPositionAndAngles( const Vector &vecPos, const QAngle &angDir, bool syncManipulators ) +{ + SetIdentityMatrix( m_CameraPivot ); + AngleMatrix( angDir, vecPos, m_CameraPivot ); + + UpdateCameraTransform(); + if ( syncManipulators ) + { + SyncManipulation(); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::GetCameraPositionAndAngles( Vector &vecPos, QAngle &angDir ) +{ + MatrixAngles( m_CameraPivot, angDir, vecPos ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::ResetCameraPivot( void ) +{ + SetIdentityMatrix( m_CameraPivot ); +} + +//----------------------------------------------------------------------------- +// Sets the camera to look at the the thing we're spinning around +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::LookAt( float flRadius ) +{ + // Compute the distance to the camera for the object based on its + // radius and fov. + + // since tan( fov/2 ) = f/d + // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera + // d/f = r'/d' where d' is distance of camera to sphere + // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 ) + float flFOVx = m_Camera.m_flFOV; + + // Compute fov/2 in radians + flFOVx *= M_PI / 360.0f; + + // Compute an effective fov based on the aspect ratio + // if the height is smaller than the width + int w, h; + GetSize( w, h ); + if ( h < w ) + { + flFOVx = atan( h * tan( flFOVx ) / w ); + } + + m_vecCameraOffset.x = -( flRadius / sin( flFOVx ) ); + UpdateCameraTransform(); +} + + +void CPotteryWheelPanel::LookAt( const Vector &vecCenter, float flRadius ) +{ + MatrixSetColumn( vecCenter, 3, m_CameraPivot ); + LookAt( flRadius ); +} + + +//----------------------------------------------------------------------------- +// Sets up render state in the material system for rendering +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::SetupRenderState( int nDisplayWidth, int nDisplayHeight ) +{ + CMatRenderContextPtr pRenderContext( g_pMaterialSystem ); + + VMatrix view, projection; + ComputeViewMatrix( &view, m_Camera ); + ComputeProjectionMatrix( &projection, m_Camera, nDisplayWidth, nDisplayHeight ); + + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->LoadIdentity( ); + + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadMatrix( view ); + + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->LoadMatrix( projection ); + + LightDesc_t *pDesc = (LightDesc_t*)stackalloc( m_nLightCount * sizeof(LightDesc_t) ); + for ( int i = 0; i < m_nLightCount; ++i ) + { + pDesc[i] = m_Lights[i].m_Desc; + VectorTransform( m_Lights[i].m_Desc.m_Position, m_Lights[i].m_LightToWorld, pDesc->m_Position ); + VectorRotate( m_Lights[i].m_Desc.m_Direction, m_Lights[i].m_LightToWorld, pDesc->m_Direction ); + VectorNormalize( pDesc->m_Direction ); + pRenderContext->SetLight( i, pDesc[i] ); + } + + LightDesc_t desc; + desc.m_Type = MATERIAL_LIGHT_DISABLE; + int nMaxLightCount = g_pMaterialSystemHardwareConfig->MaxNumLights(); + for ( int i = m_nLightCount; i < nMaxLightCount; ++i ) + { + pRenderContext->SetLight( i, desc ); + } + + pRenderContext->SetAmbientLightCube( m_vecAmbientCube ); + + // FIXME: Remove this! This should automatically happen in DrawModel + // in studiorender. + if ( !g_pStudioRender ) + return; + + VMatrix worldToCamera; + MatrixInverseTR( view, worldToCamera ); + Vector vecOrigin, vecRight, vecUp, vecForward; + MatrixGetColumn( worldToCamera, 0, &vecRight ); + MatrixGetColumn( worldToCamera, 1, &vecUp ); + MatrixGetColumn( worldToCamera, 2, &vecForward ); + MatrixGetColumn( worldToCamera, 3, &vecOrigin ); + g_pStudioRender->SetViewState( vecOrigin, vecRight, vecUp, vecForward ); + + g_pStudioRender->SetLocalLights( m_nLightCount, pDesc ); + g_pStudioRender->SetAmbientLightColors( m_vecAmbientCube ); +} + + +//----------------------------------------------------------------------------- +// Compute the camera world position +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::UpdateCameraTransform( ) +{ + // Set up the render state for the camera + light + matrix3x4_t offset, worldToCamera; + SetIdentityMatrix( offset ); + MatrixSetColumn( m_vecCameraOffset, 3, offset ); + ConcatTransforms( m_CameraPivot, offset, worldToCamera ); + MatrixAngles( worldToCamera, m_Camera.m_angles, m_Camera.m_origin ); +} + +void CPotteryWheelPanel::ComputeCameraTransform( matrix3x4_t *pWorldToCamera ) +{ + AngleMatrix( m_Camera.m_angles, m_Camera.m_origin, *pWorldToCamera ); +} + + +//----------------------------------------------------------------------------- +// Computes the position in the panel of a particular 3D point +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::ComputePanelPosition( const Vector &vecPosition, Vector2D *pPanelPos ) +{ + int w, h; + GetSize( w, h ); + + matrix3x4_t worldToCamera; + ComputeCameraTransform( &worldToCamera ); + MatrixAngles( worldToCamera, m_Camera.m_angles, m_Camera.m_origin ); + ComputeScreenSpacePosition( pPanelPos, vecPosition, m_Camera, w, h ); +} + + +//----------------------------------------------------------------------------- +// Utility method to draw a grid at the 'ground' +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::DrawGrid() +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->LoadIdentity( ); + + pRenderContext->Bind( m_Wireframe ); + + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + + int nGridDim = 10; + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_LINES, 2 * nGridDim + 2 ); + + float bounds = 100.0f; + float delta = 2 * bounds / nGridDim; + for ( int i = 0; i < nGridDim + 1; ++i ) + { + float xy = -bounds + delta * i; + + meshBuilder.Position3f( xy, -bounds, 0 ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.AdvanceVertex(); + meshBuilder.Position3f( xy, bounds, 0 ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( -bounds, xy, 0 ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.AdvanceVertex(); + meshBuilder.Position3f( bounds, xy, 0 ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.AdvanceVertex(); + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::Paint() +{ + int iWidth, iHeight; + GetSize( iWidth, iHeight ); + + int screenw, screenh; + vgui::surface()->GetScreenSize( screenw, screenh ); + + int windowposx = 0, windowposy = 0; + GetPos( windowposx, windowposy ); + + int startx = 0, starty = 0; + if( windowposx < 0 ) + { + startx = -windowposx; + iWidth -= startx; + } + if ( windowposy < 0 ) + { + starty = -windowposy; + iHeight -= starty; + } + + int w, h; + GetSize( w, h ); + + vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h, m_bRenderToTexture ); + + if ( m_pCurrentManip ) + { + m_pCurrentManip->SetViewportSize( iWidth, iHeight ); + } + + // Set up the render state for the camera + light + SetupRenderState( iWidth, iHeight ); + + CMatRenderContextPtr pRenderContext( vgui::MaterialSystem() ); + + if ( m_bUseParentBG && GetParent() ) + { + Color bgCol = GetParent()->GetBgColor(); + pRenderContext->ClearColor4ub( bgCol.r(), bgCol.g(), bgCol.b(), bgCol.a() ); + } + else + { + pRenderContext->ClearColor4ub( m_ClearColor.r(), m_ClearColor.g(), m_ClearColor.b(), m_ClearColor.a() ); + } + pRenderContext->ClearBuffers( m_bRenderToTexture, true ); + + pRenderContext->CullMode( MATERIAL_CULLMODE_CCW ); + pRenderContext->SetIntRenderingParameter( INT_RENDERPARM_WRITE_DEPTH_TO_DESTALPHA, false ); + + if ( HasLightProbe() ) + { + IMaterial *pMaterial = ( vgui::MaterialSystemHardwareConfig()->GetHDRType() == HDR_TYPE_NONE ) ? + m_LightProbeBackground : m_LightProbeHDRBackground; + + RenderBox( m_Camera.m_origin, vec3_angle, Vector( -100, -100, -100 ), Vector( 100, 100, 100 ), + Color( 255, 255, 255, 255 ), pMaterial, true ); + } + + OnPaint3D(); + + pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); + + vgui::MatSystemSurface()->End3DPaint( ); +} + + +//----------------------------------------------------------------------------- +// called when we're ticked... +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::OnTick() +{ + BaseClass::OnTick(); + if ( m_pCurrentManip ) + { + m_pCurrentManip->OnTick(); + UpdateCameraTransform(); + } +} + + +//----------------------------------------------------------------------------- +// input +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::OnKeyCodePressed(KeyCode code) +{ + if ( m_pCurrentManip ) + { + switch( code ) + { + case KEY_RSHIFT: + case KEY_LSHIFT: + // start translate mode + AcceptManipulation( false ); + EnterManipulationMode( CAMERA_TRANSLATE, false ); + break; + + case KEY_RCONTROL: + case KEY_LCONTROL: + // start light mode + AcceptManipulation( false ); + EnterManipulationMode( LIGHT_MODE, false ); + break; + } + } + + BaseClass::OnKeyCodePressed( code ); +} + + + +//----------------------------------------------------------------------------- +// Purpose: soaks up any remaining messages +//----------------------------------------------------------------------------- +void CPotteryWheelPanel::OnKeyCodeReleased(KeyCode code) +{ + if ( m_pCurrentManip ) + { + switch( code ) + { + case KEY_RSHIFT: + case KEY_LSHIFT: + case KEY_RCONTROL: + case KEY_LCONTROL: + { + // stop manipulation mode + AcceptManipulation( false ); + switch ( m_nCaptureMouseCode ) + { + default: + case MOUSE_LEFT: + EnterManipulationMode( CAMERA_ROTATE, false ); + break; + + case MOUSE_MIDDLE: + EnterManipulationMode( CAMERA_TRANSLATE, false ); + break; + + case MOUSE_RIGHT: + EnterManipulationMode( CAMERA_ZOOM, false ); + break; + } + } + break; + } + } + BaseClass::OnKeyCodeReleased( code ); +} + +void CPotteryWheelPanel::OnMousePressed( vgui::MouseCode code ) +{ + if ( m_pCurrentManip ) + return; + + RequestFocus(); + + if ( input()->IsKeyDown( KEY_RSHIFT ) || input()->IsKeyDown( KEY_LSHIFT ) ) + { + EnterManipulationMode( CAMERA_TRANSLATE, true, code ); + } + else if ( input()->IsKeyDown( KEY_RCONTROL ) || input()->IsKeyDown( KEY_LCONTROL ) ) + { + EnterManipulationMode( LIGHT_MODE, true, code ); + } + else + { + switch ( code ) + { + case MOUSE_LEFT: + EnterManipulationMode( CAMERA_ROTATE, true, code ); + break; + + case MOUSE_MIDDLE: + EnterManipulationMode( CAMERA_TRANSLATE, true, code ); + break; + + case MOUSE_RIGHT: + EnterManipulationMode( CAMERA_ZOOM, true, code ); + break; + } + } + + BaseClass::OnMousePressed( code ); +} + +void CPotteryWheelPanel::OnMouseReleased( vgui::MouseCode code ) +{ + int x, y; + input()->GetCursorPos( x, y ); + ScreenToLocal( x, y ); + + AcceptManipulation(); + + BaseClass::OnMouseReleased( code ); +} + +void CPotteryWheelPanel::OnCursorMoved( int x, int y ) +{ + if ( m_pCurrentManip ) + { + if ( WarpMouse( x, y ) ) + { + m_pCurrentManip->OnCursorMoved( x, y ); + } + } + + BaseClass::OnCursorMoved( x, y ); +} + +void CPotteryWheelPanel::OnMouseWheeled( int delta ) +{ + if ( m_pCurrentManip ) + { + m_pCurrentManip->OnMouseWheeled( delta ); + } + + BaseClass::OnMouseWheeled( delta ); +} + + +void CPotteryWheelPanel::EnterManipulationMode( ManipulationMode_t manipMode, bool bMouseCapture, vgui::MouseCode mouseCode /* = -1 */ ) +{ + switch ( manipMode ) + { + case CAMERA_ROTATE: + m_pCurrentManip = m_pCameraRotate; + break; + + case CAMERA_TRANSLATE: + m_pCurrentManip = m_pCameraTranslate; + break; + + case CAMERA_ZOOM: + m_pCurrentManip = m_pCameraZoom; + break; + + case LIGHT_MODE: + m_pCurrentManip = m_pLightManip; + break; + } + + if ( !m_pCurrentManip ) + return; + + m_pCurrentManip->OnBeginManipulation(); + + m_xoffset = m_yoffset = 0; + + // Warp the mouse to the center of the screen + int width, height; + GetSize( width, height ); + int x = width / 2; + int y = height / 2; + + if ( bMouseCapture ) + { + input()->GetCursorPos( m_nManipStartX, m_nManipStartY ); + EnableMouseCapture( true, mouseCode ); + + int xpos = x; + int ypos = y; + LocalToScreen( xpos, ypos ); + input()->SetCursorPos( xpos, ypos ); + } + + m_pCurrentManip->OnMousePressed( mouseCode, x, y ); +} + +void CPotteryWheelPanel::AcceptManipulation( bool bReleaseMouseCapture ) +{ + if ( m_pCurrentManip ) + { + m_pCurrentManip->OnAcceptManipulation(); + + if ( bReleaseMouseCapture ) + { + EnableMouseCapture( false ); + input()->SetCursorPos( m_nManipStartX, m_nManipStartY ); + } + + m_pCurrentManip = NULL; + } +} + +void CPotteryWheelPanel::CancelManipulation() +{ + if ( m_pCurrentManip ) + { + m_pCurrentManip->OnCancelManipulation(); + + EnableMouseCapture( false ); + input()->SetCursorPos( m_nManipStartX, m_nManipStartY ); + + m_pCurrentManip = NULL; + } + +} + +void CPotteryWheelPanel::ApplyManipulation() +{ + if ( dynamic_cast< CRotationManipulator * >( m_pCameraRotate ) ) + { + dynamic_cast< CRotationManipulator * >( m_pCameraRotate )->UpdateTransform(); + } + UpdateCameraTransform(); +} + +void CPotteryWheelPanel::SyncManipulation() +{ + if ( dynamic_cast< CRotationManipulator * >( m_pCameraRotate ) ) + { + dynamic_cast< CRotationManipulator * >( m_pCameraRotate )->UpdateFromMatrix(); + } +} + +void CPotteryWheelPanel::OnMouseCaptureLost() +{ + SetCursor( vgui::dc_arrow ); + m_nCaptureMouseCode = vgui::MouseCode( -1 ); +} + +void CPotteryWheelPanel::EnableMouseCapture( bool enable, vgui::MouseCode mouseCode /* = -1 */ ) +{ + if ( enable ) + { + m_nCaptureMouseCode = mouseCode; + SetCursor( vgui::dc_none ); + input()->SetMouseCaptureEx( GetVPanel(), m_nCaptureMouseCode ); + } + else + { + m_nCaptureMouseCode = vgui::MouseCode( -1 ); + input()->SetMouseCapture( (VPANEL)0 ); + SetCursor( vgui::dc_arrow ); + } +} + +bool CPotteryWheelPanel::WarpMouse( int &x, int &y ) +{ + // Re-force capture if it was lost... + if ( input()->GetMouseCapture() != GetVPanel() ) + { + input()->GetCursorPos( m_nManipStartX, m_nManipStartY ); + EnableMouseCapture( true, m_nCaptureMouseCode ); + } + + int width, height; + GetSize( width, height ); + + int centerx = width / 2; + int centery = height / 2; + + // skip this event + if ( x == centerx && y == centery ) + return false; + + int xpos = centerx; + int ypos = centery; + LocalToScreen( xpos, ypos ); + +#if defined( DX_TO_GL_ABSTRACTION ) + // + // Really reset the cursor to the center for the PotteryWheel Control + // + // In TF2's edit loadout dialog there is a character model that you can rotate + // around using the mouse. This control resets the cursor to the center of the window + // after each mouse move. Except the input()->SetCursorPos results (after a lot of redirection) to + // vgui/matsurface/Cursor.cpp function CursorSetPos but it has a (needed) test to not move the + // cursor if it's currently hidden. Rather than change all the levels between here and there + // to support a flag, we are just jumping to the chase and directly calling the inputsystem + // SetCursorPosition on OpenGL platforms + // + g_pInputSystem->SetCursorPosition( xpos, ypos ); +#else + input()->SetCursorPos( xpos, ypos ); +#endif + + int dx = x - centerx; + int dy = y - centery; + + x += m_xoffset; + y += m_yoffset; + + m_xoffset += dx; + m_yoffset += dy; + + return true; +} diff --git a/src/vgui2/matsys_controls/proceduraltexturepanel.cpp b/src/vgui2/matsys_controls/proceduraltexturepanel.cpp new file mode 100644 index 000000000..a198dd5f3 --- /dev/null +++ b/src/vgui2/matsys_controls/proceduraltexturepanel.cpp @@ -0,0 +1,239 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/proceduraltexturepanel.h" +#include "matsys_controls/matsyscontrols.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/itexture.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "tier1/KeyValues.h" +#include "pixelwriter.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CProceduralTexturePanel::CProceduralTexturePanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_pImageBuffer = NULL; + m_bMaintainProportions = false; + m_bUsePaintRect = false; + m_PaintRect.x = m_PaintRect.y = 0; + m_PaintRect.width = m_PaintRect.height = 0; +} + +CProceduralTexturePanel::~CProceduralTexturePanel() +{ + CleanUp(); +} + + +//----------------------------------------------------------------------------- +// initialization, shutdown +//----------------------------------------------------------------------------- +bool CProceduralTexturePanel::Init( int nWidth, int nHeight, bool bAllocateImageBuffer ) +{ + m_nWidth = nWidth; + m_nHeight = nHeight; + if ( bAllocateImageBuffer ) + { + m_pImageBuffer = new BGRA8888_t[nWidth * nHeight]; + } + + m_TextureSubRect.x = m_TextureSubRect.y = 0; + m_TextureSubRect.width = nWidth; + m_TextureSubRect.height = nHeight; + + char pTemp[512]; + Q_snprintf( pTemp, 512, "__%s", GetName() ); + + ITexture *pTex = MaterialSystem()->CreateProceduralTexture( pTemp, TEXTURE_GROUP_VGUI, + m_nWidth, m_nHeight, IMAGE_FORMAT_BGRX8888, + TEXTUREFLAGS_CLAMPS | TEXTUREFLAGS_CLAMPT | TEXTUREFLAGS_NOMIP | + TEXTUREFLAGS_NOLOD | TEXTUREFLAGS_PROCEDURAL | TEXTUREFLAGS_SINGLECOPY ); + pTex->SetTextureRegenerator( this ); + m_ProceduralTexture.Init( pTex ); + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + pVMTKeyValues->SetString( "$basetexture", pTemp ); + pVMTKeyValues->SetInt( "$nocull", 1 ); + pVMTKeyValues->SetInt( "$nodebug", 1 ); + m_ProceduralMaterial.Init( MaterialSystem()->CreateMaterial( pTemp, pVMTKeyValues )); + + m_nTextureID = MatSystemSurface()->CreateNewTextureID( false ); + MatSystemSurface()->DrawSetTextureMaterial( m_nTextureID, m_ProceduralMaterial ); + return true; +} + +void CProceduralTexturePanel::Shutdown() +{ + CleanUp(); +} + + +//----------------------------------------------------------------------------- +// Maintain proportions when drawing +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::MaintainProportions( bool bEnable ) +{ + m_bMaintainProportions = bEnable; +} + + +//----------------------------------------------------------------------------- +// Returns the image buffer + dimensions +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::CleanUp() +{ + if ( (ITexture*)m_ProceduralTexture ) + { + m_ProceduralTexture->SetTextureRegenerator( NULL ); + } + m_ProceduralTexture.Shutdown(); + m_ProceduralMaterial.Shutdown(); + + if ( m_pImageBuffer ) + { + delete[] m_pImageBuffer; + m_pImageBuffer = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Default implementation of regenerate texture bits +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::RegenerateTextureBits( ITexture *pTexture, IVTFTexture *pVTFTexture, Rect_t *pRect ) +{ + Assert( m_pImageBuffer ); + Assert( pVTFTexture->FrameCount() == 1 ); + Assert( pVTFTexture->FaceCount() == 1 ); + Assert( !pTexture->IsMipmapped() ); + + int nWidth, nHeight, nDepth; + pVTFTexture->ComputeMipLevelDimensions( 0, &nWidth, &nHeight, &nDepth ); + Assert( nDepth == 1 ); + Assert( nWidth == m_nWidth && nHeight == m_nHeight ); + + CPixelWriter pixelWriter; + pixelWriter.SetPixelMemory( pVTFTexture->Format(), + pVTFTexture->ImageData( 0, 0, 0 ), pVTFTexture->RowSizeInBytes( 0 ) ); + + for ( int y = 0; y < nHeight; ++y ) + { + pixelWriter.Seek( 0, y ); + BGRA8888_t *pTexel = &m_pImageBuffer[y * m_nWidth]; + for ( int x = 0; x < nWidth; ++x, ++pTexel ) + { + pixelWriter.WritePixel( pTexel->r, pTexel->g, pTexel->b, pTexel->a ); + } + } +} + + +//----------------------------------------------------------------------------- +// Returns the image buffer + dimensions +//----------------------------------------------------------------------------- +BGRA8888_t *CProceduralTexturePanel::GetImageBuffer() +{ + Assert( m_pImageBuffer ); + return m_pImageBuffer; +} + +int CProceduralTexturePanel::GetImageWidth() const +{ + return m_nWidth; +} + +int CProceduralTexturePanel::GetImageHeight() const +{ + return m_nHeight; +} + + +//----------------------------------------------------------------------------- +// Sets the paint rect +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::SetPaintRect( const Rect_t *pPaintRect ) +{ + m_bUsePaintRect = ( pPaintRect != NULL ); + if ( m_bUsePaintRect ) + { + m_PaintRect = *pPaintRect; + } +} + + +//----------------------------------------------------------------------------- +// Sets the draw rect +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::SetTextureSubRect( const Rect_t &subRect ) +{ + m_TextureSubRect = subRect; +} + + +//----------------------------------------------------------------------------- +// Redownloads the procedural texture +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::DownloadTexture() +{ + m_ProceduralTexture->Download(); +} + + +//----------------------------------------------------------------------------- +// Paints the texture +//----------------------------------------------------------------------------- +void CProceduralTexturePanel::Paint( void ) +{ + vgui::surface()->DrawSetTexture( m_nTextureID ); + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + + int x = 0; + int y = 0; + int w, h; + GetSize( w, h ); + if ( m_bUsePaintRect ) + { + x = m_PaintRect.x; + y = m_PaintRect.y; + w = m_PaintRect.width; + h = m_PaintRect.height; + } + + if ( m_bMaintainProportions ) + { + if ( m_TextureSubRect.width > m_TextureSubRect.height ) + { + h = w * m_TextureSubRect.height / m_TextureSubRect.width; + } + else + { + w = h * m_TextureSubRect.width / m_TextureSubRect.height; + } + } + + // Rotated version of the bitmap! + // Rotate about the center of the bitmap + vgui::Vertex_t verts[4]; + verts[0].m_Position.Init( x, y ); + verts[0].m_TexCoord.Init( (float)m_TextureSubRect.x / m_nWidth, (float)m_TextureSubRect.y / m_nHeight ); + + verts[1].m_Position.Init( w+x, y ); + verts[1].m_TexCoord.Init( (float)(m_TextureSubRect.x + m_TextureSubRect.width) / m_nWidth, (float)m_TextureSubRect.y / m_nHeight ); + + verts[2].m_Position.Init( w+x, h+y ); + verts[2].m_TexCoord.Init( (float)(m_TextureSubRect.x + m_TextureSubRect.width) / m_nWidth, (float)(m_TextureSubRect.y + m_TextureSubRect.height) / m_nHeight ); + + verts[3].m_Position.Init( x, h+y ); + verts[3].m_TexCoord.Init( (float)m_TextureSubRect.x / m_nWidth, (float)(m_TextureSubRect.y + m_TextureSubRect.height) / m_nHeight ); + + vgui::surface()->DrawTexturedPolygon( 4, verts ); +} diff --git a/src/vgui2/matsys_controls/sequencepicker.cpp b/src/vgui2/matsys_controls/sequencepicker.cpp new file mode 100644 index 000000000..7cc7b0348 --- /dev/null +++ b/src/vgui2/matsys_controls/sequencepicker.cpp @@ -0,0 +1,485 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "matsys_controls/sequencepicker.h" + +#include "tier1/utldict.h" +#include "tier1/KeyValues.h" +#include "studio.h" +#include "vgui/IInput.h" +#include "vgui/ISurface.h" +#include "vgui_controls/Splitter.h" +#include "vgui_controls/PropertyPage.h" +#include "vgui_controls/PropertySheet.h" +#include "vgui_controls/ListPanel.h" +#include "vgui_controls/Button.h" +#include "matsys_controls/matsyscontrols.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include "tier0/memdbgon.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Sequence Picker +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Sort by sequence name +//----------------------------------------------------------------------------- +static int __cdecl SequenceSortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("sequence"); + const char *string2 = item2.kv->GetString("sequence"); + return stricmp( string1, string2 ); +} + +static int __cdecl ActivitySortFunc( vgui::ListPanel *pPanel, const ListPanelItem &item1, const ListPanelItem &item2 ) +{ + const char *string1 = item1.kv->GetString("activity"); + const char *string2 = item2.kv->GetString("activity"); + return stricmp( string1, string2 ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CSequencePicker::CSequencePicker( vgui::Panel *pParent, int nFlags ) : BaseClass( pParent, "SequencePicker" ) +{ + m_hSelectedMDL = MDLHANDLE_INVALID; + + // property sheet - revisions, changes, etc. + m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + + vgui::Panel *pSplitterTopSide = m_pPreviewSplitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_pPreviewSplitter->GetChild( 1 ); + + // MDL preview + m_pMDLPreview = new CMDLPanel( pSplitterTopSide, "MDLPreview" ); + SetSkipChildDuringPainting( m_pMDLPreview ); + + m_pViewsSheet = new vgui::PropertySheet( pSplitterBottomSide, "ViewsSheet" ); + m_pViewsSheet->AddActionSignalTarget( this ); + + // sequences + m_pSequencesPage = NULL; + m_pSequencesList = NULL; + if ( nFlags & PICK_SEQUENCES ) + { + m_pSequencesPage = new PropertyPage( m_pViewsSheet, "SequencesPage" ); + m_pViewsSheet->AddPage( m_pSequencesPage, "Sequences" ); + m_pSequencesList = new ListPanel( m_pSequencesPage, "SequencesList" ); + m_pSequencesList->AddColumnHeader( 0, "sequence", "sequence", 52, 0 ); + m_pSequencesList->AddActionSignalTarget( this ); + m_pSequencesList->SetSelectIndividualCells( true ); + m_pSequencesList->SetEmptyListText(".MDL file contains no activities"); + m_pSequencesList->SetDragEnabled( true ); + m_pSequencesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + m_pSequencesList->SetSortFunc( 0, SequenceSortFunc ); + m_pSequencesList->SetSortColumn( 0 ); + } + + // Activities + m_pActivitiesPage = NULL; + m_pActivitiesList = NULL; + if ( nFlags & PICK_ACTIVITIES ) + { + m_pActivitiesPage = new PropertyPage( m_pViewsSheet, "ActivitiesPage" ); + m_pViewsSheet->AddPage( m_pActivitiesPage, "Activities" ); + m_pActivitiesList = new ListPanel( m_pActivitiesPage, "ActivitiesList" ); + m_pActivitiesList->AddColumnHeader( 0, "activity", "activity", 52, 0 ); + m_pActivitiesList->AddActionSignalTarget( this ); + m_pActivitiesList->SetSelectIndividualCells( true ); + m_pActivitiesList->SetEmptyListText( ".MDL file contains no activities" ); + m_pActivitiesList->SetDragEnabled( true ); + m_pActivitiesList->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); + m_pActivitiesList->SetSortFunc( 0, ActivitySortFunc ); + m_pActivitiesList->SetSortColumn( 0 ); + } + + // Load layout settings; has to happen before pinning occurs in code + LoadControlSettingsAndUserConfig( "resource/sequencepicker.res" ); + + SETUP_PANEL( this ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Destructor +//----------------------------------------------------------------------------- +CSequencePicker::~CSequencePicker() +{ +} + + +//----------------------------------------------------------------------------- +// Performs layout +//----------------------------------------------------------------------------- +void CSequencePicker::PerformLayout() +{ + // NOTE: This call should cause auto-resize to occur + // which should fix up the width of the panels + BaseClass::PerformLayout(); + + int w, h; + GetSize( w, h ); + + // Layout the mdl splitter + m_pPreviewSplitter->SetBounds( 0, 0, w, h ); +} + + +//----------------------------------------------------------------------------- +// Purpose: rebuilds the list of activities + sequences +//----------------------------------------------------------------------------- +void CSequencePicker::RefreshActivitiesAndSequencesList() +{ + if ( m_pActivitiesList ) + { + m_pActivitiesList->RemoveAll(); + } + + if ( m_pSequencesList ) + { + m_pSequencesList->RemoveAll(); + } + + m_pMDLPreview->SetSequence( 0 ); + + if ( m_hSelectedMDL == MDLHANDLE_INVALID ) + return; + + studiohdr_t *hdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + + CUtlDict activityNames( true, 0, hdr->GetNumSeq() ); + + for (int j = 0; j < hdr->GetNumSeq(); j++) + { + if ( /*g_viewerSettings.showHidden ||*/ !(hdr->pSeqdesc(j).flags & STUDIO_HIDDEN)) + { + const char *pActivityName = hdr->pSeqdesc(j).pszActivityName(); + if ( m_pActivitiesList && pActivityName && pActivityName[0] ) + { + // Multiple sequences can have the same activity name; only add unique activity names + if ( activityNames.Find( pActivityName ) == activityNames.InvalidIndex() ) + { + KeyValues *pkv = new KeyValues("node", "activity", pActivityName ); + int nItemID = m_pActivitiesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pActivityName ); + pDrag->SetString( "texttype", "activityName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) ); + m_pActivitiesList->SetItemDragData( nItemID, pDrag ); + + activityNames.Insert( pActivityName, j ); + } + } + + const char *pSequenceName = hdr->pSeqdesc(j).pszLabel(); + if ( m_pSequencesList && pSequenceName && pSequenceName[0] ) + { + KeyValues *pkv = new KeyValues("node", "sequence", pSequenceName); + int nItemID = m_pSequencesList->AddItem( pkv, 0, false, false ); + + KeyValues *pDrag = new KeyValues( "drag", "text", pSequenceName ); + pDrag->SetString( "texttype", "sequenceName" ); + pDrag->SetString( "mdl", vgui::MDLCache()->GetModelName( m_hSelectedMDL ) ); + m_pSequencesList->SetItemDragData( nItemID, pDrag ); + } + } + } + + if ( m_pSequencesList ) + { + m_pSequencesList->SortList(); + } + + if ( m_pActivitiesList ) + { + m_pActivitiesList->SortList(); + } +} + + +/* +//----------------------------------------------------------------------------- +// Purpose: Selects an sequence based on an activity +//----------------------------------------------------------------------------- +int SelectWeightedSequence( studiohdr_t *pstudiohdr, int activity, int curSequence ) +{ + if (! pstudiohdr) + return 0; + + VerifySequenceIndex( pstudiohdr ); + + int weighttotal = 0; + int seq = ACTIVITY_NOT_AVAILABLE; + int weight = 0; + for (int i = 0; i < pstudiohdr->GetNumSeq(); i++) + { + int curActivity = GetSequenceActivity( pstudiohdr, i, &weight ); + if (curActivity == activity) + { + if ( curSequence == i && weight < 0 ) + { + seq = i; + break; + } + weighttotal += iabs(weight); + + int randomValue; + if ( IsInPrediction() ) + randomValue = SharedRandomInt( "SelectWeightedSequence", 0, weighttotal - 1, i ); + else + randomValue = RandomInt( 0, weighttotal - 1 ); + + if (!weighttotal || randomValue < iabs(weight)) + seq = i; + } + } + + return seq; +} +*/ + + +//----------------------------------------------------------------------------- +// Gets the selected activity/sequence +//----------------------------------------------------------------------------- +CSequencePicker::PickType_t CSequencePicker::GetSelectedSequenceType( ) +{ + if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) ) + return PICK_SEQUENCES; + if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) ) + return PICK_ACTIVITIES; + return PICK_NONE; +} + +const char *CSequencePicker::GetSelectedSequenceName( ) +{ + if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) ) + { + int nIndex = m_pSequencesList->GetSelectedItem( 0 ); + if ( nIndex >= 0 ) + { + KeyValues *pkv = m_pSequencesList->GetItem( nIndex ); + return pkv->GetString( "sequence", NULL ); + } + return NULL; + } + + if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) ) + { + int nIndex = m_pActivitiesList->GetSelectedItem( 0 ); + if ( nIndex >= 0 ) + { + KeyValues *pkv = m_pActivitiesList->GetItem( nIndex ); + return pkv->GetString( "activity", NULL ); + } + return NULL; + } + + return NULL; +} + + +//----------------------------------------------------------------------------- +// Plays the selected activity +//----------------------------------------------------------------------------- +void CSequencePicker::PlayActivity( const char *pActivityName ) +{ + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + for ( int i = 0; i < pstudiohdr->GetNumSeq(); i++ ) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( stricmp( seqdesc.pszActivityName(), pActivityName ) == 0 ) + { + // FIXME: Add weighted sequence selection logic? + m_pMDLPreview->SetSequence( i ); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Plays the selected sequence +//----------------------------------------------------------------------------- +void CSequencePicker::PlaySequence( const char *pSequenceName ) +{ + studiohdr_t *pstudiohdr = vgui::MDLCache()->GetStudioHdr( m_hSelectedMDL ); + for (int i = 0; i < pstudiohdr->GetNumSeq(); i++) + { + mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( i ); + if ( !Q_stricmp( seqdesc.pszLabel(), pSequenceName ) ) + { + m_pMDLPreview->SetSequence( i ); + break; + } + } +} + + +//----------------------------------------------------------------------------- +// Purpose: Called when a page is shown +//----------------------------------------------------------------------------- +void CSequencePicker::OnPageChanged( ) +{ + if ( m_pSequencesPage && ( m_pViewsSheet->GetActivePage() == m_pSequencesPage ) ) + { + const char *pSequenceName = GetSelectedSequenceName(); + if ( pSequenceName ) + { + PlaySequence( pSequenceName ); + PostActionSignal( new KeyValues( "SequencePreviewChanged", "sequence", pSequenceName ) ); + } + return; + } + + if ( m_pActivitiesPage && ( m_pViewsSheet->GetActivePage() == m_pActivitiesPage ) ) + { + const char *pActivityName = GetSelectedSequenceName(); + if ( pActivityName ) + { + PlayActivity( pActivityName ); + PostActionSignal( new KeyValues( "SequencePreviewChanged", "activity", pActivityName ) ); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: refreshes dialog on text changing +//----------------------------------------------------------------------------- +void CSequencePicker::OnItemSelected( KeyValues *kv ) +{ + Panel *pPanel = (Panel *)kv->GetPtr("panel", NULL); + if ( m_pSequencesList && (pPanel == m_pSequencesList ) ) + { + const char *pSequenceName = GetSelectedSequenceName(); + if ( pSequenceName ) + { + PlaySequence( pSequenceName ); + PostActionSignal( new KeyValues( "SequencePreviewChanged", "sequence", pSequenceName ) ); + } + return; + } + + if ( m_pActivitiesList && ( pPanel == m_pActivitiesList ) ) + { + const char *pActivityName = GetSelectedSequenceName(); + if ( pActivityName ) + { + PlayActivity( pActivityName ); + PostActionSignal( new KeyValues( "SequencePreviewChanged", "activity", pActivityName ) ); + } + return; + } +} + + +//----------------------------------------------------------------------------- +// Sets the MDL to select sequences in +//----------------------------------------------------------------------------- +void CSequencePicker::SetMDL( const char *pMDLName ) +{ + m_hSelectedMDL = pMDLName ? vgui::MDLCache()->FindMDL( pMDLName ) : MDLHANDLE_INVALID; + if ( vgui::MDLCache()->IsErrorModel( m_hSelectedMDL ) ) + { + m_hSelectedMDL = MDLHANDLE_INVALID; + } + m_pMDLPreview->SetMDL( m_hSelectedMDL ); + m_pMDLPreview->LookAtMDL(); + RefreshActivitiesAndSequencesList(); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CSequencePickerFrame::CSequencePickerFrame( vgui::Panel *pParent, int nFlags ) : BaseClass( pParent, "SequencePickerFrame" ) +{ + SetDeleteSelfOnClose( true ); + m_pPicker = new CSequencePicker( this, nFlags ); + m_pPicker->AddActionSignalTarget( this ); + m_pOpenButton = new Button( this, "OpenButton", "#FileOpenDialog_Open", this, "Open" ); + m_pCancelButton = new Button( this, "CancelButton", "#FileOpenDialog_Cancel", this, "Cancel" ); + SetBlockDragChaining( true ); + + LoadControlSettingsAndUserConfig( "resource/sequencepickerframe.res" ); + + m_pOpenButton->SetEnabled( false ); +} + + +//----------------------------------------------------------------------------- +// Purpose: Activate the dialog +//----------------------------------------------------------------------------- +void CSequencePickerFrame::DoModal( const char *pMDLName ) +{ + m_pPicker->SetMDL( pMDLName ); + BaseClass::DoModal(); +} + + +//----------------------------------------------------------------------------- +// On mdl preview changed +//----------------------------------------------------------------------------- +void CSequencePickerFrame::OnSequencePreviewChanged( KeyValues *pKeyValues ) +{ + const char *pSequence = pKeyValues->GetString( "sequence", NULL ); + const char *pActivity = pKeyValues->GetString( "activity", NULL ); + m_pOpenButton->SetEnabled( pSequence || pActivity ); +} + + +//----------------------------------------------------------------------------- +// On command +//----------------------------------------------------------------------------- +void CSequencePickerFrame::OnCommand( const char *pCommand ) +{ + if ( !Q_stricmp( pCommand, "Open" ) ) + { + CSequencePicker::PickType_t type = m_pPicker->GetSelectedSequenceType( ); + if (( type == CSequencePicker::PICK_SEQUENCES ) || ( type == CSequencePicker::PICK_ACTIVITIES )) + { + const char *pSequenceName = m_pPicker->GetSelectedSequenceName(); + if ( pSequenceName ) + { + if ( type == CSequencePicker::PICK_SEQUENCES ) + { + PostActionSignal( new KeyValues("SequenceSelected", "sequence", pSequenceName ) ); + } + else + { + PostActionSignal( new KeyValues("SequenceSelected", "activity", pSequenceName ) ); + } + CloseModal(); + return; + } + } + return; + } + + if ( !Q_stricmp( pCommand, "Cancel" ) ) + { + CloseModal(); + return; + } + + BaseClass::OnCommand( pCommand ); +} + + diff --git a/src/vgui2/matsys_controls/tgapreviewpanel.cpp b/src/vgui2/matsys_controls/tgapreviewpanel.cpp new file mode 100644 index 000000000..c91b6f359 --- /dev/null +++ b/src/vgui2/matsys_controls/tgapreviewpanel.cpp @@ -0,0 +1,72 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/tgapreviewpanel.h" +#include "bitmap/tgaloader.h" +#include "tier1/utlbuffer.h" +#include "filesystem.h" + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// TGA Preview panel +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CTGAPreviewPanel::CTGAPreviewPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ +} + + +//----------------------------------------------------------------------------- +// Sets the current TGA +//----------------------------------------------------------------------------- +void CTGAPreviewPanel::SetTGA( const char *pFullPath ) +{ + int nWidth, nHeight; + ImageFormat format; + float flGamma; + + CUtlBuffer buf; + if ( !g_pFullFileSystem->ReadFile( pFullPath, NULL, buf ) ) + { + Warning( "Can't open TGA file: %s\n", pFullPath ); + return; + } + + TGALoader::GetInfo( buf, &nWidth, &nHeight, &format, &flGamma ); + + Shutdown(); + Init( nWidth, nHeight, true ); + m_TGAName = pFullPath; + + buf.SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + if ( !TGALoader::Load( (unsigned char*)GetImageBuffer(), buf, + nWidth, nHeight, IMAGE_FORMAT_BGRA8888, flGamma, false ) ) + { + Shutdown(); + } + else + { + DownloadTexture(); + } +} + + +//----------------------------------------------------------------------------- +// Gets the current TGA +//----------------------------------------------------------------------------- +const char *CTGAPreviewPanel::GetTGA() const +{ + return m_TGAName; +} diff --git a/src/vgui2/matsys_controls/vmtpanel.cpp b/src/vgui2/matsys_controls/vmtpanel.cpp new file mode 100644 index 000000000..2b37f1f56 --- /dev/null +++ b/src/vgui2/matsys_controls/vmtpanel.cpp @@ -0,0 +1,458 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//===========================================================================// + +#include "matsys_controls/vmtpanel.h" +#include "materialsystem/imesh.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/itexture.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "vgui_controls/ScrollBar.h" +#include "matsys_controls/matsyscontrols.h" +#include "vgui/IVGui.h" +#include "vgui_controls/ToolWindow.h" +#include "tier2/renderutils.h" + +using namespace vgui; + +//----------------------------------------------------------------------------- +// Enums +//----------------------------------------------------------------------------- +enum +{ + SCROLLBAR_SIZE=18, // the width of a scrollbar + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + +#define SPHERE_RADIUS 10.0f + + +//----------------------------------------------------------------------------- +// Constructor, destructor +//----------------------------------------------------------------------------- +CVMTPanel::CVMTPanel( vgui::Panel *pParent, const char *pName ) : BaseClass( pParent, pName ) +{ + m_bUseActualSize = true; + m_pMaterial = NULL; + + m_pHorizontalBar = new ScrollBar( this, "HorizScrollBar", false ); + m_pHorizontalBar->AddActionSignalTarget(this); + m_pHorizontalBar->SetVisible(false); + + m_pVerticalBar = new ScrollBar( this, "VertScrollBar", true ); + m_pVerticalBar->AddActionSignalTarget(this); + m_pVerticalBar->SetVisible(false); + + LookAt( SPHERE_RADIUS ); + + m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" ); + m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true ); +} + +CVMTPanel::~CVMTPanel() +{ + m_pLightmapTexture.Shutdown(); + m_DefaultEnvCubemap.Shutdown(); + if (m_pMaterial) + { + m_pMaterial->DecrementReferenceCount(); + } +} + + +//----------------------------------------------------------------------------- +// Scheme +//----------------------------------------------------------------------------- +void CVMTPanel::ApplySchemeSettings( vgui::IScheme *pScheme ) +{ + BaseClass::ApplySchemeSettings( pScheme ); + SetBorder( pScheme->GetBorder( "MenuBorder") ); +} + + +//----------------------------------------------------------------------------- +// Set the material to draw +//----------------------------------------------------------------------------- +void CVMTPanel::SetMaterial( IMaterial *pMaterial ) +{ + if (pMaterial) + { + pMaterial->IncrementReferenceCount(); + } + if (m_pMaterial) + { + m_pMaterial->DecrementReferenceCount(); + } + m_pMaterial = pMaterial; + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Set rendering mode (stretch to full screen, or use actual size) +//----------------------------------------------------------------------------- +void CVMTPanel::RenderUsingActualSize( bool bEnable ) +{ + m_bUseActualSize = bEnable; + InvalidateLayout(); +} + + +//----------------------------------------------------------------------------- +// Purpose: relayouts out the panel after any internal changes +//----------------------------------------------------------------------------- +void CVMTPanel::PerformLayout() +{ + BaseClass::PerformLayout(); + return; + + // Get the current size, see if it's big enough to view the entire thing + int iWidth, iHeight; + GetSize( iWidth, iHeight ); + + // In the case of stretching, just stretch to the size and blow off + // the scrollbars. Same holds true if there's no material + if (!m_bUseActualSize || !m_pMaterial) + { + m_iViewableWidth = iWidth; + m_iViewableHeight = iHeight; + m_pHorizontalBar->SetVisible(false); + m_pVerticalBar->SetVisible(false); + return; + } + + // Check the size of the material... + int iMaterialWidth = m_pMaterial->GetMappingWidth(); + int iMaterialHeight = m_pMaterial->GetMappingHeight(); + + // Check if the scroll bars are visible + bool bHorizScrollVisible = (iMaterialWidth > iWidth); + bool bVertScrollVisible = (iMaterialHeight > iHeight); + + m_pHorizontalBar->SetVisible(bHorizScrollVisible); + m_pVerticalBar->SetVisible(bVertScrollVisible); + + // Shrink the bars if both are visible + m_iViewableWidth = bVertScrollVisible ? iWidth - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iWidth; + m_iViewableHeight = bHorizScrollVisible ? iHeight - SCROLLBAR_SIZE - WINDOW_BORDER_WIDTH : iHeight; + + // Set the position of the horizontal bar... + if (bHorizScrollVisible) + { + m_pHorizontalBar->SetPos(0, iHeight - SCROLLBAR_SIZE); + m_pHorizontalBar->SetSize( m_iViewableWidth, SCROLLBAR_SIZE ); + + m_pHorizontalBar->SetRangeWindow( m_iViewableWidth ); + m_pHorizontalBar->SetRange( 0, iMaterialWidth ); + + // FIXME: Change scroll amount based on how much is not visible? + m_pHorizontalBar->SetButtonPressedScrollValue( 5 ); + } + + // Set the position of the vertical bar... + if (bVertScrollVisible) + { + m_pVerticalBar->SetPos(iWidth - SCROLLBAR_SIZE, 0); + m_pVerticalBar->SetSize(SCROLLBAR_SIZE, m_iViewableHeight); + + m_pVerticalBar->SetRangeWindow( m_iViewableHeight ); + m_pVerticalBar->SetRange( 0, iMaterialHeight); + m_pVerticalBar->SetButtonPressedScrollValue( 5 ); + } +} + + +//----------------------------------------------------------------------------- +// paint it stretched to the window size +//----------------------------------------------------------------------------- +void CVMTPanel::DrawStretchedToPanel( CMeshBuilder &meshBuilder ) +{ + // Draw a polygon the size of the panel + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( 0, 0, 0 ); + meshBuilder.TexCoord2f( 0, 0, 0 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( 0, m_iViewableHeight, 0 ); + meshBuilder.TexCoord2f( 0, 0, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( m_iViewableWidth, m_iViewableHeight, 0 ); + meshBuilder.TexCoord2f( 0, 1, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( m_iViewableWidth, 0, 0 ); + meshBuilder.TexCoord2f( 0, 0, 1 ); + meshBuilder.AdvanceVertex(); +} + + +//----------------------------------------------------------------------------- +// paint it actual size +//----------------------------------------------------------------------------- +void CVMTPanel::DrawActualSize( CMeshBuilder &meshBuilder ) +{ + // Check the size of the material... + int iMaterialWidth = m_pMaterial->GetMappingWidth(); + int iMaterialHeight = m_pMaterial->GetMappingHeight(); + + Vector2D ul; + Vector2D lr; + Vector2D tul; + Vector2D tlr; + + if (m_iViewableWidth >= iMaterialWidth) + { + // Center the material if we've got enough horizontal space + ul.x = (m_iViewableWidth - iMaterialWidth) * 0.5f; + lr.x = ul.x + iMaterialWidth; + tul.x = 0.0f; tlr.x = 1.0f; + } + else + { + // Use the scrollbars here... + int val = m_pHorizontalBar->GetValue(); + tul.x = (float)val / (float)iMaterialWidth; + tlr.x = tul.x + (float)m_iViewableWidth / (float)iMaterialWidth; + + ul.x = 0; + lr.x = m_iViewableWidth; + } + + if (m_iViewableHeight >= iMaterialHeight) + { + // Center the material if we've got enough vertical space + ul.y = (m_iViewableHeight - iMaterialHeight) * 0.5f; + lr.y = ul.y + iMaterialHeight; + tul.y = 0.0f; tlr.y = 1.0f; + } + else + { + // Use the scrollbars here... + int val = m_pVerticalBar->GetValue(); + + tul.y = (float)val / (float)iMaterialHeight; + tlr.y = tul.y + (float)m_iViewableHeight / (float)iMaterialHeight; + + ul.y = 0; + lr.y = m_iViewableHeight; + } + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( ul.x, ul.y, 0 ); + meshBuilder.TexCoord2f( 0, tul.x, tul.y ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( lr.x, ul.y, 0 ); + meshBuilder.TexCoord2f( 0, tlr.x, tul.y ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( lr.x, lr.y, 0 ); + meshBuilder.TexCoord2f( 0, tlr.x, tlr.y ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.Position3f( ul.x, lr.y, 0 ); + meshBuilder.TexCoord2f( 0, tul.x, tlr.y ); + meshBuilder.AdvanceVertex(); +} + + +//----------------------------------------------------------------------------- +// Draw it on a sphere +//----------------------------------------------------------------------------- +void CVMTPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ) +{ + int nVertices = nTheta * nPhi; + int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + pRenderContext->FogMode( MATERIAL_FOG_NONE ); + pRenderContext->SetNumBoneWeights( 0 ); + pRenderContext->Bind( m_pMaterial ); + pRenderContext->BindLightmapTexture( m_pLightmapTexture ); + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices ); + + bool bIsUsingLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ); + bool bIsUsingBumpedLightmap = m_pMaterial->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); + + int nLightmapWidth = m_pLightmapTexture->GetActualWidth(); + float flHalfLuxel = 0.5f / nLightmapWidth; + + // + // Build the index buffer. + // + int i, j; + for ( i = 0; i < nPhi; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + float u = j / ( float )(nTheta - 1); + float v = i / ( float )(nPhi - 1); + float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f; + float phi = M_PI * v; + + Vector vecPos; + vecPos.x = flRadius * sin(phi) * cos(theta); + vecPos.y = flRadius * sin(phi) * sin(theta); + vecPos.z = flRadius * cos(phi); + + Vector vecNormal = vecPos; + VectorNormalize( vecNormal ); + + Vector4D vecTangentS; + Vector vecTangentT; + vecTangentS.Init( -vecPos.y, vecPos.x, 0.0f, 1.0f ); + if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f ) + { + vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f ); + } + + CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT ); + + unsigned char red = (int)( u * 255.0f ); + unsigned char green = (int)( v * 255.0f ); + unsigned char blue = (int)( v * 255.0f ); + unsigned char alpha = (int)( v * 255.0f ); + + vecPos += vCenter; + + float u1, u2, v1, v2; + u1 = u2 = u; + v1 = v2 = v; + + if ( bIsUsingLightmap ) + { + u1 = RemapVal( u1, 0.0f, 1.0f, flHalfLuxel, 0.25 - flHalfLuxel ); + + if ( bIsUsingBumpedLightmap ) + { + u2 = 0.25f; + v2 = 0.0f; + } + } + + meshBuilder.Position3fv( vecPos.Base() ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( red, green, blue, alpha ); + meshBuilder.TexCoord2f( 0, u, v ); + meshBuilder.TexCoord2f( 1, u1, v1 ); + meshBuilder.TexCoord2f( 2, u2, v2 ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + } + } + + // + // Emit the triangle strips. + // + int idx = 0; + for ( i = 0; i < nPhi - 1; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + idx = nTheta * i + j; + + meshBuilder.FastIndex( idx ); + meshBuilder.FastIndex( idx + nTheta ); + } + + // + // Emit a degenerate triangle to skip to the next row without + // a connecting triangle. + // + if ( i < nPhi - 2 ) + { + meshBuilder.FastIndex( idx + 1 ); + meshBuilder.FastIndex( idx + 1 + nTheta ); + } + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Power of two FB texture +//----------------------------------------------------------------------------- +static CTextureReference s_pPowerOfTwoFrameBufferTexture; + +static ITexture *GetPowerOfTwoFrameBufferTexture( void ) +{ + if( !s_pPowerOfTwoFrameBufferTexture ) + { + s_pPowerOfTwoFrameBufferTexture.Init( materials->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) ); + } + + return s_pPowerOfTwoFrameBufferTexture; +} + + +//----------------------------------------------------------------------------- +// paint it! +//----------------------------------------------------------------------------- +void CVMTPanel::OnPaint3D() +{ + if (!m_pMaterial) + return; + + // Deal with refraction + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + if ( m_pMaterial->NeedsPowerOfTwoFrameBufferTexture() ) + { + ITexture *pTexture = GetPowerOfTwoFrameBufferTexture(); + if ( pTexture && !pTexture->IsError() ) + { + pRenderContext->CopyRenderTargetToTexture( pTexture ); + pRenderContext->SetFrameBufferCopyTexture( pTexture ); + } + } + + // Draw a background (translucent objects will appear that way) + + // FIXME: Draw the outline of this panel? + + pRenderContext->CullMode( MATERIAL_CULLMODE_CW ); + + RenderSphere( vec3_origin, SPHERE_RADIUS, 20, 20 ); + /* + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->LoadIdentity(); + pRenderContext->Ortho( 0, 0, m_iViewableWidth, m_iViewableHeight, 0, 1 ); + + pRenderContext->Bind( m_pMaterial ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + CMeshBuilder meshBuilder; + + meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); + + if (!m_bUseActualSize) + { + DrawStretchedToPanel( meshBuilder ); + } + else + { + DrawActualSize( meshBuilder ); + } + + meshBuilder.End(); + pMesh->Draw(); + */ +} + \ No newline at end of file diff --git a/src/vgui2/matsys_controls/vmtpicker.cpp b/src/vgui2/matsys_controls/vmtpicker.cpp new file mode 100644 index 000000000..f642067a9 --- /dev/null +++ b/src/vgui2/matsys_controls/vmtpicker.cpp @@ -0,0 +1,78 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "filesystem.h" +#include "matsys_controls/vmtpicker.h" +#include "matsys_controls/vmtpreviewpanel.h" +#include "vgui_controls/Splitter.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Asset Picker with no preview +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CVMTPicker::CVMTPicker( vgui::Panel *pParent, bool bAllowMultiselect ) : + BaseClass( pParent, "VMT Files", "vmt", "materials", "vmtName" ) +{ + // Horizontal splitter for preview + m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pPreviewSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pPreviewSplitter->GetChild( 1 ); + + m_p2D3DSplitter = new Splitter( pSplitterRightSide, "2D3DSplitter", SPLITTER_MODE_HORIZONTAL, 1 ); + vgui::Panel *pSplitterTopSide = m_p2D3DSplitter->GetChild( 0 ); + vgui::Panel *pSplitterBottomSide = m_p2D3DSplitter->GetChild( 1 ); + + // VMT preview + m_pVMTPreview2D = new CVMTPreviewPanel( pSplitterTopSide, "VMTPreview2D" ); + m_pVMTPreview3D = new CVMTPreviewPanel( pSplitterBottomSide, "VMTPreview3D" ); + m_pVMTPreview3D->DrawIn3DMode( true ); + + // Standard browser controls + CreateStandardControls( pSplitterLeftSide, bAllowMultiselect ); + + LoadControlSettingsAndUserConfig( "resource/vmtpicker.res" ); +} + +CVMTPicker::~CVMTPicker() +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes have this called when the previewed asset changes +//----------------------------------------------------------------------------- +void CVMTPicker::OnSelectedAssetPicked( const char *pAssetName ) +{ + m_pVMTPreview2D->SetVMT( pAssetName ); + m_pVMTPreview3D->SetVMT( pAssetName ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CVMTPickerFrame::CVMTPickerFrame( vgui::Panel *pParent, const char *pTitle, bool bAllowMultiselect ) : + BaseClass( pParent ) +{ + SetAssetPicker( new CVMTPicker( this, bAllowMultiselect ) ); + LoadControlSettingsAndUserConfig( "resource/vmtpickerframe.res" ); + SetTitle( pTitle, false ); +} + + + diff --git a/src/vgui2/matsys_controls/vmtpreviewpanel.cpp b/src/vgui2/matsys_controls/vmtpreviewpanel.cpp new file mode 100644 index 000000000..6616df249 --- /dev/null +++ b/src/vgui2/matsys_controls/vmtpreviewpanel.cpp @@ -0,0 +1,625 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/vmtpreviewpanel.h" +#include "matsys_controls/matsyscontrols.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "materialsystem/MaterialSystemUtil.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/imaterial.h" +#include "materialsystem/itexture.h" +#include "materialsystem/imesh.h" +#include "tier1/KeyValues.h" + + +using namespace vgui; + + +#define FOV 90.0f +#define ZNEAR 0.1f +#define ZFAR 2000.0f +#define ROTATION_SPEED 40.0f // degrees/sec +#define VIEW_DISTANCE 12.0f + +//----------------------------------------------------------------------------- +// +// VMT Preview panel +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CVMTPreviewPanel::CVMTPreviewPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + SetVMT( "//platform/materials/vgui/vtfnotloaded" ); + + m_pLightmapTexture.Init( "//platform/materials/debug/defaultlightmap", "editor" ); + m_DefaultEnvCubemap.Init( "editor/cubemap", "editor", true ); + m_LightDirection.Init( 0.0f, 1.0f, -1.0f ); + m_LightColor.SetColor( 255, 255, 255, 255 ); + m_flLightIntensity = 2.0f; + m_bDrawIn3DMode = false; + + // Reset the camera direction + m_vecCameraDirection.Init( 1.0f, 0.0f, 0.0f ); + m_flLastRotationTime = Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Sets the current VMT +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::SetVMT( const char *pMaterialName ) +{ + m_Material.Init( pMaterialName, "editor material" ); + m_VMTName = pMaterialName; +} + + +//----------------------------------------------------------------------------- +// Gets the current VMT +//----------------------------------------------------------------------------- +const char *CVMTPreviewPanel::GetVMT() const +{ + return m_VMTName; +} + + +//----------------------------------------------------------------------------- +// View it in 3D or 2D mode +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::DrawIn3DMode( bool b3DMode ) +{ + m_bDrawIn3DMode = b3DMode; +} + + +//----------------------------------------------------------------------------- +// Sets up lighting state +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::SetupLightingState() +{ + LightDesc_t desc; + memset( &desc, 0, sizeof(desc) ); + + desc.m_Type = MATERIAL_LIGHT_DIRECTIONAL; + + desc.m_Color[0] = m_LightColor.r(); + desc.m_Color[1] = m_LightColor.g(); + desc.m_Color[2] = m_LightColor.b(); + desc.m_Color *= m_flLightIntensity / 255.0f; + + desc.m_Attenuation0 = 1.0f; + desc.m_Attenuation1 = 0.0f; + desc.m_Attenuation2 = 0.0f; + desc.m_Flags = LIGHTTYPE_OPTIMIZATIONFLAGS_HAS_ATTENUATION0; + + desc.m_Direction = m_LightDirection; + VectorNormalize( desc.m_Direction ); + + desc.m_Theta = 0.0f; + desc.m_Phi = 0.0f; + desc.m_Falloff = 1.0f; + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + + pRenderContext->SetLight( 0, desc ); +} + + +//----------------------------------------------------------------------------- +// Draw a sphere +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ) +{ + int nVertices = nTheta * nPhi; + int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices ); + + bool bIsUsingLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ); + bool bIsUsingBumpedLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); + + int nLightmapWidth = m_pLightmapTexture->GetActualWidth(); + float flHalfLuxel = 0.5f / nLightmapWidth; + + // + // Build the index buffer. + // + int i, j; + for ( i = 0; i < nPhi; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + float u = j / ( float )(nTheta - 1); + float v = i / ( float )(nPhi - 1); + float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f; + float phi = M_PI * v; + + Vector vecPos; + vecPos.x = flRadius * sin(phi) * cos(theta); + vecPos.y = flRadius * sin(phi) * sin(theta); + vecPos.z = flRadius * cos(phi); + + Vector vecNormal = vecPos; + VectorNormalize( vecNormal ); + + Vector4D vecTangentS; + Vector vecTangentT; + vecTangentS.Init( vecPos.z, -vecPos.x, 0.0f, 1.0f ); + if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f ) + { + vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f ); + } + + CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT ); + + unsigned char red = (int)( u * 255.0f ); + unsigned char green = (int)( v * 255.0f ); + unsigned char blue = (int)( v * 255.0f ); + unsigned char alpha = (int)( v * 255.0f ); + + vecPos += vCenter; + + float u1, u2, v1, v2; + u1 = u2 = u; + v1 = v2 = v; + + if ( bIsUsingLightmap ) + { + u1 = RemapVal( u1, 0.0f, 1.0f, flHalfLuxel, 0.25 - flHalfLuxel ); + + if ( bIsUsingBumpedLightmap ) + { + u2 = 0.25f; + v2 = 0.0f; + } + } + + meshBuilder.Position3fv( vecPos.Base() ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( red, green, blue, alpha ); + meshBuilder.TexCoord2f( 0, 2.0f * u, v ); + meshBuilder.TexCoord2f( 1, u1, v1 ); + meshBuilder.TexCoord2f( 2, u2, v2 ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + } + } + + // + // Emit the triangle strips. + // + int idx = 0; + for ( i = 0; i < nPhi - 1; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + idx = nTheta * i + j; + + meshBuilder.FastIndex( idx ); + meshBuilder.FastIndex( idx + nTheta ); + } + + // + // Emit a degenerate triangle to skip to the next row without + // a connecting triangle. + // + if ( i < nPhi - 2 ) + { + meshBuilder.FastIndex( idx + 1 ); + meshBuilder.FastIndex( idx + 1 + nTheta ); + } + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Draw sprite-card based materials +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::RenderSpriteCard( const Vector &vCenter, float flRadius ) +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + IMesh *pMesh = pRenderContext->GetDynamicMesh(); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_QUADS, 1 ); + + // Draw a polygon the size of the panel + meshBuilder.Position3fv( vCenter.Base() ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f ); + meshBuilder.TexCoord2f( 3, 0, 0 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3fv( vCenter.Base() ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f ); + meshBuilder.TexCoord2f( 3, 0, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3fv( vCenter.Base() ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f ); + meshBuilder.TexCoord2f( 3, 1, 1 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3fv( vCenter.Base() ); + meshBuilder.Color4ub( 255, 255, 255, 255 ); + meshBuilder.TexCoord4f( 0, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 1, 0.0f, 0.0f, 1.0f, 1.0f ); + meshBuilder.TexCoord4f( 2, 0.0f, 0.0f, flRadius, 0.0f ); + meshBuilder.TexCoord2f( 3, 1, 0 ); + meshBuilder.AdvanceVertex(); + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Paints a regular texture +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::DrawRectangle( void ) +{ + // Get the aspect ratio of the material + int tw = m_Material->GetMappingWidth(); + int th = m_Material->GetMappingHeight(); + + if ( tw <= 0 || th <= 0 ) + return; + + int w, h; + GetSize( w, h ); + if ( w == 0 || h == 0 ) + return; + + SetupOrthoMatrix( w, h ); + SetupLightingState(); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadIdentity(); + + pRenderContext->MatrixMode( MATERIAL_MODEL ); + pRenderContext->LoadIdentity(); + + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 4, 4 ); + + bool bIsUsingLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_LIGHTMAP ); + bool bIsUsingBumpedLightmap = m_Material->GetPropertyFlag( MATERIAL_PROPERTY_NEEDS_BUMPED_LIGHTMAPS ); + + int nLightmapWidth = m_pLightmapTexture->GetActualWidth(); + float flHalfLuxel = 0.5f / nLightmapWidth; + Vector2D halfTexel( 0.5f / tw, 0.5f / th ); + + Vector vecNormal( 0.0f, 0.0f, 1.0f ); + Vector4D vecTangentS( 1.0f, 0.0f, 0.0f, 1.0f ); + Vector vecTangentT; + CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT ); + + float screenaspect = (float)tw / (float)th; + float aspect = (float)w / (float)h; + + float ratio = screenaspect / aspect; + + // Screen is wider, need bars at top and bottom + int x2, y2; + int x, y; + x = y = 0; + int nXBorder = w > 15 ? 5 : w / 3; + int nYBorder = h > 15 ? 5 : h / 3; + w -= 2 * nXBorder; + h -= 2 * nYBorder; + if ( ratio > 1.0f ) + { + int usetall = (float)w / screenaspect; + y = ( h - usetall ) / 2; + h = usetall; + } + // Screen is narrower, need bars at left/right + else + { + int usewide = (float)h * screenaspect; + x = ( w - usewide ) / 2; + w = usewide; + } + x += nXBorder; + y += nYBorder; + + x2 = x+w; y2 = y+h; + + float u = halfTexel.x; + float v = halfTexel.y; + + float u1_l, u1_r, v1_t, v1_b; + float u2_l, u2_r, v2_t, v2_b; + + u1_l = u2_l = u; + u1_r = u2_r = 1.0f - u; + v1_t = v2_t = v; + v1_b = v2_b = 1.0f - v; + + if ( bIsUsingLightmap ) + { + u1_l = v1_t = flHalfLuxel; + u1_r = v1_b = 0.25 - flHalfLuxel; + if ( bIsUsingBumpedLightmap ) + { + u2_l = u2_r = 0.25f; + v2_t = v2_b = 0.0f; + } + } + + meshBuilder.Position3f( x, y2, 0.0f ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( 255, 0, 0, 255 ); + meshBuilder.TexCoord2f( 0, u, v ); + meshBuilder.TexCoord2f( 1, u1_l, v1_t ); + meshBuilder.TexCoord2f( 2, u2_l, v2_t ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( x, y, 0.0f ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( 255, 255, 255, 64 ); + meshBuilder.TexCoord2f( 0, u, 1.0f - v ); + meshBuilder.TexCoord2f( 1, u1_l, v1_b ); + meshBuilder.TexCoord2f( 2, u2_l, v2_b ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( x2, y2, 0.0f ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( 0, 0, 255, 255 ); + meshBuilder.TexCoord2f( 0, 1.0f - u, v ); + meshBuilder.TexCoord2f( 1, u1_r, v1_t ); + meshBuilder.TexCoord2f( 2, u2_r, v2_t ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.Position3f( x2, y, 0.0f ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( 0, 255, 0, 64 ); + meshBuilder.TexCoord2f( 0, 1.0f - u, 1.0f - v ); + meshBuilder.TexCoord2f( 1, u1_r, v1_b ); + meshBuilder.TexCoord2f( 2, u2_r, v2_b ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + + meshBuilder.FastIndex( 0 ); + meshBuilder.FastIndex( 1 ); + meshBuilder.FastIndex( 2 ); + meshBuilder.FastIndex( 3 ); + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Paints a cubemap texture +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::DrawSphere( void ) +{ + float flNewTime = Plat_FloatTime(); + + // Circle the camera around the origin + VMatrix rot; + MatrixBuildRotateZ( rot, ROTATION_SPEED * (flNewTime - m_flLastRotationTime ) ); + Vector vecTemp; + Vector3DMultiply( rot, m_vecCameraDirection, vecTemp ); + m_vecCameraDirection = vecTemp; + m_flLastRotationTime = flNewTime; + + int w, h; + GetSize( w, h ); + SetupProjectionMatrix( w, h ); + SetupLightingState(); + + LookAt( vec3_origin, VIEW_DISTANCE ); + + // Draw a sphere at the origin + if ( !m_Material->IsSpriteCard() ) + { + RenderSphere( vec3_origin, 10.0f, 20, 20 ); + } + else + { + RenderSpriteCard( vec3_origin, 10.0f ); + } +} + + +//----------------------------------------------------------------------------- +// Sets the camera to look at the the thing we're spinning around +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::LookAt( const Vector &vecLookAt, float flRadius ) +{ + // Compute the distance to the camera for the object based on its + // radius and fov. + + // since tan( fov/2 ) = f/d + // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera + // d/f = r'/d' where d' is distance of camera to sphere + // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 ) + float flFOVx = FOV; + + // Compute fov/2 in radians + flFOVx *= M_PI / 360.0f; + + // Compute an effective fov based on the aspect ratio + // if the height is smaller than the width + int w, h; + GetSize( w, h ); + if ( h < w ) + { + flFOVx = atan( h * tan( flFOVx ) / w ); + } + + float flDistance = flRadius / sin( flFOVx ); + + Vector vecMDLOrigin = vecLookAt; + Vector vecCameraOrigin; + VectorMA( vecMDLOrigin, -flDistance, m_vecCameraDirection, vecCameraOrigin ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + QAngle angles; + VectorAngles( m_vecCameraDirection, angles ); + + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadIdentity(); + + // convert from a right handed system to a left handed system + // since dx for wants it that way. +// pRenderContext->Scale( 1.0f, 1.0f, -1.0f ); + + pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up + pRenderContext->Rotate( 90, 0, 0, 1 ); // put Z going up + pRenderContext->Rotate( -angles[2], 1, 0, 0 ); + pRenderContext->Rotate( -angles[0], 0, 1, 0 ); + pRenderContext->Rotate( -angles[1], 0, 0, 1 ); + pRenderContext->Translate( -vecCameraOrigin[0], -vecCameraOrigin[1], -vecCameraOrigin[2] ); +} + + +//----------------------------------------------------------------------------- +// Set up a projection matrix for a 90 degree fov +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::SetupProjectionMatrix( int nWidth, int nHeight ) +{ + VMatrix proj; + float flFOV = FOV; + float flZNear = ZNEAR; + float flZFar = ZFAR; + float flApsectRatio = (nHeight != 0.0f) ? (float)nWidth / (float)nHeight : 100.0f; + + float halfWidth = tan( flFOV * M_PI / 360.0 ); + float halfHeight = halfWidth / flApsectRatio; + + memset( proj.Base(), 0, sizeof( proj ) ); + proj[0][0] = 1.0f / halfWidth; + proj[1][1] = 1.0f / halfHeight; + proj[2][2] = flZFar / ( flZNear - flZFar ); + proj[3][2] = -1.0f; + proj[2][3] = flZNear * flZFar / ( flZNear - flZFar ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->LoadMatrix( proj ); +} + + +//----------------------------------------------------------------------------- +// Set up a orthographic projection matrix +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::SetupOrthoMatrix( int nWidth, int nHeight ) +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->LoadIdentity(); + pRenderContext->Ortho( 0, 0, nWidth, nHeight, -1.0f, 1.0f ); +} + + +//----------------------------------------------------------------------------- +// Power of two FB texture +//----------------------------------------------------------------------------- +static CTextureReference s_pPowerOfTwoFrameBufferTexture; + +static ITexture *GetPowerOfTwoFrameBufferTexture( void ) +{ + if( !s_pPowerOfTwoFrameBufferTexture ) + { + s_pPowerOfTwoFrameBufferTexture.Init( vgui::MaterialSystem()->FindTexture( "_rt_PowerOfTwoFB", TEXTURE_GROUP_RENDER_TARGET ) ); + } + + return s_pPowerOfTwoFrameBufferTexture; +} + + +//----------------------------------------------------------------------------- +// Paints the texture +//----------------------------------------------------------------------------- +void CVMTPreviewPanel::Paint( void ) +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + int w, h; + GetSize( w, h ); + vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h ); + + // Deal with refraction + if ( m_Material->NeedsPowerOfTwoFrameBufferTexture() ) + { + ITexture *pTexture = GetPowerOfTwoFrameBufferTexture(); + if ( pTexture && !pTexture->IsError() ) + { + pRenderContext->CopyRenderTargetToTexture( pTexture ); + pRenderContext->SetFrameBufferCopyTexture( pTexture ); + } + } + + pRenderContext->ClearColor4ub( 76, 88, 68, 255 ); + pRenderContext->ClearBuffers( true, true ); + + pRenderContext->FogMode( MATERIAL_FOG_NONE ); + pRenderContext->SetNumBoneWeights( 0 ); + pRenderContext->Bind( m_Material ); + pRenderContext->BindLightmapTexture( m_pLightmapTexture ); + pRenderContext->BindLocalCubemap( m_DefaultEnvCubemap ); + + if ( m_bDrawIn3DMode || m_Material->IsSpriteCard() ) + { + DrawSphere(); + } + else + { + DrawRectangle(); + } + + vgui::MatSystemSurface()->End3DPaint( ); +} \ No newline at end of file diff --git a/src/vgui2/matsys_controls/vtfpicker.cpp b/src/vgui2/matsys_controls/vtfpicker.cpp new file mode 100644 index 000000000..55973769f --- /dev/null +++ b/src/vgui2/matsys_controls/vtfpicker.cpp @@ -0,0 +1,71 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "filesystem.h" +#include "matsys_controls/vtfpicker.h" +#include "matsys_controls/vtfpreviewpanel.h" +#include "vgui_controls/Splitter.h" + + +using namespace vgui; + + +//----------------------------------------------------------------------------- +// +// Asset Picker with no preview +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// Purpose: Constructor +//----------------------------------------------------------------------------- +CVTFPicker::CVTFPicker( vgui::Panel *pParent ) : + BaseClass( pParent, "VTF Files", "vtf", "materials", "vtfName" ) +{ + // Horizontal splitter for preview + m_pPreviewSplitter = new Splitter( this, "PreviewSplitter", SPLITTER_MODE_VERTICAL, 1 ); + vgui::Panel *pSplitterLeftSide = m_pPreviewSplitter->GetChild( 0 ); + vgui::Panel *pSplitterRightSide = m_pPreviewSplitter->GetChild( 1 ); + + // VTF preview + m_pVTFPreview = new CVTFPreviewPanel( pSplitterRightSide, "VTFPreview" ); + + // Standard browser controls + CreateStandardControls( pSplitterLeftSide ); + + LoadControlSettingsAndUserConfig( "resource/vtfpicker.res" ); +} + +CVTFPicker::~CVTFPicker() +{ +} + + +//----------------------------------------------------------------------------- +// Derived classes have this called when the previewed asset changes +//----------------------------------------------------------------------------- +void CVTFPicker::OnSelectedAssetPicked( const char *pAssetName ) +{ + m_pVTFPreview->SetVTF( pAssetName ); +} + + +//----------------------------------------------------------------------------- +// +// Purpose: Modal picker frame +// +//----------------------------------------------------------------------------- +CVTFPickerFrame::CVTFPickerFrame( vgui::Panel *pParent, const char *pTitle ) : + BaseClass( pParent ) +{ + SetAssetPicker( new CVTFPicker( this ) ); + LoadControlSettingsAndUserConfig( "resource/vtfpickerframe.res" ); + SetTitle( pTitle, false ); +} + + + diff --git a/src/vgui2/matsys_controls/vtfpreviewpanel.cpp b/src/vgui2/matsys_controls/vtfpreviewpanel.cpp new file mode 100644 index 000000000..abd7afa88 --- /dev/null +++ b/src/vgui2/matsys_controls/vtfpreviewpanel.cpp @@ -0,0 +1,398 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//===========================================================================// + +#include "matsys_controls/vtfpreviewpanel.h" +#include "matsys_controls/matsyscontrols.h" +#include "VGuiMatSurface/IMatSystemSurface.h" +#include "materialsystem/MaterialSystemUtil.h" +#include "materialsystem/imaterialsystem.h" +#include "materialsystem/itexture.h" +#include "materialsystem/imesh.h" +#include "tier1/KeyValues.h" + + +using namespace vgui; + + +#define FOV 90.0f +#define ZNEAR 0.1f +#define ZFAR 2000.0f +#define ROTATION_SPEED 120.0f // degrees/sec + +//----------------------------------------------------------------------------- +// +// VTF Preview panel +// +//----------------------------------------------------------------------------- + + +//----------------------------------------------------------------------------- +// constructor +//----------------------------------------------------------------------------- +CVTFPreviewPanel::CVTFPreviewPanel( vgui::Panel *pParent, const char *pName ) : + BaseClass( pParent, pName ) +{ + SetVTF( "//platform/materials/vgui/vtfnotloaded", true ); + m_nTextureID = MatSystemSurface()->CreateNewTextureID( false ); +} + +CVTFPreviewPanel::~CVTFPreviewPanel() +{ + if ( vgui::surface() && m_nTextureID != -1 ) + { + vgui::surface()->DestroyTextureID( m_nTextureID ); + m_nTextureID = -1; + } +} + + +//----------------------------------------------------------------------------- +// Sets the current VTF +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::SetVTF( const char *pFullPath, bool bLoadImmediately ) +{ + m_PreviewTexture.Init( pFullPath, "editor texture" ); + m_VTFName = pFullPath; + + KeyValues *pVMTKeyValues = new KeyValues( "UnlitGeneric" ); + if ( m_PreviewTexture->IsCubeMap() ) + { + pVMTKeyValues->SetString( "$envmap", pFullPath ); + } + else if ( m_PreviewTexture->IsNormalMap() ) + { + pVMTKeyValues->SetString( "$bumpmap", pFullPath ); + } + else + { + pVMTKeyValues->SetString( "$basetexture", pFullPath ); + } + pVMTKeyValues->SetInt( "$nocull", 1 ); + pVMTKeyValues->SetInt( "$nodebug", 1 ); + m_PreviewMaterial.Init( MaterialSystem()->CreateMaterial( pFullPath, pVMTKeyValues )); + + MatSystemSurface()->DrawSetTextureMaterial( m_nTextureID, m_PreviewMaterial ); + + // Reset the camera direction + m_vecCameraDirection.Init( 1.0f, 0.0f, 0.0f ); + m_flLastRotationTime = Plat_FloatTime(); +} + + +//----------------------------------------------------------------------------- +// Gets the current VTF +//----------------------------------------------------------------------------- +const char *CVTFPreviewPanel::GetVTF() const +{ + return m_VTFName; +} + + +//----------------------------------------------------------------------------- +// Draw a sphere +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::RenderSphere( const Vector &vCenter, float flRadius, int nTheta, int nPhi ) +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + + int nVertices = nTheta * nPhi; + int nIndices = 2 * ( nTheta + 1 ) * ( nPhi - 1 ); + + pRenderContext->FogMode( MATERIAL_FOG_NONE ); + pRenderContext->SetNumBoneWeights( 0 ); + pRenderContext->Bind( m_PreviewMaterial ); + + IMesh* pMesh = pRenderContext->GetDynamicMesh(); + + CMeshBuilder meshBuilder; + meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, nVertices, nIndices ); + + // + // Build the index buffer. + // + int i, j; + for ( i = 0; i < nPhi; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + float u = j / ( float )(nTheta - 1); + float v = i / ( float )(nPhi - 1); + float theta = ( j != nTheta-1 ) ? 2.0f * M_PI * u : 0.0f; + float phi = M_PI * v; + + Vector vecPos; + vecPos.x = flRadius * sin(phi) * cos(theta); + vecPos.y = flRadius * cos(phi); + vecPos.z = -flRadius * sin(phi) * sin(theta); + + Vector vecNormal = vecPos; + VectorNormalize( vecNormal ); + + Vector4D vecTangentS; + Vector vecTangentT; + vecTangentS.Init( vecPos.z, -vecPos.x, 0.0f, 1.0f ); + if ( VectorNormalize( vecTangentS.AsVector3D() ) == 0.0f ) + { + vecTangentS.Init( 1.0f, 0.0f, 0.0f, 1.0f ); + } + + CrossProduct( vecNormal, vecTangentS.AsVector3D(), vecTangentT ); + + unsigned char red = (int)( u * 255.0f ); + unsigned char green = (int)( v * 255.0f ); + unsigned char blue = (int)( v * 255.0f ); + unsigned char alpha = (int)( v * 255.0f ); + + vecPos += vCenter; + + float u1, u2, v1, v2; + u1 = u2 = u; + v1 = v2 = v; + + meshBuilder.Position3fv( vecPos.Base() ); + meshBuilder.Normal3fv( vecNormal.Base() ); + meshBuilder.Color4ub( red, green, blue, alpha ); + meshBuilder.TexCoord2f( 0, u, v ); + meshBuilder.TexCoord2f( 1, u1, v1 ); + meshBuilder.TexCoord2f( 2, u2, v2 ); + meshBuilder.TangentS3fv( vecTangentS.Base() ); + meshBuilder.TangentT3fv( vecTangentT.Base() ); + meshBuilder.BoneWeight( 0, 1.0f ); + meshBuilder.BoneMatrix( 0, 0 ); + meshBuilder.UserData( vecTangentS.Base() ); + meshBuilder.AdvanceVertex(); + } + } + + // + // Emit the triangle strips. + // + int idx = 0; + for ( i = 0; i < nPhi - 1; ++i ) + { + for ( j = 0; j < nTheta; ++j ) + { + idx = nTheta * i + j; + + meshBuilder.FastIndex( idx ); + meshBuilder.FastIndex( idx + nTheta ); + } + + // + // Emit a degenerate triangle to skip to the next row without + // a connecting triangle. + // + if ( i < nPhi - 2 ) + { + meshBuilder.FastIndex( idx + 1 ); + meshBuilder.FastIndex( idx + 1 + nTheta ); + } + } + + meshBuilder.End(); + pMesh->Draw(); +} + + +//----------------------------------------------------------------------------- +// Paints a regular texture +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::PaintStandardTexture( void ) +{ + int x, y, w, h; + x = y = 0; + GetSize( w, h ); + vgui::surface()->DrawSetTexture( m_nTextureID ); + vgui::surface()->DrawSetColor( 255, 255, 255, 255 ); + + // Get the aspect ratio of the texture + int tw = m_PreviewTexture->GetActualWidth(); + int th = m_PreviewTexture->GetActualHeight(); + + if ( th > 0 && h > 0 ) + { + float screenaspect = (float)tw / (float)th; + float aspect = (float)w / (float)h; + + float ratio = screenaspect / aspect; + + // Screen is wider, need bars at top and bottom + if ( ratio > 1.0f ) + { + int usetall = (float)w / screenaspect; + y = ( h - usetall ) / 2; + h = usetall; + } + // Screen is narrower, need bars at left/right + else + { + int usewide = (float)h * screenaspect; + x = ( w - usewide ) / 2; + w = usewide; + } + } + + vgui::surface()->DrawTexturedRect( x, y, x+w, y+h ); +} + + +//----------------------------------------------------------------------------- +// Paints a normalmap texture +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::PaintNormalMapTexture( void ) +{ +} + + +//----------------------------------------------------------------------------- +// Paints a volume texture +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::PaintVolumeTexture( void ) +{ +} + + +//----------------------------------------------------------------------------- +// Paints a cubemap texture +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::PaintCubeTexture( void ) +{ + float flNewTime = Plat_FloatTime(); + + // Circle the camera around the origin + VMatrix rot; + MatrixBuildRotateZ( rot, ROTATION_SPEED * (flNewTime - m_flLastRotationTime ) ); + Vector vecTemp; + Vector3DMultiply( rot, m_vecCameraDirection, vecTemp ); + m_vecCameraDirection = vecTemp; + m_flLastRotationTime = flNewTime; + + LookAt( vec3_origin, 12.0f ); + + // Draw a sphere at the origin + RenderSphere( vec3_origin, 10.0f, 20, 20 ); +} + + +//----------------------------------------------------------------------------- +// Sets the camera to look at the the thing we're spinning around +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::LookAt( const Vector &vecLookAt, float flRadius ) +{ + // Compute the distance to the camera for the object based on its + // radius and fov. + + // since tan( fov/2 ) = f/d + // cos( fov/2 ) = r / r' where r = sphere radius, r' = perp distance from sphere center to max extent of camera + // d/f = r'/d' where d' is distance of camera to sphere + // d' = r' / tan( fov/2 ) * r' = r / ( cos (fov/2) * tan( fov/2 ) ) = r / sin( fov/2 ) + float flFOVx = FOV; + + // Compute fov/2 in radians + flFOVx *= M_PI / 360.0f; + + // Compute an effective fov based on the aspect ratio + // if the height is smaller than the width + int w, h; + GetSize( w, h ); + if ( h < w ) + { + flFOVx = atan( h * tan( flFOVx ) / w ); + } + + float flDistance = flRadius / sin( flFOVx ); + + Vector vecMDLOrigin = vecLookAt; + Vector vecCameraOrigin; + VectorMA( vecMDLOrigin, -flDistance, m_vecCameraDirection, vecCameraOrigin ); + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + QAngle angles; + VectorAngles( m_vecCameraDirection, angles ); + + pRenderContext->MatrixMode( MATERIAL_VIEW ); + pRenderContext->LoadIdentity(); + + // convert from a right handed system to a left handed system + // since dx for wants it that way. +// pRenderContext->Scale( 1.0f, 1.0f, -1.0f ); + + pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up + pRenderContext->Rotate( 90, 0, 0, 1 ); // put Z going up + pRenderContext->Rotate( -angles[2], 1, 0, 0 ); + pRenderContext->Rotate( -angles[0], 0, 1, 0 ); + pRenderContext->Rotate( -angles[1], 0, 0, 1 ); + pRenderContext->Translate( -vecCameraOrigin[0], -vecCameraOrigin[1], -vecCameraOrigin[2] ); +} + + +//----------------------------------------------------------------------------- +// Set up a projection matrix for a 90 degree fov +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::SetupProjectionMatrix( int nWidth, int nHeight ) +{ + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + VMatrix proj; + float flFOV = FOV; + float flZNear = ZNEAR; + float flZFar = ZFAR; + float flApsectRatio = (nHeight != 0.0f) ? (float)nWidth / (float)nHeight : 100.0f; + +#if 1 + float halfWidth = tan( flFOV * M_PI / 360.0 ); + float halfHeight = halfWidth / flApsectRatio; +#else + float halfHeight = tan( flFOV * M_PI / 360.0 ); + float halfWidth = flApsectRatio * halfHeight; +#endif + memset( proj.Base(), 0, sizeof( proj ) ); + proj[0][0] = 1.0f / halfWidth; + proj[1][1] = 1.0f / halfHeight; + proj[2][2] = flZFar / ( flZNear - flZFar ); + proj[3][2] = -1.0f; + proj[2][3] = flZNear * flZFar / ( flZNear - flZFar ); + + pRenderContext->MatrixMode( MATERIAL_PROJECTION ); + pRenderContext->LoadMatrix( proj ); +} + + +//----------------------------------------------------------------------------- +// Paints the texture +//----------------------------------------------------------------------------- +void CVTFPreviewPanel::Paint( void ) +{ + if ( !m_PreviewTexture->IsCubeMap() && /*!m_PreviewTexture->IsNormalMap() &&*/ !m_PreviewTexture->IsVolumeTexture() ) + { + PaintStandardTexture(); + return; + } + + CMatRenderContextPtr pRenderContext( MaterialSystem() ); + int w, h; + GetSize( w, h ); + vgui::MatSystemSurface()->Begin3DPaint( 0, 0, w, h ); + + pRenderContext->ClearColor4ub( 76, 88, 68, 255 ); + pRenderContext->ClearBuffers( true, true ); + + SetupProjectionMatrix( w, h ); + + if ( m_PreviewTexture->IsCubeMap() ) + { + PaintCubeTexture(); + } + else if ( m_PreviewTexture->IsNormalMap() ) + { + PaintNormalMapTexture(); + } + else if ( m_PreviewTexture->IsVolumeTexture() ) + { + PaintVolumeTexture(); + } + + vgui::MatSystemSurface()->End3DPaint( ); +} \ No newline at end of file diff --git a/src/vpc_scripts/groups.vgc b/src/vpc_scripts/groups.vgc index 0ff57f71b..de84d17bb 100644 --- a/src/vpc_scripts/groups.vgc +++ b/src/vpc_scripts/groups.vgc @@ -19,6 +19,7 @@ $Group "game" "client" "launcher_main" "mathlib" + "matsys_controls" "raytrace" "server" "tier1" @@ -40,6 +41,7 @@ $Group "everything" "height2normal" "launcher_main" "mathlib" + "matsys_controls" "motionmapper" "phonemeextractor" "qc_eyes" diff --git a/src/vpc_scripts/projects.vgc b/src/vpc_scripts/projects.vgc index b277b1a2b..2a62976c2 100644 --- a/src/vpc_scripts/projects.vgc +++ b/src/vpc_scripts/projects.vgc @@ -65,6 +65,11 @@ $Project "mathlib" "mathlib\mathlib.vpc" } +$Project "matsys_controls" +{ + "vgui2\matsys_controls\matsys_controls.vpc" +} + $Project "motionmapper" { "utils\motionmapper\motionmapper.vpc" [$WINDOWS] diff --git a/src/vpc_scripts/source_win32_base.vpc b/src/vpc_scripts/source_win32_base.vpc index 8fa5b704d..9bd65c394 100644 --- a/src/vpc_scripts/source_win32_base.vpc +++ b/src/vpc_scripts/source_win32_base.vpc @@ -58,6 +58,8 @@ $Configuration $DisableSpecificWarnings "$BASE;4577" [$AT_LEAST_VS2015] // 'noexcept' used with no exception handling mode specified; termination on exception is not guaranteed. Specify /EHsc $DisableSpecificWarnings "$BASE;4091" [$AT_LEAST_VS2015] // 'keyword' : ignored on left of 'type' when no variable is declared + $DisableSpecificWarnings "$BASE;4355" [$AT_LEAST_VS2015] + // Started appearning now since compiler updates... We have warnings as errors on Win32, so disable this there. $DisableSpecificWarnings "$BASE;4101" [$WIN32 && $AT_LEAST_VS2022] // 'Start': unreferenced local variable