openmohaa/code/tiki/tiki_anim.cpp

518 lines
12 KiB
C++
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
2023-11-06 18:08:21 +01:00
Copyright (C) 2023 the OpenMoHAA team
2016-03-27 11:49:47 +02:00
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
===============
*/
2023-11-06 18:08:21 +01:00
static void *context;
static int AnimCompareFunc(const void *a, const void *b)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
dloaddef_t *ld = (dloaddef_t *)context;
return stricmp(ld->loadanims[*(int *)a]->alias, ld->loadanims[*(int *)b]->alias);
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_GetAnimOrder
===============
*/
2023-11-06 18:08:21 +01:00
void TIKI_GetAnimOrder(dloaddef_t *ld, int *order)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
int i;
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
for (i = 0; i < ld->numanims; i++) {
order[i] = i;
}
context = ld;
qsort(order, ld->numanims, sizeof(int), AnimCompareFunc);
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_NameForNum
===============
*/
2023-11-06 18:08:21 +01:00
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];
2023-11-06 18:08:21 +01:00
return panimdef->alias;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_NumForName
===============
*/
2023-11-06 18:08:21 +01:00
int TIKI_Anim_NumForName(dtiki_t *pmdl, const char *name)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
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);
2023-11-06 18:08:21 +01:00
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;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_Random
===============
*/
2023-11-06 18:08:21 +01:00
int TIKI_Anim_Random(dtiki_t *pmdl, const char *name)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
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];
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_NumFrames
===============
*/
2023-11-06 18:08:21 +01:00
int TIKI_Anim_NumFrames(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return 0;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0;
}
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
return animData->numFrames;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_Time
===============
*/
2023-11-06 18:08:21 +01:00
float TIKI_Anim_Time(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
if (!pmdl) {
return 0.0;
}
2023-05-23 23:06:26 +02:00
2023-11-06 18:08:21 +01:00
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0.0;
}
2023-05-23 23:06:26 +02:00
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
return animData->flags & TAF_DELTADRIVEN ? animData->frameTime * animData->numFrames
: animData->frameTime * (animData->numFrames - 1);
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_Frametime
===============
*/
2023-11-06 18:08:21 +01:00
float TIKI_Anim_Frametime(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return 0.0;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0.0;
}
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
return animData->frameTime;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_Delta
===============
*/
2023-11-06 18:08:21 +01:00
void TIKI_Anim_Delta(dtiki_t *pmdl, int animnum, float *delta)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return;
}
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
VectorScale(animData->totalDelta, pmdl->load_scale, delta);
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_HasDelta
===============
*/
2023-11-06 18:08:21 +01:00
qboolean TIKI_Anim_HasDelta(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return qfalse;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return qfalse;
}
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
return animData->bHasDelta;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_DeltaOverTime
===============
*/
2023-11-06 18:08:21 +01:00
void TIKI_Anim_DeltaOverTime(dtiki_t *pTiki, int iAnimnum, float fTime1, float fTime2, vec3_t vDelta)
2016-03-27 11:49:47 +02:00
{
2023-11-06 18:08:21 +01:00
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);
}
2016-03-27 11:49:47 +02:00
}
2023-08-11 21:10:29 +02:00
/*
===============
TIKI_Anim_AngularDeltaOverTime
===============
*/
2023-11-06 18:08:21 +01:00
void TIKI_Anim_AngularDeltaOverTime(dtiki_t *pTiki, int iAnimnum, float fTime1, float fTime2, float *fDelta)
2023-08-11 21:10:29 +02:00
{
2023-11-06 18:08:21 +01:00
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;
}
2023-08-11 21:10:29 +02:00
}
2016-03-27 11:49:47 +02:00
/*
===============
TIKI_Anim_Flags
===============
*/
2023-11-06 18:08:21 +01:00
int TIKI_Anim_Flags(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return 0;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0;
}
2023-11-06 18:08:21 +01:00
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
return panimdef->flags;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_FlagsSkel
===============
*/
2023-11-06 18:08:21 +01:00
int TIKI_Anim_FlagsSkel(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return 0;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0;
}
2023-11-06 18:08:21 +01:00
skelAnimDataGameHeader_t *animData;
int flags;
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
animData = SkeletorCacheGetData(pmdl->a->m_aliases[animnum]);
flags = animData->flags;
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
if (animData->bHasDelta) {
flags |= TAF_HASDELTA;
}
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
if (animData->bHasMorph) {
flags |= TAF_HASMORPH;
}
2016-03-27 11:49:47 +02:00
2023-11-06 18:08:21 +01:00
if (animData->bHasUpper) {
flags |= TAF_HASUPPER;
}
2019-08-18 22:06:49 +02:00
2023-11-06 18:08:21 +01:00
return flags;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_HasServerCommands
===============
*/
2023-11-06 18:08:21 +01:00
qboolean TIKI_Anim_HasServerCommands(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return qfalse;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return qfalse;
}
2023-11-06 18:08:21 +01:00
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
return panimdef->num_server_cmds > 0;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_HasClientCommands
===============
*/
2023-11-06 18:08:21 +01:00
qboolean TIKI_Anim_HasClientCommands(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return qfalse;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return qfalse;
}
2023-11-06 18:08:21 +01:00
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
return panimdef->num_client_cmds > 0;
2016-03-27 11:49:47 +02:00
}
/*
===============
TIKI_Anim_CrossblendTime
===============
*/
2023-11-06 18:08:21 +01:00
float TIKI_Anim_CrossblendTime(dtiki_t *pmdl, int animnum)
2016-03-27 11:49:47 +02:00
{
2023-05-23 23:06:26 +02:00
if (!pmdl) {
2023-11-06 18:08:21 +01:00
return 0.0;
2023-05-23 23:06:26 +02:00
}
if (!pmdl->a || animnum < 0 || animnum >= pmdl->a->num_anims) {
return 0.0;
}
2023-11-06 18:08:21 +01:00
dtikianimdef_t *panimdef = pmdl->a->animdefs[animnum];
return panimdef->blendtime;
2016-03-27 11:49:47 +02:00
}