Models on VGUI Panels

From Valve Developer Community
Revision as of 21:25, 11 March 2006 by Ging (talk | contribs)
Jump to navigation Jump to search

Basic code dump for model render in vgui panel :

Player model rendered in VGUI panel

This article shows how to render a player model to a custom VGUI image panel, the player model can be shown with a weapon and any animation.

It involves editing clientmode.cpp, teammenu.cpp and teammenu.h (or as appropriate for your mod). The basic premise is to generate a global list of the custom image panel, checking if any of the panel's is visible and then drawing the contents.

Possible expansions to this feature are to allow specification of the weapon model and animations required in the .res file entry.


sdk_clientmode.cpp

void UpdateClassImageEntity( const char *pModelName, int x, int y, int width, int height );
CHandle<C_BaseAnimatingOverlay> g_ClassImagePlayer;	// player
CHandle<C_BaseAnimating> g_ClassImageWeapon;	// weapon
// Utility to determine if the vgui panel is visible
bool WillPanelBeVisible( vgui::VPANEL hPanel )
{
    while ( hPanel )
    {
        if ( !vgui::ipanel()->IsVisible( hPanel ) )
            return false;
        hPanel = vgui::ipanel()->GetParent( hPanel );
    }
    return true;
}
// Called to see if we should be creating or recreating the model instances
bool ShouldRecreateClassImageEntity( C_BaseAnimating *pEnt, const char *pNewModelName )
{
    if ( !pNewModelName || !pNewModelName[0] )
        return false;
    if ( !pEnt )
        return true;
const model_t *pModel = pEnt->GetModel();
if ( !pModel ) return true; const char *pName = modelinfo->GetModelName( pModel );
if ( !pName ) return true; // reload only if names are different return( Q_stricmp( pName, pNewModelName ) != 0 ); }
void ClientModeSDKNormal::PostRenderVGui()
{
    // If the team menu is up, then we will render the model of the character that is currently selected.
    for ( int i=0; i < g_ClassImagePanels.Count(); i++ )
    {
        CClassImagePanel *pPanel = g_ClassImagePanels[i];
        if ( WillPanelBeVisible( pPanel->GetVPanel() ) )
        {
            // Ok, we have a visible class image panel.
            int x, y, w, h;
            pPanel->GetBounds( x, y, w, h );
            pPanel->LocalToScreen( x, y );
// Allow for the border. x += 3; y += 5; w -= 2; h -= 10;
UpdateClassImageEntity( g_ClassImagePanels[i]->m_ModelName, x, y, w, h ); return; } } }
void UpdateClassImageEntity( 
        const char *pModelName,
        int x, int y, int width, int height )
{
    C_BasePlayer *pLocalPlayer = C_BasePlayer::GetLocalPlayer();
if ( !pLocalPlayer ) return;

const char *pWeaponName = "models/weapons/f2000/w_f2000.mdl"; const char *pWeaponSequence = "Idle_Upper_Aug";
C_BaseAnimatingOverlay *pPlayerModel = g_ClassImagePlayer.Get();
// Does the entity even exist yet? bool recreatePlayer = ShouldRecreateClassImageEntity( pPlayerModel, pModelName ); if ( recreatePlayer ) { if ( pPlayerModel ) pPlayerModel->Remove();
pPlayerModel = new C_BaseAnimatingOverlay; pPlayerModel->InitializeAsClientEntity( pModelName, RENDER_GROUP_OPAQUE_ENTITY ); pPlayerModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally
// have the player stand idle pPlayerModel->SetSequence( pPlayerModel->LookupSequence( "Idle_lower" ) ); pPlayerModel->SetPoseParameter( 0, 0.0f ); // move_yaw pPlayerModel->SetPoseParameter( 1, 10.0f ); // body_pitch, look down a bit pPlayerModel->SetPoseParameter( 2, 0.0f ); // body_yaw pPlayerModel->SetPoseParameter( 3, 0.0f ); // move_y pPlayerModel->SetPoseParameter( 4, 0.0f ); // move_x, walk forward
g_ClassImagePlayer = pPlayerModel; }
C_BaseAnimating *pWeaponModel = g_ClassImageWeapon.Get();
// Does the entity even exist yet? if ( recreatePlayer || ShouldRecreateClassImageEntity( pWeaponModel, pWeaponName ) ) { if ( pWeaponModel ) pWeaponModel->Remove();
pWeaponModel = new C_BaseAnimating; pWeaponModel->InitializeAsClientEntity( pWeaponName, RENDER_GROUP_OPAQUE_ENTITY ); pWeaponModel->AddEffects( EF_NODRAW ); // don't let the renderer draw the model normally pWeaponModel->FollowEntity( pPlayerModel ); // attach to player model g_ClassImageWeapon = pWeaponModel; }
Vector origin = pLocalPlayer->EyePosition(); Vector lightOrigin = origin;
// find a spot inside the world for the dlight's origin, or it won't illuminate the model Vector testPos( origin.x - 100, origin.y, origin.z + 100 ); trace_t tr; UTIL_TraceLine( origin, testPos, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1.0f ) { lightOrigin = tr.endpos; } else { // Now move the model away so we get the correct illumination lightOrigin = tr.endpos + Vector( 1, 0, -1 ); // pull out from the solid Vector start = lightOrigin; Vector end = lightOrigin + Vector( 100, 0, -100 ); UTIL_TraceLine( start, end, MASK_OPAQUE, pLocalPlayer, COLLISION_GROUP_NONE, &tr ); origin = tr.endpos; }
float ambient = engine->GetLightForPoint( origin, true ).Length();
// Make a light so the model is well lit. // use a non-zero number so we cannibalize ourselves next frame dlight_t *dl = effects->CL_AllocDlight( LIGHT_INDEX_TE_DYNAMIC+1 );
dl->flags = DLIGHT_NO_WORLD_ILLUMINATION; dl->origin = lightOrigin; // Go away immediately so it doesn't light the world dl->die = gpGlobals->curtime + 0.1f; too.
dl->color.r = dl->color.g = dl->color.b = 250; if ( ambient < 1.0f ) { dl->color.exponent = 1 + (1 - ambient) * 2; } dl->radius = 400;
// move player model in front of our view pPlayerModel->SetAbsOrigin( origin ); pPlayerModel->SetAbsAngles( QAngle( 0, 210, 0 ) );
// set upper body animation pPlayerModel->m_SequenceTransitioner.Update( pPlayerModel->GetModelPtr(), pPlayerModel->LookupSequence( "walk_lower" ), pPlayerModel->GetCycle(), pPlayerModel->GetPlaybackRate(), gpGlobals->realtime, false, true );
// Now, blend the lower and upper (aim) anims together pPlayerModel->SetNumAnimOverlays( 2 ); int numOverlays = pPlayerModel->GetNumAnimOverlays();
for ( int i=0; i < numOverlays; ++i ) { C_AnimationLayer *layer = pPlayerModel->GetAnimOverlay( i );
layer->flCycle = pPlayerModel->GetCycle();
if ( i ) layer->nSequence = pPlayerModel->LookupSequence( pWeaponSequence ); else layer->nSequence = pPlayerModel->LookupSequence( "walk_lower" ); layer->flPlaybackrate = 1.0; layer->flWeight = 1.0f; layer->SetOrder( i ); }
pPlayerModel->FrameAdvance( gpGlobals->frametime );
// Now draw it. CViewSetup view; view.x = x; view.y = y; view.width = width; view.height = height;
view.m_bOrtho = false; view.fov = 54;
view.origin = origin + Vector( -110, -5, -5 );
Vector vMins, vMaxs; pPlayerModel->C_BaseAnimating::GetRenderBounds( vMins, vMaxs ); view.origin.z += ( vMins.z + vMaxs.z ) * 0.55f;
view.angles.Init(); view.m_vUnreflectedOrigin = view.origin; view.zNear = VIEW_NEARZ; view.zFar = 1000; view.m_bForceAspectRatio1To1 = false;
Frustum dummyFrustum; render->ViewSetup3D( &view, dummyFrustum );
pPlayerModel->DrawModel( STUDIO_RENDER );
if ( pWeaponModel ) { pWeaponModel->DrawModel( STUDIO_RENDER ); } }

teammenu.h

class CClassImagePanel : public vgui::ImagePanel
{
    public:
typedef vgui::ImagePanel BaseClass;
CClassImagePanel( vgui::Panel *pParent, const char *pName ); virtual ~CClassImagePanel(); virtual void ApplySettings( KeyValues *inResourceData ); virtual void Paint();

public: char m_ModelName[128]; };
extern CUtlVector<CClassImagePanel*> g_ClassImagePanels;

teammenu.cpp

CUtlVector<CClassImagePanel*> g_ClassImagePanels;
CClassImagePanel::CClassImagePanel( vgui::Panel *pParent, const char *pName )  : vgui::ImagePanel( pParent, pName ) { g_ClassImagePanels.AddToTail( this ); m_ModelName[0] = 0; }
CClassImagePanel::~CClassImagePanel() { g_ClassImagePanels.FindAndRemove( this ); }
void CClassImagePanel::ApplySettings( KeyValues *inResourceData ) { const char *pName = inResourceData->GetString( "3DModel" ); if ( pName ) { Q_strncpy( m_ModelName, pName, sizeof( m_ModelName ) ); }
BaseClass::ApplySettings( inResourceData ); }
void CClassImagePanel::Paint() { BaseClass::Paint(); }
Panel *TeamMenu::CreateControlByName(const char *controlName) { if ( Q_stricmp( controlName, "ClassImagePanel" ) == 0 ) { return new CClassImagePanel( NULL, controlName ); }
return BaseClass::CreateControlByName( controlName ); }

teammenu.res

"classimage"
{
        "ControlName"		"ClassImagePanel"
        "fieldName"		"classimage"
        "xpos"		"270"
        "ypos"		"170"
        "wide"		"512"
        "tall"		"384"
        "autoResize"	"0"
        "pinCorner"		"0"
        "visible"		"1"
        "enabled"		"1"
        "textAlignment"		"west"
        "3DModel"		"models/player/iris.mdl"
        "scaleImage"	"1"
        "zpos"		"1"
}