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