added cl_screen.c/h (eventually most 2D stuff should be moved here)
[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", "0"};
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
1221                 CalcSurfaceExtents (out);
1222
1223                 // lighting info
1224                 for (i = 0;i < MAXLIGHTMAPS;i++)
1225                         out->styles[i] = in->styles[i];
1226                 i = LittleLong(in->lightofs);
1227                 if (i == -1)
1228                         out->samples = NULL;
1229                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1230                         out->samples = loadmodel->lightdata + i;
1231                 else // LordHavoc: white lighting (bsp version 29)
1232                         out->samples = loadmodel->lightdata + (i * 3);
1233
1234                 Mod_GenerateSurfacePolygon(out);
1235
1236                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1237                 {
1238                         out->shader = &Cshader_sky;
1239                         out->samples = NULL;
1240                         Mod_GenerateWarpMesh (out);
1241                         continue;
1242                 }
1243
1244                 if (out->texinfo->texture->flags & SURF_DRAWTURB)
1245                 {
1246                         out->shader = &Cshader_water;
1247                         /*
1248                         for (i=0 ; i<2 ; i++)
1249                         {
1250                                 out->extents[i] = 16384*1024;
1251                                 out->texturemins[i] = -8192*1024;
1252                         }
1253                         */
1254                         out->samples = NULL;
1255                         Mod_GenerateWarpMesh (out);
1256                         continue;
1257                 }
1258
1259                 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1260                         out->flags |= SURF_CLIPSOLID;
1261                 if (out->texinfo->flags & TEX_SPECIAL)
1262                 {
1263                         // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1264                         out->shader = &Cshader_water;
1265                         out->samples = NULL;
1266                         Mod_GenerateWarpMesh (out);
1267                 }
1268                 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1269                 {
1270                         Con_Printf ("Bad surface extents, converting to fullbright polygon");
1271                         out->shader = &Cshader_wall_fullbright;
1272                         out->samples = NULL;
1273                         Mod_GenerateVertexMesh(out);
1274                 }
1275                 else if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1276                 {
1277                         out->shader = &Cshader_wall_vertex;
1278                         Mod_GenerateVertexLitMesh(out);
1279                 }
1280                 else
1281                 {
1282                         out->shader = &Cshader_wall_lightmap;
1283                         Mod_GenerateLightmappedMesh(out);
1284                 }
1285         }
1286 }
1287
1288 static model_t *sortmodel;
1289
1290 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1291 {
1292         const msurface_t *a, *b;
1293         a = *((const msurface_t **)voida);
1294         b = *((const msurface_t **)voidb);
1295         if (a->shader != b->shader)
1296                 return (long) a->shader - (long) b->shader;
1297         if (a->texinfo->texture != b->texinfo->texture);
1298                 return a->texinfo->texture - b->texinfo->texture;
1299         return 0;
1300 }
1301
1302 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1303 {
1304         int surfnum;
1305         sortmodel = model;
1306         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1307         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1308                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1309
1310         qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1311 }
1312
1313
1314 /*
1315 =================
1316 Mod_SetParent
1317 =================
1318 */
1319 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1320 {
1321         node->parent = parent;
1322         if (node->contents < 0)
1323                 return;
1324         Mod_SetParent (node->children[0], node);
1325         Mod_SetParent (node->children[1], node);
1326 }
1327
1328 /*
1329 =================
1330 Mod_LoadNodes
1331 =================
1332 */
1333 static void Mod_LoadNodes (lump_t *l)
1334 {
1335         int                     i, j, count, p;
1336         dnode_t         *in;
1337         mnode_t         *out;
1338
1339         in = (void *)(mod_base + l->fileofs);
1340         if (l->filelen % sizeof(*in))
1341                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1342         count = l->filelen / sizeof(*in);
1343         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1344
1345         loadmodel->nodes = out;
1346         loadmodel->numnodes = count;
1347
1348         for ( i=0 ; i<count ; i++, in++, out++)
1349         {
1350                 for (j=0 ; j<3 ; j++)
1351                 {
1352                         out->mins[j] = LittleShort (in->mins[j]);
1353                         out->maxs[j] = LittleShort (in->maxs[j]);
1354                 }
1355
1356                 p = LittleLong(in->planenum);
1357                 out->plane = loadmodel->planes + p;
1358
1359                 out->firstsurface = LittleShort (in->firstface);
1360                 out->numsurfaces = LittleShort (in->numfaces);
1361
1362                 for (j=0 ; j<2 ; j++)
1363                 {
1364                         p = LittleShort (in->children[j]);
1365                         if (p >= 0)
1366                                 out->children[j] = loadmodel->nodes + p;
1367                         else
1368                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1369                 }
1370         }
1371
1372         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1373 }
1374
1375 /*
1376 =================
1377 Mod_LoadLeafs
1378 =================
1379 */
1380 static void Mod_LoadLeafs (lump_t *l)
1381 {
1382         dleaf_t         *in;
1383         mleaf_t         *out;
1384         int                     i, j, count, p;
1385
1386         in = (void *)(mod_base + l->fileofs);
1387         if (l->filelen % sizeof(*in))
1388                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1389         count = l->filelen / sizeof(*in);
1390         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1391
1392         loadmodel->leafs = out;
1393         loadmodel->numleafs = count;
1394
1395         for ( i=0 ; i<count ; i++, in++, out++)
1396         {
1397                 for (j=0 ; j<3 ; j++)
1398                 {
1399                         out->mins[j] = LittleShort (in->mins[j]);
1400                         out->maxs[j] = LittleShort (in->maxs[j]);
1401                 }
1402
1403                 p = LittleLong(in->contents);
1404                 out->contents = p;
1405
1406                 out->firstmarksurface = loadmodel->marksurfaces +
1407                         LittleShort(in->firstmarksurface);
1408                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1409
1410                 p = LittleLong(in->visofs);
1411                 if (p == -1)
1412                         out->compressed_vis = NULL;
1413                 else
1414                         out->compressed_vis = loadmodel->visdata + p;
1415
1416                 for (j=0 ; j<4 ; j++)
1417                         out->ambient_sound_level[j] = in->ambient_level[j];
1418
1419                 // gl underwater warp
1420                 // LordHavoc: disabled underwater warping
1421                 /*
1422                 if (out->contents != CONTENTS_EMPTY)
1423                 {
1424                         for (j=0 ; j<out->nummarksurfaces ; j++)
1425                                 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
1426                 }
1427                 */
1428         }
1429 }
1430
1431 /*
1432 =================
1433 Mod_LoadClipnodes
1434 =================
1435 */
1436 static void Mod_LoadClipnodes (lump_t *l)
1437 {
1438         dclipnode_t *in, *out;
1439         int                     i, count;
1440         hull_t          *hull;
1441
1442         in = (void *)(mod_base + l->fileofs);
1443         if (l->filelen % sizeof(*in))
1444                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1445         count = l->filelen / sizeof(*in);
1446         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1447
1448         loadmodel->clipnodes = out;
1449         loadmodel->numclipnodes = count;
1450
1451         if (loadmodel->ishlbsp)
1452         {
1453                 hull = &loadmodel->hulls[1];
1454                 hull->clipnodes = out;
1455                 hull->firstclipnode = 0;
1456                 hull->lastclipnode = count-1;
1457                 hull->planes = loadmodel->planes;
1458                 hull->clip_mins[0] = -16;
1459                 hull->clip_mins[1] = -16;
1460                 hull->clip_mins[2] = -36;
1461                 hull->clip_maxs[0] = 16;
1462                 hull->clip_maxs[1] = 16;
1463                 hull->clip_maxs[2] = 36;
1464
1465                 hull = &loadmodel->hulls[2];
1466                 hull->clipnodes = out;
1467                 hull->firstclipnode = 0;
1468                 hull->lastclipnode = count-1;
1469                 hull->planes = loadmodel->planes;
1470                 hull->clip_mins[0] = -32;
1471                 hull->clip_mins[1] = -32;
1472                 hull->clip_mins[2] = -32;
1473                 hull->clip_maxs[0] = 32;
1474                 hull->clip_maxs[1] = 32;
1475                 hull->clip_maxs[2] = 32;
1476
1477                 hull = &loadmodel->hulls[3];
1478                 hull->clipnodes = out;
1479                 hull->firstclipnode = 0;
1480                 hull->lastclipnode = count-1;
1481                 hull->planes = loadmodel->planes;
1482                 hull->clip_mins[0] = -16;
1483                 hull->clip_mins[1] = -16;
1484                 hull->clip_mins[2] = -18;
1485                 hull->clip_maxs[0] = 16;
1486                 hull->clip_maxs[1] = 16;
1487                 hull->clip_maxs[2] = 18;
1488         }
1489         else
1490         {
1491                 hull = &loadmodel->hulls[1];
1492                 hull->clipnodes = out;
1493                 hull->firstclipnode = 0;
1494                 hull->lastclipnode = count-1;
1495                 hull->planes = loadmodel->planes;
1496                 hull->clip_mins[0] = -16;
1497                 hull->clip_mins[1] = -16;
1498                 hull->clip_mins[2] = -24;
1499                 hull->clip_maxs[0] = 16;
1500                 hull->clip_maxs[1] = 16;
1501                 hull->clip_maxs[2] = 32;
1502
1503                 hull = &loadmodel->hulls[2];
1504                 hull->clipnodes = out;
1505                 hull->firstclipnode = 0;
1506                 hull->lastclipnode = count-1;
1507                 hull->planes = loadmodel->planes;
1508                 hull->clip_mins[0] = -32;
1509                 hull->clip_mins[1] = -32;
1510                 hull->clip_mins[2] = -24;
1511                 hull->clip_maxs[0] = 32;
1512                 hull->clip_maxs[1] = 32;
1513                 hull->clip_maxs[2] = 64;
1514         }
1515
1516         for (i=0 ; i<count ; i++, out++, in++)
1517         {
1518                 out->planenum = LittleLong(in->planenum);
1519                 out->children[0] = LittleShort(in->children[0]);
1520                 out->children[1] = LittleShort(in->children[1]);
1521                 if (out->children[0] >= count || out->children[1] >= count)
1522                         Host_Error("Corrupt clipping hull (out of range child)\n");
1523         }
1524 }
1525
1526 /*
1527 =================
1528 Mod_MakeHull0
1529
1530 Duplicate the drawing hull structure as a clipping hull
1531 =================
1532 */
1533 static void Mod_MakeHull0 (void)
1534 {
1535         mnode_t         *in;
1536         dclipnode_t *out;
1537         int                     i;
1538         hull_t          *hull;
1539
1540         hull = &loadmodel->hulls[0];
1541
1542         in = loadmodel->nodes;
1543         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1544
1545         hull->clipnodes = out;
1546         hull->firstclipnode = 0;
1547         hull->lastclipnode = loadmodel->numnodes - 1;
1548         hull->planes = loadmodel->planes;
1549
1550         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1551         {
1552                 out->planenum = in->plane - loadmodel->planes;
1553                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1554                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1555         }
1556 }
1557
1558 /*
1559 =================
1560 Mod_LoadMarksurfaces
1561 =================
1562 */
1563 static void Mod_LoadMarksurfaces (lump_t *l)
1564 {
1565         int             i, j;
1566         short   *in;
1567
1568         in = (void *)(mod_base + l->fileofs);
1569         if (l->filelen % sizeof(*in))
1570                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1571         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1572         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1573
1574         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1575         {
1576                 j = (unsigned) LittleShort(in[i]);
1577                 if (j >= loadmodel->numsurfaces)
1578                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1579                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1580         }
1581 }
1582
1583 /*
1584 =================
1585 Mod_LoadSurfedges
1586 =================
1587 */
1588 static void Mod_LoadSurfedges (lump_t *l)
1589 {
1590         int             i;
1591         int             *in;
1592
1593         in = (void *)(mod_base + l->fileofs);
1594         if (l->filelen % sizeof(*in))
1595                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1596         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1597         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1598
1599         for (i = 0;i < loadmodel->numsurfedges;i++)
1600                 loadmodel->surfedges[i] = LittleLong (in[i]);
1601 }
1602
1603
1604 /*
1605 =================
1606 Mod_LoadPlanes
1607 =================
1608 */
1609 static void Mod_LoadPlanes (lump_t *l)
1610 {
1611         int                     i;
1612         mplane_t        *out;
1613         dplane_t        *in;
1614
1615         in = (void *)(mod_base + l->fileofs);
1616         if (l->filelen % sizeof(*in))
1617                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1618
1619         loadmodel->numplanes = l->filelen / sizeof(*in);
1620         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1621
1622         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1623         {
1624                 out->normal[0] = LittleFloat (in->normal[0]);
1625                 out->normal[1] = LittleFloat (in->normal[1]);
1626                 out->normal[2] = LittleFloat (in->normal[2]);
1627                 out->dist = LittleFloat (in->dist);
1628
1629                 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1630 //              out->type = LittleLong (in->type);
1631                 PlaneClassify(out);
1632         }
1633 }
1634
1635 #define MAX_POINTS_ON_WINDING 64
1636
1637 typedef struct
1638 {
1639         int numpoints;
1640         int padding;
1641         double points[8][3]; // variable sized
1642 }
1643 winding_t;
1644
1645 /*
1646 ==================
1647 NewWinding
1648 ==================
1649 */
1650 static winding_t *NewWinding (int points)
1651 {
1652         winding_t *w;
1653         int size;
1654
1655         if (points > MAX_POINTS_ON_WINDING)
1656                 Sys_Error("NewWinding: too many points\n");
1657
1658         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1659         w = Mem_Alloc(loadmodel->mempool, size);
1660         memset (w, 0, size);
1661
1662         return w;
1663 }
1664
1665 static void FreeWinding (winding_t *w)
1666 {
1667         Mem_Free(w);
1668 }
1669
1670 /*
1671 =================
1672 BaseWindingForPlane
1673 =================
1674 */
1675 static winding_t *BaseWindingForPlane (mplane_t *p)
1676 {
1677         double org[3], vright[3], vup[3], normal[3];
1678         winding_t *w;
1679
1680         VectorCopy(p->normal, normal);
1681         VectorVectorsDouble(normal, vright, vup);
1682
1683         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1684         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1685
1686         // project a really big axis aligned box onto the plane
1687         w = NewWinding (4);
1688
1689         VectorScale (p->normal, p->dist, org);
1690
1691         VectorSubtract (org, vright, w->points[0]);
1692         VectorAdd (w->points[0], vup, w->points[0]);
1693
1694         VectorAdd (org, vright, w->points[1]);
1695         VectorAdd (w->points[1], vup, w->points[1]);
1696
1697         VectorAdd (org, vright, w->points[2]);
1698         VectorSubtract (w->points[2], vup, w->points[2]);
1699
1700         VectorSubtract (org, vright, w->points[3]);
1701         VectorSubtract (w->points[3], vup, w->points[3]);
1702
1703         w->numpoints = 4;
1704
1705         return w;
1706 }
1707
1708 /*
1709 ==================
1710 ClipWinding
1711
1712 Clips the winding to the plane, returning the new winding on the positive side
1713 Frees the input winding.
1714 If keepon is true, an exactly on-plane winding will be saved, otherwise
1715 it will be clipped away.
1716 ==================
1717 */
1718 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1719 {
1720         double  dists[MAX_POINTS_ON_WINDING + 1];
1721         int             sides[MAX_POINTS_ON_WINDING + 1];
1722         int             counts[3];
1723         double  dot;
1724         int             i, j;
1725         double  *p1, *p2;
1726         double  mid[3];
1727         winding_t       *neww;
1728         int             maxpts;
1729
1730         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1731
1732         // determine sides for each point
1733         for (i = 0;i < in->numpoints;i++)
1734         {
1735                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1736                 if (dot > ON_EPSILON)
1737                         sides[i] = SIDE_FRONT;
1738                 else if (dot < -ON_EPSILON)
1739                         sides[i] = SIDE_BACK;
1740                 else
1741                         sides[i] = SIDE_ON;
1742                 counts[sides[i]]++;
1743         }
1744         sides[i] = sides[0];
1745         dists[i] = dists[0];
1746
1747         if (keepon && !counts[0] && !counts[1])
1748                 return in;
1749
1750         if (!counts[0])
1751         {
1752                 FreeWinding (in);
1753                 return NULL;
1754         }
1755         if (!counts[1])
1756                 return in;
1757
1758         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1759         if (maxpts > MAX_POINTS_ON_WINDING)
1760                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1761
1762         neww = NewWinding (maxpts);
1763
1764         for (i = 0;i < in->numpoints;i++)
1765         {
1766                 if (neww->numpoints >= maxpts)
1767                         Sys_Error ("ClipWinding: points exceeded estimate");
1768
1769                 p1 = in->points[i];
1770
1771                 if (sides[i] == SIDE_ON)
1772                 {
1773                         VectorCopy (p1, neww->points[neww->numpoints]);
1774                         neww->numpoints++;
1775                         continue;
1776                 }
1777
1778                 if (sides[i] == SIDE_FRONT)
1779                 {
1780                         VectorCopy (p1, neww->points[neww->numpoints]);
1781                         neww->numpoints++;
1782                 }
1783
1784                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1785                         continue;
1786
1787                 // generate a split point
1788                 p2 = in->points[(i+1)%in->numpoints];
1789
1790                 dot = dists[i] / (dists[i]-dists[i+1]);
1791                 for (j = 0;j < 3;j++)
1792                 {       // avoid round off error when possible
1793                         if (split->normal[j] == 1)
1794                                 mid[j] = split->dist;
1795                         else if (split->normal[j] == -1)
1796                                 mid[j] = -split->dist;
1797                         else
1798                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1799                 }
1800
1801                 VectorCopy (mid, neww->points[neww->numpoints]);
1802                 neww->numpoints++;
1803         }
1804
1805         // free the original winding
1806         FreeWinding (in);
1807
1808         // debugging
1809         //Mem_CheckSentinels(neww);
1810
1811         return neww;
1812 }
1813
1814
1815 /*
1816 ==================
1817 DivideWinding
1818
1819 Divides a winding by a plane, producing one or two windings.  The
1820 original winding is not damaged or freed.  If only on one side, the
1821 returned winding will be the input winding.  If on both sides, two
1822 new windings will be created.
1823 ==================
1824 */
1825 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1826 {
1827         double  dists[MAX_POINTS_ON_WINDING + 1];
1828         int             sides[MAX_POINTS_ON_WINDING + 1];
1829         int             counts[3];
1830         double  dot;
1831         int             i, j;
1832         double  *p1, *p2;
1833         double  mid[3];
1834         winding_t       *f, *b;
1835         int             maxpts;
1836
1837         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1838
1839         // determine sides for each point
1840         for (i = 0;i < in->numpoints;i++)
1841         {
1842                 dot = DotProduct (in->points[i], split->normal);
1843                 dot -= split->dist;
1844                 dists[i] = dot;
1845                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1846                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1847                 else sides[i] = SIDE_ON;
1848                 counts[sides[i]]++;
1849         }
1850         sides[i] = sides[0];
1851         dists[i] = dists[0];
1852
1853         *front = *back = NULL;
1854
1855         if (!counts[0])
1856         {
1857                 *back = in;
1858                 return;
1859         }
1860         if (!counts[1])
1861         {
1862                 *front = in;
1863                 return;
1864         }
1865
1866         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1867
1868         if (maxpts > MAX_POINTS_ON_WINDING)
1869                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1870
1871         *front = f = NewWinding (maxpts);
1872         *back = b = NewWinding (maxpts);
1873
1874         for (i = 0;i < in->numpoints;i++)
1875         {
1876                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1877                         Sys_Error ("DivideWinding: points exceeded estimate");
1878
1879                 p1 = in->points[i];
1880
1881                 if (sides[i] == SIDE_ON)
1882                 {
1883                         VectorCopy (p1, f->points[f->numpoints]);
1884                         f->numpoints++;
1885                         VectorCopy (p1, b->points[b->numpoints]);
1886                         b->numpoints++;
1887                         continue;
1888                 }
1889
1890                 if (sides[i] == SIDE_FRONT)
1891                 {
1892                         VectorCopy (p1, f->points[f->numpoints]);
1893                         f->numpoints++;
1894                 }
1895                 else if (sides[i] == SIDE_BACK)
1896                 {
1897                         VectorCopy (p1, b->points[b->numpoints]);
1898                         b->numpoints++;
1899                 }
1900
1901                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1902                         continue;
1903
1904                 // generate a split point
1905                 p2 = in->points[(i+1)%in->numpoints];
1906
1907                 dot = dists[i] / (dists[i]-dists[i+1]);
1908                 for (j = 0;j < 3;j++)
1909                 {       // avoid round off error when possible
1910                         if (split->normal[j] == 1)
1911                                 mid[j] = split->dist;
1912                         else if (split->normal[j] == -1)
1913                                 mid[j] = -split->dist;
1914                         else
1915                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1916                 }
1917
1918                 VectorCopy (mid, f->points[f->numpoints]);
1919                 f->numpoints++;
1920                 VectorCopy (mid, b->points[b->numpoints]);
1921                 b->numpoints++;
1922         }
1923
1924         // debugging
1925         //Mem_CheckSentinels(f);
1926         //Mem_CheckSentinels(b);
1927 }
1928
1929 typedef struct portal_s
1930 {
1931         mplane_t plane;
1932         mnode_t *nodes[2];              // [0] = front side of plane
1933         struct portal_s *next[2];
1934         winding_t *winding;
1935         struct portal_s *chain; // all portals are linked into a list
1936 }
1937 portal_t;
1938
1939 static portal_t *portalchain;
1940
1941 /*
1942 ===========
1943 AllocPortal
1944 ===========
1945 */
1946 static portal_t *AllocPortal (void)
1947 {
1948         portal_t *p;
1949         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1950         //memset(p, 0, sizeof(portal_t));
1951         p->chain = portalchain;
1952         portalchain = p;
1953         return p;
1954 }
1955
1956 static void FreePortal(portal_t *p)
1957 {
1958         Mem_Free(p);
1959 }
1960
1961 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1962 {
1963         // calculate children first
1964         if (node->children[0]->contents >= 0)
1965                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1966         if (node->children[1]->contents >= 0)
1967                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1968
1969         // make combined bounding box from children
1970         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1971         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1972         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1973         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1974         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
1975         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
1976 }
1977
1978 static void Mod_FinalizePortals(void)
1979 {
1980         int i, j, numportals, numpoints;
1981         portal_t *p, *pnext;
1982         mportal_t *portal;
1983         mvertex_t *point;
1984         mleaf_t *leaf, *endleaf;
1985         winding_t *w;
1986
1987         //Mem_CheckSentinelsGlobal();
1988
1989         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1990         leaf = loadmodel->leafs;
1991         endleaf = leaf + loadmodel->numleafs;
1992         for (;leaf < endleaf;leaf++)
1993         {
1994                 VectorSet( 2000000000,  2000000000,  2000000000, leaf->mins);
1995                 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1996         }
1997         p = portalchain;
1998         while(p)
1999         {
2000                 if (p->winding)
2001                 {
2002                         for (i = 0;i < 2;i++)
2003                         {
2004                                 leaf = (mleaf_t *)p->nodes[i];
2005                                 w = p->winding;
2006                                 for (j = 0;j < w->numpoints;j++)
2007                                 {
2008                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2009                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2010                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2011                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2012                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2013                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2014                                 }
2015                         }
2016                 }
2017                 p = p->chain;
2018         }
2019
2020         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2021
2022         //Mem_CheckSentinelsGlobal();
2023
2024         // tally up portal and point counts
2025         p = portalchain;
2026         numportals = 0;
2027         numpoints = 0;
2028         while(p)
2029         {
2030                 // note: this check must match the one below or it will usually corrupt memory
2031                 // 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
2032                 if (p->winding && p->nodes[0] != p->nodes[1]
2033                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2034                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2035                 {
2036                         numportals += 2;
2037                         numpoints += p->winding->numpoints * 2;
2038                 }
2039                 p = p->chain;
2040         }
2041         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2042         loadmodel->numportals = numportals;
2043         loadmodel->portalpoints = (void *) ((long) loadmodel->portals + numportals * sizeof(mportal_t));
2044         loadmodel->numportalpoints = numpoints;
2045         // clear all leaf portal chains
2046         for (i = 0;i < loadmodel->numleafs;i++)
2047                 loadmodel->leafs[i].portals = NULL;
2048         // process all portals in the global portal chain, while freeing them
2049         portal = loadmodel->portals;
2050         point = loadmodel->portalpoints;
2051         p = portalchain;
2052         portalchain = NULL;
2053         while (p)
2054         {
2055                 pnext = p->chain;
2056
2057                 if (p->winding)
2058                 {
2059                         // note: this check must match the one above or it will usually corrupt memory
2060                         // 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
2061                         if (p->nodes[0] != p->nodes[1]
2062                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2063                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2064                         {
2065                                 // first make the back to front portal (forward portal)
2066                                 portal->points = point;
2067                                 portal->numpoints = p->winding->numpoints;
2068                                 portal->plane.dist = p->plane.dist;
2069                                 VectorCopy(p->plane.normal, portal->plane.normal);
2070                                 portal->here = (mleaf_t *)p->nodes[1];
2071                                 portal->past = (mleaf_t *)p->nodes[0];
2072                                 // copy points
2073                                 for (j = 0;j < portal->numpoints;j++)
2074                                 {
2075                                         VectorCopy(p->winding->points[j], point->position);
2076                                         point++;
2077                                 }
2078                                 PlaneClassify(&portal->plane);
2079
2080                                 // link into leaf's portal chain
2081                                 portal->next = portal->here->portals;
2082                                 portal->here->portals = portal;
2083
2084                                 // advance to next portal
2085                                 portal++;
2086
2087                                 // then make the front to back portal (backward portal)
2088                                 portal->points = point;
2089                                 portal->numpoints = p->winding->numpoints;
2090                                 portal->plane.dist = -p->plane.dist;
2091                                 VectorNegate(p->plane.normal, portal->plane.normal);
2092                                 portal->here = (mleaf_t *)p->nodes[0];
2093                                 portal->past = (mleaf_t *)p->nodes[1];
2094                                 // copy points
2095                                 for (j = portal->numpoints - 1;j >= 0;j--)
2096                                 {
2097                                         VectorCopy(p->winding->points[j], point->position);
2098                                         point++;
2099                                 }
2100                                 PlaneClassify(&portal->plane);
2101
2102                                 // link into leaf's portal chain
2103                                 portal->next = portal->here->portals;
2104                                 portal->here->portals = portal;
2105
2106                                 // advance to next portal
2107                                 portal++;
2108                         }
2109                         FreeWinding(p->winding);
2110                 }
2111                 FreePortal(p);
2112                 p = pnext;
2113         }
2114
2115         //Mem_CheckSentinelsGlobal();
2116 }
2117
2118 /*
2119 =============
2120 AddPortalToNodes
2121 =============
2122 */
2123 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2124 {
2125         if (!front)
2126                 Host_Error ("AddPortalToNodes: NULL front node");
2127         if (!back)
2128                 Host_Error ("AddPortalToNodes: NULL back node");
2129         if (p->nodes[0] || p->nodes[1])
2130                 Host_Error ("AddPortalToNodes: already included");
2131         // 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
2132
2133         p->nodes[0] = front;
2134         p->next[0] = (portal_t *)front->portals;
2135         front->portals = (mportal_t *)p;
2136
2137         p->nodes[1] = back;
2138         p->next[1] = (portal_t *)back->portals;
2139         back->portals = (mportal_t *)p;
2140 }
2141
2142 /*
2143 =============
2144 RemovePortalFromNode
2145 =============
2146 */
2147 static void RemovePortalFromNodes(portal_t *portal)
2148 {
2149         int i;
2150         mnode_t *node;
2151         void **portalpointer;
2152         portal_t *t;
2153         for (i = 0;i < 2;i++)
2154         {
2155                 node = portal->nodes[i];
2156
2157                 portalpointer = (void **) &node->portals;
2158                 while (1)
2159                 {
2160                         t = *portalpointer;
2161                         if (!t)
2162                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2163
2164                         if (t == portal)
2165                         {
2166                                 if (portal->nodes[0] == node)
2167                                 {
2168                                         *portalpointer = portal->next[0];
2169                                         portal->nodes[0] = NULL;
2170                                 }
2171                                 else if (portal->nodes[1] == node)
2172                                 {
2173                                         *portalpointer = portal->next[1];
2174                                         portal->nodes[1] = NULL;
2175                                 }
2176                                 else
2177                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2178                                 break;
2179                         }
2180
2181                         if (t->nodes[0] == node)
2182                                 portalpointer = (void **) &t->next[0];
2183                         else if (t->nodes[1] == node)
2184                                 portalpointer = (void **) &t->next[1];
2185                         else
2186                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2187                 }
2188         }
2189 }
2190
2191 static void Mod_RecursiveNodePortals (mnode_t *node)
2192 {
2193         int side;
2194         mnode_t *front, *back, *other_node;
2195         mplane_t clipplane, *plane;
2196         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2197         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2198
2199         //      CheckLeafPortalConsistancy (node);
2200
2201         // if a leaf, we're done
2202         if (node->contents)
2203                 return;
2204
2205         plane = node->plane;
2206
2207         front = node->children[0];
2208         back = node->children[1];
2209         if (front == back)
2210                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2211
2212         // create the new portal by generating a polygon for the node plane,
2213         // and clipping it by all of the other portals (which came from nodes above this one)
2214         nodeportal = AllocPortal ();
2215         nodeportal->plane = *node->plane;
2216
2217         nodeportalwinding = BaseWindingForPlane (node->plane);
2218         //Mem_CheckSentinels(nodeportalwinding);
2219         side = 0;       // shut up compiler warning
2220         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2221         {
2222                 clipplane = portal->plane;
2223                 if (portal->nodes[0] == portal->nodes[1])
2224                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2225                 if (portal->nodes[0] == node)
2226                         side = 0;
2227                 else if (portal->nodes[1] == node)
2228                 {
2229                         clipplane.dist = -clipplane.dist;
2230                         VectorNegate (clipplane.normal, clipplane.normal);
2231                         side = 1;
2232                 }
2233                 else
2234                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2235
2236                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2237                 if (!nodeportalwinding)
2238                 {
2239                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2240                         break;
2241                 }
2242         }
2243
2244         if (nodeportalwinding)
2245         {
2246                 // if the plane was not clipped on all sides, there was an error
2247                 nodeportal->winding = nodeportalwinding;
2248                 AddPortalToNodes (nodeportal, front, back);
2249         }
2250
2251         // split the portals of this node along this node's plane and assign them to the children of this node
2252         // (migrating the portals downward through the tree)
2253         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2254         {
2255                 if (portal->nodes[0] == portal->nodes[1])
2256                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2257                 if (portal->nodes[0] == node)
2258                         side = 0;
2259                 else if (portal->nodes[1] == node)
2260                         side = 1;
2261                 else
2262                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2263                 nextportal = portal->next[side];
2264
2265                 other_node = portal->nodes[!side];
2266                 RemovePortalFromNodes (portal);
2267
2268                 // cut the portal into two portals, one on each side of the node plane
2269                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2270
2271                 if (!frontwinding)
2272                 {
2273                         if (side == 0)
2274                                 AddPortalToNodes (portal, back, other_node);
2275                         else
2276                                 AddPortalToNodes (portal, other_node, back);
2277                         continue;
2278                 }
2279                 if (!backwinding)
2280                 {
2281                         if (side == 0)
2282                                 AddPortalToNodes (portal, front, other_node);
2283                         else
2284                                 AddPortalToNodes (portal, other_node, front);
2285                         continue;
2286                 }
2287
2288                 // the winding is split
2289                 splitportal = AllocPortal ();
2290                 temp = splitportal->chain;
2291                 *splitportal = *portal;
2292                 splitportal->chain = temp;
2293                 splitportal->winding = backwinding;
2294                 FreeWinding (portal->winding);
2295                 portal->winding = frontwinding;
2296
2297                 if (side == 0)
2298                 {
2299                         AddPortalToNodes (portal, front, other_node);
2300                         AddPortalToNodes (splitportal, back, other_node);
2301                 }
2302                 else
2303                 {
2304                         AddPortalToNodes (portal, other_node, front);
2305                         AddPortalToNodes (splitportal, other_node, back);
2306                 }
2307         }
2308
2309         Mod_RecursiveNodePortals(front);
2310         Mod_RecursiveNodePortals(back);
2311 }
2312
2313 /*
2314 void Mod_MakeOutsidePortals(mnode_t *node)
2315 {
2316         int                     i, j;
2317         portal_t        *p, *portals[6];
2318         mnode_t         *outside_node;
2319
2320         outside_node = Mem_Alloc(loadmodel->mempool, sizeof(mnode_t));
2321         outside_node->contents = CONTENTS_SOLID;
2322         outside_node->portals = NULL;
2323
2324         for (i = 0;i < 3;i++)
2325         {
2326                 for (j = 0;j < 2;j++)
2327                 {
2328                         portals[j*3 + i] = p = AllocPortal ();
2329                         memset (&p->plane, 0, sizeof(mplane_t));
2330                         p->plane.normal[i] = j ? -1 : 1;
2331                         p->plane.dist = -65536;
2332                         p->winding = BaseWindingForPlane (&p->plane);
2333                         if (j)
2334                                 AddPortalToNodes (p, outside_node, node);
2335                         else
2336                                 AddPortalToNodes (p, node, outside_node);
2337                 }
2338         }
2339
2340         // clip the basewindings by all the other planes
2341         for (i = 0;i < 6;i++)
2342         {
2343                 for (j = 0;j < 6;j++)
2344                 {
2345                         if (j == i)
2346                                 continue;
2347                         portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
2348                 }
2349         }
2350 }
2351 */
2352
2353 static void Mod_MakePortals(void)
2354 {
2355 //      Con_Printf("building portals for %s\n", loadmodel->name);
2356
2357         portalchain = NULL;
2358 //      Mod_MakeOutsidePortals (loadmodel->nodes);
2359         Mod_RecursiveNodePortals (loadmodel->nodes);
2360         Mod_FinalizePortals();
2361 }
2362
2363 /*
2364 =================
2365 Mod_LoadBrushModel
2366 =================
2367 */
2368 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2369 {
2370         int                     i, j;
2371         dheader_t       *header;
2372         dmodel_t        *bm;
2373         mempool_t       *mainmempool;
2374
2375         mod->type = mod_brush;
2376
2377         header = (dheader_t *)buffer;
2378
2379         i = LittleLong (header->version);
2380         if (i != BSPVERSION && i != 30)
2381                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
2382         mod->ishlbsp = i == 30;
2383         if (loadmodel->isworldmodel)
2384                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2385
2386 // swap all the lumps
2387         mod_base = (byte *)header;
2388
2389         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2390                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2391
2392 // load into heap
2393
2394         // store which lightmap format to use
2395         mod->lightmaprgba = r_lightmaprgba.integer;
2396
2397 //      Mem_CheckSentinelsGlobal();
2398         // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
2399         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2400 //      Mem_CheckSentinelsGlobal();
2401         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2402 //      Mem_CheckSentinelsGlobal();
2403         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2404 //      Mem_CheckSentinelsGlobal();
2405         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2406 //      Mem_CheckSentinelsGlobal();
2407         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2408 //      Mem_CheckSentinelsGlobal();
2409         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2410 //      Mem_CheckSentinelsGlobal();
2411         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2412 //      Mem_CheckSentinelsGlobal();
2413         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2414 //      Mem_CheckSentinelsGlobal();
2415         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2416 //      Mem_CheckSentinelsGlobal();
2417         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2418 //      Mem_CheckSentinelsGlobal();
2419         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2420 //      Mem_CheckSentinelsGlobal();
2421         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2422 //      Mem_CheckSentinelsGlobal();
2423         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2424 //      Mem_CheckSentinelsGlobal();
2425         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2426 //      Mem_CheckSentinelsGlobal();
2427 //      Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2428         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2429 //      Mem_CheckSentinelsGlobal();
2430
2431         Mod_MakeHull0 ();
2432 //      Mem_CheckSentinelsGlobal();
2433         Mod_MakePortals();
2434 //      Mem_CheckSentinelsGlobal();
2435
2436         mod->numframes = 2;             // regular and alternate animation
2437
2438         mainmempool = mod->mempool;
2439
2440 //
2441 // set up the submodels (FIXME: this is confusing)
2442 //
2443         for (i = 0;i < mod->numsubmodels;i++)
2444         {
2445                 int k, l;
2446                 float dist, modelyawradius, modelradius, *vec;
2447                 msurface_t *surf;
2448
2449                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2450                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2451                 modelyawradius = 0;
2452                 modelradius = 0;
2453
2454                 bm = &mod->submodels[i];
2455
2456                 mod->hulls[0].firstclipnode = bm->headnode[0];
2457                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2458                 {
2459                         mod->hulls[j].firstclipnode = bm->headnode[j];
2460                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2461                 }
2462
2463                 mod->firstmodelsurface = bm->firstface;
2464                 mod->nummodelsurfaces = bm->numfaces;
2465
2466                 mod->DrawSky = NULL;
2467                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2468                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2469                 {
2470                         // we only need to have a drawsky function if it is used (usually only on world model)
2471                         if (surf->shader == &Cshader_sky)
2472                                 mod->DrawSky = R_DrawBrushModelSky;
2473                         for (k = 0;k < surf->numedges;k++)
2474                         {
2475                                 l = mod->surfedges[k + surf->firstedge];
2476                                 if (l > 0)
2477                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2478                                 else
2479                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2480                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2481                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2482                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2483                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2484                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2485                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2486                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2487                                 if (modelyawradius < dist)
2488                                         modelyawradius = dist;
2489                                 dist += vec[2]*vec[2];
2490                                 if (modelradius < dist)
2491                                         modelradius = dist;
2492                         }
2493                 }
2494                 modelyawradius = sqrt(modelyawradius);
2495                 modelradius = sqrt(modelradius);
2496                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2497                 mod->yawmins[2] = mod->normalmins[2];
2498                 mod->yawmaxs[2] = mod->normalmaxs[2];
2499                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2500                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2501 //              mod->modelradius = modelradius;
2502
2503 //              VectorCopy (bm->maxs, mod->maxs);
2504 //              VectorCopy (bm->mins, mod->mins);
2505
2506 //              mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
2507
2508                 mod->numleafs = bm->visleafs;
2509
2510                 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2511                 mod->Draw = R_DrawBrushModelNormal;
2512                 mod->DrawShadow = NULL;
2513
2514                 Mod_BrushSortedSurfaces(mod, mainmempool);
2515
2516                 // LordHavoc: only register submodels if it is the world
2517                 // (prevents bsp models from replacing world submodels)
2518                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2519                 {
2520                         char    name[10];
2521                         // duplicate the basic information
2522                         sprintf (name, "*%i", i+1);
2523                         loadmodel = Mod_FindName (name);
2524                         *loadmodel = *mod;
2525                         strcpy (loadmodel->name, name);
2526                         // textures and memory belong to the main model
2527                         loadmodel->texturepool = NULL;
2528                         loadmodel->mempool = NULL;
2529                         mod = loadmodel;
2530                 }
2531         }
2532 //      Mem_CheckSentinelsGlobal();
2533 }