2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
33 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
34 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
36 #define NUM_DETAILTEXTURES 1
37 static rtexture_t *detailtextures[NUM_DETAILTEXTURES];
38 static rtexturepool_t *detailtexturepool;
45 void Mod_BrushInit (void)
47 // Cvar_RegisterVariable(&r_subdivide_size);
48 Cvar_RegisterVariable(&halflifebsp);
49 Cvar_RegisterVariable(&r_novis);
50 Cvar_RegisterVariable(&r_miplightmaps);
51 Cvar_RegisterVariable(&r_lightmaprgba);
52 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
53 Cvar_RegisterVariable(&r_nosurftextures);
54 Cvar_RegisterVariable(&r_sortsurfaces);
55 memset(mod_novis, 0xff, sizeof(mod_novis));
58 void Mod_BrushStartup (void)
61 float vc[3], vx[3], vy[3], vn[3], lightdir[3];
62 #define DETAILRESOLUTION 256
63 qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
64 detailtexturepool = R_AllocTexturePool();
68 VectorNormalize(lightdir);
69 for (i = 0;i < NUM_DETAILTEXTURES;i++)
71 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
72 for (y = 0;y < DETAILRESOLUTION;y++)
74 for (x = 0;x < DETAILRESOLUTION;x++)
78 vc[2] = noise[y][x] * (1.0f / 32.0f);
81 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
84 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
85 VectorSubtract(vx, vc, vx);
86 VectorSubtract(vy, vc, vy);
87 CrossProduct(vx, vy, vn);
89 light = 128 - DotProduct(vn, lightdir) * 128;
90 light = bound(0, light, 255);
91 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
95 detailtextures[i] = R_LoadTexture(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE);
99 void Mod_BrushShutdown (void)
102 for (i = 0;i < NUM_DETAILTEXTURES;i++)
103 R_FreeTexture(detailtextures[i]);
104 R_FreeTexturePool(&detailtexturepool);
112 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model)
119 Mod_CheckLoaded(model);
121 // LordHavoc: modified to start at first clip node,
122 // in other words: first node of the (sub)model
123 node = model->nodes + model->hulls[0].firstclipnode;
124 while (node->contents == 0)
125 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
127 return (mleaf_t *)node;
130 int Mod_PointContents (const vec3_t p, model_t *model)
135 return CONTENTS_EMPTY;
137 Mod_CheckLoaded(model);
139 // LordHavoc: modified to start at first clip node,
140 // in other words: first node of the (sub)model
141 node = model->nodes + model->hulls[0].firstclipnode;
142 while (node->contents == 0)
143 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
145 return ((mleaf_t *)node)->contents;
148 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
150 if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
151 pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
152 pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
154 pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
155 pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
157 pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
158 pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
168 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
170 static qbyte decompressed[MAX_MAP_LEAFS/8];
175 row = (model->numleafs+7)>>3;
193 } while (out - decompressed < row);
198 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
200 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
202 return Mod_DecompressVis (leaf->compressed_vis, model);
210 static void Mod_LoadTextures (lump_t *l)
212 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
214 texture_t *tx, *tx2, *anims[10], *altanims[10];
216 qbyte *data, *mtdata, *data2;
219 loadmodel->textures = NULL;
224 m = (dmiptexlump_t *)(mod_base + l->fileofs);
226 m->nummiptex = LittleLong (m->nummiptex);
228 // add two slots for notexture walls and notexture liquids
229 loadmodel->numtextures = m->nummiptex + 2;
230 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
232 // fill out all slots with notexture
233 for (i = 0;i < loadmodel->numtextures;i++)
235 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
238 tx->texture = r_notexture;
239 if (i == loadmodel->numtextures - 1)
240 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
243 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
245 // LordHavoc: mostly rewritten map texture loader
246 for (i = 0;i < m->nummiptex;i++)
248 dofs[i] = LittleLong(dofs[i]);
249 if (dofs[i] == -1 || r_nosurftextures.integer)
251 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
253 // make sure name is no more than 15 characters
254 for (j = 0;dmiptex->name[j] && j < 15;j++)
255 name[j] = dmiptex->name[j];
258 mtwidth = LittleLong (dmiptex->width);
259 mtheight = LittleLong (dmiptex->height);
261 j = LittleLong (dmiptex->offsets[0]);
265 if (j < 40 || j + mtwidth * mtheight > l->filelen)
267 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
270 mtdata = (qbyte *)dmiptex + j;
273 if ((mtwidth & 15) || (mtheight & 15))
274 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
276 // LordHavoc: force all names to lowercase
277 for (j = 0;name[j];j++)
278 if (name[j] >= 'A' && name[j] <= 'Z')
279 name[j] += 'a' - 'A';
281 tx = loadmodel->textures[i];
282 strcpy(tx->name, name);
284 tx->height = mtheight;
286 tx->glowtexture = NULL;
287 tx->fogtexture = NULL;
291 sprintf(tx->name, "unnamed%i", i);
292 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
295 // LordHavoc: HL sky textures are entirely different than quake
296 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
298 if (loadmodel->isworldmodel)
300 data = loadimagepixels(tx->name, false, 0, 0);
303 if (image_width == 256 && image_height == 128)
311 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
313 R_InitSky (mtdata, 1);
316 else if (mtdata != NULL)
317 R_InitSky (mtdata, 1);
320 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
322 tx->fogtexture = image_masktex;
323 strcpy(name, tx->name);
324 strcat(name, "_glow");
325 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
329 if (loadmodel->ishlbsp)
331 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
334 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
335 if (R_TextureHasAlpha(tx->texture))
338 for (j = 0;j < image_width * image_height;j++)
339 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
340 strcpy(name, tx->name);
341 strcat(name, "_fog");
342 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
346 else if ((data = W_GetTexture(tx->name)))
348 // get the size from the wad texture
349 tx->width = image_width;
350 tx->height = image_height;
351 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
352 if (R_TextureHasAlpha(tx->texture))
355 for (j = 0;j < image_width * image_height;j++)
356 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
357 strcpy(name, tx->name);
358 strcat(name, "_fog");
359 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
367 tx->texture = r_notexture;
372 if (mtdata) // texture included
377 if (r_fullbrights.value && tx->name[0] != '*')
379 for (j = 0;j < tx->width*tx->height;j++)
381 if (data[j] >= 224) // fullbright
390 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
391 for (j = 0;j < tx->width*tx->height;j++)
392 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
393 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
394 strcpy(name, tx->name);
395 strcat(name, "_glow");
396 for (j = 0;j < tx->width*tx->height;j++)
397 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
398 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
402 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
404 else // no texture, and no external replacement texture was found
408 tx->texture = r_notexture;
413 if (tx->name[0] == '*')
415 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
416 // LordHavoc: some turbulent textures should be fullbright and solid
417 if (!strncmp(tx->name,"*lava",5)
418 || !strncmp(tx->name,"*teleport",9)
419 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
420 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
422 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
423 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
426 tx->flags |= SURF_LIGHTMAP;
427 if (!R_TextureHasAlpha(tx->texture))
428 tx->flags |= SURF_CLIPSOLID;
431 tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
434 // sequence the animations
435 for (i = 0;i < m->nummiptex;i++)
437 tx = loadmodel->textures[i];
438 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
440 if (tx->anim_total[0] || tx->anim_total[1])
441 continue; // already sequenced
443 // find the number of frames in the animation
444 memset (anims, 0, sizeof(anims));
445 memset (altanims, 0, sizeof(altanims));
447 for (j = i;j < m->nummiptex;j++)
449 tx2 = loadmodel->textures[j];
450 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
454 if (num >= '0' && num <= '9')
455 anims[num - '0'] = tx2;
456 else if (num >= 'a' && num <= 'j')
457 altanims[num - 'a'] = tx2;
459 Con_Printf ("Bad animating texture %s\n", tx->name);
463 for (j = 0;j < 10;j++)
470 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
473 for (j = 0;j < max;j++)
477 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
481 for (j = 0;j < altmax;j++)
485 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
494 // if there is no alternate animation, duplicate the primary
495 // animation into the alternate
497 for (k = 0;k < 10;k++)
498 altanims[k] = anims[k];
501 // link together the primary animation
502 for (j = 0;j < max;j++)
505 tx2->animated = true;
506 tx2->anim_total[0] = max;
507 tx2->anim_total[1] = altmax;
508 for (k = 0;k < 10;k++)
510 tx2->anim_frames[0][k] = anims[k];
511 tx2->anim_frames[1][k] = altanims[k];
515 // if there really is an alternate anim...
516 if (anims[0] != altanims[0])
518 // link together the alternate animation
519 for (j = 0;j < altmax;j++)
522 tx2->animated = true;
523 // the primary/alternate are reversed here
524 tx2->anim_total[0] = altmax;
525 tx2->anim_total[1] = max;
526 for (k = 0;k < 10;k++)
528 tx2->anim_frames[0][k] = altanims[k];
529 tx2->anim_frames[1][k] = anims[k];
541 static void Mod_LoadLighting (lump_t *l)
544 qbyte *in, *out, *data, d;
545 char litfilename[1024];
546 loadmodel->lightdata = NULL;
547 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
549 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
550 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
552 else // LordHavoc: bsp version 29 (normal white lighting)
554 // LordHavoc: hope is not lost yet, check for a .lit file to load
555 strcpy(litfilename, loadmodel->name);
556 COM_StripExtension(litfilename, litfilename);
557 strcat(litfilename, ".lit");
558 data = (qbyte*) COM_LoadFile (litfilename, false);
561 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
563 i = LittleLong(((int *)data)[1]);
566 Con_DPrintf("%s loaded", litfilename);
567 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
568 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
574 Con_Printf("Unknown .lit file version (%d)\n", i);
581 Con_Printf("Empty .lit file, ignoring\n");
583 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
587 // LordHavoc: oh well, expand the white lighting data
590 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
591 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
592 out = loadmodel->lightdata;
593 memcpy (in, mod_base + l->fileofs, l->filelen);
594 for (i = 0;i < l->filelen;i++)
604 void Mod_LoadLightList(void)
607 char lightsfilename[1024], *s, *t, *lightsstring;
610 strcpy(lightsfilename, loadmodel->name);
611 COM_StripExtension(lightsfilename, lightsfilename);
612 strcat(lightsfilename, ".lights");
613 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
619 while (*s && *s != '\n')
623 Mem_Free(lightsstring);
624 Host_Error("lights file must end with a newline\n");
629 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
632 while (*s && n < numlights)
635 while (*s && *s != '\n')
639 Mem_Free(lightsstring);
640 Host_Error("misparsed lights file!\n");
642 e = loadmodel->lights + n;
644 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
648 Mem_Free(lightsstring);
649 Host_Error("invalid lights file, found %d parameters on line %i, should be 13 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone style)\n", a, n + 1);
656 Mem_Free(lightsstring);
657 Host_Error("misparsed lights file!\n");
659 loadmodel->numlights = numlights;
660 Mem_Free(lightsstring);
670 static void Mod_LoadVisibility (lump_t *l)
672 loadmodel->visdata = NULL;
675 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
676 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
679 // used only for HalfLife maps
680 void Mod_ParseWadsFromEntityLump(char *data)
682 char key[128], value[4096];
687 data = COM_Parse(data);
690 if (com_token[0] != '{')
694 data = COM_Parse(data);
697 if (com_token[0] == '}')
698 break; // end of worldspawn
699 if (com_token[0] == '_')
700 strcpy(key, com_token + 1);
702 strcpy(key, com_token);
703 while (key[strlen(key)-1] == ' ') // remove trailing spaces
704 key[strlen(key)-1] = 0;
705 data = COM_Parse(data);
708 strcpy(value, com_token);
709 if (!strcmp("wad", key)) // for HalfLife maps
711 if (loadmodel->ishlbsp)
714 for (i = 0;i < 4096;i++)
715 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
721 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
722 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
724 else if (value[i] == ';' || value[i] == 0)
728 strcpy(wadname, "textures/");
729 strcat(wadname, &value[j]);
730 W_LoadTextureWadFile (wadname, false);
747 static void Mod_LoadEntities (lump_t *l)
749 loadmodel->entities = NULL;
752 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
753 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
754 if (loadmodel->ishlbsp)
755 Mod_ParseWadsFromEntityLump(loadmodel->entities);
764 static void Mod_LoadVertexes (lump_t *l)
770 in = (void *)(mod_base + l->fileofs);
771 if (l->filelen % sizeof(*in))
772 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
773 count = l->filelen / sizeof(*in);
774 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
776 loadmodel->vertexes = out;
777 loadmodel->numvertexes = count;
779 for ( i=0 ; i<count ; i++, in++, out++)
781 out->position[0] = LittleFloat (in->point[0]);
782 out->position[1] = LittleFloat (in->point[1]);
783 out->position[2] = LittleFloat (in->point[2]);
792 static void Mod_LoadSubmodels (lump_t *l)
798 in = (void *)(mod_base + l->fileofs);
799 if (l->filelen % sizeof(*in))
800 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
801 count = l->filelen / sizeof(*in);
802 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
804 loadmodel->submodels = out;
805 loadmodel->numsubmodels = count;
807 for ( i=0 ; i<count ; i++, in++, out++)
809 for (j=0 ; j<3 ; j++)
811 // spread the mins / maxs by a pixel
812 out->mins[j] = LittleFloat (in->mins[j]) - 1;
813 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
814 out->origin[j] = LittleFloat (in->origin[j]);
816 for (j=0 ; j<MAX_MAP_HULLS ; j++)
817 out->headnode[j] = LittleLong (in->headnode[j]);
818 out->visleafs = LittleLong (in->visleafs);
819 out->firstface = LittleLong (in->firstface);
820 out->numfaces = LittleLong (in->numfaces);
829 static void Mod_LoadEdges (lump_t *l)
835 in = (void *)(mod_base + l->fileofs);
836 if (l->filelen % sizeof(*in))
837 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
838 count = l->filelen / sizeof(*in);
839 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
841 loadmodel->edges = out;
842 loadmodel->numedges = count;
844 for ( i=0 ; i<count ; i++, in++, out++)
846 out->v[0] = (unsigned short)LittleShort(in->v[0]);
847 out->v[1] = (unsigned short)LittleShort(in->v[1]);
856 static void Mod_LoadTexinfo (lump_t *l)
860 int i, j, k, count, miptex;
862 in = (void *)(mod_base + l->fileofs);
863 if (l->filelen % sizeof(*in))
864 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
865 count = l->filelen / sizeof(*in);
866 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
868 loadmodel->texinfo = out;
869 loadmodel->numtexinfo = count;
871 for (i = 0;i < count;i++, in++, out++)
873 for (k = 0;k < 2;k++)
874 for (j = 0;j < 4;j++)
875 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
877 miptex = LittleLong (in->miptex);
878 out->flags = LittleLong (in->flags);
881 if (loadmodel->textures)
883 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
884 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
886 out->texture = loadmodel->textures[miptex];
888 if (out->texture == NULL)
890 // choose either the liquid notexture, or the normal notexture
891 if (out->flags & TEX_SPECIAL)
892 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
894 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
903 Fills in s->texturemins[] and s->extents[]
906 static void CalcSurfaceExtents (msurface_t *s)
908 float mins[2], maxs[2], val;
912 int bmins[2], bmaxs[2];
914 mins[0] = mins[1] = 999999999;
915 maxs[0] = maxs[1] = -999999999;
919 for (i=0 ; i<s->numedges ; i++)
921 e = loadmodel->surfedges[s->firstedge+i];
923 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
925 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
927 for (j=0 ; j<2 ; j++)
929 val = v->position[0] * tex->vecs[j][0] +
930 v->position[1] * tex->vecs[j][1] +
931 v->position[2] * tex->vecs[j][2] +
940 for (i=0 ; i<2 ; i++)
942 bmins[i] = floor(mins[i]/16);
943 bmaxs[i] = ceil(maxs[i]/16);
945 s->texturemins[i] = bmins[i] * 16;
946 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
951 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
956 mins[0] = mins[1] = mins[2] = 9999;
957 maxs[0] = maxs[1] = maxs[2] = -9999;
959 for (i = 0;i < numverts;i++)
961 for (j = 0;j < 3;j++, v++)
972 #define MAX_SUBDIVPOLYTRIANGLES 4096
973 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
975 static int subdivpolyverts, subdivpolytriangles;
976 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
977 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
979 static int subdivpolylookupvert(vec3_t v)
982 for (i = 0;i < subdivpolyverts;i++)
983 if (subdivpolyvert[i][0] == v[0]
984 && subdivpolyvert[i][1] == v[1]
985 && subdivpolyvert[i][2] == v[2])
987 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
988 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
989 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
990 return subdivpolyverts++;
993 static void SubdividePolygon (int numverts, float *verts)
995 int i, i1, i2, i3, f, b, c, p;
996 vec3_t mins, maxs, front[256], back[256];
997 float m, *pv, *cv, dist[256], frac;
1000 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1002 BoundPoly (numverts, verts, mins, maxs);
1004 for (i = 0;i < 3;i++)
1006 m = (mins[i] + maxs[i]) * 0.5;
1007 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1008 if (maxs[i] - m < 8)
1010 if (m - mins[i] < 8)
1014 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1015 dist[c] = cv[i] - m;
1018 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1022 VectorCopy (pv, front[f]);
1027 VectorCopy (pv, back[b]);
1030 if (dist[p] == 0 || dist[c] == 0)
1032 if ( (dist[p] > 0) != (dist[c] > 0) )
1035 frac = dist[p] / (dist[p] - dist[c]);
1036 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1037 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1038 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1044 SubdividePolygon (f, front[0]);
1045 SubdividePolygon (b, back[0]);
1049 i1 = subdivpolylookupvert(verts);
1050 i2 = subdivpolylookupvert(verts + 3);
1051 for (i = 2;i < numverts;i++)
1053 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1055 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1059 i3 = subdivpolylookupvert(verts + i * 3);
1060 subdivpolyindex[subdivpolytriangles][0] = i1;
1061 subdivpolyindex[subdivpolytriangles][1] = i2;
1062 subdivpolyindex[subdivpolytriangles][2] = i3;
1064 subdivpolytriangles++;
1070 Mod_GenerateWarpMesh
1072 Breaks a polygon up along axial 64 unit
1073 boundaries so that turbulent and sky warps
1074 can be done reasonably.
1077 void Mod_GenerateWarpMesh (msurface_t *surf)
1083 subdivpolytriangles = 0;
1084 subdivpolyverts = 0;
1085 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1086 if (subdivpolytriangles < 1)
1087 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1089 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1090 mesh->numverts = subdivpolyverts;
1091 mesh->numtriangles = subdivpolytriangles;
1092 mesh->vertex = (surfvertex_t *)(mesh + 1);
1093 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1094 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1096 for (i = 0;i < mesh->numtriangles;i++)
1097 for (j = 0;j < 3;j++)
1098 mesh->index[i*3+j] = subdivpolyindex[i][j];
1100 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1102 VectorCopy(subdivpolyvert[i], v->v);
1103 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1104 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1109 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1111 int i, iu, iv, *index, smax, tmax;
1112 float *in, s, t, u, v, ubase, vbase, uscale, vscale;
1115 smax = surf->extents[0] >> 4;
1116 tmax = surf->extents[1] >> 4;
1120 surf->lightmaptexturestride = 0;
1121 surf->lightmaptexture = NULL;
1129 surf->flags |= SURF_LIGHTMAP;
1130 if (r_miplightmaps.integer)
1132 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1133 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE);
1137 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1138 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
1140 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1141 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1142 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1145 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * (4 + 2 + 2 + 2 + 1) * sizeof(float));
1146 mesh->numverts = surf->poly_numverts;
1147 mesh->numtriangles = surf->poly_numverts - 2;
1148 mesh->verts = (float *)(mesh + 1);
1149 mesh->st = mesh->verts + mesh->numverts * 4;
1150 mesh->uv = mesh->st + mesh->numverts * 2;
1151 mesh->ab = mesh->uv + mesh->numverts * 2;
1152 mesh->lightmapoffsets = (int *)(mesh->ab + mesh->numverts * 2);
1153 mesh->index = mesh->lightmapoffsets + mesh->numverts;
1155 index = mesh->index;
1156 for (i = 0;i < mesh->numtriangles;i++)
1163 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1165 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1166 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1167 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0) * uscale + ubase;
1168 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0) * vscale + vbase;
1169 // LordHavoc: calc lightmap data offset for vertex lighting to use
1172 iu = bound(0, iu, smax);
1173 iv = bound(0, iv, tmax);
1175 mesh->verts[i * 4 + 0] = in[0];
1176 mesh->verts[i * 4 + 1] = in[1];
1177 mesh->verts[i * 4 + 2] = in[2];
1178 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1179 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1180 mesh->uv[i * 2 + 0] = u;
1181 mesh->uv[i * 2 + 1] = v;
1182 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1183 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1184 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1188 void Mod_GenerateVertexMesh (msurface_t *surf)
1194 surf->lightmaptexturestride = 0;
1195 surf->lightmaptexture = NULL;
1197 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * (4 + 2 + 2) * sizeof(float));
1198 mesh->numverts = surf->poly_numverts;
1199 mesh->numtriangles = surf->poly_numverts - 2;
1200 mesh->verts = (float *)(mesh + 1);
1201 mesh->st = mesh->verts + mesh->numverts * 4;
1202 mesh->ab = mesh->st + mesh->numverts * 2;
1203 mesh->index = (int *)(mesh->ab + mesh->numverts * 2);
1205 index = mesh->index;
1206 for (i = 0;i < mesh->numtriangles;i++)
1213 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1215 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1216 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1217 mesh->verts[i * 4 + 0] = in[0];
1218 mesh->verts[i * 4 + 1] = in[1];
1219 mesh->verts[i * 4 + 2] = in[2];
1220 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1221 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1222 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1223 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1227 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1230 float *vec, *vert, mins[3], maxs[3];
1232 // convert edges back to a normal polygon
1233 surf->poly_numverts = surf->numedges;
1234 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1235 for (i = 0;i < surf->numedges;i++)
1237 lindex = loadmodel->surfedges[surf->firstedge + i];
1239 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1241 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1242 VectorCopy (vec, vert);
1245 vert = surf->poly_verts;
1246 VectorCopy(vert, mins);
1247 VectorCopy(vert, maxs);
1249 for (i = 1;i < surf->poly_numverts;i++)
1251 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1252 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1253 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1256 VectorCopy(mins, surf->poly_mins);
1257 VectorCopy(maxs, surf->poly_maxs);
1258 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1259 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1260 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1268 static void Mod_LoadFaces (lump_t *l)
1272 int i, count, surfnum, planenum, ssize, tsize;
1274 in = (void *)(mod_base + l->fileofs);
1275 if (l->filelen % sizeof(*in))
1276 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1277 count = l->filelen / sizeof(*in);
1278 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1280 loadmodel->surfaces = out;
1281 loadmodel->numsurfaces = count;
1283 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1285 // FIXME: validate edges, texinfo, etc?
1286 out->firstedge = LittleLong(in->firstedge);
1287 out->numedges = LittleShort(in->numedges);
1288 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1289 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1291 i = LittleShort (in->texinfo);
1292 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1293 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1294 out->texinfo = loadmodel->texinfo + i;
1295 out->flags = out->texinfo->texture->flags;
1297 planenum = LittleShort(in->planenum);
1298 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1299 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1301 if (LittleShort(in->side))
1302 out->flags |= SURF_PLANEBACK;
1304 out->plane = loadmodel->planes + planenum;
1306 // clear lightmap (filled in later)
1307 out->lightmaptexture = NULL;
1309 // force lightmap upload on first time seeing the surface
1310 out->cached_dlight = true;
1311 out->cached_ambient = -1000;
1312 out->cached_lightscalebit = -1000;
1314 CalcSurfaceExtents (out);
1316 ssize = (out->extents[0] >> 4) + 1;
1317 tsize = (out->extents[1] >> 4) + 1;
1320 for (i = 0;i < MAXLIGHTMAPS;i++)
1321 out->styles[i] = in->styles[i];
1322 i = LittleLong(in->lightofs);
1324 out->samples = NULL;
1325 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1326 out->samples = loadmodel->lightdata + i;
1327 else // LordHavoc: white lighting (bsp version 29)
1328 out->samples = loadmodel->lightdata + (i * 3);
1330 Mod_GenerateSurfacePolygon(out);
1332 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1334 out->shader = &Cshader_sky;
1335 out->samples = NULL;
1336 Mod_GenerateVertexMesh (out);
1338 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1340 out->shader = &Cshader_water;
1341 out->samples = NULL;
1342 Mod_GenerateVertexMesh (out);
1346 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1347 out->flags |= SURF_CLIPSOLID;
1348 if (out->texinfo->flags & TEX_SPECIAL)
1350 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1351 out->shader = &Cshader_water;
1352 out->shader = &Cshader_water;
1353 out->samples = NULL;
1354 Mod_GenerateVertexMesh (out);
1356 else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1358 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1359 out->shader = &Cshader_wall_fullbright;
1360 out->samples = NULL;
1361 Mod_GenerateVertexMesh(out);
1365 // stainmap for permanent marks on walls
1366 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1368 memset(out->stainsamples, 255, ssize * tsize * 3);
1369 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1371 out->shader = &Cshader_wall_vertex;
1372 Mod_GenerateWallMesh (out, true);
1376 out->shader = &Cshader_wall_lightmap;
1377 Mod_GenerateWallMesh (out, false);
1389 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1391 node->parent = parent;
1392 if (node->contents < 0)
1394 Mod_SetParent (node->children[0], node);
1395 Mod_SetParent (node->children[1], node);
1403 static void Mod_LoadNodes (lump_t *l)
1409 in = (void *)(mod_base + l->fileofs);
1410 if (l->filelen % sizeof(*in))
1411 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1412 count = l->filelen / sizeof(*in);
1413 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1415 loadmodel->nodes = out;
1416 loadmodel->numnodes = count;
1418 for ( i=0 ; i<count ; i++, in++, out++)
1420 for (j=0 ; j<3 ; j++)
1422 out->mins[j] = LittleShort (in->mins[j]);
1423 out->maxs[j] = LittleShort (in->maxs[j]);
1426 p = LittleLong(in->planenum);
1427 out->plane = loadmodel->planes + p;
1429 out->firstsurface = LittleShort (in->firstface);
1430 out->numsurfaces = LittleShort (in->numfaces);
1432 for (j=0 ; j<2 ; j++)
1434 p = LittleShort (in->children[j]);
1436 out->children[j] = loadmodel->nodes + p;
1438 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1442 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1450 static void Mod_LoadLeafs (lump_t *l)
1456 in = (void *)(mod_base + l->fileofs);
1457 if (l->filelen % sizeof(*in))
1458 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1459 count = l->filelen / sizeof(*in);
1460 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1462 loadmodel->leafs = out;
1463 loadmodel->numleafs = count;
1465 for ( i=0 ; i<count ; i++, in++, out++)
1467 for (j=0 ; j<3 ; j++)
1469 out->mins[j] = LittleShort (in->mins[j]);
1470 out->maxs[j] = LittleShort (in->maxs[j]);
1473 p = LittleLong(in->contents);
1476 out->firstmarksurface = loadmodel->marksurfaces +
1477 LittleShort(in->firstmarksurface);
1478 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1480 p = LittleLong(in->visofs);
1482 out->compressed_vis = NULL;
1484 out->compressed_vis = loadmodel->visdata + p;
1486 for (j=0 ; j<4 ; j++)
1487 out->ambient_sound_level[j] = in->ambient_level[j];
1489 // FIXME: Insert caustics here
1498 static void Mod_LoadClipnodes (lump_t *l)
1500 dclipnode_t *in, *out;
1504 in = (void *)(mod_base + l->fileofs);
1505 if (l->filelen % sizeof(*in))
1506 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1507 count = l->filelen / sizeof(*in);
1508 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1510 loadmodel->clipnodes = out;
1511 loadmodel->numclipnodes = count;
1513 if (loadmodel->ishlbsp)
1515 hull = &loadmodel->hulls[1];
1516 hull->clipnodes = out;
1517 hull->firstclipnode = 0;
1518 hull->lastclipnode = count-1;
1519 hull->planes = loadmodel->planes;
1520 hull->clip_mins[0] = -16;
1521 hull->clip_mins[1] = -16;
1522 hull->clip_mins[2] = -36;
1523 hull->clip_maxs[0] = 16;
1524 hull->clip_maxs[1] = 16;
1525 hull->clip_maxs[2] = 36;
1526 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1528 hull = &loadmodel->hulls[2];
1529 hull->clipnodes = out;
1530 hull->firstclipnode = 0;
1531 hull->lastclipnode = count-1;
1532 hull->planes = loadmodel->planes;
1533 hull->clip_mins[0] = -32;
1534 hull->clip_mins[1] = -32;
1535 hull->clip_mins[2] = -32;
1536 hull->clip_maxs[0] = 32;
1537 hull->clip_maxs[1] = 32;
1538 hull->clip_maxs[2] = 32;
1539 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1541 hull = &loadmodel->hulls[3];
1542 hull->clipnodes = out;
1543 hull->firstclipnode = 0;
1544 hull->lastclipnode = count-1;
1545 hull->planes = loadmodel->planes;
1546 hull->clip_mins[0] = -16;
1547 hull->clip_mins[1] = -16;
1548 hull->clip_mins[2] = -18;
1549 hull->clip_maxs[0] = 16;
1550 hull->clip_maxs[1] = 16;
1551 hull->clip_maxs[2] = 18;
1552 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1556 hull = &loadmodel->hulls[1];
1557 hull->clipnodes = out;
1558 hull->firstclipnode = 0;
1559 hull->lastclipnode = count-1;
1560 hull->planes = loadmodel->planes;
1561 hull->clip_mins[0] = -16;
1562 hull->clip_mins[1] = -16;
1563 hull->clip_mins[2] = -24;
1564 hull->clip_maxs[0] = 16;
1565 hull->clip_maxs[1] = 16;
1566 hull->clip_maxs[2] = 32;
1567 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1569 hull = &loadmodel->hulls[2];
1570 hull->clipnodes = out;
1571 hull->firstclipnode = 0;
1572 hull->lastclipnode = count-1;
1573 hull->planes = loadmodel->planes;
1574 hull->clip_mins[0] = -32;
1575 hull->clip_mins[1] = -32;
1576 hull->clip_mins[2] = -24;
1577 hull->clip_maxs[0] = 32;
1578 hull->clip_maxs[1] = 32;
1579 hull->clip_maxs[2] = 64;
1580 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1583 for (i=0 ; i<count ; i++, out++, in++)
1585 out->planenum = LittleLong(in->planenum);
1586 out->children[0] = LittleShort(in->children[0]);
1587 out->children[1] = LittleShort(in->children[1]);
1588 if (out->children[0] >= count || out->children[1] >= count)
1589 Host_Error("Corrupt clipping hull (out of range child)\n");
1597 Duplicate the drawing hull structure as a clipping hull
1600 static void Mod_MakeHull0 (void)
1607 hull = &loadmodel->hulls[0];
1609 in = loadmodel->nodes;
1610 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1612 hull->clipnodes = out;
1613 hull->firstclipnode = 0;
1614 hull->lastclipnode = loadmodel->numnodes - 1;
1615 hull->planes = loadmodel->planes;
1617 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1619 out->planenum = in->plane - loadmodel->planes;
1620 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1621 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1627 Mod_LoadMarksurfaces
1630 static void Mod_LoadMarksurfaces (lump_t *l)
1635 in = (void *)(mod_base + l->fileofs);
1636 if (l->filelen % sizeof(*in))
1637 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1638 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1639 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1641 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1643 j = (unsigned) LittleShort(in[i]);
1644 if (j >= loadmodel->numsurfaces)
1645 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1646 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1655 static void Mod_LoadSurfedges (lump_t *l)
1660 in = (void *)(mod_base + l->fileofs);
1661 if (l->filelen % sizeof(*in))
1662 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1663 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1664 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1666 for (i = 0;i < loadmodel->numsurfedges;i++)
1667 loadmodel->surfedges[i] = LittleLong (in[i]);
1676 static void Mod_LoadPlanes (lump_t *l)
1682 in = (void *)(mod_base + l->fileofs);
1683 if (l->filelen % sizeof(*in))
1684 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1686 loadmodel->numplanes = l->filelen / sizeof(*in);
1687 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1689 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1691 out->normal[0] = LittleFloat (in->normal[0]);
1692 out->normal[1] = LittleFloat (in->normal[1]);
1693 out->normal[2] = LittleFloat (in->normal[2]);
1694 out->dist = LittleFloat (in->dist);
1700 #define MAX_POINTS_ON_WINDING 64
1706 double points[8][3]; // variable sized
1715 static winding_t *NewWinding (int points)
1720 if (points > MAX_POINTS_ON_WINDING)
1721 Sys_Error("NewWinding: too many points\n");
1723 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1724 w = Mem_Alloc(loadmodel->mempool, size);
1725 memset (w, 0, size);
1730 static void FreeWinding (winding_t *w)
1740 static winding_t *BaseWindingForPlane (mplane_t *p)
1742 double org[3], vright[3], vup[3], normal[3];
1745 VectorCopy(p->normal, normal);
1746 VectorVectorsDouble(normal, vright, vup);
1748 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1749 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1751 // project a really big axis aligned box onto the plane
1754 VectorScale (p->normal, p->dist, org);
1756 VectorSubtract (org, vright, w->points[0]);
1757 VectorAdd (w->points[0], vup, w->points[0]);
1759 VectorAdd (org, vright, w->points[1]);
1760 VectorAdd (w->points[1], vup, w->points[1]);
1762 VectorAdd (org, vright, w->points[2]);
1763 VectorSubtract (w->points[2], vup, w->points[2]);
1765 VectorSubtract (org, vright, w->points[3]);
1766 VectorSubtract (w->points[3], vup, w->points[3]);
1777 Clips the winding to the plane, returning the new winding on the positive side
1778 Frees the input winding.
1779 If keepon is true, an exactly on-plane winding will be saved, otherwise
1780 it will be clipped away.
1783 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1785 double dists[MAX_POINTS_ON_WINDING + 1];
1786 int sides[MAX_POINTS_ON_WINDING + 1];
1795 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1797 // determine sides for each point
1798 for (i = 0;i < in->numpoints;i++)
1800 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1801 if (dot > ON_EPSILON)
1802 sides[i] = SIDE_FRONT;
1803 else if (dot < -ON_EPSILON)
1804 sides[i] = SIDE_BACK;
1809 sides[i] = sides[0];
1810 dists[i] = dists[0];
1812 if (keepon && !counts[0] && !counts[1])
1823 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1824 if (maxpts > MAX_POINTS_ON_WINDING)
1825 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1827 neww = NewWinding (maxpts);
1829 for (i = 0;i < in->numpoints;i++)
1831 if (neww->numpoints >= maxpts)
1832 Sys_Error ("ClipWinding: points exceeded estimate");
1836 if (sides[i] == SIDE_ON)
1838 VectorCopy (p1, neww->points[neww->numpoints]);
1843 if (sides[i] == SIDE_FRONT)
1845 VectorCopy (p1, neww->points[neww->numpoints]);
1849 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1852 // generate a split point
1853 p2 = in->points[(i+1)%in->numpoints];
1855 dot = dists[i] / (dists[i]-dists[i+1]);
1856 for (j = 0;j < 3;j++)
1857 { // avoid round off error when possible
1858 if (split->normal[j] == 1)
1859 mid[j] = split->dist;
1860 else if (split->normal[j] == -1)
1861 mid[j] = -split->dist;
1863 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1866 VectorCopy (mid, neww->points[neww->numpoints]);
1870 // free the original winding
1881 Divides a winding by a plane, producing one or two windings. The
1882 original winding is not damaged or freed. If only on one side, the
1883 returned winding will be the input winding. If on both sides, two
1884 new windings will be created.
1887 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1889 double dists[MAX_POINTS_ON_WINDING + 1];
1890 int sides[MAX_POINTS_ON_WINDING + 1];
1899 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1901 // determine sides for each point
1902 for (i = 0;i < in->numpoints;i++)
1904 dot = DotProduct (in->points[i], split->normal);
1907 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1908 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1909 else sides[i] = SIDE_ON;
1912 sides[i] = sides[0];
1913 dists[i] = dists[0];
1915 *front = *back = NULL;
1928 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1930 if (maxpts > MAX_POINTS_ON_WINDING)
1931 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1933 *front = f = NewWinding (maxpts);
1934 *back = b = NewWinding (maxpts);
1936 for (i = 0;i < in->numpoints;i++)
1938 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1939 Sys_Error ("DivideWinding: points exceeded estimate");
1943 if (sides[i] == SIDE_ON)
1945 VectorCopy (p1, f->points[f->numpoints]);
1947 VectorCopy (p1, b->points[b->numpoints]);
1952 if (sides[i] == SIDE_FRONT)
1954 VectorCopy (p1, f->points[f->numpoints]);
1957 else if (sides[i] == SIDE_BACK)
1959 VectorCopy (p1, b->points[b->numpoints]);
1963 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1966 // generate a split point
1967 p2 = in->points[(i+1)%in->numpoints];
1969 dot = dists[i] / (dists[i]-dists[i+1]);
1970 for (j = 0;j < 3;j++)
1971 { // avoid round off error when possible
1972 if (split->normal[j] == 1)
1973 mid[j] = split->dist;
1974 else if (split->normal[j] == -1)
1975 mid[j] = -split->dist;
1977 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1980 VectorCopy (mid, f->points[f->numpoints]);
1982 VectorCopy (mid, b->points[b->numpoints]);
1987 typedef struct portal_s
1990 mnode_t *nodes[2]; // [0] = front side of plane
1991 struct portal_s *next[2];
1993 struct portal_s *chain; // all portals are linked into a list
1997 static portal_t *portalchain;
2004 static portal_t *AllocPortal (void)
2007 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2008 p->chain = portalchain;
2013 static void FreePortal(portal_t *p)
2018 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2020 // calculate children first
2021 if (node->children[0]->contents >= 0)
2022 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2023 if (node->children[1]->contents >= 0)
2024 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2026 // make combined bounding box from children
2027 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2028 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2029 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2030 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2031 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2032 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2035 static void Mod_FinalizePortals(void)
2037 int i, j, numportals, numpoints;
2038 portal_t *p, *pnext;
2041 mleaf_t *leaf, *endleaf;
2044 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2045 leaf = loadmodel->leafs;
2046 endleaf = leaf + loadmodel->numleafs;
2047 for (;leaf < endleaf;leaf++)
2049 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2050 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2057 for (i = 0;i < 2;i++)
2059 leaf = (mleaf_t *)p->nodes[i];
2061 for (j = 0;j < w->numpoints;j++)
2063 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2064 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2065 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2066 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2067 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2068 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2075 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2077 // tally up portal and point counts
2083 // note: this check must match the one below or it will usually corrupt memory
2084 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2085 if (p->winding && p->nodes[0] != p->nodes[1]
2086 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2087 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2090 numpoints += p->winding->numpoints * 2;
2094 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2095 loadmodel->numportals = numportals;
2096 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2097 loadmodel->numportalpoints = numpoints;
2098 // clear all leaf portal chains
2099 for (i = 0;i < loadmodel->numleafs;i++)
2100 loadmodel->leafs[i].portals = NULL;
2101 // process all portals in the global portal chain, while freeing them
2102 portal = loadmodel->portals;
2103 point = loadmodel->portalpoints;
2112 // note: this check must match the one above or it will usually corrupt memory
2113 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2114 if (p->nodes[0] != p->nodes[1]
2115 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2116 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2118 // first make the back to front portal (forward portal)
2119 portal->points = point;
2120 portal->numpoints = p->winding->numpoints;
2121 portal->plane.dist = p->plane.dist;
2122 VectorCopy(p->plane.normal, portal->plane.normal);
2123 portal->here = (mleaf_t *)p->nodes[1];
2124 portal->past = (mleaf_t *)p->nodes[0];
2126 for (j = 0;j < portal->numpoints;j++)
2128 VectorCopy(p->winding->points[j], point->position);
2131 PlaneClassify(&portal->plane);
2133 // link into leaf's portal chain
2134 portal->next = portal->here->portals;
2135 portal->here->portals = portal;
2137 // advance to next portal
2140 // then make the front to back portal (backward portal)
2141 portal->points = point;
2142 portal->numpoints = p->winding->numpoints;
2143 portal->plane.dist = -p->plane.dist;
2144 VectorNegate(p->plane.normal, portal->plane.normal);
2145 portal->here = (mleaf_t *)p->nodes[0];
2146 portal->past = (mleaf_t *)p->nodes[1];
2148 for (j = portal->numpoints - 1;j >= 0;j--)
2150 VectorCopy(p->winding->points[j], point->position);
2153 PlaneClassify(&portal->plane);
2155 // link into leaf's portal chain
2156 portal->next = portal->here->portals;
2157 portal->here->portals = portal;
2159 // advance to next portal
2162 FreeWinding(p->winding);
2174 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2177 Host_Error ("AddPortalToNodes: NULL front node");
2179 Host_Error ("AddPortalToNodes: NULL back node");
2180 if (p->nodes[0] || p->nodes[1])
2181 Host_Error ("AddPortalToNodes: already included");
2182 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2184 p->nodes[0] = front;
2185 p->next[0] = (portal_t *)front->portals;
2186 front->portals = (mportal_t *)p;
2189 p->next[1] = (portal_t *)back->portals;
2190 back->portals = (mportal_t *)p;
2195 RemovePortalFromNode
2198 static void RemovePortalFromNodes(portal_t *portal)
2202 void **portalpointer;
2204 for (i = 0;i < 2;i++)
2206 node = portal->nodes[i];
2208 portalpointer = (void **) &node->portals;
2213 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2217 if (portal->nodes[0] == node)
2219 *portalpointer = portal->next[0];
2220 portal->nodes[0] = NULL;
2222 else if (portal->nodes[1] == node)
2224 *portalpointer = portal->next[1];
2225 portal->nodes[1] = NULL;
2228 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2232 if (t->nodes[0] == node)
2233 portalpointer = (void **) &t->next[0];
2234 else if (t->nodes[1] == node)
2235 portalpointer = (void **) &t->next[1];
2237 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2242 static void Mod_RecursiveNodePortals (mnode_t *node)
2245 mnode_t *front, *back, *other_node;
2246 mplane_t clipplane, *plane;
2247 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2248 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2250 // if a leaf, we're done
2254 plane = node->plane;
2256 front = node->children[0];
2257 back = node->children[1];
2259 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2261 // create the new portal by generating a polygon for the node plane,
2262 // and clipping it by all of the other portals (which came from nodes above this one)
2263 nodeportal = AllocPortal ();
2264 nodeportal->plane = *node->plane;
2266 nodeportalwinding = BaseWindingForPlane (node->plane);
2267 side = 0; // shut up compiler warning
2268 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2270 clipplane = portal->plane;
2271 if (portal->nodes[0] == portal->nodes[1])
2272 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2273 if (portal->nodes[0] == node)
2275 else if (portal->nodes[1] == node)
2277 clipplane.dist = -clipplane.dist;
2278 VectorNegate (clipplane.normal, clipplane.normal);
2282 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2284 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2285 if (!nodeportalwinding)
2287 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2292 if (nodeportalwinding)
2294 // if the plane was not clipped on all sides, there was an error
2295 nodeportal->winding = nodeportalwinding;
2296 AddPortalToNodes (nodeportal, front, back);
2299 // split the portals of this node along this node's plane and assign them to the children of this node
2300 // (migrating the portals downward through the tree)
2301 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2303 if (portal->nodes[0] == portal->nodes[1])
2304 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2305 if (portal->nodes[0] == node)
2307 else if (portal->nodes[1] == node)
2310 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2311 nextportal = portal->next[side];
2313 other_node = portal->nodes[!side];
2314 RemovePortalFromNodes (portal);
2316 // cut the portal into two portals, one on each side of the node plane
2317 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2322 AddPortalToNodes (portal, back, other_node);
2324 AddPortalToNodes (portal, other_node, back);
2330 AddPortalToNodes (portal, front, other_node);
2332 AddPortalToNodes (portal, other_node, front);
2336 // the winding is split
2337 splitportal = AllocPortal ();
2338 temp = splitportal->chain;
2339 *splitportal = *portal;
2340 splitportal->chain = temp;
2341 splitportal->winding = backwinding;
2342 FreeWinding (portal->winding);
2343 portal->winding = frontwinding;
2347 AddPortalToNodes (portal, front, other_node);
2348 AddPortalToNodes (splitportal, back, other_node);
2352 AddPortalToNodes (portal, other_node, front);
2353 AddPortalToNodes (splitportal, other_node, back);
2357 Mod_RecursiveNodePortals(front);
2358 Mod_RecursiveNodePortals(back);
2362 static void Mod_MakePortals(void)
2365 Mod_RecursiveNodePortals (loadmodel->nodes);
2366 Mod_FinalizePortals();
2374 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2379 mempool_t *mainmempool;
2382 mod->type = mod_brush;
2384 header = (dheader_t *)buffer;
2386 i = LittleLong (header->version);
2387 if (i != BSPVERSION && i != 30)
2388 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2389 mod->ishlbsp = i == 30;
2390 if (loadmodel->isworldmodel)
2392 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2393 // until we get a texture for it...
2397 // swap all the lumps
2398 mod_base = (qbyte *)header;
2400 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2401 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2405 // store which lightmap format to use
2406 mod->lightmaprgba = r_lightmaprgba.integer;
2408 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2409 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2410 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2411 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2412 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2413 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2414 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2415 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2416 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2417 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2418 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2419 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2420 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2421 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2422 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2427 mod->numframes = 2; // regular and alternate animation
2429 mainmempool = mod->mempool;
2430 loadname = mod->name;
2432 Mod_LoadLightList ();
2435 // set up the submodels (FIXME: this is confusing)
2437 for (i = 0;i < mod->numsubmodels;i++)
2440 float dist, modelyawradius, modelradius, *vec;
2443 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2444 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2448 bm = &mod->submodels[i];
2450 mod->hulls[0].firstclipnode = bm->headnode[0];
2451 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2453 mod->hulls[j].firstclipnode = bm->headnode[j];
2454 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2457 mod->firstmodelsurface = bm->firstface;
2458 mod->nummodelsurfaces = bm->numfaces;
2460 mod->DrawSky = NULL;
2461 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2462 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2464 // we only need to have a drawsky function if it is used (usually only on world model)
2465 if (surf->shader == &Cshader_sky)
2466 mod->DrawSky = R_DrawBrushModelSky;
2467 for (k = 0;k < surf->numedges;k++)
2469 l = mod->surfedges[k + surf->firstedge];
2471 vec = mod->vertexes[mod->edges[l].v[0]].position;
2473 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2474 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2475 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2476 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2477 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2478 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2479 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2480 dist = vec[0]*vec[0]+vec[1]*vec[1];
2481 if (modelyawradius < dist)
2482 modelyawradius = dist;
2483 dist += vec[2]*vec[2];
2484 if (modelradius < dist)
2488 modelyawradius = sqrt(modelyawradius);
2489 modelradius = sqrt(modelradius);
2490 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2491 mod->yawmins[2] = mod->normalmins[2];
2492 mod->yawmaxs[2] = mod->normalmaxs[2];
2493 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2494 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2495 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2496 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2498 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2499 VectorClear(mod->normalmins);
2500 VectorClear(mod->normalmaxs);
2501 VectorClear(mod->yawmins);
2502 VectorClear(mod->yawmaxs);
2503 VectorClear(mod->rotatedmins);
2504 VectorClear(mod->rotatedmaxs);
2507 mod->numleafs = bm->visleafs;
2509 mod->Draw = R_DrawBrushModelNormal;
2510 mod->DrawShadow = NULL;
2512 // LordHavoc: only register submodels if it is the world
2513 // (prevents bsp models from replacing world submodels)
2514 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2517 // duplicate the basic information
2518 sprintf (name, "*%i", i+1);
2519 loadmodel = Mod_FindName (name);
2521 strcpy (loadmodel->name, name);
2522 // textures and memory belong to the main model
2523 loadmodel->texturepool = NULL;
2524 loadmodel->mempool = NULL;