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);
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 qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
123 static qbyte 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 qbyte *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)
173 qbyte pix[16][16][4];
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 qbyte *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 *)((qbyte *)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 = (qbyte *)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 qbyte *in, *out, *data, d;
490 char litfilename[1024];
491 loadmodel->lightdata = NULL;
492 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
494 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
495 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
497 else // LordHavoc: bsp version 29 (normal white lighting)
499 // LordHavoc: hope is not lost yet, check for a .lit file to load
500 strcpy(litfilename, loadmodel->name);
501 COM_StripExtension(litfilename, litfilename);
502 strcat(litfilename, ".lit");
503 data = (qbyte*) COM_LoadFile (litfilename, false);
506 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
508 i = LittleLong(((int *)data)[1]);
511 Con_DPrintf("%s loaded", litfilename);
512 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
513 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
519 Con_Printf("Unknown .lit file version (%d)\n", i);
526 Con_Printf("Empty .lit file, ignoring\n");
528 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
532 // LordHavoc: oh well, expand the white lighting data
535 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
536 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
537 out = loadmodel->lightdata;
538 memcpy (in, mod_base + l->fileofs, l->filelen);
539 for (i = 0;i < l->filelen;i++)
555 static void Mod_LoadVisibility (lump_t *l)
559 loadmodel->visdata = NULL;
562 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
563 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
566 // used only for HalfLife maps
567 void Mod_ParseWadsFromEntityLump(char *data)
569 char key[128], value[4096];
574 data = COM_Parse(data);
577 if (com_token[0] != '{')
581 data = COM_Parse(data);
584 if (com_token[0] == '}')
585 break; // end of worldspawn
586 if (com_token[0] == '_')
587 strcpy(key, com_token + 1);
589 strcpy(key, com_token);
590 while (key[strlen(key)-1] == ' ') // remove trailing spaces
591 key[strlen(key)-1] = 0;
592 data = COM_Parse(data);
595 strcpy(value, com_token);
596 if (!strcmp("wad", key)) // for HalfLife maps
598 if (loadmodel->ishlbsp)
601 for (i = 0;i < 4096;i++)
602 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
608 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
609 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
611 else if (value[i] == ';' || value[i] == 0)
615 strcpy(wadname, "textures/");
616 strcat(wadname, &value[j]);
617 W_LoadTextureWadFile (wadname, false);
634 static void Mod_LoadEntities (lump_t *l)
638 loadmodel->entities = NULL;
641 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
642 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
643 if (loadmodel->ishlbsp)
644 Mod_ParseWadsFromEntityLump(loadmodel->entities);
653 static void Mod_LoadVertexes (lump_t *l)
659 in = (void *)(mod_base + l->fileofs);
660 if (l->filelen % sizeof(*in))
661 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
662 count = l->filelen / sizeof(*in);
663 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
665 loadmodel->vertexes = out;
666 loadmodel->numvertexes = count;
668 for ( i=0 ; i<count ; i++, in++, out++)
670 out->position[0] = LittleFloat (in->point[0]);
671 out->position[1] = LittleFloat (in->point[1]);
672 out->position[2] = LittleFloat (in->point[2]);
681 static void Mod_LoadSubmodels (lump_t *l)
687 in = (void *)(mod_base + l->fileofs);
688 if (l->filelen % sizeof(*in))
689 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
690 count = l->filelen / sizeof(*in);
691 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
693 loadmodel->submodels = out;
694 loadmodel->numsubmodels = count;
696 for ( i=0 ; i<count ; i++, in++, out++)
698 for (j=0 ; j<3 ; j++)
700 // spread the mins / maxs by a pixel
701 out->mins[j] = LittleFloat (in->mins[j]) - 1;
702 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
703 out->origin[j] = LittleFloat (in->origin[j]);
705 for (j=0 ; j<MAX_MAP_HULLS ; j++)
706 out->headnode[j] = LittleLong (in->headnode[j]);
707 out->visleafs = LittleLong (in->visleafs);
708 out->firstface = LittleLong (in->firstface);
709 out->numfaces = LittleLong (in->numfaces);
718 static void Mod_LoadEdges (lump_t *l)
724 in = (void *)(mod_base + l->fileofs);
725 if (l->filelen % sizeof(*in))
726 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
727 count = l->filelen / sizeof(*in);
728 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
730 loadmodel->edges = out;
731 loadmodel->numedges = count;
733 for ( i=0 ; i<count ; i++, in++, out++)
735 out->v[0] = (unsigned short)LittleShort(in->v[0]);
736 out->v[1] = (unsigned short)LittleShort(in->v[1]);
745 static void Mod_LoadTexinfo (lump_t *l)
752 in = (void *)(mod_base + l->fileofs);
753 if (l->filelen % sizeof(*in))
754 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
755 count = l->filelen / sizeof(*in);
756 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
758 loadmodel->texinfo = out;
759 loadmodel->numtexinfo = count;
761 for (i = 0;i < count;i++, in++, out++)
763 for (k = 0;k < 2;k++)
764 for (j = 0;j < 4;j++)
765 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
767 miptex = LittleLong (in->miptex);
768 out->flags = LittleLong (in->flags);
770 if (!loadmodel->textures)
771 out->texture = &loadmodel->notexture;
775 Host_Error ("miptex < 0");
776 if (miptex >= loadmodel->numtextures)
777 Host_Error ("miptex >= loadmodel->numtextures");
778 out->texture = loadmodel->textures[miptex];
781 out->texture = &loadmodel->notexture;
789 Fills in s->texturemins[] and s->extents[]
792 static void CalcSurfaceExtents (msurface_t *s)
794 float mins[2], maxs[2], val;
798 int bmins[2], bmaxs[2];
800 mins[0] = mins[1] = 999999999;
801 maxs[0] = maxs[1] = -999999999;
805 for (i=0 ; i<s->numedges ; i++)
807 e = loadmodel->surfedges[s->firstedge+i];
809 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
811 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
813 for (j=0 ; j<2 ; j++)
815 val = v->position[0] * tex->vecs[j][0] +
816 v->position[1] * tex->vecs[j][1] +
817 v->position[2] * tex->vecs[j][2] +
826 for (i=0 ; i<2 ; i++)
828 bmins[i] = floor(mins[i]/16);
829 bmaxs[i] = ceil(maxs[i]/16);
831 s->texturemins[i] = bmins[i] * 16;
832 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
837 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
842 mins[0] = mins[1] = mins[2] = 9999;
843 maxs[0] = maxs[1] = maxs[2] = -9999;
845 for (i = 0;i < numverts;i++)
847 for (j = 0;j < 3;j++, v++)
857 #define MAX_SUBDIVPOLYTRIANGLES 4096
858 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
860 static int subdivpolyverts, subdivpolytriangles;
861 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
862 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
864 static int subdivpolylookupvert(vec3_t v)
867 for (i = 0;i < subdivpolyverts;i++)
868 if (subdivpolyvert[i][0] == v[0]
869 && subdivpolyvert[i][1] == v[1]
870 && subdivpolyvert[i][2] == v[2])
872 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
873 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
874 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
875 return subdivpolyverts++;
878 static void SubdividePolygon (int numverts, float *verts)
880 int i, i1, i2, i3, f, b, c, p;
881 vec3_t mins, maxs, front[256], back[256];
882 float m, *pv, *cv, dist[256], frac;
885 Host_Error ("SubdividePolygon: ran out of verts in buffer");
887 BoundPoly (numverts, verts, mins, maxs);
889 for (i = 0;i < 3;i++)
891 m = (mins[i] + maxs[i]) * 0.5;
892 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
899 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
903 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
907 VectorCopy (pv, front[f]);
912 VectorCopy (pv, back[b]);
915 if (dist[p] == 0 || dist[c] == 0)
917 if ( (dist[p] > 0) != (dist[c] > 0) )
920 frac = dist[p] / (dist[p] - dist[c]);
921 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
922 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
923 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
929 SubdividePolygon (f, front[0]);
930 SubdividePolygon (b, back[0]);
934 i1 = subdivpolylookupvert(verts);
935 i2 = subdivpolylookupvert(verts + 3);
936 for (i = 2;i < numverts;i++)
938 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
940 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
944 i3 = subdivpolylookupvert(verts + i * 3);
945 subdivpolyindex[subdivpolytriangles][0] = i1;
946 subdivpolyindex[subdivpolytriangles][1] = i2;
947 subdivpolyindex[subdivpolytriangles][2] = i3;
949 subdivpolytriangles++;
957 Breaks a polygon up along axial 64 unit
958 boundaries so that turbulent and sky warps
959 can be done reasonably.
962 void Mod_GenerateWarpMesh (msurface_t *surf)
968 subdivpolytriangles = 0;
970 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
973 mesh->numverts = subdivpolyverts;
974 mesh->numtriangles = subdivpolytriangles;
975 if (mesh->numtriangles < 1)
976 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
977 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
978 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
979 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
981 for (i = 0;i < mesh->numtriangles;i++)
983 for (j = 0;j < 3;j++)
985 mesh->index[i*3+j] = subdivpolyindex[i][j];
986 //if (mesh->index[i] < 0 || mesh->index[i] >= mesh->numverts)
987 // Host_Error("Mod_GenerateWarpMesh: invalid index generated\n");
991 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
993 VectorCopy(subdivpolyvert[i], v->v);
994 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
995 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
999 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1001 int i, is, it, *index, smax, tmax;
1006 //surf->flags |= SURF_LIGHTMAP;
1007 smax = surf->extents[0] >> 4;
1008 tmax = surf->extents[1] >> 4;
1009 surf->lightmaptexturestride = 0;
1010 surf->lightmaptexture = NULL;
1013 mesh->numverts = surf->poly_numverts;
1014 mesh->numtriangles = surf->poly_numverts - 2;
1015 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1016 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1017 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1019 index = mesh->index;
1020 for (i = 0;i < mesh->numtriangles;i++)
1027 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1029 VectorCopy (in, out->v);
1031 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1032 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1034 out->st[0] = s / surf->texinfo->texture->width;
1035 out->st[1] = t / surf->texinfo->texture->height;
1037 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1038 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1040 // lightmap coordinates
1044 // LordHavoc: calc lightmap data offset for vertex lighting to use
1047 is = bound(0, is, smax);
1048 it = bound(0, it, tmax);
1049 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1053 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1055 int i, is, it, *index, smax, tmax;
1056 float *in, s, t, xbase, ybase, xscale, yscale;
1060 surf->flags |= SURF_LIGHTMAP;
1061 smax = surf->extents[0] >> 4;
1062 tmax = surf->extents[1] >> 4;
1063 if (r_miplightmaps.integer)
1065 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1066 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);
1070 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1071 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);
1073 // 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);
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_PRECACHE);
1075 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1076 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1077 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1080 mesh->numverts = surf->poly_numverts;
1081 mesh->numtriangles = surf->poly_numverts - 2;
1082 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1083 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1084 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1086 index = mesh->index;
1087 for (i = 0;i < mesh->numtriangles;i++)
1094 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1096 VectorCopy (in, out->v);
1098 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1099 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1101 out->st[0] = s / surf->texinfo->texture->width;
1102 out->st[1] = t / surf->texinfo->texture->height;
1104 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1105 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1107 // lightmap coordinates
1108 out->uv[0] = s * xscale + xbase;
1109 out->uv[1] = t * yscale + ybase;
1111 // LordHavoc: calc lightmap data offset for vertex lighting to use
1114 is = bound(0, is, smax);
1115 it = bound(0, it, tmax);
1116 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1120 void Mod_GenerateVertexMesh (msurface_t *surf)
1127 surf->lightmaptexturestride = 0;
1128 surf->lightmaptexture = NULL;
1131 mesh->numverts = surf->poly_numverts;
1132 mesh->numtriangles = surf->poly_numverts - 2;
1133 mesh->index = Mem_Alloc(loadmodel->mempool, mesh->numtriangles * sizeof(int[3]) + mesh->numverts * sizeof(surfvertex_t));
1134 mesh->vertex = (surfvertex_t *)((long) mesh->index + mesh->numtriangles * sizeof(int[3]));
1135 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1137 index = mesh->index;
1138 for (i = 0;i < mesh->numtriangles;i++)
1145 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1147 VectorCopy (in, out->v);
1148 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1149 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1153 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1160 // convert edges back to a normal polygon
1161 surf->poly_numverts = surf->numedges;
1162 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1163 for (i = 0;i < surf->numedges;i++)
1165 lindex = loadmodel->surfedges[surf->firstedge + i];
1167 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1169 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1170 VectorCopy (vec, vert);
1180 static void Mod_LoadFaces (lump_t *l)
1184 int i, count, surfnum, planenum, side, ssize, tsize;
1186 in = (void *)(mod_base + l->fileofs);
1187 if (l->filelen % sizeof(*in))
1188 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1189 count = l->filelen / sizeof(*in);
1190 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1192 loadmodel->surfaces = out;
1193 loadmodel->numsurfaces = count;
1195 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1197 // FIXME: validate edges, texinfo, etc?
1198 out->firstedge = LittleLong(in->firstedge);
1199 out->numedges = LittleShort(in->numedges);
1201 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1202 out->flags = out->texinfo->texture->flags;
1204 planenum = LittleShort(in->planenum);
1205 side = LittleShort(in->side);
1207 out->flags |= SURF_PLANEBACK;
1209 out->plane = loadmodel->planes + planenum;
1211 // clear lightmap (filled in later)
1212 out->lightmaptexture = NULL;
1214 // force lightmap upload on first time seeing the surface
1215 out->cached_dlight = true;
1216 out->cached_ambient = -1000;
1217 out->cached_lightscalebit = -1000;
1219 CalcSurfaceExtents (out);
1221 ssize = (out->extents[0] >> 4) + 1;
1222 tsize = (out->extents[1] >> 4) + 1;
1225 for (i = 0;i < MAXLIGHTMAPS;i++)
1226 out->styles[i] = in->styles[i];
1227 i = LittleLong(in->lightofs);
1229 out->samples = NULL;
1230 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1231 out->samples = loadmodel->lightdata + i;
1232 else // LordHavoc: white lighting (bsp version 29)
1233 out->samples = loadmodel->lightdata + (i * 3);
1235 Mod_GenerateSurfacePolygon(out);
1237 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1239 out->shader = &Cshader_sky;
1240 out->samples = NULL;
1241 Mod_GenerateWarpMesh (out);
1245 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1247 if (out->texinfo->texture->glowtexture)
1248 out->shader = &Cshader_water_glow;
1250 out->shader = &Cshader_water;
1252 for (i=0 ; i<2 ; i++)
1254 out->extents[i] = 16384*1024;
1255 out->texturemins[i] = -8192*1024;
1258 out->samples = NULL;
1259 Mod_GenerateWarpMesh (out);
1263 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1264 out->flags |= SURF_CLIPSOLID;
1265 if (out->texinfo->flags & TEX_SPECIAL)
1267 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1268 if (out->texinfo->texture->glowtexture)
1269 out->shader = &Cshader_water_glow;
1271 out->shader = &Cshader_water;
1272 out->shader = &Cshader_water;
1273 out->samples = NULL;
1274 Mod_GenerateWarpMesh (out);
1276 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1278 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1279 if (out->texinfo->texture->glowtexture)
1280 out->shader = &Cshader_wall_fullbright_glow;
1282 out->shader = &Cshader_wall_fullbright;
1283 out->samples = NULL;
1284 Mod_GenerateVertexMesh(out);
1288 // stainmap for permanent marks on walls
1289 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1291 memset(out->stainsamples, 255, ssize * tsize * 3);
1292 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1294 if (out->texinfo->texture->glowtexture)
1295 out->shader = &Cshader_wall_vertex_glow;
1297 out->shader = &Cshader_wall_vertex;
1298 Mod_GenerateVertexLitMesh(out);
1302 if (out->texinfo->texture->glowtexture)
1303 out->shader = &Cshader_wall_lightmap_glow;
1305 out->shader = &Cshader_wall_lightmap;
1306 Mod_GenerateLightmappedMesh(out);
1312 static model_t *sortmodel;
1314 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1316 const msurface_t *a, *b;
1317 a = *((const msurface_t **)voida);
1318 b = *((const msurface_t **)voidb);
1319 if (a->shader != b->shader)
1320 return (long) a->shader - (long) b->shader;
1321 if (a->texinfo->texture != b->texinfo->texture);
1322 return a->texinfo->texture - b->texinfo->texture;
1326 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1330 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1331 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1332 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1334 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1343 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1345 node->parent = parent;
1346 if (node->contents < 0)
1348 Mod_SetParent (node->children[0], node);
1349 Mod_SetParent (node->children[1], node);
1357 static void Mod_LoadNodes (lump_t *l)
1363 in = (void *)(mod_base + l->fileofs);
1364 if (l->filelen % sizeof(*in))
1365 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1366 count = l->filelen / sizeof(*in);
1367 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1369 loadmodel->nodes = out;
1370 loadmodel->numnodes = count;
1372 for ( i=0 ; i<count ; i++, in++, out++)
1374 for (j=0 ; j<3 ; j++)
1376 out->mins[j] = LittleShort (in->mins[j]);
1377 out->maxs[j] = LittleShort (in->maxs[j]);
1380 p = LittleLong(in->planenum);
1381 out->plane = loadmodel->planes + p;
1383 out->firstsurface = LittleShort (in->firstface);
1384 out->numsurfaces = LittleShort (in->numfaces);
1386 for (j=0 ; j<2 ; j++)
1388 p = LittleShort (in->children[j]);
1390 out->children[j] = loadmodel->nodes + p;
1392 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1396 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1404 static void Mod_LoadLeafs (lump_t *l)
1410 in = (void *)(mod_base + l->fileofs);
1411 if (l->filelen % sizeof(*in))
1412 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1413 count = l->filelen / sizeof(*in);
1414 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1416 loadmodel->leafs = out;
1417 loadmodel->numleafs = count;
1419 for ( i=0 ; i<count ; i++, in++, out++)
1421 for (j=0 ; j<3 ; j++)
1423 out->mins[j] = LittleShort (in->mins[j]);
1424 out->maxs[j] = LittleShort (in->maxs[j]);
1427 p = LittleLong(in->contents);
1430 out->firstmarksurface = loadmodel->marksurfaces +
1431 LittleShort(in->firstmarksurface);
1432 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1434 p = LittleLong(in->visofs);
1436 out->compressed_vis = NULL;
1438 out->compressed_vis = loadmodel->visdata + p;
1440 for (j=0 ; j<4 ; j++)
1441 out->ambient_sound_level[j] = in->ambient_level[j];
1443 // gl underwater warp
1444 // LordHavoc: disabled underwater warping
1446 if (out->contents != CONTENTS_EMPTY)
1448 for (j=0 ; j<out->nummarksurfaces ; j++)
1449 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1460 static void Mod_LoadClipnodes (lump_t *l)
1462 dclipnode_t *in, *out;
1466 in = (void *)(mod_base + l->fileofs);
1467 if (l->filelen % sizeof(*in))
1468 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1469 count = l->filelen / sizeof(*in);
1470 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1472 loadmodel->clipnodes = out;
1473 loadmodel->numclipnodes = count;
1475 if (loadmodel->ishlbsp)
1477 hull = &loadmodel->hulls[1];
1478 hull->clipnodes = out;
1479 hull->firstclipnode = 0;
1480 hull->lastclipnode = count-1;
1481 hull->planes = loadmodel->planes;
1482 hull->clip_mins[0] = -16;
1483 hull->clip_mins[1] = -16;
1484 hull->clip_mins[2] = -36;
1485 hull->clip_maxs[0] = 16;
1486 hull->clip_maxs[1] = 16;
1487 hull->clip_maxs[2] = 36;
1489 hull = &loadmodel->hulls[2];
1490 hull->clipnodes = out;
1491 hull->firstclipnode = 0;
1492 hull->lastclipnode = count-1;
1493 hull->planes = loadmodel->planes;
1494 hull->clip_mins[0] = -32;
1495 hull->clip_mins[1] = -32;
1496 hull->clip_mins[2] = -32;
1497 hull->clip_maxs[0] = 32;
1498 hull->clip_maxs[1] = 32;
1499 hull->clip_maxs[2] = 32;
1501 hull = &loadmodel->hulls[3];
1502 hull->clipnodes = out;
1503 hull->firstclipnode = 0;
1504 hull->lastclipnode = count-1;
1505 hull->planes = loadmodel->planes;
1506 hull->clip_mins[0] = -16;
1507 hull->clip_mins[1] = -16;
1508 hull->clip_mins[2] = -18;
1509 hull->clip_maxs[0] = 16;
1510 hull->clip_maxs[1] = 16;
1511 hull->clip_maxs[2] = 18;
1515 hull = &loadmodel->hulls[1];
1516 hull->clipnodes = out;
1517 hull->firstclipnode = 0;
1518 hull->lastclipnode = count-1;
1519 hull->planes = loadmodel->planes;
1520 hull->clip_mins[0] = -16;
1521 hull->clip_mins[1] = -16;
1522 hull->clip_mins[2] = -24;
1523 hull->clip_maxs[0] = 16;
1524 hull->clip_maxs[1] = 16;
1525 hull->clip_maxs[2] = 32;
1527 hull = &loadmodel->hulls[2];
1528 hull->clipnodes = out;
1529 hull->firstclipnode = 0;
1530 hull->lastclipnode = count-1;
1531 hull->planes = loadmodel->planes;
1532 hull->clip_mins[0] = -32;
1533 hull->clip_mins[1] = -32;
1534 hull->clip_mins[2] = -24;
1535 hull->clip_maxs[0] = 32;
1536 hull->clip_maxs[1] = 32;
1537 hull->clip_maxs[2] = 64;
1540 for (i=0 ; i<count ; i++, out++, in++)
1542 out->planenum = LittleLong(in->planenum);
1543 out->children[0] = LittleShort(in->children[0]);
1544 out->children[1] = LittleShort(in->children[1]);
1545 if (out->children[0] >= count || out->children[1] >= count)
1546 Host_Error("Corrupt clipping hull (out of range child)\n");
1554 Duplicate the drawing hull structure as a clipping hull
1557 static void Mod_MakeHull0 (void)
1564 hull = &loadmodel->hulls[0];
1566 in = loadmodel->nodes;
1567 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1569 hull->clipnodes = out;
1570 hull->firstclipnode = 0;
1571 hull->lastclipnode = loadmodel->numnodes - 1;
1572 hull->planes = loadmodel->planes;
1574 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1576 out->planenum = in->plane - loadmodel->planes;
1577 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1578 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1584 Mod_LoadMarksurfaces
1587 static void Mod_LoadMarksurfaces (lump_t *l)
1592 in = (void *)(mod_base + l->fileofs);
1593 if (l->filelen % sizeof(*in))
1594 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1595 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1596 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1598 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1600 j = (unsigned) LittleShort(in[i]);
1601 if (j >= loadmodel->numsurfaces)
1602 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1603 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1612 static void Mod_LoadSurfedges (lump_t *l)
1617 in = (void *)(mod_base + l->fileofs);
1618 if (l->filelen % sizeof(*in))
1619 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1620 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1621 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1623 for (i = 0;i < loadmodel->numsurfedges;i++)
1624 loadmodel->surfedges[i] = LittleLong (in[i]);
1633 static void Mod_LoadPlanes (lump_t *l)
1639 in = (void *)(mod_base + l->fileofs);
1640 if (l->filelen % sizeof(*in))
1641 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1643 loadmodel->numplanes = l->filelen / sizeof(*in);
1644 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1646 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1648 out->normal[0] = LittleFloat (in->normal[0]);
1649 out->normal[1] = LittleFloat (in->normal[1]);
1650 out->normal[2] = LittleFloat (in->normal[2]);
1651 out->dist = LittleFloat (in->dist);
1653 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1654 // out->type = LittleLong (in->type);
1659 #define MAX_POINTS_ON_WINDING 64
1665 double points[8][3]; // variable sized
1674 static winding_t *NewWinding (int points)
1679 if (points > MAX_POINTS_ON_WINDING)
1680 Sys_Error("NewWinding: too many points\n");
1682 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1683 w = Mem_Alloc(loadmodel->mempool, size);
1684 memset (w, 0, size);
1689 static void FreeWinding (winding_t *w)
1699 static winding_t *BaseWindingForPlane (mplane_t *p)
1701 double org[3], vright[3], vup[3], normal[3];
1704 VectorCopy(p->normal, normal);
1705 VectorVectorsDouble(normal, vright, vup);
1707 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1708 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1710 // project a really big axis aligned box onto the plane
1713 VectorScale (p->normal, p->dist, org);
1715 VectorSubtract (org, vright, w->points[0]);
1716 VectorAdd (w->points[0], vup, w->points[0]);
1718 VectorAdd (org, vright, w->points[1]);
1719 VectorAdd (w->points[1], vup, w->points[1]);
1721 VectorAdd (org, vright, w->points[2]);
1722 VectorSubtract (w->points[2], vup, w->points[2]);
1724 VectorSubtract (org, vright, w->points[3]);
1725 VectorSubtract (w->points[3], vup, w->points[3]);
1736 Clips the winding to the plane, returning the new winding on the positive side
1737 Frees the input winding.
1738 If keepon is true, an exactly on-plane winding will be saved, otherwise
1739 it will be clipped away.
1742 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1744 double dists[MAX_POINTS_ON_WINDING + 1];
1745 int sides[MAX_POINTS_ON_WINDING + 1];
1754 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1756 // determine sides for each point
1757 for (i = 0;i < in->numpoints;i++)
1759 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1760 if (dot > ON_EPSILON)
1761 sides[i] = SIDE_FRONT;
1762 else if (dot < -ON_EPSILON)
1763 sides[i] = SIDE_BACK;
1768 sides[i] = sides[0];
1769 dists[i] = dists[0];
1771 if (keepon && !counts[0] && !counts[1])
1782 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1783 if (maxpts > MAX_POINTS_ON_WINDING)
1784 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1786 neww = NewWinding (maxpts);
1788 for (i = 0;i < in->numpoints;i++)
1790 if (neww->numpoints >= maxpts)
1791 Sys_Error ("ClipWinding: points exceeded estimate");
1795 if (sides[i] == SIDE_ON)
1797 VectorCopy (p1, neww->points[neww->numpoints]);
1802 if (sides[i] == SIDE_FRONT)
1804 VectorCopy (p1, neww->points[neww->numpoints]);
1808 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1811 // generate a split point
1812 p2 = in->points[(i+1)%in->numpoints];
1814 dot = dists[i] / (dists[i]-dists[i+1]);
1815 for (j = 0;j < 3;j++)
1816 { // avoid round off error when possible
1817 if (split->normal[j] == 1)
1818 mid[j] = split->dist;
1819 else if (split->normal[j] == -1)
1820 mid[j] = -split->dist;
1822 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1825 VectorCopy (mid, neww->points[neww->numpoints]);
1829 // free the original winding
1833 //Mem_CheckSentinels(neww);
1843 Divides a winding by a plane, producing one or two windings. The
1844 original winding is not damaged or freed. If only on one side, the
1845 returned winding will be the input winding. If on both sides, two
1846 new windings will be created.
1849 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1851 double dists[MAX_POINTS_ON_WINDING + 1];
1852 int sides[MAX_POINTS_ON_WINDING + 1];
1861 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1863 // determine sides for each point
1864 for (i = 0;i < in->numpoints;i++)
1866 dot = DotProduct (in->points[i], split->normal);
1869 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1870 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1871 else sides[i] = SIDE_ON;
1874 sides[i] = sides[0];
1875 dists[i] = dists[0];
1877 *front = *back = NULL;
1890 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1892 if (maxpts > MAX_POINTS_ON_WINDING)
1893 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1895 *front = f = NewWinding (maxpts);
1896 *back = b = NewWinding (maxpts);
1898 for (i = 0;i < in->numpoints;i++)
1900 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1901 Sys_Error ("DivideWinding: points exceeded estimate");
1905 if (sides[i] == SIDE_ON)
1907 VectorCopy (p1, f->points[f->numpoints]);
1909 VectorCopy (p1, b->points[b->numpoints]);
1914 if (sides[i] == SIDE_FRONT)
1916 VectorCopy (p1, f->points[f->numpoints]);
1919 else if (sides[i] == SIDE_BACK)
1921 VectorCopy (p1, b->points[b->numpoints]);
1925 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1928 // generate a split point
1929 p2 = in->points[(i+1)%in->numpoints];
1931 dot = dists[i] / (dists[i]-dists[i+1]);
1932 for (j = 0;j < 3;j++)
1933 { // avoid round off error when possible
1934 if (split->normal[j] == 1)
1935 mid[j] = split->dist;
1936 else if (split->normal[j] == -1)
1937 mid[j] = -split->dist;
1939 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1942 VectorCopy (mid, f->points[f->numpoints]);
1944 VectorCopy (mid, b->points[b->numpoints]);
1949 //Mem_CheckSentinels(f);
1950 //Mem_CheckSentinels(b);
1953 typedef struct portal_s
1956 mnode_t *nodes[2]; // [0] = front side of plane
1957 struct portal_s *next[2];
1959 struct portal_s *chain; // all portals are linked into a list
1963 static portal_t *portalchain;
1970 static portal_t *AllocPortal (void)
1973 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1974 //memset(p, 0, sizeof(portal_t));
1975 p->chain = portalchain;
1980 static void FreePortal(portal_t *p)
1985 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1987 // calculate children first
1988 if (node->children[0]->contents >= 0)
1989 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1990 if (node->children[1]->contents >= 0)
1991 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1993 // make combined bounding box from children
1994 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1995 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1996 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1997 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1998 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1999 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2002 static void Mod_FinalizePortals(void)
2004 int i, j, numportals, numpoints;
2005 portal_t *p, *pnext;
2008 mleaf_t *leaf, *endleaf;
2011 //Mem_CheckSentinelsGlobal();
2013 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2014 leaf = loadmodel->leafs;
2015 endleaf = leaf + loadmodel->numleafs;
2016 for (;leaf < endleaf;leaf++)
2018 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2019 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2026 for (i = 0;i < 2;i++)
2028 leaf = (mleaf_t *)p->nodes[i];
2030 for (j = 0;j < w->numpoints;j++)
2032 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2033 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2034 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2035 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2036 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2037 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2044 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2046 //Mem_CheckSentinelsGlobal();
2048 // tally up portal and point counts
2054 // note: this check must match the one below or it will usually corrupt memory
2055 // 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
2056 if (p->winding && p->nodes[0] != p->nodes[1]
2057 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2058 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2061 numpoints += p->winding->numpoints * 2;
2065 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2066 loadmodel->numportals = numportals;
2067 loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2068 loadmodel->numportalpoints = numpoints;
2069 // clear all leaf portal chains
2070 for (i = 0;i < loadmodel->numleafs;i++)
2071 loadmodel->leafs[i].portals = NULL;
2072 // process all portals in the global portal chain, while freeing them
2073 portal = loadmodel->portals;
2074 point = loadmodel->portalpoints;
2083 // note: this check must match the one above or it will usually corrupt memory
2084 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2085 if (p->nodes[0] != p->nodes[1]
2086 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2087 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2089 // first make the back to front portal (forward portal)
2090 portal->points = point;
2091 portal->numpoints = p->winding->numpoints;
2092 portal->plane.dist = p->plane.dist;
2093 VectorCopy(p->plane.normal, portal->plane.normal);
2094 portal->here = (mleaf_t *)p->nodes[1];
2095 portal->past = (mleaf_t *)p->nodes[0];
2097 for (j = 0;j < portal->numpoints;j++)
2099 VectorCopy(p->winding->points[j], point->position);
2102 PlaneClassify(&portal->plane);
2104 // link into leaf's portal chain
2105 portal->next = portal->here->portals;
2106 portal->here->portals = portal;
2108 // advance to next portal
2111 // then make the front to back portal (backward portal)
2112 portal->points = point;
2113 portal->numpoints = p->winding->numpoints;
2114 portal->plane.dist = -p->plane.dist;
2115 VectorNegate(p->plane.normal, portal->plane.normal);
2116 portal->here = (mleaf_t *)p->nodes[0];
2117 portal->past = (mleaf_t *)p->nodes[1];
2119 for (j = portal->numpoints - 1;j >= 0;j--)
2121 VectorCopy(p->winding->points[j], point->position);
2124 PlaneClassify(&portal->plane);
2126 // link into leaf's portal chain
2127 portal->next = portal->here->portals;
2128 portal->here->portals = portal;
2130 // advance to next portal
2133 FreeWinding(p->winding);
2139 //Mem_CheckSentinelsGlobal();
2147 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2150 Host_Error ("AddPortalToNodes: NULL front node");
2152 Host_Error ("AddPortalToNodes: NULL back node");
2153 if (p->nodes[0] || p->nodes[1])
2154 Host_Error ("AddPortalToNodes: already included");
2155 // 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
2157 p->nodes[0] = front;
2158 p->next[0] = (portal_t *)front->portals;
2159 front->portals = (mportal_t *)p;
2162 p->next[1] = (portal_t *)back->portals;
2163 back->portals = (mportal_t *)p;
2168 RemovePortalFromNode
2171 static void RemovePortalFromNodes(portal_t *portal)
2175 void **portalpointer;
2177 for (i = 0;i < 2;i++)
2179 node = portal->nodes[i];
2181 portalpointer = (void **) &node->portals;
2186 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2190 if (portal->nodes[0] == node)
2192 *portalpointer = portal->next[0];
2193 portal->nodes[0] = NULL;
2195 else if (portal->nodes[1] == node)
2197 *portalpointer = portal->next[1];
2198 portal->nodes[1] = NULL;
2201 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2205 if (t->nodes[0] == node)
2206 portalpointer = (void **) &t->next[0];
2207 else if (t->nodes[1] == node)
2208 portalpointer = (void **) &t->next[1];
2210 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2215 static void Mod_RecursiveNodePortals (mnode_t *node)
2218 mnode_t *front, *back, *other_node;
2219 mplane_t clipplane, *plane;
2220 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2221 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2223 // CheckLeafPortalConsistancy (node);
2225 // if a leaf, we're done
2229 plane = node->plane;
2231 front = node->children[0];
2232 back = node->children[1];
2234 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2236 // create the new portal by generating a polygon for the node plane,
2237 // and clipping it by all of the other portals (which came from nodes above this one)
2238 nodeportal = AllocPortal ();
2239 nodeportal->plane = *node->plane;
2241 nodeportalwinding = BaseWindingForPlane (node->plane);
2242 //Mem_CheckSentinels(nodeportalwinding);
2243 side = 0; // shut up compiler warning
2244 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2246 clipplane = portal->plane;
2247 if (portal->nodes[0] == portal->nodes[1])
2248 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2249 if (portal->nodes[0] == node)
2251 else if (portal->nodes[1] == node)
2253 clipplane.dist = -clipplane.dist;
2254 VectorNegate (clipplane.normal, clipplane.normal);
2258 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2260 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2261 if (!nodeportalwinding)
2263 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2268 if (nodeportalwinding)
2270 // if the plane was not clipped on all sides, there was an error
2271 nodeportal->winding = nodeportalwinding;
2272 AddPortalToNodes (nodeportal, front, back);
2275 // split the portals of this node along this node's plane and assign them to the children of this node
2276 // (migrating the portals downward through the tree)
2277 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2279 if (portal->nodes[0] == portal->nodes[1])
2280 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2281 if (portal->nodes[0] == node)
2283 else if (portal->nodes[1] == node)
2286 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2287 nextportal = portal->next[side];
2289 other_node = portal->nodes[!side];
2290 RemovePortalFromNodes (portal);
2292 // cut the portal into two portals, one on each side of the node plane
2293 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2298 AddPortalToNodes (portal, back, other_node);
2300 AddPortalToNodes (portal, other_node, back);
2306 AddPortalToNodes (portal, front, other_node);
2308 AddPortalToNodes (portal, other_node, front);
2312 // the winding is split
2313 splitportal = AllocPortal ();
2314 temp = splitportal->chain;
2315 *splitportal = *portal;
2316 splitportal->chain = temp;
2317 splitportal->winding = backwinding;
2318 FreeWinding (portal->winding);
2319 portal->winding = frontwinding;
2323 AddPortalToNodes (portal, front, other_node);
2324 AddPortalToNodes (splitportal, back, other_node);
2328 AddPortalToNodes (portal, other_node, front);
2329 AddPortalToNodes (splitportal, other_node, back);
2333 Mod_RecursiveNodePortals(front);
2334 Mod_RecursiveNodePortals(back);
2338 void Mod_MakeOutsidePortals(mnode_t *node)
2341 portal_t *p, *portals[6];
2342 mnode_t *outside_node;
2344 outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2345 outside_node->contents = CONTENTS_SOLID;
2346 outside_node->portals = NULL;
2348 for (i = 0;i < 3;i++)
2350 for (j = 0;j < 2;j++)
2352 portals[j*3 + i] = p = AllocPortal ();
2353 memset (&p->plane, 0, sizeof(mplane_t));
2354 p->plane.normal[i] = j ? -1 : 1;
2355 p->plane.dist = -65536;
2356 p->winding = BaseWindingForPlane (&p->plane);
2358 AddPortalToNodes (p, outside_node, node);
2360 AddPortalToNodes (p, node, outside_node);
2364 // clip the basewindings by all the other planes
2365 for (i = 0;i < 6;i++)
2367 for (j = 0;j < 6;j++)
2371 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2377 static void Mod_MakePortals(void)
2379 // Con_Printf("building portals for %s\n", loadmodel->name);
2382 // Mod_MakeOutsidePortals (loadmodel->nodes);
2383 Mod_RecursiveNodePortals (loadmodel->nodes);
2384 Mod_FinalizePortals();
2392 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2397 mempool_t *mainmempool;
2400 mod->type = mod_brush;
2402 header = (dheader_t *)buffer;
2404 i = LittleLong (header->version);
2405 if (i != BSPVERSION && i != 30)
2406 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2407 mod->ishlbsp = i == 30;
2408 if (loadmodel->isworldmodel)
2409 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2411 // swap all the lumps
2412 mod_base = (qbyte *)header;
2414 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2415 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2419 // store which lightmap format to use
2420 mod->lightmaprgba = r_lightmaprgba.integer;
2422 // Mem_CheckSentinelsGlobal();
2423 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
2424 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2425 // Mem_CheckSentinelsGlobal();
2426 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2427 // Mem_CheckSentinelsGlobal();
2428 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2429 // Mem_CheckSentinelsGlobal();
2430 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2431 // Mem_CheckSentinelsGlobal();
2432 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2433 // Mem_CheckSentinelsGlobal();
2434 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2435 // Mem_CheckSentinelsGlobal();
2436 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2437 // Mem_CheckSentinelsGlobal();
2438 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2439 // Mem_CheckSentinelsGlobal();
2440 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2441 // Mem_CheckSentinelsGlobal();
2442 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2443 // Mem_CheckSentinelsGlobal();
2444 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2445 // Mem_CheckSentinelsGlobal();
2446 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2447 // Mem_CheckSentinelsGlobal();
2448 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2449 // Mem_CheckSentinelsGlobal();
2450 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2451 // Mem_CheckSentinelsGlobal();
2452 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2453 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2454 // Mem_CheckSentinelsGlobal();
2457 // Mem_CheckSentinelsGlobal();
2459 // Mem_CheckSentinelsGlobal();
2461 mod->numframes = 2; // regular and alternate animation
2463 mainmempool = mod->mempool;
2464 loadname = mod->name;
2467 // set up the submodels (FIXME: this is confusing)
2469 for (i = 0;i < mod->numsubmodels;i++)
2472 float dist, modelyawradius, modelradius, *vec;
2475 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2476 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2480 bm = &mod->submodels[i];
2482 mod->hulls[0].firstclipnode = bm->headnode[0];
2483 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2485 mod->hulls[j].firstclipnode = bm->headnode[j];
2486 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2489 mod->firstmodelsurface = bm->firstface;
2490 mod->nummodelsurfaces = bm->numfaces;
2492 mod->DrawSky = NULL;
2493 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2494 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2496 // we only need to have a drawsky function if it is used (usually only on world model)
2497 if (surf->shader == &Cshader_sky)
2498 mod->DrawSky = R_DrawBrushModelSky;
2499 for (k = 0;k < surf->numedges;k++)
2501 l = mod->surfedges[k + surf->firstedge];
2503 vec = mod->vertexes[mod->edges[l].v[0]].position;
2505 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2506 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2507 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2508 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2509 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2510 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2511 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2512 dist = vec[0]*vec[0]+vec[1]*vec[1];
2513 if (modelyawradius < dist)
2514 modelyawradius = dist;
2515 dist += vec[2]*vec[2];
2516 if (modelradius < dist)
2520 modelyawradius = sqrt(modelyawradius);
2521 modelradius = sqrt(modelradius);
2522 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2523 mod->yawmins[2] = mod->normalmins[2];
2524 mod->yawmaxs[2] = mod->normalmaxs[2];
2525 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2526 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2527 // mod->modelradius = modelradius;
2528 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2529 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2531 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2532 VectorClear(mod->normalmins);
2533 VectorClear(mod->normalmaxs);
2534 VectorClear(mod->yawmins);
2535 VectorClear(mod->yawmaxs);
2536 VectorClear(mod->rotatedmins);
2537 VectorClear(mod->rotatedmaxs);
2538 //mod->modelradius = 0;
2541 // VectorCopy (bm->maxs, mod->maxs);
2542 // VectorCopy (bm->mins, mod->mins);
2544 // mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2546 mod->numleafs = bm->visleafs;
2548 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2549 mod->Draw = R_DrawBrushModelNormal;
2550 mod->DrawShadow = NULL;
2552 Mod_BrushSortedSurfaces(mod, mainmempool);
2554 // LordHavoc: only register submodels if it is the world
2555 // (prevents bsp models from replacing world submodels)
2556 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2559 // duplicate the basic information
2560 sprintf (name, "*%i", i+1);
2561 loadmodel = Mod_FindName (name);
2563 strcpy (loadmodel->name, name);
2564 // textures and memory belong to the main model
2565 loadmodel->texturepool = NULL;
2566 loadmodel->mempool = NULL;
2570 // Mem_CheckSentinelsGlobal();