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