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