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