mirror of
https://github.com/openmoh/openmohaa.git
synced 2025-04-28 21:57:57 +03:00
542 lines
12 KiB
C++
542 lines
12 KiB
C++
/*
|
|
===========================================================================
|
|
Copyright (C) 2023 the OpenMoHAA team
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
OpenMoHAA source code 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 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
OpenMoHAA source code 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 OpenMoHAA source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
|
|
// tiki_anim.cpp : TIKI Anim
|
|
|
|
#include "q_shared.h"
|
|
#include "qcommon.h"
|
|
#include "../skeletor/skeletor.h"
|
|
#include <mem_blockalloc.h>
|
|
#include <con_set.h>
|
|
#include "tiki_files.h"
|
|
|
|
/*
|
|
===============
|
|
AnimCompareFunc
|
|
===============
|
|
*/
|
|
static void *context;
|
|
|
|
static int AnimCompareFunc(const void *a, const void *b)
|
|
{
|
|
dloaddef_t *ld = (dloaddef_t *)context;
|
|
return stricmp(ld->loadanims[*(int *)a]->alias, ld->loadanims[*(int *)b]->alias);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_GetAnimOrder
|
|
===============
|
|
*/
|
|
void TIKI_GetAnimOrder(dloaddef_t *ld, int *order)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < ld->numanims; i++) {
|
|
order[i] = i;
|
|
}
|
|
context = ld;
|
|
qsort(order, ld->numanims, sizeof(int), AnimCompareFunc);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_NameForNum
|
|
===============
|
|
*/
|
|
const char *TIKI_Anim_NameForNum(dtiki_t *pmdl, int animnum)
|
|
{
|
|
dtikianimdef_t *panimdef;
|
|
|
|
if (!pmdl || !pmdl->a) {
|
|
return NULL;
|
|
}
|
|
|
|
if (animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return NULL;
|
|
}
|
|
|
|
panimdef = pmdl->a->animdefs[animnum];
|
|
return panimdef->alias;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_NumForName
|
|
===============
|
|
*/
|
|
int TIKI_Anim_NumForName(dtiki_t *pmdl, const char *name)
|
|
{
|
|
int iTop;
|
|
int iBottom;
|
|
int iMiddle;
|
|
int iComp;
|
|
dtikianimdef_t *panimdef;
|
|
float fAnimWeights[MAX_FRAMEINFOS];
|
|
float fWeight;
|
|
float fTotalWeight;
|
|
int iAnimCount;
|
|
int i, k;
|
|
|
|
if (!pmdl) {
|
|
return -1;
|
|
}
|
|
|
|
iBottom = 0;
|
|
iTop = pmdl->a->num_anims - 1;
|
|
|
|
while (iBottom <= iTop) {
|
|
iMiddle = (iBottom + iTop) / 2;
|
|
|
|
panimdef = pmdl->a->animdefs[iMiddle];
|
|
iComp = stricmp(panimdef->alias, name);
|
|
|
|
if (!iComp) {
|
|
if (!(panimdef->flags & TAF_RANDOM)) {
|
|
return iMiddle;
|
|
}
|
|
|
|
for (i = iMiddle; i > 0; i--) {
|
|
if (!pmdl->a->animdefs[i - 1] || stricmp(panimdef->alias, pmdl->a->animdefs[i - 1]->alias)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
k = i;
|
|
|
|
for (iMiddle++; iMiddle < pmdl->a->num_anims; iMiddle++) {
|
|
if (!pmdl->a->animdefs[iMiddle] || stricmp(panimdef->alias, pmdl->a->animdefs[iMiddle]->alias)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
fTotalWeight = 0.0f;
|
|
iAnimCount = 0;
|
|
|
|
for (; i < iMiddle; i++) {
|
|
panimdef = pmdl->a->animdefs[i];
|
|
if (!panimdef) {
|
|
continue;
|
|
}
|
|
|
|
if (panimdef->flags & TAF_AUTOSTEPS) {
|
|
fAnimWeights[iAnimCount] = 0.0f;
|
|
panimdef->flags &= ~TAF_AUTOSTEPS;
|
|
} else {
|
|
fAnimWeights[iAnimCount] = panimdef->weight;
|
|
fTotalWeight += panimdef->weight;
|
|
}
|
|
|
|
iAnimCount++;
|
|
}
|
|
|
|
fWeight = randweight() * fTotalWeight;
|
|
for (i = 0; i < iAnimCount; i++) {
|
|
if (fWeight < fAnimWeights[i]) {
|
|
break;
|
|
}
|
|
|
|
fWeight -= fAnimWeights[i];
|
|
}
|
|
|
|
iMiddle = i + k;
|
|
panimdef = pmdl->a->animdefs[iMiddle];
|
|
if (panimdef && panimdef->flags & TAF_NOREPEAT) {
|
|
panimdef->flags |= TAF_AUTOSTEPS;
|
|
}
|
|
|
|
return iMiddle;
|
|
}
|
|
|
|
if (iComp > 0) {
|
|
iTop = iMiddle - 1;
|
|
} else {
|
|
iBottom = iMiddle + 1;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_Random
|
|
===============
|
|
*/
|
|
int TIKI_Anim_Random(dtiki_t *pmdl, const char *name)
|
|
{
|
|
dtikianimdef_t *panimdef;
|
|
int i;
|
|
float totalweight;
|
|
float weights[MAX_FRAMEINFOS];
|
|
int anim[MAX_FRAMEINFOS];
|
|
int num;
|
|
size_t len;
|
|
int diff;
|
|
float weight;
|
|
|
|
len = strlen(name);
|
|
if (!len || !pmdl) {
|
|
return -1;
|
|
}
|
|
|
|
num = 0;
|
|
totalweight = 0.0f;
|
|
for (i = 0; i < pmdl->a->num_anims; i++) {
|
|
panimdef = pmdl->a->animdefs[i];
|
|
diff = strnicmp(panimdef->alias, name, len);
|
|
if (diff || panimdef->alias[len] == '_') {
|
|
if (diff > 0) {
|
|
break;
|
|
}
|
|
} else {
|
|
if (num >= MAX_FRAMEINFOS) {
|
|
break;
|
|
}
|
|
|
|
totalweight += panimdef->weight;
|
|
anim[num] = i;
|
|
weights[num] = panimdef->weight;
|
|
|
|
num++;
|
|
}
|
|
}
|
|
|
|
// animation name not found
|
|
if (!num) {
|
|
return -1;
|
|
}
|
|
|
|
// find a random animation based on the weight
|
|
weight = randweight() * totalweight;
|
|
for (i = 0; i < num; i++) {
|
|
if (weight < weights[i]) {
|
|
break;
|
|
}
|
|
|
|
weight -= weights[i];
|
|
}
|
|
|
|
return anim[i];
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_NumFrames
|
|
===============
|
|
*/
|
|
int TIKI_Anim_NumFrames(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
return animData->numFrames;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_Time
|
|
===============
|
|
*/
|
|
float TIKI_Anim_Time(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0.0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0.0;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
|
|
return animData->flags & TAF_DELTADRIVEN ? animData->frameTime * animData->numFrames
|
|
: animData->frameTime * (animData->numFrames - 1);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_Frametime
|
|
===============
|
|
*/
|
|
float TIKI_Anim_Frametime(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0.0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0.0;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
return animData->frameTime;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_Delta
|
|
===============
|
|
*/
|
|
void TIKI_Anim_Delta(dtiki_t *pmdl, int animnum, float *delta)
|
|
{
|
|
if (!pmdl) {
|
|
VectorClear(delta);
|
|
return;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
VectorClear(delta);
|
|
return;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
VectorScale(animData->totalDelta, pmdl->load_scale, delta);
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_AngularDelta
|
|
|
|
Added in 2.0
|
|
===============
|
|
*/
|
|
void TIKI_Anim_AngularDelta(dtiki_t *pmdl, int animnum, float *delta)
|
|
{
|
|
if (!pmdl) {
|
|
*delta = 0;
|
|
return;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
*delta = 0;
|
|
return;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
*delta = animData->totalAngleDelta;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_HasDelta
|
|
===============
|
|
*/
|
|
qboolean TIKI_Anim_HasDelta(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return qfalse;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return qfalse;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
return animData->bHasDelta;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_DeltaOverTime
|
|
===============
|
|
*/
|
|
void TIKI_Anim_DeltaOverTime(dtiki_t *pTiki, int iAnimnum, float fTime1, float fTime2, vec3_t vDelta)
|
|
{
|
|
int realAnimIndex;
|
|
skelAnimDataGameHeader_t *animData;
|
|
SkelVec3 absDelta;
|
|
|
|
if (!pTiki || !pTiki->a) {
|
|
return;
|
|
}
|
|
|
|
if (iAnimnum >= pTiki->a->num_anims || iAnimnum < 0) {
|
|
return;
|
|
}
|
|
|
|
if (fTime2 < fTime1) {
|
|
return;
|
|
}
|
|
|
|
realAnimIndex = pTiki->a->m_aliases[iAnimnum];
|
|
if (realAnimIndex != -1) {
|
|
animData = SkeletorCacheGetData(realAnimIndex);
|
|
absDelta = animData->GetDeltaOverTime(fTime1, fTime2);
|
|
VectorScale(absDelta, pTiki->load_scale, vDelta);
|
|
} else {
|
|
TIKI_Error("Skeletor GetDeltaOverTime: Couldn't find animation with index %i\n", iAnimnum);
|
|
VectorClear(vDelta);
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_AngularDeltaOverTime
|
|
===============
|
|
*/
|
|
void TIKI_Anim_AngularDeltaOverTime(dtiki_t *pTiki, int iAnimnum, float fTime1, float fTime2, float *fDelta)
|
|
{
|
|
int realAnimIndex;
|
|
skelAnimDataGameHeader_t *animData;
|
|
|
|
*fDelta = 0;
|
|
|
|
if (!pTiki || !pTiki->a) {
|
|
return;
|
|
}
|
|
|
|
if (iAnimnum >= pTiki->a->num_anims || iAnimnum < 0) {
|
|
return;
|
|
}
|
|
|
|
if (fTime2 < fTime1) {
|
|
return;
|
|
}
|
|
|
|
realAnimIndex = pTiki->a->m_aliases[iAnimnum];
|
|
if (realAnimIndex != -1) {
|
|
animData = SkeletorCacheGetData(realAnimIndex);
|
|
*fDelta = animData->GetAngularDeltaOverTime(fTime1, fTime2);
|
|
} else {
|
|
TIKI_Error("Skeletor GetAngularDeltaOverTime: Couldn't find animation with index %i\n", iAnimnum);
|
|
*fDelta = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_Flags
|
|
===============
|
|
*/
|
|
int TIKI_Anim_Flags(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0;
|
|
}
|
|
|
|
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
|
|
return panimdef->flags;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_FlagsSkel
|
|
===============
|
|
*/
|
|
int TIKI_Anim_FlagsSkel(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0;
|
|
}
|
|
|
|
skelAnimDataGameHeader_t *animData;
|
|
int flags;
|
|
|
|
animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
|
|
flags = animData->flags;
|
|
|
|
if (animData->bHasDelta) {
|
|
flags |= TAF_HASDELTA;
|
|
}
|
|
|
|
if (animData->bHasMorph) {
|
|
flags |= TAF_HASMORPH;
|
|
}
|
|
|
|
if (animData->bHasUpper) {
|
|
flags |= TAF_HASUPPER;
|
|
}
|
|
|
|
return flags;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_HasServerCommands
|
|
===============
|
|
*/
|
|
qboolean TIKI_Anim_HasServerCommands(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return qfalse;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return qfalse;
|
|
}
|
|
|
|
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
|
|
return panimdef->num_server_cmds > 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_HasClientCommands
|
|
===============
|
|
*/
|
|
qboolean TIKI_Anim_HasClientCommands(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return qfalse;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return qfalse;
|
|
}
|
|
|
|
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
|
|
return panimdef->num_client_cmds > 0;
|
|
}
|
|
|
|
/*
|
|
===============
|
|
TIKI_Anim_CrossblendTime
|
|
===============
|
|
*/
|
|
float TIKI_Anim_CrossblendTime(dtiki_t *pmdl, int animnum)
|
|
{
|
|
if (!pmdl) {
|
|
return 0.0;
|
|
}
|
|
|
|
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
|
|
return 0.0;
|
|
}
|
|
|
|
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
|
|
return panimdef->blendtime;
|
|
}
|