Paranoia2/utils/hlmv/studio_render.cpp
2020-08-31 19:50:41 +03:00

1704 lines
43 KiB
C++

/***
*
* 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 <gl.h>
#include <GL/glu.h>
#include <mxMessageBox.h>
#include "StudioModel.h"
#include "ViewerSettings.h"
#include "ControlPanel.h"
#include "GlWindow.h"
typedef struct sortedmesh_s
{
mstudiomesh_t *mesh;
int flags; // face flags
} sortedmesh_t;
////////////////////////////////////////////////////////////////////////
Vector g_xformverts[MAXSTUDIOVERTS]; // transformed vertices
Vector g_xformnorms[MAXSTUDIOVERTS]; // transformed vertices
Vector g_lightvalues[MAXSTUDIOVERTS]; // light surface normals
Vector *g_pxformverts;
Vector *g_pxformnorms;
Vector *g_pvlightvalues;
Vector g_lightvec; // light vector in model reference frame
Vector g_blightvec[MAXSTUDIOBONES]; // light vectors in bone reference frames
int g_ambientlight; // ambient world light
float g_shadelight; // direct world light
Vector g_lightcolor;
int g_smodels_total; // cookie
sortedmesh_t g_sorted_meshes[1024]; // sorted meshes
matrix3x4 m_protationmatrix;
Vector2D g_chrome[MAXSTUDIOVERTS]; // texture coords for surface normals
int g_chromeage[MAXSTUDIOBONES]; // last time chrome vectors were updated
Vector g_chromeup[MAXSTUDIOBONES]; // chrome vector "up" in bone reference frames
Vector g_chromeright[MAXSTUDIOBONES]; // chrome vector "right" in bone reference frames
bool bUseWeaponOrigin = false;
bool bUseWeaponLeftHand = false;
bool bUseParanoiaFOV = false;
extern bool g_bStopPlaying;
CBaseBoneSetup g_boneSetup; // new blender implementation with IK :-)
static float hullcolor[8][3] =
{
{ 1.0f, 1.0f, 1.0f },
{ 1.0f, 0.5f, 0.5f },
{ 0.5f, 1.0f, 0.5f },
{ 1.0f, 1.0f, 0.5f },
{ 0.5f, 0.5f, 1.0f },
{ 1.0f, 0.5f, 1.0f },
{ 0.5f, 1.0f, 1.0f },
{ 1.0f, 1.0f, 1.0f },
};
//-----------------------------------------------------------------------------
// 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;
}
/*
===============
MeshCompare
Sorting opaque entities by model type
===============
*/
static int MeshCompare( const void *s1, const void *s2 )
{
sortedmesh_t *a = (sortedmesh_t *)s1;
sortedmesh_t *b = (sortedmesh_t *)s2;
if( FBitSet( a->flags, STUDIO_NF_ADDITIVE ))
return 1;
if( FBitSet( a->flags, STUDIO_NF_MASKED ))
return -1;
return 0;
}
////////////////////////////////////////////////////////////////////////
void StudioModel :: centerView( bool reset )
{
Vector min, max;
ExtractBbox( min, max );
float dx = max[0] - min[0];
float dy = max[1] - min[1];
float dz = max[2] - min[2];
float d = max( dx, max( dy, dz ));
if( reset )
{
g_viewerSettings.trans[0] = 0;
g_viewerSettings.trans[1] = 0;
g_viewerSettings.trans[2] = 0;
}
else
{
g_viewerSettings.trans[0] = 0;
g_viewerSettings.trans[1] = min[2] + dz / 2.0f;
g_viewerSettings.trans[2] = d * 1.0f;
}
g_viewerSettings.rot[0] = -90.0f;
g_viewerSettings.rot[1] = -90.0f;
g_viewerSettings.rot[2] = 0.0f;
g_viewerSettings.movementScale = Q_max( 1.0f, d * 0.01f );
}
bool StudioModel :: AdvanceFrame( float dt )
{
if( !m_pstudiohdr ) return false;
mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + m_sequence;
if( dt > 0.1f )
dt = 0.1f;
m_dt = dt;
float t = GetDuration( );
if( t > 0.0f )
{
if( dt > 0.0f )
{
m_cycle += (dt / t);
if( pseqdesc->flags & STUDIO_LOOPING || g_viewerSettings.sequence_autoplay )
m_cycle -= (int)(m_cycle);
else m_cycle = bound( 0.0f, m_cycle, 1.0f );
}
}
else
{
m_cycle = 0;
}
return true;
}
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 )
{
return g_boneSetup.LocalMaxFrame( m_sequence );
}
int StudioModel :: SetFrame( int frame )
{
if( frame == -1 )
return GetFrame();
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;
}
void StudioModel :: SetupTransform( bool bMirror )
{
Vector origin, angles;
float scale = 1.0f;
origin = angles = g_vecZero;
if( !bUseWeaponOrigin && FBitSet( m_pstudiohdr->flags, STUDIO_ROTATE ))
angles[1] = anglemod( 100.0f * m_flTime );
if( g_viewerSettings.editMode == EDIT_SOURCE )
origin = m_editfields[0].origin;
// build the rotation matrix
m_protationmatrix = matrix3x4( origin, angles, scale );
if( bMirror )
{
m_protationmatrix.SetUp( -m_protationmatrix.GetUp() );
}
if( bUseWeaponOrigin && bUseWeaponLeftHand )
{
// inverse the right vector
m_protationmatrix.SetRight( -m_protationmatrix.GetRight() );
}
}
void StudioModel :: BlendSequence( Vector pos[], Vector4D q[], blend_sequence_t *seqblend )
{
static Vector pos1b[MAXSTUDIOBONES];
static Vector4D q1b[MAXSTUDIOBONES];
float s;
if( seqblend->blendtime != 0.0f && ( seqblend->blendtime + seqblend->fadeout > m_flTime ) && ( seqblend->sequence < m_pstudiohdr->numseq ))
{
s = 1.0f - (m_flTime - seqblend->blendtime) / seqblend->fadeout;
if( s > 0 && s <= 1.0 )
{
// do a nice spline curve
s = 3 * s * s - 2 * s * s * s;
}
else if( s > 1.0f )
{
// Shouldn't happen, but maybe curtime is behind animtime?
s = 1.0f;
}
g_boneSetup.AccumulatePose( &m_ik, pos, q, seqblend->sequence, seqblend->cycle, s );
}
}
void StudioModel :: SetUpBones( bool bMirror )
{
int i;
mstudiobone_t *pbones;
mstudioboneinfo_t *pboneinfo;
CIKContext *pIK = NULL;
float adj[MAXSTUDIOCONTROLLERS];
static Vector pos[MAXSTUDIOBONES];
static Vector4D q[MAXSTUDIOBONES];
matrix3x4 bonematrix;
if( m_sequence >= m_pstudiohdr->numseq )
m_sequence = 0;
Vector a1 = m_protationmatrix.GetAngles();
Vector p1 = m_protationmatrix.GetOrigin();
m_ik.Init( &g_boneSetup, a1, p1, GetRealtimeTime(), m_iFramecounter );
pIK = NULL;
if( g_viewerSettings.enableIK && !bMirror )
{
pIK = &m_ik;
}
g_boneSetup.InitPose( pos, q );
g_boneSetup.UpdateRealTime( GetRealtimeTime() );
g_boneSetup.CalcBoneAdj( adj, m_controller, m_mouth );
g_boneSetup.AccumulatePose( pIK, pos, q, m_sequence, m_cycle, 1.0 );
// blends from previous sequences
for( i = 0; i < MAX_SEQBLENDS; i++ )
BlendSequence( pos, q, &m_seqblend[i] );
CIKContext auto_ik;
auto_ik.Init( &g_boneSetup, a1, p1, 0.0f, 0 );
g_boneSetup.UpdateRealTime( GetAutoPlayTime() );
g_boneSetup.CalcAutoplaySequences( &auto_ik, pos, q );
// g_boneSetup.CalcBoneAdj( pos, q, m_controller, m_mouth );
if( pIK )
{
Vector deltaPos;
Vector deltaAngles;
GetMovement( m_prevcycle, deltaPos, deltaAngles );
deltaPos = m_protationmatrix.VectorRotate( deltaPos );
pIK->UpdateTargets( pos, q, m_pbonetransform );
glDisable (GL_TEXTURE_2D);
glDisable (GL_CULL_FACE);
if (g_viewerSettings.transparency < 1.0f)
glDisable (GL_DEPTH_TEST);
else
glEnable (GL_DEPTH_TEST);
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// FIXME: check number of slots?
for( int i = 0; i < pIK->m_target.Count(); i++ )
{
CIKTarget *pTarget = &pIK->m_target[i];
switch( pTarget->type )
{
case IK_GROUND:
{
// g_boneSetup.debugLine( pTarget->est.pos, pTarget->est.pos + pTarget->offset.pos, 0, 255, 0 );
// hack in movement
pTarget->est.pos -= deltaPos;
Vector tmp = m_protationmatrix.VectorITransform( pTarget->est.pos );
tmp.z = pTarget->est.floor;
pTarget->est.pos = m_protationmatrix.VectorTransform( tmp );
pTarget->est.q = m_protationmatrix.GetQuaternion();
float color[4] = { 1.0f, 1.0f, 0.0f, 1.0f };
if( pTarget->est.latched > 0.0f )
color[1] = 1.0 - Q_min( pTarget->est.flWeight, 1.0 );
else color[0] = 1.0 - Q_min( pTarget->est.flWeight, 1.0 );
float r = Q_max( pTarget->est.radius, 1.0f );
Vector p0 = tmp + Vector( -r, -r, 0.1f );
Vector p2 = tmp + Vector( r, r, 0.1f );
drawTransparentBox( p0, p2, m_protationmatrix, color );
}
break;
case IK_ATTACHMENT:
{
matrix3x4 m = matrix3x4( pTarget->est.pos, pTarget->est.q );
drawTransform( m, 4 );
}
break;
}
// g_boneSetup.drawLine( pTarget->est.pos, pTarget->latched.pos, 255, 0, 0 );
}
pIK->SolveDependencies( pos, q, m_pbonetransform );
g_GlWindow->setupRenderMode(); // restore right rendermode
}
pbones = (mstudiobone_t *)((byte *)m_pstudiohdr + m_pstudiohdr->boneindex);
pboneinfo = (mstudioboneinfo_t *)((byte *)pbones + m_pstudiohdr->numbones * sizeof( mstudiobone_t ));
for( i = 0; i < m_pstudiohdr->numbones; i++ )
{
m_pbonecolor[i] = Vector( 0.0f, 0.0f, 1.0f ); // animated bone is blue (guess)
// animate all non-simulated bones
if( CalcProceduralBone( m_pstudiohdr, i, m_pbonetransform ))
{
m_pbonecolor[i] = Vector( 0.0f, 1.0f, 0.0f ); // procedural bone is green
continue;
}
// initialize bonematrix
bonematrix = matrix3x4( pos[i], q[i] );
if( FBitSet( pbones[i].flags, BONE_JIGGLE_PROCEDURAL ) && FBitSet( m_pstudiohdr->flags, STUDIO_HAS_BONEINFO ))
{
// Physics-based "jiggle" bone
// Bone is assumed to be along the Z axis
// Pitch around X, yaw around Y
// compute desired bone orientation
matrix3x4 goalMX;
if( pbones[i].parent == -1 ) goalMX = m_protationmatrix.ConcatTransforms( bonematrix );
else goalMX = m_pbonetransform[pbones[i].parent].ConcatTransforms( bonematrix );
// get jiggle properties from QC data
mstudiojigglebone_t *jiggleInfo = (mstudiojigglebone_t *)((byte *)m_pstudiohdr + pboneinfo[i].procindex);
if( !m_pJiggleBones ) m_pJiggleBones = new CJiggleBones;
// do jiggle physics
if( pboneinfo[i].proctype == STUDIO_PROC_JIGGLE )
{
m_pJiggleBones->BuildJiggleTransformations( i, m_flTime, jiggleInfo, goalMX, m_pbonetransform[i] );
m_pbonecolor[i] = Vector( 1.0f, 0.5f, 0.0f ); // jiggle bone is orange
}
else m_pbonetransform[i] = goalMX; // fallback
}
else if( pbones[i].parent == -1 )
{
m_pbonetransform[i] = m_protationmatrix.ConcatTransforms( bonematrix );
}
else
{
m_pbonetransform[i] = m_pbonetransform[pbones[i].parent].ConcatTransforms( bonematrix );
}
}
for( i = 0; i < m_pstudiohdr->numbones; i++ )
{
m_pworldtransform[i] = m_pbonetransform[i].ConcatTransforms( m_plocaltransform[i] );
}
}
/*
================
StudioModel::TransformFinalVert
================
*/
void StudioModel :: Lighting( Vector &lv, int bone, int flags, const Vector &normal )
{
float illum;
float lightcos;
illum = g_ambientlight;
if (flags & STUDIO_NF_FULLBRIGHT)
{
lv = Vector( 1.0f );
return;
}
if (flags & STUDIO_NF_FLATSHADE)
{
illum += g_shadelight * 0.8;
}
else
{
float r;
if( bone != -1 ) lightcos = DotProduct (normal, g_blightvec[bone]);
else lightcos = DotProduct (normal, g_lightvec);
if (lightcos > 1.0f) lightcos = 1.0f; // -1 colinear, 1 opposite
illum += g_shadelight;
r = 1.5f; // lambert
// do modified hemispherical lighting
if( r <= 1.0f )
{
r += 1.0f;
lightcos = (( r - 1.0f ) - lightcos) / r;
if( lightcos > 0.0f )
illum += g_shadelight * lightcos;
}
else
{
lightcos = (lightcos + ( r - 1.0f )) / r;
if( lightcos > 0.0f )
illum -= g_shadelight * lightcos;
}
if (illum <= 0)
illum = 0;
}
if (illum > 255)
illum = 255;
lv = g_lightcolor * Vector( illum / 255.0 ); // Light from 0 to 1.0
}
void StudioModel :: Chrome( Vector2D &chrome, int bone, const Vector &normal )
{
float n;
if (g_chromeage[bone] != g_smodels_total)
{
// calculate vectors from the viewer to the bone. This roughly adjusts for position
Vector chromeupvec; // g_chrome t vector in world reference frame
Vector chromerightvec; // g_chrome s vector in world reference frame
Vector tmp, v_left; // vector pointing at bone in world reference frame
tmp = -m_protationmatrix.GetOrigin();
tmp += m_pbonetransform[bone].GetOrigin();
tmp = tmp.Normalize();
v_left = Vector( 0.0f, -1.0f, 0.0f );
chromeupvec = CrossProduct( tmp, v_left ).Normalize();
chromerightvec = CrossProduct( tmp, chromeupvec ).Normalize();
chromeupvec = -chromeupvec; // GoldSrc rules
g_chromeup[bone] = m_pbonetransform[bone].VectorIRotate( chromeupvec );
g_chromeright[bone] = m_pbonetransform[bone].VectorIRotate( chromerightvec );
g_chromeage[bone] = g_smodels_total;
}
// calc s coord
n = DotProduct( normal, g_chromeright[bone] );
chrome.x = (n + 1.0f) * 32.0f;
// calc t coord
n = DotProduct( normal, g_chromeup[bone] );
chrome.y = (n + 1.0f) * 32.0f;
}
void StudioModel :: DrawSpriteQuad( const Vector &org, const Vector &right, const Vector &up, float scale )
{
Vector point;
glBegin( GL_QUADS );
glTexCoord2f( 0.0f, 1.0f );
point = org + up * (-32.0f * scale);
point = point + right * (-32.0f * scale);
glVertex3fv( point );
glTexCoord2f( 0.0f, 0.0f );
point = org + up * (32.0f * scale);
point = point + right * (-32.0f * scale);
glVertex3fv( point );
glTexCoord2f( 1.0f, 0.0f );
point = org + up * (32.0f * scale);
point = point + right * (32.0f * scale);
glVertex3fv( point );
glTexCoord2f( 1.0f, 1.0f );
point = org + up * (-32.0f * scale);
point = point + right * (32.0f * scale);
glVertex3fv( point );
glEnd();
}
void StudioModel::RenderMuzzleFlash( muzzleflash_t *muzzle )
{
Vector v_right, v_up;
float sr, cr;
if( muzzle->time < m_flTime )
{
memset( muzzle, 0, sizeof( muzzleflash_t ));
return; // expired
}
SinCos( DEG2RAD( muzzle->rotate ), &sr, &cr );
for( int i = 0; i < 3; i++ )
{
v_right[i] = (g_GlWindow->vectors[1][i] * cr + g_GlWindow->vectors[0][i] * sr);
v_up[i] = g_GlWindow->vectors[1][i] * -sr + g_GlWindow->vectors[0][i] * cr;
}
if( bUseWeaponOrigin && bUseWeaponLeftHand )
v_right = -v_right;
glEnable( GL_BLEND );
glDepthMask( GL_FALSE );
glBlendFunc( GL_SRC_ALPHA, GL_ONE );
glColor4f( 1.0f, 1.0f, 1.0f, 1.0f );
glBindTexture( GL_TEXTURE_2D, TEXTURE_MUZZLEFLASH1 + muzzle->texture );
DrawSpriteQuad( muzzle->origin, v_right, v_up, muzzle->scale );
glDisable( GL_BLEND );
glDepthMask( GL_TRUE );
}
void StudioModel::MuzzleFlash( int attachment, int type )
{
if( attachment >= m_pstudiohdr->numattachments )
return; // bad attachment
muzzleflash_t *muzzle;
// move current sequence into circular buffer
m_current_muzzle = (m_current_muzzle + 1) & MASK_MUZZLEFLASHES;
muzzle = &m_muzzleflash[m_current_muzzle];
muzzle->texture = ( type % 10 ) % 3;
muzzle->scale = ( type / 10 ) * 0.1f;
if( muzzle->scale == 0.0f )
muzzle->scale = 0.5f;
// don't rotate on paused
if( !g_viewerSettings.pause && !g_bStopPlaying )
{
if( muzzle->texture == 0 )
muzzle->rotate = RANDOM_LONG( 0, 20 ); // rifle flash
else muzzle->rotate = RANDOM_LONG( 0, 359 );
}
bool isInEditMode = (g_ControlPanel->getTableIndex() == TAB_MODELEDITOR) ? true : false;
bool isEditSource = (g_viewerSettings.editMode == EDIT_SOURCE) ? true : false;
if( isInEditMode && isEditSource )
{
for( int i = 0; i < m_numeditfields; i++ )
{
edit_field_t *ed = &m_editfields[i];
if( ed->type != TYPE_ATTACHMENT || ed->id != attachment )
continue;
muzzle->origin = m_pbonetransform[ed->bone].VectorTransform( ed->origin );
break;
}
}
else
{
mstudioattachment_t *pattachment = (mstudioattachment_t *) ((byte *) m_pstudiohdr + m_pstudiohdr->attachmentindex) + attachment;
muzzle->origin = m_pbonetransform[pattachment->bone].VectorTransform( pattachment->org );
}
if( !g_viewerSettings.pause && !g_bStopPlaying )
muzzle->time = m_flTime + 0.015f;
else muzzle->time = m_flTime + 0.0099f;
}
void StudioModel::ClientEvents( void )
{
mstudioseqdesc_t *pseqdesc;
mstudioevent_t *pevent;
float end, start;
pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + m_sequence;
// no events for this animation
if( pseqdesc->numevents == 0 )
return;
start = GetFrame() - g_viewerSettings.speedScale * m_flFrameTime * pseqdesc->fps;
end = GetFrame();
if( sequence_reset )
{
if( !FBitSet( pseqdesc->flags, STUDIO_LOOPING ))
start = -0.01f;
sequence_reset = false;
}
pevent = (mstudioevent_t *)((byte *)m_pstudiohdr + pseqdesc->eventindex);
for( int i = 0; i < pseqdesc->numevents; i++ )
{
if( (float)pevent[i].frame > start && pevent[i].frame <= end )
{
switch( pevent[i].event )
{
case 5001:
MuzzleFlash( 0, atoi( pevent[i].options ));
break;
case 5011:
MuzzleFlash( 1, atoi( pevent[i].options ));
break;
case 5021:
MuzzleFlash( 2, atoi( pevent[i].options ));
break;
case 5031:
MuzzleFlash( 3, atoi( pevent[i].options ));
break;
}
}
}
}
/*
================
StudioModel::SetupLighting
set some global variables based on entity position
inputs:
outputs:
g_ambientlight
g_shadelight
================
*/
void StudioModel::SetupLighting ( )
{
g_ambientlight = 95;
g_shadelight = 160;
g_lightvec[0] = g_viewerSettings.gLightVec[0];
g_lightvec[1] = g_viewerSettings.gLightVec[1];
g_lightvec[2] = g_viewerSettings.gLightVec[2];
g_lightcolor[0] = g_viewerSettings.lColor[0];
g_lightcolor[1] = g_viewerSettings.lColor[1];
g_lightcolor[2] = g_viewerSettings.lColor[2];
g_lightvec = g_lightvec.Normalize();
// TODO: only do it for bones that actually have textures
for( int i = 0; i < m_pstudiohdr->numbones; i++ )
g_blightvec[i] = m_pbonetransform[i].VectorIRotate( g_lightvec ).Normalize();
}
/*
=================
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 = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + bodypart;
index = m_bodynum / pbodypart->base;
index = index % pbodypart->nummodels;
m_pmodel = (mstudiomodel_t *)((byte *)m_pstudiohdr + pbodypart->modelindex) + index;
}
void drawBox (Vector *v, float const * color = NULL)
{
if( color ) glColor4fv( color );
glBegin (GL_QUAD_STRIP);
for (int i = 0; i < 10; i++)
glVertex3fv (v[i & 7]);
glEnd ();
glBegin (GL_QUAD_STRIP);
glVertex3fv (v[6]);
glVertex3fv (v[0]);
glVertex3fv (v[4]);
glVertex3fv (v[2]);
glEnd ();
glBegin (GL_QUAD_STRIP);
glVertex3fv (v[1]);
glVertex3fv (v[7]);
glVertex3fv (v[3]);
glVertex3fv (v[5]);
glEnd ();
}
//-----------------------------------------------------------------------------
// Draws the position and axies of a transformation matrix, x=red,y=green,z=blue
//-----------------------------------------------------------------------------
void StudioModel :: drawTransform( matrix3x4 &m, float flLength )
{
glBegin( GL_LINES );
for( int k = 0; k < 3; k++ )
{
glColor3f( 1, 0, 0 );
glVertex3fv( m.GetOrigin() );
glColor3f( 1, 1, 1 );
glVertex3fv( m.GetOrigin() + m.GetRow( k ) * 4.0f );
}
glEnd();
}
//-----------------------------------------------------------------------------
// Draws a transparent box with a wireframe outline
//-----------------------------------------------------------------------------
void StudioModel :: drawTransparentBox( Vector const &bbmin, Vector const &bbmax, const matrix3x4 &m, float const *color )
{
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];
v2[0] = m.VectorTransform( v[0] );
v2[1] = m.VectorTransform( v[1] );
v2[2] = m.VectorTransform( v[2] );
v2[3] = m.VectorTransform( v[3] );
v2[4] = m.VectorTransform( v[4] );
v2[5] = m.VectorTransform( v[5] );
v2[6] = m.VectorTransform( v[6] );
v2[7] = m.VectorTransform( v[7] );
drawBox (v2, color);
}
/*
================
StudioModel::DrawModel
inputs:
currententity
r_entorigin
================
*/
void StudioModel :: DrawModel( bool bMirror )
{
int i;
if( !m_pstudiohdr ) return;
g_smodels_total++; // render data cache cookie
g_pxformverts = &g_xformverts[0];
g_pxformnorms = &g_xformnorms[0];
g_pvlightvalues = &g_lightvalues[0];
if( m_pstudiohdr->numbodyparts == 0 )
return;
bool isInEditMode = (g_ControlPanel->getTableIndex() == TAB_MODELEDITOR) ? true : false;
bool drawEyePos = g_viewerSettings.showAttachments;
bool drawAttachments = g_viewerSettings.showAttachments;
bool drawHitboxes = g_viewerSettings.showHitBoxes;
bool drawAbsBox = false;
int drawIndex = -1; // draw all
int colorIndex = -1;
if( isInEditMode && m_pedit )
{
drawEyePos = (m_pedit->type == TYPE_EYEPOSITION) ? true : false;
drawAttachments = (m_pedit->type == TYPE_ATTACHMENT) ? true : false;
drawHitboxes = (m_pedit->type == TYPE_HITBOX) ? true : false;
drawAbsBox = (m_pedit->type == TYPE_BBOX || m_pedit->type == TYPE_CBOX) ? true : false;
drawIndex = m_pedit->id;
if(m_pedit->type == TYPE_HITBOX)
{
mstudiobbox_t *phitbox = (mstudiobbox_t *) ((byte *) m_pstudiohdr + m_pstudiohdr->hitboxindex) + m_pedit->id;
if( g_viewerSettings.editMode == EDIT_MODEL )
colorIndex = (phitbox->group % 8);
else colorIndex = (m_pedit->hitgroup % 8);
}
}
SetupTransform( bMirror );
SetUpBones( bMirror );
if( !bMirror )
{
updateModel();
}
SetupLighting( );
ClientEvents( );
for( i = 0; i < m_pstudiohdr->numbodyparts; i++ )
{
SetupModel( i );
if( g_viewerSettings.transparency > 0.0f )
DrawPoints();
if( g_viewerSettings.showWireframeOverlay && g_viewerSettings.renderMode != RM_WIREFRAME )
DrawPoints( true );
}
glDisable( GL_MULTISAMPLE );
for( i = 0; i < MAX_MUZZLEFLASHES; i++ )
{
if( m_muzzleflash[i].time == 0.0f )
continue;
RenderMuzzleFlash ( &m_muzzleflash[i] );
}
if( drawEyePos )
{
glDisable( GL_TEXTURE_2D );
glDisable( GL_DEPTH_TEST );
glDisable( GL_BLEND );
glPointSize( 7.0f );
glColor3f( 1.0f, 0.5f, 1.0f );
glBegin( GL_POINTS );
if( isInEditMode && m_pedit && g_viewerSettings.editMode == EDIT_SOURCE )
glVertex3fv( m_protationmatrix.VectorTransform( m_pedit->origin ));
else glVertex3fv( m_protationmatrix.VectorTransform( m_pstudiohdr->eyeposition ));
glEnd();
glPointSize( 1.0f );
}
// draw abs box
if( drawAbsBox )
{
vec3_t tmp, bbox[8];
glDisable( GL_MULTISAMPLE );
glDisable (GL_TEXTURE_2D);
glDisable (GL_CULL_FACE);
glDisable( GL_BLEND );
if (g_viewerSettings.transparency < 1.0f)
glDisable (GL_DEPTH_TEST);
else
glEnable (GL_DEPTH_TEST);
if( m_pedit && m_pedit->type == TYPE_BBOX )
glColor4f (1, 0, 0, 1.0f);
else glColor4f (1, 0.5, 0, 1.0f);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if( g_ControlPanel->getTableIndex() == TAB_SEQUENCES )
{
mstudioseqdesc_t *pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence;
for( int j = 0; j < 8; j++ )
{
tmp[0] = (j & 1) ? pseqdesc->bbmin[0] : pseqdesc->bbmax[0];
tmp[1] = (j & 2) ? pseqdesc->bbmin[1] : pseqdesc->bbmax[1];
tmp[2] = (j & 4) ? pseqdesc->bbmin[2] : pseqdesc->bbmax[2];
bbox[j] = tmp;
}
}
else if( g_viewerSettings.editMode == EDIT_MODEL )
{
if( m_pedit->type == TYPE_BBOX )
{
for( int j = 0; j < 8; j++ )
{
tmp[0] = (j & 1) ? m_pstudiohdr->min[0] : m_pstudiohdr->max[0];
tmp[1] = (j & 2) ? m_pstudiohdr->min[1] : m_pstudiohdr->max[1];
tmp[2] = (j & 4) ? m_pstudiohdr->min[2] : m_pstudiohdr->max[2];
bbox[j] = tmp;
}
}
else
{
for( int j = 0; j < 8; j++ )
{
tmp[0] = (j & 1) ? m_pstudiohdr->bbmin[0] : m_pstudiohdr->bbmax[0];
tmp[1] = (j & 2) ? m_pstudiohdr->bbmin[1] : m_pstudiohdr->bbmax[1];
tmp[2] = (j & 4) ? m_pstudiohdr->bbmin[2] : m_pstudiohdr->bbmax[2];
bbox[j] = tmp;
}
}
}
else
{
for( int j = 0; j < 8; j++ )
{
tmp[0] = (j & 1) ? m_pedit->mins[0] : m_pedit->maxs[0];
tmp[1] = (j & 2) ? m_pedit->mins[1] : m_pedit->maxs[1];
tmp[2] = (j & 4) ? m_pedit->mins[2] : m_pedit->maxs[2];
bbox[j] = tmp;
}
}
glBegin( GL_LINES );
for( int i = 0; i < 2; i += 1 )
{
glVertex3fv( bbox[i+0] );
glVertex3fv( bbox[i+2] );
glVertex3fv( bbox[i+4] );
glVertex3fv( bbox[i+6] );
glVertex3fv( bbox[i+0] );
glVertex3fv( bbox[i+4] );
glVertex3fv( bbox[i+2] );
glVertex3fv( bbox[i+6] );
glVertex3fv( bbox[i*2+0] );
glVertex3fv( bbox[i*2+1] );
glVertex3fv( bbox[i*2+4] );
glVertex3fv( bbox[i*2+5] );
}
glEnd();
}
// draw bones
if( g_viewerSettings.showBones )
{
mstudiobone_t *pbones = (mstudiobone_t *)((byte *) m_pstudiohdr + m_pstudiohdr->boneindex);
glDisable( GL_MULTISAMPLE );
glDisable( GL_TEXTURE_2D );
glDisable( GL_DEPTH_TEST );
glDisable( GL_BLEND );
for( i = 0; i < m_pstudiohdr->numbones; i++ )
{
if( pbones[i].parent >= 0 )
{
glPointSize( 4.0f );
glColor3f( 1.0f, 0.7f, 0.0f );
glBegin( GL_LINES );
glVertex3fv( m_pbonetransform[pbones[i].parent].GetOrigin());
glVertex3fv( m_pbonetransform[i].GetOrigin());
glEnd();
glBegin( GL_POINTS );
if( pbones[pbones[i].parent].parent != -1 )
{
glColor3fv( m_pbonecolor[pbones[i].parent] );
glVertex3fv( m_pbonetransform[pbones[i].parent].GetOrigin());
}
glColor3fv( m_pbonecolor[i] );
glVertex3fv( m_pbonetransform[i].GetOrigin());
glEnd ();
}
else
{
// draw parent bone node
glPointSize( 6.0f );
glColor3f( 0.8f, 0, 0 );
glBegin( GL_POINTS );
glVertex3fv( m_pbonetransform[i].GetOrigin() );
glEnd ();
}
}
glPointSize( 1.0f );
}
if( drawAttachments )
{
glDisable( GL_MULTISAMPLE );
glDisable( GL_TEXTURE_2D );
glDisable( GL_CULL_FACE );
glDisable( GL_DEPTH_TEST );
glDisable( GL_BLEND );
for (i = 0; i < m_pstudiohdr->numattachments; i++)
{
matrix3x4 local, world;
if( drawIndex != -1 && i != drawIndex )
continue;
mstudioattachment_t *pattachments = (mstudioattachment_t *)((byte *) m_pstudiohdr + m_pstudiohdr->attachmentindex);
local.SetForward( pattachments[i].vectors[0] );
local.SetRight( pattachments[i].vectors[1] );
local.SetUp( pattachments[i].vectors[2] );
if( drawIndex != -1 && g_viewerSettings.editMode == EDIT_SOURCE )
local.SetOrigin( m_pedit->origin );
else local.SetOrigin( pattachments[i].org );
world = m_pbonetransform[pattachments[i].bone].ConcatTransforms( local );
// draw the vector from bone to attachment
glBegin( GL_LINES );
glColor3f( 1, 0, 0 );
glVertex3fv( world.GetOrigin() );
glColor3f( 1, 1, 1 );
glVertex3fv( m_pbonetransform[pattachments[i].bone].GetOrigin() );
glEnd();
// draw the matrix axes
if( FBitSet( pattachments[i].flags, STUDIO_ATTACHMENT_LOCAL ))
drawTransform( world );
glPointSize( 5 );
glColor3f( 0, 1, 0 );
glBegin( GL_POINTS );
glVertex3fv( world.GetOrigin() );
glEnd();
glPointSize (1);
}
}
if( drawHitboxes )
{
glDisable( GL_MULTISAMPLE );
glDisable (GL_TEXTURE_2D);
glDisable (GL_CULL_FACE);
glDisable( GL_BLEND );
if (g_viewerSettings.transparency < 1.0f)
glDisable (GL_DEPTH_TEST);
else
glEnable (GL_DEPTH_TEST);
if( colorIndex == -1 )
glColor4f (1, 0, 0, 0.5f);
else glColor4f( hullcolor[colorIndex][0], hullcolor[colorIndex][1], hullcolor[colorIndex][2], 1.0f );
glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (i = 0; i < m_pstudiohdr->numhitboxes; i++)
{
int bone;
if( g_viewerSettings.showHitBoxes && isInEditMode )
{
if( i != drawIndex )
glColor4f( 1.0f, 0.0f, 0.0f, 1.0f );
else glColor4f( 0.0f, 1.0f, 0.0f, 1.0f );
}
else
{
if( drawIndex != -1 && i != drawIndex )
continue;
}
mstudiobbox_t *pbboxes = (mstudiobbox_t *) ((byte *) m_pstudiohdr + m_pstudiohdr->hitboxindex);
Vector v[8], v2[8], bbmin, bbmax;
if( g_viewerSettings.showHitBoxes && isInEditMode && g_viewerSettings.editMode == EDIT_SOURCE )
{
for( int j = 0; j < m_numeditfields; j++ )
{
edit_field_t *ed = &m_editfields[j];
if( ed->type != TYPE_HITBOX || ed->id != i )
continue;
bbmin = ed->mins;
bbmax = ed->maxs;
bone = ed->bone;
break;
}
}
else
{
if( drawIndex != -1 && g_viewerSettings.editMode == EDIT_SOURCE )
{
bbmin = m_pedit->mins;
bbmax = m_pedit->maxs;
bone = m_pedit->bone;
}
else
{
bbmin = pbboxes[i].bbmin;
bbmax = pbboxes[i].bbmax;
bone = pbboxes[i].bone;
}
}
drawTransparentBox( bbmin, bbmax, m_pbonetransform[bone] );
}
}
}
/*
================
================
*/
void StudioModel::DrawPoints ( bool bWireframe )
{
int i, j, k;
mstudiomesh_t *pmesh;
byte *pvertbone;
byte *pnormbone;
Vector *pstudioverts;
Vector *pstudionorms;
mstudioboneweight_t *pvertweight;
mstudioboneweight_t *pnormweight;
mstudiotexture_t *ptexture;
bool texEnabled;
bool need_sort;
matrix3x4 skinMat;
float *av, *nv;
Vector *lv;
Vector lv_tmp;
short *pskinref;
pvertbone = ((byte *)m_pstudiohdr + m_pmodel->vertinfoindex);
pnormbone = ((byte *)m_pstudiohdr + m_pmodel->norminfoindex);
ptexture = (mstudiotexture_t *)((byte *)m_ptexturehdr + m_ptexturehdr->textureindex);
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex);
pstudioverts = (Vector *)((byte *)m_pstudiohdr + m_pmodel->vertindex);
pstudionorms = (Vector *)((byte *)m_pstudiohdr + m_pmodel->normindex);
pskinref = (short *)((byte *)m_ptexturehdr + m_ptexturehdr->skinindex);
if (m_skinnum != 0 && m_skinnum < m_ptexturehdr->numskinfamilies)
pskinref += (m_skinnum * m_ptexturehdr->numskinref);
if( FBitSet( m_pstudiohdr->flags, STUDIO_HAS_BONEWEIGHTS ) && m_pmodel->blendvertinfoindex != 0 && m_pmodel->blendnorminfoindex != 0 )
{
pvertweight = (mstudioboneweight_t *)((byte *)m_pstudiohdr + m_pmodel->blendvertinfoindex);
pnormweight = (mstudioboneweight_t *)((byte *)m_pstudiohdr + m_pmodel->blendnorminfoindex);
for (i = 0; i < m_pmodel->numverts; i++)
{
ComputeSkinMatrix( &pvertweight[i], skinMat );
g_pxformverts[i] = skinMat.VectorTransform( pstudioverts[i] );
}
for (i = 0; i < m_pmodel->numnorms; i++)
{
ComputeSkinMatrix( &pnormweight[i], skinMat );
g_pxformnorms[i] = skinMat.VectorRotate( pstudionorms[i] );
if( g_viewerSettings.renderMode == RM_BONEWEIGHTS )
ComputeWeightColor( &pnormweight[i], g_pvlightvalues[i] );
}
}
else
{
for( i = 0; i < m_pmodel->numverts; i++ )
{
g_pxformverts[i] = m_pbonetransform[pvertbone[i]].VectorTransform( pstudioverts[i] );
}
for( i = 0; i < m_pmodel->numnorms; i++ )
{
g_pxformnorms[i] = m_pbonetransform[pnormbone[i]].VectorRotate( pstudionorms[i] );
}
if( g_viewerSettings.renderMode == RM_BONEWEIGHTS )
{
for( i = 0; i < m_pmodel->numnorms; i++ )
g_pvlightvalues[i] = Vector( 0.0f, 1.0f, 0.0f );
}
}
texEnabled = glIsEnabled( GL_TEXTURE_2D ) ? true : false;
if( bWireframe )
{
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glDisable( GL_TEXTURE_2D );
glColor4f( 1.0f, 0.0f, 0.0f, 0.99f );
glEnable( GL_LINE_SMOOTH );
glEnable( GL_POLYGON_SMOOTH );
glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );
glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );
}
else if (g_viewerSettings.transparency < 1.0f)
{
glEnable (GL_BLEND);
glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask( GL_TRUE );
}
if( bWireframe == false )
glEnable( GL_MULTISAMPLE );
//
// clip and draw all triangles
//
lv = g_pvlightvalues;
need_sort = false;
for( j = k = 0; j < m_pmodel->nummesh; j++ )
{
int flags = ptexture[pskinref[pmesh[j].skinref]].flags;
// fill in sortedmesh info
g_sorted_meshes[j].mesh = &pmesh[j];
g_sorted_meshes[j].flags = flags;
if( FBitSet( flags, STUDIO_NF_MASKED|STUDIO_NF_ADDITIVE ))
need_sort = true;
for( i = 0; i < pmesh[j].numnorms; i++, k++, lv++, pstudionorms++, pnormbone++ )
{
if( g_viewerSettings.renderMode != RM_BONEWEIGHTS )
{
if( FBitSet( m_pstudiohdr->flags, STUDIO_HAS_BONEWEIGHTS ))
Lighting ( *lv, -1, flags, g_pxformnorms[k] );
else Lighting ( *lv, *pnormbone, flags, *pstudionorms );
}
// FIX: move this check out of the inner loop
if (flags & STUDIO_NF_CHROME)
Chrome( g_chrome[k], *pnormbone, *pstudionorms );
}
}
if( need_sort )
{
// resort opaque and translucent meshes draw order
qsort( g_sorted_meshes, m_pmodel->nummesh, sizeof( sortedmesh_t ), MeshCompare );
}
for (j = 0; j < m_pmodel->nummesh; j++)
{
float s, t;
float transparency = g_viewerSettings.transparency;
short *ptricmds;
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex) + j;
pmesh = g_sorted_meshes[j].mesh;
ptricmds = (short *)((byte *)m_pstudiohdr + pmesh->triindex);
s = 1.0/(float)ptexture[pskinref[pmesh->skinref]].width;
t = 1.0/(float)ptexture[pskinref[pmesh->skinref]].height;
if( bWireframe == false )
{
//glBindTexture( GL_TEXTURE_2D, ptexture[pskinref[pmesh->skinref]].index );
glBindTexture( GL_TEXTURE_2D, TEXTURE_COUNT + pskinref[pmesh->skinref] );
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_TWOSIDE)
glDisable( GL_CULL_FACE );
else glEnable( GL_CULL_FACE );
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_MASKED)
{
glEnable( GL_ALPHA_TEST );
glAlphaFunc( GL_GREATER, 0.5f );
}
else glDisable( GL_ALPHA_TEST );
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_ADDITIVE)
{
glEnable( GL_BLEND );
glBlendFunc( GL_ONE, GL_ONE );
// glDepthMask( GL_FALSE );
}
else if (g_viewerSettings.transparency < 1.0f)
{
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// glDepthMask( GL_FALSE );
}
}
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_CHROME)
{
while (i = *(ptricmds++))
{
if (i < 0)
{
glBegin( GL_TRIANGLE_FAN );
i = -i;
}
else
{
glBegin( GL_TRIANGLE_STRIP );
}
if( bWireframe == false )
g_viewerSettings.drawn_polys += (i - 2);
for( ; i > 0; i--, ptricmds += 4)
{
if( bWireframe == false )
{
// FIX: put these in as integer coords, not floats
glTexCoord2f(g_chrome[ptricmds[1]].x * s, g_chrome[ptricmds[1]].y * t);
lv = &g_pvlightvalues[ptricmds[1]];
glColor4f( lv->x, lv->y, lv->z, transparency);
}
av = g_pxformverts[ptricmds[0]];
glVertex3f(av[0], av[1], av[2]);
}
glEnd( );
}
}
else
{
while (i = *(ptricmds++))
{
if (i < 0)
{
glBegin( GL_TRIANGLE_FAN );
i = -i;
}
else
{
glBegin( GL_TRIANGLE_STRIP );
}
if( bWireframe == false )
g_viewerSettings.drawn_polys += (i - 2);
for( ; i > 0; i--, ptricmds += 4)
{
if( bWireframe == false )
{
if( ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_UV_COORDS )
{
glTexCoord2f( HalfToFloat( ptricmds[2] ), HalfToFloat( ptricmds[3] ));
}
else
{
// FIX: put these in as integer coords, not floats
glTexCoord2f(ptricmds[2]*s, ptricmds[3]*t);
}
lv = &g_pvlightvalues[ptricmds[1]];
glColor4f( lv->x, lv->y, lv->z, transparency);
}
av = g_pxformverts[ptricmds[0]];
glVertex3f(av[0], av[1], av[2]);
}
glEnd( );
}
}
if( bWireframe == false )
{
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_MASKED)
glDisable( GL_ALPHA_TEST );
if (ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_ADDITIVE || g_viewerSettings.transparency < 1.0f)
{
glDepthMask( GL_TRUE );
glDisable( GL_BLEND );
}
}
}
if( bWireframe )
{
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
if( texEnabled ) glEnable( GL_TEXTURE_2D );
glDisable( GL_BLEND );
glDisable( GL_LINE_SMOOTH );
glDisable(GL_POLYGON_SMOOTH);
}
else
{
glDisable( GL_MULTISAMPLE );
}
if( g_viewerSettings.showNormals )
{
if( texEnabled ) glDisable( GL_TEXTURE_2D );
glColor4f( 0.3f, 0.4f, 0.5f, 0.99f );
glBegin( GL_LINES );
for (j = 0; j < m_pmodel->nummesh; j++)
{
short *ptricmds;
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex) + j;
ptricmds = (short *)((byte *)m_pstudiohdr + pmesh->triindex);
while( i = *(ptricmds++))
{
for( i = abs( i ); i > 0; i--, ptricmds += 4 )
{
av = g_pxformverts[ptricmds[0]];
nv = g_pxformnorms[ptricmds[1]];
glVertex3f( av[0], av[1], av[2] );
glVertex3f( av[0] + nv[0] * 2.0f, av[1] + nv[1] * 2.0f, av[2] + nv[2] * 2.0f );
}
}
}
glEnd();
if( texEnabled ) glEnable( GL_TEXTURE_2D );
}
}
/*
================
================
*/
void StudioModel::DrawUVMapPoints()
{
int i, j, k;
mstudiomesh_t *pmesh;
byte *pvertbone;
byte *pnormbone;
Vector *pstudioverts;
Vector *pstudionorms;
mstudiotexture_t *ptexture;
short *pskinref_src;
short *pskinref;
pvertbone = ((byte *)m_pstudiohdr + m_pmodel->vertinfoindex);
pnormbone = ((byte *)m_pstudiohdr + m_pmodel->norminfoindex);
ptexture = (mstudiotexture_t *)((byte *)m_ptexturehdr + m_ptexturehdr->textureindex);
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex);
pstudioverts = (Vector *)((byte *)m_pstudiohdr + m_pmodel->vertindex);
pstudionorms = (Vector *)((byte *)m_pstudiohdr + m_pmodel->normindex);
pskinref_src = (short *)((byte *)m_ptexturehdr + m_ptexturehdr->skinindex);
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
glDisable( GL_TEXTURE_2D );
glColor4f( 1.0f, 1.0f, 1.0f, 0.99f );
//
// clip and draw all triangles
//
for (k = 0; k < m_ptexturehdr->numskinfamilies; k++ )
{
// try all the skinfamilies
pskinref = pskinref_src + (k * m_ptexturehdr->numskinref);
for (j = 0; j < m_pmodel->nummesh; j++)
{
float s, t, x, y;
float tex_w, tex_h;
short *ptricmds;
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex) + j;
ptricmds = (short *)((byte *)m_pstudiohdr + pmesh->triindex);
if( pskinref[pmesh->skinref] != g_viewerSettings.texture )
continue;
tex_w = (float)ptexture[pskinref[pmesh->skinref]].width;
tex_h = (float)ptexture[pskinref[pmesh->skinref]].height;
s = 1.0 / tex_w;
t = 1.0 / tex_h;
glBindTexture( GL_TEXTURE_2D, TEXTURE_COUNT + pskinref[pmesh->skinref] );
while (i = *(ptricmds++))
{
if (i < 0)
{
glBegin( GL_TRIANGLE_FAN );
i = -i;
}
else
{
glBegin( GL_TRIANGLE_STRIP );
}
for( ; i > 0; i--, ptricmds += 4)
{
if( ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_UV_COORDS )
{
x = HalfToFloat( ptricmds[2] ) * tex_w;
y = HalfToFloat( ptricmds[3] ) * tex_h;
if( y < 0.0f ) y += tex_h; // OpenGL issues
if(( x < 0.0f || x > tex_w ) || ( y < 0.0f || y > tex_h ))
glColor3f( 1.0f, 0.0f, 0.0f );
else glColor3f( 1.0f, 1.0f, 1.0f );
x = max( 0.0f, min( x, tex_w ));
y = max( 0.0f, min( y, tex_h ));
}
else
{
x = (float)ptricmds[2];
y = (float)ptricmds[3];
}
x *= g_viewerSettings.textureScale;
y *= g_viewerSettings.textureScale;
glVertex2f( offset_x + x, offset_y + y );
}
glEnd( );
}
}
}
glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
glEnable( GL_TEXTURE_2D );
glDisable( GL_BLEND );
}
void StudioModel :: DrawModelUVMap( void )
{
if( !m_pstudiohdr ) return;
// draw UV from all the bodyparts and skinfamilies
for( int i = 0; i < m_pstudiohdr->numbodyparts; i++ )
{
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + i;
for( int j = 0; j < pbodypart->nummodels; j++ )
{
int index = j / pbodypart->base;
index = index % pbodypart->nummodels;
m_pmodel = (mstudiomodel_t *)((byte *)m_pstudiohdr + pbodypart->modelindex) + index;
DrawUVMapPoints();
}
}
}
void StudioModel :: ConvertTexCoords( void )
{
if( !m_pstudiohdr ) return;
// draw UV from all the bodyparts and skinfamilies
for( int i = 0; i < m_pstudiohdr->numbodyparts; i++ )
{
mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + i;
for( int j = 0; j < pbodypart->nummodels; j++ )
{
int index = j / pbodypart->base;
index = index % pbodypart->nummodels;
m_pmodel = (mstudiomodel_t *)((byte *)m_pstudiohdr + pbodypart->modelindex) + index;
mstudiotexture_t *ptexture = (mstudiotexture_t *)((byte *)m_ptexturehdr + m_ptexturehdr->textureindex);
mstudiomesh_t *pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex);
short *pskinref_src = (short *)((byte *)m_ptexturehdr + m_ptexturehdr->skinindex);
for( int k = 0; k < m_ptexturehdr->numskinfamilies; k++ )
{
// try all the skinfamilies
short *pskinref = pskinref_src + (k * m_ptexturehdr->numskinref);
for( int m = 0; m < m_pmodel->nummesh; m++ )
{
short *ptricmds;
pmesh = (mstudiomesh_t *)((byte *)m_pstudiohdr + m_pmodel->meshindex) + m;
ptricmds = (short *)((byte *)m_pstudiohdr + pmesh->triindex);
while( int l = *( ptricmds++ ))
{
for( l = abs( l ); l > 0; l--, ptricmds += 4 )
{
if( ptexture[pskinref[pmesh->skinref]].flags & STUDIO_NF_UV_COORDS )
{
ptricmds[2] = FloatToHalf((float)ptricmds[2] * (1.0f / 32768.0f));
ptricmds[3] = FloatToHalf((float)ptricmds[3] * (1.0f / 32768.0f));
g_viewerSettings.numModelChanges++;
}
}
}
}
}
}
}
}