/*** * * 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 #include #include #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++; } } } } } } } }