summaryrefslogtreecommitdiff
path: root/utils/hlmv/studio_render.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/hlmv/studio_render.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'utils/hlmv/studio_render.cpp')
-rw-r--r--utils/hlmv/studio_render.cpp2368
1 files changed, 2368 insertions, 0 deletions
diff --git a/utils/hlmv/studio_render.cpp b/utils/hlmv/studio_render.cpp
new file mode 100644
index 0000000..340ec67
--- /dev/null
+++ b/utils/hlmv/studio_render.cpp
@@ -0,0 +1,2368 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+// studio_render.cpp: routines for drawing Half-Life 3DStudio models
+// updates:
+// 1-4-99 fixed AdvanceFrame wraping bug
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+#include <windows.h> // for OutputDebugString. . has to be a better way!
+
+
+#include "ViewerSettings.h"
+#include "StudioModel.h"
+#include "vphysics/constraints.h"
+#include "physmesh.h"
+#include "materialsystem/imaterialsystem.h"
+#include "materialsystem/imaterial.h"
+#include "materialsystem/imaterialvar.h"
+#include "matsyswin.h"
+#include "istudiorender.h"
+#include "utldict.h"
+#include "filesystem.h"
+#include "studio_render.h"
+#include "materialsystem/imesh.h"
+#include "bone_setup.h"
+#include "materialsystem/MaterialSystem_Config.h"
+#include "MDLViewer.h"
+#include "bone_accessor.h"
+#include "jigglebones.h"
+#include "debugdrawmodel.h"
+
+// FIXME:
+extern ViewerSettings g_viewerSettings;
+int g_dxlevel = 0;
+
+#pragma warning( disable : 4244 ) // double to float
+
+////////////////////////////////////////////////////////////////////////
+
+CStudioHdr *g_pCacheHdr = NULL;
+
+Vector g_flexedverts[MAXSTUDIOVERTS];
+Vector g_flexednorms[MAXSTUDIOVERTS];
+int g_flexages[MAXSTUDIOVERTS];
+
+Vector *g_pflexedverts;
+Vector *g_pflexednorms;
+int *g_pflexages;
+
+int g_smodels_total; // cookie
+
+matrix3x4_t g_viewtransform; // view transformation
+//matrix3x4_t g_posetoworld[MAXSTUDIOBONES]; // bone transformation matrix
+matrix3x4_t g_mCachedViewTransform; // copy of view transform for boneMerge passes
+
+static int maxNumVertices;
+static int first = 1;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+mstudioseqdesc_t &StudioModel::GetSeqDesc( int seq )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return pStudioHdr->pSeqdesc( seq );
+}
+
+mstudioanimdesc_t &StudioModel::GetAnimDesc( int anim )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return pStudioHdr->pAnimdesc( anim );
+}
+
+
+//-----------------------------------------------------------------------------
+// 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 = GetTickCount();
+ // limit delta so that float time doesn't overflow
+ if (g_prevTicks == 0)
+ g_prevTicks = ticks;
+
+ g_time += ( (ticks - g_prevTicks) / 1000.0f ) * g_viewerSettings.speedScale;
+ g_prevTicks = ticks;
+
+ return g_time;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Keeps a global clock for "realtime" overlays to run from
+//-----------------------------------------------------------------------------
+float GetRealtimeTime( void )
+{
+ // renamed static's so debugger doesn't get confused and show the wrong one
+ static int g_prevTicksRT;
+ static float g_timeRT;
+
+ int ticks = GetTickCount();
+ // limit delta so that float time doesn't overflow
+ if (g_prevTicksRT == 0)
+ g_prevTicksRT = ticks;
+
+ g_timeRT += ( (ticks - g_prevTicksRT) / 1000.0f );
+ g_prevTicksRT = ticks;
+
+ return g_timeRT;
+}
+
+void StudioModel::AdvanceFrame( float dt )
+{
+ if (dt > 0.1)
+ dt = 0.1f;
+
+ m_dt = dt;
+
+ float t = GetDuration( );
+
+ if (t > 0)
+ {
+ if (dt > 0)
+ {
+ m_cycle += dt / t;
+ m_sequencetime += dt;
+
+ // wrap
+ m_cycle -= (int)(m_cycle);
+ }
+ }
+ else
+ {
+ m_cycle = 0;
+ }
+
+
+ for (int i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ t = GetDuration( m_Layer[i].m_sequence );
+ if (t > 0)
+ {
+ if (dt > 0)
+ {
+ m_Layer[i].m_cycle += (dt / t) * m_Layer[i].m_playbackrate;
+ m_Layer[i].m_cycle -= (int)(m_Layer[i].m_cycle);
+ }
+ }
+ else
+ {
+ m_Layer[i].m_cycle = 0;
+ }
+ }
+}
+
+float StudioModel::GetInterval( void )
+{
+ return m_dt;
+}
+
+float StudioModel::GetCycle( void )
+{
+ return m_cycle;
+}
+
+float StudioModel::GetFrame( void )
+{
+ return GetCycle() * GetMaxFrame();
+}
+
+int StudioModel::GetMaxFrame( void )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return Studio_MaxFrame( pStudioHdr, m_sequence, m_poseparameter );
+}
+
+int StudioModel::SetFrame( int frame )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( frame <= 0 )
+ frame = 0;
+
+ int maxFrame = GetMaxFrame();
+ if ( frame >= maxFrame )
+ {
+ frame = maxFrame;
+ m_cycle = 0.99999;
+ return frame;
+ }
+
+ m_cycle = frame / (float)maxFrame;
+ return frame;
+}
+
+
+float StudioModel::GetCycle( int iLayer )
+{
+ if (iLayer == 0)
+ {
+ return m_cycle;
+ }
+ else if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ return m_Layer[index].m_cycle;
+ }
+ return 0;
+}
+
+
+float StudioModel::GetFrame( int iLayer )
+{
+ return GetCycle( iLayer ) * GetMaxFrame( iLayer );
+}
+
+
+int StudioModel::GetMaxFrame( int iLayer )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( pStudioHdr )
+ {
+ if (iLayer == 0)
+ return Studio_MaxFrame( pStudioHdr, m_sequence, m_poseparameter );
+
+ if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ return Studio_MaxFrame( pStudioHdr, m_Layer[index].m_sequence, m_poseparameter );
+ }
+ }
+
+ return 0;
+}
+
+
+int StudioModel::SetFrame( int iLayer, int frame )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr )
+ return 0;
+
+ if ( frame <= 0 )
+ frame = 0;
+
+ int maxFrame = GetMaxFrame( iLayer );
+ float cycle = 0;
+ if (maxFrame)
+ {
+ if ( frame >= maxFrame )
+ {
+ frame = maxFrame;
+ cycle = 0.99999;
+ }
+ cycle = frame / (float)maxFrame;
+ }
+
+ if (iLayer == 0)
+ {
+ m_cycle = cycle;
+ }
+ else if (iLayer <= MAXSTUDIOANIMLAYERS)
+ {
+ int index = iLayer - 1;
+ m_Layer[index].m_cycle = cycle;
+ }
+
+ return frame;
+}
+
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Maps from local axis (X,Y,Z) to Half-Life (PITCH,YAW,ROLL) axis/rotation mappings
+//-----------------------------------------------------------------------------
+static int RemapAxis( int axis )
+{
+ switch( axis )
+ {
+ case 0:
+ return 2;
+ case 1:
+ return 0;
+ case 2:
+ return 1;
+ }
+
+ return 0;
+}
+
+void StudioModel::Physics_SetPreview( int previewBone, int axis, float t )
+{
+ m_physPreviewBone = previewBone;
+ m_physPreviewAxis = axis;
+ m_physPreviewParam = t;
+}
+
+
+void StudioModel::OverrideBones( bool *override )
+{
+ matrix3x4_t basematrix;
+ matrix3x4_t bonematrix;
+
+ QAngle tmp;
+ // offset for the base pose to world transform of 90 degrees around up axis
+ tmp[0] = 0; tmp[1] = 90; tmp[2] = 0;
+ AngleMatrix( tmp, bonematrix );
+ ConcatTransforms( g_viewtransform, bonematrix, basematrix );
+
+ for ( int i = 0; i < m_pPhysics->Count(); i++ )
+ {
+ CPhysmesh *pmesh = m_pPhysics->GetMesh( i );
+ // BUGBUG: Cache this if you care about performance!
+ int boneIndex = FindBone(pmesh->m_boneName);
+
+ // bone is not constrained, don't override rotations
+ if ( pmesh->m_constraint.parentIndex == 0 && pmesh->m_constraint.childIndex == 0 )
+ {
+ boneIndex = -1;
+ }
+
+ if ( boneIndex >= 0 )
+ {
+ matrix3x4_t *parentMatrix = &basematrix;
+ override[boneIndex] = true;
+ int parentBone = -1;
+ if ( pmesh->m_constraint.parentIndex >= 0 )
+ {
+ parentBone = FindBone( m_pPhysics->GetMesh(pmesh->m_constraint.parentIndex)->m_boneName );
+ }
+ if ( parentBone >= 0 )
+ {
+ parentMatrix = &m_pBoneToWorld[ parentBone ];
+ }
+
+ if ( m_physPreviewBone == i )
+ {
+ matrix3x4_t tmpmatrix;
+ QAngle rot;
+ constraint_axislimit_t *axis = pmesh->m_constraint.axes + m_physPreviewAxis;
+
+ int hlAxis = RemapAxis( m_physPreviewAxis );
+ rot.Init();
+ rot[hlAxis] = axis->minRotation + (axis->maxRotation - axis->minRotation) * m_physPreviewParam;
+ AngleMatrix( rot, tmpmatrix );
+ ConcatTransforms( pmesh->m_matrix, tmpmatrix, bonematrix );
+ }
+ else
+ {
+ MatrixCopy( pmesh->m_matrix, bonematrix );
+ }
+
+ ConcatTransforms( *parentMatrix, bonematrix, m_pBoneToWorld[ boneIndex ] );
+ }
+ }
+}
+
+
+int StudioModel::BoneMask( void )
+{
+ int lod = g_viewerSettings.autoLOD ? 0 : g_viewerSettings.lod;
+
+ int mask = BONE_USED_BY_VERTEX_AT_LOD(lod);
+ if (g_viewerSettings.showAttachments || g_viewerSettings.m_iEditAttachment != -1 || m_nSolveHeadTurn != 0 || LookupAttachment( "eyes" ) != -1)
+ {
+ mask |= BONE_USED_BY_ATTACHMENT;
+ }
+
+ if (g_viewerSettings.showHitBoxes)
+ {
+ mask |= BONE_USED_BY_HITBOX;
+ }
+
+ mask |= BONE_USED_BY_BONE_MERGE;
+
+ return mask;
+ // return BONE_USED_BY_ANYTHING_AT_LOD( lod );
+
+ // return BONE_USED_BY_ANYTHING;
+}
+
+void StudioModel::SetUpBones( bool mergeBones )
+{
+ int i, j;
+
+ mstudiobone_t *pbones;
+
+ static Vector pos[MAXSTUDIOBONES];
+ matrix3x4_t bonematrix;
+ static Quaternion q[MAXSTUDIOBONES];
+ bool override[MAXSTUDIOBONES];
+
+ static matrix3x4_t boneCache[MAXSTUDIOBONES];
+
+ // For blended transitions
+ static Vector pos2[MAXSTUDIOBONES];
+ static Quaternion q2[MAXSTUDIOBONES];
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioseqdesc_t &seqdesc = pStudioHdr->pSeqdesc( m_sequence );
+
+ QAngle a1;
+ Vector p1;
+ MatrixAngles( g_viewtransform, a1, p1 );
+ CIKContext *pIK = NULL;
+ m_ik.Init( pStudioHdr, a1, p1, GetRealtimeTime(), m_iFramecounter, BoneMask( ) );
+ if ( g_viewerSettings.enableIK )
+ {
+ pIK = &m_ik;
+ }
+
+ IBoneSetup boneSetup( pStudioHdr, BoneMask(), m_poseparameter );
+
+ boneSetup.InitPose( pos, q );
+
+ boneSetup.AccumulatePose( pos, q, m_sequence, m_cycle, 1.0, GetRealtimeTime(), pIK );
+
+ if ( g_viewerSettings.blendSequenceChanges &&
+ m_sequencetime < m_blendtime &&
+ m_prevsequence != m_sequence &&
+ m_prevsequence < pStudioHdr->GetNumSeq() &&
+ !(seqdesc.flags & STUDIO_SNAP) )
+ {
+ // Make sure frame is valid
+ if ( m_prevcycle >= 1.0 )
+ {
+ m_prevcycle = 0.0f;
+ }
+
+ float s = 1.0 - ( m_sequencetime / m_blendtime );
+ s = 3 * s * s - 2 * s * s * s;
+
+ boneSetup.AccumulatePose( pos, q, m_prevsequence, m_prevcycle, s, GetRealtimeTime(), NULL );
+ // Con_DPrintf("%d %f : %d %f : %f\n", pev->sequence, f, pev->prevsequence, pev->prevframe, s );
+ }
+ else
+ {
+ m_prevcycle = m_cycle;
+ }
+
+ int iMaxPriority = 0;
+ for (i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ if (m_Layer[i].m_weight > 0)
+ {
+ iMaxPriority = max( m_Layer[i].m_priority, iMaxPriority );
+ }
+ }
+
+ for (j = 0; j <= iMaxPriority; j++)
+ {
+ for (i = 0; i < MAXSTUDIOANIMLAYERS; i++)
+ {
+ if (m_Layer[i].m_priority == j && m_Layer[i].m_weight > 0)
+ {
+ boneSetup.AccumulatePose( pos, q, m_Layer[i].m_sequence, m_Layer[i].m_cycle, m_Layer[i].m_weight, GetRealtimeTime(), pIK );
+ }
+ }
+ }
+
+ if (m_nSolveHeadTurn != 0)
+ {
+ GetBodyPoseParametersFromFlex( );
+ }
+
+ CalcHeadRotation( pos, q );
+
+ CIKContext auto_ik;
+ auto_ik.Init( pStudioHdr, a1, p1, 0.0, 0, BoneMask( ) );
+ boneSetup.CalcAutoplaySequences( pos, q, GetAutoPlayTime(), &auto_ik );
+
+ boneSetup.CalcBoneAdj( pos, q, m_controller );
+
+ CBoneBitList boneComputed;
+ if (pIK)
+ {
+ Vector deltaPos;
+ QAngle deltaAngles;
+
+ GetMovement( m_prevIKCycles, deltaPos, deltaAngles );
+
+ Vector tmp;
+ VectorRotate( deltaPos, g_viewtransform, tmp );
+ deltaPos = tmp;
+
+ pIK->UpdateTargets( pos, q, m_pBoneToWorld, boneComputed );
+
+ // FIXME: check number of slots?
+ for (int i = 0; i < pIK->m_target.Count(); i++)
+ {
+ trace_t tr;
+ CIKTarget *pTarget = &pIK->m_target[i];
+
+ switch( pTarget->type )
+ {
+ case IK_GROUND:
+ {
+ // drawLine( pTarget->est.pos, pTarget->est.pos + pTarget->offset.pos, 0, 255, 0 );
+
+ // hack in movement
+ pTarget->est.pos -= deltaPos;
+
+ matrix3x4_t invViewTransform;
+ MatrixInvert( g_viewtransform, invViewTransform );
+ Vector tmp;
+ VectorTransform( pTarget->est.pos, invViewTransform, tmp );
+ tmp.z = pTarget->est.floor;
+ VectorTransform( tmp, g_viewtransform, pTarget->est.pos );
+ Vector p1;
+ Quaternion q1;
+ MatrixAngles( g_viewtransform, q1, p1 );
+ pTarget->est.q = q1;
+
+ float color[4] = { 0, 0, 0, 0 };
+ float wirecolor[4] = { 1, 1, 0, 1 };
+ if (pTarget->est.latched > 0.0)
+ {
+ wirecolor[1] = 1.0 - pTarget->est.flWeight;
+ }
+ else
+ {
+ wirecolor[0] = 1.0 - pTarget->est.flWeight;
+ }
+
+ float r = max(pTarget->est.radius,1.f);
+ Vector p0 = tmp + Vector( -r, -r, 0 );
+ Vector p2 = tmp + Vector( r, r, 0 );
+ drawTransparentBox( p0, p2, g_viewtransform, color, wirecolor );
+
+
+ if (!g_viewerSettings.enableTargetIK)
+ {
+ pTarget->est.flWeight = 0.0;
+ }
+ }
+ break;
+ case IK_ATTACHMENT:
+ {
+ matrix3x4_t m;
+
+ QuaternionMatrix( pTarget->est.q, pTarget->est.pos, m );
+
+ drawTransform( m, 4 );
+ }
+ break;
+ }
+
+ // drawLine( pTarget->est.pos, pTarget->latched.pos, 255, 0, 0 );
+ }
+
+ pIK->SolveDependencies( pos, q, m_pBoneToWorld, boneComputed );
+ }
+
+ pbones = pStudioHdr->pBone( 0 );
+
+ memset( override, 0, sizeof(bool)*pStudioHdr->numbones() );
+
+ if ( g_viewerSettings.showPhysicsPreview )
+ {
+ OverrideBones( override );
+ }
+
+ for (i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if ( !(pStudioHdr->pBone( i )->flags & BoneMask()))
+ {
+ int j, k;
+ for (j = 0; j < 3; j++)
+ {
+ for (k = 0; k < 4; k++)
+ {
+ m_pBoneToWorld[i][j][k] = VEC_T_NAN;
+ }
+ }
+ continue;
+ }
+
+ if ( override[i] )
+ {
+ continue;
+ }
+ else if (boneComputed.IsBoneMarked(i))
+ {
+ // already calculated
+ }
+ else if (CalcProceduralBone( pStudioHdr, i, CBoneAccessor( m_pBoneToWorld ) ))
+ {
+ continue;
+ }
+ else
+ {
+ QuaternionMatrix( q[i], bonematrix );
+
+ bonematrix[0][3] = pos[i][0];
+ bonematrix[1][3] = pos[i][1];
+ bonematrix[2][3] = pos[i][2];
+
+ if ( (pStudioHdr->pBone( 0 )[i].flags & BONE_ALWAYS_PROCEDURAL) &&
+ (pStudioHdr->pBone( 0 )[i].proctype & STUDIO_PROC_JIGGLE) )
+ {
+ //
+ // Physics-based "jiggle" bone
+ // Bone is assumed to be along the Z axis
+ // Pitch around X, yaw around Y
+ //
+
+ // compute desired bone orientation
+ matrix3x4_t goalMX;
+
+ if (pbones[i].parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, goalMX );
+ }
+ else
+ {
+ ConcatTransforms( m_pBoneToWorld[ pbones[i].parent ], bonematrix, goalMX );
+ }
+
+ // get jiggle properties from QC data
+ mstudiojigglebone_t *jiggleInfo = (mstudiojigglebone_t *)pStudioHdr->pBone( 0 )[i].pProcedure( );
+
+ if (!m_pJiggleBones)
+ {
+ m_pJiggleBones = new CJiggleBones;
+ }
+
+ // do jiggle physics
+ m_pJiggleBones->BuildJiggleTransformations( i, GetRealtimeTime(), jiggleInfo, goalMX, m_pBoneToWorld[ i ] );
+ }
+ else if (pbones[i].parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, m_pBoneToWorld[ i ] );
+ // MatrixCopy(bonematrix, g_bonetoworld[i]);
+ }
+ else
+ {
+ ConcatTransforms( m_pBoneToWorld[ pbones[i].parent ], bonematrix, m_pBoneToWorld[ i ] );
+ }
+ }
+
+ if (!mergeBones)
+ {
+ g_pCacheHdr = pStudioHdr;
+ MatrixCopy( m_pBoneToWorld[ i ], boneCache[i] );
+ }
+ else if (g_pCacheHdr)
+ {
+ for (j = 0; j < g_pCacheHdr->numbones(); j++)
+ {
+ if ( Q_stricmp( pStudioHdr->pBone( i )->pszName(), g_pCacheHdr->pBone( j )->pszName() ) == 0 )
+ break;
+ }
+ if (j < g_pCacheHdr->numbones())
+ {
+ MatrixCopy( boneCache[j], m_pBoneToWorld[ i ] );
+ }
+ }
+ }
+
+ if ( mergeBones )
+ {
+ Studio_RunBoneFlexDrivers( m_flexweight, pStudioHdr, pos, m_pBoneToWorld, g_mCachedViewTransform );
+ }
+ else
+ {
+ MatrixCopy( g_viewtransform, g_mCachedViewTransform );
+ Studio_RunBoneFlexDrivers( m_flexweight, pStudioHdr, pos, m_pBoneToWorld, g_viewtransform );
+ }
+
+ if (g_viewerSettings.showAttachments)
+ {
+ // drawTransform( m_pBoneToWorld[ 0 ] );
+ }
+}
+
+
+
+/*
+================
+StudioModel::SetupLighting
+ set some global variables based on entity position
+inputs:
+outputs:
+================
+*/
+void StudioModel::SetupLighting ( )
+{
+ LightDesc_t light[2];
+
+ light[0].m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ light[0].m_Attenuation0 = 1.0f;
+ light[0].m_Attenuation1 = 0.0;
+ light[0].m_Attenuation2 = 0.0;
+ light[0].m_Color[0] = g_viewerSettings.lColor[0];
+ light[0].m_Color[1] = g_viewerSettings.lColor[1];
+ light[0].m_Color[2] = g_viewerSettings.lColor[2];
+ light[0].m_Range = 2000;
+
+ // DEBUG: Spin the light around the head for debugging
+// g_viewerSettings.lightrot = QAngle( 0, 0, 0 );
+// g_viewerSettings.lightrot.y = fmod( (90 * GetTickCount( ) / 1000.0), 360.0);
+
+ AngleVectors( g_viewerSettings.lightrot, &light[0].m_Direction, NULL, NULL );
+ g_pStudioRender->SetLocalLights( 1, light );
+
+#if 0
+ light[1].m_Type = MATERIAL_LIGHT_DIRECTIONAL;
+ light[1].m_Attenuation0 = 1.0f;
+ light[1].m_Attenuation1 = 0.0;
+ light[1].m_Attenuation2 = 0.0;
+ light[1].m_Range = 2000;
+ light[1].m_Color[0] = g_viewerSettings.lColor[2];
+ light[1].m_Color[1] = g_viewerSettings.lColor[1];
+ light[1].m_Color[2] = g_viewerSettings.lColor[0];
+ light[1].m_Direction.x = -light[0].m_Direction.y;
+ light[1].m_Direction.y = light[0].m_Direction.x;
+ light[1].m_Direction.z = light[0].m_Direction.z;
+ g_pStudioRender->SetLocalLights( 2, light );
+#endif
+
+ int i;
+ for( i = 0; i < g_pStudioRender->GetNumAmbientLightSamples(); i++ )
+ {
+ m_AmbientLightColors[i][0] = g_viewerSettings.aColor[0];
+ m_AmbientLightColors[i][1] = g_viewerSettings.aColor[1];
+ m_AmbientLightColors[i][2] = g_viewerSettings.aColor[2];
+ }
+
+ //m_AmbientLightColors[0][0] = 1.0;
+ //m_AmbientLightColors[0][1] = 1.0;
+ //m_AmbientLightColors[0][2] = 1.0;
+
+ g_pStudioRender->SetAmbientLightColors( m_AmbientLightColors );
+}
+
+
+int FindBoneIndex( CStudioHdr *pstudiohdr, const char *pName )
+{
+ mstudiobone_t *pbones = pstudiohdr->pBone( 0 );
+ for (int i = 0; i < pstudiohdr->numbones(); i++)
+ {
+ if ( !strcmpi( pName, pbones[i].pszName() ) )
+ return i;
+ }
+
+ return -1;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Find the named bone index, -1 if not found
+// Input : *pName - bone name
+//-----------------------------------------------------------------------------
+int StudioModel::FindBone( const char *pName )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return FindBoneIndex( pStudioHdr, pName );
+}
+
+
+int StudioModel::Physics_GetBoneIndex( const char *pName )
+{
+ for (int i = 0; i < m_pPhysics->Count(); i++)
+ {
+ CPhysmesh *pmesh = m_pPhysics->GetMesh(i);
+ if ( !strcmpi( pName, pmesh[i].m_boneName ) )
+ return i;
+ }
+
+ return -1;
+}
+
+
+/*
+=================
+StudioModel::SetupModel
+ based on the body part, figure out which mesh it should be using.
+inputs:
+ currententity
+outputs:
+ pstudiomesh
+ pmdl
+=================
+*/
+
+void StudioModel::SetupModel ( int bodypart )
+{
+ int index;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (bodypart > pStudioHdr->numbodyparts())
+ {
+ // Con_DPrintf ("StudioModel::SetupModel: no such bodypart %d\n", bodypart);
+ bodypart = 0;
+ }
+
+ mstudiobodyparts_t *pbodypart = pStudioHdr->pBodypart( bodypart );
+
+ index = m_bodynum / pbodypart->base;
+ index = index % pbodypart->nummodels;
+
+ m_pmodel = pbodypart->pModel( index );
+
+ if(first){
+ maxNumVertices = m_pmodel->numvertices;
+ first = 0;
+ }
+}
+
+
+static IMaterial *g_pAlpha;
+
+
+//-----------------------------------------------------------------------------
+// Draws a box, not wireframed
+//-----------------------------------------------------------------------------
+
+void StudioModel::drawBox (Vector const *v, float const * color )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+
+ // The four sides
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 * 4 );
+ for (int i = 0; i < 10; i++)
+ {
+ meshBuilder.Position3fv (v[i & 7].Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+
+ // top and bottom
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[0].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[4].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[2].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ meshBuilder.Begin( pMesh, MATERIAL_TRIANGLE_STRIP, 2 );
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[3].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[5].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Draws a wireframed box
+//-----------------------------------------------------------------------------
+
+void StudioModel::drawWireframeBox (Vector const *v, float const* color )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+
+ // The four sides
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 4 );
+ for (int i = 0; i < 10; i++)
+ {
+ meshBuilder.Position3fv (v[i & 7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ meshBuilder.End();
+ pMesh->Draw();
+
+ // top and bottom
+ meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, 4 );
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[0].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[2].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[4].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[6].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINE_STRIP, 4 );
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[7].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[5].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[3].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3fv (v[1].Base());
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+//-----------------------------------------------------------------------------
+// Draws the position and axies of a transformation matrix, x=red,y=green,z=blue
+//-----------------------------------------------------------------------------
+void StudioModel::drawTransform( matrix3x4_t& m, float flLength )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ for (int k = 0; k < 3; k++)
+ {
+ static unsigned char color[3][3] =
+ {
+ { 255, 0, 0 },
+ { 0, 255, 0 },
+ { 0, 0, 255 }
+ };
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ meshBuilder.Color3ubv( color[k] );
+ meshBuilder.Position3f( m[0][3], m[1][3], m[2][3]);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3ubv( color[k] );
+ meshBuilder.Position3f( m[0][3] + m[0][k] * flLength, m[1][3] + m[1][k] * flLength, m[2][3] + m[2][k] * flLength);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+}
+
+void drawLine( Vector const &p1, Vector const &p2, int r, int g, int b, bool noDepthTest, float duration )
+{
+ g_pStudioModel->drawLine( p1, p2, r, g, b );
+}
+
+
+void StudioModel::drawLine( Vector const &p1, Vector const &p2, int r, int g, int b )
+{
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialVertexColor );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ meshBuilder.Color3ub( r, g, b );
+ meshBuilder.Position3f( p1.x, p1.y, p1.z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Color3ub( r, g, b );
+ meshBuilder.Position3f( p2.x, p2.y, p2.z );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws a transparent box with a wireframe outline
+//-----------------------------------------------------------------------------
+void StudioModel::drawTransparentBox( Vector const &bbmin, Vector const &bbmax,
+ const matrix3x4_t& m, float const *color, float const *wirecolor )
+{
+ Vector v[8], v2[8];
+
+ v[0][0] = bbmin[0];
+ v[0][1] = bbmax[1];
+ v[0][2] = bbmin[2];
+
+ v[1][0] = bbmin[0];
+ v[1][1] = bbmin[1];
+ v[1][2] = bbmin[2];
+
+ v[2][0] = bbmax[0];
+ v[2][1] = bbmax[1];
+ v[2][2] = bbmin[2];
+
+ v[3][0] = bbmax[0];
+ v[3][1] = bbmin[1];
+ v[3][2] = bbmin[2];
+
+ v[4][0] = bbmax[0];
+ v[4][1] = bbmax[1];
+ v[4][2] = bbmax[2];
+
+ v[5][0] = bbmax[0];
+ v[5][1] = bbmin[1];
+ v[5][2] = bbmax[2];
+
+ v[6][0] = bbmin[0];
+ v[6][1] = bbmax[1];
+ v[6][2] = bbmax[2];
+
+ v[7][0] = bbmin[0];
+ v[7][1] = bbmin[1];
+ v[7][2] = bbmax[2];
+
+ VectorTransform (v[0], m, v2[0]);
+ VectorTransform (v[1], m, v2[1]);
+ VectorTransform (v[2], m, v2[2]);
+ VectorTransform (v[3], m, v2[3]);
+ VectorTransform (v[4], m, v2[4]);
+ VectorTransform (v[5], m, v2[5]);
+ VectorTransform (v[6], m, v2[6]);
+ VectorTransform (v[7], m, v2[7]);
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_pAlpha );
+ drawBox( v2, color );
+
+ pRenderContext->Bind( g_materialBones );
+ drawWireframeBox( v2, wirecolor );
+}
+
+
+
+void StudioModel::UpdateStudioRenderConfig( bool bWireframe, bool bZBufferWireframe, bool bNormals, bool bTangentFrame )
+{
+ StudioRenderConfig_t config;
+ memset( &config, 0, sizeof( config ) );
+ config.fEyeShiftX = 0.0f;
+ config.fEyeShiftY = 0.0f;
+ config.fEyeShiftZ = 0.0f;
+ config.fEyeSize = 0;
+ config.drawEntities = 1;
+ config.skin = 0;
+ config.fullbright = 0;
+ config.bEyeMove = true;
+ config.bWireframe = bWireframe;
+
+ if ( g_viewerSettings.renderMode == RM_WIREFRAME || g_viewerSettings.softwareSkin || config.bWireframe || bNormals || bTangentFrame )
+ {
+ config.bSoftwareSkin = true;
+ }
+ else
+ {
+ config.bSoftwareSkin = false;
+ }
+
+ config.bSoftwareLighting = false;
+ config.bNoHardware = false;
+ config.bNoSoftware = false;
+ config.bTeeth = true;
+ config.bEyes = true;
+ config.bFlex = true;
+ config.bDrawNormals = bNormals;
+ config.bDrawTangentFrame = bTangentFrame;
+ config.bDrawZBufferedWireframe = bZBufferWireframe;
+ config.bShowEnvCubemapOnly = false;
+ g_pStudioRender->UpdateConfig( config );
+
+ MaterialSystem_Config_t matSysConfig = g_pMaterialSystem->GetCurrentConfigForVideoCard();
+ extern void InitMaterialSystemConfig(MaterialSystem_Config_t *pConfig);
+ InitMaterialSystemConfig( &matSysConfig );
+ matSysConfig.nFullbright = 0;
+ if( g_viewerSettings.renderMode == RM_SMOOTHSHADED )
+ {
+ matSysConfig.nFullbright = 2;
+ }
+
+ if ( g_dxlevel != 0 )
+ {
+ matSysConfig.dxSupportLevel = g_dxlevel;
+ }
+ g_pMaterialSystem->OverrideConfig( matSysConfig, false );
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws the skeleton
+//-----------------------------------------------------------------------------
+void StudioModel::DrawBones( )
+{
+ // draw bones
+ if (!g_viewerSettings.showBones && (g_viewerSettings.highlightBone < 0))
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudiobone_t *pbones = pStudioHdr->pBone( 0 );
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ IMesh* pMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+
+ bool drawRed = (g_viewerSettings.highlightBone >= 0);
+
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ if ( !(pStudioHdr->pBone( i )->flags & BoneMask()))
+ continue;
+
+ if ( pbones[i].parent >= 0 )
+ {
+ int j = pbones[i].parent;
+ if ( (g_viewerSettings.highlightBone < 0 ) || (j == g_viewerSettings.highlightBone) )
+ {
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 1 );
+
+ if (drawRed)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( m_pBoneToWorld[j][0][3], m_pBoneToWorld[j][1][3], m_pBoneToWorld[j][2][3]);
+ meshBuilder.AdvanceVertex();
+
+ if (drawRed)
+ meshBuilder.Color3ub( 255, 255, 0 );
+ else
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( m_pBoneToWorld[i][0][3], m_pBoneToWorld[i][1][3], m_pBoneToWorld[i][2][3]);
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+
+ if (g_viewerSettings.highlightBone >= 0)
+ {
+ if (i != g_viewerSettings.highlightBone)
+ continue;
+ }
+
+ drawTransform( m_pBoneToWorld[i] );
+ }
+
+ // manadatory to access correct verts
+ SetCurrentModel();
+
+ // highlight used vertices with point
+ /*
+ if (g_viewerSettings.highlightBone >= 0)
+ {
+ int k, j, n;
+ for (i = 0; i < pStudioHdr->numbodyparts; i++)
+ {
+ for (j = 0; j < pStudioHdr->pBodypart( i )->nummodels; j++)
+ {
+ mstudiomodel_t *pModel = pStudioHdr->pBodypart( i )->pModel( j );
+
+ const mstudio_modelvertexdata_t *vertData = pModel->GetVertexData();
+ Assert( vertData ); // This can only return NULL on X360 for now
+
+ meshBuilder.Begin( pMesh, MATERIAL_POINTS, 1 );
+
+ for (k = 0; k < pModel->numvertices; k++)
+ {
+ for (n = 0; n < vertData->BoneWeights( k )->numbones; n++)
+ {
+ if (vertData->BoneWeights( k )->bone[n] == g_viewerSettings.highlightBone)
+ {
+ Vector tmp;
+ Transform( *vertData->Position( k ), vertData->BoneWeights( k ), tmp );
+
+ meshBuilder.Color3ub( 0, 255, 255 );
+ meshBuilder.Position3f( tmp.x, tmp.y, tmp.z );
+ meshBuilder.AdvanceVertex();
+ break;
+ }
+ }
+ }
+
+ meshBuilder.End();
+ pMesh->Draw();
+ }
+ }
+ }
+ */
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws attachments
+//-----------------------------------------------------------------------------
+void StudioModel::DrawAttachments( )
+{
+ if ( !g_viewerSettings.showAttachments )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ for (int i = 0; i < pStudioHdr->GetNumAttachments(); i++)
+ {
+ mstudioattachment_t &pattachments = (mstudioattachment_t &)pStudioHdr->pAttachment( i );
+
+ matrix3x4_t world;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( i ) ], pattachments.local, world );
+
+ drawTransform( world );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws Axis
+//-----------------------------------------------------------------------------
+void StudioModel::DrawOriginAxis( )
+{
+ if ( !g_viewerSettings.showOriginAxis )
+ return;
+
+ const float fAxisLength = g_viewerSettings.originAxisLength;
+ if ( fAxisLength <= 0.0f )
+ return;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PushMatrix();;
+ pRenderContext->LoadIdentity();
+
+ pRenderContext->MatrixMode( MATERIAL_VIEW );
+ pRenderContext->LoadIdentity( );
+
+ pRenderContext->Rotate( -90, 1, 0, 0 ); // put Z going up
+ pRenderContext->Rotate( -90, 0, 0, 1 );
+
+ pRenderContext->Translate( -g_pStudioModel->m_origin[0], -g_pStudioModel->m_origin[1], -g_pStudioModel->m_origin[2] );
+
+ pRenderContext->Rotate( g_pStudioModel->m_angles[1], 0, 0, 1 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[0], 0, 1, 0 );
+ pRenderContext->Rotate( g_pStudioModel->m_angles[2], 1, 0, 0 );
+
+ IMesh *pMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMesh, MATERIAL_LINES, 3 );
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( fAxisLength, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 255, 0, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, fAxisLength, 0.0f );
+ meshBuilder.Color4ub( 0, 255, 0, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, 0.0f );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.Position3f( 0.0f, 0.0f, fAxisLength );
+ meshBuilder.Color4ub( 0, 0, 255, 255 );
+ meshBuilder.AdvanceVertex();
+
+ meshBuilder.End();
+ pMesh->Draw();
+
+ pRenderContext->MatrixMode(MATERIAL_MODEL);
+ pRenderContext->PopMatrix();
+ pRenderContext->MatrixMode(MATERIAL_VIEW);
+ pRenderContext->PopMatrix();
+}
+
+
+void StudioModel::DrawEditAttachment()
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ int iEditAttachment = g_viewerSettings.m_iEditAttachment;
+ if ( iEditAttachment >= 0 && iEditAttachment < pStudioHdr->GetNumAttachments() )
+ {
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( g_materialBones );
+
+ mstudioattachment_t &pAttachment = (mstudioattachment_t &)pStudioHdr->pAttachment( iEditAttachment );
+
+ matrix3x4_t world;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( iEditAttachment ) ], pAttachment.local, world );
+
+ drawTransform( world );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Draws hitboxes
+//-----------------------------------------------------------------------------
+
+
+static float hullcolor[8][4] =
+{
+ { 1.0, 1.0, 1.0, 1.0 },
+ { 1.0, 0.5, 0.5, 1.0 },
+ { 0.5, 1.0, 0.5, 1.0 },
+ { 1.0, 1.0, 0.5, 1.0 },
+ { 0.5, 0.5, 1.0, 1.0 },
+ { 1.0, 0.5, 1.0, 1.0 },
+ { 0.5, 1.0, 1.0, 1.0 },
+ { 1.0, 1.0, 1.0, 1.0 }
+};
+
+
+void StudioModel::DrawHitboxes( )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!g_pAlpha)
+ {
+ g_pAlpha = g_pMaterialSystem->FindMaterial("debug/debughitbox", TEXTURE_GROUP_OTHER, false);
+ if ( g_pAlpha )
+ {
+ g_pAlpha->AddRef();
+ }
+ }
+
+ if (g_viewerSettings.showHitBoxes || (g_viewerSettings.highlightHitbox >= 0))
+ {
+ int hitboxset = g_MDLViewer->GetCurrentHitboxSet();
+
+ HitboxList_t &list = g_pStudioModel->m_HitboxSets[ hitboxset ].m_Hitboxes;
+ for (unsigned short j = list.Head(); j != list.InvalidIndex(); j = list.Next(j) )
+ {
+ // Only draw one hitbox if we've selected it.
+ if ((g_viewerSettings.highlightHitbox >= 0) &&
+ (g_viewerSettings.highlightHitbox != j))
+ continue;
+
+ mstudiobbox_t *pBBox = &list[j].m_BBox;
+
+ float interiorcolor[4];
+ int c = pBBox->group % 8;
+ interiorcolor[0] = hullcolor[c][0] * 0.7;
+ interiorcolor[1] = hullcolor[c][1] * 0.7;
+ interiorcolor[2] = hullcolor[c][2] * 0.7;
+ interiorcolor[3] = hullcolor[c][3] * 0.4;
+
+ drawTransparentBox( pBBox->bbmin, pBBox->bbmax, m_pBoneToWorld[ pBBox->bone ], interiorcolor, hullcolor[ c ] );
+ }
+ }
+
+ /*
+ float color2[] = { 0, 0.7, 1, 0.6 };
+ float wirecolor2[] = { 0, 1, 1, 1.0 };
+ drawTransparentBox( pStudioHdr->min, pStudioHdr->max, g_viewtransform, color2, wirecolor2 );
+ */
+
+ if (g_viewerSettings.showSequenceBoxes)
+ {
+ float color[] = { 0.7, 1, 0, 0.6 };
+ float wirecolor[] = { 1, 1, 0, 1.0 };
+
+ drawTransparentBox( pStudioHdr->pSeqdesc( m_sequence ).bbmin, pStudioHdr->pSeqdesc( m_sequence ).bbmax, g_viewtransform, color, wirecolor );
+ }
+}
+
+void StudioModel::DrawIllumPosition( )
+{
+ if( !g_viewerSettings.showIllumPosition )
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+
+ Vector modelPt0;
+ Vector modelPt1;
+ Vector worldPt0;
+ Vector worldPt1;
+
+ // draw axis through illum position
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.x -= 4;
+ modelPt1.x += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 255, 0, 0 );
+
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.y -= 4;
+ modelPt1.y += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 0, 255, 0 );
+
+ VectorCopy(pStudioHdr->illumposition(), modelPt0);
+ VectorCopy(pStudioHdr->illumposition(), modelPt1);
+ modelPt0.z -= 4;
+ modelPt1.z += 4;
+ VectorTransform (modelPt0, g_viewtransform, worldPt0);
+ VectorTransform (modelPt1, g_viewtransform, worldPt1);
+ drawLine( worldPt0, worldPt1, 0, 0, 255 );
+
+}
+
+//-----------------------------------------------------------------------------
+// Draws the physics model
+//-----------------------------------------------------------------------------
+
+void StudioModel::DrawPhysicsModel( )
+{
+ if (!g_viewerSettings.showPhysicsModel)
+ return;
+
+ if ( g_viewerSettings.renderMode == RM_WIREFRAME && m_pPhysics->Count() == 1 )
+ {
+ // show the convex pieces in solid
+ DrawPhysConvex( m_pPhysics->GetMesh(0), g_materialFlatshaded );
+ }
+ else
+ {
+ for (int i = 0; i < m_pPhysics->Count(); i++)
+ {
+ float red[] = { 1.0, 0, 0, 0.25 };
+ float yellow[] = { 1.0, 1.0, 0, 0.5 };
+
+ CPhysmesh *pmesh = m_pPhysics->GetMesh(i);
+ int boneIndex = FindBone(pmesh->m_boneName);
+
+ if ( boneIndex >= 0 )
+ {
+ if ( (i+1) == g_viewerSettings.highlightPhysicsBone )
+ {
+ DrawPhysmesh( pmesh, boneIndex, g_materialBones, red );
+ }
+ else
+ {
+ if ( g_viewerSettings.highlightPhysicsBone < 1 )
+ {
+ // yellow for most
+ DrawPhysmesh( pmesh, boneIndex, g_materialBones, yellow );
+ }
+ }
+ }
+ else
+ {
+ DrawPhysmesh( pmesh, -1, g_materialBones, red );
+ }
+ }
+ }
+}
+
+
+
+
+void StudioModel::SetViewTarget( void )
+{
+ // only valid if the attachment bones are used
+ if ((BoneMask() & BONE_USED_BY_ATTACHMENT) == 0)
+ {
+ return;
+ }
+
+ int iEyeAttachment = LookupAttachment( "eyes" );
+
+ if (iEyeAttachment == -1)
+ return;
+
+ Vector local;
+ Vector tmp;
+
+ // look forward
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioattachment_t &patt = (mstudioattachment_t &)pStudioHdr->pAttachment( iEyeAttachment );
+ matrix3x4_t attToWorld;
+ ConcatTransforms( m_pBoneToWorld[ pStudioHdr->GetAttachmentBone( iEyeAttachment ) ], patt.local, attToWorld );
+ local = Vector( 32, 0, 0 );
+ Vector vEyes;
+ MatrixPosition( attToWorld, vEyes );
+
+ // aim the eyes if there's a target
+ if (m_vecHeadTargets.Count() > 0 && !m_vecHeadTargets.Tail().m_bSelf)
+ {
+ VectorITransform( m_vecHeadTargets.Tail().m_vecPosition - vEyes, attToWorld, local );
+ }
+
+ float flDist = local.Length();
+
+ VectorNormalize( local );
+
+ // calculate animated eye deflection
+ Vector eyeDeflect;
+ QAngle eyeAng( GetFlexController("eyes_updown"), GetFlexController("eyes_rightleft"), 0 );
+
+ // debugoverlay->AddTextOverlay( m_vecOrigin + Vector( 0, 0, 64 ), 0, 0, "%.2f %.2f", eyeAng.x, eyeAng.y );
+
+ AngleVectors( eyeAng, &eyeDeflect );
+ eyeDeflect.x = 0;
+
+ // reduce deflection the more the eye is off center
+ // FIXME: this angles make no damn sense
+ eyeDeflect = eyeDeflect * (local.x * local.x);
+ local = local + eyeDeflect;
+ VectorNormalize( local );
+
+ // check to see if the eye is aiming outside the max eye deflection
+ float flMaxEyeDeflection = pStudioHdr->MaxEyeDeflection();
+ if ( local.x < flMaxEyeDeflection )
+ {
+ // if so, clamp it to 30 degrees offset
+ // debugoverlay->AddTextOverlay( GetAbsOrigin() + Vector( 0, 0, 64 ), 1, 0, "%5.3f %5.3f %5.3f", local.x, local.y, local.z );
+ local.x = 0;
+ float d = local.LengthSqr();
+ if ( d > 0.0f )
+ {
+ d = sqrtf( ( 1.0f - flMaxEyeDeflection * flMaxEyeDeflection ) / ( local.y*local.y + local.z*local.z ) );
+ local.x = flMaxEyeDeflection;
+ local.y = local.y * d;
+ local.z = local.z * d;
+ }
+ else
+ {
+ local.x = 1.0;
+ }
+ }
+ local = local * flDist;
+ VectorTransform( local, attToWorld, tmp );
+
+ g_pStudioRender->SetEyeViewTarget( pStudioHdr->GetRenderHdr(), m_bodynum, tmp );
+}
+
+
+float UTIL_VecToYaw( const matrix3x4_t& matrix, const Vector &vec )
+{
+ Vector tmp = vec;
+ VectorNormalize( tmp );
+
+ float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
+ float y = matrix[0][1] * tmp.x + matrix[1][1] * tmp.y + matrix[2][1] * tmp.z;
+
+ if (x == 0.0f && y == 0.0f)
+ return 0.0f;
+
+ float yaw = atan2( -y, x );
+
+ yaw = RAD2DEG(yaw);
+
+ if (yaw < 0)
+ yaw += 360;
+
+ return yaw;
+}
+
+
+float UTIL_VecToPitch( const matrix3x4_t& matrix, const Vector &vec )
+{
+ Vector tmp = vec;
+ VectorNormalize( tmp );
+
+ float x = matrix[0][0] * tmp.x + matrix[1][0] * tmp.y + matrix[2][0] * tmp.z;
+ float z = matrix[0][2] * tmp.x + matrix[1][2] * tmp.y + matrix[2][2] * tmp.z;
+
+ if (x == 0.0f && z == 0.0f)
+ return 0.0f;
+
+ float pitch = atan2( z, x );
+
+ pitch = RAD2DEG(pitch);
+
+ if (pitch < 0)
+ pitch += 360;
+
+ return pitch;
+}
+
+
+float UTIL_AngleDiff( float destAngle, float srcAngle )
+{
+ float delta;
+
+ delta = destAngle - srcAngle;
+ if ( destAngle > srcAngle )
+ {
+ while ( delta >= 180 )
+ delta -= 360;
+ }
+ else
+ {
+ while ( delta <= -180 )
+ delta += 360;
+ }
+ return delta;
+}
+
+
+void StudioModel::UpdateBoneChain(
+ Vector pos[],
+ Quaternion q[],
+ int iBone,
+ matrix3x4_t *pBoneToWorld )
+{
+ matrix3x4_t bonematrix;
+
+ QuaternionMatrix( q[iBone], pos[iBone], bonematrix );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ int parent = pStudioHdr->pBone( iBone )->parent;
+ if (parent == -1)
+ {
+ ConcatTransforms( g_viewtransform, bonematrix, pBoneToWorld[iBone] );
+ }
+ else
+ {
+ // evil recursive!!!
+ UpdateBoneChain( pos, q, parent, pBoneToWorld );
+ ConcatTransforms( pBoneToWorld[parent], bonematrix, pBoneToWorld[iBone] );
+ }
+}
+
+
+
+
+void StudioModel::GetBodyPoseParametersFromFlex( )
+{
+ float flGoal;
+
+ flGoal = GetFlexController( "move_rightleft" );
+ SetPoseParameter( "body_trans_Y", flGoal );
+
+ flGoal = GetFlexController( "move_forwardback" );
+ SetPoseParameter( "body_trans_X", flGoal );
+
+ flGoal = GetFlexController( "move_updown" );
+ SetPoseParameter( "body_lift", flGoal );
+
+ flGoal = GetFlexController( "body_rightleft" ) + GetBodyYaw();
+ SetPoseParameter( "body_yaw", flGoal );
+
+ flGoal = GetFlexController( "body_updown" );
+ SetPoseParameter( "body_pitch", flGoal );
+
+ flGoal = GetFlexController( "body_tilt" );
+ SetPoseParameter( "body_roll", flGoal );
+
+ flGoal = GetFlexController( "chest_rightleft" ) + GetSpineYaw();
+ SetPoseParameter( "spine_yaw", flGoal );
+
+ flGoal = GetFlexController( "chest_updown" );
+ SetPoseParameter( "spine_pitch", flGoal );
+
+ flGoal = GetFlexController( "chest_tilt" );
+ SetPoseParameter( "spine_roll", flGoal );
+
+ flGoal = GetFlexController( "head_forwardback" );
+ SetPoseParameter( "neck_trans", flGoal );
+
+ flGoal = GetFlexController( "gesture_updown" );
+ SetPoseParameter( "gesture_height", flGoal );
+
+ flGoal = GetFlexController( "gesture_rightleft" );
+ SetPoseParameter( "gesture_width", flGoal );
+}
+
+
+
+
+void StudioModel::CalcHeadRotation( Vector pos[], Quaternion q[] )
+{
+ static Vector pos2[MAXSTUDIOBONES];
+ static Quaternion q2[MAXSTUDIOBONES];
+
+ if (m_nSolveHeadTurn == 0)
+ return;
+
+ if (m_dt == 0.0f)
+ {
+ m_dt = 0.1;
+ }
+
+ // GetAttachment( "eyes", vEyePosition, vEyeAngles );
+ int iForwardAttachment = LookupAttachment( "forward" );
+ if (iForwardAttachment == -1)
+ return;
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ mstudioattachment_t &patt = (mstudioattachment_t &)pStudioHdr->pAttachment( iForwardAttachment );
+
+ matrix3x4_t attToWorld;
+ int iBone = pStudioHdr->GetAttachmentBone( iForwardAttachment );
+ BuildBoneChain( pStudioHdr, g_viewtransform, pos, q, iBone, m_pBoneToWorld );
+ ConcatTransforms( m_pBoneToWorld[ iBone ], patt.local, attToWorld );
+
+ Vector vForward;
+ VectorRotate( Vector( 1, 0, 0 ), attToWorld, vForward );
+
+ float dt = m_dt;
+ if (m_nSolveHeadTurn == 2)
+ {
+ dt = 0.1;
+ }
+
+ Vector vEyes;
+ MatrixPosition( attToWorld, vEyes );
+
+ Vector vHead = vForward;
+ float flHeadInfluence = 0.0;
+ int i;
+ for (i = 0; i < m_vecHeadTargets.Count(); i++)
+ {
+ Vector dir;
+
+ if (m_vecHeadTargets[i].m_bSelf)
+ {
+ dir = vForward;
+ }
+ else
+ {
+ dir = m_vecHeadTargets[i].m_vecPosition - vEyes;
+ }
+ VectorNormalize( dir );
+ float flInterest = m_vecHeadTargets[i].m_flWeight;
+ if (flInterest > 0.0)
+ {
+ if (flHeadInfluence == 0.0)
+ {
+ vHead = dir;
+ flHeadInfluence = flInterest;
+ }
+ else
+ {
+ flHeadInfluence = flHeadInfluence * (1 - flInterest) + flInterest;
+ float w = flInterest / flHeadInfluence;
+ vHead = vHead * (1 - w) + dir * w;
+ }
+ }
+ }
+
+ Vector vTargetDir = Vector( 0, 0, 0 );
+ vTargetDir = vForward * (1.0 - flHeadInfluence) + vHead * flHeadInfluence;
+ VectorNormalize( vTargetDir );
+
+ SetPoseParameter( "head_pitch", 0.0 );
+ SetPoseParameter( "head_yaw", 0.0 );
+ SetPoseParameter( "head_roll", 0.0 );
+ SetHeadPosition( attToWorld, vTargetDir, dt );
+
+ // Msg( "yaw %f pitch %f\n", vEyeAngles.y, vEyeAngles.x );
+}
+
+
+
+float StudioModel::SetHeadPosition( matrix3x4_t& attToWorld, Vector const &vTargetPos, float dt )
+{
+ float flDiff;
+ int iPose;
+ QAngle vEyeAngles;
+ float flMoved = 0.0f;
+ matrix3x4_t targetXform, invAttToWorld;
+ matrix3x4_t headXform;
+
+ // align current "forward direction" to target direction
+ targetXform = attToWorld;
+ Studio_AlignIKMatrix( targetXform, vTargetPos );
+
+ // calc head movement needed
+ MatrixInvert( attToWorld, invAttToWorld );
+ ConcatTransforms( invAttToWorld, targetXform, headXform );
+
+ MatrixAngles( headXform, vEyeAngles );
+
+ // FIXME: add chest compression
+
+ // Msg( "yaw %f pitch %f\n", vEyeAngles.y, vEyeAngles.x );
+
+ float flMin, flMax;
+
+#if 1
+ //--------------------------------------
+ // Set head yaw
+ //--------------------------------------
+ // flDiff = vEyeAngles.y + GetFlexController( "head_rightleft" );
+ iPose = LookupPoseParameter( "head_yaw" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.y + GetFlexController( "head_rightleft" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+#if 1
+ //--------------------------------------
+ // Set head pitch
+ //--------------------------------------
+ iPose = LookupPoseParameter( "head_pitch" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.x + GetFlexController( "head_updown" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+#if 1
+ //--------------------------------------
+ // Set head roll
+ //--------------------------------------
+ iPose = LookupPoseParameter( "head_roll" );
+ GetPoseParameterRange( iPose, &flMin, &flMax );
+ flDiff = RangeCompressor( vEyeAngles.z + GetFlexController( "head_tilt" ), flMin, flMax, 0.0 );
+ SetPoseParameter( iPose, flDiff );
+#endif
+
+ return flMoved;
+}
+
+
+DrawModelInfo_t g_DrawModelInfo;
+DrawModelResults_t g_DrawModelResults;
+bool g_bDrawModelInfoValid = false;
+
+
+void StudioModel::GetModelTransform( matrix3x4_t &mat )
+{
+ AngleMatrix( m_angles, mat );
+
+ Vector vecModelOrigin;
+ VectorMultiply( m_origin, -1.0f, vecModelOrigin );
+ MatrixSetColumn( vecModelOrigin, 3, mat );
+}
+
+void StudioModel::SetModelTransform( const matrix3x4_t &mat )
+{
+ m_origin.x = -mat.m_flMatVal[0][3];
+ m_origin.y = -mat.m_flMatVal[1][3];
+ m_origin.z = -mat.m_flMatVal[2][3];
+
+ MatrixAngles( mat, m_angles );
+}
+
+
+/*
+================
+StudioModel::DrawModel
+inputs:
+ currententity
+ r_entorigin
+================
+*/
+int StudioModel::DrawModel( bool mergeBones )
+{
+ MDLCACHE_CRITICAL_SECTION_( g_pMDLCache );
+
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ if (!pStudioHdr)
+ return 0;
+
+ g_smodels_total++; // render data cache cookie
+
+ // 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 );
+
+ // NOTE: UpdateStudioRenderConfig can delete the studio hdr
+ pStudioHdr = GetStudioHdr();
+ if ( !pStudioHdr || pStudioHdr->numbodyparts() == 0)
+ return 0;
+
+ // Construct a transform to apply to the model. The camera is stuck in a fixed position
+ static Vector vecModelOrigin;
+ if ( !mergeBones )
+ {
+ AngleMatrix( m_angles, g_viewtransform );
+ VectorMultiply( m_origin, -1.0f, vecModelOrigin );
+ MatrixSetColumn( vecModelOrigin, 3, g_viewtransform );
+ }
+
+ // These values HAVE to be sent down for LOD to work correctly.
+ Vector viewOrigin, viewRight, viewUp, viewPlaneNormal;
+ g_pStudioRender->SetViewState( vec3_origin, Vector(0, 1, 0), Vector(0, 0, 1), Vector( 1, 0, 0 ) );
+
+ // g_pStudioRender->SetEyeViewTarget( viewOrigin );
+
+ SetUpBones( mergeBones );
+
+ SetupLighting( );
+
+ SetViewTarget( );
+
+
+ extern float g_flexdescweight[MAXSTUDIOFLEXDESC]; // garymcthack
+ extern float g_flexdescweight2[MAXSTUDIOFLEXDESC]; // garymcthack
+
+ int i;
+ for (i = 0; i < pStudioHdr->numflexdesc(); i++)
+ {
+ g_flexdescweight[i] = 0.0;
+ }
+
+ RunFlexRules( );
+
+ float d = 0.8;
+
+ if (m_dt != 0)
+ {
+ d = ExponentialDecay( 0.8, 0.033, m_dt );
+ }
+
+ float *pFlexWeights, *pFlexDelayedWeights;
+ g_pStudioRender->LockFlexWeights( pStudioHdr->numflexdesc(), &pFlexWeights, &pFlexDelayedWeights );
+
+ for (i = 0; i < pStudioHdr->numflexdesc(); i++)
+ {
+ g_flexdescweight2[i] = g_flexdescweight2[i] * d + g_flexdescweight[i] * (1 - d);
+
+ pFlexWeights[i] = g_flexdescweight[i];
+ pFlexDelayedWeights[i] = g_flexdescweight2[i];
+ }
+
+ g_pStudioRender->UnlockFlexWeights( );
+
+
+ // draw
+
+ g_pStudioRender->SetAlphaModulation( 1.0f );
+
+ g_bDrawModelInfoValid = true;
+ memset( &g_DrawModelInfo, 0, sizeof( g_DrawModelInfo ) );
+ g_DrawModelInfo.m_pStudioHdr = (studiohdr_t *)pStudioHdr->GetRenderHdr();
+ g_DrawModelInfo.m_pHardwareData = GetHardwareData();
+ if ( !g_DrawModelInfo.m_pHardwareData )
+ return 0;
+ g_DrawModelInfo.m_Decals = STUDIORENDER_DECAL_INVALID;
+ g_DrawModelInfo.m_Skin = m_skinnum;
+ g_DrawModelInfo.m_Body = m_bodynum;
+ g_DrawModelInfo.m_HitboxSet = g_MDLViewer->GetCurrentHitboxSet();
+ g_DrawModelInfo.m_pClientEntity = NULL;
+ g_DrawModelInfo.m_Lod = g_viewerSettings.autoLOD ? -1 : g_viewerSettings.lod;
+ g_DrawModelInfo.m_pColorMeshes = NULL;
+
+
+ if( g_viewerSettings.renderMode == RM_SHOWBADVERTEXDATA )
+ {
+ DebugDrawModelBadVerts( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin );
+
+ DebugDrawModelWireframe( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin, Vector( 0.2f, 0.2f, 0.2f ) );
+
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+
+#if 0
+ // overlay wireframe
+
+ // Set the state to trigger wireframe rendering
+ UpdateStudioRenderConfig( true, true, false, false );
+
+ // Draw wireframe
+ count = g_pStudioRender->DrawModel( &g_DrawModelResults, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin, STUDIORENDER_DRAW_ENTIRE_MODEL );
+ m_LodUsed = g_DrawModelResults.m_nLODUsed;
+ m_LodMetric = g_DrawModelResults.m_flLodMetric;
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+#endif
+ }
+ else if( g_viewerSettings.renderMode == RM_BONEWEIGHTS )
+ {
+ g_DrawModelInfo.m_Lod = 0;
+ DebugDrawModelBoneWeights( g_pStudioRender, g_DrawModelInfo, m_pBoneToWorld, vecModelOrigin );
+ g_DrawModelInfo.m_Lod = m_LodUsed;
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+ }
+ else if( g_viewerSettings.renderMode == RM_TEXCOORDS )
+ {
+ const char *pMatName = "";
+ if ( g_DrawModelInfo.m_pHardwareData->m_pLODs && g_viewerSettings.materialIndex < g_DrawModelInfo.m_pHardwareData->m_pLODs[0].numMaterials )
+ {
+ pMatName = g_DrawModelInfo.m_pHardwareData->m_pLODs[0].ppMaterials[g_viewerSettings.materialIndex]->GetName();
+ }
+ DebugDrawModelTexCoord( g_pStudioRender, pMatName, g_DrawModelInfo, m_pBoneToWorld, g_viewerSettings.width, g_viewerSettings.height );
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+ m_LodUsed = g_DrawModelInfo.m_Lod;
+ }
+ else
+ {
+ // Draw the model normally (may include normal and/or tangent line segments)
+ g_pStudioRender->DrawModel( &g_DrawModelResults, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+ m_LodUsed = g_DrawModelResults.m_nLODUsed;
+ m_LodMetric = g_DrawModelResults.m_flLODMetric;
+
+ g_pStudioRender->GetPerfStats( &g_DrawModelResults, g_DrawModelInfo, NULL );
+
+ // Optionally overlay wireframe...
+ if ( g_viewerSettings.overlayWireframe && !(g_viewerSettings.renderMode == RM_WIREFRAME) )
+ {
+ // Set the state to trigger wireframe rendering
+ UpdateStudioRenderConfig( true, true, false, false );
+
+ // Draw the wireframe over top of the model
+ g_pStudioRender->DrawModel( NULL, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+ }
+ }
+
+ int nCount = g_DrawModelResults.m_ActualTriCount;
+
+ DrawBones();
+ DrawAttachments();
+ DrawOriginAxis();
+ DrawEditAttachment();
+ DrawHitboxes();
+ DrawPhysicsModel();
+ DrawIllumPosition();
+
+ // Only draw the shadow if the ground is also drawn
+ if ( g_viewerSettings.showShadow && g_viewerSettings.showGround )
+ {
+ matrix3x4_t invViewTransform;
+
+ MatrixInvert( g_viewtransform, invViewTransform );
+
+ for (int i = 0; i < pStudioHdr->numbones(); i++)
+ {
+ matrix3x4_t *pMatrix = &m_pBoneToWorld[ i ];
+
+ matrix3x4_t tmp1;
+
+ ConcatTransforms( invViewTransform, *pMatrix, tmp1 );
+ tmp1[2][0] = 0.0;
+ tmp1[2][1] = 0.0;
+ tmp1[2][2] = 0.0;
+ tmp1[2][3] = 0.05;
+ ConcatTransforms( g_viewtransform, tmp1, *pMatrix );
+ }
+ g_DrawModelInfo.m_Lod = GetHardwareData()->m_NumLODs - 1;
+
+ float zero[4] = { 0, 0, 0, 0 };
+ g_pStudioRender->SetColorModulation( zero );
+ g_pStudioRender->ForcedMaterialOverride( g_materialShadow );
+
+ // Turn off any wireframe, normals or tangent frame display for the drop shadow
+ UpdateStudioRenderConfig( false, false, false, false );
+
+ g_pStudioRender->DrawModel( NULL, g_DrawModelInfo, m_pBoneToWorld,
+ pFlexWeights, pFlexDelayedWeights, vecModelOrigin );
+
+ // Restore the studio render config
+ UpdateStudioRenderConfig( g_viewerSettings.renderMode == RM_WIREFRAME, false,
+ g_viewerSettings.showNormals,
+ g_viewerSettings.showTangentFrame );
+
+ g_pStudioRender->ForcedMaterialOverride( NULL );
+ float one[4] = { 1, 1, 1, 1 };
+ g_pStudioRender->SetColorModulation( one );
+ }
+
+ return nCount;
+}
+
+
+void StudioModel::DrawPhysmesh( CPhysmesh *pMesh, int boneIndex, IMaterial* pMaterial, float* color )
+{
+ matrix3x4_t *pMatrix;
+ if ( boneIndex >= 0 )
+ {
+ pMatrix = &m_pBoneToWorld[ boneIndex ];
+ }
+ else
+ {
+ pMatrix = &g_viewtransform;
+ }
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+ IMesh* pMatMesh = pRenderContext->GetDynamicMesh( );
+
+ CMeshBuilder meshBuilder;
+ meshBuilder.Begin( pMatMesh, MATERIAL_TRIANGLES, pMesh->m_vertCount/3 );
+
+ int vertIndex = 0;
+ for ( int i = 0; i < pMesh->m_vertCount; i+=3 )
+ {
+ Vector v;
+
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ VectorTransform (pMesh->m_pVerts[vertIndex], *pMatrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+
+ vertIndex ++;
+ }
+ meshBuilder.End();
+ pMatMesh->Draw();
+}
+
+
+void RandomColor( float *color, int key )
+{
+ static bool first = true;
+ static colorVec colors[256];
+
+ if ( first )
+ {
+ int r, g, b;
+ first = false;
+ for ( int i = 0; i < 256; i++ )
+ {
+ do
+ {
+ r = rand()&255;
+ g = rand()&255;
+ b = rand()&255;
+ } while ( (r+g+b)<256 );
+ colors[i].r = r;
+ colors[i].g = g;
+ colors[i].b = b;
+ colors[i].a = 255;
+ }
+ }
+
+ int index = key & 255;
+ color[0] = colors[index].r * (1.f / 255.f);
+ color[1] = colors[index].g * (1.f / 255.f);
+ color[2] = colors[index].b * (1.f / 255.f);
+ color[3] = colors[index].a * (1.f / 255.f);
+}
+
+void StudioModel::DrawPhysConvex( CPhysmesh *pMesh, IMaterial* pMaterial )
+{
+ matrix3x4_t &matrix = g_viewtransform;
+
+ CMatRenderContextPtr pRenderContext( g_pMaterialSystem );
+ pRenderContext->Bind( pMaterial );
+
+ for ( int i = 0; i < pMesh->m_pCollisionModel->ConvexCount(); i++ )
+ {
+ float color[4];
+ RandomColor( color, i );
+ IMesh* pMatMesh = pRenderContext->GetDynamicMesh( );
+ CMeshBuilder meshBuilder;
+ int triCount = pMesh->m_pCollisionModel->TriangleCount( i );
+ meshBuilder.Begin( pMatMesh, MATERIAL_TRIANGLES, triCount );
+
+ for ( int j = 0; j < triCount; j++ )
+ {
+ Vector objectSpaceVerts[3];
+ pMesh->m_pCollisionModel->GetTriangleVerts( i, j, objectSpaceVerts );
+
+ for ( int k = 0; k < 3; k++ )
+ {
+ Vector v;
+
+ VectorTransform (objectSpaceVerts[k], matrix, v);
+ meshBuilder.Position3fv( v.Base() );
+ meshBuilder.Color4fv( color );
+ meshBuilder.AdvanceVertex();
+ }
+ }
+ meshBuilder.End();
+ pMatMesh->Draw();
+ }
+}
+
+
+
+/*
+================
+
+================
+*/
+
+
+int StudioModel::GetLodUsed( void )
+{
+ return m_LodUsed;
+}
+
+float StudioModel::GetLodMetric( void )
+{
+ return m_LodMetric;
+}
+
+
+const char *StudioModel::GetKeyValueText( int iSequence )
+{
+ CStudioHdr *pStudioHdr = GetStudioHdr();
+ return Studio_GetKeyValueText( pStudioHdr, iSequence );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : solve -
+//-----------------------------------------------------------------------------
+void StudioModel::SetSolveHeadTurn( int solve )
+{
+ m_nSolveHeadTurn = solve;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : int
+//-----------------------------------------------------------------------------
+int StudioModel::GetSolveHeadTurn() const
+{
+ return m_nSolveHeadTurn;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : target -
+//-----------------------------------------------------------------------------
+void StudioModel::ClearLookTargets( void )
+{
+ m_vecHeadTargets.RemoveAll();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : target -
+//-----------------------------------------------------------------------------
+void StudioModel::AddLookTarget( const Vector& vecPosition, float flWeight )
+{
+ if (m_vecHeadTargets.Count() > 8)
+ return;
+
+ StudioLookTarget tmp;
+
+ tmp.m_flWeight = flWeight;
+ tmp.m_vecPosition = vecPosition;
+ tmp.m_bSelf = false;
+
+ m_vecHeadTargets.AddToTail( tmp );
+}
+
+
+void StudioModel::AddLookTargetSelf( float flWeight )
+{
+ if (m_vecHeadTargets.Count() > 8)
+ return;
+
+ StudioLookTarget tmp;
+
+ tmp.m_flWeight = flWeight;
+ tmp.m_vecPosition = Vector(0,0,0);
+ tmp.m_bSelf = true;
+
+ m_vecHeadTargets.AddToTail( tmp );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+void StudioModel::SetModelYaw( float flYaw )
+{
+ m_flModelYaw = flYaw;
+}
+
+float StudioModel::GetModelYaw( void ) const
+{
+ return m_flModelYaw;
+}
+
+void StudioModel::SetBodyYaw( float flYaw )
+{
+ m_flBodyYaw = flYaw;
+}
+
+float StudioModel::GetBodyYaw( void ) const
+{
+ return m_flBodyYaw;
+}
+
+void StudioModel::SetSpineYaw( float flYaw )
+{
+ m_flSpineYaw = flYaw;
+}
+
+float StudioModel::GetSpineYaw( void ) const
+{
+ return m_flSpineYaw;
+}
+