added r_sortsurfaces cvar, default off, sorting them seems to be a very minor slowdow...
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
24
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
26
27 cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
33 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
34 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 #define MAX_SUBDIVPOLYTRIANGLES 4096
896 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
897
898 static int subdivpolyverts, subdivpolytriangles;
899 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
900 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
901
902 static int subdivpolylookupvert(vec3_t v)
903 {
904         int i;
905         for (i = 0;i < subdivpolyverts;i++)
906                 if (subdivpolyvert[i][0] == v[0]
907                  && subdivpolyvert[i][1] == v[1]
908                  && subdivpolyvert[i][2] == v[2])
909                         return i;
910         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
911                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
912         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
913         return subdivpolyverts++;
914 }
915
916 static void SubdividePolygon (int numverts, float *verts)
917 {
918         int             i, i1, i2, i3, f, b, c, p;
919         vec3_t  mins, maxs, front[256], back[256];
920         float   m, *pv, *cv, dist[256], frac;
921
922         if (numverts > 250)
923                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
924
925         BoundPoly (numverts, verts, mins, maxs);
926
927         for (i = 0;i < 3;i++)
928         {
929                 m = (mins[i] + maxs[i]) * 0.5;
930                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
931                 if (maxs[i] - m < 8)
932                         continue;
933                 if (m - mins[i] < 8)
934                         continue;
935
936                 // cut it
937                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
938                         dist[c] = cv[i] - m;
939
940                 f = b = 0;
941                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
942                 {
943                         if (dist[p] >= 0)
944                         {
945                                 VectorCopy (pv, front[f]);
946                                 f++;
947                         }
948                         if (dist[p] <= 0)
949                         {
950                                 VectorCopy (pv, back[b]);
951                                 b++;
952                         }
953                         if (dist[p] == 0 || dist[c] == 0)
954                                 continue;
955                         if ( (dist[p] > 0) != (dist[c] > 0) )
956                         {
957                                 // clip point
958                                 frac = dist[p] / (dist[p] - dist[c]);
959                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
960                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
961                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
962                                 f++;
963                                 b++;
964                         }
965                 }
966
967                 SubdividePolygon (f, front[0]);
968                 SubdividePolygon (b, back[0]);
969                 return;
970         }
971
972         i1 = subdivpolylookupvert(verts);
973         i2 = subdivpolylookupvert(verts + 3);
974         for (i = 2;i < numverts;i++)
975         {
976                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
977                 {
978                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
979                         return;
980                 }
981
982                 i3 = subdivpolylookupvert(verts + i * 3);
983                 subdivpolyindex[subdivpolytriangles][0] = i1;
984                 subdivpolyindex[subdivpolytriangles][1] = i2;
985                 subdivpolyindex[subdivpolytriangles][2] = i3;
986                 i2 = i3;
987                 subdivpolytriangles++;
988         }
989 }
990
991 /*
992 ================
993 Mod_GenerateWarpMesh
994
995 Breaks a polygon up along axial 64 unit
996 boundaries so that turbulent and sky warps
997 can be done reasonably.
998 ================
999 */
1000 void Mod_GenerateWarpMesh (msurface_t *surf)
1001 {
1002         int i, j;
1003         surfvertex_t *v;
1004         surfmesh_t *mesh;
1005
1006         subdivpolytriangles = 0;
1007         subdivpolyverts = 0;
1008         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1009         if (subdivpolytriangles < 1)
1010                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1011
1012         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1013         mesh->numverts = subdivpolyverts;
1014         mesh->numtriangles = subdivpolytriangles;
1015         mesh->vertex = (surfvertex_t *)(mesh + 1);
1016         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1017         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1018
1019         for (i = 0;i < mesh->numtriangles;i++)
1020                 for (j = 0;j < 3;j++)
1021                         mesh->index[i*3+j] = subdivpolyindex[i][j];
1022
1023         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1024         {
1025                 VectorCopy(subdivpolyvert[i], v->v);
1026                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1027                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1028         }
1029 }
1030
1031 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1032 {
1033         int                             i, is, it, *index, smax, tmax;
1034         float                   *in, s, t;
1035         surfvertex_t    *out;
1036         surfmesh_t              *mesh;
1037
1038         smax = surf->extents[0] >> 4;
1039         tmax = surf->extents[1] >> 4;
1040         surf->lightmaptexturestride = 0;
1041         surf->lightmaptexture = NULL;
1042
1043         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1044         mesh->numverts = surf->poly_numverts;
1045         mesh->numtriangles = surf->poly_numverts - 2;
1046         mesh->vertex = (surfvertex_t *)(mesh + 1);
1047         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1048         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1049
1050         index = mesh->index;
1051         for (i = 0;i < mesh->numtriangles;i++)
1052         {
1053                 *index++ = 0;
1054                 *index++ = i + 1;
1055                 *index++ = i + 2;
1056         }
1057
1058         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1059         {
1060                 VectorCopy (in, out->v);
1061
1062                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1063                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1064
1065                 out->st[0] = s / surf->texinfo->texture->width;
1066                 out->st[1] = t / surf->texinfo->texture->height;
1067
1068                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1069                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1070
1071                 // lightmap coordinates
1072                 out->uv[0] = 0;
1073                 out->uv[1] = 0;
1074
1075                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1076                 is = (int) s;
1077                 it = (int) t;
1078                 is = bound(0, is, smax);
1079                 it = bound(0, it, tmax);
1080                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1081         }
1082 }
1083
1084 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1085 {
1086         int                             i, is, it, *index, smax, tmax;
1087         float                   *in, s, t, xbase, ybase, xscale, yscale;
1088         surfvertex_t    *out;
1089         surfmesh_t              *mesh;
1090
1091         surf->flags |= SURF_LIGHTMAP;
1092         smax = surf->extents[0] >> 4;
1093         tmax = surf->extents[1] >> 4;
1094         if (r_miplightmaps.integer)
1095         {
1096                 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1097                 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);
1098         }
1099         else
1100         {
1101                 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1102                 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);
1103         }
1104         R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1105         xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1106         yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1107
1108         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1109         mesh->numverts = surf->poly_numverts;
1110         mesh->numtriangles = surf->poly_numverts - 2;
1111         mesh->vertex = (surfvertex_t *)(mesh + 1);
1112         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1113         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1114
1115         index = mesh->index;
1116         for (i = 0;i < mesh->numtriangles;i++)
1117         {
1118                 *index++ = 0;
1119                 *index++ = i + 1;
1120                 *index++ = i + 2;
1121         }
1122
1123         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1124         {
1125                 VectorCopy (in, out->v);
1126
1127                 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1128                 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1129
1130                 out->st[0] = s / surf->texinfo->texture->width;
1131                 out->st[1] = t / surf->texinfo->texture->height;
1132
1133                 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1134                 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1135
1136                 // lightmap coordinates
1137                 out->uv[0] = s * xscale + xbase;
1138                 out->uv[1] = t * yscale + ybase;
1139
1140                 // LordHavoc: calc lightmap data offset for vertex lighting to use
1141                 is = (int) s;
1142                 it = (int) t;
1143                 is = bound(0, is, smax);
1144                 it = bound(0, it, tmax);
1145                 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1146         }
1147 }
1148
1149 void Mod_GenerateVertexMesh (msurface_t *surf)
1150 {
1151         int                             i, *index;
1152         float                   *in;
1153         surfvertex_t    *out;
1154         surfmesh_t              *mesh;
1155
1156         surf->lightmaptexturestride = 0;
1157         surf->lightmaptexture = NULL;
1158
1159         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1160         mesh->numverts = surf->poly_numverts;
1161         mesh->numtriangles = surf->poly_numverts - 2;
1162         mesh->vertex = (surfvertex_t *)(mesh + 1);
1163         mesh->index = (int *)(mesh->vertex + mesh->numverts);
1164         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1165
1166         index = mesh->index;
1167         for (i = 0;i < mesh->numtriangles;i++)
1168         {
1169                 *index++ = 0;
1170                 *index++ = i + 1;
1171                 *index++ = i + 2;
1172         }
1173
1174         for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1175         {
1176                 VectorCopy (in, out->v);
1177                 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1178                 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1179         }
1180 }
1181
1182 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1183 {
1184         float           *vert;
1185         int                     i;
1186         int                     lindex;
1187         float           *vec;
1188
1189         // convert edges back to a normal polygon
1190         surf->poly_numverts = surf->numedges;
1191         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1192         for (i = 0;i < surf->numedges;i++)
1193         {
1194                 lindex = loadmodel->surfedges[surf->firstedge + i];
1195                 if (lindex > 0)
1196                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1197                 else
1198                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1199                 VectorCopy (vec, vert);
1200                 vert += 3;
1201         }
1202 }
1203
1204 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1205 {
1206         int j, base, tricount, newvertexcount, *index, *vertexremap;
1207         surfmesh_t *newmesh, *oldmesh, *firstmesh;
1208         if (s->mesh->numtriangles > 1000)
1209         {
1210                 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1211                 base = 0;
1212                 oldmesh = NULL;
1213                 firstmesh = NULL;
1214                 newmesh = NULL;
1215                 while (base < s->mesh->numtriangles)
1216                 {
1217                         tricount = s->mesh->numtriangles - base;
1218                         if (tricount > 1000)
1219                                 tricount = 1000;
1220                         index = s->mesh->index + base * 3;
1221                         base += tricount;
1222
1223                         newvertexcount = 0;
1224                         memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1225                         for (j = 0;j < tricount * 3;j++)
1226                                 if (vertexremap[index[j]] < 0)
1227                                         vertexremap[index[j]] = newvertexcount++;
1228
1229                         newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1230                         newmesh->chain = NULL;
1231                         newmesh->numverts = newvertexcount;
1232                         newmesh->numtriangles = tricount;
1233                         newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1234                         newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1235                         for (j = 0;j < tricount * 3;j++)
1236                         {
1237                                 newmesh->index[j] = vertexremap[index[j]];
1238                                 // yes this copies the same vertex multiple times in many cases...  but that's ok...
1239                                 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1240                         }
1241                         if (oldmesh)
1242                                 oldmesh->chain = newmesh;
1243                         else
1244                                 firstmesh = newmesh;
1245                         oldmesh = newmesh;
1246                 }
1247                 Mem_Free(vertexremap);
1248                 Mem_Free(s->mesh);
1249                 s->mesh = firstmesh;
1250         }
1251 }
1252
1253 /*
1254 =================
1255 Mod_LoadFaces
1256 =================
1257 */
1258 static void Mod_LoadFaces (lump_t *l)
1259 {
1260         dface_t *in;
1261         msurface_t      *out;
1262         int i, count, surfnum, planenum, ssize, tsize;
1263
1264         in = (void *)(mod_base + l->fileofs);
1265         if (l->filelen % sizeof(*in))
1266                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1267         count = l->filelen / sizeof(*in);
1268         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1269
1270         loadmodel->surfaces = out;
1271         loadmodel->numsurfaces = count;
1272
1273         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1274         {
1275                 // FIXME: validate edges, texinfo, etc?
1276                 out->firstedge = LittleLong(in->firstedge);
1277                 out->numedges = LittleShort(in->numedges);
1278                 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1279                         Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1280
1281                 i = LittleShort (in->texinfo);
1282                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1283                         Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1284                 out->texinfo = loadmodel->texinfo + i;
1285                 out->flags = out->texinfo->texture->flags;
1286
1287                 planenum = LittleShort(in->planenum);
1288                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1289                         Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1290
1291                 if (LittleShort(in->side))
1292                         out->flags |= SURF_PLANEBACK;
1293
1294                 out->plane = loadmodel->planes + planenum;
1295
1296                 // clear lightmap (filled in later)
1297                 out->lightmaptexture = NULL;
1298
1299                 // force lightmap upload on first time seeing the surface
1300                 out->cached_dlight = true;
1301                 out->cached_ambient = -1000;
1302                 out->cached_lightscalebit = -1000;
1303
1304                 CalcSurfaceExtents (out);
1305
1306                 ssize = (out->extents[0] >> 4) + 1;
1307                 tsize = (out->extents[1] >> 4) + 1;
1308
1309                 // lighting info
1310                 for (i = 0;i < MAXLIGHTMAPS;i++)
1311                         out->styles[i] = in->styles[i];
1312                 i = LittleLong(in->lightofs);
1313                 if (i == -1)
1314                         out->samples = NULL;
1315                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1316                         out->samples = loadmodel->lightdata + i;
1317                 else // LordHavoc: white lighting (bsp version 29)
1318                         out->samples = loadmodel->lightdata + (i * 3);
1319
1320                 Mod_GenerateSurfacePolygon(out);
1321
1322                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1323                 {
1324                         out->shader = &Cshader_sky;
1325                         out->samples = NULL;
1326                         Mod_GenerateWarpMesh (out);
1327                 }
1328                 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1329                 {
1330                         out->shader = &Cshader_water;
1331                         out->samples = NULL;
1332                         Mod_GenerateWarpMesh (out);
1333                 }
1334                 else
1335                 {
1336                         if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1337                                 out->flags |= SURF_CLIPSOLID;
1338                         if (out->texinfo->flags & TEX_SPECIAL)
1339                         {
1340                                 // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1341                                 out->shader = &Cshader_water;
1342                                 out->shader = &Cshader_water;
1343                                 out->samples = NULL;
1344                                 Mod_GenerateWarpMesh (out);
1345                         }
1346                         else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1347                         {
1348                                 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1349                                 out->shader = &Cshader_wall_fullbright;
1350                                 out->samples = NULL;
1351                                 Mod_GenerateVertexMesh(out);
1352                         }
1353                         else
1354                         {
1355                                 // stainmap for permanent marks on walls
1356                                 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1357                                 // clear to white
1358                                 memset(out->stainsamples, 255, ssize * tsize * 3);
1359                                 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1360                                 {
1361                                         out->shader = &Cshader_wall_vertex;
1362                                         Mod_GenerateVertexLitMesh(out);
1363                                 }
1364                                 else
1365                                 {
1366                                         out->shader = &Cshader_wall_lightmap;
1367                                         Mod_GenerateLightmappedMesh(out);
1368                                 }
1369                         }
1370                 }
1371                 Mod_SplitSurfMeshIfTooBig(out);
1372         }
1373 }
1374
1375 static model_t *sortmodel;
1376
1377 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1378 {
1379         const msurface_t *a, *b;
1380         a = *((const msurface_t **)voida);
1381         b = *((const msurface_t **)voidb);
1382         if (a->shader != b->shader)
1383                 return (qbyte *) a->shader - (qbyte *) b->shader;
1384         if (a->texinfo->texture != b->texinfo->texture);
1385                 return a->texinfo->texture - b->texinfo->texture;
1386         return 0;
1387 }
1388
1389 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1390 {
1391         int surfnum;
1392         sortmodel = model;
1393         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1394         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1395                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1396
1397         if (r_sortsurfaces.integer)
1398                 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1399 }
1400
1401
1402 /*
1403 =================
1404 Mod_SetParent
1405 =================
1406 */
1407 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1408 {
1409         node->parent = parent;
1410         if (node->contents < 0)
1411                 return;
1412         Mod_SetParent (node->children[0], node);
1413         Mod_SetParent (node->children[1], node);
1414 }
1415
1416 /*
1417 =================
1418 Mod_LoadNodes
1419 =================
1420 */
1421 static void Mod_LoadNodes (lump_t *l)
1422 {
1423         int                     i, j, count, p;
1424         dnode_t         *in;
1425         mnode_t         *out;
1426
1427         in = (void *)(mod_base + l->fileofs);
1428         if (l->filelen % sizeof(*in))
1429                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1430         count = l->filelen / sizeof(*in);
1431         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1432
1433         loadmodel->nodes = out;
1434         loadmodel->numnodes = count;
1435
1436         for ( i=0 ; i<count ; i++, in++, out++)
1437         {
1438                 for (j=0 ; j<3 ; j++)
1439                 {
1440                         out->mins[j] = LittleShort (in->mins[j]);
1441                         out->maxs[j] = LittleShort (in->maxs[j]);
1442                 }
1443
1444                 p = LittleLong(in->planenum);
1445                 out->plane = loadmodel->planes + p;
1446
1447                 out->firstsurface = LittleShort (in->firstface);
1448                 out->numsurfaces = LittleShort (in->numfaces);
1449
1450                 for (j=0 ; j<2 ; j++)
1451                 {
1452                         p = LittleShort (in->children[j]);
1453                         if (p >= 0)
1454                                 out->children[j] = loadmodel->nodes + p;
1455                         else
1456                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1457                 }
1458         }
1459
1460         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1461 }
1462
1463 /*
1464 =================
1465 Mod_LoadLeafs
1466 =================
1467 */
1468 static void Mod_LoadLeafs (lump_t *l)
1469 {
1470         dleaf_t         *in;
1471         mleaf_t         *out;
1472         int                     i, j, count, p;
1473
1474         in = (void *)(mod_base + l->fileofs);
1475         if (l->filelen % sizeof(*in))
1476                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1477         count = l->filelen / sizeof(*in);
1478         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1479
1480         loadmodel->leafs = out;
1481         loadmodel->numleafs = count;
1482
1483         for ( i=0 ; i<count ; i++, in++, out++)
1484         {
1485                 for (j=0 ; j<3 ; j++)
1486                 {
1487                         out->mins[j] = LittleShort (in->mins[j]);
1488                         out->maxs[j] = LittleShort (in->maxs[j]);
1489                 }
1490
1491                 p = LittleLong(in->contents);
1492                 out->contents = p;
1493
1494                 out->firstmarksurface = loadmodel->marksurfaces +
1495                         LittleShort(in->firstmarksurface);
1496                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1497
1498                 p = LittleLong(in->visofs);
1499                 if (p == -1)
1500                         out->compressed_vis = NULL;
1501                 else
1502                         out->compressed_vis = loadmodel->visdata + p;
1503
1504                 for (j=0 ; j<4 ; j++)
1505                         out->ambient_sound_level[j] = in->ambient_level[j];
1506
1507                 // FIXME: Insert caustics here
1508         }
1509 }
1510
1511 /*
1512 =================
1513 Mod_LoadClipnodes
1514 =================
1515 */
1516 static void Mod_LoadClipnodes (lump_t *l)
1517 {
1518         dclipnode_t *in, *out;
1519         int                     i, count;
1520         hull_t          *hull;
1521
1522         in = (void *)(mod_base + l->fileofs);
1523         if (l->filelen % sizeof(*in))
1524                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1525         count = l->filelen / sizeof(*in);
1526         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1527
1528         loadmodel->clipnodes = out;
1529         loadmodel->numclipnodes = count;
1530
1531         if (loadmodel->ishlbsp)
1532         {
1533                 hull = &loadmodel->hulls[1];
1534                 hull->clipnodes = out;
1535                 hull->firstclipnode = 0;
1536                 hull->lastclipnode = count-1;
1537                 hull->planes = loadmodel->planes;
1538                 hull->clip_mins[0] = -16;
1539                 hull->clip_mins[1] = -16;
1540                 hull->clip_mins[2] = -36;
1541                 hull->clip_maxs[0] = 16;
1542                 hull->clip_maxs[1] = 16;
1543                 hull->clip_maxs[2] = 36;
1544                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1545
1546                 hull = &loadmodel->hulls[2];
1547                 hull->clipnodes = out;
1548                 hull->firstclipnode = 0;
1549                 hull->lastclipnode = count-1;
1550                 hull->planes = loadmodel->planes;
1551                 hull->clip_mins[0] = -32;
1552                 hull->clip_mins[1] = -32;
1553                 hull->clip_mins[2] = -32;
1554                 hull->clip_maxs[0] = 32;
1555                 hull->clip_maxs[1] = 32;
1556                 hull->clip_maxs[2] = 32;
1557                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1558
1559                 hull = &loadmodel->hulls[3];
1560                 hull->clipnodes = out;
1561                 hull->firstclipnode = 0;
1562                 hull->lastclipnode = count-1;
1563                 hull->planes = loadmodel->planes;
1564                 hull->clip_mins[0] = -16;
1565                 hull->clip_mins[1] = -16;
1566                 hull->clip_mins[2] = -18;
1567                 hull->clip_maxs[0] = 16;
1568                 hull->clip_maxs[1] = 16;
1569                 hull->clip_maxs[2] = 18;
1570                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1571         }
1572         else
1573         {
1574                 hull = &loadmodel->hulls[1];
1575                 hull->clipnodes = out;
1576                 hull->firstclipnode = 0;
1577                 hull->lastclipnode = count-1;
1578                 hull->planes = loadmodel->planes;
1579                 hull->clip_mins[0] = -16;
1580                 hull->clip_mins[1] = -16;
1581                 hull->clip_mins[2] = -24;
1582                 hull->clip_maxs[0] = 16;
1583                 hull->clip_maxs[1] = 16;
1584                 hull->clip_maxs[2] = 32;
1585                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1586
1587                 hull = &loadmodel->hulls[2];
1588                 hull->clipnodes = out;
1589                 hull->firstclipnode = 0;
1590                 hull->lastclipnode = count-1;
1591                 hull->planes = loadmodel->planes;
1592                 hull->clip_mins[0] = -32;
1593                 hull->clip_mins[1] = -32;
1594                 hull->clip_mins[2] = -24;
1595                 hull->clip_maxs[0] = 32;
1596                 hull->clip_maxs[1] = 32;
1597                 hull->clip_maxs[2] = 64;
1598                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1599         }
1600
1601         for (i=0 ; i<count ; i++, out++, in++)
1602         {
1603                 out->planenum = LittleLong(in->planenum);
1604                 out->children[0] = LittleShort(in->children[0]);
1605                 out->children[1] = LittleShort(in->children[1]);
1606                 if (out->children[0] >= count || out->children[1] >= count)
1607                         Host_Error("Corrupt clipping hull (out of range child)\n");
1608         }
1609 }
1610
1611 /*
1612 =================
1613 Mod_MakeHull0
1614
1615 Duplicate the drawing hull structure as a clipping hull
1616 =================
1617 */
1618 static void Mod_MakeHull0 (void)
1619 {
1620         mnode_t         *in;
1621         dclipnode_t *out;
1622         int                     i;
1623         hull_t          *hull;
1624
1625         hull = &loadmodel->hulls[0];
1626
1627         in = loadmodel->nodes;
1628         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1629
1630         hull->clipnodes = out;
1631         hull->firstclipnode = 0;
1632         hull->lastclipnode = loadmodel->numnodes - 1;
1633         hull->planes = loadmodel->planes;
1634
1635         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1636         {
1637                 out->planenum = in->plane - loadmodel->planes;
1638                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1639                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1640         }
1641 }
1642
1643 /*
1644 =================
1645 Mod_LoadMarksurfaces
1646 =================
1647 */
1648 static void Mod_LoadMarksurfaces (lump_t *l)
1649 {
1650         int             i, j;
1651         short   *in;
1652
1653         in = (void *)(mod_base + l->fileofs);
1654         if (l->filelen % sizeof(*in))
1655                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1656         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1657         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1658
1659         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1660         {
1661                 j = (unsigned) LittleShort(in[i]);
1662                 if (j >= loadmodel->numsurfaces)
1663                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1664                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1665         }
1666 }
1667
1668 /*
1669 =================
1670 Mod_LoadSurfedges
1671 =================
1672 */
1673 static void Mod_LoadSurfedges (lump_t *l)
1674 {
1675         int             i;
1676         int             *in;
1677
1678         in = (void *)(mod_base + l->fileofs);
1679         if (l->filelen % sizeof(*in))
1680                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1681         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1682         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1683
1684         for (i = 0;i < loadmodel->numsurfedges;i++)
1685                 loadmodel->surfedges[i] = LittleLong (in[i]);
1686 }
1687
1688
1689 /*
1690 =================
1691 Mod_LoadPlanes
1692 =================
1693 */
1694 static void Mod_LoadPlanes (lump_t *l)
1695 {
1696         int                     i;
1697         mplane_t        *out;
1698         dplane_t        *in;
1699
1700         in = (void *)(mod_base + l->fileofs);
1701         if (l->filelen % sizeof(*in))
1702                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1703
1704         loadmodel->numplanes = l->filelen / sizeof(*in);
1705         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1706
1707         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1708         {
1709                 out->normal[0] = LittleFloat (in->normal[0]);
1710                 out->normal[1] = LittleFloat (in->normal[1]);
1711                 out->normal[2] = LittleFloat (in->normal[2]);
1712                 out->dist = LittleFloat (in->dist);
1713
1714                 PlaneClassify(out);
1715         }
1716 }
1717
1718 #define MAX_POINTS_ON_WINDING 64
1719
1720 typedef struct
1721 {
1722         int numpoints;
1723         int padding;
1724         double points[8][3]; // variable sized
1725 }
1726 winding_t;
1727
1728 /*
1729 ==================
1730 NewWinding
1731 ==================
1732 */
1733 static winding_t *NewWinding (int points)
1734 {
1735         winding_t *w;
1736         int size;
1737
1738         if (points > MAX_POINTS_ON_WINDING)
1739                 Sys_Error("NewWinding: too many points\n");
1740
1741         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1742         w = Mem_Alloc(loadmodel->mempool, size);
1743         memset (w, 0, size);
1744
1745         return w;
1746 }
1747
1748 static void FreeWinding (winding_t *w)
1749 {
1750         Mem_Free(w);
1751 }
1752
1753 /*
1754 =================
1755 BaseWindingForPlane
1756 =================
1757 */
1758 static winding_t *BaseWindingForPlane (mplane_t *p)
1759 {
1760         double org[3], vright[3], vup[3], normal[3];
1761         winding_t *w;
1762
1763         VectorCopy(p->normal, normal);
1764         VectorVectorsDouble(normal, vright, vup);
1765
1766         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1767         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1768
1769         // project a really big axis aligned box onto the plane
1770         w = NewWinding (4);
1771
1772         VectorScale (p->normal, p->dist, org);
1773
1774         VectorSubtract (org, vright, w->points[0]);
1775         VectorAdd (w->points[0], vup, w->points[0]);
1776
1777         VectorAdd (org, vright, w->points[1]);
1778         VectorAdd (w->points[1], vup, w->points[1]);
1779
1780         VectorAdd (org, vright, w->points[2]);
1781         VectorSubtract (w->points[2], vup, w->points[2]);
1782
1783         VectorSubtract (org, vright, w->points[3]);
1784         VectorSubtract (w->points[3], vup, w->points[3]);
1785
1786         w->numpoints = 4;
1787
1788         return w;
1789 }
1790
1791 /*
1792 ==================
1793 ClipWinding
1794
1795 Clips the winding to the plane, returning the new winding on the positive side
1796 Frees the input winding.
1797 If keepon is true, an exactly on-plane winding will be saved, otherwise
1798 it will be clipped away.
1799 ==================
1800 */
1801 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1802 {
1803         double  dists[MAX_POINTS_ON_WINDING + 1];
1804         int             sides[MAX_POINTS_ON_WINDING + 1];
1805         int             counts[3];
1806         double  dot;
1807         int             i, j;
1808         double  *p1, *p2;
1809         double  mid[3];
1810         winding_t       *neww;
1811         int             maxpts;
1812
1813         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1814
1815         // determine sides for each point
1816         for (i = 0;i < in->numpoints;i++)
1817         {
1818                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1819                 if (dot > ON_EPSILON)
1820                         sides[i] = SIDE_FRONT;
1821                 else if (dot < -ON_EPSILON)
1822                         sides[i] = SIDE_BACK;
1823                 else
1824                         sides[i] = SIDE_ON;
1825                 counts[sides[i]]++;
1826         }
1827         sides[i] = sides[0];
1828         dists[i] = dists[0];
1829
1830         if (keepon && !counts[0] && !counts[1])
1831                 return in;
1832
1833         if (!counts[0])
1834         {
1835                 FreeWinding (in);
1836                 return NULL;
1837         }
1838         if (!counts[1])
1839                 return in;
1840
1841         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1842         if (maxpts > MAX_POINTS_ON_WINDING)
1843                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1844
1845         neww = NewWinding (maxpts);
1846
1847         for (i = 0;i < in->numpoints;i++)
1848         {
1849                 if (neww->numpoints >= maxpts)
1850                         Sys_Error ("ClipWinding: points exceeded estimate");
1851
1852                 p1 = in->points[i];
1853
1854                 if (sides[i] == SIDE_ON)
1855                 {
1856                         VectorCopy (p1, neww->points[neww->numpoints]);
1857                         neww->numpoints++;
1858                         continue;
1859                 }
1860
1861                 if (sides[i] == SIDE_FRONT)
1862                 {
1863                         VectorCopy (p1, neww->points[neww->numpoints]);
1864                         neww->numpoints++;
1865                 }
1866
1867                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1868                         continue;
1869
1870                 // generate a split point
1871                 p2 = in->points[(i+1)%in->numpoints];
1872
1873                 dot = dists[i] / (dists[i]-dists[i+1]);
1874                 for (j = 0;j < 3;j++)
1875                 {       // avoid round off error when possible
1876                         if (split->normal[j] == 1)
1877                                 mid[j] = split->dist;
1878                         else if (split->normal[j] == -1)
1879                                 mid[j] = -split->dist;
1880                         else
1881                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1882                 }
1883
1884                 VectorCopy (mid, neww->points[neww->numpoints]);
1885                 neww->numpoints++;
1886         }
1887
1888         // free the original winding
1889         FreeWinding (in);
1890
1891         return neww;
1892 }
1893
1894
1895 /*
1896 ==================
1897 DivideWinding
1898
1899 Divides a winding by a plane, producing one or two windings.  The
1900 original winding is not damaged or freed.  If only on one side, the
1901 returned winding will be the input winding.  If on both sides, two
1902 new windings will be created.
1903 ==================
1904 */
1905 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1906 {
1907         double  dists[MAX_POINTS_ON_WINDING + 1];
1908         int             sides[MAX_POINTS_ON_WINDING + 1];
1909         int             counts[3];
1910         double  dot;
1911         int             i, j;
1912         double  *p1, *p2;
1913         double  mid[3];
1914         winding_t       *f, *b;
1915         int             maxpts;
1916
1917         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1918
1919         // determine sides for each point
1920         for (i = 0;i < in->numpoints;i++)
1921         {
1922                 dot = DotProduct (in->points[i], split->normal);
1923                 dot -= split->dist;
1924                 dists[i] = dot;
1925                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1926                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1927                 else sides[i] = SIDE_ON;
1928                 counts[sides[i]]++;
1929         }
1930         sides[i] = sides[0];
1931         dists[i] = dists[0];
1932
1933         *front = *back = NULL;
1934
1935         if (!counts[0])
1936         {
1937                 *back = in;
1938                 return;
1939         }
1940         if (!counts[1])
1941         {
1942                 *front = in;
1943                 return;
1944         }
1945
1946         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1947
1948         if (maxpts > MAX_POINTS_ON_WINDING)
1949                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1950
1951         *front = f = NewWinding (maxpts);
1952         *back = b = NewWinding (maxpts);
1953
1954         for (i = 0;i < in->numpoints;i++)
1955         {
1956                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1957                         Sys_Error ("DivideWinding: points exceeded estimate");
1958
1959                 p1 = in->points[i];
1960
1961                 if (sides[i] == SIDE_ON)
1962                 {
1963                         VectorCopy (p1, f->points[f->numpoints]);
1964                         f->numpoints++;
1965                         VectorCopy (p1, b->points[b->numpoints]);
1966                         b->numpoints++;
1967                         continue;
1968                 }
1969
1970                 if (sides[i] == SIDE_FRONT)
1971                 {
1972                         VectorCopy (p1, f->points[f->numpoints]);
1973                         f->numpoints++;
1974                 }
1975                 else if (sides[i] == SIDE_BACK)
1976                 {
1977                         VectorCopy (p1, b->points[b->numpoints]);
1978                         b->numpoints++;
1979                 }
1980
1981                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1982                         continue;
1983
1984                 // generate a split point
1985                 p2 = in->points[(i+1)%in->numpoints];
1986
1987                 dot = dists[i] / (dists[i]-dists[i+1]);
1988                 for (j = 0;j < 3;j++)
1989                 {       // avoid round off error when possible
1990                         if (split->normal[j] == 1)
1991                                 mid[j] = split->dist;
1992                         else if (split->normal[j] == -1)
1993                                 mid[j] = -split->dist;
1994                         else
1995                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1996                 }
1997
1998                 VectorCopy (mid, f->points[f->numpoints]);
1999                 f->numpoints++;
2000                 VectorCopy (mid, b->points[b->numpoints]);
2001                 b->numpoints++;
2002         }
2003 }
2004
2005 typedef struct portal_s
2006 {
2007         mplane_t plane;
2008         mnode_t *nodes[2];              // [0] = front side of plane
2009         struct portal_s *next[2];
2010         winding_t *winding;
2011         struct portal_s *chain; // all portals are linked into a list
2012 }
2013 portal_t;
2014
2015 static portal_t *portalchain;
2016
2017 /*
2018 ===========
2019 AllocPortal
2020 ===========
2021 */
2022 static portal_t *AllocPortal (void)
2023 {
2024         portal_t *p;
2025         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2026         p->chain = portalchain;
2027         portalchain = p;
2028         return p;
2029 }
2030
2031 static void FreePortal(portal_t *p)
2032 {
2033         Mem_Free(p);
2034 }
2035
2036 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2037 {
2038         // calculate children first
2039         if (node->children[0]->contents >= 0)
2040                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2041         if (node->children[1]->contents >= 0)
2042                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2043
2044         // make combined bounding box from children
2045         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2046         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2047         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2048         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2049         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2050         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2051 }
2052
2053 static void Mod_FinalizePortals(void)
2054 {
2055         int i, j, numportals, numpoints;
2056         portal_t *p, *pnext;
2057         mportal_t *portal;
2058         mvertex_t *point;
2059         mleaf_t *leaf, *endleaf;
2060         winding_t *w;
2061
2062         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2063         leaf = loadmodel->leafs;
2064         endleaf = leaf + loadmodel->numleafs;
2065         for (;leaf < endleaf;leaf++)
2066         {
2067                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2068                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2069         }
2070         p = portalchain;
2071         while(p)
2072         {
2073                 if (p->winding)
2074                 {
2075                         for (i = 0;i < 2;i++)
2076                         {
2077                                 leaf = (mleaf_t *)p->nodes[i];
2078                                 w = p->winding;
2079                                 for (j = 0;j < w->numpoints;j++)
2080                                 {
2081                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2082                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2083                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2084                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2085                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2086                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2087                                 }
2088                         }
2089                 }
2090                 p = p->chain;
2091         }
2092
2093         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2094
2095         // tally up portal and point counts
2096         p = portalchain;
2097         numportals = 0;
2098         numpoints = 0;
2099         while(p)
2100         {
2101                 // note: this check must match the one below or it will usually corrupt memory
2102                 // 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
2103                 if (p->winding && p->nodes[0] != p->nodes[1]
2104                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2105                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2106                 {
2107                         numportals += 2;
2108                         numpoints += p->winding->numpoints * 2;
2109                 }
2110                 p = p->chain;
2111         }
2112         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2113         loadmodel->numportals = numportals;
2114         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2115         loadmodel->numportalpoints = numpoints;
2116         // clear all leaf portal chains
2117         for (i = 0;i < loadmodel->numleafs;i++)
2118                 loadmodel->leafs[i].portals = NULL;
2119         // process all portals in the global portal chain, while freeing them
2120         portal = loadmodel->portals;
2121         point = loadmodel->portalpoints;
2122         p = portalchain;
2123         portalchain = NULL;
2124         while (p)
2125         {
2126                 pnext = p->chain;
2127
2128                 if (p->winding)
2129                 {
2130                         // note: this check must match the one above or it will usually corrupt memory
2131                         // 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
2132                         if (p->nodes[0] != p->nodes[1]
2133                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2134                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2135                         {
2136                                 // first make the back to front portal (forward portal)
2137                                 portal->points = point;
2138                                 portal->numpoints = p->winding->numpoints;
2139                                 portal->plane.dist = p->plane.dist;
2140                                 VectorCopy(p->plane.normal, portal->plane.normal);
2141                                 portal->here = (mleaf_t *)p->nodes[1];
2142                                 portal->past = (mleaf_t *)p->nodes[0];
2143                                 // copy points
2144                                 for (j = 0;j < portal->numpoints;j++)
2145                                 {
2146                                         VectorCopy(p->winding->points[j], point->position);
2147                                         point++;
2148                                 }
2149                                 PlaneClassify(&portal->plane);
2150
2151                                 // link into leaf's portal chain
2152                                 portal->next = portal->here->portals;
2153                                 portal->here->portals = portal;
2154
2155                                 // advance to next portal
2156                                 portal++;
2157
2158                                 // then make the front to back portal (backward portal)
2159                                 portal->points = point;
2160                                 portal->numpoints = p->winding->numpoints;
2161                                 portal->plane.dist = -p->plane.dist;
2162                                 VectorNegate(p->plane.normal, portal->plane.normal);
2163                                 portal->here = (mleaf_t *)p->nodes[0];
2164                                 portal->past = (mleaf_t *)p->nodes[1];
2165                                 // copy points
2166                                 for (j = portal->numpoints - 1;j >= 0;j--)
2167                                 {
2168                                         VectorCopy(p->winding->points[j], point->position);
2169                                         point++;
2170                                 }
2171                                 PlaneClassify(&portal->plane);
2172
2173                                 // link into leaf's portal chain
2174                                 portal->next = portal->here->portals;
2175                                 portal->here->portals = portal;
2176
2177                                 // advance to next portal
2178                                 portal++;
2179                         }
2180                         FreeWinding(p->winding);
2181                 }
2182                 FreePortal(p);
2183                 p = pnext;
2184         }
2185 }
2186
2187 /*
2188 =============
2189 AddPortalToNodes
2190 =============
2191 */
2192 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2193 {
2194         if (!front)
2195                 Host_Error ("AddPortalToNodes: NULL front node");
2196         if (!back)
2197                 Host_Error ("AddPortalToNodes: NULL back node");
2198         if (p->nodes[0] || p->nodes[1])
2199                 Host_Error ("AddPortalToNodes: already included");
2200         // 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
2201
2202         p->nodes[0] = front;
2203         p->next[0] = (portal_t *)front->portals;
2204         front->portals = (mportal_t *)p;
2205
2206         p->nodes[1] = back;
2207         p->next[1] = (portal_t *)back->portals;
2208         back->portals = (mportal_t *)p;
2209 }
2210
2211 /*
2212 =============
2213 RemovePortalFromNode
2214 =============
2215 */
2216 static void RemovePortalFromNodes(portal_t *portal)
2217 {
2218         int i;
2219         mnode_t *node;
2220         void **portalpointer;
2221         portal_t *t;
2222         for (i = 0;i < 2;i++)
2223         {
2224                 node = portal->nodes[i];
2225
2226                 portalpointer = (void **) &node->portals;
2227                 while (1)
2228                 {
2229                         t = *portalpointer;
2230                         if (!t)
2231                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2232
2233                         if (t == portal)
2234                         {
2235                                 if (portal->nodes[0] == node)
2236                                 {
2237                                         *portalpointer = portal->next[0];
2238                                         portal->nodes[0] = NULL;
2239                                 }
2240                                 else if (portal->nodes[1] == node)
2241                                 {
2242                                         *portalpointer = portal->next[1];
2243                                         portal->nodes[1] = NULL;
2244                                 }
2245                                 else
2246                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2247                                 break;
2248                         }
2249
2250                         if (t->nodes[0] == node)
2251                                 portalpointer = (void **) &t->next[0];
2252                         else if (t->nodes[1] == node)
2253                                 portalpointer = (void **) &t->next[1];
2254                         else
2255                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2256                 }
2257         }
2258 }
2259
2260 static void Mod_RecursiveNodePortals (mnode_t *node)
2261 {
2262         int side;
2263         mnode_t *front, *back, *other_node;
2264         mplane_t clipplane, *plane;
2265         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2266         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2267
2268         // if a leaf, we're done
2269         if (node->contents)
2270                 return;
2271
2272         plane = node->plane;
2273
2274         front = node->children[0];
2275         back = node->children[1];
2276         if (front == back)
2277                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2278
2279         // create the new portal by generating a polygon for the node plane,
2280         // and clipping it by all of the other portals (which came from nodes above this one)
2281         nodeportal = AllocPortal ();
2282         nodeportal->plane = *node->plane;
2283
2284         nodeportalwinding = BaseWindingForPlane (node->plane);
2285         side = 0;       // shut up compiler warning
2286         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2287         {
2288                 clipplane = portal->plane;
2289                 if (portal->nodes[0] == portal->nodes[1])
2290                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2291                 if (portal->nodes[0] == node)
2292                         side = 0;
2293                 else if (portal->nodes[1] == node)
2294                 {
2295                         clipplane.dist = -clipplane.dist;
2296                         VectorNegate (clipplane.normal, clipplane.normal);
2297                         side = 1;
2298                 }
2299                 else
2300                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2301
2302                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2303                 if (!nodeportalwinding)
2304                 {
2305                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2306                         break;
2307                 }
2308         }
2309
2310         if (nodeportalwinding)
2311         {
2312                 // if the plane was not clipped on all sides, there was an error
2313                 nodeportal->winding = nodeportalwinding;
2314                 AddPortalToNodes (nodeportal, front, back);
2315         }
2316
2317         // split the portals of this node along this node's plane and assign them to the children of this node
2318         // (migrating the portals downward through the tree)
2319         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2320         {
2321                 if (portal->nodes[0] == portal->nodes[1])
2322                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2323                 if (portal->nodes[0] == node)
2324                         side = 0;
2325                 else if (portal->nodes[1] == node)
2326                         side = 1;
2327                 else
2328                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2329                 nextportal = portal->next[side];
2330
2331                 other_node = portal->nodes[!side];
2332                 RemovePortalFromNodes (portal);
2333
2334                 // cut the portal into two portals, one on each side of the node plane
2335                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2336
2337                 if (!frontwinding)
2338                 {
2339                         if (side == 0)
2340                                 AddPortalToNodes (portal, back, other_node);
2341                         else
2342                                 AddPortalToNodes (portal, other_node, back);
2343                         continue;
2344                 }
2345                 if (!backwinding)
2346                 {
2347                         if (side == 0)
2348                                 AddPortalToNodes (portal, front, other_node);
2349                         else
2350                                 AddPortalToNodes (portal, other_node, front);
2351                         continue;
2352                 }
2353
2354                 // the winding is split
2355                 splitportal = AllocPortal ();
2356                 temp = splitportal->chain;
2357                 *splitportal = *portal;
2358                 splitportal->chain = temp;
2359                 splitportal->winding = backwinding;
2360                 FreeWinding (portal->winding);
2361                 portal->winding = frontwinding;
2362
2363                 if (side == 0)
2364                 {
2365                         AddPortalToNodes (portal, front, other_node);
2366                         AddPortalToNodes (splitportal, back, other_node);
2367                 }
2368                 else
2369                 {
2370                         AddPortalToNodes (portal, other_node, front);
2371                         AddPortalToNodes (splitportal, other_node, back);
2372                 }
2373         }
2374
2375         Mod_RecursiveNodePortals(front);
2376         Mod_RecursiveNodePortals(back);
2377 }
2378
2379
2380 static void Mod_MakePortals(void)
2381 {
2382         portalchain = NULL;
2383         Mod_RecursiveNodePortals (loadmodel->nodes);
2384         Mod_FinalizePortals();
2385 }
2386
2387 /*
2388 =================
2389 Mod_LoadBrushModel
2390 =================
2391 */
2392 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2393 {
2394         int                     i, j;
2395         dheader_t       *header;
2396         dmodel_t        *bm;
2397         mempool_t       *mainmempool;
2398         char            *loadname;
2399
2400         mod->type = mod_brush;
2401
2402         header = (dheader_t *)buffer;
2403
2404         i = LittleLong (header->version);
2405         if (i != BSPVERSION && i != 30)
2406                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2407         mod->ishlbsp = i == 30;
2408         if (loadmodel->isworldmodel)
2409                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2410
2411 // swap all the lumps
2412         mod_base = (qbyte *)header;
2413
2414         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2415                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2416
2417 // load into heap
2418
2419         // store which lightmap format to use
2420         mod->lightmaprgba = r_lightmaprgba.integer;
2421
2422         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2423         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2424         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2425         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2426         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2427         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2428         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2429         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2430         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2431         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2432         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2433         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2434         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2435         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2436         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2437
2438         Mod_MakeHull0 ();
2439         Mod_MakePortals();
2440
2441         mod->numframes = 2;             // regular and alternate animation
2442
2443         mainmempool = mod->mempool;
2444         loadname = mod->name;
2445
2446         Mod_LoadLightList ();
2447
2448 //
2449 // set up the submodels (FIXME: this is confusing)
2450 //
2451         for (i = 0;i < mod->numsubmodels;i++)
2452         {
2453                 int k, l;
2454                 float dist, modelyawradius, modelradius, *vec;
2455                 msurface_t *surf;
2456
2457                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2458                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2459                 modelyawradius = 0;
2460                 modelradius = 0;
2461
2462                 bm = &mod->submodels[i];
2463
2464                 mod->hulls[0].firstclipnode = bm->headnode[0];
2465                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2466                 {
2467                         mod->hulls[j].firstclipnode = bm->headnode[j];
2468                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2469                 }
2470
2471                 mod->firstmodelsurface = bm->firstface;
2472                 mod->nummodelsurfaces = bm->numfaces;
2473
2474                 mod->DrawSky = NULL;
2475                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2476                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2477                 {
2478                         // we only need to have a drawsky function if it is used (usually only on world model)
2479                         if (surf->shader == &Cshader_sky)
2480                                 mod->DrawSky = R_DrawBrushModelSky;
2481                         for (k = 0;k < surf->numedges;k++)
2482                         {
2483                                 l = mod->surfedges[k + surf->firstedge];
2484                                 if (l > 0)
2485                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2486                                 else
2487                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2488                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2489                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2490                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2491                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2492                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2493                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2494                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2495                                 if (modelyawradius < dist)
2496                                         modelyawradius = dist;
2497                                 dist += vec[2]*vec[2];
2498                                 if (modelradius < dist)
2499                                         modelradius = dist;
2500                         }
2501                 }
2502                 modelyawradius = sqrt(modelyawradius);
2503                 modelradius = sqrt(modelradius);
2504                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2505                 mod->yawmins[2] = mod->normalmins[2];
2506                 mod->yawmaxs[2] = mod->normalmaxs[2];
2507                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2508                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2509                 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2510                 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2511                 {
2512                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2513                         VectorClear(mod->normalmins);
2514                         VectorClear(mod->normalmaxs);
2515                         VectorClear(mod->yawmins);
2516                         VectorClear(mod->yawmaxs);
2517                         VectorClear(mod->rotatedmins);
2518                         VectorClear(mod->rotatedmaxs);
2519                 }
2520
2521                 mod->numleafs = bm->visleafs;
2522
2523                 mod->Draw = R_DrawBrushModelNormal;
2524                 mod->DrawShadow = NULL;
2525
2526                 Mod_BrushSortedSurfaces(mod, mainmempool);
2527
2528                 // LordHavoc: only register submodels if it is the world
2529                 // (prevents bsp models from replacing world submodels)
2530                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2531                 {
2532                         char    name[10];
2533                         // duplicate the basic information
2534                         sprintf (name, "*%i", i+1);
2535                         loadmodel = Mod_FindName (name);
2536                         *loadmodel = *mod;
2537                         strcpy (loadmodel->name, name);
2538                         // textures and memory belong to the main model
2539                         loadmodel->texturepool = NULL;
2540                         loadmodel->mempool = NULL;
2541                         mod = loadmodel;
2542                 }
2543         }
2544 }
2545