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