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