/* =========================================================================== 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_cache.cpp : TIKI's fast implementation #include "q_shared.h" #include "qcommon.h" #include "../skeletor/skeletor.h" #include #include #include "tiki_files.h" #include "tiki_skel.h" struct pchar { const char *m_Value; pchar() { m_Value = NULL; } pchar(const char *value) { m_Value = value; } friend bool operator==(const pchar& l, const pchar& r) { return !strcmp(l.m_Value, r.m_Value); } }; con_map *tikianimcache; con_map *tikicache; static skeletor_c *skel_entity_cache[TIKI_MAX_ENTITY_CACHE]; template<> int HashCode(const pchar& key) { return HashCode(key.m_Value); } /* =============== TIKI_FindTikiAnim =============== */ dtikianim_t *TIKI_FindTikiAnim(const char *path) { char filename[1024]; if (tikianimcache) { dtikianim_t **t; t = tikianimcache->find(filename); if (t) { return *t; } } return NULL; } /* =============== TIKI_FindTiki =============== */ dtiki_t *TIKI_FindTiki(const char *path) { char filename[1024]; if (tikicache) { dtiki_t **t; Q_strncpyz(filename, path, sizeof(filename)); FS_CanonicalFilename(filename); t = tikicache->find(filename); if (t) { return *t; } } return NULL; } /* =============== TIKI_RegisterTikiAnimFlags =============== */ dtikianim_t *TIKI_RegisterTikiAnimFlags(const char *path, qboolean use) { dtikianim_t *tiki; char filename[1024]; Q_strncpyz(filename, path, sizeof(filename)); FS_CanonicalFilename(filename); if (tikianimcache) { dtikianim_t **t; t = tikianimcache->find(filename); if (t) { return *t; } } else { tikianimcache = new con_map; } tiki = TIKI_LoadTikiAnim(filename); if (tiki) { if (use) { Com_DPrintf("^~^~^ Add the following line to the *_precache.scr map script:\n"); Com_DPrintf("cache %s\n", filename); } (*tikianimcache)[tiki->name] = tiki; } return tiki; } /* =============== TIKI_RegisterTikiAnim =============== */ dtikianim_t *TIKI_RegisterTikiAnim(const char *path) { return TIKI_RegisterTikiAnimFlags(path, qfalse); } /* =============== TIKI_RegisterTikiFlags =============== */ dtiki_t *TIKI_RegisterTikiFlags(const char *path, qboolean use) { dtiki_t *tiki = NULL; dtikianim_t *tikianim = NULL; con_map keyValues; const char *next_path; str key; str value; const char *name; char filename[1024]; char full_filename[1024]; full_filename[0] = 0; name = path; for (next_path = strstr(name, "|"); next_path; next_path = strstr(name, "|")) { key = name; key[next_path - name] = 0; name = next_path + 1; next_path = strstr(name, "|"); if (!next_path) { break; } value = name; value[next_path - name] = 0; name = next_path + 1; // add it to the entry keyValues[key] = value; strcat(full_filename, key.c_str()); strcat(full_filename, "|"); strcat(full_filename, value.c_str()); strcat(full_filename, "|"); } Q_strncpyz(filename, name, sizeof(filename)); FS_CanonicalFilename(filename); Q_strcat(full_filename, sizeof(full_filename), filename); if (!tikicache) { tikicache = new con_map; } else { dtiki_t **t; t = tikicache->find(full_filename); if (t) { return *t; } } tikianim = TIKI_RegisterTikiAnimFlags(filename, use); if (tikianim) { tiki = TIKI_LoadTikiModel(tikianim, full_filename, &keyValues); if (tiki) { // cache the tiki (*tikicache)[tiki->name] = tiki; } } return tiki; } /* =============== TIKI_RegisterTiki =============== */ dtiki_t *TIKI_RegisterTiki(const char *path) { return TIKI_RegisterTikiFlags(path, qfalse); } /* =============== TIKI_FreeAll =============== */ void TIKI_FreeAll() { dtiki_t **entry; dtikianim_t **entryanim; dtiki_t *tiki; dtikianim_t *tikianim; int i; if (tikicache) { con_map_enum en = *tikicache; for (entry = en.NextValue(); entry != NULL; entry = en.NextValue()) { skeletor_c *skeletor; tiki = *entry; skeletor = (skeletor_c *)tiki->skeletor; if (skeletor) { delete skeletor; } tiki->m_boneList.CleanUpChannels(); // Fixed in OPM // It's better to free aliases when actually clearing the anim cache /* if (tiki->a->m_aliases) { TIKI_Free(tiki->a->m_aliases); tiki->a->m_aliases = NULL; tiki->a->num_anims = 0; } */ TIKI_Free(tiki); } tikicache->clear(); } if (tikianimcache) { con_map_enum en = *tikianimcache; for (entryanim = en.NextValue(); entryanim != NULL; entryanim = en.NextValue()) { tikianim = *entryanim; TIKI_RemoveTiki(tikianim); // Fixed in OPM // Each tikianim should free their aliases if (tikianim->m_aliases) { TIKI_Free(tikianim->m_aliases); tikianim->m_aliases = NULL; tikianim->num_anims = 0; } TIKI_Free(tikianim); } tikianimcache->clear(); } tiki_loading = true; for (i = 0; i < cache_maxskel; i++) { if (skelcache[i].skel) { TIKI_FreeSkel(i); } } } /* =============== TIKI_GetSkeletor =============== */ static qboolean tiki_started; void *TIKI_GetSkeletor(dtiki_t *tiki, int entnum) { skeletor_c *skel; int i; int index; if (entnum == ENTITYNUM_NONE) { if (!tiki->skeletor) { tiki->skeletor = new skeletor_c(tiki); } skel = (skeletor_c *)tiki->skeletor; } else { // Added in 2.30 // Multiple caches per entity for (i = 0; i < TIKI_MAX_ENTITY_CACHE_PER_ENT; i++) { index = ((entnum % TIKI_MAX_ENTITIES) * TIKI_MAX_ENTITY_CACHE_PER_ENT) + i; skel = skel_entity_cache[index]; if (!skel) { break; } if (skel->m_Tiki == tiki) { return skel; } } if (i == TIKI_MAX_ENTITY_CACHE_PER_ENT) { i = 0; } index = ((entnum % TIKI_MAX_ENTITIES) * TIKI_MAX_ENTITY_CACHE_PER_ENT) + i; skel = skel_entity_cache[index]; if (skel) { delete skel; } skel = new skeletor_c(tiki); skel_entity_cache[index] = skel; } return skel; } /* =============== TIKI_DeleteSkeletor =============== */ static void TIKI_DeleteSkeletor(int entnum) { skeletor_c *skel; int i; if (entnum == ENTITYNUM_NONE) { return; } for (i = 0; i < TIKI_MAX_ENTITY_CACHE_PER_ENT; i++) { skel = skel_entity_cache[entnum * TIKI_MAX_ENTITY_CACHE_PER_ENT + i]; if (skel) { delete skel; } } } /* =============== TIKI_Begin =============== */ void TIKI_Begin(void) { int i; for (i = 0; i < TIKI_MAX_ENTITY_CACHE; i++) { skel_entity_cache[i] = 0; } tiki_started = true; } /* =============== TIKI_End =============== */ void TIKI_End(void) { int i; for (i = 0; i < MAX_GENTITIES; i++) { TIKI_DeleteSkeletor(i); } tiki_started = false; } /* =============== TIKI_FinishLoad =============== */ void TIKI_FinishLoad(void) { con_map_enum en; dtikianim_t **entry; if (!tiki_loading) { return; } tiki_loading = false; SkeletorCacheCleanCache(); if (!low_anim_memory || !low_anim_memory->integer) { return; } if (tikianimcache) { en = *tikianimcache; for (entry = en.NextValue(); entry != NULL; entry = en.NextValue()) { TIKI_LoadAnim(*entry); } } } /* =============== TIKI_FreeImages =============== */ void TIKI_FreeImages(void) { dtikisurface_t *dsurf; dtiki_t *tiki; int j, k; dtiki_t **entry; con_map_enum en; if (!tikicache) { return; } en = *tikicache; for (entry = en.NextValue(); entry != NULL; entry = en.NextValue()) { tiki = *entry; for (k = 0; k < tiki->num_surfaces; k++) { dsurf = &tiki->surfaces[k]; for (j = 0; j < dsurf->numskins; j++) { dsurf->hShader[j] = 0; } } } } /* =============== TIKI_TikiAnimList_f =============== */ void TIKI_TikiAnimList_f(void) { con_map_enum en; dtikianim_t **entry; Com_Printf("\ntikianimlist:\n"); if (tikicache) { en = *tikianimcache; for (entry = en.NextValue(); entry != NULL; entry = en.NextValue()) { Com_Printf("%s\n", (*entry)->name); } } } /* =============== TIKI_TikiList_f =============== */ void TIKI_TikiList_f(void) { con_map_enum en; dtiki_t **entry; Com_Printf("\ntikilist:\n"); if (tikicache) { en = *tikicache; for (entry = en.NextValue(); entry != NULL; entry = en.NextValue()) { Com_Printf("%s\n", (*entry)->name); } } }