//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// // updates: // 1-4-99 fixed file texture load and file read bug //////////////////////////////////////////////////////////////////////// #include #include #include #include #include "StudioModel.h" #include "vphysics/constraints.h" #include "physmesh.h" #include "materialsystem/imaterialsystem.h" #include "materialsystem/imaterial.h" #include "ViewerSettings.h" #include "bone_setup.h" #include "UtlMemory.h" #include "mxtk/mx.h" #include "filesystem.h" #include "IStudioRender.h" #include "materialsystem/IMaterialSystemHardwareConfig.h" #include "MDLViewer.h" #include "optimize.h" extern char g_appTitle[]; Vector *StudioModel::m_AmbientLightColors; #pragma warning( disable : 4244 ) // double to float static StudioModel g_studioModel; static StudioModel *g_pActiveModel; // Expose it to the rest of the app StudioModel *g_pStudioModel = &g_studioModel; StudioModel *g_pStudioExtraModel[HLMV_MAX_MERGED_MODELS]; StudioModel::StudioModel() { m_MDLHandle = MDLHANDLE_INVALID; ClearLookTargets(); } void StudioModel::Init() { m_AmbientLightColors = new Vector[g_pStudioRender->GetNumAmbientLightSamples()]; // JasonM & garymcthack - should really only do this once a frame and at init time. UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false, g_viewerSettings.showNormals, g_viewerSettings.showTangentFrame ); } void StudioModel::Shutdown( void ) { g_pStudioModel->FreeModel( false ); delete [] m_AmbientLightColors; } void StudioModel::SetCurrentModel() { // track the correct model g_pActiveModel = this; } void StudioModel::ReleaseStudioModel() { SaveViewerSettings( g_pStudioModel->GetFileName(), g_pStudioModel ); g_pStudioModel->FreeModel( true ); } void StudioModel::RestoreStudioModel() { // should view settings be loaded before the model is loaded? if ( g_pStudioModel->LoadModel( g_pStudioModel->m_pModelName ) ) { g_pStudioModel->PostLoadModel( g_pStudioModel->m_pModelName ); } } //----------------------------------------------------------------------------- // Purpose: Frees the model data and releases textures from OpenGL. //----------------------------------------------------------------------------- void StudioModel::FreeModel( bool bReleasing ) { if ( m_pStudioHdr ) { delete m_pStudioHdr; m_pStudioHdr = NULL; } if ( m_MDLHandle != MDLHANDLE_INVALID ) { g_pMDLCache->Release( m_MDLHandle ); m_MDLHandle = MDLHANDLE_INVALID; } if ( !bReleasing ) { if (m_pModelName) { delete[] m_pModelName; m_pModelName = NULL; } } m_SurfaceProps.Purge(); DestroyPhysics( m_pPhysics ); m_pPhysics = NULL; } void *StudioModel::operator new( size_t stAllocateBlock ) { // call into engine to get memory Assert( stAllocateBlock != 0 ); return calloc( 1, stAllocateBlock ); } void StudioModel::operator delete( void *pMem ) { #ifdef _DEBUG // set the memory to a known value int size = _msize( pMem ); memset( pMem, 0xcd, size ); #endif // get the engine to free the memory free( pMem ); } void *StudioModel::operator new( size_t stAllocateBlock, int nBlockUse, const char *pFileName, int nLine ) { // call into engine to get memory Assert( stAllocateBlock != 0 ); return calloc( 1, stAllocateBlock ); } void StudioModel::operator delete( void *pMem, int nBlockUse, const char *pFileName, int nLine ) { #ifdef _DEBUG // set the memory to a known value int size = _msize( pMem ); memset( pMem, 0xcd, size ); #endif // get the engine to free the memory free( pMem ); } bool StudioModel::LoadModel( const char *pModelName ) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); if (!pModelName) return 0; // In the case of restore, m_pModelName == modelname if (m_pModelName != pModelName) { // Copy over the model name; we'll need it later... if (m_pModelName) { delete[] m_pModelName; } m_pModelName = new char[Q_strlen(pModelName) + 1]; strcpy( m_pModelName, pModelName ); } m_MDLHandle = g_pMDLCache->FindMDL( pModelName ); // allocate a pool for a studiohdr cache if (m_pStudioHdr != NULL) { delete m_pStudioHdr; } m_pStudioHdr = new CStudioHdr( g_pMDLCache->GetStudioHdr( m_MDLHandle ), g_pMDLCache ); // manadatory to access correct verts SetCurrentModel(); m_pPhysics = LoadPhysics( m_MDLHandle ); // Copy over all of the hitboxes; we may add and remove elements m_HitboxSets.RemoveAll(); CStudioHdr *pStudioHdr = GetStudioHdr(); int i; for ( int s = 0; s < pStudioHdr->numhitboxsets(); s++ ) { mstudiohitboxset_t *pSrcSet = pStudioHdr->pHitboxSet( s ); if ( !pSrcSet ) continue; int j = m_HitboxSets.AddToTail(); HitboxSet_t &set = m_HitboxSets[j]; set.m_Name = pSrcSet->pszName(); for ( i = 0; i < pSrcSet->numhitboxes; ++i ) { mstudiobbox_t *pHit = pSrcSet->pHitbox(i); int nIndex = set.m_Hitboxes.AddToTail( ); HitboxInfo_t &hitbox = set.m_Hitboxes[nIndex]; hitbox.m_Name = pHit->pszHitboxName(); hitbox.m_BBox = *pHit; // Blat out bbox name index so we don't use it by mistake... hitbox.m_BBox.szhitboxnameindex = 0; } } // Copy over all of the surface props; we may change them... for ( i = 0; i < pStudioHdr->numbones(); ++i ) { mstudiobone_t* pBone = pStudioHdr->pBone(i); CUtlSymbol prop( pBone->pszSurfaceProp() ); m_SurfaceProps.AddToTail( prop ); } m_physPreviewBone = -1; bool forceOpaque = (pStudioHdr->flags() & STUDIOHDR_FLAGS_FORCE_OPAQUE) != 0; bool vertexLit = false; m_bIsTransparent = false; m_bHasProxy = false; studiohwdata_t *pHardwareData = g_pMDLCache->GetHardwareData( m_MDLHandle ); if ( !pHardwareData ) { Assert( 0 ); return false; } for( int lodID = pHardwareData->m_RootLOD; lodID < pHardwareData->m_NumLODs; lodID++ ) { studioloddata_t *pLODData = &pHardwareData->m_pLODs[lodID]; for ( i = 0; i < pLODData->numMaterials; ++i ) { if (pLODData->ppMaterials[i]->IsVertexLit()) { vertexLit = true; } if ((!forceOpaque) && pLODData->ppMaterials[i]->IsTranslucent()) { m_bIsTransparent = true; //Msg("Translucent material %s for model %s\n", pLODData->ppMaterials[i]->GetName(), pStudioHdr->name ); } if (pLODData->ppMaterials[i]->HasProxy()) { m_bHasProxy = true; } } } return true; } bool StudioModel::PostLoadModel( const char *modelname ) { MDLCACHE_CRITICAL_SECTION_( g_pMDLCache ); CStudioHdr *pStudioHdr = GetStudioHdr(); if (pStudioHdr == NULL) return false; SetSequence (0); SetController (0, 0.0f); SetController (1, 0.0f); SetController (2, 0.0f); SetController (3, 0.0f); SetBlendTime( DEFAULT_BLEND_TIME ); // SetHeadTurn( 1.0f ); // FIXME:!!! int n; for (n = 0; n < pStudioHdr->numbodyparts(); n++) { SetBodygroup (n, 0); } SetSkin (0); /* Vector mins, maxs; ExtractBbox (mins, maxs); if (mins[2] < 5.0f) m_origin[2] = -mins[2]; */ return true; } //------------------------------------------------------------------------------ // Returns true if the model has at least one body part with model data, false if not. //------------------------------------------------------------------------------ bool StudioModel::HasModel() { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return false; for ( int i = 0; i < pStudioHdr->numbodyparts(); i++ ) { if ( pStudioHdr->pBodypart(i)->nummodels ) { return true; } } return false; } //////////////////////////////////////////////////////////////////////// int StudioModel::GetSequence( ) { return m_sequence; } int StudioModel::SetSequence( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0; if (iSequence < 0) return 0; if (iSequence > pStudioHdr->GetNumSeq()) return m_sequence; m_prevsequence = m_sequence; m_sequence = iSequence; m_cycle = 0; m_sequencetime = 0.0; return m_sequence; } const char* StudioModel::GetSequenceName( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return NULL; if (iSequence < 0) return NULL; if (iSequence > pStudioHdr->GetNumSeq()) return NULL; return pStudioHdr->pSeqdesc( iSequence ).pszLabel(); } void StudioModel::ClearOverlaysSequences( void ) { ClearAnimationLayers( ); memset( m_Layer, 0, sizeof( m_Layer ) ); } void StudioModel::ClearAnimationLayers( void ) { m_iActiveLayers = 0; } int StudioModel::GetNewAnimationLayer( int iPriority ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0; if ( m_iActiveLayers >= MAXSTUDIOANIMLAYERS ) { Assert( 0 ); return MAXSTUDIOANIMLAYERS - 1; } m_Layer[m_iActiveLayers].m_priority = iPriority; return m_iActiveLayers++; } int StudioModel::SetOverlaySequence( int iLayer, int iSequence, float flWeight ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0; if (iSequence < 0) return 0; if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) { Assert(0); return 0; } if (iSequence > pStudioHdr->GetNumSeq()) return m_Layer[iLayer].m_sequence; m_Layer[iLayer].m_sequence = iSequence; m_Layer[iLayer].m_weight = flWeight; m_Layer[iLayer].m_playbackrate = 1.0; return iSequence; } float StudioModel::SetOverlayRate( int iLayer, float flCycle, float flPlaybackRate ) { if (iLayer >= 0 && iLayer < MAXSTUDIOANIMLAYERS) { m_Layer[iLayer].m_cycle = flCycle; m_Layer[iLayer].m_playbackrate = flPlaybackRate; } return flCycle; } int StudioModel::GetOverlaySequence( int iLayer ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return -1; if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) { Assert(0); return 0; } return m_Layer[iLayer].m_sequence; } float StudioModel::GetOverlaySequenceWeight( int iLayer ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return -1; if (iLayer < 0 || iLayer >= MAXSTUDIOANIMLAYERS) { Assert(0); return 0; } return m_Layer[iLayer].m_weight; } int StudioModel::LookupSequence( const char *szSequence ) { int i; CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return -1; for (i = 0; i < pStudioHdr->GetNumSeq(); i++) { if (!stricmp( szSequence, pStudioHdr->pSeqdesc( i ).pszLabel() )) { return i; } } return -1; } int StudioModel::LookupActivity( const char *szActivity ) { int i; CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return -1; for (i = 0; i < pStudioHdr->GetNumSeq(); i++) { if (!stricmp( szActivity, pStudioHdr->pSeqdesc( i ).pszActivityName() )) { return i; } } return -1; } int StudioModel::SetSequence( const char *szSequence ) { return SetSequence( LookupSequence( szSequence ) ); } void StudioModel::StartBlending( void ) { // Switch back to old sequence ( this will oscillate between this one and the last one ) SetSequence( m_prevsequence ); } void StudioModel::SetBlendTime( float blendtime ) { if ( blendtime > 0.0f ) { m_blendtime = blendtime; } } float StudioModel::GetTransitionAmount( void ) { if ( g_viewerSettings.blendSequenceChanges && m_sequencetime < m_blendtime && m_prevsequence != m_sequence ) { float s; s = ( m_sequencetime / m_blendtime ); return s; } return 0.0f; } LocalFlexController_t StudioModel::LookupFlexController( char *szName ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return LocalFlexController_t(0); for (LocalFlexController_t iFlex = LocalFlexController_t(0); iFlex < pStudioHdr->numflexcontrollers(); iFlex++) { if (stricmp( szName, pStudioHdr->pFlexcontroller( iFlex )->pszName() ) == 0) { return iFlex; } } return LocalFlexController_t(-1); } void StudioModel::SetFlexController( char *szName, float flValue ) { SetFlexController( LookupFlexController( szName ), flValue ); } void StudioModel::SetFlexController( LocalFlexController_t iFlex, float flValue ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return; if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) { mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); if (pflex->min != pflex->max) { flValue = (flValue - pflex->min) / (pflex->max - pflex->min); } m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f ); } } void StudioModel::SetFlexControllerRaw( LocalFlexController_t iFlex, float flValue ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return; if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) { // mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); m_flexweight[iFlex] = clamp( flValue, 0.0f, 1.0f ); } } float StudioModel::GetFlexController( char *szName ) { return GetFlexController( LookupFlexController( szName ) ); } float StudioModel::GetFlexController( LocalFlexController_t iFlex ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0.0f; if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) { mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); float flValue = m_flexweight[iFlex]; if (pflex->min != pflex->max) { flValue = flValue * (pflex->max - pflex->min) + pflex->min; } return flValue; } return 0.0; } float StudioModel::GetFlexControllerRaw( LocalFlexController_t iFlex ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0.0f; if (iFlex >= 0 && iFlex < pStudioHdr->numflexcontrollers()) { // mstudioflexcontroller_t *pflex = pStudioHdr->pFlexcontroller(iFlex); return m_flexweight[iFlex]; } return 0.0; } int StudioModel::GetNumLODs() const { return g_pStudioRender->GetNumLODs( *GetHardwareData() ); } float StudioModel::GetLODSwitchValue( int lod ) const { return g_pStudioRender->GetLODSwitchValue( *GetHardwareData(), lod ); } void StudioModel::SetLODSwitchValue( int lod, float switchValue ) { g_pStudioRender->SetLODSwitchValue( *GetHardwareData(), lod, switchValue ); } void StudioModel::ExtractBbox( Vector &mins, Vector &maxs ) { studiohdr_t *pStudioHdr = GetStudioRenderHdr(); if ( !pStudioHdr ) return; // look for hull if ( ((Vector)pStudioHdr->hull_min).Length() != 0 ) { mins = pStudioHdr->hull_min; maxs = pStudioHdr->hull_max; } // look for view clip else if (((Vector)pStudioHdr->view_bbmin).Length() != 0) { mins = pStudioHdr->view_bbmin; maxs = pStudioHdr->view_bbmax; } else { mstudioseqdesc_t &pseqdesc = pStudioHdr->pSeqdesc( m_sequence ); mins = pseqdesc.bbmin; maxs = pseqdesc.bbmax; } } void StudioModel::GetSequenceInfo( int iSequence, float *pflFrameRate, float *pflGroundSpeed ) { float t = GetDuration( iSequence ); if (t > 0) { *pflFrameRate = 1.0 / t; } else { *pflFrameRate = 1.0; } *pflGroundSpeed = GetGroundSpeed( iSequence ); } void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed ) { GetSequenceInfo( m_sequence, pflFrameRate, pflGroundSpeed ); } float StudioModel::GetFPS( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0.0f; return Studio_FPS( pStudioHdr, iSequence, m_poseparameter ); } float StudioModel::GetFPS( void ) { return GetFPS( m_sequence ); } float StudioModel::GetDuration( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return 0.0f; return Studio_Duration( pStudioHdr, iSequence, m_poseparameter ); } int StudioModel::GetNumFrames( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr || iSequence < 0 || iSequence >= pStudioHdr->GetNumSeq() ) { return 1; } return Studio_MaxFrame( pStudioHdr, iSequence, m_poseparameter ); } static int GetSequenceFlags( CStudioHdr *pstudiohdr, int sequence ) { if ( !pstudiohdr || sequence < 0 || sequence >= pstudiohdr->GetNumSeq() ) { return 0; } mstudioseqdesc_t &seqdesc = pstudiohdr->pSeqdesc( sequence ); return seqdesc.flags; } //----------------------------------------------------------------------------- // Purpose: // Input : iSequence - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool StudioModel::GetSequenceLoops( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return false; int flags = GetSequenceFlags( pStudioHdr, iSequence ); bool looping = flags & STUDIO_LOOPING ? true : false; return looping; } float StudioModel::GetDuration( ) { return GetDuration( m_sequence ); } void StudioModel::GetMovement( float prevcycle[5], Vector &vecPos, QAngle &vecAngles ) { vecPos.Init(); vecAngles.Init(); CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return; // assume that changes < -0.5 are loops.... if (m_cycle - prevcycle[0] < -0.5) { prevcycle[0] = prevcycle[0] - 1.0; } Studio_SeqMovement( pStudioHdr, m_sequence, prevcycle[0], m_cycle, m_poseparameter, vecPos, vecAngles ); prevcycle[0] = m_cycle; int i; for (i = 0; i < 4; i++) { Vector vecTmp; QAngle angTmp; if (m_Layer[i].m_cycle - prevcycle[i+1] < -0.5) { prevcycle[i+1] = prevcycle[i+1] - 1.0; } if (m_Layer[i].m_weight > 0.0) { vecTmp.Init(); angTmp.Init(); if (Studio_SeqMovement( pStudioHdr, m_Layer[i].m_sequence, prevcycle[i+1], m_Layer[i].m_cycle, m_poseparameter, vecTmp, angTmp )) { vecPos = vecPos * ( 1.0 - m_Layer[i].m_weight ) + vecTmp * m_Layer[i].m_weight; } } prevcycle[i+1] = m_Layer[i].m_cycle; } return; } void StudioModel::GetMovement( int iSequence, float prevCycle, float nextCycle, Vector &vecPos, QAngle &vecAngles ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) { vecPos.Init(); vecAngles.Init(); return; } // FIXME: this doesn't consider layers Studio_SeqMovement( pStudioHdr, iSequence, prevCycle, nextCycle, m_poseparameter, vecPos, vecAngles ); return; } //----------------------------------------------------------------------------- // Purpose: Returns the ground speed of the specifed sequence. //----------------------------------------------------------------------------- float StudioModel::GetGroundSpeed( int iSequence ) { Vector vecMove; QAngle vecAngles; GetMovement( iSequence, 0, 1, vecMove, vecAngles ); float t = GetDuration( iSequence ); float flGroundSpeed = 0; if (t > 0) { flGroundSpeed = vecMove.Length() / t; } return flGroundSpeed; } //----------------------------------------------------------------------------- // Purpose: Returns the ground speed of the current sequence. //----------------------------------------------------------------------------- float StudioModel::GetGroundSpeed( void ) { return GetGroundSpeed( m_sequence ); } //----------------------------------------------------------------------------- // Purpose: Returns the ground speed of the current sequence. //----------------------------------------------------------------------------- float StudioModel::GetCurrentVelocity( void ) { Vector vecVelocity; CStudioHdr *pStudioHdr = GetStudioHdr(); if (pStudioHdr && Studio_SeqVelocity( pStudioHdr, m_sequence, m_cycle, m_poseparameter, vecVelocity )) { return vecVelocity.Length(); } return 0; } //----------------------------------------------------------------------------- // Purpose: Returns the the sequence should be hidden or not //----------------------------------------------------------------------------- bool StudioModel::IsHidden( int iSequence ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (pStudioHdr->pSeqdesc( iSequence ).flags & STUDIO_HIDDEN) return true; return false; } void StudioModel::GetSeqAnims( int iSequence, mstudioanimdesc_t *panim[4], float *weight ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return; mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( iSequence ); Studio_SeqAnims( pStudioHdr, seqdesc, iSequence, m_poseparameter, panim, weight ); } void StudioModel::GetSeqAnims( mstudioanimdesc_t *panim[4], float *weight ) { GetSeqAnims( m_sequence, panim, weight ); } float StudioModel::SetController( int iController, float flValue ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return 0.0f; return Studio_SetController( pStudioHdr, iController, flValue, m_controller[iController] ); } int StudioModel::LookupPoseParameter( char const *szName ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return false; for (int iParameter = 0; iParameter < pStudioHdr->GetNumPoseParameters(); iParameter++) { if (stricmp( szName, pStudioHdr->pPoseParameter( iParameter ).pszName() ) == 0) { return iParameter; } } return -1; } float StudioModel::SetPoseParameter( char const *szName, float flValue ) { return SetPoseParameter( LookupPoseParameter( szName ), flValue ); } float StudioModel::SetPoseParameter( int iParameter, float flValue ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return 0.0f; return Studio_SetPoseParameter( pStudioHdr, iParameter, flValue, m_poseparameter[iParameter] ); } float StudioModel::GetPoseParameter( char const *szName ) { return GetPoseParameter( LookupPoseParameter( szName ) ); } float* StudioModel::GetPoseParameters() { return m_poseparameter; } float StudioModel::GetPoseParameter( int iParameter ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return 0.0f; return Studio_GetPoseParameter( pStudioHdr, iParameter, m_poseparameter[iParameter] ); } bool StudioModel::GetPoseParameterRange( int iParameter, float *pflMin, float *pflMax ) { *pflMin = 0; *pflMax = 0; CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return false; if (iParameter < 0 || iParameter >= pStudioHdr->GetNumPoseParameters()) return false; const mstudioposeparamdesc_t &Pose = pStudioHdr->pPoseParameter( iParameter ); *pflMin = Pose.start; *pflMax = Pose.end; return true; } int StudioModel::LookupAttachment( char const *szName ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if ( !pStudioHdr ) return -1; for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++) { if (stricmp( pStudioHdr->pAttachment( i ).pszName(), szName ) == 0) { return i; } } return -1; } int StudioModel::SetBodygroup( int iGroup, int iValue /*= -1*/ ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return 0; if (iGroup > pStudioHdr->numbodyparts()) return -1; mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( iGroup ); int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels; // if the submodel index is not specified or out of range, just use the current value if ( iValue < 0 || iValue >= pbodypart->nummodels ) return iCurrent; m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base)); return iValue; } int StudioModel::SetSkin( int iValue ) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return 0; if (iValue >= pStudioHdr->numskinfamilies()) { return m_skinnum; } m_skinnum = iValue; return iValue; } void StudioModel::scaleMeshes (float scale) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return; int i, j, k; // manadatory to access correct verts SetCurrentModel(); // scale verts int tmp = m_bodynum; for (i = 0; i < pStudioHdr->numbodyparts(); i++) { mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( i ); for (j = 0; j < pbodypart->nummodels; j++) { SetBodygroup (i, j); SetupModel (i); const mstudio_modelvertexdata_t *vertData = m_pmodel->GetVertexData(); Assert( vertData ); // This can only return NULL on X360 for now for (k = 0; k < m_pmodel->numvertices; k++) { *vertData->Position(k) *= scale; } } } m_bodynum = tmp; // scale complex hitboxes int hitboxset = g_MDLViewer->GetCurrentHitboxSet(); mstudiobbox_t *pbboxes = pStudioHdr->pHitbox( 0, hitboxset ); for (i = 0; i < pStudioHdr->iHitboxCount( hitboxset ); i++) { VectorScale (pbboxes[i].bbmin, scale, pbboxes[i].bbmin); VectorScale (pbboxes[i].bbmax, scale, pbboxes[i].bbmax); } // scale bounding boxes for (i = 0; i < pStudioHdr->GetNumSeq(); i++) { mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( i ); Vector tmp; tmp = seqdesc.bbmin; VectorScale( tmp, scale, tmp ); seqdesc.bbmin = tmp; tmp = seqdesc.bbmax; VectorScale( tmp, scale, tmp ); seqdesc.bbmax = tmp; } // maybe scale exeposition, pivots, attachments } void StudioModel::scaleBones (float scale) { CStudioHdr *pStudioHdr = GetStudioHdr(); if (!pStudioHdr) return; mstudiobone_t *pbones = pStudioHdr->pBone( 0 ); for (int i = 0; i < pStudioHdr->numbones(); i++) { pbones[i].pos *= scale; pbones[i].posscale *= scale; } } int StudioModel::Physics_GetBoneCount( void ) { return m_pPhysics->Count(); } const char *StudioModel::Physics_GetBoneName( int index ) { CPhysmesh *pmesh = m_pPhysics->GetMesh( index ); if ( !pmesh ) return NULL; return pmesh->m_boneName; } void StudioModel::Physics_GetData( int boneIndex, hlmvsolid_t *psolid, constraint_ragdollparams_t *pConstraint ) const { CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex ); if ( !pMesh ) return; if ( psolid ) { memcpy( psolid, &pMesh->m_solid, sizeof(*psolid) ); } if ( pConstraint ) { *pConstraint = pMesh->m_constraint; } } void StudioModel::Physics_SetData( int boneIndex, const hlmvsolid_t *psolid, const constraint_ragdollparams_t *pConstraint ) { CPhysmesh *pMesh = m_pPhysics->GetMesh( boneIndex ); if ( !pMesh ) return; if ( psolid ) { memcpy( &pMesh->m_solid, psolid, sizeof(*psolid) ); } if ( pConstraint ) { pMesh->m_constraint = *pConstraint; } } float StudioModel::Physics_GetMass( void ) { return m_pPhysics->GetMass(); } void StudioModel::Physics_SetMass( float mass ) { m_physMass = mass; } char *StudioModel::Physics_DumpQC( void ) { return m_pPhysics->DumpQC(); } const vertexFileHeader_t * mstudiomodel_t::CacheVertexData( void * pModelData ) { Assert( pModelData == NULL ); Assert( g_pActiveModel ); return g_pStudioDataCache->CacheVertexData( g_pActiveModel->GetStudioRenderHdr() ); } //----------------------------------------------------------------------------- // FIXME: This trashy glue code is really not acceptable. Figure out a way of making it unnecessary. //----------------------------------------------------------------------------- const studiohdr_t *studiohdr_t::FindModel( void **cache, char const *pModelName ) const { MDLHandle_t handle = g_pMDLCache->FindMDL( pModelName ); *cache = (void*)handle; return g_pMDLCache->GetStudioHdr( handle ); } virtualmodel_t *studiohdr_t::GetVirtualModel( void ) const { return g_pMDLCache->GetVirtualModel( (MDLHandle_t)virtualModel ); } byte *studiohdr_t::GetAnimBlock( int i ) const { return g_pMDLCache->GetAnimBlock( (MDLHandle_t)virtualModel, i ); } int studiohdr_t::GetAutoplayList( unsigned short **pOut ) const { return g_pMDLCache->GetAutoplayList( (MDLHandle_t)virtualModel, pOut ); } const studiohdr_t *virtualgroup_t::GetStudioHdr( void ) const { return g_pMDLCache->GetStudioHdr( (MDLHandle_t)cache ); }