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