void CModel::DeleteSequence(CSequence* deleteSequence) { // linklist is only 1-way, so we need to find the stage previous to this (if any)... CSequence* prevSequence = NULL; CSequence* scanSequence = GetFirstSequence(); while (scanSequence && scanSequence != deleteSequence) { prevSequence = scanSequence; scanSequence = scanSequence->GetNext(); } if (scanSequence == deleteSequence) { // we found it, so was this the first sequence in the list? if (prevSequence) { prevSequence->SetNext(scanSequence->GetNext()); // ...no } else { m_sequences = scanSequence->GetNext(); // ...yes } scanSequence->Delete(); } }
int CModel::AnimEnumInUse(LPCSTR psAnimEnum) { int iCount = 0; if (strlen(psAnimEnum)) // added for G2 models, which don't necessarily use enums yet { CSequence *curSequence = GetFirstSequence(); while (curSequence) { if (!strcmp(psAnimEnum,curSequence->GetEnum())) { iCount++;// return true; } // new bit, ask the additional sequences as well... // for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++) { if (!strcmp(psAnimEnum,curSequence->AdditionalSeqs[i]->GetEnum())) { iCount++;// return true; } } curSequence = curSequence->GetNext(); } } return iCount;//false; }
void CModel::ReOrderSequences() { typedef vector<CSequence*> sequences_t; sequences_t sequences; // add sequences to list... CSequence *curSequence = m_sequences; while (curSequence) { sequences.push_back(curSequence); curSequence = curSequence->GetNext(); } // re-order sequences... qsort( (void *)&sequences[0], (size_t)(sequences.size()), sizeof(CSequence *), ModelSequenceCompareFunc ); // now rebuild links... int iTotMasterSequences = GetTotMasterSequences(); // this needs to be eval'd here, you can't do it in the for-next below m_sequences = NULL; for (int i=0; i<iTotMasterSequences; i++) { curSequence = sequences[i]; curSequence->SetNext(NULL); AddSequence(curSequence); } Resequence(); }
void CModel::AddSequence(CSequence* sequence) { if (m_sequences == NULL) { m_sequences = sequence; } else { CSequence* curSequence = m_sequences; while(curSequence->GetNext() != NULL) { curSequence = curSequence->GetNext(); } curSequence->SetNext(sequence); } }
int CModel::GetTotMasterSequences() { int tot = 0; CSequence* curSequence = m_sequences; while(curSequence != NULL) { tot++; curSequence = curSequence->GetNext(); } return tot; }
void CModel::GetMasterEnumBoundaryFrameNumbers(int *piFirstFrameAfterBOTH, int *piFirstFrameAfterTORSO) { ENUMTYPE prevET = ET_INVALID; int iFirstFrameAfterBOTH = 0; int iFirstFrameAfterTORSO= 0; CSequence* curSequence = m_sequences; while(curSequence != NULL) { ENUMTYPE thisET = curSequence->GetEnumType(); // update any frame markers first... if (prevET == ET_BOTH && thisET != ET_BOTH) { iFirstFrameAfterBOTH = curSequence->GetTargetFrame(); iFirstFrameAfterTORSO= curSequence->GetTargetFrame(); // set this as well in case there are no TORSOs at all } if (prevET == ET_TORSO && thisET != ET_TORSO) { iFirstFrameAfterTORSO= curSequence->GetTargetFrame(); } prevET = thisET; curSequence = curSequence->GetNext(); } // bug fix, if there are no leg frames at all, then we need to check if the ...AfterTORSO marker needs moving... if (prevET == ET_BOTH) { iFirstFrameAfterBOTH = GetTotFrames(); iFirstFrameAfterTORSO= GetTotFrames(); } if (prevET == ET_TORSO) { iFirstFrameAfterTORSO = GetTotFrames(); } if (piFirstFrameAfterBOTH) { *piFirstFrameAfterBOTH = iFirstFrameAfterBOTH; } if (piFirstFrameAfterTORSO) { *piFirstFrameAfterTORSO= iFirstFrameAfterTORSO; } }
LPCSTR CModel::GLAName(void) { CSequence* curSequence = GetFirstSequence(); while (curSequence) { if (curSequence->IsGLA()) { return curSequence->GetName(); } curSequence = curSequence->GetNext(); } return NULL; }
bool CModel::HasGLA() { CSequence* curSequence = GetFirstSequence(); while (curSequence) { if (curSequence->IsGLA()) { return true; } curSequence = curSequence->GetNext(); } return false; }
void CModel::Delete() { while(m_comments != NULL) { CComment* curComment = m_comments; m_comments = curComment->GetNext(); curComment->Delete(); } while(m_sequences != NULL) { CSequence* curSequence = m_sequences; m_sequences = curSequence->GetNext(); curSequence->Delete(); } if (m_name != NULL) { free(m_name); m_name = NULL; } if (m_path != NULL) { free(m_path); m_path = NULL; } if (m_psSkelPath != NULL) { free(m_psSkelPath); m_psSkelPath = NULL; } if (m_psMakeSkelPath != NULL) { free(m_psMakeSkelPath); m_psMakeSkelPath = NULL; } if (m_psRefGLAPath != NULL) { free(m_psRefGLAPath); m_psRefGLAPath = NULL; } m_curSequence = NULL; PCJList_Clear(); // not really necessary, but useful reminder delete this; }
int CModel::GetTotSequences() { int tot = 0; CSequence* curSequence = m_sequences; while(curSequence != NULL) { tot++; for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++) { if (curSequence->AdditionalSeqs[i]->AdditionalSequenceIsValid()) tot++; } curSequence = curSequence->GetNext(); } return tot; }
bool CModel::ContainsFile(LPCSTR psFilename) { if (m_sequences == NULL) { return false; } else { CSequence* curSequence = m_sequences; while(curSequence) { if (_stricmp(curSequence->GetPath(),psFilename)==0) return true; curSequence = curSequence->GetNext(); } } return false; }
bool CModel::WriteExternal(bool bPromptForNames, bool& bCFGWritten) { bCFGWritten = false; CString filename; if (bPromptForNames) { CString strInitialPrompt(ghAssimilateView->GetDocument()->GetPathName()); Filename_RemoveFilename(strInitialPrompt); strInitialPrompt.Replace("/","\\"); strInitialPrompt += "\\"; strInitialPrompt += sANIMATION_CFG_NAME; CFileDialog dialog(FALSE, ".cfg", strInitialPrompt, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, "Config Data Files (*.cfg)|*.cfg|All Files (*.*)|*.*||", NULL); if (dialog.DoModal() != IDOK) { return false; } filename = dialog.GetPathName(); // eg. {"Q:\quake\baseq3\models\players\ste_assimilate_test\ste_assimilate_test.cfg"} } else { filename = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); filename+= GetPath(); filename.MakeLower(); filename.Replace('\\', '/'); int loc = filename.Find(m_name);//"/root"); if (loc>=0) { filename = filename.Left(loc+strlen(m_name)); } // dup the dirname to use as the model name... (eg "/.../.../klingon" becomes "/.../.../klingon/klingon" filename += "/"; filename += sANIMATION_CFG_NAME; } CTxtFile* file = CTxtFile::Create(filename); if (file == NULL || !file->IsValid()) { ErrorBox(va("Error creating file \"%s\"!",filename)); return false; } // new bit, check for the existance of an animation.pre file, which means export this in Q3 format (rather than trek) CString strQ3FormatCheckName(filename); Filename_RemoveFilename(strQ3FormatCheckName); strQ3FormatCheckName += "\\"; strQ3FormatCheckName += sANIMATION_PRE_NAME; strQ3FormatCheckName.Replace("/","\\"); bool bExportFormatIsQuake3Multiplayer = //FileExists(strQ3FormatCheckName); ((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode(); CString strPrePend; if (bExportFormatIsQuake3Multiplayer) { // multi-player format, check for optional animation.pre file... FILE *fhPRE = fopen(strQ3FormatCheckName, "rt"); if (fhPRE) { // read all the lines in this file and just write them straight to the output file... char sLine[16384]; char *psLine; CString strTrimmed; while ((psLine = fgets( sLine, sizeof(sLine), fhPRE ))!=NULL) { strTrimmed = psLine; strTrimmed.Replace("\n",""); strTrimmed.TrimRight(); strTrimmed.TrimLeft(); file->Writeln(strTrimmed); } if (ferror(fhPRE)) { ErrorBox(va("Error during reading of file \"%s\"!\n\n( this shouldn't happen )",(LPCSTR)strQ3FormatCheckName)); } fclose(fhPRE); } file->Writeln(""); file->Writeln("//"); file->Writeln("// Format: targetFrame, frameCount, loopFrame, frameSpeed"); file->Writeln("//"); } else { // single-player format... CString commentLine; CTime time = CTime::GetCurrentTime(); commentLine.Format("// %s %d frames; %d sequences; updated %s", filename, m_totFrames, GetTotSequences(), time.Format("%H:%M %A, %B %d, %Y")); file->Writeln(commentLine); // the Writeln functions I have to call don't handle "\n" chars properly because of being opened in binary mode // (sigh), so I have to explicitly call the Writeln() functions to output CRs... :-( file->Writeln("//"); file->Writeln("// Format: enum, targetFrame, frameCount, loopFrame, frameSpeed"); file->Writeln("//"); } CSequence* curSequence = m_sequences; while(curSequence != NULL) { curSequence->WriteExternal(this, file, bExportFormatIsQuake3Multiplayer); curSequence = curSequence->GetNext(); } file->Delete(); if (HasGLA()) { _unlink(filename); // zap it, since it's meaningless here (only has one seq/enum: the whole GLA) } else { bCFGWritten = true; } return true; }
void CModel::Resequence(bool bReScanASEFiles /* = false */) { CWaitCursor wait; CRect Rect; CProgressCtrl *pProgress = NULL; if (bReScanASEFiles && ((CAssimilateApp*)AfxGetApp())->m_pMainWnd) { pProgress = new CProgressCtrl; bool bOK = !!pProgress->Create( WS_CHILD|WS_VISIBLE|PBS_SMOOTH, // DWORD dwStyle, CRect(100,100,200,200), // const RECT& rect, ((CAssimilateApp*)AfxGetApp())->m_pMainWnd, // CWnd* pParentWnd, 1 // UINT nID ); if (!bOK) { delete pProgress; pProgress = NULL; } } int iTotMasterSequences = GetTotMasterSequences(); if (pProgress) { pProgress->SetRange(0,iTotMasterSequences); } int iSequenceNumber=0; int curFrame = 0; CSequence* curSequence = m_sequences; while(curSequence != NULL) { if (pProgress) { pProgress->SetPos(iSequenceNumber++); wait.Restore(); } // mark current enums as valid or not... curSequence->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(curSequence->GetEnum())); for (int _i=0; _i<MAX_ADDITIONAL_SEQUENCES; _i++) { CSequence *additionalSeq = curSequence->AdditionalSeqs[_i]; additionalSeq->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(additionalSeq->GetEnum())); } if ( bReScanASEFiles ) { // new code, first of all check for changed framecounts (ie updated ASE file), and update sequence if nec... CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir(); nameASE+= curSequence->GetPath(); if (!FileExists(nameASE)) { if (gbCarWash_DoingScan) { strCarWashErrors += va("Model file missing: \"%s\"\n",nameASE); } else { if ( gbReportMissingASEs ) { gbReportMissingASEs = GetYesNo(va("Model file missing: \"%s\"\n\nContinue recieving this message?",nameASE)); } } } else { int iStartFrame, iFrameCount, iFrameSpeed; iFrameCount = curSequence->GetFrameCount(); // default it in case we skip an XSI read iFrameSpeed = curSequence->GetFrameSpeed(); // default it in case we cache this file curSequence->ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeed, true); // true = can skip XSI read if ( iFrameCount != curSequence->GetFrameCount() ) { if (gbCarWash_DoingScan) { strCarWashErrors += va("file: \"%s\" has a framecount of %d, but .CAR file says %d\n",nameASE,iFrameCount,curSequence->GetFrameCount()); } else { // don't mention it if the current count is zero, it's probably a new anim we've just added... if ( curSequence->GetFrameCount() ) { if (giFixUpdatedASEFrameCounts == YES || giFixUpdatedASEFrameCounts == NO) { CYesNoYesAllNoAll query( va("Model file: \"%s\"",nameASE), "", va("... has a framecount of %d instead of %d as the QDT/CAR file says",iFrameCount, curSequence->GetFrameCount()), "", "", "Do you want me to fix this?" ); giFixUpdatedASEFrameCounts = query.DoModal(); } } // update the sequence?... if (giFixUpdatedASEFrameCounts == YES || giFixUpdatedASEFrameCounts == YES_ALL || !curSequence->GetFrameCount() // update: I think this should be here? ) { curSequence->SetFrameCount( iFrameCount ); } } } } // findmeste: this no longer seems to do anything under JK2, presumablt EF1-only? #if 0 // now try to do any auto-associate between the ASE filename base and the existing enums, // so if we find (eg) /...../...../CROUCH.ASE and we have BOTH_CROUCH then auto-set the enum to BOTH_CROUCH CString stringASEName = nameASE; Filename_BaseOnly(stringASEName); // now = (eg) "falldeath" or "injured" etc for (int i=0; ; i++) { LPCSTR p = ((CAssimilateApp*)AfxGetApp())->GetEnumEntry(i); if (!p) // EOS? break; CString stringEnum = p; // note, I could check stuff like "IsEnumSeperator(LPCSTR lpString)" on <p>, but you'd never // have one of those enums assigned to a sequence anyway. char *psEnumPosAfterUnderScore = strchr(stringEnum,'_'); if (psEnumPosAfterUnderScore++) // check it, and skip to next char { // does this enum match the ASE name? if ( !stricmp( psEnumPosAfterUnderScore, stringASEName ) ) { // ok, we've found a good candidate, so set it... (no need for query-prev code, but I wanted to) if ( strcmp( curSequence->GetEnum(), stringEnum)) { curSequence->SetEnum(stringEnum); } } } else { // this should never happen... if (gbCarWash_DoingScan) { strCarWashErrors += va("found an anim enum with no underscore: \"%s\"\n",stringEnum); } else { ASSERT(0); ErrorBox(va("Error! Somehow I found an anim enum with no underscore: \"%s\"",stringEnum)); } } } #endif } // More bollox for Gummelt... :-) // now do the other freaky trick (you'd better be grateful for all this Mike!!! <g>), which is: // If you find the substring DEATH in this (master) sequence's enum, then ensure that the first *additional* // sequence of it is set to be the corresponding DEAD enum, but using the last frame only (and non-looping) // // (... or something...) { // keep scope local for neatness if ( strstr (curSequence->GetEnum(), "DEATH") ) { // scan this sequence's additional sequences for a DEAD of the same basic type... CString stringEnumDEAD = curSequence->GetEnum(); ASSERT(!IsEnumSeperator(stringEnumDEAD)); stringEnumDEAD.Replace("DEATH","DEAD"); // 1st, is there even a corresponding DEAD enum in the global enum table that we can look for... CString stringEnum; bool bEnumFound = false; for (int iEnumEntry=0; !bEnumFound; iEnumEntry++) { LPCSTR p = ((CAssimilateApp*)AfxGetApp())->GetEnumEntry(iEnumEntry); if (!p) // EOS? break; stringEnum = p; // note, I could check stuff like "IsEnumSeperator(LPCSTR lpString)" on <p>, but you'd never // have one of those enums assigned to a sequence anyway. // does this enum match the one we've built? if ( !_stricmp( stringEnum, stringEnumDEAD ) ) { bEnumFound = true; } } if ( bEnumFound ) { // ok, there *is* one of these, so let's scan this sequence's additional sequences to see if we've // got it... CSequence *additionalSeq; // outside FOR scope for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++) { additionalSeq = curSequence->AdditionalSeqs[i]; if (additionalSeq->AdditionalSequenceIsValid()) { if (!strcmp(additionalSeq->GetEnum(),stringEnum)) { break; // we've found one! } } } // if we didn't find one, NULL the ptr if (int i=MAX_ADDITIONAL_SEQUENCES) { additionalSeq = NULL; } // did we find one? (or did it have the wrong info in?) if ( additionalSeq == NULL // didn't find one || additionalSeq->GetFrameCount()!=1 || additionalSeq->GetLoopFrame() !=-1 || additionalSeq->GetStartFrame()!= curSequence->GetFrameCount()-1 || additionalSeq->GetFrameSpeed()!= curSequence->GetFrameSpeed() ) { // find a slot to add this new sequence to, or use the faulty one... if (additionalSeq == NULL) { for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++) { additionalSeq = curSequence->AdditionalSeqs[i]; if (!additionalSeq->AdditionalSequenceIsValid()) { break; // found an unused slot } } } // so have we got a slot to work with? if ( additionalSeq == NULL ) { if (gbCarWash_DoingScan) { strCarWashErrors += va( "F**k!!!: I need an 'additional sequence' slot free in the entry: \"%s\" to generate a DEAD seq, but there isn't one spare. Edit this yourself later.\n",curSequence->GetPath()); } else { ErrorBox( va( "F**k!!!\n\nI need an 'additional sequence' slot free in the ASE:\n\n\"%s\"\n\n... to generate a DEAD seq, but there isn't one spare. Edit this yourself later.",curSequence->GetPath())); } } else { additionalSeq->SetStartFrame( curSequence->GetFrameCount()-1 ); additionalSeq->SetFrameCount( 1 ); additionalSeq->SetLoopFrame (-1 ); additionalSeq->SetFrameSpeed( curSequence->GetFrameSpeed() ); additionalSeq->SetEnum ( stringEnumDEAD ); } } } } } curSequence->SetTargetFrame(curFrame + curSequence->GetStartFrame()); // slightly more legal than just (curFrame) // update: now set any additional sequences within it... for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++) { curSequence->AdditionalSeqs[i]->SetTargetFrame(curFrame + curSequence->AdditionalSeqs[i]->GetStartFrame()); } curFrame += curSequence->GetFrameCount(); curFrame += curSequence->GetGenLoopFrame()?1:0; // findme: is this right? I hate this system curSequence = curSequence->GetNext(); } m_totFrames = curFrame; ghAssimilateView->GetDocument()->SetModifiedFlag(); if (pProgress) { delete pProgress; pProgress = 0; } }
void CModel::Write(CTxtFile* file) { file->Write("$"); file->Writeln(CAssimilateDoc::GetKeyword(TK_AS_GRABINIT, TABLE_QDT)); if (!HasGLA()) { if (GetScale() != 1.0f) { file->Write("$"); file->Write(CAssimilateDoc::GetKeyword(TK_AS_SCALE, TABLE_QDT)," "); file->Writeln(va("%g",GetScale())); } if (GetKeepMotion()) { file->Write("$"); file->Writeln(CAssimilateDoc::GetKeyword(TK_AS_KEEPMOTION, TABLE_QDT)); } if (GetRefGLAPath()) { file->Write("$"); file->Write(CAssimilateDoc::GetKeyword(TK_AS_REF_GLA, TABLE_QDT), " "); file->Writeln(va("%s", GetRefGLAPath())); } for (int iPCJ=0; iPCJ < PCJList_GetEntries(); iPCJ++) { file->Write("$"); file->Write(CAssimilateDoc::GetKeyword(TK_AS_PCJ, TABLE_QDT)," "); file->Writeln( PCJList_GetEntry(iPCJ) ); } } CComment* curComment = m_comments; while(curComment != NULL) { curComment->Write(file); curComment = curComment->GetNext(); } bool bFirstSeqWritten = false; CSequence* curSequence = m_sequences; while(curSequence != NULL) { curSequence->Write(file, !bFirstSeqWritten && GetPreQuat()); curSequence = curSequence->GetNext(); bFirstSeqWritten = true; } file->Writeln("$", CAssimilateDoc::GetKeyword(TK_AS_GRABFINALIZE, TABLE_QDT)); if (m_path != NULL) { file->Write("$", CAssimilateDoc::GetKeyword(GetConvertType(), TABLE_QDT)); CString path = m_path; int loc = path.Find("/base"); if (loc > -1) { path = path.Right(path.GetLength() - loc - 5); loc = path.Find("/"); path = path.Right(path.GetLength() - loc - 1); } if (!path.GetLength()) // check that some dopey artist hasn't use the name "base" on the right hand side { path = m_path; } file->Write(" ", path, " "); // params stuff... if (IsGhoul2()) { if (GetMakeSkin()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_MAKESKIN, TABLE_CONVERT), " "); } if (GetSmooth()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SMOOTH, TABLE_CONVERT), " "); } if (GetLoseDupVerts()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_LOSEDUPVERTS, TABLE_CONVERT), " "); } if (GetIgnoreBaseDeviations()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_IGNOREBASEDEVIATIONS, TABLE_CONVERT), " "); } if (GetSkew90()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_SKEW90, TABLE_CONVERT), " "); } if (GetNoSkew90()) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_NOSKEW90, TABLE_CONVERT), " "); } if (GetMakeSkelPath() && strlen(GetMakeSkelPath())) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_MAKESKEL, TABLE_CONVERT), " "); file->Write(GetMakeSkelPath(), " "); } // params below not used for ghoul2 } else { if (giLODLevelOverride) { file->Write(va("-lod %d ",giLODLevelOverride)); } file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_PLAYERPARMS, TABLE_CONVERT), " "); file->Write(0);//m_skipStart+1); // ignore these, but don't want to update parser and have invalid prev files file->Space(); file->Write(0);//m_skipEnd);//max upper frames); file->Space(); } if (m_originx || m_originy || m_originz) { file->Write("-", CAssimilateDoc::GetKeyword(TK_AS_ORIGIN, TABLE_CONVERT), " "); file->Write(m_originx); file->Space(); file->Write(m_originy); file->Space(); file->Write(m_originz); } file->Writeln(); } }