#include "quakedef.h" enum { // values for model_t's needload NL_PRESENT, NL_NEEDS_LOADED, NL_UNREFERENCED, MAX_MOD_KNOWN = 4096, }; static char loadname[32]; // for hunk tags static model_t *mod_known; static int mod_numknown; static model_t *Mod_LoadModel(model_t *mod, bool crash); void Mod_Init(void) { mod_known = Hunk_Alloc(MAX_MOD_KNOWN * sizeof(*mod_known)); } /* Caches the data if needed */ void * Mod_Extradata(model_t *mod) { void *r; if((r = Cache_Check(&mod->cache)) != nil) return r; Mod_LoadModel(mod, true); if(!mod->cache.data) Host_Error("Mod_Extradata: caching failed: %s", mod->name); return mod->cache.data; } mleaf_t * Mod_PointInLeaf(const vec3_t p, model_t *model) { mnode_t *node; float d; mplane_t *plane; if(!model || !model->nodes) Host_Error("Mod_PointInLeaf: bad model"); node = model->nodes; while(1) { if(node->contents < 0) return (mleaf_t *)node; plane = node->plane; d = DotProduct(p, plane->normal) - plane->dist; node = node->children[d <= 0]; } } static byte * Mod_DecompressVis(byte *in, model_t *model, int *outsz) { static byte *decompressed; static int decompressed_size; int c; byte *out; int row; row = (model->numleafs + 7) / 8; if(decompressed == nil || row > decompressed_size) { decompressed_size = row; decompressed = realloc(decompressed, decompressed_size); } out = decompressed; *outsz = row; if(!in) { // no vis info, so make all visible memset(out, 0xff, row); return decompressed; } do { if(*in) { *out++ = *in++; continue; } c = in[1]; in += 2; while(c && out - decompressed < row) { *out++ = 0; c--; } } while(out - decompressed < row); return decompressed; } byte * Mod_LeafPVS(mleaf_t *leaf, model_t *model, int *outsz) { static byte *mod_novis; static int mod_novis_size; int sz; sz = ((model->numleafs + 7) / 8 + 3) & ~3; *outsz = sz; if(leaf == model->leafs) { if(mod_novis == nil || mod_novis_size < sz) { mod_novis = realloc(mod_novis, sz); mod_novis_size = sz; } memset(mod_novis, 0xff, mod_novis_size); return mod_novis; } return Mod_DecompressVis(leaf->compressed_vis, model, outsz); } void Mod_ClearAll(void) { int i; model_t *mod; for(i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { mod->needload = NL_UNREFERENCED; // FIX FOR CACHE_ALLOC ERRORS: if(mod->type == mod_sprite) mod->cache.data = nil; } } model_t * Mod_FindName(const char *name) { int i; model_t *mod, *avail = nil; if(!name[0]) Host_Error("Mod_FindName: nil name"); // search the currently loaded models for(i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { if(strcmp(mod->name, name) == 0) break; if(mod->needload == NL_UNREFERENCED) if(!avail || mod->type != mod_alias) avail = mod; } if(i == mod_numknown) { if(mod_numknown == MAX_MOD_KNOWN) { if(avail) { mod = avail; if(mod->type == mod_alias) if(Cache_Check(&mod->cache)) Cache_Free(&mod->cache); } else Host_Error("mod_numknown == MAX_MOD_KNOWN"); } else mod_numknown++; strcpy(mod->name, name); mod->needload = NL_NEEDS_LOADED; } return mod; } void Mod_TouchModel(const char *name) { model_t *mod; mod = Mod_FindName(name); if(mod->needload == NL_PRESENT) { if(mod->type == mod_alias) Cache_Check(&mod->cache); } } static model_t * Mod_LoadModel(model_t *mod, bool crash) { byte *buf; int len; if(mod->type == mod_alias) { if(Cache_Check(&mod->cache)) { mod->needload = NL_PRESENT; return mod; } } else if(mod->needload == NL_PRESENT) return mod; // because the world is so huge, load it one piece at a time if((buf = loadstklmp(mod->name, nil, 0, &len)) == nil) { if(crash) Host_Error("Mod_LoadModel: %s", lerr()); return nil; } // allocate a new model radix(mod->name, loadname); // fill it in mod->lmpfrom = fs_lmpfrom; // call the apropriate loader mod->needload = NL_PRESENT; switch(LittleLong(*(unsigned *)buf)) { case IDPOLYHEADER: Mod_LoadAliasModel(mod, buf); break; case IDSPRITEHEADER: Mod_LoadSpriteModel(mod, buf, len); break; default: Mod_LoadBrushModel(mod, buf, len); break; } return mod; } model_t * Mod_ForName(const char *name, bool crash) { return Mod_LoadModel(Mod_FindName(name), crash); } void Mod_Print_f(cmd_t *c) { int i; model_t *mod; USED(c); Con_Printf("Cached models:\n"); for(i = 0, mod = mod_known; i < mod_numknown; i++, mod++) { Con_Printf("%8p : %s", mod->cache.data, mod->name); if(mod->needload & NL_UNREFERENCED) Con_Printf(" (!R)"); if(mod->needload & NL_NEEDS_LOADED) Con_Printf(" (!P)"); Con_Printf("\n"); } }