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