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