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