/*** * * Copyright (c) 1996-2002, 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. * ****/ // updates: // 1-4-99 fixed file texture load and file read bug // 2-8-99 fixed demand loaded sequence bug (thanks to Frans 'Otis' Bouma) //////////////////////////////////////////////////////////////////////// #include #include #include #include #include "mathlib.h" #include "../../public/steam/steamtypes.h" // defines int32, required by studio.h #include "..\..\engine\studio.h" #include "mdlviewer.h" #pragma warning( disable : 4244 ) // double to float //////////////////////////////////////////////////////////////////////// static int g_texnum = 1; void StudioModel::UploadTexture(mstudiotexture_t *ptexture, byte *data, byte *pal) { // unsigned *in, int inwidth, int inheight, unsigned *out, int outwidth, int outheight; int outwidth, outheight; int i, j; int row1[256], row2[256], col1[256], col2[256]; byte *pix1, *pix2, *pix3, *pix4; byte *tex, *out; // convert texture to power of 2 for (outwidth = 1; outwidth < ptexture->width; outwidth <<= 1) ; if (outwidth > 256) outwidth = 256; for (outheight = 1; outheight < ptexture->height; outheight <<= 1) ; if (outheight > 256) outheight = 256; tex = out = (byte *)malloc( outwidth * outheight * 4); for (i = 0; i < outwidth; i++) { col1[i] = (i + 0.25) * (ptexture->width / (float)outwidth); col2[i] = (i + 0.75) * (ptexture->width / (float)outwidth); } for (i = 0; i < outheight; i++) { row1[i] = (int)((i + 0.25) * (ptexture->height / (float)outheight)) * ptexture->width; row2[i] = (int)((i + 0.75) * (ptexture->height / (float)outheight)) * ptexture->width; } // scale down and convert to 32bit RGB for (i=0 ; i>2; out[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; out[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; out[3] = 0xFF; } } glBindTexture( GL_TEXTURE_2D, g_texnum ); glTexImage2D( GL_TEXTURE_2D, 0, 3/*??*/, outwidth, outheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex ); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // ptexture->width = outwidth; // ptexture->height = outheight; ptexture->index = g_texnum; g_texnum++; free( tex ); } studiohdr_t *StudioModel::LoadModel( char *modelname ) { FILE *fp; long size; void *buffer; // load the model if( (fp = fopen( modelname, "rb" )) == NULL) { printf("unable to open %s\n", modelname ); exit(1); } fseek( fp, 0, SEEK_END ); size = ftell( fp ); fseek( fp, 0, SEEK_SET ); buffer = malloc( size ); fread( buffer, size, 1, fp ); int i; byte *pin; studiohdr_t *phdr; mstudiotexture_t *ptexture; pin = (byte *)buffer; phdr = (studiohdr_t *)pin; ptexture = (mstudiotexture_t *)(pin + phdr->textureindex); if (phdr->textureindex != 0) { for (i = 0; i < phdr->numtextures; i++) { // strcpy( name, mod->name ); // strcpy( name, ptexture[i].name ); UploadTexture( &ptexture[i], pin + ptexture[i].index, pin + ptexture[i].width * ptexture[i].height + ptexture[i].index ); } } // UNDONE: free texture memory fclose( fp ); return (studiohdr_t *)buffer; } studioseqhdr_t *StudioModel::LoadDemandSequences( char *modelname ) { FILE *fp; long size; void *buffer; // load the model if( (fp = fopen( modelname, "rb" )) == NULL) { printf("unable to open %s\n", modelname ); exit(1); } fseek( fp, 0, SEEK_END ); size = ftell( fp ); fseek( fp, 0, SEEK_SET ); buffer = malloc( size ); fread( buffer, size, 1, fp ); fclose( fp ); return (studioseqhdr_t *)buffer; } void StudioModel::Init( char *modelname ) { m_pstudiohdr = LoadModel( modelname ); // preload textures if (m_pstudiohdr->numtextures == 0) { char texturename[256]; strcpy( texturename, modelname ); strcpy( &texturename[strlen(texturename) - 4], "T.mdl" ); m_ptexturehdr = LoadModel( texturename ); } else { m_ptexturehdr = m_pstudiohdr; } // preload animations if (m_pstudiohdr->numseqgroups > 1) { for (int i = 1; i < m_pstudiohdr->numseqgroups; i++) { char seqgroupname[256]; strcpy( seqgroupname, modelname ); sprintf( &seqgroupname[strlen(seqgroupname) - 4], "%02d.mdl", i ); m_panimhdr[i] = LoadDemandSequences( seqgroupname ); } } } //////////////////////////////////////////////////////////////////////// int StudioModel::GetSequence( ) { return m_sequence; } int StudioModel::SetSequence( int iSequence ) { if (iSequence > m_pstudiohdr->numseq) iSequence = 0; if (iSequence < 0) iSequence = m_pstudiohdr->numseq-1; m_sequence = iSequence; m_frame = 0; return m_sequence; } void StudioModel::ExtractBbox( float *mins, float *maxs ) { mstudioseqdesc_t *pseqdesc; pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex); mins[0] = pseqdesc[ m_sequence ].bbmin[0]; mins[1] = pseqdesc[ m_sequence ].bbmin[1]; mins[2] = pseqdesc[ m_sequence ].bbmin[2]; maxs[0] = pseqdesc[ m_sequence ].bbmax[0]; maxs[1] = pseqdesc[ m_sequence ].bbmax[1]; maxs[2] = pseqdesc[ m_sequence ].bbmax[2]; } void StudioModel::GetSequenceInfo( float *pflFrameRate, float *pflGroundSpeed ) { mstudioseqdesc_t *pseqdesc; pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence; if (pseqdesc->numframes > 1) { *pflFrameRate = 256 * pseqdesc->fps / (pseqdesc->numframes - 1); *pflGroundSpeed = sqrt( pseqdesc->linearmovement[0]*pseqdesc->linearmovement[0]+ pseqdesc->linearmovement[1]*pseqdesc->linearmovement[1]+ pseqdesc->linearmovement[2]*pseqdesc->linearmovement[2] ); *pflGroundSpeed = *pflGroundSpeed * pseqdesc->fps / (pseqdesc->numframes - 1); } else { *pflFrameRate = 256.0; *pflGroundSpeed = 0.0; } } float StudioModel::SetController( int iController, float flValue ) { int i; mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex); // find first controller that matches the index for (i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++) { if (pbonecontroller->index == iController) break; } if (i >= m_pstudiohdr->numbonecontrollers) return flValue; // wrap 0..360 if it's a rotational controller if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) { // ugly hack, invert value if end < start if (pbonecontroller->end < pbonecontroller->start) flValue = -flValue; // does the controller not wrap? if (pbonecontroller->start + 359.0 >= pbonecontroller->end) { if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) flValue = flValue - 360; if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) flValue = flValue + 360; } else { if (flValue > 360) flValue = flValue - (int)(flValue / 360.0) * 360.0; else if (flValue < 0) flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; } } int setting = 255 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); if (setting < 0) setting = 0; if (setting > 255) setting = 255; m_controller[iController] = setting; return setting * (1.0 / 255.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; } float StudioModel::SetMouth( float flValue ) { mstudiobonecontroller_t *pbonecontroller = (mstudiobonecontroller_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bonecontrollerindex); // find first controller that matches the mouth for (int i = 0; i < m_pstudiohdr->numbonecontrollers; i++, pbonecontroller++) { if (pbonecontroller->index == 4) break; } // wrap 0..360 if it's a rotational controller if (pbonecontroller->type & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) { // ugly hack, invert value if end < start if (pbonecontroller->end < pbonecontroller->start) flValue = -flValue; // does the controller not wrap? if (pbonecontroller->start + 359.0 >= pbonecontroller->end) { if (flValue > ((pbonecontroller->start + pbonecontroller->end) / 2.0) + 180) flValue = flValue - 360; if (flValue < ((pbonecontroller->start + pbonecontroller->end) / 2.0) - 180) flValue = flValue + 360; } else { if (flValue > 360) flValue = flValue - (int)(flValue / 360.0) * 360.0; else if (flValue < 0) flValue = flValue + (int)((flValue / -360.0) + 1) * 360.0; } } int setting = 64 * (flValue - pbonecontroller->start) / (pbonecontroller->end - pbonecontroller->start); if (setting < 0) setting = 0; if (setting > 64) setting = 64; m_mouth = setting; return setting * (1.0 / 64.0) * (pbonecontroller->end - pbonecontroller->start) + pbonecontroller->start; } float StudioModel::SetBlending( int iBlender, float flValue ) { mstudioseqdesc_t *pseqdesc; pseqdesc = (mstudioseqdesc_t *)((byte *)m_pstudiohdr + m_pstudiohdr->seqindex) + (int)m_sequence; if (pseqdesc->blendtype[iBlender] == 0) return flValue; if (pseqdesc->blendtype[iBlender] & (STUDIO_XR | STUDIO_YR | STUDIO_ZR)) { // ugly hack, invert value if end < start if (pseqdesc->blendend[iBlender] < pseqdesc->blendstart[iBlender]) flValue = -flValue; // does the controller not wrap? if (pseqdesc->blendstart[iBlender] + 359.0 >= pseqdesc->blendend[iBlender]) { if (flValue > ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) + 180) flValue = flValue - 360; if (flValue < ((pseqdesc->blendstart[iBlender] + pseqdesc->blendend[iBlender]) / 2.0) - 180) flValue = flValue + 360; } } int setting = 255 * (flValue - pseqdesc->blendstart[iBlender]) / (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]); if (setting < 0) setting = 0; if (setting > 255) setting = 255; m_blending[iBlender] = setting; return setting * (1.0 / 255.0) * (pseqdesc->blendend[iBlender] - pseqdesc->blendstart[iBlender]) + pseqdesc->blendstart[iBlender]; } int StudioModel::SetBodygroup( int iGroup, int iValue ) { if (iGroup > m_pstudiohdr->numbodyparts) return -1; mstudiobodyparts_t *pbodypart = (mstudiobodyparts_t *)((byte *)m_pstudiohdr + m_pstudiohdr->bodypartindex) + iGroup; int iCurrent = (m_bodynum / pbodypart->base) % pbodypart->nummodels; if (iValue >= pbodypart->nummodels) return iCurrent; m_bodynum = (m_bodynum - (iCurrent * pbodypart->base) + (iValue * pbodypart->base)); return iValue; } int StudioModel::SetSkin( int iValue ) { if (iValue < m_pstudiohdr->numskinfamilies) { return m_skinnum; } m_skinnum = iValue; return iValue; }