1 /**
2 * @file
3 * @brief model loading and caching
4 */
5
6 /*
7 Copyright (C) 1997-2001 Id Software, Inc.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License
11 as published by the Free Software Foundation; either version 2
12 of the License, or (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
18 See the GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, write to the Free Software
22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24 */
25
26 #include "r_local.h"
27
28 static model_t r_models[MAX_MOD_KNOWN];
29 static int r_numModels;
30 static int r_numModelsStatic;
31
32 model_t* r_mapTiles[MAX_MAPTILES];
33 int r_numMapTiles;
34
35 /* the inline * models from the current map are kept separate */
36 model_t r_modelsInline[MAX_MOD_KNOWN];
37 int r_numModelsInline;
38
39 static char* r_actorSkinNames[MAX_ACTORSKINNAME];
40 static int r_numActorSkinName;
41
42 /**
43 * @brief all supported model formats
44 * @sa modtype_t
45 */
46 static char const* const mod_extensions[] = {
47 "md2", "md3", "obj", nullptr
48 };
49
50 /**
51 * @brief Prints all loaded models
52 */
R_ModModellist_f(void)53 void R_ModModellist_f (void)
54 {
55 int i;
56 model_t* mod;
57
58 Com_Printf("Loaded models:\n");
59 Com_Printf("Type | #Slot | #Tris | #Meshes | Filename\n");
60 for (i = 0, mod = r_models; i < r_numModels; i++, mod++) {
61 if (!mod->name[0]) {
62 Com_Printf("Empty slot %i\n", i);
63 continue;
64 }
65 switch(mod->type) {
66 case mod_alias_md3:
67 Com_Printf("MD3 ");
68 break;
69 case mod_alias_md2:
70 Com_Printf("MD2 ");
71 break;
72 case mod_bsp:
73 Com_Printf("BSP ");
74 break;
75 case mod_bsp_submodel:
76 Com_Printf("SUB ");
77 break;
78 case mod_obj:
79 Com_Printf("OBJ ");
80 break;
81 default:
82 Com_Printf("%3i ", mod->type);
83 break;
84 }
85 if (mod->alias.num_meshes) {
86 int j;
87 Com_Printf(" | %5i | %7i | %7i | %s (skins: %i)\n", i, mod->alias.num_meshes, mod->alias.meshes[0].num_tris, mod->name, mod->alias.meshes[0].num_skins);
88 for (j = 0; j < mod->alias.meshes[0].num_skins; j++) {
89 mAliasSkin_t* skin = &mod->alias.meshes[0].skins[j];
90 Com_Printf(" \\-- skin %i: '%s' (texnum %i and image type %i)\n", j + 1, skin->name, skin->skin->texnum, skin->skin->type);
91 }
92 } else
93 Com_Printf(" | %5i | %7i | unknown | %s\n", i, mod->alias.num_meshes, mod->name);
94 }
95 Com_Printf("%4i models loaded\n", r_numModels);
96 Com_Printf(" - %4i static models\n", r_numModelsStatic);
97 Com_Printf(" - %4i bsp models\n", r_numMapTiles);
98 Com_Printf(" - %4i inline models\n", r_numModelsInline);
99 }
100
R_AllocModelSlot(void)101 model_t* R_AllocModelSlot (void)
102 {
103 /* get new model */
104 if (r_numModels < 0 || r_numModels >= MAX_MOD_KNOWN)
105 Com_Error(ERR_DROP, "R_ModAddMapTile: r_numModels >= MAX_MOD_KNOWN");
106 return &r_models[r_numModels++];
107 }
108
109 /**
110 * @brief Loads in a model for the given name
111 * @param[in] filename Filename relative to base dir and with extension (models/model.md2)
112 * @param[in,out] mod Structure to initialize
113 * @return True if the loading was succeed. True or false structure mod was edited.
114 */
R_LoadModel(model_t * mod,const char * filename)115 static bool R_LoadModel (model_t* mod, const char* filename)
116 {
117 byte* buf;
118 int modfilelen;
119 char animname[MAX_QPATH];
120
121 if (filename[0] == '\0')
122 Com_Error(ERR_FATAL, "R_ModForName: nullptr name");
123
124 /* load the file */
125 modfilelen = FS_LoadFile(filename, &buf);
126 if (!buf)
127 return false;
128
129 OBJZERO(*mod);
130 Q_strncpyz(mod->name, filename, sizeof(mod->name));
131
132 /* call the appropriate loader */
133 switch (LittleLong(*(unsigned *) buf)) {
134 case IDALIASHEADER:
135 /* MD2 header */
136 R_ModLoadAliasMD2Model(mod, buf, modfilelen, true);
137 break;
138
139 case IDMD3HEADER:
140 /* MD3 header */
141 R_ModLoadAliasMD3Model(mod, buf, modfilelen);
142 break;
143
144 case IDBSPHEADER:
145 Com_Error(ERR_FATAL, "R_ModForName: don't load BSPs with this function");
146 break;
147
148 default:
149 {
150 const char* ext = Com_GetExtension(filename);
151 if (ext != nullptr && !Q_strcasecmp(ext, "obj"))
152 R_LoadObjModel(mod, buf, modfilelen);
153 else
154 Com_Error(ERR_FATAL, "R_ModForName: unknown fileid for %s", mod->name);
155 }
156 }
157
158 /* load the animations */
159 Com_StripExtension(mod->name, animname, sizeof(animname));
160 Com_DefaultExtension(animname, sizeof(animname), ".anm");
161
162 /* try to load the animation file */
163 if (FS_CheckFile("%s", animname) != -1) {
164 R_ModLoadAnims(&mod->alias, animname);
165 }
166
167 FS_FreeFile(buf);
168
169 return true;
170 }
171
R_ModelExists(const char * name)172 bool R_ModelExists (const char* name)
173 {
174 if (Com_GetExtension(name) == nullptr) {
175 int i;
176 for (i = 0; mod_extensions[i] != nullptr; i++) {
177 if (FS_CheckFile("models/%s.%s", name, mod_extensions[i]) != -1) {
178 return true;
179 }
180 }
181 return false;
182 }
183
184 return FS_CheckFile("models/%s", name) != -1;
185 }
186
187 /**
188 * @brief Tries to load a model
189 * @param[in] name The model path or name (with or without extension) - see notes
190 * this parameter is always relative to the game base dir - it can also be relative
191 * to the models/ dir in the game folder
192 * @note trying all supported model formats is only supported when you are using
193 * a name without extension and relative to models/ dir
194 * @note if first char of name is a '*' - this is an inline model
195 * @note if there is not extension in the given filename the function will
196 * try to load one of the supported model formats
197 * @return nullptr if no model could be found with the given name, model_t otherwise
198 */
R_FindModel(const char * name)199 model_t* R_FindModel (const char* name)
200 {
201 model_t* mod;
202 model_t model;
203 bool loaded = false;
204 int i;
205
206 if (!name || !name[0])
207 return nullptr;
208
209 /* search for existing models */
210 mod = R_GetModel(name);
211 if (mod != nullptr)
212 return mod;
213
214 /* no inline bsp models here */
215 if (name[0] == '*')
216 return nullptr;
217
218 /* load model */
219 if (Com_GetExtension(name) == nullptr) {
220 char filename[MAX_QPATH];
221
222 for (i = 0; mod_extensions[i] != nullptr; i++) {
223 Com_sprintf(filename, sizeof(filename), "models/%s.%s", name, mod_extensions[i]);
224 loaded = R_LoadModel(&model, filename);
225 if (loaded) {
226 /* use short name */
227 Q_strncpyz(model.name, name, sizeof(model.name));
228 break;
229 }
230 }
231 } else {
232 /** @todo this case should be useless, do we ever use extension? */
233 loaded = R_LoadModel(&model, name);
234 }
235
236 if (!loaded) {
237 Com_Printf("R_FindModel: Could not find: '%s'\n", name);
238 return nullptr;
239 }
240
241 /* register the new model only after the loading is finished */
242
243 /* find a free model slot spot */
244 for (i = 0, mod = r_models; i < r_numModels; i++, mod++) {
245 if (!mod->name[0])
246 break;
247 }
248 if (i == r_numModels) {
249 if (r_numModels == MAX_MOD_KNOWN)
250 Com_Error(ERR_FATAL, "r_numModels == MAX_MOD_KNOWN");
251 r_numModels++;
252 }
253
254 /* copy the model to the slot */
255 r_models[i] = model;
256 return &r_models[i];
257 }
258
259 /**
260 * @brief Get a model for the given name already loaded.
261 * @return A model for the given name, else nullptr.
262 * @sa R_FindModel
263 * @param[in] name Short name of the model relative to base dir without (models/model)
264 */
R_GetModel(const char * name)265 model_t* R_GetModel (const char* name)
266 {
267 model_t* mod;
268 int i;
269
270 if (name[0] == '\0')
271 Com_Error(ERR_FATAL, "R_ModForName: nullptr name");
272
273 /* inline models are grabbed only from worldmodel */
274 if (name[0] == '*') {
275 i = atoi(name + 1) - 1;
276 if (i < 0 || i >= r_numModelsInline)
277 Com_Error(ERR_FATAL, "bad inline model number '%s' (%i/%i)", name, i, r_numModelsInline);
278 return &r_modelsInline[i];
279 }
280
281 /* search the currently loaded models */
282 for (i = 0, mod = r_models; i < r_numModels; i++, mod++)
283 if (Q_streq(mod->name, name))
284 return mod;
285 return nullptr;
286 }
287
288 #define MEM_TAG_STATIC_MODELS 1
289 /**
290 * @brief After all static models are loaded, switch the pool tag for these models
291 * to not free them everytime R_ShutdownModels is called
292 * @sa CL_InitAfter
293 * @sa R_FreeWorldImages
294 */
R_SwitchModelMemPoolTag(void)295 void R_SwitchModelMemPoolTag (void)
296 {
297 int i, j, k;
298 model_t* mod;
299
300 r_numModelsStatic = r_numModels;
301 Mem_ChangeTag(vid_modelPool, 0, MEM_TAG_STATIC_MODELS);
302
303 /* mark the static model textures as it_static, thus R_FreeWorldImages
304 * won't free them */
305 for (i = 0, mod = r_models; i < r_numModelsStatic; i++, mod++) {
306 if (!mod->alias.num_meshes)
307 Com_Printf("Model '%s' has no meshes\n", mod->name);
308 for (j = 0; j < mod->alias.num_meshes; j++) {
309 mAliasMesh_t* mesh = &mod->alias.meshes[j];
310 if (!mesh->num_skins)
311 Com_Printf("Model '%s' has no skins\n", mod->name);
312 for (k = 0; k < mesh->num_skins; k++) {
313 mAliasSkin_t* modelSkin = &mesh->skins[k];
314 if (modelSkin->skin != r_noTexture)
315 modelSkin->skin->type = it_static;
316 else
317 Com_Printf("No skin for #%i of '%s'\n", j, mod->name);
318 }
319 }
320 }
321
322 Com_Printf("%i static models loaded\n", r_numModels);
323 }
324
325 /**
326 * @brief Register an actorskin name
327 * @return The id where the actorskin is registered
328 */
R_ModAllocateActorSkin(const char * name)329 int R_ModAllocateActorSkin (const char* name)
330 {
331 if (r_numActorSkinName >= lengthof(r_actorSkinNames))
332 return -1;
333
334 r_actorSkinNames[r_numActorSkinName] = Mem_StrDup(name);
335
336 return r_numActorSkinName++;
337 }
338
R_UseActorSkin(void)339 bool R_UseActorSkin (void)
340 {
341 return r_numActorSkinName != 0;
342 }
343
R_GetActorSkin(int id)344 static const char* R_GetActorSkin (int id)
345 {
346 assert(id >= 0 && id < r_numActorSkinName);
347 return r_actorSkinNames[id];
348 }
349
350 /**
351 * @brief Load actor skins from a default skin to a a mesh.
352 * @param outMesh Mesh target of skins
353 * @param defaultSkin Default skin of the mesh
354 */
R_LoadActorSkinsFromModel(mAliasMesh_t * outMesh,image_t * defaultSkin)355 void R_LoadActorSkinsFromModel (mAliasMesh_t* outMesh, image_t* defaultSkin)
356 {
357 int i;
358 assert(outMesh);
359
360 outMesh->num_skins = r_numActorSkinName;
361 outMesh->skins = Mem_PoolAllocTypeN(mAliasSkin_t, outMesh->num_skins, vid_modelPool);
362
363 if (defaultSkin == r_noTexture)
364 Com_Printf("R_LoadActorSkinsFromModel: No default skin found for model \"%s\"\n", outMesh->name);
365
366 for (i = 0; i < outMesh->num_skins; i++) {
367 mAliasSkin_t* modelSkin = &outMesh->skins[i];
368 if (i == 0) {
369 modelSkin->skin = defaultSkin;
370 } else {
371 const char* skin = R_GetActorSkin(i);
372 modelSkin->skin = R_AliasModelGetSkin(nullptr, va("%s_%s", defaultSkin->name, skin));
373 /** @todo should we add warning here? */
374 if (modelSkin->skin == r_noTexture)
375 modelSkin->skin = defaultSkin;
376 }
377 Q_strncpyz(modelSkin->name, modelSkin->skin->name, sizeof(outMesh->skins[i].name));
378 }
379 }
380
381 /**
382 * @brief Frees the model pool
383 * @param complete If this is true the static mesh models are freed, too
384 * @sa R_SwitchModelMemPoolTag
385 */
R_ShutdownModels(bool complete)386 void R_ShutdownModels (bool complete)
387 {
388 int i;
389 const int start = complete ? 0 : r_numModelsStatic;
390
391 /* free the vertex buffer - but not for the static models
392 * the world, the submodels and all the misc_models are located in the
393 * r_models array */
394 for (i = start; i < r_numModels; i++) {
395 model_t* mod = &r_models[i];
396 mBspModel_t* bsp = &mod->bsp;
397
398 if (bsp->vertex_buffer)
399 qglDeleteBuffers(1, &bsp->vertex_buffer);
400 if (bsp->texcoord_buffer)
401 qglDeleteBuffers(1, &bsp->texcoord_buffer);
402 if (bsp->lmtexcoord_buffer)
403 qglDeleteBuffers(1, &bsp->lmtexcoord_buffer);
404 if (bsp->normal_buffer)
405 qglDeleteBuffers(1, &bsp->normal_buffer);
406 if (bsp->tangent_buffer)
407 qglDeleteBuffers(1, &bsp->tangent_buffer);
408 if (bsp->index_buffer)
409 qglDeleteBuffers(1, &bsp->index_buffer);
410 }
411
412 /* don't free the static models with the tag MEM_TAG_STATIC_MODELS */
413 if (complete) {
414 if (vid_modelPool)
415 Mem_FreePool(vid_modelPool);
416 if (vid_lightPool)
417 Mem_FreePool(vid_lightPool);
418 r_numModels = 0;
419 r_numModelsInline = 0;
420 r_numMapTiles = 0;
421 OBJZERO(r_models);
422 } else {
423 if (vid_modelPool)
424 Mem_FreeTag(vid_modelPool, 0);
425 if (vid_lightPool)
426 Mem_FreeTag(vid_lightPool, 0);
427 r_numModels = r_numModelsStatic;
428 }
429 }
430