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