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