md3view/md3view.cpp

986 lines
26 KiB
C++
Executable File

/*
Copyright (C) 2010 Matthew Baranowski, Sander van Rossen & Raven software.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "system.h"
#include "ndictionary.h"
#include "md3gl.h"
#include "md3view.h"
#include "animation.h"
#include "text.h"
#include "oddbits.h"
MDViewState mdview;
void draw_stats();
/*
renders the view which so far is just a model frame
*/
void render_mdview()
{
//draw_view();
draw_viewSkeleton();
draw_stats();
}
void reset_viewpos(void)
{
mdview.xPos = mdview.yPos = 0.0f;
mdview.zPos = -2.f;
mdview.rotAngleX = mdview.rotAngleY = 0;
mdview.rotAngleZ = -90.0f;
}
/*
initializes the viewer global state data
*/
void init_mdview(const char* lpcommandline)
{
mdview.dFOV = 90.0f;
mdview.iLODLevel = 0;
mdview.iSkinNumber = 0;
mdview.bAxisView = false;
reset_viewpos();
mdview.animSpeed = 0.1; // so 1/this = 10 = 10FPS
mdview.timeStamp1 = getDoubleTime();
mdview.texMode = TEX_FILTERED;
mdview.faceSide = GL_CCW;
mdview.animate = false;
mdview.interpolate = true;
mdview.bUseAlpha = false; // default this to OFF since artists keep making 32 bit textures with no alpha, then complain about pieces missing!!!!
mdview.bBBox = false;
mdview._R = mdview._G = mdview._B = 256/5; // dark grey
mdview.bAnimCFGLoaded = false;
mdview.bAnimIsMultiPlayerFormat = false;
// allocate texture resource manager
mdview.textureRes = new NodeDictionaryInfo( new StrKeyComparatorInfo() );
mdview.topTextureBind = 0;
// allocate model list
mdview.modelList = new NodeSequenceInfo();
// strcpy( mdview.basepath, "/");
if ( lpcommandline != NULL && lpcommandline[0] != '\0' ) { //got an initial dir
char szDirName[256]={0};
strcpy (szDirName, lpcommandline );
char *p = strrchr(szDirName,'\\'); //back up off the filename.
if (p) {
*p=0;
}
SetCurrentDirectory( szDirName); //this will make future open dialogs in the same dir as the command line file
strcpy( mdview.basepath, szDirName);
} else {
#if 1
strcpy( mdview.basepath, "w:\\game\\base\\models\\"); // this is just to default the 1st use of OpenFileDialog, and doesn't affect anything else once the app gets going
SetCurrentDirectory( mdview.basepath );
#else
char szDirName[256]={0};
GetModuleFileName(NULL, szDirName, 256);
char *p = strrchr(szDirName,'\\'); //back up off the filename.
if (p) {
*p=0;
}
SetCurrentDirectory( szDirName );
strcpy( mdview.basepath, szDirName);
#endif
}
}
void delete_gl_model( gl_model *model );
bool loadmdx(gl_model& newModel, char* filename);
/* ------------------------------------------------ loading code -------------------------------------------------- */
/*
loads up md3 data
*/
int giTagMenuSubtractValue_Torso;
int giTagMenuSubtractValue_Head;
int giTagMenuSubtractValue_Weapon; // this shit gets worse by the minute...
int giTagMenuSubtractValue_Barrel;
int giTagMenuSubtractValue_Barrel2;
int giTagMenuSubtractValue_Barrel3;
int giTagMenuSubtractValue_Barrel4;
gl_model* pLastLoadedModel;
// if psLOD0Filename != NULL, then we're loading an MD3 LOD model, and psLOD0Filename is a pointer to original name
//
bool loadmdl_original( char *filename, LPCSTR psLOD0Filename, gl_model* pLOD1, gl_model* pLOD2 )
{
gl_model* model = new gl_model; memset( model, 0, sizeof(gl_model) );
if (pLOD1)
{
model->pModel_LOD1 = pLOD1;
pLOD1->pModel_LOD0 = model;
}
if (pLOD2)
{
model->pModel_LOD2 = pLOD2;
pLOD2->pModel_LOD0 = model;
}
// if there's a default skin for this model, don't try and load the internal shaders, this is just so
// we don't get bugged by loads of file-missing errors if the model is only designed to use skin files...
//
bool bDefaultSkinExists = file_exists(SkinName_FromPathedModelName(filename));
if (bDefaultSkinExists)
{
gbIgnoreTextureLoad = true;
}
if (!loadmdx( *model, filename ))
{
gbIgnoreTextureLoad = false;
delete_gl_model( model );model = NULL;
return false;
}
gbIgnoreTextureLoad = false;
pLastLoadedModel = model;
strcpy( model->sHeadSkinName,"default");
strcpy( model->sMDXFullPathname, filename);
strcpy( model->sMD3BaseName,Filename_WithoutPath(
// Filename_WithoutExt(
filename
// )
)
);
if (psLOD0Filename)
return true; // don't bother with the rest of this stuff now...
giTagMenuSubtractValue_Torso =
giTagMenuSubtractValue_Head =
giTagMenuSubtractValue_Weapon =
giTagMenuSubtractValue_Barrel =
giTagMenuSubtractValue_Barrel2=
giTagMenuSubtractValue_Barrel3=
giTagMenuSubtractValue_Barrel4= -1;
Model_ApplySkin(model, SkinName_FromPathedModelName(model->sMDXFullPathname));
model->modelListPosition = mdview.modelList->insertLast( model );
mdview.baseModel = (gl_model *)mdview.modelList->first()->element();
tagMenu_seperatorAppend( filename );
for (unsigned int i=0 ; i<model->iNumTags ; i++)
{
tagMenu_append( model->tags[0][i].Name, (GLMODEL_DBLPTR)&model->linkedModels[i] );
if (!stricmp(model->tags[0][i].Name,"tag_torso"))
{
giTagMenuSubtractValue_Torso = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_head"))
{
giTagMenuSubtractValue_Head = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_weapon"))
{
giTagMenuSubtractValue_Weapon = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_barrel"))
{
giTagMenuSubtractValue_Barrel = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_barrel2"))
{
giTagMenuSubtractValue_Barrel2 = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_barrel3"))
{
giTagMenuSubtractValue_Barrel3 = i;
}
else
if (!stricmp(model->tags[0][i].Name,"tag_barrel4"))
{
giTagMenuSubtractValue_Barrel4 = i;
}
}
// arrrghhh!!!!...
//
giTagMenuSubtractValue_Torso = model->iNumTags - giTagMenuSubtractValue_Torso;
giTagMenuSubtractValue_Head = model->iNumTags - giTagMenuSubtractValue_Head;
giTagMenuSubtractValue_Weapon = (giTagMenuSubtractValue_Weapon ==-1)?-1:model->iNumTags - giTagMenuSubtractValue_Weapon;
giTagMenuSubtractValue_Barrel = (giTagMenuSubtractValue_Barrel ==-1)?-1:model->iNumTags - giTagMenuSubtractValue_Barrel;
giTagMenuSubtractValue_Barrel2 = (giTagMenuSubtractValue_Barrel2==-1)?-1:model->iNumTags - giTagMenuSubtractValue_Barrel2;
giTagMenuSubtractValue_Barrel3 = (giTagMenuSubtractValue_Barrel3==-1)?-1:model->iNumTags - giTagMenuSubtractValue_Barrel3;
giTagMenuSubtractValue_Barrel4 = (giTagMenuSubtractValue_Barrel4==-1)?-1:model->iNumTags - giTagMenuSubtractValue_Barrel4;
//
// this value is now the value (if NZ) to sub from the menu count (then add ID_TAG_START) to reach "tag_torso"ID of the menu item
//
SetFaceSkin(0); // safety to always do this
return true;
}
// this now auto-loads LOD models for MD3s...
//
bool loadmdl( char *filename )
{
gl_model* pModel_LOD1 = NULL;
gl_model* pModel_LOD2 = NULL;
// new stuff, if it's an MD3, try loading in LOD versions of it as well, but do them first, so that
// all the goofy shit to do with tagmenu stuff is still left in valid state...
//
LPCSTR psMD3EXT = strstr(String_ToLower(filename),".md3");
if (psMD3EXT)
{
char sNewFilename[MAX_PATH];
strcpy(sNewFilename,Filename_WithoutExt(filename));
strcat(sNewFilename,"_1.md3");
if (loadmdl_original(sNewFilename, filename, NULL, NULL))
pModel_LOD1 = pLastLoadedModel;
strcpy(sNewFilename,Filename_WithoutExt(filename));
strcat(sNewFilename,"_2.md3");
if (loadmdl_original(sNewFilename, filename, NULL, NULL))
pModel_LOD2 = pLastLoadedModel;
}
// finally, load the model we were originally asking for...
//
return loadmdl_original(filename, NULL, pModel_LOD1, pModel_LOD2);
}
/*
loads a model into the slot pointed to by dblptr
*/
void loadmdl_totag( char *fullName, GLMODEL_DBLPTR dblptr )
{
gl_model **m_dblptr = (gl_model **)dblptr;
gl_model *newmodel;
NodePosition pos;
if (loadmdl( fullName )) {
pos = mdview.modelList->last();
newmodel = (gl_model *)pos->element();
*m_dblptr = newmodel;
}
}
/*
loads a new skin
*/
void importNewSkin( char *fullName )
{
/*
GLuint newTexBind = loadTexture( fullName );
NodePosition pos;
gl_model *model;
gl_mesh *mesh;
int iNumMeshes,i;
unsigned int j;
for (pos=mdview.modelList->first() ; pos!=NULL ; pos=mdview.modelList->after(pos)) {
model = (gl_model *)pos->element();
iNumMeshes = model->iNumMeshes;
for (i=0 ; i<iNumMeshes ; i++) {
mesh = &model->meshes[i];
for (j=0 ; j<mesh->skinNum ; j++) {
mesh->bindings[j] = newTexBind;
}
}
}
*/
ApplyNewSkin(fullName);
}
bool ParseSequenceLockFile(LPCSTR psFilename)
{
#define UL_BAD 0
#define UL_UPPER 1
#define UL_LOWER 2
int iUpperOrLower = UL_BAD;
FILE *fHandle = fopen(psFilename,"rt");
if (fHandle)
{
// file is text file,
// first line is "upper" or "lower" (ignore case), then sequence lock names till empty line or EOF
static char sLineBuffer[2048];
int iMembers = 0;
MultiSequenceLock_t MultiSequenceLock;
MultiSequenceLock.clear();
while (1)
{
ZEROMEM(sLineBuffer);
if (!fgets(sLineBuffer,sizeof(sLineBuffer),fHandle))
{
if (ferror(fHandle))
{
ErrorBox(va("Error while reading \"%s\"!",psFilename));
iUpperOrLower = UL_BAD;
}
break; // whether error or EOF
}
// zap any comments...
//
char *p = strstr(sLineBuffer,"//");
if (p)
*p=0;
strcpy(sLineBuffer,String_ToUpper(String_LoseWhitespace(sLineBuffer)));
if (strlen(sLineBuffer))
{
// analyse this line...
//
if (!iMembers)
{
if (strnicmp(sLineBuffer,"upper",5)==0)
{
iUpperOrLower = UL_UPPER;
}
else
if (strnicmp(sLineBuffer,"lower",5)==0)
{
iUpperOrLower = UL_LOWER;
}
else
{
ErrorBox("First line of file must be \"upper\" or \"lower\"!");
iUpperOrLower = UL_BAD;
break;
}
}
else
{
// is this string a valid sequence for upper/lower?...
//
int iCount = (iUpperOrLower == UL_UPPER)?Animation_GetNumUpperSequences():Animation_GetNumLowerSequences();
int iScanIndex;
for (iScanIndex=0; iScanIndex<iCount; iScanIndex++)
{
Sequence_t* pSeq = (iUpperOrLower == UL_UPPER)?Animation_GetUpperSequence(iScanIndex):Animation_GetLowerSequence(iScanIndex);
if (strcmp(pSeq->sName.c_str(),sLineBuffer)==0)
{
// found this line, it's valid, so keep it...
//
MultiSequenceLock.push_back(iScanIndex);
break;
}
}
if (iScanIndex==iCount)
{
ErrorBox(va("Couldn't find specified sequence \"%s\" for model \"%s\"",sLineBuffer,(iUpperOrLower == UL_UPPER)?"UPPER":"LOWER"));
iUpperOrLower = UL_BAD;
break;
}
}
iMembers++;
}
}
if (iUpperOrLower != UL_BAD // avoid giving an error because of an existing error
&& iMembers == 1) // ie seq file only contains "upper" or "lower"
{
ErrorBox("No enum entries found in file");
iUpperOrLower = UL_BAD;
}
switch (iUpperOrLower)
{
case UL_BAD:
// do nothing
//
break;
case UL_UPPER:
MultiSequenceLock_Upper.clear();
MultiSequenceLock_Upper = MultiSequenceLock;
iAnimLockNumber_Upper = -1;
break;
case UL_LOWER:
MultiSequenceLock_Lower.clear();
MultiSequenceLock_Lower = MultiSequenceLock;
iAnimLockNumber_Lower = -1;
break;
default: // I must have forgotten some new type
assert(0);
break;
}
fclose(fHandle);
}
else
{
ErrorBox(va("Error opening file \"%s\"",psFilename));
}
return (iUpperOrLower != UL_BAD);
}
/* ------------------------------------------ delete code ------------------------------------------------------- */
/*
frees all mdviewdata, and effectively resets the data state back to init
*/
void free_mdviewdata()
{
NodePosition pos;
gl_model *model;
/*
for(pos = mdview.modelList->first() ; pos!=NULL ; pos=next) {
model = (gl_model *)pos->element();
// takes care of removing model from the list and everything, so assume pos is invalid
delete_gl_model( model );
// get next after cuz the list might have been changed by delete_gl_model
next = mdview.modelList->after(pos);
}
*/
if (!mdview.modelList->isEmpty()) {
do {
pos = mdview.modelList->first();
model = (gl_model *)pos->element();
delete_gl_model( model ); // this removes its own position from the list
} while (mdview.modelList->first()!=NULL);
}
// if there is anything else left in texture res remove,
// though deleting all gl_models in list should make it empty
freeTextureRes();
ClearAnimationCFG();
}
/*
frees a model that has been loaded before by loadmdl_totag into the modelptr slot
*/
void freemdl_fromtag( GLMODEL_DBLPTR modelptr )
{
gl_model **m_dblptr = (gl_model **)modelptr;
gl_model *model = *m_dblptr;
if (!model) return;
delete_gl_model( model );
*m_dblptr = NULL;
}
/*
frees data as for shutdown, call this only as the last thing before exiting or bad thigns will happen
*/
void shutdown_mdviewdata()
{
free_mdviewdata();
delete mdview.modelList; mdview.modelList = NULL;
delete mdview.textureRes; mdview.textureRes = NULL;
}
int iTextX;
int iTextY;
int giTotVerts;
int giTotTris;
int giModelsStatted;
void stat_gl_model( gl_model *__model, char *psAttachedVia )
{
#define ARB_NAME_PADDING 26
#define ARB_LOD_PADDING 9
#define ARB_ATTACHNAME_PADDING 24
#define ARB_VERTINFO_PADDING 15
gl_model* model = __model;
if (model)
{
giModelsStatted++;
if (model->modelType == MODTYPE_MD3)
{
switch (mdview.iLODLevel)
{
case 0: break;
case 1: model = model->pModel_LOD1; break;
case 2: model = model->pModel_LOD2; break;
}
if (!model)
{
Text_DisplayFlat( va("%s", String_EnsureMinLength("( LOD model missing )",ARB_NAME_PADDING)),
iTextX, iTextY,
0, 255,0, // RGB
false
);
iTextY += TEXT_DEPTH;
return;
}
}
}
int iNextX = 0;
if (model)
{
iNextX =
Text_DisplayFlat( va("%s Frame %4d/%4d",
String_EnsureMinLength(va("\"%s\"",model->sMD3BaseName),ARB_NAME_PADDING),
model->currentFrame, model->iNumFrames),
iTextX, iTextY,
0, 255,0, // RGB
false
);
}
else
{
iTextY += TEXT_DEPTH; // seperate totals a bit
iNextX =
// va("%s Frame 1234/1234"
Text_DisplayFlat( va("%s ", String_EnsureMinLength("( totals )",ARB_NAME_PADDING)),
iTextX, iTextY,
0, 255,0, // RGB
false
);
}
if (model)
{
// LODs info...
//
if (model->modelType == MODTYPE_MDR)
{
md4LOD_t *pLOD = (md4LOD_t *) ((byte *)model->Q3ModelStuff.md4 + model->Q3ModelStuff.md4->ofsLODs);
int iWhichLod = mdview.iLODLevel;
if (iWhichLod >= model->Q3ModelStuff.md4->numLODs)
iWhichLod = model->Q3ModelStuff.md4->numLODs-1;
iNextX = Text_DisplayFlat( String_EnsureMinLength(va("(LOD %1d/%1d)",iWhichLod+1,model->Q3ModelStuff.md4->numLODs),ARB_LOD_PADDING),
iNextX+(2*TEXT_WIDTH), iTextY,
255/2,255,255/2, // RGB
false
);
}
else
{
iNextX = Text_DisplayFlat( String_EnsureMinLength(va("(LOD %1d)",mdview.iLODLevel+1),ARB_LOD_PADDING),
iNextX+(2*TEXT_WIDTH), iTextY,
255/2,255,255/2, // RGB
false
);
}
}
else
{
iNextX = Text_DisplayFlat( String_EnsureMinLength("",ARB_LOD_PADDING),
iNextX+(2*TEXT_WIDTH), iTextY,
255/2,255,255/2, // RGB
false
);
}
if (model)
{
// Verts/tris info...
//
iNextX =
Text_DisplayFlat( String_EnsureMinLength(
va("(V:%4d T:%4d)",model->iRenderedVerts,model->iRenderedTris),
ARB_VERTINFO_PADDING
),
iNextX+(2*TEXT_WIDTH), iTextY,
255, 255/2, 255/2, // RGB (pink)
false
);
giTotVerts+= model->iRenderedVerts;
giTotTris += model->iRenderedTris;
}
else
{
// Verts/tris info...
//
iNextX =
Text_DisplayFlat( String_EnsureMinLength(
va("(V:%4d T:%4d)",giTotVerts,giTotTris),
ARB_VERTINFO_PADDING
),
iNextX+(2*TEXT_WIDTH), iTextY,
255, 255/2, 255/2, // RGB (pink)
false
);
}
if (model)
{
// attached-via info...
//
iNextX =
Text_DisplayFlat( String_EnsureMinLength(
va("%s", psAttachedVia?va("(attached: \"%s\")",psAttachedVia):""),
ARB_ATTACHNAME_PADDING
),
iNextX+(2*TEXT_WIDTH), iTextY,
0, 255/2,0, // RGB
false
);
// print either the locked local frame number in red (if anim locking is on), or just the anim sequence name
// if there's one corresponding to this...
//
Sequence_t* pSeq = NULL;
bool bIsUpper = false;
if ((model == pModel_Upper || model->pModel_LOD0 == pModel_Upper ) && iAnimLockNumber_Upper)
{
pSeq = Animation_GetUpperSequence( iAnimLockNumber_Upper-1 );
bIsUpper = true;
}
if ((model == pModel_Lower || model->pModel_LOD0 == pModel_Lower ) && iAnimLockNumber_Lower)
{
pSeq = Animation_GetLowerSequence( iAnimLockNumber_Lower-1 );
bIsUpper = false;
}
if (pSeq)
{
bool bWasMulti = false;
if (pSeq->bMultiSeq)
{
pSeq = GetMultiLockedSequenceFromFrame(model->currentFrame, bIsUpper );
bWasMulti = true;
}
if (pSeq)
{
iNextX = Text_DisplayFlat( va("%d/%d",model->currentFrame-pSeq->iTargetFrame,pSeq->iFrameCount),
iNextX+(2*TEXT_WIDTH), iTextY,
255, // R
0, // G
bWasMulti?255:0, // B // multi displays as purple
false
);
}
}
//else
if (!pSeq)
{
if ((model == pModel_Upper || model->pModel_LOD0 == pModel_Upper ) && !iAnimLockNumber_Upper)
{
pSeq = Animation_FromUpperFrame( model->currentFrame );
}
if ((model == pModel_Lower || model->pModel_LOD0 == pModel_Lower ) && !iAnimLockNumber_Lower)
{
pSeq = Animation_FromLowerFrame( model->currentFrame );
}
if (pSeq)
{
iNextX = Text_DisplayFlat( va("%s",pSeq->sName.c_str()), iNextX + (2*TEXT_WIDTH), iTextY, 0,200,200, true); // dim cyan
}
}
}
iTextY += TEXT_DEPTH;
}
void stat_skeleton( gl_model *model, char *psAttachedVia )
{
if (psAttachedVia == NULL)
{
// first time, so zap totals...
//
giTotVerts = 0;
giTotTris = 0;
giModelsStatted = 0;
}
stat_gl_model( model, psAttachedVia );
unsigned int sf=model->currentFrame, ef=model->currentFrame+1;
// draw all the attached child models
for (UINT j=0; j<model->iNumTags ; j++)
{
gl_model *child = model->linkedModels[j];
if (child)
{
Tag *tag = &model->tags[sf][j];
stat_skeleton( child, tag->Name );
}
}
if (psAttachedVia == NULL)
{
// done, print totals...
//
if (giModelsStatted>1) // no point if only 1 printed
stat_gl_model( NULL, NULL );
}
}
void draw_stats()
{
if (mdview.baseModel)
{
iTextX = 2*TEXT_WIDTH; // arb start pos 2 in from both edges
iTextY = 4*TEXT_DEPTH; // ... or 4... :-)
// // Displays text at a 2d screen coord. 0,0 is top left corner, add TEXT_DEPTH per Y to go down a line
//
// Text_DisplayFlat("testing testing HELLO!!", 100,100, 0, 255,0, true);
//
// Vec3 v={0,0,0};
// Text_Display("testing testing HELLO!!", v, 255,0,0);
//
//
gl_model *model = mdview.baseModel;
stat_skeleton( model, NULL );
// anim locks on the whole model?, if so display 'em...
//
char sString[1024];
for (int i=0; i<2; i++)
{
// display anim locks at bottom of screen...
//
Sequence_t* pSeq=NULL;
bool bIsUpper = false;
if (i==0 && iAnimLockNumber_Upper )
{
pSeq = Animation_GetUpperSequence( iAnimLockNumber_Upper-1 );
bIsUpper = true;
}
if (i==1 && iAnimLockNumber_Lower )
{
pSeq = Animation_GetLowerSequence( iAnimLockNumber_Lower-1 );
bIsUpper = false;
}
if (pSeq)
{
int iYpos = g_iScreenHeight-((!i?4:3)*TEXT_DEPTH);
// cheat, do this next bit here just to get the X coord, then overwrite later
sprintf(sString,"%s anim lock: %s Frames: %4d...%4d%s",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25));
int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH);
if (pSeq->bMultiSeq)
{
Sequence_t *pCurrentMultiSeq = GetMultiLockedSequenceFromFrame(bIsUpper?pModel_Upper->currentFrame:pModel_Lower->currentFrame, bIsUpper );
int iStrlenAtCurrentSeqPoint = 0;
sprintf(sString,"%s anim lock: ",bIsUpper?"Upper":"Lower");
MultiSequenceLock_t* pMultiLock = (bIsUpper)?&MultiSequenceLock_Upper:&MultiSequenceLock_Lower;
MultiSequenceLock_t::iterator it;
for (it = pMultiLock->begin(); it != pMultiLock->end(); ++it)
{
int iSeqIndex = *it;
pSeq = (bIsUpper)?Animation_GetUpperSequence(iSeqIndex):Animation_GetLowerSequence(iSeqIndex);
assert(pSeq);
if (pSeq)
{
if (pSeq == pCurrentMultiSeq)
iStrlenAtCurrentSeqPoint = strlen(sString);
strcat(sString,va("%s ",pSeq->sName.c_str()));
}
}
// int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH);
Text_DisplayFlat(sString, iXpos, iYpos, 255,0,0, true);
// now overlay the highlighted one for current...
//
if (pCurrentMultiSeq)
{
iXpos += iStrlenAtCurrentSeqPoint*TEXT_WIDTH;
Text_DisplayFlat(pCurrentMultiSeq->sName.c_str(), iXpos, iYpos, 255,0,255, true);
}
}
else
{
sprintf(sString,"%s anim lock: %s Frames: %4d...%4d%s",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25));
// int iXpos = (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH);
Text_DisplayFlat(sString, iXpos, iYpos, 255,0,0, true);
}
}
if (RunningNT() == 4) // only needed on NT4, NT 2000 and W95/98 are ok
{
pSeq = NULL;
if (i==0 && iAnimDisplayNumber_Upper)
pSeq = Animation_GetUpperSequence( iAnimDisplayNumber_Upper-1);
if (i==1 && iAnimDisplayNumber_Lower)
pSeq = Animation_GetLowerSequence( iAnimDisplayNumber_Lower-1);
if (pSeq)
{
if (pSeq->bMultiSeq)
{
assert(0); // should never be able to get here in the animlock display-cycler
}
else
{
sprintf(sString,"( %s anim : %s Frames: %4d...%4d%s )",!i?"Upper":"Lower",String_EnsureMinLength(pSeq->sName.c_str(),iAnimLockLongestString),pSeq->iTargetFrame,(pSeq->iTargetFrame+pSeq->iFrameCount)-1,String_EnsureMinLength((pSeq->iLoopFrame==-1)?"":va(" loop: %3d(%3d)",pSeq->iLoopFrame,pSeq->iTargetFrame+pSeq->iLoopFrame),25));
int iXpos = (g_iScreenWidth/2)-( ((strlen(sString)/2)/*+2*/)*TEXT_WIDTH); // 2 chars back from LOCk string, because of bracket+space at start
int iYpos = g_iScreenHeight-((!i?7:6)*TEXT_DEPTH);
Text_DisplayFlat(sString, iXpos, iYpos, 0,200,200, true); // dim yellow
}
}
}
}
// display current FPS and interp state...
//
sprintf(sString,"FPS: %2.2f %s",1/(mdview.animSpeed),mdview.animate?"(Playing)":"(Stopped)");
iTextX =
Text_DisplayFlat(sString, (g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH),
1*TEXT_DEPTH,
255,255,255,
false
);
if (mdview.interpolate)
{
iTextX = Text_DisplayFlat("(Interpolated)", iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 255/2,255/2,255/2,false);
}
iTextX = Text_DisplayFlat(va("(LOD: %d)",mdview.iLODLevel+1), iTextX+(2*TEXT_WIDTH), 1*TEXT_DEPTH, 255/2,255,255/2,false);
/* Text_DisplayFlat(sString, g_iScreenWidth-((strlen(sString)+2)*TEXT_WIDTH),
2 *TEXT_DEPTH,
255,255,255,
false
);*/
iTextX = Text_DisplayFlat(va("( FOV: %g )",mdview.dFOV), iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 255, 255, 255, false);
if (mdview.bUseAlpha)
{
Text_DisplayFlat("( Alpha )", iTextX+(2*TEXT_WIDTH),1*TEXT_DEPTH, 128, 128, 128, false);
}
// display head skin numbers in top left...
//
gl_model* pModel = R_FindModel( mdview.baseModel, "head");
if (pModel)
{
sprintf(sString,"Head skin: %s", va("\"%s%s\"",model->sHeadSkinName,!mdview.iSkinNumber?"":va("-%1d",mdview.iSkinNumber)));
Text_DisplayFlat(sString, 2*TEXT_WIDTH,//(g_iScreenWidth/2)-( (strlen(sString)/2)*TEXT_WIDTH),
1*TEXT_DEPTH,
200,200,200,
false
);
}
// display which format anim table this is using (if any)...
//
if (mdview.bAnimCFGLoaded)
{
if (mdview.bAnimIsMultiPlayerFormat)
sprintf(sString,"( MultiPlayer ) ");
else
sprintf(sString,"( SinglePlayer ) ");
int iYpos = g_iScreenHeight-(2*TEXT_DEPTH);
int iXpos = g_iScreenWidth -(strlen(sString)*TEXT_WIDTH);
Text_DisplayFlat(sString, iXpos, iYpos, 128,128,128, true); // grey
}
// display current picmip state...
//
{
extern int TextureList_GetMip(void);
int iYpos = g_iScreenHeight-(2*TEXT_DEPTH);
int iXpos = 1*TEXT_WIDTH;
sprintf(sString,"( PICMIP: %d )",TextureList_GetMip());
Text_DisplayFlat(sString,
iXpos, iYpos,
100,100,100,
false
);
}
}
}