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 byte 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);
63 // if (!model || !model->nodes)
64 // Sys_Error ("Mod_PointInLeaf: bad model");
66 // LordHavoc: modified to start at first clip node,
67 // in other words: first node of the (sub)model
68 node = model->nodes + model->hulls[0].firstclipnode;
69 while (node->contents == 0)
70 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
72 return (mleaf_t *)node;
75 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
77 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
78 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
79 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
81 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
82 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
84 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
85 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
90 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
96 if (!model || !model->nodes)
97 Sys_Error ("Mod_PointInLeaf: bad model");
102 if (node->contents < 0)
103 return (mleaf_t *)node;
105 d = DotProduct (p,plane->normal) - plane->dist;
107 node = node->children[0];
109 node = node->children[1];
112 return NULL; // never reached
121 static byte *Mod_DecompressVis (byte *in, model_t *model)
123 static byte decompressed[MAX_MAP_LEAFS/8];
128 row = (model->numleafs+7)>>3;
133 { // no vis info, so make all visible
158 } while (out - decompressed < row);
163 byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
165 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
167 return Mod_DecompressVis (leaf->compressed_vis, model);
170 void Mod_SetupNoTexture(void)
175 for (y = 0;y < 16;y++)
177 for (x = 0;x < 16;x++)
179 if ((y < 8) ^ (x < 8))
196 memset(&loadmodel->notexture, 0, sizeof(texture_t));
197 strcpy(loadmodel->notexture.name, "notexture");
198 loadmodel->notexture.width = 16;
199 loadmodel->notexture.height = 16;
200 loadmodel->notexture.flags = 0;
201 loadmodel->notexture.texture = R_LoadTexture(loadmodel->texturepool, "notexture", 16, 16, &pix[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP);
209 static void Mod_LoadTextures (lump_t *l)
211 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
213 texture_t *tx, *tx2, *anims[10], *altanims[10];
215 byte *data, *mtdata, *data2;
218 Mod_SetupNoTexture();
222 loadmodel->textures = NULL;
226 m = (dmiptexlump_t *)(mod_base + l->fileofs);
228 m->nummiptex = LittleLong (m->nummiptex);
230 loadmodel->numtextures = m->nummiptex;
231 loadmodel->textures = Mem_Alloc(loadmodel->mempool, m->nummiptex * sizeof(*loadmodel->textures));
233 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
235 for (i = 0;i < m->nummiptex;i++)
237 dofs[i] = LittleLong(dofs[i]);
240 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
241 mtwidth = LittleLong (dmiptex->width);
242 mtheight = LittleLong (dmiptex->height);
244 j = LittleLong (dmiptex->offsets[0]);
248 if (j < 40 || j + mtwidth * mtheight > l->filelen)
249 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
250 mtdata = (byte *)dmiptex + j;
253 if ((mtwidth & 15) || (mtheight & 15))
254 Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
255 // LordHavoc: rewriting the map texture loader for GLQuake
256 tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
257 memset(tx, 0, sizeof(texture_t));
259 tx->alternate_anims = NULL;
260 loadmodel->textures[i] = tx;
262 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
263 for (j = 0;dmiptex->name[j] && j < 15;j++)
265 if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
266 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
268 tx->name[j] = dmiptex->name[j];
275 Con_Printf("warning: unnamed texture in %s\n", loadmodel->name);
276 sprintf(tx->name, "unnamed%i", i);
280 tx->height = mtheight;
282 tx->glowtexture = NULL;
283 tx->fogtexture = NULL;
285 if (!loadmodel->ishlbsp && !strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128) // LordHavoc: HL sky textures are entirely unrelated
287 data = loadimagepixels(tx->name, false, 0, 0);
290 if (image_width == 256 && image_height == 128)
292 if (loadmodel->isworldmodel)
299 Host_Error("Mod_LoadTextures: replacement sky image must be 256x128 pixels\n");
302 else if (loadmodel->isworldmodel)
303 R_InitSky (mtdata, 1);
305 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
307 tx->fogtexture = image_masktex;
308 strcpy(name, tx->name);
309 strcat(name, "_glow");
310 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
314 if (loadmodel->ishlbsp)
316 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
319 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
320 if (R_TextureHasAlpha(tx->texture))
323 for (j = 0;j < image_width * image_height;j++)
324 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
325 strcpy(name, tx->name);
326 strcat(name, "_fog");
327 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
331 else if ((data = W_GetTexture(tx->name)))
333 // get the size from the wad texture
334 tx->width = image_width;
335 tx->height = image_height;
336 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
337 if (R_TextureHasAlpha(tx->texture))
340 for (j = 0;j < image_width * image_height;j++)
341 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
342 strcpy(name, tx->name);
343 strcat(name, "_fog");
344 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
352 tx->texture = loadmodel->notexture.texture;
357 if (mtdata) // texture included
362 if (r_fullbrights.value && tx->name[0] != '*')
364 for (j = 0;j < tx->width*tx->height;j++)
366 if (data[j] >= 224) // fullbright
375 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
376 for (j = 0;j < tx->width*tx->height;j++)
377 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
378 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
379 strcpy(name, tx->name);
380 strcat(name, "_glow");
381 for (j = 0;j < tx->width*tx->height;j++)
382 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
383 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
387 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
389 else // no texture, and no external replacement texture was found
393 tx->texture = loadmodel->notexture.texture;
398 if (tx->name[0] == '*')
400 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
401 // LordHavoc: some turbulent textures should be fullbright and solid
402 if (!strncmp(tx->name,"*lava",5)
403 || !strncmp(tx->name,"*teleport",9)
404 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
405 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
407 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
408 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
411 tx->flags |= SURF_LIGHTMAP;
412 if (!R_TextureHasAlpha(tx->texture))
413 tx->flags |= SURF_CLIPSOLID;
418 // sequence the animations
420 for (i = 0;i < m->nummiptex;i++)
422 tx = loadmodel->textures[i];
423 if (!tx || tx->name[0] != '+')
426 continue; // already sequenced
428 // find the number of frames in the animation
429 memset (anims, 0, sizeof(anims));
430 memset (altanims, 0, sizeof(altanims));
433 for (j = i;j < m->nummiptex;j++)
435 tx2 = loadmodel->textures[j];
436 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
440 if (num >= '0' && num <= '9')
441 anims[num - '0'] = tx2;
442 else if (num >= 'a' && num <= 'j')
443 altanims[num - 'a'] = tx2;
445 Host_Error ("Bad animating texture %s", tx->name);
448 for (j = 0;j < 10;j++)
450 if (anims[j] != NULL)
452 if (altanims[j] != NULL)
456 // link them all together
457 for (j = 0;j < max;j++)
461 Host_Error ("Missing frame %i of %s", j, tx->name);
462 tx2->anim_total = max;
463 tx2->alternate_anims = altanims[0]; // NULL if there is no alternate
464 for (k = 0;k < 10;k++)
465 tx2->anim_frames[k] = anims[k];
468 for (j = 0;j < altmax;j++)
472 Host_Error ("Missing frame %i of %s", j, tx->name);
473 tx2->anim_total = altmax;
474 tx2->alternate_anims = anims[0]; // NULL if there is no alternate
475 for (k = 0;k < 10;k++)
476 tx2->anim_frames[k] = altanims[k];
486 static void Mod_LoadLighting (lump_t *l)
489 byte *in, *out, *data;
491 char litfilename[1024];
492 loadmodel->lightdata = NULL;
493 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
495 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
496 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
498 else // LordHavoc: bsp version 29 (normal white lighting)
500 // LordHavoc: hope is not lost yet, check for a .lit file to load
501 strcpy(litfilename, loadmodel->name);
502 COM_StripExtension(litfilename, litfilename);
503 strcat(litfilename, ".lit");
504 data = (byte*) COM_LoadFile (litfilename, false);
507 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
509 i = LittleLong(((int *)data)[1]);
512 Con_DPrintf("%s loaded", litfilename);
513 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
514 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
520 Con_Printf("Unknown .lit file version (%d)\n", i);
527 Con_Printf("Empty .lit file, ignoring\n");
529 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
533 // LordHavoc: oh well, expand the white lighting data
536 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
537 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
538 out = loadmodel->lightdata;
539 memcpy (in, mod_base + l->fileofs, l->filelen);
540 for (i = 0;i < l->filelen;i++)
556 static void Mod_LoadVisibility (lump_t *l)
560 loadmodel->visdata = NULL;
563 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
564 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
567 // used only for HalfLife maps
568 void Mod_ParseWadsFromEntityLump(char *data)
570 char key[128], value[4096];
575 data = COM_Parse(data);
578 if (com_token[0] != '{')
582 data = COM_Parse(data);
585 if (com_token[0] == '}')
586 break; // end of worldspawn
587 if (com_token[0] == '_')
588 strcpy(key, com_token + 1);
590 strcpy(key, com_token);
591 while (key[strlen(key)-1] == ' ') // remove trailing spaces
592 key[strlen(key)-1] = 0;
593 data = COM_Parse(data);
596 strcpy(value, com_token);
597 if (!strcmp("wad", key)) // for HalfLife maps
599 if (loadmodel->ishlbsp)
602 for (i = 0;i < 4096;i++)
603 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
609 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
610 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
612 else if (value[i] == ';' || value[i] == 0)
616 strcpy(wadname, "textures/");
617 strcat(wadname, &value[j]);
618 W_LoadTextureWadFile (wadname, false);
635 static void Mod_LoadEntities (lump_t *l)
639 loadmodel->entities = NULL;
642 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
643 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
644 if (loadmodel->ishlbsp)
645 Mod_ParseWadsFromEntityLump(loadmodel->entities);
654 static void Mod_LoadVertexes (lump_t *l)
660 in = (void *)(mod_base + l->fileofs);
661 if (l->filelen % sizeof(*in))
662 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
663 count = l->filelen / sizeof(*in);
664 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
666 loadmodel->vertexes = out;
667 loadmodel->numvertexes = count;
669 for ( i=0 ; i<count ; i++, in++, out++)
671 out->position[0] = LittleFloat (in->point[0]);
672 out->position[1] = LittleFloat (in->point[1]);
673 out->position[2] = LittleFloat (in->point[2]);
682 static void Mod_LoadSubmodels (lump_t *l)
688 in = (void *)(mod_base + l->fileofs);
689 if (l->filelen % sizeof(*in))
690 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
691 count = l->filelen / sizeof(*in);
692 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
694 loadmodel->submodels = out;
695 loadmodel->numsubmodels = count;
697 for ( i=0 ; i<count ; i++, in++, out++)
699 for (j=0 ; j<3 ; j++)
701 // spread the mins / maxs by a pixel
702 out->mins[j] = LittleFloat (in->mins[j]) - 1;
703 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
704 out->origin[j] = LittleFloat (in->origin[j]);
706 for (j=0 ; j<MAX_MAP_HULLS ; j++)
707 out->headnode[j] = LittleLong (in->headnode[j]);
708 out->visleafs = LittleLong (in->visleafs);
709 out->firstface = LittleLong (in->firstface);
710 out->numfaces = LittleLong (in->numfaces);
719 static void Mod_LoadEdges (lump_t *l)
725 in = (void *)(mod_base + l->fileofs);
726 if (l->filelen % sizeof(*in))
727 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
728 count = l->filelen / sizeof(*in);
729 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
731 loadmodel->edges = out;
732 loadmodel->numedges = count;
734 for ( i=0 ; i<count ; i++, in++, out++)
736 out->v[0] = (unsigned short)LittleShort(in->v[0]);
737 out->v[1] = (unsigned short)LittleShort(in->v[1]);
746 static void Mod_LoadTexinfo (lump_t *l)
753 in = (void *)(mod_base + l->fileofs);
754 if (l->filelen % sizeof(*in))
755 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
756 count = l->filelen / sizeof(*in);
757 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
759 loadmodel->texinfo = out;
760 loadmodel->numtexinfo = count;
762 for (i = 0;i < count;i++, in++, out++)
764 for (k = 0;k < 2;k++)
765 for (j = 0;j < 4;j++)
766 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
768 miptex = LittleLong (in->miptex);
769 out->flags = LittleLong (in->flags);
771 if (!loadmodel->textures)
772 out->texture = &loadmodel->notexture;
776 Host_Error ("miptex < 0");
777 if (miptex >= loadmodel->numtextures)
778 Host_Error ("miptex >= loadmodel->numtextures");
779 out->texture = loadmodel->textures[miptex];
782 out->texture = &loadmodel->notexture;
790 Fills in s->texturemins[] and s->extents[]
793 static void CalcSurfaceExtents (msurface_t *s)
795 float mins[2], maxs[2], val;
799 int bmins[2], bmaxs[2];
801 mins[0] = mins[1] = 999999999;
802 maxs[0] = maxs[1] = -999999999;
806 for (i=0 ; i<s->numedges ; i++)
808 e = loadmodel->surfedges[s->firstedge+i];
810 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
812 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
814 for (j=0 ; j<2 ; j++)
816 val = v->position[0] * tex->vecs[j][0] +
817 v->position[1] * tex->vecs[j][1] +
818 v->position[2] * tex->vecs[j][2] +
827 for (i=0 ; i<2 ; i++)
829 bmins[i] = floor(mins[i]/16);
830 bmaxs[i] = ceil(maxs[i]/16);
832 s->texturemins[i] = bmins[i] * 16;
833 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
838 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
843 mins[0] = mins[1] = mins[2] = 9999;
844 maxs[0] = maxs[1] = maxs[2] = -9999;
846 for (i = 0;i < numverts;i++)
848 for (j = 0;j < 3;j++, v++)
858 #define MAX_SUBDIVPOLYTRIANGLES 4096
859 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
861 static int subdivpolyverts, subdivpolytriangles;
862 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
863 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
865 static int subdivpolylookupvert(vec3_t v)
868 for (i = 0;i < subdivpolyverts;i++)
869 if (subdivpolyvert[i][0] == v[0]
870 && subdivpolyvert[i][1] == v[1]
871 && subdivpolyvert[i][2] == v[2])
873 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
874 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
875 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
876 return subdivpolyverts++;
879 static void SubdividePolygon (int numverts, float *verts)
881 int i, i1, i2, i3, f, b, c, p;
882 vec3_t mins, maxs, front[256], back[256];
883 float m, *pv, *cv, dist[256], frac;
886 Host_Error ("SubdividePolygon: ran out of verts in buffer");
888 BoundPoly (numverts, verts, mins, maxs);
890 for (i = 0;i < 3;i++)
892 m = (mins[i] + maxs[i]) * 0.5;
893 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
900 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
904 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
908 VectorCopy (pv, front[f]);
913 VectorCopy (pv, back[b]);
916 if (dist[p] == 0 || dist[c] == 0)
918 if ( (dist[p] > 0) != (dist[c] > 0) )
921 frac = dist[p] / (dist[p] - dist[c]);
922 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
923 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
924 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
930 SubdividePolygon (f, front[0]);
931 SubdividePolygon (b, back[0]);
935 i1 = subdivpolylookupvert(verts);
936 i2 = subdivpolylookupvert(verts + 3);
937 for (i = 2;i < numverts;i++)
939 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
941 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
945 i3 = subdivpolylookupvert(verts + i * 3);
946 subdivpolyindex[subdivpolytriangles][0] = i1;
947 subdivpolyindex[subdivpolytriangles][1] = i2;
948 subdivpolyindex[subdivpolytriangles][2] = i3;
950 subdivpolytriangles++;
958 Breaks a polygon up along axial 64 unit
959 boundaries so that turbulent and sky warps
960 can be done reasonably.
963 void Mod_GenerateWarpMesh (msurface_t *surf)
969 subdivpolytriangles = 0;
971 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
974 mesh->numverts = subdivpolyverts;
975 mesh->numtriangles = subdivpolytriangles;
976 if (mesh->numtriangles < 1)
977 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
978 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
979 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
980 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
982 for (i = 0;i < mesh->numtriangles;i++)
984 for (j = 0;j < 3;j++)
986 mesh->index[i*3+j] = subdivpolyindex[i][j];
987 //if (mesh->index[i] < 0 || mesh->index[i] >= mesh->numverts)
988 // Host_Error("Mod_GenerateWarpMesh: invalid index generated\n");
992 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
994 VectorCopy(subdivpolyvert[i], v->v);
995 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
996 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1000 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1002 int i, is, it, *index, smax, tmax;
1007 //surf->flags |= SURF_LIGHTMAP;
1008 smax = surf->extents[0] >> 4;
1009 tmax = surf->extents[1] >> 4;
1010 surf->lightmaptexturestride = 0;
1011 surf->lightmaptexture = NULL;
1014 mesh->numverts = surf->poly_numverts;
1015 mesh->numtriangles = surf->poly_numverts - 2;
1016 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1017 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1018 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1020 index = mesh->index;
1021 for (i = 0;i < mesh->numtriangles;i++)
1028 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1030 VectorCopy (in, out->v);
1032 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1033 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1035 out->st[0] = s / surf->texinfo->texture->width;
1036 out->st[1] = t / surf->texinfo->texture->height;
1038 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1039 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1041 // lightmap coordinates
1045 // LordHavoc: calc lightmap data offset for vertex lighting to use
1048 is = bound(0, is, smax);
1049 it = bound(0, it, tmax);
1050 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1054 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1056 int i, is, it, *index, smax, tmax;
1057 float *in, s, t, xbase, ybase, xscale, yscale;
1061 surf->flags |= SURF_LIGHTMAP;
1062 smax = surf->extents[0] >> 4;
1063 tmax = surf->extents[1] >> 4;
1064 if (r_miplightmaps.integer)
1066 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1067 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);
1071 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1072 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);
1074 // surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, va("lightmap%08x", lightmapnum), surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
1075 // surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, va("lightmap%08x", lightmapnum), surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_PRECACHE);
1076 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1077 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1078 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1081 mesh->numverts = surf->poly_numverts;
1082 mesh->numtriangles = surf->poly_numverts - 2;
1083 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1084 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1085 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1087 index = mesh->index;
1088 for (i = 0;i < mesh->numtriangles;i++)
1095 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1097 VectorCopy (in, out->v);
1099 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1100 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1102 out->st[0] = s / surf->texinfo->texture->width;
1103 out->st[1] = t / surf->texinfo->texture->height;
1105 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1106 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1108 // lightmap coordinates
1109 out->uv[0] = s * xscale + xbase;
1110 out->uv[1] = t * yscale + ybase;
1112 // LordHavoc: calc lightmap data offset for vertex lighting to use
1115 is = bound(0, is, smax);
1116 it = bound(0, it, tmax);
1117 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1121 void Mod_GenerateVertexMesh (msurface_t *surf)
1128 surf->lightmaptexturestride = 0;
1129 surf->lightmaptexture = NULL;
1132 mesh->numverts = surf->poly_numverts;
1133 mesh->numtriangles = surf->poly_numverts - 2;
1134 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1135 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1136 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1138 index = mesh->index;
1139 for (i = 0;i < mesh->numtriangles;i++)
1146 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1148 VectorCopy (in, out->v);
1149 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1150 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1154 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1161 // convert edges back to a normal polygon
1162 surf->poly_numverts = surf->numedges;
1163 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1164 for (i = 0;i < surf->numedges;i++)
1166 lindex = loadmodel->surfedges[surf->firstedge + i];
1168 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1170 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1171 VectorCopy (vec, vert);
1181 static void Mod_LoadFaces (lump_t *l)
1185 int i, count, surfnum, planenum, side, ssize, tsize;
1187 in = (void *)(mod_base + l->fileofs);
1188 if (l->filelen % sizeof(*in))
1189 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1190 count = l->filelen / sizeof(*in);
1191 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1193 loadmodel->surfaces = out;
1194 loadmodel->numsurfaces = count;
1196 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1198 // FIXME: validate edges, texinfo, etc?
1199 out->firstedge = LittleLong(in->firstedge);
1200 out->numedges = LittleShort(in->numedges);
1202 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1203 out->flags = out->texinfo->texture->flags;
1205 planenum = LittleShort(in->planenum);
1206 side = LittleShort(in->side);
1208 out->flags |= SURF_PLANEBACK;
1210 out->plane = loadmodel->planes + planenum;
1212 // clear lightmap (filled in later)
1213 out->lightmaptexture = NULL;
1215 // force lightmap upload on first time seeing the surface
1216 out->cached_dlight = true;
1217 out->cached_ambient = -1000;
1218 out->cached_lightscalebit = -1000;
1220 CalcSurfaceExtents (out);
1222 ssize = (out->extents[0] >> 4) + 1;
1223 tsize = (out->extents[1] >> 4) + 1;
1226 for (i = 0;i < MAXLIGHTMAPS;i++)
1227 out->styles[i] = in->styles[i];
1228 i = LittleLong(in->lightofs);
1230 out->samples = NULL;
1231 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1232 out->samples = loadmodel->lightdata + i;
1233 else // LordHavoc: white lighting (bsp version 29)
1234 out->samples = loadmodel->lightdata + (i * 3);
1236 Mod_GenerateSurfacePolygon(out);
1238 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1240 out->shader = &Cshader_sky;
1241 out->samples = NULL;
1242 Mod_GenerateWarpMesh (out);
1246 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1248 out->shader = &Cshader_water;
1250 for (i=0 ; i<2 ; i++)
1252 out->extents[i] = 16384*1024;
1253 out->texturemins[i] = -8192*1024;
1256 out->samples = NULL;
1257 Mod_GenerateWarpMesh (out);
1261 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1262 out->flags |= SURF_CLIPSOLID;
1263 if (out->texinfo->flags & TEX_SPECIAL)
1265 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1266 out->shader = &Cshader_water;
1267 out->samples = NULL;
1268 Mod_GenerateWarpMesh (out);
1270 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1272 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1273 out->shader = &Cshader_wall_fullbright;
1274 out->samples = NULL;
1275 Mod_GenerateVertexMesh(out);
1279 // stainmap for permanent marks on walls
1280 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1282 memset(out->stainsamples, 255, ssize * tsize * 3);
1283 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1285 out->shader = &Cshader_wall_vertex;
1286 Mod_GenerateVertexLitMesh(out);
1290 out->shader = &Cshader_wall_lightmap;
1291 Mod_GenerateLightmappedMesh(out);
1297 static model_t *sortmodel;
1299 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1301 const msurface_t *a, *b;
1302 a = *((const msurface_t **)voida);
1303 b = *((const msurface_t **)voidb);
1304 if (a->shader != b->shader)
1305 return (long) a->shader - (long) b->shader;
1306 if (a->texinfo->texture != b->texinfo->texture);
1307 return a->texinfo->texture - b->texinfo->texture;
1311 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1315 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1316 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1317 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1319 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1328 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1330 node->parent = parent;
1331 if (node->contents < 0)
1333 Mod_SetParent (node->children[0], node);
1334 Mod_SetParent (node->children[1], node);
1342 static void Mod_LoadNodes (lump_t *l)
1348 in = (void *)(mod_base + l->fileofs);
1349 if (l->filelen % sizeof(*in))
1350 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1351 count = l->filelen / sizeof(*in);
1352 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1354 loadmodel->nodes = out;
1355 loadmodel->numnodes = count;
1357 for ( i=0 ; i<count ; i++, in++, out++)
1359 for (j=0 ; j<3 ; j++)
1361 out->mins[j] = LittleShort (in->mins[j]);
1362 out->maxs[j] = LittleShort (in->maxs[j]);
1365 p = LittleLong(in->planenum);
1366 out->plane = loadmodel->planes + p;
1368 out->firstsurface = LittleShort (in->firstface);
1369 out->numsurfaces = LittleShort (in->numfaces);
1371 for (j=0 ; j<2 ; j++)
1373 p = LittleShort (in->children[j]);
1375 out->children[j] = loadmodel->nodes + p;
1377 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1381 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1389 static void Mod_LoadLeafs (lump_t *l)
1395 in = (void *)(mod_base + l->fileofs);
1396 if (l->filelen % sizeof(*in))
1397 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1398 count = l->filelen / sizeof(*in);
1399 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1401 loadmodel->leafs = out;
1402 loadmodel->numleafs = count;
1404 for ( i=0 ; i<count ; i++, in++, out++)
1406 for (j=0 ; j<3 ; j++)
1408 out->mins[j] = LittleShort (in->mins[j]);
1409 out->maxs[j] = LittleShort (in->maxs[j]);
1412 p = LittleLong(in->contents);
1415 out->firstmarksurface = loadmodel->marksurfaces +
1416 LittleShort(in->firstmarksurface);
1417 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1419 p = LittleLong(in->visofs);
1421 out->compressed_vis = NULL;
1423 out->compressed_vis = loadmodel->visdata + p;
1425 for (j=0 ; j<4 ; j++)
1426 out->ambient_sound_level[j] = in->ambient_level[j];
1428 // gl underwater warp
1429 // LordHavoc: disabled underwater warping
1431 if (out->contents != CONTENTS_EMPTY)
1433 for (j=0 ; j<out->nummarksurfaces ; j++)
1434 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1445 static void Mod_LoadClipnodes (lump_t *l)
1447 dclipnode_t *in, *out;
1451 in = (void *)(mod_base + l->fileofs);
1452 if (l->filelen % sizeof(*in))
1453 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1454 count = l->filelen / sizeof(*in);
1455 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1457 loadmodel->clipnodes = out;
1458 loadmodel->numclipnodes = count;
1460 if (loadmodel->ishlbsp)
1462 hull = &loadmodel->hulls[1];
1463 hull->clipnodes = out;
1464 hull->firstclipnode = 0;
1465 hull->lastclipnode = count-1;
1466 hull->planes = loadmodel->planes;
1467 hull->clip_mins[0] = -16;
1468 hull->clip_mins[1] = -16;
1469 hull->clip_mins[2] = -36;
1470 hull->clip_maxs[0] = 16;
1471 hull->clip_maxs[1] = 16;
1472 hull->clip_maxs[2] = 36;
1474 hull = &loadmodel->hulls[2];
1475 hull->clipnodes = out;
1476 hull->firstclipnode = 0;
1477 hull->lastclipnode = count-1;
1478 hull->planes = loadmodel->planes;
1479 hull->clip_mins[0] = -32;
1480 hull->clip_mins[1] = -32;
1481 hull->clip_mins[2] = -32;
1482 hull->clip_maxs[0] = 32;
1483 hull->clip_maxs[1] = 32;
1484 hull->clip_maxs[2] = 32;
1486 hull = &loadmodel->hulls[3];
1487 hull->clipnodes = out;
1488 hull->firstclipnode = 0;
1489 hull->lastclipnode = count-1;
1490 hull->planes = loadmodel->planes;
1491 hull->clip_mins[0] = -16;
1492 hull->clip_mins[1] = -16;
1493 hull->clip_mins[2] = -18;
1494 hull->clip_maxs[0] = 16;
1495 hull->clip_maxs[1] = 16;
1496 hull->clip_maxs[2] = 18;
1500 hull = &loadmodel->hulls[1];
1501 hull->clipnodes = out;
1502 hull->firstclipnode = 0;
1503 hull->lastclipnode = count-1;
1504 hull->planes = loadmodel->planes;
1505 hull->clip_mins[0] = -16;
1506 hull->clip_mins[1] = -16;
1507 hull->clip_mins[2] = -24;
1508 hull->clip_maxs[0] = 16;
1509 hull->clip_maxs[1] = 16;
1510 hull->clip_maxs[2] = 32;
1512 hull = &loadmodel->hulls[2];
1513 hull->clipnodes = out;
1514 hull->firstclipnode = 0;
1515 hull->lastclipnode = count-1;
1516 hull->planes = loadmodel->planes;
1517 hull->clip_mins[0] = -32;
1518 hull->clip_mins[1] = -32;
1519 hull->clip_mins[2] = -24;
1520 hull->clip_maxs[0] = 32;
1521 hull->clip_maxs[1] = 32;
1522 hull->clip_maxs[2] = 64;
1525 for (i=0 ; i<count ; i++, out++, in++)
1527 out->planenum = LittleLong(in->planenum);
1528 out->children[0] = LittleShort(in->children[0]);
1529 out->children[1] = LittleShort(in->children[1]);
1530 if (out->children[0] >= count || out->children[1] >= count)
1531 Host_Error("Corrupt clipping hull (out of range child)\n");
1539 Duplicate the drawing hull structure as a clipping hull
1542 static void Mod_MakeHull0 (void)
1549 hull = &loadmodel->hulls[0];
1551 in = loadmodel->nodes;
1552 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1554 hull->clipnodes = out;
1555 hull->firstclipnode = 0;
1556 hull->lastclipnode = loadmodel->numnodes - 1;
1557 hull->planes = loadmodel->planes;
1559 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1561 out->planenum = in->plane - loadmodel->planes;
1562 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1563 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1569 Mod_LoadMarksurfaces
1572 static void Mod_LoadMarksurfaces (lump_t *l)
1577 in = (void *)(mod_base + l->fileofs);
1578 if (l->filelen % sizeof(*in))
1579 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1580 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1581 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1583 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1585 j = (unsigned) LittleShort(in[i]);
1586 if (j >= loadmodel->numsurfaces)
1587 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1588 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1597 static void Mod_LoadSurfedges (lump_t *l)
1602 in = (void *)(mod_base + l->fileofs);
1603 if (l->filelen % sizeof(*in))
1604 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1605 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1606 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1608 for (i = 0;i < loadmodel->numsurfedges;i++)
1609 loadmodel->surfedges[i] = LittleLong (in[i]);
1618 static void Mod_LoadPlanes (lump_t *l)
1624 in = (void *)(mod_base + l->fileofs);
1625 if (l->filelen % sizeof(*in))
1626 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1628 loadmodel->numplanes = l->filelen / sizeof(*in);
1629 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1631 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1633 out->normal[0] = LittleFloat (in->normal[0]);
1634 out->normal[1] = LittleFloat (in->normal[1]);
1635 out->normal[2] = LittleFloat (in->normal[2]);
1636 out->dist = LittleFloat (in->dist);
1638 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1639 // out->type = LittleLong (in->type);
1644 #define MAX_POINTS_ON_WINDING 64
1650 double points[8][3]; // variable sized
1659 static winding_t *NewWinding (int points)
1664 if (points > MAX_POINTS_ON_WINDING)
1665 Sys_Error("NewWinding: too many points\n");
1667 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1668 w = Mem_Alloc(loadmodel->mempool, size);
1669 memset (w, 0, size);
1674 static void FreeWinding (winding_t *w)
1684 static winding_t *BaseWindingForPlane (mplane_t *p)
1686 double org[3], vright[3], vup[3], normal[3];
1689 VectorCopy(p->normal, normal);
1690 VectorVectorsDouble(normal, vright, vup);
1692 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1693 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1695 // project a really big axis aligned box onto the plane
1698 VectorScale (p->normal, p->dist, org);
1700 VectorSubtract (org, vright, w->points[0]);
1701 VectorAdd (w->points[0], vup, w->points[0]);
1703 VectorAdd (org, vright, w->points[1]);
1704 VectorAdd (w->points[1], vup, w->points[1]);
1706 VectorAdd (org, vright, w->points[2]);
1707 VectorSubtract (w->points[2], vup, w->points[2]);
1709 VectorSubtract (org, vright, w->points[3]);
1710 VectorSubtract (w->points[3], vup, w->points[3]);
1721 Clips the winding to the plane, returning the new winding on the positive side
1722 Frees the input winding.
1723 If keepon is true, an exactly on-plane winding will be saved, otherwise
1724 it will be clipped away.
1727 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1729 double dists[MAX_POINTS_ON_WINDING + 1];
1730 int sides[MAX_POINTS_ON_WINDING + 1];
1739 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1741 // determine sides for each point
1742 for (i = 0;i < in->numpoints;i++)
1744 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1745 if (dot > ON_EPSILON)
1746 sides[i] = SIDE_FRONT;
1747 else if (dot < -ON_EPSILON)
1748 sides[i] = SIDE_BACK;
1753 sides[i] = sides[0];
1754 dists[i] = dists[0];
1756 if (keepon && !counts[0] && !counts[1])
1767 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1768 if (maxpts > MAX_POINTS_ON_WINDING)
1769 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1771 neww = NewWinding (maxpts);
1773 for (i = 0;i < in->numpoints;i++)
1775 if (neww->numpoints >= maxpts)
1776 Sys_Error ("ClipWinding: points exceeded estimate");
1780 if (sides[i] == SIDE_ON)
1782 VectorCopy (p1, neww->points[neww->numpoints]);
1787 if (sides[i] == SIDE_FRONT)
1789 VectorCopy (p1, neww->points[neww->numpoints]);
1793 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1796 // generate a split point
1797 p2 = in->points[(i+1)%in->numpoints];
1799 dot = dists[i] / (dists[i]-dists[i+1]);
1800 for (j = 0;j < 3;j++)
1801 { // avoid round off error when possible
1802 if (split->normal[j] == 1)
1803 mid[j] = split->dist;
1804 else if (split->normal[j] == -1)
1805 mid[j] = -split->dist;
1807 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1810 VectorCopy (mid, neww->points[neww->numpoints]);
1814 // free the original winding
1818 //Mem_CheckSentinels(neww);
1828 Divides a winding by a plane, producing one or two windings. The
1829 original winding is not damaged or freed. If only on one side, the
1830 returned winding will be the input winding. If on both sides, two
1831 new windings will be created.
1834 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1836 double dists[MAX_POINTS_ON_WINDING + 1];
1837 int sides[MAX_POINTS_ON_WINDING + 1];
1846 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1848 // determine sides for each point
1849 for (i = 0;i < in->numpoints;i++)
1851 dot = DotProduct (in->points[i], split->normal);
1854 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1855 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1856 else sides[i] = SIDE_ON;
1859 sides[i] = sides[0];
1860 dists[i] = dists[0];
1862 *front = *back = NULL;
1875 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1877 if (maxpts > MAX_POINTS_ON_WINDING)
1878 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1880 *front = f = NewWinding (maxpts);
1881 *back = b = NewWinding (maxpts);
1883 for (i = 0;i < in->numpoints;i++)
1885 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1886 Sys_Error ("DivideWinding: points exceeded estimate");
1890 if (sides[i] == SIDE_ON)
1892 VectorCopy (p1, f->points[f->numpoints]);
1894 VectorCopy (p1, b->points[b->numpoints]);
1899 if (sides[i] == SIDE_FRONT)
1901 VectorCopy (p1, f->points[f->numpoints]);
1904 else if (sides[i] == SIDE_BACK)
1906 VectorCopy (p1, b->points[b->numpoints]);
1910 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1913 // generate a split point
1914 p2 = in->points[(i+1)%in->numpoints];
1916 dot = dists[i] / (dists[i]-dists[i+1]);
1917 for (j = 0;j < 3;j++)
1918 { // avoid round off error when possible
1919 if (split->normal[j] == 1)
1920 mid[j] = split->dist;
1921 else if (split->normal[j] == -1)
1922 mid[j] = -split->dist;
1924 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1927 VectorCopy (mid, f->points[f->numpoints]);
1929 VectorCopy (mid, b->points[b->numpoints]);
1934 //Mem_CheckSentinels(f);
1935 //Mem_CheckSentinels(b);
1938 typedef struct portal_s
1941 mnode_t *nodes[2]; // [0] = front side of plane
1942 struct portal_s *next[2];
1944 struct portal_s *chain; // all portals are linked into a list
1948 static portal_t *portalchain;
1955 static portal_t *AllocPortal (void)
1958 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1959 //memset(p, 0, sizeof(portal_t));
1960 p->chain = portalchain;
1965 static void FreePortal(portal_t *p)
1970 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1972 // calculate children first
1973 if (node->children[0]->contents >= 0)
1974 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1975 if (node->children[1]->contents >= 0)
1976 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1978 // make combined bounding box from children
1979 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1980 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1981 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1982 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1983 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1984 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
1987 static void Mod_FinalizePortals(void)
1989 int i, j, numportals, numpoints;
1990 portal_t *p, *pnext;
1993 mleaf_t *leaf, *endleaf;
1996 //Mem_CheckSentinelsGlobal();
1998 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1999 leaf = loadmodel->leafs;
2000 endleaf = leaf + loadmodel->numleafs;
2001 for (;leaf < endleaf;leaf++)
2003 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2004 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2011 for (i = 0;i < 2;i++)
2013 leaf = (mleaf_t *)p->nodes[i];
2015 for (j = 0;j < w->numpoints;j++)
2017 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2018 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2019 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2020 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2021 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2022 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2029 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2031 //Mem_CheckSentinelsGlobal();
2033 // tally up portal and point counts
2039 // note: this check must match the one below or it will usually corrupt memory
2040 // 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
2041 if (p->winding && p->nodes[0] != p->nodes[1]
2042 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2043 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2046 numpoints += p->winding->numpoints * 2;
2050 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2051 loadmodel->numportals = numportals;
2052 loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2053 loadmodel->numportalpoints = numpoints;
2054 // clear all leaf portal chains
2055 for (i = 0;i < loadmodel->numleafs;i++)
2056 loadmodel->leafs[i].portals = NULL;
2057 // process all portals in the global portal chain, while freeing them
2058 portal = loadmodel->portals;
2059 point = loadmodel->portalpoints;
2068 // note: this check must match the one above or it will usually corrupt memory
2069 // 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
2070 if (p->nodes[0] != p->nodes[1]
2071 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2072 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2074 // first make the back to front portal (forward portal)
2075 portal->points = point;
2076 portal->numpoints = p->winding->numpoints;
2077 portal->plane.dist = p->plane.dist;
2078 VectorCopy(p->plane.normal, portal->plane.normal);
2079 portal->here = (mleaf_t *)p->nodes[1];
2080 portal->past = (mleaf_t *)p->nodes[0];
2082 for (j = 0;j < portal->numpoints;j++)
2084 VectorCopy(p->winding->points[j], point->position);
2087 PlaneClassify(&portal->plane);
2089 // link into leaf's portal chain
2090 portal->next = portal->here->portals;
2091 portal->here->portals = portal;
2093 // advance to next portal
2096 // then make the front to back portal (backward portal)
2097 portal->points = point;
2098 portal->numpoints = p->winding->numpoints;
2099 portal->plane.dist = -p->plane.dist;
2100 VectorNegate(p->plane.normal, portal->plane.normal);
2101 portal->here = (mleaf_t *)p->nodes[0];
2102 portal->past = (mleaf_t *)p->nodes[1];
2104 for (j = portal->numpoints - 1;j >= 0;j--)
2106 VectorCopy(p->winding->points[j], point->position);
2109 PlaneClassify(&portal->plane);
2111 // link into leaf's portal chain
2112 portal->next = portal->here->portals;
2113 portal->here->portals = portal;
2115 // advance to next portal
2118 FreeWinding(p->winding);
2124 //Mem_CheckSentinelsGlobal();
2132 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2135 Host_Error ("AddPortalToNodes: NULL front node");
2137 Host_Error ("AddPortalToNodes: NULL back node");
2138 if (p->nodes[0] || p->nodes[1])
2139 Host_Error ("AddPortalToNodes: already included");
2140 // 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
2142 p->nodes[0] = front;
2143 p->next[0] = (portal_t *)front->portals;
2144 front->portals = (mportal_t *)p;
2147 p->next[1] = (portal_t *)back->portals;
2148 back->portals = (mportal_t *)p;
2153 RemovePortalFromNode
2156 static void RemovePortalFromNodes(portal_t *portal)
2160 void **portalpointer;
2162 for (i = 0;i < 2;i++)
2164 node = portal->nodes[i];
2166 portalpointer = (void **) &node->portals;
2171 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2175 if (portal->nodes[0] == node)
2177 *portalpointer = portal->next[0];
2178 portal->nodes[0] = NULL;
2180 else if (portal->nodes[1] == node)
2182 *portalpointer = portal->next[1];
2183 portal->nodes[1] = NULL;
2186 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2190 if (t->nodes[0] == node)
2191 portalpointer = (void **) &t->next[0];
2192 else if (t->nodes[1] == node)
2193 portalpointer = (void **) &t->next[1];
2195 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2200 static void Mod_RecursiveNodePortals (mnode_t *node)
2203 mnode_t *front, *back, *other_node;
2204 mplane_t clipplane, *plane;
2205 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2206 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2208 // CheckLeafPortalConsistancy (node);
2210 // if a leaf, we're done
2214 plane = node->plane;
2216 front = node->children[0];
2217 back = node->children[1];
2219 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2221 // create the new portal by generating a polygon for the node plane,
2222 // and clipping it by all of the other portals (which came from nodes above this one)
2223 nodeportal = AllocPortal ();
2224 nodeportal->plane = *node->plane;
2226 nodeportalwinding = BaseWindingForPlane (node->plane);
2227 //Mem_CheckSentinels(nodeportalwinding);
2228 side = 0; // shut up compiler warning
2229 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2231 clipplane = portal->plane;
2232 if (portal->nodes[0] == portal->nodes[1])
2233 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2234 if (portal->nodes[0] == node)
2236 else if (portal->nodes[1] == node)
2238 clipplane.dist = -clipplane.dist;
2239 VectorNegate (clipplane.normal, clipplane.normal);
2243 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2245 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2246 if (!nodeportalwinding)
2248 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2253 if (nodeportalwinding)
2255 // if the plane was not clipped on all sides, there was an error
2256 nodeportal->winding = nodeportalwinding;
2257 AddPortalToNodes (nodeportal, front, back);
2260 // split the portals of this node along this node's plane and assign them to the children of this node
2261 // (migrating the portals downward through the tree)
2262 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2264 if (portal->nodes[0] == portal->nodes[1])
2265 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2266 if (portal->nodes[0] == node)
2268 else if (portal->nodes[1] == node)
2271 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2272 nextportal = portal->next[side];
2274 other_node = portal->nodes[!side];
2275 RemovePortalFromNodes (portal);
2277 // cut the portal into two portals, one on each side of the node plane
2278 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2283 AddPortalToNodes (portal, back, other_node);
2285 AddPortalToNodes (portal, other_node, back);
2291 AddPortalToNodes (portal, front, other_node);
2293 AddPortalToNodes (portal, other_node, front);
2297 // the winding is split
2298 splitportal = AllocPortal ();
2299 temp = splitportal->chain;
2300 *splitportal = *portal;
2301 splitportal->chain = temp;
2302 splitportal->winding = backwinding;
2303 FreeWinding (portal->winding);
2304 portal->winding = frontwinding;
2308 AddPortalToNodes (portal, front, other_node);
2309 AddPortalToNodes (splitportal, back, other_node);
2313 AddPortalToNodes (portal, other_node, front);
2314 AddPortalToNodes (splitportal, other_node, back);
2318 Mod_RecursiveNodePortals(front);
2319 Mod_RecursiveNodePortals(back);
2323 void Mod_MakeOutsidePortals(mnode_t *node)
2326 portal_t *p, *portals[6];
2327 mnode_t *outside_node;
2329 outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2330 outside_node->contents = CONTENTS_SOLID;
2331 outside_node->portals = NULL;
2333 for (i = 0;i < 3;i++)
2335 for (j = 0;j < 2;j++)
2337 portals[j*3 + i] = p = AllocPortal ();
2338 memset (&p->plane, 0, sizeof(mplane_t));
2339 p->plane.normal[i] = j ? -1 : 1;
2340 p->plane.dist = -65536;
2341 p->winding = BaseWindingForPlane (&p->plane);
2343 AddPortalToNodes (p, outside_node, node);
2345 AddPortalToNodes (p, node, outside_node);
2349 // clip the basewindings by all the other planes
2350 for (i = 0;i < 6;i++)
2352 for (j = 0;j < 6;j++)
2356 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2362 static void Mod_MakePortals(void)
2364 // Con_Printf("building portals for %s\n", loadmodel->name);
2367 // Mod_MakeOutsidePortals (loadmodel->nodes);
2368 Mod_RecursiveNodePortals (loadmodel->nodes);
2369 Mod_FinalizePortals();
2377 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2382 mempool_t *mainmempool;
2384 mod->type = mod_brush;
2386 header = (dheader_t *)buffer;
2388 i = LittleLong (header->version);
2389 if (i != BSPVERSION && i != 30)
2390 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2391 mod->ishlbsp = i == 30;
2392 if (loadmodel->isworldmodel)
2393 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2395 // swap all the lumps
2396 mod_base = (byte *)header;
2398 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2399 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2403 // store which lightmap format to use
2404 mod->lightmaprgba = r_lightmaprgba.integer;
2406 // Mem_CheckSentinelsGlobal();
2407 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
2408 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2409 // Mem_CheckSentinelsGlobal();
2410 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2411 // Mem_CheckSentinelsGlobal();
2412 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2413 // Mem_CheckSentinelsGlobal();
2414 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2415 // Mem_CheckSentinelsGlobal();
2416 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2417 // Mem_CheckSentinelsGlobal();
2418 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2419 // Mem_CheckSentinelsGlobal();
2420 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2421 // Mem_CheckSentinelsGlobal();
2422 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2423 // Mem_CheckSentinelsGlobal();
2424 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2425 // Mem_CheckSentinelsGlobal();
2426 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2427 // Mem_CheckSentinelsGlobal();
2428 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2429 // Mem_CheckSentinelsGlobal();
2430 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2431 // Mem_CheckSentinelsGlobal();
2432 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2433 // Mem_CheckSentinelsGlobal();
2434 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2435 // Mem_CheckSentinelsGlobal();
2436 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2437 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2438 // Mem_CheckSentinelsGlobal();
2441 // Mem_CheckSentinelsGlobal();
2443 // Mem_CheckSentinelsGlobal();
2445 mod->numframes = 2; // regular and alternate animation
2447 mainmempool = mod->mempool;
2450 // set up the submodels (FIXME: this is confusing)
2452 for (i = 0;i < mod->numsubmodels;i++)
2455 float dist, modelyawradius, modelradius, *vec;
2458 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2459 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2463 bm = &mod->submodels[i];
2465 mod->hulls[0].firstclipnode = bm->headnode[0];
2466 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2468 mod->hulls[j].firstclipnode = bm->headnode[j];
2469 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2472 mod->firstmodelsurface = bm->firstface;
2473 mod->nummodelsurfaces = bm->numfaces;
2475 mod->DrawSky = NULL;
2476 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2477 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2479 // we only need to have a drawsky function if it is used (usually only on world model)
2480 if (surf->shader == &Cshader_sky)
2481 mod->DrawSky = R_DrawBrushModelSky;
2482 for (k = 0;k < surf->numedges;k++)
2484 l = mod->surfedges[k + surf->firstedge];
2486 vec = mod->vertexes[mod->edges[l].v[0]].position;
2488 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2489 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2490 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2491 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2492 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2493 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2494 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2495 dist = vec[0]*vec[0]+vec[1]*vec[1];
2496 if (modelyawradius < dist)
2497 modelyawradius = dist;
2498 dist += vec[2]*vec[2];
2499 if (modelradius < dist)
2503 modelyawradius = sqrt(modelyawradius);
2504 modelradius = sqrt(modelradius);
2505 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2506 mod->yawmins[2] = mod->normalmins[2];
2507 mod->yawmaxs[2] = mod->normalmaxs[2];
2508 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2509 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2510 // mod->modelradius = modelradius;
2512 // VectorCopy (bm->maxs, mod->maxs);
2513 // VectorCopy (bm->mins, mod->mins);
2515 // mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2517 mod->numleafs = bm->visleafs;
2519 mod->SERAddEntity = Mod_Brush_SERAddEntity;
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;
2541 // Mem_CheckSentinelsGlobal();