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 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
25 cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
26 cvar_t halflifebsp = {0, "halflifebsp", "0"};
27 cvar_t r_novis = {0, "r_novis", "0"};
28 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
29 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
30 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
37 void Mod_BrushInit (void)
39 Cvar_RegisterVariable(&r_subdivide_size);
40 Cvar_RegisterVariable(&halflifebsp);
41 Cvar_RegisterVariable(&r_novis);
42 Cvar_RegisterVariable(&r_miplightmaps);
43 Cvar_RegisterVariable(&r_lightmaprgba);
44 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
45 memset(mod_novis, 0xff, sizeof(mod_novis));
48 void Mod_Brush_SERAddEntity(void)
50 R_Clip_AddBox(currentrenderentity->mins, currentrenderentity->maxs, R_Entity_Callback, currentrenderentity, NULL);
58 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
62 Mod_CheckLoaded(model);
64 // LordHavoc: modified to start at first clip node,
65 // in other words: first node of the (sub)model
66 node = model->nodes + model->hulls[0].firstclipnode;
67 while (node->contents == 0)
68 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
70 return (mleaf_t *)node;
73 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
75 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
76 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
77 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
79 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
80 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
82 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
83 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
93 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
95 static qbyte decompressed[MAX_MAP_LEAFS/8];
100 row = (model->numleafs+7)>>3;
118 } while (out - decompressed < row);
123 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
125 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
127 return Mod_DecompressVis (leaf->compressed_vis, model);
130 void Mod_SetupNoTexture(void)
133 qbyte pix[16][16][4];
135 for (y = 0;y < 16;y++)
137 for (x = 0;x < 16;x++)
139 if ((y < 8) ^ (x < 8))
156 memset(&loadmodel->notexture, 0, sizeof(texture_t));
157 strcpy(loadmodel->notexture.name, "notexture");
158 loadmodel->notexture.width = 16;
159 loadmodel->notexture.height = 16;
160 loadmodel->notexture.flags = 0;
161 loadmodel->notexture.texture = R_LoadTexture(loadmodel->texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP);
169 static void Mod_LoadTextures (lump_t *l)
171 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
173 texture_t *tx, *tx2, *anims[10], *altanims[10];
175 qbyte *data, *mtdata, *data2;
178 Mod_SetupNoTexture();
182 loadmodel->textures = NULL;
186 m = (dmiptexlump_t *)(mod_base + l->fileofs);
188 m->nummiptex = LittleLong (m->nummiptex);
190 loadmodel->numtextures = m->nummiptex;
191 loadmodel->textures = Mem_Alloc(loadmodel->mempool, m->nummiptex * sizeof(*loadmodel->textures));
193 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
195 for (i = 0;i < m->nummiptex;i++)
197 dofs[i] = LittleLong(dofs[i]);
200 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
201 mtwidth = LittleLong (dmiptex->width);
202 mtheight = LittleLong (dmiptex->height);
204 j = LittleLong (dmiptex->offsets[0]);
208 if (j < 40 || j + mtwidth * mtheight > l->filelen)
209 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
210 mtdata = (qbyte *)dmiptex + j;
213 if ((mtwidth & 15) || (mtheight & 15))
214 Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
215 // LordHavoc: rewriting the map texture loader for GLQuake
216 tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
217 memset(tx, 0, sizeof(texture_t));
219 tx->alternate_anims = NULL;
220 loadmodel->textures[i] = tx;
222 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
223 for (j = 0;dmiptex->name[j] && j < 15;j++)
225 if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
226 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
228 tx->name[j] = dmiptex->name[j];
235 Con_Printf("warning: unnamed texture in %s\n", loadmodel->name);
236 sprintf(tx->name, "unnamed%i", i);
240 tx->height = mtheight;
242 tx->glowtexture = NULL;
243 tx->fogtexture = NULL;
245 if (!loadmodel->ishlbsp && !strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128) // LordHavoc: HL sky textures are entirely unrelated
247 data = loadimagepixels(tx->name, false, 0, 0);
250 if (image_width == 256 && image_height == 128)
252 if (loadmodel->isworldmodel)
259 Host_Error("Mod_LoadTextures: replacement sky image must be 256x128 pixels\n");
262 else if (loadmodel->isworldmodel)
263 R_InitSky (mtdata, 1);
265 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
267 tx->fogtexture = image_masktex;
268 strcpy(name, tx->name);
269 strcat(name, "_glow");
270 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
274 if (loadmodel->ishlbsp)
276 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
279 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
280 if (R_TextureHasAlpha(tx->texture))
283 for (j = 0;j < image_width * image_height;j++)
284 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
285 strcpy(name, tx->name);
286 strcat(name, "_fog");
287 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
291 else if ((data = W_GetTexture(tx->name)))
293 // get the size from the wad texture
294 tx->width = image_width;
295 tx->height = image_height;
296 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
297 if (R_TextureHasAlpha(tx->texture))
300 for (j = 0;j < image_width * image_height;j++)
301 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
302 strcpy(name, tx->name);
303 strcat(name, "_fog");
304 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
312 tx->texture = loadmodel->notexture.texture;
317 if (mtdata) // texture included
322 if (r_fullbrights.value && tx->name[0] != '*')
324 for (j = 0;j < tx->width*tx->height;j++)
326 if (data[j] >= 224) // fullbright
335 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
336 for (j = 0;j < tx->width*tx->height;j++)
337 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
338 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
339 strcpy(name, tx->name);
340 strcat(name, "_glow");
341 for (j = 0;j < tx->width*tx->height;j++)
342 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
343 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
347 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
349 else // no texture, and no external replacement texture was found
353 tx->texture = loadmodel->notexture.texture;
358 if (tx->name[0] == '*')
360 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
361 // LordHavoc: some turbulent textures should be fullbright and solid
362 if (!strncmp(tx->name,"*lava",5)
363 || !strncmp(tx->name,"*teleport",9)
364 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
365 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
367 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
368 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
371 tx->flags |= SURF_LIGHTMAP;
372 if (!R_TextureHasAlpha(tx->texture))
373 tx->flags |= SURF_CLIPSOLID;
378 // sequence the animations
380 for (i = 0;i < m->nummiptex;i++)
382 tx = loadmodel->textures[i];
383 if (!tx || tx->name[0] != '+')
386 continue; // already sequenced
388 // find the number of frames in the animation
389 memset (anims, 0, sizeof(anims));
390 memset (altanims, 0, sizeof(altanims));
393 for (j = i;j < m->nummiptex;j++)
395 tx2 = loadmodel->textures[j];
396 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
400 if (num >= '0' && num <= '9')
401 anims[num - '0'] = tx2;
402 else if (num >= 'a' && num <= 'j')
403 altanims[num - 'a'] = tx2;
405 Host_Error ("Bad animating texture %s", tx->name);
408 for (j = 0;j < 10;j++)
410 if (anims[j] != NULL)
412 if (altanims[j] != NULL)
416 // link them all together
417 for (j = 0;j < max;j++)
421 Host_Error ("Missing frame %i of %s", j, tx->name);
422 tx2->anim_total = max;
423 tx2->alternate_anims = altanims[0]; // NULL if there is no alternate
424 for (k = 0;k < 10;k++)
425 tx2->anim_frames[k] = anims[k];
428 for (j = 0;j < altmax;j++)
432 Host_Error ("Missing frame %i of %s", j, tx->name);
433 tx2->anim_total = altmax;
434 tx2->alternate_anims = anims[0]; // NULL if there is no alternate
435 for (k = 0;k < 10;k++)
436 tx2->anim_frames[k] = altanims[k];
446 static void Mod_LoadLighting (lump_t *l)
449 qbyte *in, *out, *data, d;
450 char litfilename[1024];
451 loadmodel->lightdata = NULL;
452 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
454 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
455 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
457 else // LordHavoc: bsp version 29 (normal white lighting)
459 // LordHavoc: hope is not lost yet, check for a .lit file to load
460 strcpy(litfilename, loadmodel->name);
461 COM_StripExtension(litfilename, litfilename);
462 strcat(litfilename, ".lit");
463 data = (qbyte*) COM_LoadFile (litfilename, false);
466 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
468 i = LittleLong(((int *)data)[1]);
471 Con_DPrintf("%s loaded", litfilename);
472 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
473 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
479 Con_Printf("Unknown .lit file version (%d)\n", i);
486 Con_Printf("Empty .lit file, ignoring\n");
488 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
492 // LordHavoc: oh well, expand the white lighting data
495 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
496 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
497 out = loadmodel->lightdata;
498 memcpy (in, mod_base + l->fileofs, l->filelen);
499 for (i = 0;i < l->filelen;i++)
509 void Mod_LoadLightList(void)
512 char lightsfilename[1024], *s, *t, *lightsstring;
515 strcpy(lightsfilename, loadmodel->name);
516 COM_StripExtension(lightsfilename, lightsfilename);
517 strcat(lightsfilename, ".lights");
518 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
524 while (*s && *s != '\n')
528 Mem_Free(lightsstring);
529 Host_Error("lights file must end with a newline\n");
534 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
537 while (*s && n < numlights)
540 while (*s && *s != '\n')
544 Mem_Free(lightsstring);
545 Host_Error("misparsed lights file!\n");
547 e = loadmodel->lights + n;
549 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);
553 Mem_Free(lightsstring);
554 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);
561 Mem_Free(lightsstring);
562 Host_Error("misparsed lights file!\n");
564 loadmodel->numlights = numlights;
565 Mem_Free(lightsstring);
575 static void Mod_LoadVisibility (lump_t *l)
579 loadmodel->visdata = NULL;
582 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
583 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
586 // used only for HalfLife maps
587 void Mod_ParseWadsFromEntityLump(char *data)
589 char key[128], value[4096];
594 data = COM_Parse(data);
597 if (com_token[0] != '{')
601 data = COM_Parse(data);
604 if (com_token[0] == '}')
605 break; // end of worldspawn
606 if (com_token[0] == '_')
607 strcpy(key, com_token + 1);
609 strcpy(key, com_token);
610 while (key[strlen(key)-1] == ' ') // remove trailing spaces
611 key[strlen(key)-1] = 0;
612 data = COM_Parse(data);
615 strcpy(value, com_token);
616 if (!strcmp("wad", key)) // for HalfLife maps
618 if (loadmodel->ishlbsp)
621 for (i = 0;i < 4096;i++)
622 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
628 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
629 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
631 else if (value[i] == ';' || value[i] == 0)
635 strcpy(wadname, "textures/");
636 strcat(wadname, &value[j]);
637 W_LoadTextureWadFile (wadname, false);
654 static void Mod_LoadEntities (lump_t *l)
658 loadmodel->entities = NULL;
661 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
662 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
663 if (loadmodel->ishlbsp)
664 Mod_ParseWadsFromEntityLump(loadmodel->entities);
673 static void Mod_LoadVertexes (lump_t *l)
679 in = (void *)(mod_base + l->fileofs);
680 if (l->filelen % sizeof(*in))
681 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
682 count = l->filelen / sizeof(*in);
683 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
685 loadmodel->vertexes = out;
686 loadmodel->numvertexes = count;
688 for ( i=0 ; i<count ; i++, in++, out++)
690 out->position[0] = LittleFloat (in->point[0]);
691 out->position[1] = LittleFloat (in->point[1]);
692 out->position[2] = LittleFloat (in->point[2]);
701 static void Mod_LoadSubmodels (lump_t *l)
707 in = (void *)(mod_base + l->fileofs);
708 if (l->filelen % sizeof(*in))
709 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
710 count = l->filelen / sizeof(*in);
711 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
713 loadmodel->submodels = out;
714 loadmodel->numsubmodels = count;
716 for ( i=0 ; i<count ; i++, in++, out++)
718 for (j=0 ; j<3 ; j++)
720 // spread the mins / maxs by a pixel
721 out->mins[j] = LittleFloat (in->mins[j]) - 1;
722 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
723 out->origin[j] = LittleFloat (in->origin[j]);
725 for (j=0 ; j<MAX_MAP_HULLS ; j++)
726 out->headnode[j] = LittleLong (in->headnode[j]);
727 out->visleafs = LittleLong (in->visleafs);
728 out->firstface = LittleLong (in->firstface);
729 out->numfaces = LittleLong (in->numfaces);
738 static void Mod_LoadEdges (lump_t *l)
744 in = (void *)(mod_base + l->fileofs);
745 if (l->filelen % sizeof(*in))
746 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
747 count = l->filelen / sizeof(*in);
748 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
750 loadmodel->edges = out;
751 loadmodel->numedges = count;
753 for ( i=0 ; i<count ; i++, in++, out++)
755 out->v[0] = (unsigned short)LittleShort(in->v[0]);
756 out->v[1] = (unsigned short)LittleShort(in->v[1]);
765 static void Mod_LoadTexinfo (lump_t *l)
772 in = (void *)(mod_base + l->fileofs);
773 if (l->filelen % sizeof(*in))
774 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
775 count = l->filelen / sizeof(*in);
776 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
778 loadmodel->texinfo = out;
779 loadmodel->numtexinfo = count;
781 for (i = 0;i < count;i++, in++, out++)
783 for (k = 0;k < 2;k++)
784 for (j = 0;j < 4;j++)
785 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
787 miptex = LittleLong (in->miptex);
788 out->flags = LittleLong (in->flags);
790 if (!loadmodel->textures)
791 out->texture = &loadmodel->notexture;
795 Host_Error ("miptex < 0");
796 if (miptex >= loadmodel->numtextures)
797 Host_Error ("miptex >= loadmodel->numtextures");
798 out->texture = loadmodel->textures[miptex];
801 out->texture = &loadmodel->notexture;
809 Fills in s->texturemins[] and s->extents[]
812 static void CalcSurfaceExtents (msurface_t *s)
814 float mins[2], maxs[2], val;
818 int bmins[2], bmaxs[2];
820 mins[0] = mins[1] = 999999999;
821 maxs[0] = maxs[1] = -999999999;
825 for (i=0 ; i<s->numedges ; i++)
827 e = loadmodel->surfedges[s->firstedge+i];
829 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
831 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
833 for (j=0 ; j<2 ; j++)
835 val = v->position[0] * tex->vecs[j][0] +
836 v->position[1] * tex->vecs[j][1] +
837 v->position[2] * tex->vecs[j][2] +
846 for (i=0 ; i<2 ; i++)
848 bmins[i] = floor(mins[i]/16);
849 bmaxs[i] = ceil(maxs[i]/16);
851 s->texturemins[i] = bmins[i] * 16;
852 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
857 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
862 mins[0] = mins[1] = mins[2] = 9999;
863 maxs[0] = maxs[1] = maxs[2] = -9999;
865 for (i = 0;i < numverts;i++)
867 for (j = 0;j < 3;j++, v++)
877 #define MAX_SUBDIVPOLYTRIANGLES 4096
878 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
880 static int subdivpolyverts, subdivpolytriangles;
881 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
882 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
884 static int subdivpolylookupvert(vec3_t v)
887 for (i = 0;i < subdivpolyverts;i++)
888 if (subdivpolyvert[i][0] == v[0]
889 && subdivpolyvert[i][1] == v[1]
890 && subdivpolyvert[i][2] == v[2])
892 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
893 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
894 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
895 return subdivpolyverts++;
898 static void SubdividePolygon (int numverts, float *verts)
900 int i, i1, i2, i3, f, b, c, p;
901 vec3_t mins, maxs, front[256], back[256];
902 float m, *pv, *cv, dist[256], frac;
905 Host_Error ("SubdividePolygon: ran out of verts in buffer");
907 BoundPoly (numverts, verts, mins, maxs);
909 for (i = 0;i < 3;i++)
911 m = (mins[i] + maxs[i]) * 0.5;
912 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
919 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
923 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
927 VectorCopy (pv, front[f]);
932 VectorCopy (pv, back[b]);
935 if (dist[p] == 0 || dist[c] == 0)
937 if ( (dist[p] > 0) != (dist[c] > 0) )
940 frac = dist[p] / (dist[p] - dist[c]);
941 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
942 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
943 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
949 SubdividePolygon (f, front[0]);
950 SubdividePolygon (b, back[0]);
954 i1 = subdivpolylookupvert(verts);
955 i2 = subdivpolylookupvert(verts + 3);
956 for (i = 2;i < numverts;i++)
958 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
960 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
964 i3 = subdivpolylookupvert(verts + i * 3);
965 subdivpolyindex[subdivpolytriangles][0] = i1;
966 subdivpolyindex[subdivpolytriangles][1] = i2;
967 subdivpolyindex[subdivpolytriangles][2] = i3;
969 subdivpolytriangles++;
977 Breaks a polygon up along axial 64 unit
978 boundaries so that turbulent and sky warps
979 can be done reasonably.
982 void Mod_GenerateWarpMesh (msurface_t *surf)
988 subdivpolytriangles = 0;
990 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
991 if (subdivpolytriangles < 1)
992 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
994 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
995 mesh->numverts = subdivpolyverts;
996 mesh->numtriangles = subdivpolytriangles;
997 mesh->vertex = (surfvertex_t *)(mesh + 1);
998 mesh->index = (int *)(mesh->vertex + mesh->numverts);
999 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1001 for (i = 0;i < mesh->numtriangles;i++)
1002 for (j = 0;j < 3;j++)
1003 mesh->index[i*3+j] = subdivpolyindex[i][j];
1005 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1007 VectorCopy(subdivpolyvert[i], v->v);
1008 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1009 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1013 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1015 int i, is, it, *index, smax, tmax;
1020 smax = surf->extents[0] >> 4;
1021 tmax = surf->extents[1] >> 4;
1022 surf->lightmaptexturestride = 0;
1023 surf->lightmaptexture = NULL;
1025 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1026 mesh->numverts = surf->poly_numverts;
1027 mesh->numtriangles = surf->poly_numverts - 2;
1028 mesh->vertex = (surfvertex_t *)(mesh + 1);
1029 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1030 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1032 index = mesh->index;
1033 for (i = 0;i < mesh->numtriangles;i++)
1040 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1042 VectorCopy (in, out->v);
1044 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1045 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1047 out->st[0] = s / surf->texinfo->texture->width;
1048 out->st[1] = t / surf->texinfo->texture->height;
1050 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1051 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1053 // lightmap coordinates
1057 // LordHavoc: calc lightmap data offset for vertex lighting to use
1060 is = bound(0, is, smax);
1061 it = bound(0, it, tmax);
1062 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1066 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1068 int i, is, it, *index, smax, tmax;
1069 float *in, s, t, xbase, ybase, xscale, yscale;
1073 surf->flags |= SURF_LIGHTMAP;
1074 smax = surf->extents[0] >> 4;
1075 tmax = surf->extents[1] >> 4;
1076 if (r_miplightmaps.integer)
1078 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1079 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);
1083 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1084 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);
1086 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1087 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1088 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1090 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1091 mesh->numverts = surf->poly_numverts;
1092 mesh->numtriangles = surf->poly_numverts - 2;
1093 mesh->vertex = (surfvertex_t *)(mesh + 1);
1094 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1095 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1097 index = mesh->index;
1098 for (i = 0;i < mesh->numtriangles;i++)
1105 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1107 VectorCopy (in, out->v);
1109 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1110 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1112 out->st[0] = s / surf->texinfo->texture->width;
1113 out->st[1] = t / surf->texinfo->texture->height;
1115 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1116 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1118 // lightmap coordinates
1119 out->uv[0] = s * xscale + xbase;
1120 out->uv[1] = t * yscale + ybase;
1122 // LordHavoc: calc lightmap data offset for vertex lighting to use
1125 is = bound(0, is, smax);
1126 it = bound(0, it, tmax);
1127 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1131 void Mod_GenerateVertexMesh (msurface_t *surf)
1138 surf->lightmaptexturestride = 0;
1139 surf->lightmaptexture = NULL;
1141 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1142 mesh->numverts = surf->poly_numverts;
1143 mesh->numtriangles = surf->poly_numverts - 2;
1144 mesh->vertex = (surfvertex_t *)(mesh + 1);
1145 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1146 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1148 index = mesh->index;
1149 for (i = 0;i < mesh->numtriangles;i++)
1156 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1158 VectorCopy (in, out->v);
1159 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1160 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1164 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1171 // convert edges back to a normal polygon
1172 surf->poly_numverts = surf->numedges;
1173 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1174 for (i = 0;i < surf->numedges;i++)
1176 lindex = loadmodel->surfedges[surf->firstedge + i];
1178 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1180 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1181 VectorCopy (vec, vert);
1186 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1188 int j, base, tricount, newvertexcount, *index, *vertexremap;
1189 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1190 if (s->mesh->numtriangles > 1000)
1192 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1197 while (base < s->mesh->numtriangles)
1199 tricount = s->mesh->numtriangles - base;
1200 if (tricount > 1000)
1202 index = s->mesh->index + base * 3;
1206 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1207 for (j = 0;j < tricount * 3;j++)
1208 if (vertexremap[index[j]] < 0)
1209 vertexremap[index[j]] = newvertexcount++;
1211 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1212 newmesh->chain = NULL;
1213 newmesh->numverts = newvertexcount;
1214 newmesh->numtriangles = tricount;
1215 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1216 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1217 for (j = 0;j < tricount * 3;j++)
1219 newmesh->index[j] = vertexremap[index[j]];
1220 // yes this copies the same vertex multiple times in many cases... but that's ok...
1221 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1224 oldmesh->chain = newmesh;
1226 firstmesh = newmesh;
1229 Mem_Free(vertexremap);
1231 s->mesh = firstmesh;
1240 static void Mod_LoadFaces (lump_t *l)
1244 int i, count, surfnum, planenum, side, ssize, tsize;
1246 in = (void *)(mod_base + l->fileofs);
1247 if (l->filelen % sizeof(*in))
1248 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1249 count = l->filelen / sizeof(*in);
1250 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1252 loadmodel->surfaces = out;
1253 loadmodel->numsurfaces = count;
1255 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1257 // FIXME: validate edges, texinfo, etc?
1258 out->firstedge = LittleLong(in->firstedge);
1259 out->numedges = LittleShort(in->numedges);
1261 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1262 out->flags = out->texinfo->texture->flags;
1264 planenum = LittleShort(in->planenum);
1265 side = LittleShort(in->side);
1267 out->flags |= SURF_PLANEBACK;
1269 out->plane = loadmodel->planes + planenum;
1271 // clear lightmap (filled in later)
1272 out->lightmaptexture = NULL;
1274 // force lightmap upload on first time seeing the surface
1275 out->cached_dlight = true;
1276 out->cached_ambient = -1000;
1277 out->cached_lightscalebit = -1000;
1279 CalcSurfaceExtents (out);
1281 ssize = (out->extents[0] >> 4) + 1;
1282 tsize = (out->extents[1] >> 4) + 1;
1285 for (i = 0;i < MAXLIGHTMAPS;i++)
1286 out->styles[i] = in->styles[i];
1287 i = LittleLong(in->lightofs);
1289 out->samples = NULL;
1290 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1291 out->samples = loadmodel->lightdata + i;
1292 else // LordHavoc: white lighting (bsp version 29)
1293 out->samples = loadmodel->lightdata + (i * 3);
1295 Mod_GenerateSurfacePolygon(out);
1297 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1299 out->shader = &Cshader_sky;
1300 out->samples = NULL;
1301 Mod_GenerateWarpMesh (out);
1303 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1305 out->shader = &Cshader_water;
1306 out->samples = NULL;
1307 Mod_GenerateWarpMesh (out);
1311 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1312 out->flags |= SURF_CLIPSOLID;
1313 if (out->texinfo->flags & TEX_SPECIAL)
1315 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1316 out->shader = &Cshader_water;
1317 out->shader = &Cshader_water;
1318 out->samples = NULL;
1319 Mod_GenerateWarpMesh (out);
1321 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1323 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1324 out->shader = &Cshader_wall_fullbright;
1325 out->samples = NULL;
1326 Mod_GenerateVertexMesh(out);
1330 // stainmap for permanent marks on walls
1331 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1333 memset(out->stainsamples, 255, ssize * tsize * 3);
1334 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1336 out->shader = &Cshader_wall_vertex;
1337 Mod_GenerateVertexLitMesh(out);
1341 out->shader = &Cshader_wall_lightmap;
1342 Mod_GenerateLightmappedMesh(out);
1346 Mod_SplitSurfMeshIfTooBig(out);
1350 static model_t *sortmodel;
1352 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1354 const msurface_t *a, *b;
1355 a = *((const msurface_t **)voida);
1356 b = *((const msurface_t **)voidb);
1357 if (a->shader != b->shader)
1358 return (qbyte *) a->shader - (qbyte *) b->shader;
1359 if (a->texinfo->texture != b->texinfo->texture);
1360 return a->texinfo->texture - b->texinfo->texture;
1364 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1368 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1369 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1370 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1372 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1381 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1383 node->parent = parent;
1384 if (node->contents < 0)
1386 Mod_SetParent (node->children[0], node);
1387 Mod_SetParent (node->children[1], node);
1395 static void Mod_LoadNodes (lump_t *l)
1401 in = (void *)(mod_base + l->fileofs);
1402 if (l->filelen % sizeof(*in))
1403 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1404 count = l->filelen / sizeof(*in);
1405 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1407 loadmodel->nodes = out;
1408 loadmodel->numnodes = count;
1410 for ( i=0 ; i<count ; i++, in++, out++)
1412 for (j=0 ; j<3 ; j++)
1414 out->mins[j] = LittleShort (in->mins[j]);
1415 out->maxs[j] = LittleShort (in->maxs[j]);
1418 p = LittleLong(in->planenum);
1419 out->plane = loadmodel->planes + p;
1421 out->firstsurface = LittleShort (in->firstface);
1422 out->numsurfaces = LittleShort (in->numfaces);
1424 for (j=0 ; j<2 ; j++)
1426 p = LittleShort (in->children[j]);
1428 out->children[j] = loadmodel->nodes + p;
1430 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1434 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1442 static void Mod_LoadLeafs (lump_t *l)
1448 in = (void *)(mod_base + l->fileofs);
1449 if (l->filelen % sizeof(*in))
1450 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1451 count = l->filelen / sizeof(*in);
1452 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1454 loadmodel->leafs = out;
1455 loadmodel->numleafs = count;
1457 for ( i=0 ; i<count ; i++, in++, out++)
1459 for (j=0 ; j<3 ; j++)
1461 out->mins[j] = LittleShort (in->mins[j]);
1462 out->maxs[j] = LittleShort (in->maxs[j]);
1465 p = LittleLong(in->contents);
1468 out->firstmarksurface = loadmodel->marksurfaces +
1469 LittleShort(in->firstmarksurface);
1470 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1472 p = LittleLong(in->visofs);
1474 out->compressed_vis = NULL;
1476 out->compressed_vis = loadmodel->visdata + p;
1478 for (j=0 ; j<4 ; j++)
1479 out->ambient_sound_level[j] = in->ambient_level[j];
1481 // FIXME: Insert caustics here
1490 static void Mod_LoadClipnodes (lump_t *l)
1492 dclipnode_t *in, *out;
1496 in = (void *)(mod_base + l->fileofs);
1497 if (l->filelen % sizeof(*in))
1498 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1499 count = l->filelen / sizeof(*in);
1500 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1502 loadmodel->clipnodes = out;
1503 loadmodel->numclipnodes = count;
1505 if (loadmodel->ishlbsp)
1507 hull = &loadmodel->hulls[1];
1508 hull->clipnodes = out;
1509 hull->firstclipnode = 0;
1510 hull->lastclipnode = count-1;
1511 hull->planes = loadmodel->planes;
1512 hull->clip_mins[0] = -16;
1513 hull->clip_mins[1] = -16;
1514 hull->clip_mins[2] = -36;
1515 hull->clip_maxs[0] = 16;
1516 hull->clip_maxs[1] = 16;
1517 hull->clip_maxs[2] = 36;
1518 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1520 hull = &loadmodel->hulls[2];
1521 hull->clipnodes = out;
1522 hull->firstclipnode = 0;
1523 hull->lastclipnode = count-1;
1524 hull->planes = loadmodel->planes;
1525 hull->clip_mins[0] = -32;
1526 hull->clip_mins[1] = -32;
1527 hull->clip_mins[2] = -32;
1528 hull->clip_maxs[0] = 32;
1529 hull->clip_maxs[1] = 32;
1530 hull->clip_maxs[2] = 32;
1531 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1533 hull = &loadmodel->hulls[3];
1534 hull->clipnodes = out;
1535 hull->firstclipnode = 0;
1536 hull->lastclipnode = count-1;
1537 hull->planes = loadmodel->planes;
1538 hull->clip_mins[0] = -16;
1539 hull->clip_mins[1] = -16;
1540 hull->clip_mins[2] = -18;
1541 hull->clip_maxs[0] = 16;
1542 hull->clip_maxs[1] = 16;
1543 hull->clip_maxs[2] = 18;
1544 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1548 hull = &loadmodel->hulls[1];
1549 hull->clipnodes = out;
1550 hull->firstclipnode = 0;
1551 hull->lastclipnode = count-1;
1552 hull->planes = loadmodel->planes;
1553 hull->clip_mins[0] = -16;
1554 hull->clip_mins[1] = -16;
1555 hull->clip_mins[2] = -24;
1556 hull->clip_maxs[0] = 16;
1557 hull->clip_maxs[1] = 16;
1558 hull->clip_maxs[2] = 32;
1559 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1561 hull = &loadmodel->hulls[2];
1562 hull->clipnodes = out;
1563 hull->firstclipnode = 0;
1564 hull->lastclipnode = count-1;
1565 hull->planes = loadmodel->planes;
1566 hull->clip_mins[0] = -32;
1567 hull->clip_mins[1] = -32;
1568 hull->clip_mins[2] = -24;
1569 hull->clip_maxs[0] = 32;
1570 hull->clip_maxs[1] = 32;
1571 hull->clip_maxs[2] = 64;
1572 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1575 for (i=0 ; i<count ; i++, out++, in++)
1577 out->planenum = LittleLong(in->planenum);
1578 out->children[0] = LittleShort(in->children[0]);
1579 out->children[1] = LittleShort(in->children[1]);
1580 if (out->children[0] >= count || out->children[1] >= count)
1581 Host_Error("Corrupt clipping hull (out of range child)\n");
1589 Duplicate the drawing hull structure as a clipping hull
1592 static void Mod_MakeHull0 (void)
1599 hull = &loadmodel->hulls[0];
1601 in = loadmodel->nodes;
1602 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1604 hull->clipnodes = out;
1605 hull->firstclipnode = 0;
1606 hull->lastclipnode = loadmodel->numnodes - 1;
1607 hull->planes = loadmodel->planes;
1609 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1611 out->planenum = in->plane - loadmodel->planes;
1612 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1613 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1619 Mod_LoadMarksurfaces
1622 static void Mod_LoadMarksurfaces (lump_t *l)
1627 in = (void *)(mod_base + l->fileofs);
1628 if (l->filelen % sizeof(*in))
1629 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1630 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1631 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1633 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1635 j = (unsigned) LittleShort(in[i]);
1636 if (j >= loadmodel->numsurfaces)
1637 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1638 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1647 static void Mod_LoadSurfedges (lump_t *l)
1652 in = (void *)(mod_base + l->fileofs);
1653 if (l->filelen % sizeof(*in))
1654 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1655 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1656 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1658 for (i = 0;i < loadmodel->numsurfedges;i++)
1659 loadmodel->surfedges[i] = LittleLong (in[i]);
1668 static void Mod_LoadPlanes (lump_t *l)
1674 in = (void *)(mod_base + l->fileofs);
1675 if (l->filelen % sizeof(*in))
1676 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1678 loadmodel->numplanes = l->filelen / sizeof(*in);
1679 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1681 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1683 out->normal[0] = LittleFloat (in->normal[0]);
1684 out->normal[1] = LittleFloat (in->normal[1]);
1685 out->normal[2] = LittleFloat (in->normal[2]);
1686 out->dist = LittleFloat (in->dist);
1692 #define MAX_POINTS_ON_WINDING 64
1698 double points[8][3]; // variable sized
1707 static winding_t *NewWinding (int points)
1712 if (points > MAX_POINTS_ON_WINDING)
1713 Sys_Error("NewWinding: too many points\n");
1715 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1716 w = Mem_Alloc(loadmodel->mempool, size);
1717 memset (w, 0, size);
1722 static void FreeWinding (winding_t *w)
1732 static winding_t *BaseWindingForPlane (mplane_t *p)
1734 double org[3], vright[3], vup[3], normal[3];
1737 VectorCopy(p->normal, normal);
1738 VectorVectorsDouble(normal, vright, vup);
1740 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1741 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1743 // project a really big axis aligned box onto the plane
1746 VectorScale (p->normal, p->dist, org);
1748 VectorSubtract (org, vright, w->points[0]);
1749 VectorAdd (w->points[0], vup, w->points[0]);
1751 VectorAdd (org, vright, w->points[1]);
1752 VectorAdd (w->points[1], vup, w->points[1]);
1754 VectorAdd (org, vright, w->points[2]);
1755 VectorSubtract (w->points[2], vup, w->points[2]);
1757 VectorSubtract (org, vright, w->points[3]);
1758 VectorSubtract (w->points[3], vup, w->points[3]);
1769 Clips the winding to the plane, returning the new winding on the positive side
1770 Frees the input winding.
1771 If keepon is true, an exactly on-plane winding will be saved, otherwise
1772 it will be clipped away.
1775 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1777 double dists[MAX_POINTS_ON_WINDING + 1];
1778 int sides[MAX_POINTS_ON_WINDING + 1];
1787 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1789 // determine sides for each point
1790 for (i = 0;i < in->numpoints;i++)
1792 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1793 if (dot > ON_EPSILON)
1794 sides[i] = SIDE_FRONT;
1795 else if (dot < -ON_EPSILON)
1796 sides[i] = SIDE_BACK;
1801 sides[i] = sides[0];
1802 dists[i] = dists[0];
1804 if (keepon && !counts[0] && !counts[1])
1815 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1816 if (maxpts > MAX_POINTS_ON_WINDING)
1817 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1819 neww = NewWinding (maxpts);
1821 for (i = 0;i < in->numpoints;i++)
1823 if (neww->numpoints >= maxpts)
1824 Sys_Error ("ClipWinding: points exceeded estimate");
1828 if (sides[i] == SIDE_ON)
1830 VectorCopy (p1, neww->points[neww->numpoints]);
1835 if (sides[i] == SIDE_FRONT)
1837 VectorCopy (p1, neww->points[neww->numpoints]);
1841 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1844 // generate a split point
1845 p2 = in->points[(i+1)%in->numpoints];
1847 dot = dists[i] / (dists[i]-dists[i+1]);
1848 for (j = 0;j < 3;j++)
1849 { // avoid round off error when possible
1850 if (split->normal[j] == 1)
1851 mid[j] = split->dist;
1852 else if (split->normal[j] == -1)
1853 mid[j] = -split->dist;
1855 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1858 VectorCopy (mid, neww->points[neww->numpoints]);
1862 // free the original winding
1873 Divides a winding by a plane, producing one or two windings. The
1874 original winding is not damaged or freed. If only on one side, the
1875 returned winding will be the input winding. If on both sides, two
1876 new windings will be created.
1879 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1881 double dists[MAX_POINTS_ON_WINDING + 1];
1882 int sides[MAX_POINTS_ON_WINDING + 1];
1891 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1893 // determine sides for each point
1894 for (i = 0;i < in->numpoints;i++)
1896 dot = DotProduct (in->points[i], split->normal);
1899 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1900 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1901 else sides[i] = SIDE_ON;
1904 sides[i] = sides[0];
1905 dists[i] = dists[0];
1907 *front = *back = NULL;
1920 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1922 if (maxpts > MAX_POINTS_ON_WINDING)
1923 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1925 *front = f = NewWinding (maxpts);
1926 *back = b = NewWinding (maxpts);
1928 for (i = 0;i < in->numpoints;i++)
1930 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1931 Sys_Error ("DivideWinding: points exceeded estimate");
1935 if (sides[i] == SIDE_ON)
1937 VectorCopy (p1, f->points[f->numpoints]);
1939 VectorCopy (p1, b->points[b->numpoints]);
1944 if (sides[i] == SIDE_FRONT)
1946 VectorCopy (p1, f->points[f->numpoints]);
1949 else if (sides[i] == SIDE_BACK)
1951 VectorCopy (p1, b->points[b->numpoints]);
1955 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1958 // generate a split point
1959 p2 = in->points[(i+1)%in->numpoints];
1961 dot = dists[i] / (dists[i]-dists[i+1]);
1962 for (j = 0;j < 3;j++)
1963 { // avoid round off error when possible
1964 if (split->normal[j] == 1)
1965 mid[j] = split->dist;
1966 else if (split->normal[j] == -1)
1967 mid[j] = -split->dist;
1969 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1972 VectorCopy (mid, f->points[f->numpoints]);
1974 VectorCopy (mid, b->points[b->numpoints]);
1979 typedef struct portal_s
1982 mnode_t *nodes[2]; // [0] = front side of plane
1983 struct portal_s *next[2];
1985 struct portal_s *chain; // all portals are linked into a list
1989 static portal_t *portalchain;
1996 static portal_t *AllocPortal (void)
1999 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2000 p->chain = portalchain;
2005 static void FreePortal(portal_t *p)
2010 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2012 // calculate children first
2013 if (node->children[0]->contents >= 0)
2014 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2015 if (node->children[1]->contents >= 0)
2016 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2018 // make combined bounding box from children
2019 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2020 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2021 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2022 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2023 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2024 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2027 static void Mod_FinalizePortals(void)
2029 int i, j, numportals, numpoints;
2030 portal_t *p, *pnext;
2033 mleaf_t *leaf, *endleaf;
2036 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2037 leaf = loadmodel->leafs;
2038 endleaf = leaf + loadmodel->numleafs;
2039 for (;leaf < endleaf;leaf++)
2041 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2042 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2049 for (i = 0;i < 2;i++)
2051 leaf = (mleaf_t *)p->nodes[i];
2053 for (j = 0;j < w->numpoints;j++)
2055 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2056 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2057 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2058 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2059 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2060 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2067 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2069 // tally up portal and point counts
2075 // note: this check must match the one below or it will usually corrupt memory
2076 // 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
2077 if (p->winding && p->nodes[0] != p->nodes[1]
2078 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2079 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2082 numpoints += p->winding->numpoints * 2;
2086 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2087 loadmodel->numportals = numportals;
2088 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2089 loadmodel->numportalpoints = numpoints;
2090 // clear all leaf portal chains
2091 for (i = 0;i < loadmodel->numleafs;i++)
2092 loadmodel->leafs[i].portals = NULL;
2093 // process all portals in the global portal chain, while freeing them
2094 portal = loadmodel->portals;
2095 point = loadmodel->portalpoints;
2104 // note: this check must match the one above or it will usually corrupt memory
2105 // 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
2106 if (p->nodes[0] != p->nodes[1]
2107 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2108 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2110 // first make the back to front portal (forward portal)
2111 portal->points = point;
2112 portal->numpoints = p->winding->numpoints;
2113 portal->plane.dist = p->plane.dist;
2114 VectorCopy(p->plane.normal, portal->plane.normal);
2115 portal->here = (mleaf_t *)p->nodes[1];
2116 portal->past = (mleaf_t *)p->nodes[0];
2118 for (j = 0;j < portal->numpoints;j++)
2120 VectorCopy(p->winding->points[j], point->position);
2123 PlaneClassify(&portal->plane);
2125 // link into leaf's portal chain
2126 portal->next = portal->here->portals;
2127 portal->here->portals = portal;
2129 // advance to next portal
2132 // then make the front to back portal (backward portal)
2133 portal->points = point;
2134 portal->numpoints = p->winding->numpoints;
2135 portal->plane.dist = -p->plane.dist;
2136 VectorNegate(p->plane.normal, portal->plane.normal);
2137 portal->here = (mleaf_t *)p->nodes[0];
2138 portal->past = (mleaf_t *)p->nodes[1];
2140 for (j = portal->numpoints - 1;j >= 0;j--)
2142 VectorCopy(p->winding->points[j], point->position);
2145 PlaneClassify(&portal->plane);
2147 // link into leaf's portal chain
2148 portal->next = portal->here->portals;
2149 portal->here->portals = portal;
2151 // advance to next portal
2154 FreeWinding(p->winding);
2166 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2169 Host_Error ("AddPortalToNodes: NULL front node");
2171 Host_Error ("AddPortalToNodes: NULL back node");
2172 if (p->nodes[0] || p->nodes[1])
2173 Host_Error ("AddPortalToNodes: already included");
2174 // 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
2176 p->nodes[0] = front;
2177 p->next[0] = (portal_t *)front->portals;
2178 front->portals = (mportal_t *)p;
2181 p->next[1] = (portal_t *)back->portals;
2182 back->portals = (mportal_t *)p;
2187 RemovePortalFromNode
2190 static void RemovePortalFromNodes(portal_t *portal)
2194 void **portalpointer;
2196 for (i = 0;i < 2;i++)
2198 node = portal->nodes[i];
2200 portalpointer = (void **) &node->portals;
2205 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2209 if (portal->nodes[0] == node)
2211 *portalpointer = portal->next[0];
2212 portal->nodes[0] = NULL;
2214 else if (portal->nodes[1] == node)
2216 *portalpointer = portal->next[1];
2217 portal->nodes[1] = NULL;
2220 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2224 if (t->nodes[0] == node)
2225 portalpointer = (void **) &t->next[0];
2226 else if (t->nodes[1] == node)
2227 portalpointer = (void **) &t->next[1];
2229 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2234 static void Mod_RecursiveNodePortals (mnode_t *node)
2237 mnode_t *front, *back, *other_node;
2238 mplane_t clipplane, *plane;
2239 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2240 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2242 // if a leaf, we're done
2246 plane = node->plane;
2248 front = node->children[0];
2249 back = node->children[1];
2251 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2253 // create the new portal by generating a polygon for the node plane,
2254 // and clipping it by all of the other portals (which came from nodes above this one)
2255 nodeportal = AllocPortal ();
2256 nodeportal->plane = *node->plane;
2258 nodeportalwinding = BaseWindingForPlane (node->plane);
2259 side = 0; // shut up compiler warning
2260 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2262 clipplane = portal->plane;
2263 if (portal->nodes[0] == portal->nodes[1])
2264 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2265 if (portal->nodes[0] == node)
2267 else if (portal->nodes[1] == node)
2269 clipplane.dist = -clipplane.dist;
2270 VectorNegate (clipplane.normal, clipplane.normal);
2274 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2276 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2277 if (!nodeportalwinding)
2279 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2284 if (nodeportalwinding)
2286 // if the plane was not clipped on all sides, there was an error
2287 nodeportal->winding = nodeportalwinding;
2288 AddPortalToNodes (nodeportal, front, back);
2291 // split the portals of this node along this node's plane and assign them to the children of this node
2292 // (migrating the portals downward through the tree)
2293 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2295 if (portal->nodes[0] == portal->nodes[1])
2296 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2297 if (portal->nodes[0] == node)
2299 else if (portal->nodes[1] == node)
2302 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2303 nextportal = portal->next[side];
2305 other_node = portal->nodes[!side];
2306 RemovePortalFromNodes (portal);
2308 // cut the portal into two portals, one on each side of the node plane
2309 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2314 AddPortalToNodes (portal, back, other_node);
2316 AddPortalToNodes (portal, other_node, back);
2322 AddPortalToNodes (portal, front, other_node);
2324 AddPortalToNodes (portal, other_node, front);
2328 // the winding is split
2329 splitportal = AllocPortal ();
2330 temp = splitportal->chain;
2331 *splitportal = *portal;
2332 splitportal->chain = temp;
2333 splitportal->winding = backwinding;
2334 FreeWinding (portal->winding);
2335 portal->winding = frontwinding;
2339 AddPortalToNodes (portal, front, other_node);
2340 AddPortalToNodes (splitportal, back, other_node);
2344 AddPortalToNodes (portal, other_node, front);
2345 AddPortalToNodes (splitportal, other_node, back);
2349 Mod_RecursiveNodePortals(front);
2350 Mod_RecursiveNodePortals(back);
2354 static void Mod_MakePortals(void)
2357 Mod_RecursiveNodePortals (loadmodel->nodes);
2358 Mod_FinalizePortals();
2366 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2371 mempool_t *mainmempool;
2374 mod->type = mod_brush;
2376 header = (dheader_t *)buffer;
2378 i = LittleLong (header->version);
2379 if (i != BSPVERSION && i != 30)
2380 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2381 mod->ishlbsp = i == 30;
2382 if (loadmodel->isworldmodel)
2383 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2385 // swap all the lumps
2386 mod_base = (qbyte *)header;
2388 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2389 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2393 // store which lightmap format to use
2394 mod->lightmaprgba = r_lightmaprgba.integer;
2396 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2397 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2398 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2399 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2400 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2401 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2402 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2403 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2404 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2405 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2406 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2407 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2408 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2409 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2410 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2415 mod->numframes = 2; // regular and alternate animation
2417 mainmempool = mod->mempool;
2418 loadname = mod->name;
2420 Mod_LoadLightList ();
2423 // set up the submodels (FIXME: this is confusing)
2425 for (i = 0;i < mod->numsubmodels;i++)
2428 float dist, modelyawradius, modelradius, *vec;
2431 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2432 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2436 bm = &mod->submodels[i];
2438 mod->hulls[0].firstclipnode = bm->headnode[0];
2439 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2441 mod->hulls[j].firstclipnode = bm->headnode[j];
2442 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2445 mod->firstmodelsurface = bm->firstface;
2446 mod->nummodelsurfaces = bm->numfaces;
2448 mod->DrawSky = NULL;
2449 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2450 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2452 // we only need to have a drawsky function if it is used (usually only on world model)
2453 if (surf->shader == &Cshader_sky)
2454 mod->DrawSky = R_DrawBrushModelSky;
2455 for (k = 0;k < surf->numedges;k++)
2457 l = mod->surfedges[k + surf->firstedge];
2459 vec = mod->vertexes[mod->edges[l].v[0]].position;
2461 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2462 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2463 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2464 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2465 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2466 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2467 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2468 dist = vec[0]*vec[0]+vec[1]*vec[1];
2469 if (modelyawradius < dist)
2470 modelyawradius = dist;
2471 dist += vec[2]*vec[2];
2472 if (modelradius < dist)
2476 modelyawradius = sqrt(modelyawradius);
2477 modelradius = sqrt(modelradius);
2478 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2479 mod->yawmins[2] = mod->normalmins[2];
2480 mod->yawmaxs[2] = mod->normalmaxs[2];
2481 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2482 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2483 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2484 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2486 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2487 VectorClear(mod->normalmins);
2488 VectorClear(mod->normalmaxs);
2489 VectorClear(mod->yawmins);
2490 VectorClear(mod->yawmaxs);
2491 VectorClear(mod->rotatedmins);
2492 VectorClear(mod->rotatedmaxs);
2495 mod->numleafs = bm->visleafs;
2497 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2498 mod->Draw = R_DrawBrushModelNormal;
2499 mod->DrawShadow = NULL;
2501 Mod_BrushSortedSurfaces(mod, mainmempool);
2503 // LordHavoc: only register submodels if it is the world
2504 // (prevents bsp models from replacing world submodels)
2505 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2508 // duplicate the basic information
2509 sprintf (name, "*%i", i+1);
2510 loadmodel = Mod_FindName (name);
2512 strcpy (loadmodel->name, name);
2513 // textures and memory belong to the main model
2514 loadmodel->texturepool = NULL;
2515 loadmodel->mempool = NULL;