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