📄 studio_render.cpp
字号:
/***
*
* Copyright (c) 1998, Valve LLC. All rights reserved.
*
* This product contains software technology licensed from Id
* Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc.
* All Rights Reserved.
*
****/
// 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 "studio_render.h"
#include "materialsystem/IMesh.h"
#include "bone_setup.h"
#include "materialsystem/MaterialSystem_Config.h"
#include "MDLViewer.h"
// FIXME:
extern ViewerSettings g_viewerSettings;
int g_dxlevel = 0;
#pragma warning( disable : 4244 ) // double to float
////////////////////////////////////////////////////////////////////////
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
static int maxNumVertices;
static int first = 1;
////////////////////////////////////////////////////////////////////////
mstudioseqdesc_t *StudioModel::GetSeqDesc( int seq )
{
// these should be dynamcially loaded
return m_pstudiohdr->pSeqdesc( seq );
}
mstudioanimdesc_t *StudioModel::GetAnimDesc( int anim )
{
return m_pstudiohdr->pAnimdesc( anim );
}
mstudioanim_t *StudioModel::GetAnim( int anim )
{
mstudioanimdesc_t *panimdesc = GetAnimDesc( anim );
return panimdesc->pAnim( 0 );
}
//-----------------------------------------------------------------------------
// Purpose: Keeps a global clock to autoplay sequences to run from
// Also deals with speedScale changes
//-----------------------------------------------------------------------------
float GetAutoPlayTime( void )
{
static float g_prevTicks;
static float g_time;
g_time += ( (GetTickCount() - g_prevTicks) / 1000.0f ) * g_viewerSettings.speedScale;
g_prevTicks = GetTickCount();
return g_time;
}
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::GetCycle( void )
{
return m_cycle;
}
float StudioModel::GetFrame( void )
{
return GetCycle() * GetMaxFrame();
}
int StudioModel::GetMaxFrame( void )
{
return Studio_MaxFrame( m_pstudiohdr, m_sequence, m_poseparameter );
}
int StudioModel::SetFrame( int frame )
{
if ( !m_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 )
{
if (iLayer == 0)
{
return Studio_MaxFrame( m_pstudiohdr, m_sequence, m_poseparameter );
}
else if (iLayer <= MAXSTUDIOANIMLAYERS)
{
int index = iLayer - 1;
return Studio_MaxFrame( m_pstudiohdr, m_Layer[index].m_sequence, m_poseparameter );
}
return 0;
}
int StudioModel::SetFrame( int iLayer, int frame )
{
if ( !m_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);
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_pStudioRender->GetBoneToWorld( 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_pStudioRender->GetBoneToWorld( boneIndex ));
}
}
}
static CIKContext ik;
void StudioModel::SetUpBones ( void )
{
int i, j;
mstudiobone_t *pbones;
static Vector pos[MAXSTUDIOBONES];
matrix3x4_t bonematrix;
static Quaternion q[MAXSTUDIOBONES];
bool override[MAXSTUDIOBONES];
// For blended transitions
static Vector pos2[MAXSTUDIOBONES];
static Quaternion q2[MAXSTUDIOBONES];
mstudioseqdesc_t *pseqdesc;
pseqdesc = m_pstudiohdr->pSeqdesc( m_sequence );
QAngle a1;
Vector p1;
MatrixAngles( g_viewtransform, a1 );
MatrixPosition( g_viewtransform, p1 );
CIKContext *pIK = NULL;
if ( g_viewerSettings.enableIK )
{
pIK = &ik;
ik.Init( m_pstudiohdr, a1, p1, 0.0 );
}
InitPose( m_pstudiohdr, pos, q );
AccumulatePose( m_pstudiohdr, pIK, pos, q, m_sequence, m_cycle, m_poseparameter, BONE_USED_BY_ANYTHING );
if ( g_viewerSettings.blendSequenceChanges &&
m_sequencetime < m_blendtime &&
m_prevsequence != m_sequence &&
m_prevsequence < m_pstudiohdr->numseq &&
!(pseqdesc->flags & STUDIO_SNAP) )
{
// Make sure frame is valid
pseqdesc = m_pstudiohdr->pSeqdesc( m_prevsequence );
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;
AccumulatePose( m_pstudiohdr, NULL, pos, q, m_prevsequence, m_prevcycle, m_poseparameter, BONE_USED_BY_ANYTHING, s );
// 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)
{
AccumulatePose( m_pstudiohdr, pIK, pos, q, m_Layer[i].m_sequence, m_Layer[i].m_cycle, m_poseparameter, BONE_USED_BY_ANYTHING, m_Layer[i].m_weight );
}
}
}
if (g_viewerSettings.solveHeadTurn != 0)
{
GetBodyPoseParametersFromFlex( );
}
SetHeadPosition( pos, q );
CIKContext auto_ik;
auto_ik.Init( m_pstudiohdr, a1, p1, 0.0 );
CalcAutoplaySequences( m_pstudiohdr, &auto_ik, pos, q, m_poseparameter, BONE_USED_BY_ANYTHING, GetAutoPlayTime() );
CalcBoneAdj( m_pstudiohdr, pos, q, m_controller, BONE_USED_BY_ANYTHING );
if (pIK)
{
pIK->SolveDependencies( pos, q );
}
pbones = m_pstudiohdr->pBone( 0 );
memset( override, 0, sizeof(bool)*m_pstudiohdr->numbones );
if ( g_viewerSettings.showPhysicsPreview )
{
OverrideBones( override );
}
for (i = 0; i < m_pstudiohdr->numbones; i++)
{
if ( override[i] )
{
continue;
}
else if (CalcProceduralBone( m_pstudiohdr, i, m_pStudioRender->GetBoneToWorldArray() ))
{
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 (pbones[i].parent == -1)
{
ConcatTransforms (g_viewtransform, bonematrix, *m_pStudioRender->GetBoneToWorld( i ));
// MatrixCopy(bonematrix, g_bonetoworld[i]);
}
else
{
ConcatTransforms (*m_pStudioRender->GetBoneToWorld( pbones[i].parent ), bonematrix, *m_pStudioRender->GetBoneToWorld( i ) );
}
}
}
if (g_viewerSettings.showAttachments)
{
// drawTransform( m_pStudioRender->GetBoneToWorld( 0 ) );
}
}
/*
================
StudioModel::SetupLighting
set some global variables based on entity position
inputs:
outputs:
================
*/
void StudioModel::SetupLighting ( )
{
LightDesc_t light;
light.m_Type = MATERIAL_LIGHT_DIRECTIONAL;
light.m_Attenuation0 = 1.0f;
light.m_Attenuation1 = 0.0;
light.m_Attenuation2 = 0.0;
light.m_Color[0] = g_viewerSettings.lColor[0];
light.m_Color[1] = g_viewerSettings.lColor[1];
light.m_Color[2] = g_viewerSettings.lColor[2];
light.m_Range = 2000;
// DEBUG: Spin the light around the head for debugging
// g_viewerSettings.lightrot = Vector( 0, 0, 0 );
// g_viewerSettings.lightrot.y = fmod( (90 * GetTickCount( ) / 1000.0), 360.0);
AngleVectors( g_viewerSettings.lightrot, &light.m_Direction, NULL, NULL );
m_pStudioRender->SetLocalLights( 1, &light );
int i;
for( i = 0; i < m_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_TotalLightColors[i][0] = m_AmbientLightColors[i][0] +
-light.m_Attenuation0 * DotProduct( light.m_Direction, m_pStudioRender->GetAmbientLightDirections()[i] ) *
light.m_Color[0];
m_TotalLightColors[i][1] = m_AmbientLightColors[i][1] +
-light.m_Attenuation0 * DotProduct( light.m_Direction, m_pStudioRender->GetAmbientLightDirections()[i] ) *
light.m_Color[1];
m_TotalLightColors[i][2] = m_AmbientLightColors[i][2] +
-light.m_Attenuation0 * DotProduct( light.m_Direction, m_pStudioRender->GetAmbientLightDirections()[i] ) *
light.m_Color[2];
}
m_pStudioRender->SetAmbientLightColors( m_AmbientLightColors, m_TotalLightColors );
}
int FindBoneIndex( studiohdr_t *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 )
{
return FindBoneIndex( m_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;
if (bodypart > m_pstudiohdr->numbodyparts)
{
// Con_DPrintf ("StudioModel::SetupModel: no such bodypart %d\n", bodypart);
bodypart = 0;
}
mstudiobodyparts_t *pbodypart = m_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 )
{
IMesh* pMesh = g_pMaterialSystem->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() );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -