]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
yet more memory testing
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
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.
8
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.
12
13 See the GNU General Public License for more details.
14
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.
18
19 */
20
21 #include "quakedef.h"
22
23 byte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
24
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", "48"};
31
32 /*
33 ===============
34 Mod_BrushInit
35 ===============
36 */
37 void Mod_BrushInit (void)
38 {
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));
46 }
47
48 void Mod_Brush_SERAddEntity(void)
49 {
50         R_Clip_AddBox(currentrenderentity->mins, currentrenderentity->maxs, R_Entity_Callback, currentrenderentity, NULL);
51 }
52
53 /*
54 ===============
55 Mod_PointInLeaf
56 ===============
57 */
58 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
59 {
60         mnode_t         *node;
61
62         Mod_CheckLoaded(model);
63 //      if (!model || !model->nodes)
64 //              Sys_Error ("Mod_PointInLeaf: bad model");
65
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];
71
72         return (mleaf_t *)node;
73 }
74
75 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
76 {
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;
80         pos[0]-=1;
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;
83         pos[1]-=1;
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;
86         pos[2]-=1;
87 }
88
89 /*
90 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
91 {
92         mnode_t         *node;
93         float           d;
94         mplane_t        *plane;
95
96         if (!model || !model->nodes)
97                 Sys_Error ("Mod_PointInLeaf: bad model");
98
99         node = model->nodes;
100         while (1)
101         {
102                 if (node->contents < 0)
103                         return (mleaf_t *)node;
104                 plane = node->plane;
105                 d = DotProduct (p,plane->normal) - plane->dist;
106                 if (d > 0)
107                         node = node->children[0];
108                 else
109                         node = node->children[1];
110         }
111
112         return NULL;    // never reached
113 }
114 */
115
116 /*
117 ===================
118 Mod_DecompressVis
119 ===================
120 */
121 static byte *Mod_DecompressVis (byte *in, model_t *model)
122 {
123         static byte     decompressed[MAX_MAP_LEAFS/8];
124         int             c;
125         byte    *out;
126         int             row;
127
128         row = (model->numleafs+7)>>3;
129         out = decompressed;
130
131         /*
132         if (!in)
133         {       // no vis info, so make all visible
134                 while (row)
135                 {
136                         *out++ = 0xff;
137                         row--;
138                 }
139                 return decompressed;
140         }
141         */
142
143         do
144         {
145                 if (*in)
146                 {
147                         *out++ = *in++;
148                         continue;
149                 }
150
151                 c = in[1];
152                 in += 2;
153                 while (c)
154                 {
155                         *out++ = 0;
156                         c--;
157                 }
158         } while (out - decompressed < row);
159
160         return decompressed;
161 }
162
163 byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
164 {
165         if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
166                 return mod_novis;
167         return Mod_DecompressVis (leaf->compressed_vis, model);
168 }
169
170 void Mod_SetupNoTexture(void)
171 {
172         int             x, y;
173         byte    pix[16][16][4];
174
175         for (y = 0;y < 16;y++)
176         {
177                 for (x = 0;x < 16;x++)
178                 {
179                         if ((y < 8) ^ (x < 8))
180                         {
181                                 pix[y][x][0] = 128;
182                                 pix[y][x][1] = 128;
183                                 pix[y][x][2] = 128;
184                                 pix[y][x][3] = 255;
185                         }
186                         else
187                         {
188                                 pix[y][x][0] = 64;
189                                 pix[y][x][1] = 64;
190                                 pix[y][x][2] = 64;
191                                 pix[y][x][3] = 255;
192                         }
193                 }
194         }
195
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);
202 }
203
204 /*
205 =================
206 Mod_LoadTextures
207 =================
208 */
209 static void Mod_LoadTextures (lump_t *l)
210 {
211         int                             i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
212         miptex_t                *dmiptex;
213         texture_t               *tx, *tx2, *anims[10], *altanims[10];
214         dmiptexlump_t   *m;
215         byte                    *data, *mtdata, *data2;
216         char                    name[256];
217
218         Mod_SetupNoTexture();
219
220         if (!l->filelen)
221         {
222                 loadmodel->textures = NULL;
223                 return;
224         }
225
226         m = (dmiptexlump_t *)(mod_base + l->fileofs);
227
228         m->nummiptex = LittleLong (m->nummiptex);
229
230         loadmodel->numtextures = m->nummiptex;
231         loadmodel->textures = Mem_Alloc(loadmodel->mempool, m->nummiptex * sizeof(*loadmodel->textures));
232
233         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
234         dofs = m->dataofs;
235         for (i = 0;i < m->nummiptex;i++)
236         {
237                 dofs[i] = LittleLong(dofs[i]);
238                 if (dofs[i] == -1)
239                         continue;
240                 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
241                 mtwidth = LittleLong (dmiptex->width);
242                 mtheight = LittleLong (dmiptex->height);
243                 mtdata = NULL;
244                 j = LittleLong (dmiptex->offsets[0]);
245                 if (j)
246                 {
247                         // texture included
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;
251                 }
252
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));
258                 tx->anim_total = 0;
259                 tx->alternate_anims = NULL;
260                 loadmodel->textures[i] = tx;
261
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++)
264                 {
265                         if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
266                                 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
267                         else
268                                 tx->name[j] = dmiptex->name[j];
269                 }
270                 for (;j < 16;j++)
271                         tx->name[j] = 0;
272
273                 if (!tx->name[0])
274                 {
275                         Con_Printf("warning: unnamed texture in %s\n", loadmodel->name);
276                         sprintf(tx->name, "unnamed%i", i);
277                 }
278
279                 tx->width = mtwidth;
280                 tx->height = mtheight;
281                 tx->texture = NULL;
282                 tx->glowtexture = NULL;
283                 tx->fogtexture = NULL;
284
285                 if (!loadmodel->ishlbsp && !strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128) // LordHavoc: HL sky textures are entirely unrelated
286                 {
287                         data = loadimagepixels(tx->name, false, 0, 0);
288                         if (data)
289                         {
290                                 if (image_width == 256 && image_height == 128)
291                                 {
292                                         if (loadmodel->isworldmodel)
293                                                 R_InitSky (data, 4);
294                                         Mem_Free(data);
295                                 }
296                                 else
297                                 {
298                                         Mem_Free(data);
299                                         Host_Error("Mod_LoadTextures: replacement sky image must be 256x128 pixels\n");
300                                 }
301                         }
302                         else if (loadmodel->isworldmodel)
303                                 R_InitSky (mtdata, 1);
304                 }
305                 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
306                 {
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);
311                 }
312                 else
313                 {
314                         if (loadmodel->ishlbsp)
315                         {
316                                 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
317                                 {
318                                         // texture included
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))
321                                         {
322                                                 // make mask 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);
328                                         }
329                                         Mem_Free(data);
330                                 }
331                                 else if ((data = W_GetTexture(tx->name)))
332                                 {
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))
338                                         {
339                                                 // make mask 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);
345                                         }
346                                         Mem_Free(data);
347                                 }
348                                 else
349                                 {
350                                         tx->width = 16;
351                                         tx->height = 16;
352                                         tx->texture = loadmodel->notexture.texture;
353                                 }
354                         }
355                         else
356                         {
357                                 if (mtdata) // texture included
358                                 {
359                                         int fullbrights;
360                                         data = mtdata;
361                                         fullbrights = false;
362                                         if (r_fullbrights.value && tx->name[0] != '*')
363                                         {
364                                                 for (j = 0;j < tx->width*tx->height;j++)
365                                                 {
366                                                         if (data[j] >= 224) // fullbright
367                                                         {
368                                                                 fullbrights = true;
369                                                                 break;
370                                                         }
371                                                 }
372                                         }
373                                         if (fullbrights)
374                                         {
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);
384                                                 Mem_Free(data2);
385                                         }
386                                         else
387                                                 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
388                                 }
389                                 else // no texture, and no external replacement texture was found
390                                 {
391                                         tx->width = 16;
392                                         tx->height = 16;
393                                         tx->texture = loadmodel->notexture.texture;
394                                 }
395                         }
396                 }
397
398                 if (tx->name[0] == '*')
399                 {
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);
406                 }
407                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
408                         tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
409                 else
410                 {
411                         tx->flags |= SURF_LIGHTMAP;
412                         if (!R_TextureHasAlpha(tx->texture))
413                                 tx->flags |= SURF_CLIPSOLID;
414                 }
415         }
416
417 //
418 // sequence the animations
419 //
420         for (i = 0;i < m->nummiptex;i++)
421         {
422                 tx = loadmodel->textures[i];
423                 if (!tx || tx->name[0] != '+')
424                         continue;
425                 if (tx->anim_total)
426                         continue;       // already sequenced
427
428                 // find the number of frames in the animation
429                 memset (anims, 0, sizeof(anims));
430                 memset (altanims, 0, sizeof(altanims));
431                 max = altmax = 0;
432
433                 for (j = i;j < m->nummiptex;j++)
434                 {
435                         tx2 = loadmodel->textures[j];
436                         if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
437                                 continue;
438
439                         num = tx2->name[1];
440                         if (num >= '0' && num <= '9')
441                                 anims[num - '0'] = tx2;
442                         else if (num >= 'a' && num <= 'j')
443                                 altanims[num - 'a'] = tx2;
444                         else
445                                 Host_Error ("Bad animating texture %s", tx->name);
446                 }
447
448                 for (j = 0;j < 10;j++)
449                 {
450                         if (anims[j] != NULL)
451                                 max = j + 1;
452                         if (altanims[j] != NULL)
453                                 altmax = j + 1;
454                 }
455
456                 // link them all together
457                 for (j = 0;j < max;j++)
458                 {
459                         tx2 = anims[j];
460                         if (!tx2)
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];
466                 }
467
468                 for (j = 0;j < altmax;j++)
469                 {
470                         tx2 = altanims[j];
471                         if (!tx2)
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];
477                 }
478         }
479 }
480
481 /*
482 =================
483 Mod_LoadLighting
484 =================
485 */
486 static void Mod_LoadLighting (lump_t *l)
487 {
488         int i;
489         byte *in, *out, *data;
490         byte d;
491         char litfilename[1024];
492         loadmodel->lightdata = NULL;
493         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
494         {
495                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
496                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
497         }
498         else // LordHavoc: bsp version 29 (normal white lighting)
499         {
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);
505                 if (data)
506                 {
507                         if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
508                         {
509                                 i = LittleLong(((int *)data)[1]);
510                                 if (i == 1)
511                                 {
512                                         Con_DPrintf("%s loaded", litfilename);
513                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
514                                         memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
515                                         Mem_Free(data);
516                                         return;
517                                 }
518                                 else
519                                 {
520                                         Con_Printf("Unknown .lit file version (%d)\n", i);
521                                         Mem_Free(data);
522                                 }
523                         }
524                         else
525                         {
526                                 if (loadsize == 8)
527                                         Con_Printf("Empty .lit file, ignoring\n");
528                                 else
529                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
530                                 Mem_Free(data);
531                         }
532                 }
533                 // LordHavoc: oh well, expand the white lighting data
534                 if (!l->filelen)
535                         return;
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++)
541                 {
542                         d = *in++;
543                         *out++ = d;
544                         *out++ = d;
545                         *out++ = d;
546                 }
547         }
548 }
549
550
551 /*
552 =================
553 Mod_LoadVisibility
554 =================
555 */
556 static void Mod_LoadVisibility (lump_t *l)
557 {
558         if (!l->filelen)
559         {
560                 loadmodel->visdata = NULL;
561                 return;
562         }
563         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
564         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
565 }
566
567 // used only for HalfLife maps
568 void Mod_ParseWadsFromEntityLump(char *data)
569 {
570         char key[128], value[4096];
571         char wadname[128];
572         int i, j, k;
573         if (!data)
574                 return;
575         data = COM_Parse(data);
576         if (!data)
577                 return; // error
578         if (com_token[0] != '{')
579                 return; // error
580         while (1)
581         {
582                 data = COM_Parse(data);
583                 if (!data)
584                         return; // error
585                 if (com_token[0] == '}')
586                         break; // end of worldspawn
587                 if (com_token[0] == '_')
588                         strcpy(key, com_token + 1);
589                 else
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);
594                 if (!data)
595                         return; // error
596                 strcpy(value, com_token);
597                 if (!strcmp("wad", key)) // for HalfLife maps
598                 {
599                         if (loadmodel->ishlbsp)
600                         {
601                                 j = 0;
602                                 for (i = 0;i < 4096;i++)
603                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
604                                                 break;
605                                 if (value[i])
606                                 {
607                                         for (;i < 4096;i++)
608                                         {
609                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
610                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
611                                                         j = i+1;
612                                                 else if (value[i] == ';' || value[i] == 0)
613                                                 {
614                                                         k = value[i];
615                                                         value[i] = 0;
616                                                         strcpy(wadname, "textures/");
617                                                         strcat(wadname, &value[j]);
618                                                         W_LoadTextureWadFile (wadname, false);
619                                                         j = i+1;
620                                                         if (!k)
621                                                                 break;
622                                                 }
623                                         }
624                                 }
625                         }
626                 }
627         }
628 }
629
630 /*
631 =================
632 Mod_LoadEntities
633 =================
634 */
635 static void Mod_LoadEntities (lump_t *l)
636 {
637         if (!l->filelen)
638         {
639                 loadmodel->entities = NULL;
640                 return;
641         }
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);
646 }
647
648
649 /*
650 =================
651 Mod_LoadVertexes
652 =================
653 */
654 static void Mod_LoadVertexes (lump_t *l)
655 {
656         dvertex_t       *in;
657         mvertex_t       *out;
658         int                     i, count;
659
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));
665
666         loadmodel->vertexes = out;
667         loadmodel->numvertexes = count;
668
669         for ( i=0 ; i<count ; i++, in++, out++)
670         {
671                 out->position[0] = LittleFloat (in->point[0]);
672                 out->position[1] = LittleFloat (in->point[1]);
673                 out->position[2] = LittleFloat (in->point[2]);
674         }
675 }
676
677 /*
678 =================
679 Mod_LoadSubmodels
680 =================
681 */
682 static void Mod_LoadSubmodels (lump_t *l)
683 {
684         dmodel_t        *in;
685         dmodel_t        *out;
686         int                     i, j, count;
687
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));
693
694         loadmodel->submodels = out;
695         loadmodel->numsubmodels = count;
696
697         for ( i=0 ; i<count ; i++, in++, out++)
698         {
699                 for (j=0 ; j<3 ; j++)
700                 {
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]);
705                 }
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);
711         }
712 }
713
714 /*
715 =================
716 Mod_LoadEdges
717 =================
718 */
719 static void Mod_LoadEdges (lump_t *l)
720 {
721         dedge_t *in;
722         medge_t *out;
723         int     i, count;
724
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));
730
731         loadmodel->edges = out;
732         loadmodel->numedges = count;
733
734         for ( i=0 ; i<count ; i++, in++, out++)
735         {
736                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
737                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
738         }
739 }
740
741 /*
742 =================
743 Mod_LoadTexinfo
744 =================
745 */
746 static void Mod_LoadTexinfo (lump_t *l)
747 {
748         texinfo_t *in;
749         mtexinfo_t *out;
750         int     i, j, k, count;
751         int             miptex;
752
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));
758
759         loadmodel->texinfo = out;
760         loadmodel->numtexinfo = count;
761
762         for (i = 0;i < count;i++, in++, out++)
763         {
764                 for (k = 0;k < 2;k++)
765                         for (j = 0;j < 4;j++)
766                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
767
768                 miptex = LittleLong (in->miptex);
769                 out->flags = LittleLong (in->flags);
770
771                 if (!loadmodel->textures)
772                         out->texture = &loadmodel->notexture;
773                 else
774                 {
775                         if (miptex < 0)
776                                 Host_Error ("miptex < 0");
777                         if (miptex >= loadmodel->numtextures)
778                                 Host_Error ("miptex >= loadmodel->numtextures");
779                         out->texture = loadmodel->textures[miptex];
780                 }
781                 if (!out->texture)
782                         out->texture = &loadmodel->notexture;
783         }
784 }
785
786 /*
787 ================
788 CalcSurfaceExtents
789
790 Fills in s->texturemins[] and s->extents[]
791 ================
792 */
793 static void CalcSurfaceExtents (msurface_t *s)
794 {
795         float   mins[2], maxs[2], val;
796         int             i,j, e;
797         mvertex_t       *v;
798         mtexinfo_t      *tex;
799         int             bmins[2], bmaxs[2];
800
801         mins[0] = mins[1] = 999999999;
802         maxs[0] = maxs[1] = -999999999;
803
804         tex = s->texinfo;
805
806         for (i=0 ; i<s->numedges ; i++)
807         {
808                 e = loadmodel->surfedges[s->firstedge+i];
809                 if (e >= 0)
810                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
811                 else
812                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
813
814                 for (j=0 ; j<2 ; j++)
815                 {
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] +
819                                 tex->vecs[j][3];
820                         if (val < mins[j])
821                                 mins[j] = val;
822                         if (val > maxs[j])
823                                 maxs[j] = val;
824                 }
825         }
826
827         for (i=0 ; i<2 ; i++)
828         {
829                 bmins[i] = floor(mins[i]/16);
830                 bmaxs[i] = ceil(maxs[i]/16);
831
832                 s->texturemins[i] = bmins[i] * 16;
833                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
834         }
835 }
836
837
838 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
839 {
840         int             i, j;
841         float   *v;
842
843         mins[0] = mins[1] = mins[2] = 9999;
844         maxs[0] = maxs[1] = maxs[2] = -9999;
845         v = verts;
846         for (i = 0;i < numverts;i++)
847         {
848                 for (j = 0;j < 3;j++, v++)
849                 {
850                         if (*v < mins[j])
851                                 mins[j] = *v;
852                         if (*v > maxs[j])
853                                 maxs[j] = *v;
854                 }
855         }
856 }
857
858 #define MAX_SUBDIVPOLYTRIANGLES 4096
859 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
860
861 static int subdivpolyverts, subdivpolytriangles;
862 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
863 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
864
865 static int subdivpolylookupvert(vec3_t v)
866 {
867         int i;
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])
872                         return i;
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++;
877 }
878
879 static void SubdividePolygon (int numverts, float *verts)
880 {
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;
884
885         if (numverts > 250)
886                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
887
888         BoundPoly (numverts, verts, mins, maxs);
889
890         for (i = 0;i < 3;i++)
891         {
892                 m = (mins[i] + maxs[i]) * 0.5;
893                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
894                 if (maxs[i] - m < 8)
895                         continue;
896                 if (m - mins[i] < 8)
897                         continue;
898
899                 // cut it
900                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
901                         dist[c] = cv[i] - m;
902
903                 f = b = 0;
904                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
905                 {
906                         if (dist[p] >= 0)
907                         {
908                                 VectorCopy (pv, front[f]);
909                                 f++;
910                         }
911                         if (dist[p] <= 0)
912                         {
913                                 VectorCopy (pv, back[b]);
914                                 b++;
915                         }
916                         if (dist[p] == 0 || dist[c] == 0)
917                                 continue;
918                         if ( (dist[p] > 0) != (dist[c] > 0) )
919                         {
920                                 // clip point
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]);
925                                 f++;
926                                 b++;
927                         }
928                 }
929
930                 SubdividePolygon (f, front[0]);
931                 SubdividePolygon (b, back[0]);
932                 return;
933         }
934
935         i1 = subdivpolylookupvert(verts);
936         i2 = subdivpolylookupvert(verts + 3);
937         for (i = 2;i < numverts;i++)
938         {
939                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
940                 {
941                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
942                         return;
943                 }
944
945                 i3 = subdivpolylookupvert(verts + i * 3);
946                 subdivpolyindex[subdivpolytriangles][0] = i1;
947                 subdivpolyindex[subdivpolytriangles][1] = i2;
948                 subdivpolyindex[subdivpolytriangles][2] = i3;
949                 i2 = i3;
950                 subdivpolytriangles++;
951         }
952 }
953
954 /*
955 ================
956 Mod_GenerateWarpMesh
957
958 Breaks a polygon up along axial 64 unit
959 boundaries so that turbulent and sky warps
960 can be done reasonably.
961 ================
962 */
963 void Mod_GenerateWarpMesh (msurface_t *surf)
964 {
965         int                             i, j;
966         surfvertex_t    *v;
967         surfmesh_t              *mesh;
968
969         subdivpolytriangles = 0;
970         subdivpolyverts = 0;
971         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
972
973         mesh = &surf->mesh;
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));
981
982         for (i = 0;i < mesh->numtriangles;i++)
983         {
984                 for (j = 0;j < 3;j++)
985                 {
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");
989                 }
990         }
991
992         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
993         {
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]);
997         }
998 }
999
1000 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1001 {
1002         int                             i, is, it, *index, smax, tmax;
1003         float                   *in, s, t;
1004         surfvertex_t    *out;
1005         surfmesh_t              *mesh;
1006
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;
1012
1013         mesh = &surf->mesh;
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));
1019
1020         index = mesh->index;
1021         for (i = 0;i < mesh->numtriangles;i++)
1022         {
1023                 *index++ = 0;
1024                 *index++ = i + 1;
1025                 *index++ = i + 2;
1026         }
1027
1028         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1029         {
1030                 VectorCopy (in, out->v);
1031
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];
1034
1035                 out->st[0] = s / surf->texinfo->texture->width;
1036                 out->st[1] = t / surf->texinfo->texture->height;
1037
1038                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1039                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1040
1041                 // lightmap coordinates
1042                 out->uv[0] = 0;
1043                 out->uv[1] = 0;
1044
1045                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1046                 is = (int) s;
1047                 it = (int) t;
1048                 is = bound(0, is, smax);
1049                 it = bound(0, it, tmax);
1050                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1051         }
1052 }
1053
1054 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1055 {
1056         int                             i, is, it, *index, smax, tmax;
1057         float                   *in, s, t, xbase, ybase, xscale, yscale;
1058         surfvertex_t    *out;
1059         surfmesh_t              *mesh;
1060
1061         surf->flags |= SURF_LIGHTMAP;
1062         smax = surf->extents[0] >> 4;
1063         tmax = surf->extents[1] >> 4;
1064         if (r_miplightmaps.integer)
1065         {
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);
1068         }
1069         else
1070         {
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);
1073         }
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_GetFragmentLocation(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);
1079
1080         mesh = &surf->mesh;
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));
1086
1087         index = mesh->index;
1088         for (i = 0;i < mesh->numtriangles;i++)
1089         {
1090                 *index++ = 0;
1091                 *index++ = i + 1;
1092                 *index++ = i + 2;
1093         }
1094
1095         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1096         {
1097                 VectorCopy (in, out->v);
1098
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];
1101
1102                 out->st[0] = s / surf->texinfo->texture->width;
1103                 out->st[1] = t / surf->texinfo->texture->height;
1104
1105                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1106                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1107
1108                 // lightmap coordinates
1109                 out->uv[0] = s * xscale + xbase;
1110                 out->uv[1] = t * yscale + ybase;
1111
1112                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1113                 is = (int) s;
1114                 it = (int) t;
1115                 is = bound(0, is, smax);
1116                 it = bound(0, it, tmax);
1117                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1118         }
1119 }
1120
1121 void Mod_GenerateVertexMesh (msurface_t *surf)
1122 {
1123         int                             i, *index;
1124         float                   *in;
1125         surfvertex_t    *out;
1126         surfmesh_t              *mesh;
1127
1128         surf->lightmaptexturestride = 0;
1129         surf->lightmaptexture = NULL;
1130
1131         mesh = &surf->mesh;
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));
1137
1138         index = mesh->index;
1139         for (i = 0;i < mesh->numtriangles;i++)
1140         {
1141                 *index++ = 0;
1142                 *index++ = i + 1;
1143                 *index++ = i + 2;
1144         }
1145
1146         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1147         {
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;
1151         }
1152 }
1153
1154 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1155 {
1156         float           *vert;
1157         int                     i;
1158         int                     lindex;
1159         float           *vec;
1160
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++)
1165         {
1166                 lindex = loadmodel->surfedges[surf->firstedge + i];
1167                 if (lindex > 0)
1168                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1169                 else
1170                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1171                 VectorCopy (vec, vert);
1172                 vert += 3;
1173         }
1174 }
1175
1176 /*
1177 =================
1178 Mod_LoadFaces
1179 =================
1180 */
1181 static void Mod_LoadFaces (lump_t *l)
1182 {
1183         dface_t         *in;
1184         msurface_t      *out;
1185         int                     i, count, surfnum;
1186         int                     planenum, side;
1187
1188         in = (void *)(mod_base + l->fileofs);
1189         if (l->filelen % sizeof(*in))
1190                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1191         count = l->filelen / sizeof(*in);
1192         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1193
1194         loadmodel->surfaces = out;
1195         loadmodel->numsurfaces = count;
1196
1197         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1198         {
1199                 // FIXME: validate edges, texinfo, etc?
1200                 out->firstedge = LittleLong(in->firstedge);
1201                 out->numedges = LittleShort(in->numedges);
1202
1203                 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
1204                 out->flags = out->texinfo->texture->flags;
1205
1206                 planenum = LittleShort(in->planenum);
1207                 side = LittleShort(in->side);
1208                 if (side)
1209                         out->flags |= SURF_PLANEBACK;
1210
1211                 out->plane = loadmodel->planes + planenum;
1212
1213                 // clear lightmap (filled in later)
1214                 out->lightmaptexture = NULL;
1215
1216                 // force lightmap upload on first time seeing the surface
1217                 out->cached_dlight = true;
1218                 out->cached_ambient = -1000;
1219                 out->cached_lightscalebit = -1000;
1220                 out->cached_light[0] = -1000;
1221                 out->cached_light[1] = -1000;
1222                 out->cached_light[2] = -1000;
1223                 out->cached_light[3] = -1000;
1224
1225                 CalcSurfaceExtents (out);
1226
1227                 // lighting info
1228                 for (i = 0;i < MAXLIGHTMAPS;i++)
1229                         out->styles[i] = in->styles[i];
1230                 i = LittleLong(in->lightofs);
1231                 if (i == -1)
1232                         out->samples = NULL;
1233                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1234                         out->samples = loadmodel->lightdata + i;
1235                 else // LordHavoc: white lighting (bsp version 29)
1236                         out->samples = loadmodel->lightdata + (i * 3);
1237
1238                 Mod_GenerateSurfacePolygon(out);
1239
1240                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1241                 {
1242                         out->shader = &Cshader_sky;
1243                         out->samples = NULL;
1244                         Mod_GenerateWarpMesh (out);
1245                         continue;
1246                 }
1247
1248                 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1249                 {
1250                         out->shader = &Cshader_water;
1251                         /*
1252                         for (i=0 ; i<2 ; i++)
1253                         {
1254                                 out->extents[i] = 16384*1024;
1255                                 out->texturemins[i] = -8192*1024;
1256                         }
1257                         */
1258                         out->samples = NULL;
1259                         Mod_GenerateWarpMesh (out);
1260                         continue;
1261                 }
1262
1263                 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1264                         out->flags |= SURF_CLIPSOLID;
1265                 if (out->texinfo->flags & TEX_SPECIAL)
1266                 {
1267                         // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1268                         out->shader = &Cshader_water;
1269                         out->samples = NULL;
1270                         Mod_GenerateWarpMesh (out);
1271                 }
1272                 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1273                 {
1274                         Con_Printf ("Bad surface extents, converting to fullbright polygon");
1275                         out->shader = &Cshader_wall_fullbright;
1276                         out->samples = NULL;
1277                         Mod_GenerateVertexMesh(out);
1278                 }
1279                 else if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1280                 {
1281                         out->shader = &Cshader_wall_vertex;
1282                         Mod_GenerateVertexLitMesh(out);
1283                 }
1284                 else
1285                 {
1286                         out->shader = &Cshader_wall_lightmap;
1287                         Mod_GenerateLightmappedMesh(out);
1288                 }
1289         }
1290 }
1291
1292 static model_t *sortmodel;
1293
1294 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1295 {
1296         const msurface_t *a, *b;
1297         a = *((const msurface_t **)voida);
1298         b = *((const msurface_t **)voidb);
1299         if (a->shader != b->shader)
1300                 return (long) a->shader - (long) b->shader;
1301         if (a->texinfo->texture != b->texinfo->texture);
1302                 return a->texinfo->texture - b->texinfo->texture;
1303         return 0;
1304 }
1305
1306 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1307 {
1308         int surfnum;
1309         sortmodel = model;
1310         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1311         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1312                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1313
1314         qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1315 }
1316
1317
1318 /*
1319 =================
1320 Mod_SetParent
1321 =================
1322 */
1323 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1324 {
1325         node->parent = parent;
1326         if (node->contents < 0)
1327                 return;
1328         Mod_SetParent (node->children[0], node);
1329         Mod_SetParent (node->children[1], node);
1330 }
1331
1332 /*
1333 =================
1334 Mod_LoadNodes
1335 =================
1336 */
1337 static void Mod_LoadNodes (lump_t *l)
1338 {
1339         int                     i, j, count, p;
1340         dnode_t         *in;
1341         mnode_t         *out;
1342
1343         in = (void *)(mod_base + l->fileofs);
1344         if (l->filelen % sizeof(*in))
1345                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1346         count = l->filelen / sizeof(*in);
1347         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1348
1349         loadmodel->nodes = out;
1350         loadmodel->numnodes = count;
1351
1352         for ( i=0 ; i<count ; i++, in++, out++)
1353         {
1354                 for (j=0 ; j<3 ; j++)
1355                 {
1356                         out->mins[j] = LittleShort (in->mins[j]);
1357                         out->maxs[j] = LittleShort (in->maxs[j]);
1358                 }
1359
1360                 p = LittleLong(in->planenum);
1361                 out->plane = loadmodel->planes + p;
1362
1363                 out->firstsurface = LittleShort (in->firstface);
1364                 out->numsurfaces = LittleShort (in->numfaces);
1365
1366                 for (j=0 ; j<2 ; j++)
1367                 {
1368                         p = LittleShort (in->children[j]);
1369                         if (p >= 0)
1370                                 out->children[j] = loadmodel->nodes + p;
1371                         else
1372                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1373                 }
1374         }
1375
1376         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1377 }
1378
1379 /*
1380 =================
1381 Mod_LoadLeafs
1382 =================
1383 */
1384 static void Mod_LoadLeafs (lump_t *l)
1385 {
1386         dleaf_t         *in;
1387         mleaf_t         *out;
1388         int                     i, j, count, p;
1389
1390         in = (void *)(mod_base + l->fileofs);
1391         if (l->filelen % sizeof(*in))
1392                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1393         count = l->filelen / sizeof(*in);
1394         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1395
1396         loadmodel->leafs = out;
1397         loadmodel->numleafs = count;
1398
1399         for ( i=0 ; i<count ; i++, in++, out++)
1400         {
1401                 for (j=0 ; j<3 ; j++)
1402                 {
1403                         out->mins[j] = LittleShort (in->mins[j]);
1404                         out->maxs[j] = LittleShort (in->maxs[j]);
1405                 }
1406
1407                 p = LittleLong(in->contents);
1408                 out->contents = p;
1409
1410                 out->firstmarksurface = loadmodel->marksurfaces +
1411                         LittleShort(in->firstmarksurface);
1412                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1413
1414                 p = LittleLong(in->visofs);
1415                 if (p == -1)
1416                         out->compressed_vis = NULL;
1417                 else
1418                         out->compressed_vis = loadmodel->visdata + p;
1419
1420                 for (j=0 ; j<4 ; j++)
1421                         out->ambient_sound_level[j] = in->ambient_level[j];
1422
1423                 // gl underwater warp
1424                 // LordHavoc: disabled underwater warping
1425                 /*
1426                 if (out->contents != CONTENTS_EMPTY)
1427                 {
1428                         for (j=0 ; j<out->nummarksurfaces ; j++)
1429                                 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1430                 }
1431                 */
1432         }
1433 }
1434
1435 /*
1436 =================
1437 Mod_LoadClipnodes
1438 =================
1439 */
1440 static void Mod_LoadClipnodes (lump_t *l)
1441 {
1442         dclipnode_t *in, *out;
1443         int                     i, count;
1444         hull_t          *hull;
1445
1446         in = (void *)(mod_base + l->fileofs);
1447         if (l->filelen % sizeof(*in))
1448                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1449         count = l->filelen / sizeof(*in);
1450         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1451
1452         loadmodel->clipnodes = out;
1453         loadmodel->numclipnodes = count;
1454
1455         if (loadmodel->ishlbsp)
1456         {
1457                 hull = &loadmodel->hulls[1];
1458                 hull->clipnodes = out;
1459                 hull->firstclipnode = 0;
1460                 hull->lastclipnode = count-1;
1461                 hull->planes = loadmodel->planes;
1462                 hull->clip_mins[0] = -16;
1463                 hull->clip_mins[1] = -16;
1464                 hull->clip_mins[2] = -36;
1465                 hull->clip_maxs[0] = 16;
1466                 hull->clip_maxs[1] = 16;
1467                 hull->clip_maxs[2] = 36;
1468
1469                 hull = &loadmodel->hulls[2];
1470                 hull->clipnodes = out;
1471                 hull->firstclipnode = 0;
1472                 hull->lastclipnode = count-1;
1473                 hull->planes = loadmodel->planes;
1474                 hull->clip_mins[0] = -32;
1475                 hull->clip_mins[1] = -32;
1476                 hull->clip_mins[2] = -32;
1477                 hull->clip_maxs[0] = 32;
1478                 hull->clip_maxs[1] = 32;
1479                 hull->clip_maxs[2] = 32;
1480
1481                 hull = &loadmodel->hulls[3];
1482                 hull->clipnodes = out;
1483                 hull->firstclipnode = 0;
1484                 hull->lastclipnode = count-1;
1485                 hull->planes = loadmodel->planes;
1486                 hull->clip_mins[0] = -16;
1487                 hull->clip_mins[1] = -16;
1488                 hull->clip_mins[2] = -18;
1489                 hull->clip_maxs[0] = 16;
1490                 hull->clip_maxs[1] = 16;
1491                 hull->clip_maxs[2] = 18;
1492         }
1493         else
1494         {
1495                 hull = &loadmodel->hulls[1];
1496                 hull->clipnodes = out;
1497                 hull->firstclipnode = 0;
1498                 hull->lastclipnode = count-1;
1499                 hull->planes = loadmodel->planes;
1500                 hull->clip_mins[0] = -16;
1501                 hull->clip_mins[1] = -16;
1502                 hull->clip_mins[2] = -24;
1503                 hull->clip_maxs[0] = 16;
1504                 hull->clip_maxs[1] = 16;
1505                 hull->clip_maxs[2] = 32;
1506
1507                 hull = &loadmodel->hulls[2];
1508                 hull->clipnodes = out;
1509                 hull->firstclipnode = 0;
1510                 hull->lastclipnode = count-1;
1511                 hull->planes = loadmodel->planes;
1512                 hull->clip_mins[0] = -32;
1513                 hull->clip_mins[1] = -32;
1514                 hull->clip_mins[2] = -24;
1515                 hull->clip_maxs[0] = 32;
1516                 hull->clip_maxs[1] = 32;
1517                 hull->clip_maxs[2] = 64;
1518         }
1519
1520         for (i=0 ; i<count ; i++, out++, in++)
1521         {
1522                 out->planenum = LittleLong(in->planenum);
1523                 out->children[0] = LittleShort(in->children[0]);
1524                 out->children[1] = LittleShort(in->children[1]);
1525                 if (out->children[0] >= count || out->children[1] >= count)
1526                         Host_Error("Corrupt clipping hull (out of range child)\n");
1527         }
1528 }
1529
1530 /*
1531 =================
1532 Mod_MakeHull0
1533
1534 Duplicate the drawing hull structure as a clipping hull
1535 =================
1536 */
1537 static void Mod_MakeHull0 (void)
1538 {
1539         mnode_t         *in;
1540         dclipnode_t *out;
1541         int                     i;
1542         hull_t          *hull;
1543
1544         hull = &loadmodel->hulls[0];
1545
1546         in = loadmodel->nodes;
1547         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1548
1549         hull->clipnodes = out;
1550         hull->firstclipnode = 0;
1551         hull->lastclipnode = loadmodel->numnodes - 1;
1552         hull->planes = loadmodel->planes;
1553
1554         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1555         {
1556                 out->planenum = in->plane - loadmodel->planes;
1557                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1558                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1559         }
1560 }
1561
1562 /*
1563 =================
1564 Mod_LoadMarksurfaces
1565 =================
1566 */
1567 static void Mod_LoadMarksurfaces (lump_t *l)
1568 {
1569         int             i, j;
1570         short   *in;
1571
1572         in = (void *)(mod_base + l->fileofs);
1573         if (l->filelen % sizeof(*in))
1574                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1575         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1576         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1577
1578         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1579         {
1580                 j = (unsigned) LittleShort(in[i]);
1581                 if (j >= loadmodel->numsurfaces)
1582                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1583                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1584         }
1585 }
1586
1587 /*
1588 =================
1589 Mod_LoadSurfedges
1590 =================
1591 */
1592 static void Mod_LoadSurfedges (lump_t *l)
1593 {
1594         int             i;
1595         int             *in;
1596
1597         in = (void *)(mod_base + l->fileofs);
1598         if (l->filelen % sizeof(*in))
1599                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1600         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1601         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1602
1603         for (i = 0;i < loadmodel->numsurfedges;i++)
1604                 loadmodel->surfedges[i] = LittleLong (in[i]);
1605 }
1606
1607
1608 /*
1609 =================
1610 Mod_LoadPlanes
1611 =================
1612 */
1613 static void Mod_LoadPlanes (lump_t *l)
1614 {
1615         int                     i;
1616         mplane_t        *out;
1617         dplane_t        *in;
1618
1619         in = (void *)(mod_base + l->fileofs);
1620         if (l->filelen % sizeof(*in))
1621                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1622
1623         loadmodel->numplanes = l->filelen / sizeof(*in);
1624         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1625
1626         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1627         {
1628                 out->normal[0] = LittleFloat (in->normal[0]);
1629                 out->normal[1] = LittleFloat (in->normal[1]);
1630                 out->normal[2] = LittleFloat (in->normal[2]);
1631                 out->dist = LittleFloat (in->dist);
1632
1633                 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1634 //              out->type = LittleLong (in->type);
1635                 PlaneClassify(out);
1636         }
1637 }
1638
1639 #define MAX_POINTS_ON_WINDING 64
1640
1641 typedef struct
1642 {
1643         int numpoints;
1644         int padding;
1645         double points[8][3]; // variable sized
1646 }
1647 winding_t;
1648
1649 /*
1650 ==================
1651 NewWinding
1652 ==================
1653 */
1654 static winding_t *NewWinding (int points)
1655 {
1656         winding_t *w;
1657         int size;
1658
1659         if (points > MAX_POINTS_ON_WINDING)
1660                 Sys_Error("NewWinding: too many points\n");
1661
1662         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1663         w = Mem_Alloc(loadmodel->mempool, size);
1664         memset (w, 0, size);
1665
1666         return w;
1667 }
1668
1669 static void FreeWinding (winding_t *w)
1670 {
1671         Mem_Free(w);
1672 }
1673
1674 /*
1675 =================
1676 BaseWindingForPlane
1677 =================
1678 */
1679 static winding_t *BaseWindingForPlane (mplane_t *p)
1680 {
1681         double org[3], vright[3], vup[3], normal[3];
1682         winding_t *w;
1683
1684         VectorCopy(p->normal, normal);
1685         VectorVectorsDouble(normal, vright, vup);
1686
1687         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1688         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1689
1690         // project a really big axis aligned box onto the plane
1691         w = NewWinding (4);
1692
1693         VectorScale (p->normal, p->dist, org);
1694
1695         VectorSubtract (org, vright, w->points[0]);
1696         VectorAdd (w->points[0], vup, w->points[0]);
1697
1698         VectorAdd (org, vright, w->points[1]);
1699         VectorAdd (w->points[1], vup, w->points[1]);
1700
1701         VectorAdd (org, vright, w->points[2]);
1702         VectorSubtract (w->points[2], vup, w->points[2]);
1703
1704         VectorSubtract (org, vright, w->points[3]);
1705         VectorSubtract (w->points[3], vup, w->points[3]);
1706
1707         w->numpoints = 4;
1708
1709         return w;
1710 }
1711
1712 /*
1713 ==================
1714 ClipWinding
1715
1716 Clips the winding to the plane, returning the new winding on the positive side
1717 Frees the input winding.
1718 If keepon is true, an exactly on-plane winding will be saved, otherwise
1719 it will be clipped away.
1720 ==================
1721 */
1722 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1723 {
1724         double  dists[MAX_POINTS_ON_WINDING + 1];
1725         int             sides[MAX_POINTS_ON_WINDING + 1];
1726         int             counts[3];
1727         double  dot;
1728         int             i, j;
1729         double  *p1, *p2;
1730         double  mid[3];
1731         winding_t       *neww;
1732         int             maxpts;
1733
1734         // debugging
1735         Mem_CheckSentinels(in);
1736
1737         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1738
1739         // determine sides for each point
1740         for (i = 0;i < in->numpoints;i++)
1741         {
1742                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1743                 if (dot > ON_EPSILON)
1744                         sides[i] = SIDE_FRONT;
1745                 else if (dot < -ON_EPSILON)
1746                         sides[i] = SIDE_BACK;
1747                 else
1748                         sides[i] = SIDE_ON;
1749                 counts[sides[i]]++;
1750         }
1751         sides[i] = sides[0];
1752         dists[i] = dists[0];
1753
1754         if (keepon && !counts[0] && !counts[1])
1755                 return in;
1756
1757         if (!counts[0])
1758         {
1759                 FreeWinding (in);
1760                 return NULL;
1761         }
1762         if (!counts[1])
1763                 return in;
1764
1765         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1766         if (maxpts > MAX_POINTS_ON_WINDING)
1767                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1768
1769         neww = NewWinding (maxpts);
1770
1771         for (i = 0;i < in->numpoints;i++)
1772         {
1773                 if (neww->numpoints >= maxpts)
1774                         Sys_Error ("ClipWinding: points exceeded estimate");
1775
1776                 Mem_CheckSentinels(neww);
1777
1778                 p1 = in->points[i];
1779
1780                 if (sides[i] == SIDE_ON)
1781                 {
1782                         VectorCopy (p1, neww->points[neww->numpoints]);
1783                         neww->numpoints++;
1784                         Mem_CheckSentinels(neww);
1785                         continue;
1786                 }
1787
1788                 if (sides[i] == SIDE_FRONT)
1789                 {
1790                         VectorCopy (p1, neww->points[neww->numpoints]);
1791                         neww->numpoints++;
1792                         Mem_CheckSentinels(neww);
1793                 }
1794
1795                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1796                         continue;
1797
1798                 // generate a split point
1799                 p2 = in->points[(i+1)%in->numpoints];
1800
1801                 dot = dists[i] / (dists[i]-dists[i+1]);
1802                 for (j = 0;j < 3;j++)
1803                 {       // avoid round off error when possible
1804                         if (split->normal[j] == 1)
1805                                 mid[j] = split->dist;
1806                         else if (split->normal[j] == -1)
1807                                 mid[j] = -split->dist;
1808                         else
1809                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1810                 }
1811
1812                 VectorCopy (mid, neww->points[neww->numpoints]);
1813                 neww->numpoints++;
1814                 Mem_CheckSentinels(neww);
1815         }
1816
1817         // free the original winding
1818         FreeWinding (in);
1819
1820         // debugging
1821         Mem_CheckSentinels(neww);
1822
1823         return neww;
1824 }
1825
1826
1827 /*
1828 ==================
1829 DivideWinding
1830
1831 Divides a winding by a plane, producing one or two windings.  The
1832 original winding is not damaged or freed.  If only on one side, the
1833 returned winding will be the input winding.  If on both sides, two
1834 new windings will be created.
1835 ==================
1836 */
1837 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1838 {
1839         double  dists[MAX_POINTS_ON_WINDING + 1];
1840         int             sides[MAX_POINTS_ON_WINDING + 1];
1841         int             counts[3];
1842         double  dot;
1843         int             i, j;
1844         double  *p1, *p2;
1845         double  mid[3];
1846         winding_t       *f, *b;
1847         int             maxpts;
1848
1849         // debugging
1850         Mem_CheckSentinels(in);
1851
1852         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1853
1854         // determine sides for each point
1855         for (i = 0;i < in->numpoints;i++)
1856         {
1857                 dot = DotProduct (in->points[i], split->normal);
1858                 dot -= split->dist;
1859                 dists[i] = dot;
1860                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1861                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1862                 else sides[i] = SIDE_ON;
1863                 counts[sides[i]]++;
1864         }
1865         sides[i] = sides[0];
1866         dists[i] = dists[0];
1867
1868         *front = *back = NULL;
1869
1870         if (!counts[0])
1871         {
1872                 *back = in;
1873                 return;
1874         }
1875         if (!counts[1])
1876         {
1877                 *front = in;
1878                 return;
1879         }
1880
1881         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1882
1883         if (maxpts > MAX_POINTS_ON_WINDING)
1884                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1885
1886         *front = f = NewWinding (maxpts);
1887         *back = b = NewWinding (maxpts);
1888
1889         for (i = 0;i < in->numpoints;i++)
1890         {
1891                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1892                         Sys_Error ("DivideWinding: points exceeded estimate");
1893
1894                 p1 = in->points[i];
1895
1896                 if (sides[i] == SIDE_ON)
1897                 {
1898                         VectorCopy (p1, f->points[f->numpoints]);
1899                         f->numpoints++;
1900                         Mem_CheckSentinels(f);
1901                         VectorCopy (p1, b->points[b->numpoints]);
1902                         b->numpoints++;
1903                         Mem_CheckSentinels(b);
1904                         continue;
1905                 }
1906
1907                 if (sides[i] == SIDE_FRONT)
1908                 {
1909                         VectorCopy (p1, f->points[f->numpoints]);
1910                         f->numpoints++;
1911                         Mem_CheckSentinels(f);
1912                 }
1913                 else if (sides[i] == SIDE_BACK)
1914                 {
1915                         VectorCopy (p1, b->points[b->numpoints]);
1916                         b->numpoints++;
1917                         Mem_CheckSentinels(b);
1918                 }
1919
1920                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1921                         continue;
1922
1923                 // generate a split point
1924                 p2 = in->points[(i+1)%in->numpoints];
1925
1926                 dot = dists[i] / (dists[i]-dists[i+1]);
1927                 for (j = 0;j < 3;j++)
1928                 {       // avoid round off error when possible
1929                         if (split->normal[j] == 1)
1930                                 mid[j] = split->dist;
1931                         else if (split->normal[j] == -1)
1932                                 mid[j] = -split->dist;
1933                         else
1934                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1935                 }
1936
1937                 VectorCopy (mid, f->points[f->numpoints]);
1938                 f->numpoints++;
1939                 Mem_CheckSentinels(f);
1940                 VectorCopy (mid, b->points[b->numpoints]);
1941                 b->numpoints++;
1942                 Mem_CheckSentinels(b);
1943         }
1944
1945         // debugging
1946         Mem_CheckSentinels(front);
1947         Mem_CheckSentinels(back);
1948 }
1949
1950 typedef struct portal_s
1951 {
1952         mplane_t plane;
1953         mnode_t *nodes[2];              // [0] = front side of plane
1954         struct portal_s *next[2];
1955         winding_t *winding;
1956         struct portal_s *chain; // all portals are linked into a list
1957 }
1958 portal_t;
1959
1960 static portal_t *portalchain;
1961
1962 /*
1963 ===========
1964 AllocPortal
1965 ===========
1966 */
1967 static portal_t *AllocPortal (void)
1968 {
1969         portal_t *p;
1970         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1971         //memset(p, 0, sizeof(portal_t));
1972         p->chain = portalchain;
1973         portalchain = p;
1974         return p;
1975 }
1976
1977 static void FreePortal(portal_t *p)
1978 {
1979         Mem_Free(p);
1980 }
1981
1982 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1983 {
1984         // calculate children first
1985         if (node->children[0]->contents >= 0)
1986                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1987         if (node->children[1]->contents >= 0)
1988                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1989
1990         // make combined bounding box from children
1991         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1992         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1993         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1994         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1995         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1996         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
1997 }
1998
1999 static void Mod_FinalizePortals(void)
2000 {
2001         int i, j, numportals, numpoints;
2002         portal_t *p, *pnext;
2003         mportal_t *portal;
2004         mvertex_t *point;
2005         mleaf_t *leaf, *endleaf;
2006         winding_t *w;
2007
2008         Mem_CheckSentinelsGlobal();
2009
2010         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2011         leaf = loadmodel->leafs;
2012         endleaf = leaf + loadmodel->numleafs;
2013         for (;leaf < endleaf;leaf++)
2014         {
2015                 VectorSet( 2000000000,  2000000000,  2000000000, leaf->mins);
2016                 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
2017         }
2018         p = portalchain;
2019         while(p)
2020         {
2021                 if (p->winding)
2022                 {
2023                         for (i = 0;i < 2;i++)
2024                         {
2025                                 leaf = (mleaf_t *)p->nodes[i];
2026                                 w = p->winding;
2027                                 for (j = 0;j < w->numpoints;j++)
2028                                 {
2029                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2030                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2031                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2032                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2033                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2034                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2035                                 }
2036                         }
2037                 }
2038                 p = p->chain;
2039         }
2040
2041         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2042
2043         Mem_CheckSentinelsGlobal();
2044
2045         // tally up portal and point counts
2046         p = portalchain;
2047         numportals = 0;
2048         numpoints = 0;
2049         while(p)
2050         {
2051                 // note: this check must match the one below or it will usually corrupt memory
2052                 // 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
2053                 if (p->winding && p->nodes[0] != p->nodes[1]
2054                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2055                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2056                 {
2057                         numportals += 2;
2058                         numpoints += p->winding->numpoints * 2;
2059                 }
2060                 p = p->chain;
2061         }
2062         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2063         loadmodel->numportals = numportals;
2064         loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2065         loadmodel->numportalpoints = numpoints;
2066         // clear all leaf portal chains
2067         for (i = 0;i < loadmodel->numleafs;i++)
2068                 loadmodel->leafs[i].portals = NULL;
2069         // process all portals in the global portal chain, while freeing them
2070         portal = loadmodel->portals;
2071         point = loadmodel->portalpoints;
2072         p = portalchain;
2073         portalchain = NULL;
2074         while (p)
2075         {
2076                 pnext = p->chain;
2077
2078                 if (p->winding)
2079                 {
2080                         // note: this check must match the one above or it will usually corrupt memory
2081                         // 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
2082                         if (p->nodes[0] != p->nodes[1]
2083                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2084                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2085                         {
2086                                 // first make the back to front portal (forward portal)
2087                                 portal->points = point;
2088                                 portal->numpoints = p->winding->numpoints;
2089                                 portal->plane.dist = p->plane.dist;
2090                                 VectorCopy(p->plane.normal, portal->plane.normal);
2091                                 portal->here = (mleaf_t *)p->nodes[1];
2092                                 portal->past = (mleaf_t *)p->nodes[0];
2093                                 // copy points
2094                                 for (j = 0;j < portal->numpoints;j++)
2095                                 {
2096                                         VectorCopy(p->winding->points[j], point->position);
2097                                         point++;
2098                                 }
2099                                 PlaneClassify(&portal->plane);
2100
2101                                 // link into leaf's portal chain
2102                                 portal->next = portal->here->portals;
2103                                 portal->here->portals = portal;
2104
2105                                 // advance to next portal
2106                                 portal++;
2107
2108                                 // then make the front to back portal (backward portal)
2109                                 portal->points = point;
2110                                 portal->numpoints = p->winding->numpoints;
2111                                 portal->plane.dist = -p->plane.dist;
2112                                 VectorNegate(p->plane.normal, portal->plane.normal);
2113                                 portal->here = (mleaf_t *)p->nodes[0];
2114                                 portal->past = (mleaf_t *)p->nodes[1];
2115                                 // copy points
2116                                 for (j = portal->numpoints - 1;j >= 0;j--)
2117                                 {
2118                                         VectorCopy(p->winding->points[j], point->position);
2119                                         point++;
2120                                 }
2121                                 PlaneClassify(&portal->plane);
2122
2123                                 // link into leaf's portal chain
2124                                 portal->next = portal->here->portals;
2125                                 portal->here->portals = portal;
2126
2127                                 // advance to next portal
2128                                 portal++;
2129                         }
2130                         FreeWinding(p->winding);
2131                 }
2132                 FreePortal(p);
2133                 p = pnext;
2134         }
2135
2136         Mem_CheckSentinelsGlobal();
2137 }
2138
2139 /*
2140 =============
2141 AddPortalToNodes
2142 =============
2143 */
2144 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2145 {
2146         if (!front)
2147                 Host_Error ("AddPortalToNodes: NULL front node");
2148         if (!back)
2149                 Host_Error ("AddPortalToNodes: NULL back node");
2150         if (p->nodes[0] || p->nodes[1])
2151                 Host_Error ("AddPortalToNodes: already included");
2152         // 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
2153
2154         p->nodes[0] = front;
2155         p->next[0] = (portal_t *)front->portals;
2156         front->portals = (mportal_t *)p;
2157
2158         p->nodes[1] = back;
2159         p->next[1] = (portal_t *)back->portals;
2160         back->portals = (mportal_t *)p;
2161 }
2162
2163 /*
2164 =============
2165 RemovePortalFromNode
2166 =============
2167 */
2168 static void RemovePortalFromNodes(portal_t *portal)
2169 {
2170         int i;
2171         mnode_t *node;
2172         void **portalpointer;
2173         portal_t *t;
2174         for (i = 0;i < 2;i++)
2175         {
2176                 node = portal->nodes[i];
2177
2178                 portalpointer = (void **) &node->portals;
2179                 while (1)
2180                 {
2181                         t = *portalpointer;
2182                         if (!t)
2183                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2184
2185                         if (t == portal)
2186                         {
2187                                 if (portal->nodes[0] == node)
2188                                 {
2189                                         *portalpointer = portal->next[0];
2190                                         portal->nodes[0] = NULL;
2191                                 }
2192                                 else if (portal->nodes[1] == node)
2193                                 {
2194                                         *portalpointer = portal->next[1];
2195                                         portal->nodes[1] = NULL;
2196                                 }
2197                                 else
2198                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2199                                 break;
2200                         }
2201
2202                         if (t->nodes[0] == node)
2203                                 portalpointer = (void **) &t->next[0];
2204                         else if (t->nodes[1] == node)
2205                                 portalpointer = (void **) &t->next[1];
2206                         else
2207                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2208                 }
2209         }
2210 }
2211
2212 static void Mod_RecursiveNodePortals (mnode_t *node)
2213 {
2214         int side;
2215         mnode_t *front, *back, *other_node;
2216         mplane_t clipplane, *plane;
2217         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2218         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2219
2220         //      CheckLeafPortalConsistancy (node);
2221
2222         // if a leaf, we're done
2223         if (node->contents)
2224                 return;
2225
2226         plane = node->plane;
2227
2228         front = node->children[0];
2229         back = node->children[1];
2230         if (front == back)
2231                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2232
2233         // create the new portal by generating a polygon for the node plane,
2234         // and clipping it by all of the other portals (which came from nodes above this one)
2235         nodeportal = AllocPortal ();
2236         nodeportal->plane = *node->plane;
2237
2238         nodeportalwinding = BaseWindingForPlane (node->plane);
2239         Mem_CheckSentinels(nodeportalwinding);
2240         side = 0;       // shut up compiler warning
2241         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2242         {
2243                 clipplane = portal->plane;
2244                 if (portal->nodes[0] == portal->nodes[1])
2245                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2246                 if (portal->nodes[0] == node)
2247                         side = 0;
2248                 else if (portal->nodes[1] == node)
2249                 {
2250                         clipplane.dist = -clipplane.dist;
2251                         VectorNegate (clipplane.normal, clipplane.normal);
2252                         side = 1;
2253                 }
2254                 else
2255                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2256
2257                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2258                 if (!nodeportalwinding)
2259                 {
2260                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2261                         break;
2262                 }
2263         }
2264
2265         if (nodeportalwinding)
2266         {
2267                 // if the plane was not clipped on all sides, there was an error
2268                 nodeportal->winding = nodeportalwinding;
2269                 AddPortalToNodes (nodeportal, front, back);
2270         }
2271
2272         // split the portals of this node along this node's plane and assign them to the children of this node
2273         // (migrating the portals downward through the tree)
2274         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2275         {
2276                 if (portal->nodes[0] == portal->nodes[1])
2277                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2278                 if (portal->nodes[0] == node)
2279                         side = 0;
2280                 else if (portal->nodes[1] == node)
2281                         side = 1;
2282                 else
2283                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2284                 nextportal = portal->next[side];
2285
2286                 other_node = portal->nodes[!side];
2287                 RemovePortalFromNodes (portal);
2288
2289                 // cut the portal into two portals, one on each side of the node plane
2290                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2291
2292                 if (!frontwinding)
2293                 {
2294                         if (side == 0)
2295                                 AddPortalToNodes (portal, back, other_node);
2296                         else
2297                                 AddPortalToNodes (portal, other_node, back);
2298                         continue;
2299                 }
2300                 if (!backwinding)
2301                 {
2302                         if (side == 0)
2303                                 AddPortalToNodes (portal, front, other_node);
2304                         else
2305                                 AddPortalToNodes (portal, other_node, front);
2306                         continue;
2307                 }
2308
2309                 // the winding is split
2310                 splitportal = AllocPortal ();
2311                 temp = splitportal->chain;
2312                 *splitportal = *portal;
2313                 splitportal->chain = temp;
2314                 splitportal->winding = backwinding;
2315                 FreeWinding (portal->winding);
2316                 portal->winding = frontwinding;
2317
2318                 if (side == 0)
2319                 {
2320                         AddPortalToNodes (portal, front, other_node);
2321                         AddPortalToNodes (splitportal, back, other_node);
2322                 }
2323                 else
2324                 {
2325                         AddPortalToNodes (portal, other_node, front);
2326                         AddPortalToNodes (splitportal, other_node, back);
2327                 }
2328         }
2329
2330         Mod_RecursiveNodePortals(front);
2331         Mod_RecursiveNodePortals(back);
2332 }
2333
2334 /*
2335 void Mod_MakeOutsidePortals(mnode_t *node)
2336 {
2337         int                     i, j;
2338         portal_t        *p, *portals[6];
2339         mnode_t         *outside_node;
2340
2341         outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2342         outside_node->contents = CONTENTS_SOLID;
2343         outside_node->portals = NULL;
2344
2345         for (i = 0;i < 3;i++)
2346         {
2347                 for (j = 0;j < 2;j++)
2348                 {
2349                         portals[j*3 + i] = p = AllocPortal ();
2350                         memset (&p->plane, 0, sizeof(mplane_t));
2351                         p->plane.normal[i] = j ? -1 : 1;
2352                         p->plane.dist = -65536;
2353                         p->winding = BaseWindingForPlane (&p->plane);
2354                         if (j)
2355                                 AddPortalToNodes (p, outside_node, node);
2356                         else
2357                                 AddPortalToNodes (p, node, outside_node);
2358                 }
2359         }
2360
2361         // clip the basewindings by all the other planes
2362         for (i = 0;i < 6;i++)
2363         {
2364                 for (j = 0;j < 6;j++)
2365                 {
2366                         if (j == i)
2367                                 continue;
2368                         portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2369                 }
2370         }
2371 }
2372 */
2373
2374 static void Mod_MakePortals(void)
2375 {
2376 //      Con_Printf("building portals for %s\n", loadmodel->name);
2377
2378         portalchain = NULL;
2379 //      Mod_MakeOutsidePortals (loadmodel->nodes);
2380         Mod_RecursiveNodePortals (loadmodel->nodes);
2381         Mod_FinalizePortals();
2382 }
2383
2384 /*
2385 =================
2386 Mod_LoadBrushModel
2387 =================
2388 */
2389 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2390 {
2391         int                     i, j;
2392         dheader_t       *header;
2393         dmodel_t        *bm;
2394         mempool_t       *mainmempool;
2395
2396         mod->type = mod_brush;
2397
2398         header = (dheader_t *)buffer;
2399
2400         i = LittleLong (header->version);
2401         if (i != BSPVERSION && i != 30)
2402                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2403         mod->ishlbsp = i == 30;
2404         if (loadmodel->isworldmodel)
2405                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2406
2407 // swap all the lumps
2408         mod_base = (byte *)header;
2409
2410         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2411                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2412
2413 // load into heap
2414
2415         // store which lightmap format to use
2416         mod->lightmaprgba = r_lightmaprgba.integer;
2417
2418 //      Mem_CheckSentinelsGlobal();
2419         // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
2420         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2421 //      Mem_CheckSentinelsGlobal();
2422         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2423 //      Mem_CheckSentinelsGlobal();
2424         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2425 //      Mem_CheckSentinelsGlobal();
2426         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2427 //      Mem_CheckSentinelsGlobal();
2428         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2429 //      Mem_CheckSentinelsGlobal();
2430         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2431 //      Mem_CheckSentinelsGlobal();
2432         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2433 //      Mem_CheckSentinelsGlobal();
2434         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2435 //      Mem_CheckSentinelsGlobal();
2436         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2437 //      Mem_CheckSentinelsGlobal();
2438         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2439 //      Mem_CheckSentinelsGlobal();
2440         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2441 //      Mem_CheckSentinelsGlobal();
2442         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2443 //      Mem_CheckSentinelsGlobal();
2444         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2445 //      Mem_CheckSentinelsGlobal();
2446         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2447 //      Mem_CheckSentinelsGlobal();
2448 //      Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2449         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2450 //      Mem_CheckSentinelsGlobal();
2451
2452         Mod_MakeHull0 ();
2453 //      Mem_CheckSentinelsGlobal();
2454         Mod_MakePortals();
2455 //      Mem_CheckSentinelsGlobal();
2456
2457         mod->numframes = 2;             // regular and alternate animation
2458
2459         mainmempool = mod->mempool;
2460
2461 //
2462 // set up the submodels (FIXME: this is confusing)
2463 //
2464         for (i = 0;i < mod->numsubmodels;i++)
2465         {
2466                 int k, l;
2467                 float dist, modelyawradius, modelradius, *vec;
2468                 msurface_t *surf;
2469
2470                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2471                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2472                 modelyawradius = 0;
2473                 modelradius = 0;
2474
2475                 bm = &mod->submodels[i];
2476
2477                 mod->hulls[0].firstclipnode = bm->headnode[0];
2478                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2479                 {
2480                         mod->hulls[j].firstclipnode = bm->headnode[j];
2481                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2482                 }
2483
2484                 mod->firstmodelsurface = bm->firstface;
2485                 mod->nummodelsurfaces = bm->numfaces;
2486
2487                 mod->DrawSky = NULL;
2488                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2489                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2490                 {
2491                         // we only need to have a drawsky function if it is used (usually only on world model)
2492                         if (surf->shader == &Cshader_sky)
2493                                 mod->DrawSky = R_DrawBrushModelSky;
2494                         for (k = 0;k < surf->numedges;k++)
2495                         {
2496                                 l = mod->surfedges[k + surf->firstedge];
2497                                 if (l > 0)
2498                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2499                                 else
2500                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2501                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2502                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2503                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2504                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2505                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2506                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2507                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2508                                 if (modelyawradius < dist)
2509                                         modelyawradius = dist;
2510                                 dist += vec[2]*vec[2];
2511                                 if (modelradius < dist)
2512                                         modelradius = dist;
2513                         }
2514                 }
2515                 modelyawradius = sqrt(modelyawradius);
2516                 modelradius = sqrt(modelradius);
2517                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2518                 mod->yawmins[2] = mod->normalmins[2];
2519                 mod->yawmaxs[2] = mod->normalmaxs[2];
2520                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2521                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2522 //              mod->modelradius = modelradius;
2523
2524 //              VectorCopy (bm->maxs, mod->maxs);
2525 //              VectorCopy (bm->mins, mod->mins);
2526
2527 //              mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2528
2529                 mod->numleafs = bm->visleafs;
2530
2531                 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2532                 mod->Draw = R_DrawBrushModelNormal;
2533                 mod->DrawShadow = NULL;
2534
2535                 Mod_BrushSortedSurfaces(mod, mainmempool);
2536
2537                 // LordHavoc: only register submodels if it is the world
2538                 // (prevents bsp models from replacing world submodels)
2539                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2540                 {
2541                         char    name[10];
2542                         // duplicate the basic information
2543                         sprintf (name, "*%i", i+1);
2544                         loadmodel = Mod_FindName (name);
2545                         *loadmodel = *mod;
2546                         strcpy (loadmodel->name, name);
2547                         // textures and memory belong to the main model
2548                         loadmodel->texturepool = NULL;
2549                         loadmodel->mempool = NULL;
2550                         mod = loadmodel;
2551                 }
2552         }
2553 //      Mem_CheckSentinelsGlobal();
2554 }