openmohaa/code/tiki/tiki_files.cpp
2024-12-04 22:15:01 +01:00

1143 lines
33 KiB
C++

/*
===========================================================================
Copyright (C) 2024 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_files.cpp : TIKI File Loader
#include "q_shared.h"
#include "qcommon.h"
#include "../skeletor/skeletor.h"
#include "../skeletor/tokenizer.h"
#include "../client/client.h"
#include <tiki.h>
#include <mem_tempalloc.h>
qboolean tiki_loading;
cvar_t *dumploadedanims;
cvar_t *low_anim_memory;
cvar_t *showLoad;
cvar_t *convertAnims;
typedef struct {
char path[100];
skelAnimDataGameHeader_t *data;
int numusers;
int lookup;
} skeletorCacheEntry_t;
static int m_numInCache;
class InitSkelCache
{
public:
static class InitSkelCache init;
InitSkelCache();
};
static int m_cachedDataLookup[MAX_TIKI_ALIASES];
static skeletorCacheEntry_t m_cachedData[MAX_TIKI_ALIASES];
InitSkelCache InitSkelCache::init;
MEM_TempAlloc TIKI_allocator;
/*
===============
InitSkelCache::InitSkelCache
===============
*/
InitSkelCache::InitSkelCache()
{
int i;
for (i = 0; i < MAX_TIKI_ALIASES; i++) {
m_cachedData[i].lookup = -1;
}
}
/*
===============
TIKI_FreeStorage
===============
*/
void TIKI_FreeStorage(dloaddef_t *ld)
{
TIKI_allocator.FreeAll();
ld->tikiFile.Close();
}
/*
===============
TIKI_AllocateLoadData
===============
*/
void *TIKI_AllocateLoadData(size_t length)
{
return TIKI_allocator.Alloc(length);
}
/*
===============
TIKI_AliasExists
===============
*/
qboolean TIKI_AliasExists(dloaddef_t *ld, const char *name)
{
int i;
for (i = 0; i < ld->numanims; i++) {
if (!Q_stricmp(ld->loadanims[i]->alias, name)) {
return true;
}
}
return false;
}
/*
===============
TIKI_AddDefaultIdleAnim
===============
*/
void TIKI_AddDefaultIdleAnim(dloaddef_t *ld)
{
const char *ext;
dloadanim_t *panim;
if (ld->numskels != 1) {
return;
}
ext = strstr(ld->idleSkel, ".");
if (!ext) {
return;
}
if (TIKI_AliasExists(ld, "idle")) {
return;
}
panim = TIKI_AllocAnim(ld);
panim->alias = TIKI_CopyString("idle");
Q_strncpyz(panim->name, ld->idleSkel, ext - ld->idleSkel + 1);
panim->name[ext - ld->idleSkel] = 0;
Q_strcat(panim->name, sizeof(panim->name), ".skc");
}
/*
===============
TIKI_CopyString
===============
*/
char *TIKI_CopyString(const char *s)
{
char *result = (char *)TIKI_AllocateLoadData(strlen(s) + 1);
strcpy(result, s);
return result;
}
/*
===============
TIKI_LoadTikiAnim
===============
*/
qboolean loadtikicommands = true;
dtikianim_t *TIKI_LoadTikiAnim(const char *path)
{
dloaddef_t loaddef;
dtikianim_t *tiki = NULL;
const char *token;
float tempVec[3];
msg_t modelBuf;
str s;
char tempName[257];
const char *ext;
memset(&loaddef, 0, sizeof(dloaddef_t));
loaddef.modelBuf = &modelBuf;
TIKI_InitSetup(&loaddef);
loaddef.path = path;
loaddef.numanims = 0;
loaddef.numserverinitcmds = 0;
loaddef.numclientinitcmds = 0;
if (loaddef.tikiFile.LoadFile(path, qfalse)) {
loaddef.bInIncludesSection = false;
token = loaddef.tikiFile.GetToken(true);
if (strcmp(token, "TIKI")) {
TIKI_Error(
"TIKI_LoadTIKIfile: def file %s has wrong header (%s should be TIKI)\n",
loaddef.tikiFile.Filename(),
token
);
loaddef.tikiFile.Close();
return NULL;
}
while (loaddef.tikiFile.TokenAvailable(true)) {
token = loaddef.tikiFile.GetToken(true);
if (!Q_stricmp(token, "setup")) {
if (!TIKI_ParseSetup(&loaddef)) {
TIKI_FreeStorage(&loaddef);
return NULL;
}
} else if (!Q_stricmp(token, "init")) {
TIKI_ParseInit(&loaddef);
} else if (!Q_stricmp(token, "animations")) {
TIKI_ParseAnimations(&loaddef);
} else if (!Q_stricmp(token, "includes")) {
if (!loaddef.bInIncludesSection) {
loaddef.bInIncludesSection = TIKI_ParseIncludes(&loaddef);
} else {
TIKI_Error(
"TIKI_LoadTIKIfile: Nested Includes section in %s on line %d, the animations will be fubar\n",
token,
loaddef.tikiFile.GetLineNumber(),
loaddef.tikiFile.Filename()
);
}
} else if (!Q_stricmp(token, "}") && loaddef.bInIncludesSection) {
loaddef.bInIncludesSection = false;
} else {
TIKI_Error(
"TIKI_LoadTIKIfile: unknown section %s in %s online %d, skipping line.\n",
token,
loaddef.tikiFile.Filename(),
loaddef.tikiFile.GetLineNumber()
);
// skip the current line
while (loaddef.tikiFile.TokenAvailable(false)) {
loaddef.tikiFile.GetToken(false);
}
}
}
if (loaddef.bInIncludesSection) {
TIKI_Error("TIKI_LoadTIKIfile: Include section in %s did not terminate\n", loaddef.tikiFile.Filename());
}
} else {
loaddef.tikiFile.Close();
ext = strstr(path, ".");
if (!ext) {
return NULL;
}
Q_strncpyz(tempName, path, ext - path + 1);
tempName[ext - path] = 0;
Q_strcat(tempName, sizeof(tempName), ".skd");
WriteSkelmodel(&loaddef, tempName);
loaddef.hasSkel = true;
}
TIKI_AddDefaultIdleAnim(&loaddef);
if (loaddef.numanims) {
Com_sprintf(tempName, sizeof(tempName), "a%s", path);
UI_LoadResource(tempName);
tiki = TIKI_FillTIKIStructureSkel(&loaddef);
if (tiki) {
Com_sprintf(tempName, sizeof(tempName), "b%s", path);
UI_LoadResource(tempName);
Com_sprintf(tempName, sizeof(tempName), "c%s", path);
UI_LoadResource(tempName);
VectorSubtract(tiki->maxs, tiki->mins, tempVec);
if (VectorLength(tempVec) > 100000.0f) {
VectorSet(tiki->mins, -4.0f, -4.0f, -4.0f);
VectorSet(tiki->maxs, 4.0f, 4.0f, 4.0f);
}
TIKI_FreeStorage(&loaddef);
Com_sprintf(tempName, sizeof(tempName), "d%s", path);
UI_LoadResource(tempName);
} else {
TIKI_FreeStorage(&loaddef);
}
} else {
TIKI_Error("TIKI_LoadTIKIfile: No valid animations found in %s.\n", loaddef.tikiFile.Filename());
TIKI_FreeStorage(&loaddef);
}
return tiki;
}
/*
===============
TIKI_LoadTikiModel
===============
*/
dtiki_t *TIKI_LoadTikiModel(dtikianim_t *tikianim, const char *name, con_map<str, str> *keyValues)
{
dtiki_t *tiki;
//byte *start_ptr;
//byte *ptr;
int i, j, k;
char *strptr;
size_t defsize;
struct {
dtiki_t tiki;
short int buffer[32];
} temp;
dtiki_t* temp_tiki;
//int skel;
dloadsurface_t loadsurfaces[24];
int numSurfacesSetUp;
dtikisurface_t *tikiSurf;
dloadsurface_t *loadsurf;
int mesh;
skelHeaderGame_t *skelmodel;
skelSurfaceGame_t *surf;
int surfOffset;
qboolean found;
byte *start_ptr, *max_ptr, *ptr;
// Use a pointer directly to avoid compiler wrongly optimizing out loops
// as the mesh field of the tiki is an array of 1 element
temp_tiki = &temp.tiki;
TIKI_LoadSetup(
temp_tiki,
tikianim->name,
loadsurfaces,
&numSurfacesSetUp,
tikianim->modelData,
tikianim->modelDataSize,
keyValues
);
if (!temp_tiki->numMeshes) {
Com_DPrintf("^~^~^ Model '%s' has no skelmodel\n", tikianim->name);
return NULL;
}
defsize = sizeof(dtiki_t) - sizeof(tiki->mesh);
defsize += temp_tiki->numMeshes * sizeof(short);
defsize += strlen(name) + 1;
defsize = PAD(defsize, sizeof(void *));
defsize += temp_tiki->num_surfaces * sizeof(dtikisurface_t);
tiki = (dtiki_t *)TIKI_Alloc(defsize);
memset(tiki, 0, defsize);
start_ptr = (byte *)tiki;
max_ptr = start_ptr + defsize;
ptr = start_ptr + sizeof(dtiki_t) - sizeof(tiki->mesh) + temp_tiki->numMeshes * sizeof(tiki->mesh[0]);
tiki->a = tikianim;
tiki->m_boneList.InitChannels();
tiki->skeletor = NULL;
tiki->load_scale = temp_tiki->load_scale;
tiki->lod_scale = temp_tiki->lod_scale;
tiki->lod_bias = temp_tiki->lod_bias;
tiki->num_surfaces = temp_tiki->num_surfaces;
tiki->numMeshes = temp_tiki->numMeshes;
tiki->radius = temp_tiki->radius;
tiki->name = (char *)ptr;
strcpy(tiki->name, name);
ptr += strlen(tiki->name) + 1;
ptr = (byte *)PADP(ptr, sizeof(void *));
tikiSurf = (dtikisurface_t *)ptr;
tiki->m_boneList.ZeroChannels();
assert((byte *)(tikiSurf + temp_tiki->num_surfaces) <= max_ptr);
for (i = 0; i < temp_tiki->numMeshes; i++) {
mesh = temp_tiki->mesh[i];
tiki->mesh[i] = mesh;
skelmodel = skelcache[mesh].skel;
skelcache[mesh].numuses++;
for (j = 0; j < skelmodel->numBones; j++) {
tiki->m_boneList.AddChannel(skelmodel->pBones[j].channel);
}
}
tiki->m_boneList.PackChannels();
VectorCopy(temp_tiki->light_offset, tiki->light_offset);
VectorCopy(temp_tiki->load_origin, tiki->load_origin);
tiki->surfaces = tikiSurf;
for (i = 0; i < numSurfacesSetUp; i++) {
loadsurf = &loadsurfaces[i];
found = false;
strptr = strchr(loadsurf->name, '*');
surfOffset = 0;
if (strptr || !Q_stricmp(loadsurf->name, "all")) {
for (j = 0; j < temp_tiki->numMeshes; j++) {
mesh = temp_tiki->mesh[j];
skelmodel = TIKI_GetSkel(mesh);
surf = skelmodel->pSurfaces;
for (k = 0; k < skelmodel->numSurfaces; k++) {
tikiSurf = &tiki->surfaces[surfOffset + k];
if ((strptr && strptr != loadsurf->name
&& !strnicmp(loadsurf->name, surf->name, strptr - loadsurf->name))
|| !Q_stricmp(loadsurf->name, "all")) {
TIKI_SetupIndividualSurface(tikianim->name, tikiSurf, surf->name, loadsurf);
found = true;
}
surf = surf->pNext;
}
surfOffset += skelmodel->numSurfaces;
}
} else {
for (j = 0; j < temp_tiki->numMeshes; j++) {
mesh = temp_tiki->mesh[j];
skelmodel = TIKI_GetSkel(mesh);
surf = skelmodel->pSurfaces;
tikiSurf = &tiki->surfaces[surfOffset];
for (k = 0; k < skelmodel->numSurfaces; k++) {
if (!Q_stricmp(loadsurf->name, surf->name)) {
TIKI_SetupIndividualSurface(tikianim->name, tikiSurf, surf->name, loadsurf);
if (!tikiSurf->name[0]) {
TIKI_Warning(
"TIKI_InitTiki: Surface %i in %s(referenced in %s) has no name! Please investigate "
"and fix\n",
k,
skelmodel->name,
name
);
}
found = true;
}
surf = surf->pNext;
tikiSurf++;
}
surfOffset += skelmodel->numSurfaces;
}
}
if (!found) {
TIKI_Warning(
"TIKI_InitTiki: could not find surface '%s' in '%s' (check referenced skb/skd files).\n",
loadsurf->name,
tikianim->name
);
}
}
// Added in 2.0
// For surfaces without shader
// assign them the shader with the same name
surfOffset = 0;
for (i = 0; i < temp_tiki->numMeshes; i++, surfOffset += skelmodel->numSurfaces) {
skelmodel = TIKI_GetSkel(temp_tiki->mesh[i]);
surf = skelmodel->pSurfaces;
for (j = 0; j < skelmodel->numSurfaces; j++, surf = surf->pNext) {
tikiSurf = &tiki->surfaces[surfOffset + j];
if (tikiSurf->numskins) {
// Skip surfaces with skins
continue;
}
Q_strncpyz(tikiSurf->name, surf->name, sizeof(tikiSurf->name));
if (strlen(surf->name) != 9 || Q_strncmp(surf->name, "material", 8)) {
Q_strncpyz(tikiSurf->shader[0], surf->name, sizeof(tikiSurf->shader[0]));
}
tikiSurf->numskins = 1;
}
}
if (!tiki->radius) {
TIKI_CalcRadius(tiki);
}
return tiki;
}
/*
===============
TIKI_CalcRadius
===============
*/
void TIKI_CalcRadius(dtiki_t *tiki)
{
int j;
float radius;
float tmpVec[3];
float *bounds[2];
tiki->radius = 0.0f;
bounds[0] = &tiki->a->mins[0];
bounds[1] = &tiki->a->maxs[0];
for (j = 0; j < 4; j++) {
tmpVec[0] = bounds[j & 1][0];
tmpVec[1] = bounds[j & 1][1];
tmpVec[2] = bounds[j & 1][2];
radius = VectorLength(tmpVec);
if (radius > tiki->radius) {
tiki->radius = radius;
}
}
radius = tiki->radius * 0.7f;
tiki->radius = radius * tiki->lod_scale;
}
/*
===============
SkeletorCacheFileCallback
===============
*/
skelAnimDataGameHeader_t *SkeletorCacheFileCallback(const char *path)
{
skelAnimDataFileHeader_t *pHeader;
int iBuffLength;
char tempName[100];
char extension[100];
skelAnimDataGameHeader_t *finishedHeader;
char *buffer;
char npath[256];
Skel_ExtractFileExtension(path, extension);
if (strcmp(extension, "skc")) {
Com_DPrintf("Skeletor CacheAnimSkel: %s: File extension unknown. Attempting to open as skc file\n", path);
}
Q_strncpyz(npath, "newanim/", sizeof(npath));
Q_strcat(npath, sizeof(npath), path);
iBuffLength = TIKI_ReadFileEx(npath, (void **)&buffer, qtrue);
if (iBuffLength > 0) {
finishedHeader = skeletor_c::LoadProcessedAnim(npath, buffer, iBuffLength, path);
TIKI_FreeFile(buffer);
} else {
iBuffLength = TIKI_ReadFileEx(path, (void **)&pHeader, qtrue);
if (iBuffLength <= 0) {
Com_DPrintf("Skeletor CacheAnimSkel: Could not open binary file %s\n", path);
return NULL;
}
int ident = LittleLong(pHeader->ident);
int version = LittleLong(pHeader->version);
if (LittleLong(ident) != TIKI_SKC_HEADER_IDENT
|| (version != TIKI_SKC_HEADER_OLD_VERSION && version != TIKI_SKC_HEADER_VERSION)) {
Com_DPrintf(
"Skeletor CacheAnimSkel: anim %s has wrong header ([ident,version] = [%i,%i] should be [%i,%i])\n",
path,
ident,
version,
TIKI_SKC_HEADER_IDENT,
TIKI_SKC_HEADER_VERSION
);
TIKI_FreeFile(pHeader);
return NULL;
}
if (version == TIKI_SKC_HEADER_OLD_VERSION) {
Com_DPrintf("WARNING- DOWNGRADING TO OLD ANIMATION FORMAT FOR FILE: %s\n", path);
//
// Handle the endianness
//
pHeader->flags = LittleLong(pHeader->flags);
pHeader->nBytesUsed = LittleLong(pHeader->nBytesUsed);
pHeader->frameTime = LittleFloat(pHeader->frameTime);
pHeader->totalDelta.x = LittleFloat(pHeader->totalDelta.x);
pHeader->totalDelta.y = LittleFloat(pHeader->totalDelta.y);
pHeader->totalDelta.z = LittleFloat(pHeader->totalDelta.z);
pHeader->totalAngleDelta = LittleFloat(pHeader->totalAngleDelta);
pHeader->numChannels = LittleLong(pHeader->numChannels);
pHeader->ofsChannelNames = LittleLong(pHeader->ofsChannelNames);
pHeader->numFrames = LittleLong(pHeader->numFrames);
finishedHeader = skeletor_c::ConvertSkelFileToGame(pHeader, iBuffLength, path);
if (convertAnims && convertAnims->integer) {
skeletor_c::SaveProcessedAnim(finishedHeader, path, pHeader);
}
} else {
// looks like SKC version 14 and above are processed animations
// points the buffer to the animation data
buffer = (char *)pHeader + sizeof(int) + sizeof(int);
iBuffLength -= sizeof(int) + sizeof(int);
// loads the processed animation
finishedHeader = skeletor_c::LoadProcessedAnimEx(path, buffer, iBuffLength, path);
}
TIKI_FreeFile(pHeader);
}
if (dumploadedanims && dumploadedanims->integer) {
Com_Printf("+loadanim: %s\n", path);
}
Com_sprintf(tempName, sizeof(tempName), "g%s", path);
UI_LoadResource(tempName);
return finishedHeader;
}
/*
===============
SkeletorCacheGetData
===============
*/
skelAnimDataGameHeader_t *SkeletorCacheGetData(int index)
{
if (index < 0) {
return NULL;
}
skelAnimDataGameHeader_t *data = m_cachedData[index].data;
if (!data) {
data = SkeletorCacheFileCallback(m_cachedData[index].path);
m_cachedData[index].data = data;
}
return data;
}
/*
===============
SkeletorCacheFindFilename
===============
*/
bool SkeletorCacheFindFilename(const char *path, int *indexPtr)
{
int sortValue;
int lowerBound;
int upperBound;
int index;
lowerBound = 0;
upperBound = m_numInCache - 1;
while (lowerBound <= upperBound) {
index = (lowerBound + upperBound) / 2;
sortValue = Q_stricmp(path, m_cachedData[m_cachedDataLookup[index]].path);
if (!sortValue) {
if (indexPtr) {
*indexPtr = index;
}
return true;
}
if (sortValue < 0) {
upperBound = index - 1;
} else {
lowerBound = index + 1;
}
}
if (indexPtr) {
*indexPtr = lowerBound;
}
return false;
}
/*
===============
SkeletorCacheLoadData
===============
*/
bool SkeletorCacheLoadData(const char *path, bool precache, int newIndex)
{
int i;
skelAnimDataGameHeader_t *data;
int lookup;
if (m_numInCache >= MAX_TIKI_ALIASES) {
Com_Printf("Skeletor CacheData, Cache full, can't load %s\n", path);
return false;
}
if (strlen(path) >= 100) {
Com_Printf("^~^~^ SkeletorCache: File name over %i characters will be ignored.\n(%s)\n", 99, path);
return false;
}
if (precache) {
data = SkeletorCacheFileCallback(path);
if (!data) {
return false;
}
} else {
data = 0;
}
for (lookup = 0; lookup < MAX_TIKI_ALIASES; lookup++) {
if (m_cachedData[lookup].lookup == -1) {
break;
}
}
for (i = m_numInCache - 1; i >= newIndex; i--) {
m_cachedData[m_cachedDataLookup[i]].lookup = i + 1;
m_cachedDataLookup[i + 1] = m_cachedDataLookup[i];
}
m_cachedDataLookup[newIndex] = lookup;
m_cachedData[lookup].lookup = newIndex;
m_cachedData[lookup].data = data;
Q_strncpyz(m_cachedData[lookup].path, path, sizeof(m_cachedData[lookup].path));
m_cachedData[lookup].numusers = 0;
m_numInCache++;
return true;
}
/*
===============
SkeletorCacheUnloadData
===============
*/
void SkeletorCacheUnloadData(int index)
{
int i;
m_numInCache--;
if (dumploadedanims && dumploadedanims->integer) {
Com_Printf("-loadanim: %s\n", m_cachedData[m_cachedDataLookup[index]].path);
}
if (m_cachedData[m_cachedDataLookup[index]].data) {
skelAnimDataGameHeader_s::DeallocAnimData(m_cachedData[m_cachedDataLookup[index]].data);
m_cachedData[m_cachedDataLookup[index]].data = NULL;
}
m_cachedData[m_cachedDataLookup[index]].lookup = -1;
for (i = index; i < m_numInCache; i++) {
m_cachedDataLookup[i] = m_cachedDataLookup[i + 1];
m_cachedData[m_cachedDataLookup[i]].lookup = i;
}
}
/*
===============
SkeletorCacheCleanCache
===============
*/
void SkeletorCacheCleanCache()
{
int i;
// Fixed in OPM
// In original, i starts from 0 up to m_numInCache
// the problem is that m_numInCache can decrement
for (i = m_numInCache; i > 0; i--) {
if (!m_cachedData[m_cachedDataLookup[i - 1]].numusers) {
SkeletorCacheUnloadData(i - 1);
}
}
}
/*
===============
TikiAddToBounds
===============
*/
void TikiAddToBounds(dtikianim_t *tiki, SkelVec3 *newBounds)
{
int i;
for (i = 0; i < 3; i++) {
if (newBounds[0].val[i] < tiki->mins[i]) {
tiki->mins[i] = newBounds[0].val[i];
}
if (newBounds[1].val[i] > tiki->maxs[i]) {
tiki->maxs[i] = newBounds[1].val[i];
}
}
}
/*
===============
TIKI_AnimList_f
===============
*/
void TIKI_AnimList_f()
{
skeletorCacheEntry_t *entry;
int i;
Com_Printf("\nanimlist:\n");
for (i = 0; i < m_numInCache; i++) {
entry = &m_cachedData[m_cachedDataLookup[i]];
if (!entry) {
Com_Printf("*** NOT CACHED: ");
}
if (m_cachedData[i].path[0]) {
Com_Printf("%s\n", m_cachedData[i].path);
} else {
Com_Printf("*** EMPTY PATH ERROR\n");
}
}
for (; i < MAX_TIKI_ALIASES; i++) {
if (m_cachedData[m_cachedDataLookup[i]].path[0]) {
Com_Printf("*** CORRUPTED ENTRY\n");
}
}
}
/*
===============
TIKI_FixFrameNum
===============
*/
void TIKI_FixFrameNum(dtikianim_t *ptiki, skelAnimDataGameHeader_t *animData, dtikicmd_t *cmd, const char *alias)
{
if (cmd->frame_num >= TIKI_FRAME_LAST && cmd->frame_num < animData->numFrames) {
if (cmd->frame_num <= TIKI_FRAME_END) {
cmd->frame_num = animData->numFrames - 1;
}
} else {
TIKI_Error(
"TIKI_FixFrameNum: illegal frame number %d (total: %d) in anim '%s' in '%s'\n",
cmd->frame_num,
animData->numFrames,
alias,
ptiki->name
);
cmd->frame_num = 0;
}
}
/*
===============
TIKI_LoadAnim
===============
*/
void TIKI_LoadAnim(dtikianim_t *ptiki)
{
int i, j;
dtikianimdef_t *panim;
skelAnimDataGameHeader_t *animData;
for (i = 0; i < ptiki->num_anims; i++) {
animData = SkeletorCacheGetData(ptiki->m_aliases[i]);
if (animData) {
panim = ptiki->animdefs[i];
for (j = 0; j < panim->num_server_cmds; j++) {
TIKI_FixFrameNum(ptiki, animData, &ptiki->animdefs[i]->server_cmds[j], ptiki->animdefs[i]->alias);
}
for (j = 0; j < panim->num_client_cmds; j++) {
TIKI_FixFrameNum(ptiki, animData, &ptiki->animdefs[i]->client_cmds[j], ptiki->animdefs[i]->alias);
}
}
}
}
/*
===============
TIKI_InitTiki
===============
*/
dtikianim_t *TIKI_InitTiki(dloaddef_t *ld, size_t defsize)
{
byte *ptr;
byte *start_ptr;
byte *max_ptr;
dtikicmd_t *pcmds;
int i, k;
int numLoadedAnims;
size_t j;
size_t size;
//int anim_index;
int alias_index;
dtikianim_t *panim;
dtikianimdef_t *panimdef;
dloadanim_t *anim;
skelAnimDataGameHeader_t *data;
qboolean bModelBoundsSet = false;
bool bPrecache;
int index;
char tempName[257];
int order[MAX_TIKI_ALIASES];
short temp_aliases[MAX_TIKI_ALIASES];
panim = (dtikianim_t *)TIKI_Alloc(defsize);
start_ptr = (byte *)panim;
max_ptr = start_ptr + defsize;
ptr = start_ptr;
memset(panim, 0, defsize);
ClearBounds(panim->mins, panim->maxs);
panim->num_client_initcmds = ld->numclientinitcmds;
panim->num_server_initcmds = ld->numserverinitcmds;
panim->bIsCharacter = ld->bIsCharacter;
ptr += sizeof(dtikianim_t) - sizeof(dtikianimdef_t *) + sizeof(dtikianimdef_t *) * ld->numanims;
panim->name = (char *)ptr;
strcpy(panim->name, ld->path);
ptr += strlen(ld->path) + 1;
ptr = (byte *)PADP(ptr, sizeof(void *));
panim->server_initcmds = (dtikicmd_t *)ptr;
ptr += sizeof(*panim->server_initcmds) * panim->num_server_initcmds;
// Process server init commands
for (i = 0; i < ld->numserverinitcmds; i++) {
pcmds = &panim->server_initcmds[i];
pcmds->num_args = ld->loadserverinitcmds[i]->num_args;
pcmds->args = (char **)ptr;
ptr += sizeof(*pcmds->args) * pcmds->num_args;
for (j = 0; j < ld->loadserverinitcmds[i]->num_args; j++) {
pcmds->args[j] = (char *)ptr;
size = strlen(ld->loadserverinitcmds[i]->args[j]) + 1;
memcpy(pcmds->args[j], ld->loadserverinitcmds[i]->args[j], size);
ptr += size;
}
ptr = (byte *)PADP(ptr, sizeof(void *));
}
panim->client_initcmds = (dtikicmd_t *)ptr;
ptr += sizeof(*panim->client_initcmds) * ld->numclientinitcmds;
// Process client init commands
for (i = 0; i < ld->numclientinitcmds; i++) {
pcmds = &panim->client_initcmds[i];
pcmds->num_args = ld->loadclientinitcmds[i]->num_args;
pcmds->args = (char **)ptr;
ptr += sizeof(*pcmds->args) * pcmds->num_args;
for (j = 0; j < ld->loadclientinitcmds[i]->num_args; j++) {
pcmds->args[j] = (char *)ptr;
size = strlen(ld->loadclientinitcmds[i]->args[j]) + 1;
memcpy(pcmds->args[j], ld->loadclientinitcmds[i]->args[j], size);
ptr += size;
}
ptr = (byte *)PADP(ptr, sizeof(void *));
}
TIKI_GetAnimOrder(ld, order);
Com_sprintf(tempName, sizeof(tempName), "e%s", ld->path);
UI_LoadResource(tempName);
panim->m_aliases = temp_aliases;
assert(ptr <= max_ptr);
// Process anim commands
numLoadedAnims = 0;
for (i = 0; i < ld->numanims; i++) {
anim = ld->loadanims[order[i]];
if (!SkeletorCacheFindFilename(anim->name, &index)) {
bPrecache = false;
if ((!low_anim_memory && ld->numanims <= 49) || !low_anim_memory || !low_anim_memory->integer) {
bPrecache = true;
}
if (!SkeletorCacheLoadData(anim->name, bPrecache, index)) {
if (ld->hasSkel) {
TIKI_DPrintf("^~^~^ TIKI_InitTiki: Couldn't load %s\n", ld->path);
// Fixed in OPM
// The original game doesn't free the animation on error
TIKI_Free(panim);
return NULL;
}
TIKI_Error("TIKI_InitTiki: Failed to load animation '%s' at %s\n", anim->name, anim->location);
continue;
}
}
alias_index = m_cachedDataLookup[index];
m_cachedData[alias_index].numusers++;
panimdef = (dtikianimdef_t *)ptr;
ptr += sizeof(dtikianimdef_t);
panim->animdefs[numLoadedAnims] = panimdef;
panim->m_aliases[numLoadedAnims] = alias_index;
Q_strncpyz(panimdef->alias, anim->alias, sizeof(panimdef->alias));
panimdef->weight = anim->weight;
panimdef->flags = anim->flags;
if (!Q_stricmp(panimdef->alias, "idle")) {
data = SkeletorCacheGetData(alias_index);
if (data) {
VectorCopy(data->bounds[0].val, panim->mins);
VectorCopy(data->bounds[1].val, panim->maxs);
bModelBoundsSet = true;
}
}
if (anim->flags & TAF_RANDOM) {
j = strlen(panimdef->alias);
if (isdigit(panimdef->alias[j - 1])) {
do {
j--;
} while (isdigit(panimdef->alias[j - 1]));
panimdef->alias[j] = 0;
} else {
TIKI_DPrintf("TIKI_InitTiki: Random animation name '%s' should end with a number\n", panimdef->alias);
}
}
panimdef->blendtime = anim->blendtime;
if (loadtikicommands) {
panimdef->num_server_cmds = anim->num_server_cmds;
panimdef->num_client_cmds = anim->num_client_cmds;
} else {
panimdef->num_server_cmds = 0;
panimdef->num_client_cmds = 0;
}
panimdef->server_cmds = (dtikicmd_t *)ptr;
ptr = (byte *)(panimdef->server_cmds + anim->num_server_cmds);
// Process server anim commands
for (j = 0; j < anim->num_server_cmds; j++) {
pcmds = &panimdef->server_cmds[j];
pcmds->num_args = anim->loadservercmds[j]->num_args;
pcmds->frame_num = anim->loadservercmds[j]->frame_num;
pcmds->args = (char **)ptr;
ptr += pcmds->num_args * sizeof(*pcmds->args);
for (k = 0; k < anim->loadservercmds[j]->num_args; k++) {
pcmds->args[k] = (char *)ptr;
size = strlen(anim->loadservercmds[j]->args[k]) + 1;
memcpy(pcmds->args[k], anim->loadservercmds[j]->args[k], size);
ptr += size;
}
ptr = (byte *)PADP(ptr, sizeof(void *));
}
panimdef->client_cmds = (dtikicmd_t *)ptr;
ptr += anim->num_client_cmds * sizeof(dtikicmd_t);
// Process client anim commands
for (j = 0; j < anim->num_client_cmds; j++) {
pcmds = &panimdef->client_cmds[j];
pcmds->num_args = anim->loadclientcmds[j]->num_args;
pcmds->frame_num = anim->loadclientcmds[j]->frame_num;
pcmds->args = (char **)ptr;
ptr += pcmds->num_args * sizeof(*pcmds->args);
for (k = 0; k < anim->loadclientcmds[j]->num_args; k++) {
pcmds->args[k] = (char *)ptr;
size = strlen(anim->loadclientcmds[j]->args[k]) + 1;
memcpy(pcmds->args[k], anim->loadclientcmds[j]->args[k], size);
ptr += size;
}
ptr = (byte *)PADP(ptr, sizeof(void *));
}
assert(ptr <= max_ptr);
numLoadedAnims++;
}
panim->m_aliases = NULL;
if (numLoadedAnims) {
if (!bModelBoundsSet) {
TIKI_DPrintf("TIKI_InitTiki: no 'idle' animation found, model bounds not set for %s\n", ld->path);
}
panim->num_anims = numLoadedAnims;
panim->m_aliases = (short *)TIKI_Alloc(panim->num_anims * sizeof(short));
memcpy(panim->m_aliases, temp_aliases, panim->num_anims * sizeof(short));
panim->modelData = ptr;
panim->modelDataSize = ld->modelBuf->cursize;
memcpy(panim->modelData, ld->modelData, panim->modelDataSize);
ptr += panim->modelDataSize;
size = strlen(ld->headmodels) + 1;
panim->headmodels = (char *)ptr;
memcpy(panim->headmodels, ld->headmodels, size);
ptr += size;
size = strlen(ld->headskins) + 1;
panim->headskins = (char *)ptr;
memcpy(panim->headskins, ld->headskins, size);
ptr += size;
Com_sprintf(tempName, sizeof(tempName), "h%s", ld->path);
UI_LoadResource(tempName);
if (low_anim_memory && (!low_anim_memory->integer || !tiki_loading)) {
TIKI_LoadAnim(panim);
}
} else {
TIKI_Error("TIKI_InitTiki: No valid animations found in %s.\n", ld->path);
// Fixed in OPM
// The original game doesn't free the animation on error
TIKI_Free(panim);
panim = NULL;
}
assert(ptr <= max_ptr);
return panim;
}
/*
===============
TIKI_RemoveTiki
===============
*/
void TIKI_RemoveTiki(dtikianim_t *ptiki)
{
int i;
int alias_index;
for (i = 0; i < ptiki->num_anims; i++) {
alias_index = ptiki->m_aliases[i];
m_cachedData[alias_index].numusers--;
}
}