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"};
40 void Mod_BrushInit (void)
42 Cvar_RegisterVariable(&r_subdivide_size);
43 Cvar_RegisterVariable(&halflifebsp);
44 Cvar_RegisterVariable(&r_novis);
45 Cvar_RegisterVariable(&r_miplightmaps);
46 Cvar_RegisterVariable(&r_lightmaprgba);
47 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 memset(mod_novis, 0xff, sizeof(mod_novis));
57 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
61 Mod_CheckLoaded(model);
63 // LordHavoc: modified to start at first clip node,
64 // in other words: first node of the (sub)model
65 node = model->nodes + model->hulls[0].firstclipnode;
66 while (node->contents == 0)
67 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
69 return (mleaf_t *)node;
72 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
74 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
75 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
76 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
78 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
79 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
81 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
82 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
92 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
94 static qbyte decompressed[MAX_MAP_LEAFS/8];
99 row = (model->numleafs+7)>>3;
117 } while (out - decompressed < row);
122 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
124 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
126 return Mod_DecompressVis (leaf->compressed_vis, model);
134 static void Mod_LoadTextures (lump_t *l)
136 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
138 texture_t *tx, *tx2, *anims[10], *altanims[10];
140 qbyte *data, *mtdata, *data2;
143 loadmodel->textures = NULL;
148 m = (dmiptexlump_t *)(mod_base + l->fileofs);
150 m->nummiptex = LittleLong (m->nummiptex);
152 // add two slots for notexture walls and notexture liquids
153 loadmodel->numtextures = m->nummiptex + 2;
154 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
156 // fill out all slots with notexture
157 for (i = 0;i < loadmodel->numtextures;i++)
159 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
162 tx->texture = r_notexture;
163 if (i == loadmodel->numtextures - 1)
164 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
167 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
169 // LordHavoc: mostly rewritten map texture loader
170 for (i = 0;i < m->nummiptex;i++)
172 dofs[i] = LittleLong(dofs[i]);
173 if (dofs[i] == -1 || r_nosurftextures.integer)
175 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
177 // make sure name is no more than 15 characters
178 for (j = 0;dmiptex->name[j] && j < 15;j++)
179 name[j] = dmiptex->name[j];
182 mtwidth = LittleLong (dmiptex->width);
183 mtheight = LittleLong (dmiptex->height);
185 j = LittleLong (dmiptex->offsets[0]);
189 if (j < 40 || j + mtwidth * mtheight > l->filelen)
191 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
194 mtdata = (qbyte *)dmiptex + j;
197 if ((mtwidth & 15) || (mtheight & 15))
198 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
200 // LordHavoc: force all names to lowercase
201 for (j = 0;name[j];j++)
202 if (name[j] >= 'A' && name[j] <= 'Z')
203 name[j] += 'a' - 'A';
205 tx = loadmodel->textures[i];
206 strcpy(tx->name, name);
208 tx->height = mtheight;
210 tx->glowtexture = NULL;
211 tx->fogtexture = NULL;
215 sprintf(tx->name, "unnamed%i", i);
216 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
219 // LordHavoc: HL sky textures are entirely different than quake
220 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
222 if (loadmodel->isworldmodel)
224 data = loadimagepixels(tx->name, false, 0, 0);
227 if (image_width == 256 && image_height == 128)
235 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
237 R_InitSky (mtdata, 1);
240 else if (mtdata != NULL)
241 R_InitSky (mtdata, 1);
244 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
246 tx->fogtexture = image_masktex;
247 strcpy(name, tx->name);
248 strcat(name, "_glow");
249 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
253 if (loadmodel->ishlbsp)
255 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
258 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
259 if (R_TextureHasAlpha(tx->texture))
262 for (j = 0;j < image_width * image_height;j++)
263 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
264 strcpy(name, tx->name);
265 strcat(name, "_fog");
266 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
270 else if ((data = W_GetTexture(tx->name)))
272 // get the size from the wad texture
273 tx->width = image_width;
274 tx->height = image_height;
275 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
276 if (R_TextureHasAlpha(tx->texture))
279 for (j = 0;j < image_width * image_height;j++)
280 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
281 strcpy(name, tx->name);
282 strcat(name, "_fog");
283 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
291 tx->texture = r_notexture;
296 if (mtdata) // texture included
301 if (r_fullbrights.value && tx->name[0] != '*')
303 for (j = 0;j < tx->width*tx->height;j++)
305 if (data[j] >= 224) // fullbright
314 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
315 for (j = 0;j < tx->width*tx->height;j++)
316 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
317 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
318 strcpy(name, tx->name);
319 strcat(name, "_glow");
320 for (j = 0;j < tx->width*tx->height;j++)
321 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
322 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
326 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
328 else // no texture, and no external replacement texture was found
332 tx->texture = r_notexture;
337 if (tx->name[0] == '*')
339 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
340 // LordHavoc: some turbulent textures should be fullbright and solid
341 if (!strncmp(tx->name,"*lava",5)
342 || !strncmp(tx->name,"*teleport",9)
343 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
344 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
346 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
347 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
350 tx->flags |= SURF_LIGHTMAP;
351 if (!R_TextureHasAlpha(tx->texture))
352 tx->flags |= SURF_CLIPSOLID;
356 // sequence the animations
357 for (i = 0;i < m->nummiptex;i++)
359 tx = loadmodel->textures[i];
360 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
362 if (tx->anim_total[0] || tx->anim_total[1])
363 continue; // already sequenced
365 // find the number of frames in the animation
366 memset (anims, 0, sizeof(anims));
367 memset (altanims, 0, sizeof(altanims));
369 for (j = i;j < m->nummiptex;j++)
371 tx2 = loadmodel->textures[j];
372 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
376 if (num >= '0' && num <= '9')
377 anims[num - '0'] = tx2;
378 else if (num >= 'a' && num <= 'j')
379 altanims[num - 'a'] = tx2;
381 Con_Printf ("Bad animating texture %s\n", tx->name);
385 for (j = 0;j < 10;j++)
392 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
395 for (j = 0;j < max;j++)
399 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
403 for (j = 0;j < altmax;j++)
407 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
416 // if there is no alternate animation, duplicate the primary
417 // animation into the alternate
419 for (k = 0;k < 10;k++)
420 altanims[k] = anims[k];
423 // link together the primary animation
424 for (j = 0;j < max;j++)
427 tx2->animated = true;
428 tx2->anim_total[0] = max;
429 tx2->anim_total[1] = altmax;
430 for (k = 0;k < 10;k++)
432 tx2->anim_frames[0][k] = anims[k];
433 tx2->anim_frames[1][k] = altanims[k];
437 // if there really is an alternate anim...
438 if (anims[0] != altanims[0])
440 // link together the alternate animation
441 for (j = 0;j < altmax;j++)
444 tx2->animated = true;
445 // the primary/alternate are reversed here
446 tx2->anim_total[0] = altmax;
447 tx2->anim_total[1] = max;
448 for (k = 0;k < 10;k++)
450 tx2->anim_frames[0][k] = altanims[k];
451 tx2->anim_frames[1][k] = anims[k];
463 static void Mod_LoadLighting (lump_t *l)
466 qbyte *in, *out, *data, d;
467 char litfilename[1024];
468 loadmodel->lightdata = NULL;
469 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
471 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
472 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
474 else // LordHavoc: bsp version 29 (normal white lighting)
476 // LordHavoc: hope is not lost yet, check for a .lit file to load
477 strcpy(litfilename, loadmodel->name);
478 COM_StripExtension(litfilename, litfilename);
479 strcat(litfilename, ".lit");
480 data = (qbyte*) COM_LoadFile (litfilename, false);
483 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
485 i = LittleLong(((int *)data)[1]);
488 Con_DPrintf("%s loaded", litfilename);
489 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
490 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
496 Con_Printf("Unknown .lit file version (%d)\n", i);
503 Con_Printf("Empty .lit file, ignoring\n");
505 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
509 // LordHavoc: oh well, expand the white lighting data
512 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
513 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
514 out = loadmodel->lightdata;
515 memcpy (in, mod_base + l->fileofs, l->filelen);
516 for (i = 0;i < l->filelen;i++)
526 void Mod_LoadLightList(void)
529 char lightsfilename[1024], *s, *t, *lightsstring;
532 strcpy(lightsfilename, loadmodel->name);
533 COM_StripExtension(lightsfilename, lightsfilename);
534 strcat(lightsfilename, ".lights");
535 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
541 while (*s && *s != '\n')
545 Mem_Free(lightsstring);
546 Host_Error("lights file must end with a newline\n");
551 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
554 while (*s && n < numlights)
557 while (*s && *s != '\n')
561 Mem_Free(lightsstring);
562 Host_Error("misparsed lights file!\n");
564 e = loadmodel->lights + n;
566 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);
570 Mem_Free(lightsstring);
571 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);
578 Mem_Free(lightsstring);
579 Host_Error("misparsed lights file!\n");
581 loadmodel->numlights = numlights;
582 Mem_Free(lightsstring);
592 static void Mod_LoadVisibility (lump_t *l)
594 loadmodel->visdata = NULL;
597 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
598 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
601 // used only for HalfLife maps
602 void Mod_ParseWadsFromEntityLump(char *data)
604 char key[128], value[4096];
609 data = COM_Parse(data);
612 if (com_token[0] != '{')
616 data = COM_Parse(data);
619 if (com_token[0] == '}')
620 break; // end of worldspawn
621 if (com_token[0] == '_')
622 strcpy(key, com_token + 1);
624 strcpy(key, com_token);
625 while (key[strlen(key)-1] == ' ') // remove trailing spaces
626 key[strlen(key)-1] = 0;
627 data = COM_Parse(data);
630 strcpy(value, com_token);
631 if (!strcmp("wad", key)) // for HalfLife maps
633 if (loadmodel->ishlbsp)
636 for (i = 0;i < 4096;i++)
637 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
643 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
644 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
646 else if (value[i] == ';' || value[i] == 0)
650 strcpy(wadname, "textures/");
651 strcat(wadname, &value[j]);
652 W_LoadTextureWadFile (wadname, false);
669 static void Mod_LoadEntities (lump_t *l)
671 loadmodel->entities = NULL;
674 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
675 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
676 if (loadmodel->ishlbsp)
677 Mod_ParseWadsFromEntityLump(loadmodel->entities);
686 static void Mod_LoadVertexes (lump_t *l)
692 in = (void *)(mod_base + l->fileofs);
693 if (l->filelen % sizeof(*in))
694 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
695 count = l->filelen / sizeof(*in);
696 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
698 loadmodel->vertexes = out;
699 loadmodel->numvertexes = count;
701 for ( i=0 ; i<count ; i++, in++, out++)
703 out->position[0] = LittleFloat (in->point[0]);
704 out->position[1] = LittleFloat (in->point[1]);
705 out->position[2] = LittleFloat (in->point[2]);
714 static void Mod_LoadSubmodels (lump_t *l)
720 in = (void *)(mod_base + l->fileofs);
721 if (l->filelen % sizeof(*in))
722 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
723 count = l->filelen / sizeof(*in);
724 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
726 loadmodel->submodels = out;
727 loadmodel->numsubmodels = count;
729 for ( i=0 ; i<count ; i++, in++, out++)
731 for (j=0 ; j<3 ; j++)
733 // spread the mins / maxs by a pixel
734 out->mins[j] = LittleFloat (in->mins[j]) - 1;
735 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
736 out->origin[j] = LittleFloat (in->origin[j]);
738 for (j=0 ; j<MAX_MAP_HULLS ; j++)
739 out->headnode[j] = LittleLong (in->headnode[j]);
740 out->visleafs = LittleLong (in->visleafs);
741 out->firstface = LittleLong (in->firstface);
742 out->numfaces = LittleLong (in->numfaces);
751 static void Mod_LoadEdges (lump_t *l)
757 in = (void *)(mod_base + l->fileofs);
758 if (l->filelen % sizeof(*in))
759 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
760 count = l->filelen / sizeof(*in);
761 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
763 loadmodel->edges = out;
764 loadmodel->numedges = count;
766 for ( i=0 ; i<count ; i++, in++, out++)
768 out->v[0] = (unsigned short)LittleShort(in->v[0]);
769 out->v[1] = (unsigned short)LittleShort(in->v[1]);
778 static void Mod_LoadTexinfo (lump_t *l)
782 int i, j, k, count, miptex;
784 in = (void *)(mod_base + l->fileofs);
785 if (l->filelen % sizeof(*in))
786 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
787 count = l->filelen / sizeof(*in);
788 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
790 loadmodel->texinfo = out;
791 loadmodel->numtexinfo = count;
793 for (i = 0;i < count;i++, in++, out++)
795 for (k = 0;k < 2;k++)
796 for (j = 0;j < 4;j++)
797 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
799 miptex = LittleLong (in->miptex);
800 out->flags = LittleLong (in->flags);
803 if (loadmodel->textures)
805 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
806 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
808 out->texture = loadmodel->textures[miptex];
810 if (out->texture == NULL)
812 // choose either the liquid notexture, or the normal notexture
813 if (out->flags & TEX_SPECIAL)
814 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
816 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
825 Fills in s->texturemins[] and s->extents[]
828 static void CalcSurfaceExtents (msurface_t *s)
830 float mins[2], maxs[2], val;
834 int bmins[2], bmaxs[2];
836 mins[0] = mins[1] = 999999999;
837 maxs[0] = maxs[1] = -999999999;
841 for (i=0 ; i<s->numedges ; i++)
843 e = loadmodel->surfedges[s->firstedge+i];
845 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
847 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
849 for (j=0 ; j<2 ; j++)
851 val = v->position[0] * tex->vecs[j][0] +
852 v->position[1] * tex->vecs[j][1] +
853 v->position[2] * tex->vecs[j][2] +
862 for (i=0 ; i<2 ; i++)
864 bmins[i] = floor(mins[i]/16);
865 bmaxs[i] = ceil(maxs[i]/16);
867 s->texturemins[i] = bmins[i] * 16;
868 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
873 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
878 mins[0] = mins[1] = mins[2] = 9999;
879 maxs[0] = maxs[1] = maxs[2] = -9999;
881 for (i = 0;i < numverts;i++)
883 for (j = 0;j < 3;j++, v++)
893 #define MAX_SUBDIVPOLYTRIANGLES 4096
894 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
896 static int subdivpolyverts, subdivpolytriangles;
897 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
898 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
900 static int subdivpolylookupvert(vec3_t v)
903 for (i = 0;i < subdivpolyverts;i++)
904 if (subdivpolyvert[i][0] == v[0]
905 && subdivpolyvert[i][1] == v[1]
906 && subdivpolyvert[i][2] == v[2])
908 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
909 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
910 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
911 return subdivpolyverts++;
914 static void SubdividePolygon (int numverts, float *verts)
916 int i, i1, i2, i3, f, b, c, p;
917 vec3_t mins, maxs, front[256], back[256];
918 float m, *pv, *cv, dist[256], frac;
921 Host_Error ("SubdividePolygon: ran out of verts in buffer");
923 BoundPoly (numverts, verts, mins, maxs);
925 for (i = 0;i < 3;i++)
927 m = (mins[i] + maxs[i]) * 0.5;
928 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
935 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
939 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
943 VectorCopy (pv, front[f]);
948 VectorCopy (pv, back[b]);
951 if (dist[p] == 0 || dist[c] == 0)
953 if ( (dist[p] > 0) != (dist[c] > 0) )
956 frac = dist[p] / (dist[p] - dist[c]);
957 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
958 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
959 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
965 SubdividePolygon (f, front[0]);
966 SubdividePolygon (b, back[0]);
970 i1 = subdivpolylookupvert(verts);
971 i2 = subdivpolylookupvert(verts + 3);
972 for (i = 2;i < numverts;i++)
974 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
976 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
980 i3 = subdivpolylookupvert(verts + i * 3);
981 subdivpolyindex[subdivpolytriangles][0] = i1;
982 subdivpolyindex[subdivpolytriangles][1] = i2;
983 subdivpolyindex[subdivpolytriangles][2] = i3;
985 subdivpolytriangles++;
993 Breaks a polygon up along axial 64 unit
994 boundaries so that turbulent and sky warps
995 can be done reasonably.
998 void Mod_GenerateWarpMesh (msurface_t *surf)
1004 subdivpolytriangles = 0;
1005 subdivpolyverts = 0;
1006 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1007 if (subdivpolytriangles < 1)
1008 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1010 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1011 mesh->numverts = subdivpolyverts;
1012 mesh->numtriangles = subdivpolytriangles;
1013 mesh->vertex = (surfvertex_t *)(mesh + 1);
1014 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1015 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1017 for (i = 0;i < mesh->numtriangles;i++)
1018 for (j = 0;j < 3;j++)
1019 mesh->index[i*3+j] = subdivpolyindex[i][j];
1021 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1023 VectorCopy(subdivpolyvert[i], v->v);
1024 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1025 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1029 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1031 int i, is, it, *index, smax, tmax;
1036 smax = surf->extents[0] >> 4;
1037 tmax = surf->extents[1] >> 4;
1038 surf->lightmaptexturestride = 0;
1039 surf->lightmaptexture = NULL;
1041 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1042 mesh->numverts = surf->poly_numverts;
1043 mesh->numtriangles = surf->poly_numverts - 2;
1044 mesh->vertex = (surfvertex_t *)(mesh + 1);
1045 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1046 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1048 index = mesh->index;
1049 for (i = 0;i < mesh->numtriangles;i++)
1056 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1058 VectorCopy (in, out->v);
1060 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1061 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1063 out->st[0] = s / surf->texinfo->texture->width;
1064 out->st[1] = t / surf->texinfo->texture->height;
1066 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1067 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1069 // lightmap coordinates
1073 // LordHavoc: calc lightmap data offset for vertex lighting to use
1076 is = bound(0, is, smax);
1077 it = bound(0, it, tmax);
1078 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1082 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1084 int i, is, it, *index, smax, tmax;
1085 float *in, s, t, xbase, ybase, xscale, yscale;
1089 surf->flags |= SURF_LIGHTMAP;
1090 smax = surf->extents[0] >> 4;
1091 tmax = surf->extents[1] >> 4;
1092 if (r_miplightmaps.integer)
1094 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1095 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE, NULL, NULL, 0);
1099 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1100 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE, NULL, NULL, 0);
1102 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1103 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1104 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1106 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1107 mesh->numverts = surf->poly_numverts;
1108 mesh->numtriangles = surf->poly_numverts - 2;
1109 mesh->vertex = (surfvertex_t *)(mesh + 1);
1110 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1111 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1113 index = mesh->index;
1114 for (i = 0;i < mesh->numtriangles;i++)
1121 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1123 VectorCopy (in, out->v);
1125 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1126 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1128 out->st[0] = s / surf->texinfo->texture->width;
1129 out->st[1] = t / surf->texinfo->texture->height;
1131 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1132 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1134 // lightmap coordinates
1135 out->uv[0] = s * xscale + xbase;
1136 out->uv[1] = t * yscale + ybase;
1138 // LordHavoc: calc lightmap data offset for vertex lighting to use
1141 is = bound(0, is, smax);
1142 it = bound(0, it, tmax);
1143 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1147 void Mod_GenerateVertexMesh (msurface_t *surf)
1154 surf->lightmaptexturestride = 0;
1155 surf->lightmaptexture = NULL;
1157 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1158 mesh->numverts = surf->poly_numverts;
1159 mesh->numtriangles = surf->poly_numverts - 2;
1160 mesh->vertex = (surfvertex_t *)(mesh + 1);
1161 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1162 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1164 index = mesh->index;
1165 for (i = 0;i < mesh->numtriangles;i++)
1172 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1174 VectorCopy (in, out->v);
1175 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1176 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1180 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1187 // convert edges back to a normal polygon
1188 surf->poly_numverts = surf->numedges;
1189 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1190 for (i = 0;i < surf->numedges;i++)
1192 lindex = loadmodel->surfedges[surf->firstedge + i];
1194 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1196 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1197 VectorCopy (vec, vert);
1202 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1204 int j, base, tricount, newvertexcount, *index, *vertexremap;
1205 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1206 if (s->mesh->numtriangles > 1000)
1208 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1213 while (base < s->mesh->numtriangles)
1215 tricount = s->mesh->numtriangles - base;
1216 if (tricount > 1000)
1218 index = s->mesh->index + base * 3;
1222 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1223 for (j = 0;j < tricount * 3;j++)
1224 if (vertexremap[index[j]] < 0)
1225 vertexremap[index[j]] = newvertexcount++;
1227 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1228 newmesh->chain = NULL;
1229 newmesh->numverts = newvertexcount;
1230 newmesh->numtriangles = tricount;
1231 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1232 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1233 for (j = 0;j < tricount * 3;j++)
1235 newmesh->index[j] = vertexremap[index[j]];
1236 // yes this copies the same vertex multiple times in many cases... but that's ok...
1237 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1240 oldmesh->chain = newmesh;
1242 firstmesh = newmesh;
1245 Mem_Free(vertexremap);
1247 s->mesh = firstmesh;
1256 static void Mod_LoadFaces (lump_t *l)
1260 int i, count, surfnum, planenum, ssize, tsize;
1262 in = (void *)(mod_base + l->fileofs);
1263 if (l->filelen % sizeof(*in))
1264 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1265 count = l->filelen / sizeof(*in);
1266 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1268 loadmodel->surfaces = out;
1269 loadmodel->numsurfaces = count;
1271 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1273 // FIXME: validate edges, texinfo, etc?
1274 out->firstedge = LittleLong(in->firstedge);
1275 out->numedges = LittleShort(in->numedges);
1276 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1277 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1279 i = LittleShort (in->texinfo);
1280 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1281 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1282 out->texinfo = loadmodel->texinfo + i;
1283 out->flags = out->texinfo->texture->flags;
1285 planenum = LittleShort(in->planenum);
1286 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1287 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1289 if (LittleShort(in->side))
1290 out->flags |= SURF_PLANEBACK;
1292 out->plane = loadmodel->planes + planenum;
1294 // clear lightmap (filled in later)
1295 out->lightmaptexture = NULL;
1297 // force lightmap upload on first time seeing the surface
1298 out->cached_dlight = true;
1299 out->cached_ambient = -1000;
1300 out->cached_lightscalebit = -1000;
1302 CalcSurfaceExtents (out);
1304 ssize = (out->extents[0] >> 4) + 1;
1305 tsize = (out->extents[1] >> 4) + 1;
1308 for (i = 0;i < MAXLIGHTMAPS;i++)
1309 out->styles[i] = in->styles[i];
1310 i = LittleLong(in->lightofs);
1312 out->samples = NULL;
1313 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1314 out->samples = loadmodel->lightdata + i;
1315 else // LordHavoc: white lighting (bsp version 29)
1316 out->samples = loadmodel->lightdata + (i * 3);
1318 Mod_GenerateSurfacePolygon(out);
1320 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1322 out->shader = &Cshader_sky;
1323 out->samples = NULL;
1324 Mod_GenerateWarpMesh (out);
1326 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1328 out->shader = &Cshader_water;
1329 out->samples = NULL;
1330 Mod_GenerateWarpMesh (out);
1334 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1335 out->flags |= SURF_CLIPSOLID;
1336 if (out->texinfo->flags & TEX_SPECIAL)
1338 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1339 out->shader = &Cshader_water;
1340 out->shader = &Cshader_water;
1341 out->samples = NULL;
1342 Mod_GenerateWarpMesh (out);
1344 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1346 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1347 out->shader = &Cshader_wall_fullbright;
1348 out->samples = NULL;
1349 Mod_GenerateVertexMesh(out);
1353 // stainmap for permanent marks on walls
1354 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1356 memset(out->stainsamples, 255, ssize * tsize * 3);
1357 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1359 out->shader = &Cshader_wall_vertex;
1360 Mod_GenerateVertexLitMesh(out);
1364 out->shader = &Cshader_wall_lightmap;
1365 Mod_GenerateLightmappedMesh(out);
1369 Mod_SplitSurfMeshIfTooBig(out);
1373 static model_t *sortmodel;
1375 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1377 const msurface_t *a, *b;
1378 a = *((const msurface_t **)voida);
1379 b = *((const msurface_t **)voidb);
1380 if (a->shader != b->shader)
1381 return (qbyte *) a->shader - (qbyte *) b->shader;
1382 if (a->texinfo->texture != b->texinfo->texture);
1383 return a->texinfo->texture - b->texinfo->texture;
1387 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1391 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1392 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1393 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1395 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1404 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1406 node->parent = parent;
1407 if (node->contents < 0)
1409 Mod_SetParent (node->children[0], node);
1410 Mod_SetParent (node->children[1], node);
1418 static void Mod_LoadNodes (lump_t *l)
1424 in = (void *)(mod_base + l->fileofs);
1425 if (l->filelen % sizeof(*in))
1426 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1427 count = l->filelen / sizeof(*in);
1428 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1430 loadmodel->nodes = out;
1431 loadmodel->numnodes = count;
1433 for ( i=0 ; i<count ; i++, in++, out++)
1435 for (j=0 ; j<3 ; j++)
1437 out->mins[j] = LittleShort (in->mins[j]);
1438 out->maxs[j] = LittleShort (in->maxs[j]);
1441 p = LittleLong(in->planenum);
1442 out->plane = loadmodel->planes + p;
1444 out->firstsurface = LittleShort (in->firstface);
1445 out->numsurfaces = LittleShort (in->numfaces);
1447 for (j=0 ; j<2 ; j++)
1449 p = LittleShort (in->children[j]);
1451 out->children[j] = loadmodel->nodes + p;
1453 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1457 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1465 static void Mod_LoadLeafs (lump_t *l)
1471 in = (void *)(mod_base + l->fileofs);
1472 if (l->filelen % sizeof(*in))
1473 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1474 count = l->filelen / sizeof(*in);
1475 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1477 loadmodel->leafs = out;
1478 loadmodel->numleafs = count;
1480 for ( i=0 ; i<count ; i++, in++, out++)
1482 for (j=0 ; j<3 ; j++)
1484 out->mins[j] = LittleShort (in->mins[j]);
1485 out->maxs[j] = LittleShort (in->maxs[j]);
1488 p = LittleLong(in->contents);
1491 out->firstmarksurface = loadmodel->marksurfaces +
1492 LittleShort(in->firstmarksurface);
1493 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1495 p = LittleLong(in->visofs);
1497 out->compressed_vis = NULL;
1499 out->compressed_vis = loadmodel->visdata + p;
1501 for (j=0 ; j<4 ; j++)
1502 out->ambient_sound_level[j] = in->ambient_level[j];
1504 // FIXME: Insert caustics here
1513 static void Mod_LoadClipnodes (lump_t *l)
1515 dclipnode_t *in, *out;
1519 in = (void *)(mod_base + l->fileofs);
1520 if (l->filelen % sizeof(*in))
1521 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1522 count = l->filelen / sizeof(*in);
1523 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1525 loadmodel->clipnodes = out;
1526 loadmodel->numclipnodes = count;
1528 if (loadmodel->ishlbsp)
1530 hull = &loadmodel->hulls[1];
1531 hull->clipnodes = out;
1532 hull->firstclipnode = 0;
1533 hull->lastclipnode = count-1;
1534 hull->planes = loadmodel->planes;
1535 hull->clip_mins[0] = -16;
1536 hull->clip_mins[1] = -16;
1537 hull->clip_mins[2] = -36;
1538 hull->clip_maxs[0] = 16;
1539 hull->clip_maxs[1] = 16;
1540 hull->clip_maxs[2] = 36;
1541 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1543 hull = &loadmodel->hulls[2];
1544 hull->clipnodes = out;
1545 hull->firstclipnode = 0;
1546 hull->lastclipnode = count-1;
1547 hull->planes = loadmodel->planes;
1548 hull->clip_mins[0] = -32;
1549 hull->clip_mins[1] = -32;
1550 hull->clip_mins[2] = -32;
1551 hull->clip_maxs[0] = 32;
1552 hull->clip_maxs[1] = 32;
1553 hull->clip_maxs[2] = 32;
1554 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1556 hull = &loadmodel->hulls[3];
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] = -18;
1564 hull->clip_maxs[0] = 16;
1565 hull->clip_maxs[1] = 16;
1566 hull->clip_maxs[2] = 18;
1567 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1571 hull = &loadmodel->hulls[1];
1572 hull->clipnodes = out;
1573 hull->firstclipnode = 0;
1574 hull->lastclipnode = count-1;
1575 hull->planes = loadmodel->planes;
1576 hull->clip_mins[0] = -16;
1577 hull->clip_mins[1] = -16;
1578 hull->clip_mins[2] = -24;
1579 hull->clip_maxs[0] = 16;
1580 hull->clip_maxs[1] = 16;
1581 hull->clip_maxs[2] = 32;
1582 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1584 hull = &loadmodel->hulls[2];
1585 hull->clipnodes = out;
1586 hull->firstclipnode = 0;
1587 hull->lastclipnode = count-1;
1588 hull->planes = loadmodel->planes;
1589 hull->clip_mins[0] = -32;
1590 hull->clip_mins[1] = -32;
1591 hull->clip_mins[2] = -24;
1592 hull->clip_maxs[0] = 32;
1593 hull->clip_maxs[1] = 32;
1594 hull->clip_maxs[2] = 64;
1595 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1598 for (i=0 ; i<count ; i++, out++, in++)
1600 out->planenum = LittleLong(in->planenum);
1601 out->children[0] = LittleShort(in->children[0]);
1602 out->children[1] = LittleShort(in->children[1]);
1603 if (out->children[0] >= count || out->children[1] >= count)
1604 Host_Error("Corrupt clipping hull (out of range child)\n");
1612 Duplicate the drawing hull structure as a clipping hull
1615 static void Mod_MakeHull0 (void)
1622 hull = &loadmodel->hulls[0];
1624 in = loadmodel->nodes;
1625 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1627 hull->clipnodes = out;
1628 hull->firstclipnode = 0;
1629 hull->lastclipnode = loadmodel->numnodes - 1;
1630 hull->planes = loadmodel->planes;
1632 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1634 out->planenum = in->plane - loadmodel->planes;
1635 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1636 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1642 Mod_LoadMarksurfaces
1645 static void Mod_LoadMarksurfaces (lump_t *l)
1650 in = (void *)(mod_base + l->fileofs);
1651 if (l->filelen % sizeof(*in))
1652 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1653 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1654 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1656 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1658 j = (unsigned) LittleShort(in[i]);
1659 if (j >= loadmodel->numsurfaces)
1660 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1661 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1670 static void Mod_LoadSurfedges (lump_t *l)
1675 in = (void *)(mod_base + l->fileofs);
1676 if (l->filelen % sizeof(*in))
1677 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1678 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1679 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1681 for (i = 0;i < loadmodel->numsurfedges;i++)
1682 loadmodel->surfedges[i] = LittleLong (in[i]);
1691 static void Mod_LoadPlanes (lump_t *l)
1697 in = (void *)(mod_base + l->fileofs);
1698 if (l->filelen % sizeof(*in))
1699 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1701 loadmodel->numplanes = l->filelen / sizeof(*in);
1702 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1704 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1706 out->normal[0] = LittleFloat (in->normal[0]);
1707 out->normal[1] = LittleFloat (in->normal[1]);
1708 out->normal[2] = LittleFloat (in->normal[2]);
1709 out->dist = LittleFloat (in->dist);
1715 #define MAX_POINTS_ON_WINDING 64
1721 double points[8][3]; // variable sized
1730 static winding_t *NewWinding (int points)
1735 if (points > MAX_POINTS_ON_WINDING)
1736 Sys_Error("NewWinding: too many points\n");
1738 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1739 w = Mem_Alloc(loadmodel->mempool, size);
1740 memset (w, 0, size);
1745 static void FreeWinding (winding_t *w)
1755 static winding_t *BaseWindingForPlane (mplane_t *p)
1757 double org[3], vright[3], vup[3], normal[3];
1760 VectorCopy(p->normal, normal);
1761 VectorVectorsDouble(normal, vright, vup);
1763 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1764 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1766 // project a really big axis aligned box onto the plane
1769 VectorScale (p->normal, p->dist, org);
1771 VectorSubtract (org, vright, w->points[0]);
1772 VectorAdd (w->points[0], vup, w->points[0]);
1774 VectorAdd (org, vright, w->points[1]);
1775 VectorAdd (w->points[1], vup, w->points[1]);
1777 VectorAdd (org, vright, w->points[2]);
1778 VectorSubtract (w->points[2], vup, w->points[2]);
1780 VectorSubtract (org, vright, w->points[3]);
1781 VectorSubtract (w->points[3], vup, w->points[3]);
1792 Clips the winding to the plane, returning the new winding on the positive side
1793 Frees the input winding.
1794 If keepon is true, an exactly on-plane winding will be saved, otherwise
1795 it will be clipped away.
1798 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1800 double dists[MAX_POINTS_ON_WINDING + 1];
1801 int sides[MAX_POINTS_ON_WINDING + 1];
1810 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1812 // determine sides for each point
1813 for (i = 0;i < in->numpoints;i++)
1815 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1816 if (dot > ON_EPSILON)
1817 sides[i] = SIDE_FRONT;
1818 else if (dot < -ON_EPSILON)
1819 sides[i] = SIDE_BACK;
1824 sides[i] = sides[0];
1825 dists[i] = dists[0];
1827 if (keepon && !counts[0] && !counts[1])
1838 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1839 if (maxpts > MAX_POINTS_ON_WINDING)
1840 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1842 neww = NewWinding (maxpts);
1844 for (i = 0;i < in->numpoints;i++)
1846 if (neww->numpoints >= maxpts)
1847 Sys_Error ("ClipWinding: points exceeded estimate");
1851 if (sides[i] == SIDE_ON)
1853 VectorCopy (p1, neww->points[neww->numpoints]);
1858 if (sides[i] == SIDE_FRONT)
1860 VectorCopy (p1, neww->points[neww->numpoints]);
1864 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1867 // generate a split point
1868 p2 = in->points[(i+1)%in->numpoints];
1870 dot = dists[i] / (dists[i]-dists[i+1]);
1871 for (j = 0;j < 3;j++)
1872 { // avoid round off error when possible
1873 if (split->normal[j] == 1)
1874 mid[j] = split->dist;
1875 else if (split->normal[j] == -1)
1876 mid[j] = -split->dist;
1878 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1881 VectorCopy (mid, neww->points[neww->numpoints]);
1885 // free the original winding
1896 Divides a winding by a plane, producing one or two windings. The
1897 original winding is not damaged or freed. If only on one side, the
1898 returned winding will be the input winding. If on both sides, two
1899 new windings will be created.
1902 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1904 double dists[MAX_POINTS_ON_WINDING + 1];
1905 int sides[MAX_POINTS_ON_WINDING + 1];
1914 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1916 // determine sides for each point
1917 for (i = 0;i < in->numpoints;i++)
1919 dot = DotProduct (in->points[i], split->normal);
1922 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1923 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1924 else sides[i] = SIDE_ON;
1927 sides[i] = sides[0];
1928 dists[i] = dists[0];
1930 *front = *back = NULL;
1943 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1945 if (maxpts > MAX_POINTS_ON_WINDING)
1946 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1948 *front = f = NewWinding (maxpts);
1949 *back = b = NewWinding (maxpts);
1951 for (i = 0;i < in->numpoints;i++)
1953 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1954 Sys_Error ("DivideWinding: points exceeded estimate");
1958 if (sides[i] == SIDE_ON)
1960 VectorCopy (p1, f->points[f->numpoints]);
1962 VectorCopy (p1, b->points[b->numpoints]);
1967 if (sides[i] == SIDE_FRONT)
1969 VectorCopy (p1, f->points[f->numpoints]);
1972 else if (sides[i] == SIDE_BACK)
1974 VectorCopy (p1, b->points[b->numpoints]);
1978 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1981 // generate a split point
1982 p2 = in->points[(i+1)%in->numpoints];
1984 dot = dists[i] / (dists[i]-dists[i+1]);
1985 for (j = 0;j < 3;j++)
1986 { // avoid round off error when possible
1987 if (split->normal[j] == 1)
1988 mid[j] = split->dist;
1989 else if (split->normal[j] == -1)
1990 mid[j] = -split->dist;
1992 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1995 VectorCopy (mid, f->points[f->numpoints]);
1997 VectorCopy (mid, b->points[b->numpoints]);
2002 typedef struct portal_s
2005 mnode_t *nodes[2]; // [0] = front side of plane
2006 struct portal_s *next[2];
2008 struct portal_s *chain; // all portals are linked into a list
2012 static portal_t *portalchain;
2019 static portal_t *AllocPortal (void)
2022 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2023 p->chain = portalchain;
2028 static void FreePortal(portal_t *p)
2033 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2035 // calculate children first
2036 if (node->children[0]->contents >= 0)
2037 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2038 if (node->children[1]->contents >= 0)
2039 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2041 // make combined bounding box from children
2042 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2043 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2044 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2045 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2046 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2047 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2050 static void Mod_FinalizePortals(void)
2052 int i, j, numportals, numpoints;
2053 portal_t *p, *pnext;
2056 mleaf_t *leaf, *endleaf;
2059 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2060 leaf = loadmodel->leafs;
2061 endleaf = leaf + loadmodel->numleafs;
2062 for (;leaf < endleaf;leaf++)
2064 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2065 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2072 for (i = 0;i < 2;i++)
2074 leaf = (mleaf_t *)p->nodes[i];
2076 for (j = 0;j < w->numpoints;j++)
2078 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2079 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2080 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2081 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2082 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2083 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2090 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2092 // tally up portal and point counts
2098 // note: this check must match the one below or it will usually corrupt memory
2099 // 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
2100 if (p->winding && p->nodes[0] != p->nodes[1]
2101 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2102 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2105 numpoints += p->winding->numpoints * 2;
2109 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2110 loadmodel->numportals = numportals;
2111 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2112 loadmodel->numportalpoints = numpoints;
2113 // clear all leaf portal chains
2114 for (i = 0;i < loadmodel->numleafs;i++)
2115 loadmodel->leafs[i].portals = NULL;
2116 // process all portals in the global portal chain, while freeing them
2117 portal = loadmodel->portals;
2118 point = loadmodel->portalpoints;
2127 // note: this check must match the one above or it will usually corrupt memory
2128 // 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
2129 if (p->nodes[0] != p->nodes[1]
2130 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2131 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2133 // first make the back to front portal (forward portal)
2134 portal->points = point;
2135 portal->numpoints = p->winding->numpoints;
2136 portal->plane.dist = p->plane.dist;
2137 VectorCopy(p->plane.normal, portal->plane.normal);
2138 portal->here = (mleaf_t *)p->nodes[1];
2139 portal->past = (mleaf_t *)p->nodes[0];
2141 for (j = 0;j < portal->numpoints;j++)
2143 VectorCopy(p->winding->points[j], point->position);
2146 PlaneClassify(&portal->plane);
2148 // link into leaf's portal chain
2149 portal->next = portal->here->portals;
2150 portal->here->portals = portal;
2152 // advance to next portal
2155 // then make the front to back portal (backward portal)
2156 portal->points = point;
2157 portal->numpoints = p->winding->numpoints;
2158 portal->plane.dist = -p->plane.dist;
2159 VectorNegate(p->plane.normal, portal->plane.normal);
2160 portal->here = (mleaf_t *)p->nodes[0];
2161 portal->past = (mleaf_t *)p->nodes[1];
2163 for (j = portal->numpoints - 1;j >= 0;j--)
2165 VectorCopy(p->winding->points[j], point->position);
2168 PlaneClassify(&portal->plane);
2170 // link into leaf's portal chain
2171 portal->next = portal->here->portals;
2172 portal->here->portals = portal;
2174 // advance to next portal
2177 FreeWinding(p->winding);
2189 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2192 Host_Error ("AddPortalToNodes: NULL front node");
2194 Host_Error ("AddPortalToNodes: NULL back node");
2195 if (p->nodes[0] || p->nodes[1])
2196 Host_Error ("AddPortalToNodes: already included");
2197 // 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
2199 p->nodes[0] = front;
2200 p->next[0] = (portal_t *)front->portals;
2201 front->portals = (mportal_t *)p;
2204 p->next[1] = (portal_t *)back->portals;
2205 back->portals = (mportal_t *)p;
2210 RemovePortalFromNode
2213 static void RemovePortalFromNodes(portal_t *portal)
2217 void **portalpointer;
2219 for (i = 0;i < 2;i++)
2221 node = portal->nodes[i];
2223 portalpointer = (void **) &node->portals;
2228 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2232 if (portal->nodes[0] == node)
2234 *portalpointer = portal->next[0];
2235 portal->nodes[0] = NULL;
2237 else if (portal->nodes[1] == node)
2239 *portalpointer = portal->next[1];
2240 portal->nodes[1] = NULL;
2243 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2247 if (t->nodes[0] == node)
2248 portalpointer = (void **) &t->next[0];
2249 else if (t->nodes[1] == node)
2250 portalpointer = (void **) &t->next[1];
2252 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2257 static void Mod_RecursiveNodePortals (mnode_t *node)
2260 mnode_t *front, *back, *other_node;
2261 mplane_t clipplane, *plane;
2262 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2263 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2265 // if a leaf, we're done
2269 plane = node->plane;
2271 front = node->children[0];
2272 back = node->children[1];
2274 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2276 // create the new portal by generating a polygon for the node plane,
2277 // and clipping it by all of the other portals (which came from nodes above this one)
2278 nodeportal = AllocPortal ();
2279 nodeportal->plane = *node->plane;
2281 nodeportalwinding = BaseWindingForPlane (node->plane);
2282 side = 0; // shut up compiler warning
2283 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2285 clipplane = portal->plane;
2286 if (portal->nodes[0] == portal->nodes[1])
2287 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2288 if (portal->nodes[0] == node)
2290 else if (portal->nodes[1] == node)
2292 clipplane.dist = -clipplane.dist;
2293 VectorNegate (clipplane.normal, clipplane.normal);
2297 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2299 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2300 if (!nodeportalwinding)
2302 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2307 if (nodeportalwinding)
2309 // if the plane was not clipped on all sides, there was an error
2310 nodeportal->winding = nodeportalwinding;
2311 AddPortalToNodes (nodeportal, front, back);
2314 // split the portals of this node along this node's plane and assign them to the children of this node
2315 // (migrating the portals downward through the tree)
2316 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2318 if (portal->nodes[0] == portal->nodes[1])
2319 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2320 if (portal->nodes[0] == node)
2322 else if (portal->nodes[1] == node)
2325 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2326 nextportal = portal->next[side];
2328 other_node = portal->nodes[!side];
2329 RemovePortalFromNodes (portal);
2331 // cut the portal into two portals, one on each side of the node plane
2332 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2337 AddPortalToNodes (portal, back, other_node);
2339 AddPortalToNodes (portal, other_node, back);
2345 AddPortalToNodes (portal, front, other_node);
2347 AddPortalToNodes (portal, other_node, front);
2351 // the winding is split
2352 splitportal = AllocPortal ();
2353 temp = splitportal->chain;
2354 *splitportal = *portal;
2355 splitportal->chain = temp;
2356 splitportal->winding = backwinding;
2357 FreeWinding (portal->winding);
2358 portal->winding = frontwinding;
2362 AddPortalToNodes (portal, front, other_node);
2363 AddPortalToNodes (splitportal, back, other_node);
2367 AddPortalToNodes (portal, other_node, front);
2368 AddPortalToNodes (splitportal, other_node, back);
2372 Mod_RecursiveNodePortals(front);
2373 Mod_RecursiveNodePortals(back);
2377 static void Mod_MakePortals(void)
2380 Mod_RecursiveNodePortals (loadmodel->nodes);
2381 Mod_FinalizePortals();
2389 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2394 mempool_t *mainmempool;
2397 mod->type = mod_brush;
2399 header = (dheader_t *)buffer;
2401 i = LittleLong (header->version);
2402 if (i != BSPVERSION && i != 30)
2403 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2404 mod->ishlbsp = i == 30;
2405 if (loadmodel->isworldmodel)
2406 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2408 // swap all the lumps
2409 mod_base = (qbyte *)header;
2411 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2412 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2416 // store which lightmap format to use
2417 mod->lightmaprgba = r_lightmaprgba.integer;
2419 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2420 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2421 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2422 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2423 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2424 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2425 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2426 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2427 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2428 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2429 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2430 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2431 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2432 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2433 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2438 mod->numframes = 2; // regular and alternate animation
2440 mainmempool = mod->mempool;
2441 loadname = mod->name;
2443 Mod_LoadLightList ();
2446 // set up the submodels (FIXME: this is confusing)
2448 for (i = 0;i < mod->numsubmodels;i++)
2451 float dist, modelyawradius, modelradius, *vec;
2454 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2455 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2459 bm = &mod->submodels[i];
2461 mod->hulls[0].firstclipnode = bm->headnode[0];
2462 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2464 mod->hulls[j].firstclipnode = bm->headnode[j];
2465 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2468 mod->firstmodelsurface = bm->firstface;
2469 mod->nummodelsurfaces = bm->numfaces;
2471 mod->DrawSky = NULL;
2472 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2473 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2475 // we only need to have a drawsky function if it is used (usually only on world model)
2476 if (surf->shader == &Cshader_sky)
2477 mod->DrawSky = R_DrawBrushModelSky;
2478 for (k = 0;k < surf->numedges;k++)
2480 l = mod->surfedges[k + surf->firstedge];
2482 vec = mod->vertexes[mod->edges[l].v[0]].position;
2484 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2485 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2486 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2487 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2488 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2489 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2490 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2491 dist = vec[0]*vec[0]+vec[1]*vec[1];
2492 if (modelyawradius < dist)
2493 modelyawradius = dist;
2494 dist += vec[2]*vec[2];
2495 if (modelradius < dist)
2499 modelyawradius = sqrt(modelyawradius);
2500 modelradius = sqrt(modelradius);
2501 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2502 mod->yawmins[2] = mod->normalmins[2];
2503 mod->yawmaxs[2] = mod->normalmaxs[2];
2504 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2505 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2506 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2507 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2509 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2510 VectorClear(mod->normalmins);
2511 VectorClear(mod->normalmaxs);
2512 VectorClear(mod->yawmins);
2513 VectorClear(mod->yawmaxs);
2514 VectorClear(mod->rotatedmins);
2515 VectorClear(mod->rotatedmaxs);
2518 mod->numleafs = bm->visleafs;
2520 mod->Draw = R_DrawBrushModelNormal;
2521 mod->DrawShadow = NULL;
2523 Mod_BrushSortedSurfaces(mod, mainmempool);
2525 // LordHavoc: only register submodels if it is the world
2526 // (prevents bsp models from replacing world submodels)
2527 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2530 // duplicate the basic information
2531 sprintf (name, "*%i", i+1);
2532 loadmodel = Mod_FindName (name);
2534 strcpy (loadmodel->name, name);
2535 // textures and memory belong to the main model
2536 loadmodel->texturepool = NULL;
2537 loadmodel->mempool = NULL;