/* =============== R_InitAnimations =============== */ void R_InitAnimations() { skelAnimation_t* anim; // leave a space for nullptr animation tr.numAnimations = 0; anim = R_AllocAnimation(); anim->type = AT_BAD; strcpy(anim->name, "<default animation>"); }
/* =============== RE_RegisterAnimationIQM Animation data has already been loaded =============== */ qhandle_t RE_RegisterAnimationIQM( const char *name, IQAnim_t *data ) { qhandle_t hAnim; skelAnimation_t *anim; if ( !name || !name[ 0 ] ) { Log::Warn("Empty name passed to RE_RegisterAnimationIQM" ); return 0; } if ( strlen( name ) >= MAX_QPATH ) { Log::Warn("Animation name exceeds MAX_QPATH" ); return 0; } // search the currently loaded animations for ( hAnim = 1; hAnim < tr.numAnimations; hAnim++ ) { anim = tr.animations[ hAnim ]; if ( !Q_stricmp( anim->name, name ) ) { if ( anim->type == animType_t::AT_BAD ) { return 0; } return hAnim; } } // allocate a new model_t if ( ( anim = R_AllocAnimation() ) == nullptr ) { Log::Warn("RE_RegisterAnimationIQM: R_AllocAnimation() failed for '%s'", name ); return 0; } // only set the name after the animation has been successfully allocated Q_strncpyz( anim->name, name, sizeof( anim->name ) ); anim->type = animType_t::AT_IQM; anim->iqm = data; return anim->index; }
/* =============== RE_RegisterAnimation =============== */ qhandle_t RE_RegisterAnimation(const char *name) { qhandle_t hAnim; skelAnimation_t *anim; char *buffer; int bufferLen; qboolean loaded = qfalse; if(!name || !name[0]) { ri.Printf(PRINT_WARNING, "Empty name passed to RE_RegisterAnimation\n"); return 0; } //ri.Printf(PRINT_ALL, "RE_RegisterAnimation(%s)\n", name); if(strlen(name) >= MAX_QPATH) { ri.Printf(PRINT_WARNING, "Animation name exceeds MAX_QPATH\n"); return 0; } // search the currently loaded animations for(hAnim = 1; hAnim < tr.numAnimations; hAnim++) { anim = tr.animations[hAnim]; if(!Q_stricmp(anim->name, name)) { if(anim->type == AT_BAD) { return 0; } return hAnim; } if(anim->type == AT_PSA && anim->psa) { const char *animName; animName = strstr(name, "::"); //ri.Printf(PRINT_ALL, "animName = '%s'\n", animName ? (animName + 2) : NULL); if(animName && *(animName + 2) && !Q_stricmp(anim->psa->info.name, (animName + 2))) { return hAnim; } } } // allocate a new model_t if((anim = R_AllocAnimation()) == NULL) { ri.Printf(PRINT_WARNING, "RE_RegisterAnimation: R_AllocAnimation() failed for '%s'\n", name); return 0; } // only set the name after the animation has been successfully allocated Q_strncpyz(anim->name, name, sizeof(anim->name)); // make sure the render thread is stopped R_SyncRenderThread(); // load and parse the .md5anim file bufferLen = ri.FS_ReadFile(name, (void **)&buffer); if(!buffer) { return 0; } if(!Q_stricmpn((const char *)buffer, "MD5Version", 10)) { loaded = R_LoadMD5Anim(anim, buffer, bufferLen, name); } else if(!Q_stricmpn((const char *)buffer, "ANIMHEAD", 8)) { loaded = R_LoadPSA(anim, buffer, bufferLen, name); } else { ri.Printf(PRINT_WARNING, "RE_RegisterAnimation: unknown fileid for '%s'\n", name); } ri.FS_FreeFile(buffer); if(!loaded) { ri.Printf(PRINT_WARNING, "couldn't load '%s'\n", name); // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem anim->type = AT_BAD; } return anim->index; }
static qboolean R_LoadPSA(skelAnimation_t * skelAnim, void *buffer, int bufferSize, const char *name) { int i, j, k; memStream_t *stream; axChunkHeader_t chunkHeader; int numReferenceBones; axReferenceBone_t *refBone; axReferenceBone_t *refBones; int numSequences; axAnimationInfo_t *animInfo; axAnimationKey_t *key; psaAnimation_t *psa; skelAnimation_t *extraAnim; growList_t extraAnims; stream = AllocMemStream(buffer, bufferSize); GetChunkHeader(stream, &chunkHeader); // check indent again if(Q_stricmpn(chunkHeader.ident, "ANIMHEAD", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk indent ('%s' should be '%s')\n", name, chunkHeader.ident, "ANIMHEAD"); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); // read reference bones GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "BONENAMES", 9)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk indent ('%s' should be '%s')\n", name, chunkHeader.ident, "BONENAMES"); FreeMemStream(stream); return qfalse; } if(chunkHeader.dataSize != sizeof(axReferenceBone_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", name, chunkHeader.dataSize, sizeof(axReferenceBone_t)); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); numReferenceBones = chunkHeader.numData; if(numReferenceBones < 1) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has no bones\n", name); FreeMemStream(stream); return qfalse; } if(numReferenceBones > MAX_BONES) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has more than %i bones (%i)\n", name, MAX_BONES, numReferenceBones); FreeMemStream(stream); return qfalse; } refBones = ri.Hunk_Alloc(numReferenceBones * sizeof(axReferenceBone_t), h_low); for(i = 0, refBone = refBones; i < numReferenceBones; i++, refBone++) { MemStreamRead(stream, refBone->name, sizeof(refBone->name)); refBone->flags = MemStreamGetLong(stream); refBone->numChildren = MemStreamGetLong(stream); refBone->parentIndex = MemStreamGetLong(stream); if(i == 0) { refBone->parentIndex = -1; } GetBone(stream, &refBone->bone); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSA: axReferenceBone_t(%i):\n" "axReferenceBone_t::name: '%s'\n" "axReferenceBone_t::flags: %i\n" "axReferenceBone_t::numChildren %i\n" "axReferenceBone_t::parentIndex: %i\n" "axReferenceBone_t::quat: %f %f %f %f\n" "axReferenceBone_t::position: %f %f %f\n" "axReferenceBone_t::length: %f\n" "axReferenceBone_t::xSize: %f\n" "axReferenceBone_t::ySize: %f\n" "axReferenceBone_t::zSize: %f\n", i, refBone->name, refBone->flags, refBone->numChildren, refBone->parentIndex, refBone->bone.quat[0], refBone->bone.quat[1], refBone->bone.quat[2], refBone->bone.quat[3], refBone->bone.position[0], refBone->bone.position[1], refBone->bone.position[2], refBone->bone.length, refBone->bone.xSize, refBone->bone.ySize, refBone->bone.zSize); #endif } // load animation info GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "ANIMINFO", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk indent ('%s' should be '%s')\n", name, chunkHeader.ident, "ANIMINFO"); FreeMemStream(stream); return qfalse; } if(chunkHeader.dataSize != sizeof(axAnimationInfo_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", name, chunkHeader.dataSize, sizeof(axAnimationInfo_t)); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); numSequences = chunkHeader.numData; Com_InitGrowList(&extraAnims, numSequences -1); for(i = 0; i < numSequences; i++) { if(i == 0) { Q_strncpyz(skelAnim->name, name, sizeof(skelAnim->name)); skelAnim->type = AT_PSA; skelAnim->psa = psa = ri.Hunk_Alloc(sizeof(*psa), h_low); } else { // allocate a new skelAnimation_t if((extraAnim = R_AllocAnimation()) == NULL) { ri.Printf(PRINT_WARNING, "R_LoadPSA: R_AllocAnimation() failed for '%s'\n", name); return qfalse; } Q_strncpyz(extraAnim->name, name, sizeof(extraAnim->name)); extraAnim->type = AT_PSA; extraAnim->psa = psa = ri.Hunk_Alloc(sizeof(*psa), h_low); Com_AddToGrowList(&extraAnims, extraAnim); } psa->numBones = numReferenceBones; psa->bones = refBones; animInfo = &psa->info; MemStreamRead(stream, animInfo->name, sizeof(animInfo->name)); MemStreamRead(stream, animInfo->group, sizeof(animInfo->group)); animInfo->numBones = MemStreamGetLong(stream); if(animInfo->numBones != numReferenceBones) { ri.Error(ERR_DROP, "R_LoadPSA: axAnimationInfo_t contains different number than reference bones exist: %i != %i for anim '%s'", animInfo->numBones, numReferenceBones, name); } animInfo->rootInclude = MemStreamGetLong(stream); animInfo->keyCompressionStyle = MemStreamGetLong(stream); animInfo->keyQuotum = MemStreamGetLong(stream); animInfo->keyReduction = MemStreamGetFloat(stream); animInfo->trackTime = MemStreamGetFloat(stream); animInfo->frameRate = MemStreamGetFloat(stream); animInfo->startBoneIndex = MemStreamGetLong(stream); animInfo->firstRawFrame = MemStreamGetLong(stream); animInfo->numRawFrames = MemStreamGetLong(stream); #if 0 ri.Printf(PRINT_ALL, "R_LoadPSA: axAnimationInfo_t(%i):\n" "axAnimationInfo_t::name: '%s'\n" "axAnimationInfo_t::group: '%s'\n" "axAnimationInfo_t::numBones: %i\n" "axAnimationInfo_t::rootInclude: %i\n" "axAnimationInfo_t::keyCompressionStyle: %i\n" "axAnimationInfo_t::keyQuotum: %i\n" "axAnimationInfo_t::keyReduction: %f\n" "axAnimationInfo_t::trackTime: %f\n" "axAnimationInfo_t::frameRate: %f\n" "axAnimationInfo_t::startBoneIndex: %i\n" "axAnimationInfo_t::firstRawFrame: %i\n" "axAnimationInfo_t::numRawFrames: %i\n", i, animInfo->name, animInfo->group, animInfo->numBones, animInfo->rootInclude, animInfo->keyCompressionStyle, animInfo->keyQuotum, animInfo->keyReduction, animInfo->trackTime, animInfo->frameRate, animInfo->startBoneIndex, animInfo->firstRawFrame, animInfo->numRawFrames); #endif } // load the animation frame keys GetChunkHeader(stream, &chunkHeader); if(Q_stricmpn(chunkHeader.ident, "ANIMKEYS", 8)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk indent ('%s' should be '%s')\n", name, chunkHeader.ident, "ANIMKEYS"); FreeMemStream(stream); return qfalse; } if(chunkHeader.dataSize != sizeof(axAnimationKey_t)) { ri.Printf(PRINT_WARNING, "R_LoadPSA: '%s' has wrong chunk dataSize ('%i' should be '%i')\n", name, chunkHeader.dataSize, sizeof(axAnimationKey_t)); FreeMemStream(stream); return qfalse; } PrintChunkHeader(&chunkHeader); for(i = 0; i < numSequences; i++) { if(i == 0) { psa = skelAnim->psa; } else { extraAnim = Com_GrowListElement(&extraAnims, i - 1); psa = extraAnim->psa; } psa->numKeys = psa->info.numBones * psa->info.numRawFrames; psa->keys = ri.Hunk_Alloc(psa->numKeys * sizeof(axAnimationKey_t), h_low); for(j = 0, key = &psa->keys[0]; j < psa->numKeys; j++, key++) { for(k = 0; k < 3; k++) { key->position[k] = MemStreamGetFloat(stream); } // Tr3B: see R_LoadPSK ... if((j % psa->info.numBones) == 0) { key->quat[0] = MemStreamGetFloat(stream); key->quat[1] = -MemStreamGetFloat(stream); key->quat[2] = MemStreamGetFloat(stream); key->quat[3] = MemStreamGetFloat(stream); } else { key->quat[0] = -MemStreamGetFloat(stream); key->quat[1] = -MemStreamGetFloat(stream); key->quat[2] = -MemStreamGetFloat(stream); key->quat[3] = MemStreamGetFloat(stream); } key->time = MemStreamGetFloat(stream); } } Com_DestroyGrowList(&extraAnims); FreeMemStream(stream); return qtrue; }
/* =============== RE_RegisterAnimation =============== */ qhandle_t RE_RegisterAnimation( const char *name ) { qhandle_t hAnim; skelAnimation_t *anim; char *buffer; bool loaded = false; if ( !name || !name[ 0 ] ) { Log::Warn("Empty name passed to RE_RegisterAnimation" ); return 0; } //Log::Notice("RE_RegisterAnimation(%s)", name); if ( strlen( name ) >= MAX_QPATH ) { Log::Warn("Animation name exceeds MAX_QPATH" ); return 0; } // search the currently loaded animations for ( hAnim = 1; hAnim < tr.numAnimations; hAnim++ ) { anim = tr.animations[ hAnim ]; if ( !Q_stricmp( anim->name, name ) ) { if ( anim->type == animType_t::AT_BAD ) { return 0; } return hAnim; } } // allocate a new model_t if ( ( anim = R_AllocAnimation() ) == nullptr ) { Log::Warn("RE_RegisterAnimation: R_AllocAnimation() failed for '%s'", name ); return 0; } // only set the name after the animation has been successfully allocated Q_strncpyz( anim->name, name, sizeof( anim->name ) ); // make sure the render thread is stopped R_SyncRenderThread(); // load and parse the .md5anim file int bufferLen = ri.FS_ReadFile( name, ( void ** ) &buffer ); if ( !buffer ) { return 0; } if ( bufferLen >= 10 && !Q_strnicmp( ( const char * ) buffer, "MD5Version", 10 ) ) { loaded = R_LoadMD5Anim( anim, buffer, name ); } else { Log::Warn("RE_RegisterAnimation: unknown fileid for '%s'", name ); } ri.FS_FreeFile( buffer ); if ( !loaded ) { Log::Warn("couldn't load '%s'", name ); // we still keep the model_t around, so if the model name is asked for // again, we won't bother scanning the filesystem anim->type = animType_t::AT_BAD; } return anim->index; }