]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
edcb60930aff8f03c1e89c331adf28e83ddcf751
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
24
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
26
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_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 /*
1385 =================
1386 Mod_SetParent
1387 =================
1388 */
1389 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1390 {
1391         node->parent = parent;
1392         if (node->contents < 0)
1393                 return;
1394         Mod_SetParent (node->children[0], node);
1395         Mod_SetParent (node->children[1], node);
1396 }
1397
1398 /*
1399 =================
1400 Mod_LoadNodes
1401 =================
1402 */
1403 static void Mod_LoadNodes (lump_t *l)
1404 {
1405         int                     i, j, count, p;
1406         dnode_t         *in;
1407         mnode_t         *out;
1408
1409         in = (void *)(mod_base + l->fileofs);
1410         if (l->filelen % sizeof(*in))
1411                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1412         count = l->filelen / sizeof(*in);
1413         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1414
1415         loadmodel->nodes = out;
1416         loadmodel->numnodes = count;
1417
1418         for ( i=0 ; i<count ; i++, in++, out++)
1419         {
1420                 for (j=0 ; j<3 ; j++)
1421                 {
1422                         out->mins[j] = LittleShort (in->mins[j]);
1423                         out->maxs[j] = LittleShort (in->maxs[j]);
1424                 }
1425
1426                 p = LittleLong(in->planenum);
1427                 out->plane = loadmodel->planes + p;
1428
1429                 out->firstsurface = LittleShort (in->firstface);
1430                 out->numsurfaces = LittleShort (in->numfaces);
1431
1432                 for (j=0 ; j<2 ; j++)
1433                 {
1434                         p = LittleShort (in->children[j]);
1435                         if (p >= 0)
1436                                 out->children[j] = loadmodel->nodes + p;
1437                         else
1438                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1439                 }
1440         }
1441
1442         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1443 }
1444
1445 /*
1446 =================
1447 Mod_LoadLeafs
1448 =================
1449 */
1450 static void Mod_LoadLeafs (lump_t *l)
1451 {
1452         dleaf_t         *in;
1453         mleaf_t         *out;
1454         int                     i, j, count, p;
1455
1456         in = (void *)(mod_base + l->fileofs);
1457         if (l->filelen % sizeof(*in))
1458                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1459         count = l->filelen / sizeof(*in);
1460         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1461
1462         loadmodel->leafs = out;
1463         loadmodel->numleafs = count;
1464
1465         for ( i=0 ; i<count ; i++, in++, out++)
1466         {
1467                 for (j=0 ; j<3 ; j++)
1468                 {
1469                         out->mins[j] = LittleShort (in->mins[j]);
1470                         out->maxs[j] = LittleShort (in->maxs[j]);
1471                 }
1472
1473                 p = LittleLong(in->contents);
1474                 out->contents = p;
1475
1476                 out->firstmarksurface = loadmodel->marksurfaces +
1477                         LittleShort(in->firstmarksurface);
1478                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1479
1480                 p = LittleLong(in->visofs);
1481                 if (p == -1)
1482                         out->compressed_vis = NULL;
1483                 else
1484                         out->compressed_vis = loadmodel->visdata + p;
1485
1486                 for (j=0 ; j<4 ; j++)
1487                         out->ambient_sound_level[j] = in->ambient_level[j];
1488
1489                 // FIXME: Insert caustics here
1490         }
1491 }
1492
1493 /*
1494 =================
1495 Mod_LoadClipnodes
1496 =================
1497 */
1498 static void Mod_LoadClipnodes (lump_t *l)
1499 {
1500         dclipnode_t *in, *out;
1501         int                     i, count;
1502         hull_t          *hull;
1503
1504         in = (void *)(mod_base + l->fileofs);
1505         if (l->filelen % sizeof(*in))
1506                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1507         count = l->filelen / sizeof(*in);
1508         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1509
1510         loadmodel->clipnodes = out;
1511         loadmodel->numclipnodes = count;
1512
1513         if (loadmodel->ishlbsp)
1514         {
1515                 hull = &loadmodel->hulls[1];
1516                 hull->clipnodes = out;
1517                 hull->firstclipnode = 0;
1518                 hull->lastclipnode = count-1;
1519                 hull->planes = loadmodel->planes;
1520                 hull->clip_mins[0] = -16;
1521                 hull->clip_mins[1] = -16;
1522                 hull->clip_mins[2] = -36;
1523                 hull->clip_maxs[0] = 16;
1524                 hull->clip_maxs[1] = 16;
1525                 hull->clip_maxs[2] = 36;
1526                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1527
1528                 hull = &loadmodel->hulls[2];
1529                 hull->clipnodes = out;
1530                 hull->firstclipnode = 0;
1531                 hull->lastclipnode = count-1;
1532                 hull->planes = loadmodel->planes;
1533                 hull->clip_mins[0] = -32;
1534                 hull->clip_mins[1] = -32;
1535                 hull->clip_mins[2] = -32;
1536                 hull->clip_maxs[0] = 32;
1537                 hull->clip_maxs[1] = 32;
1538                 hull->clip_maxs[2] = 32;
1539                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1540
1541                 hull = &loadmodel->hulls[3];
1542                 hull->clipnodes = out;
1543                 hull->firstclipnode = 0;
1544                 hull->lastclipnode = count-1;
1545                 hull->planes = loadmodel->planes;
1546                 hull->clip_mins[0] = -16;
1547                 hull->clip_mins[1] = -16;
1548                 hull->clip_mins[2] = -18;
1549                 hull->clip_maxs[0] = 16;
1550                 hull->clip_maxs[1] = 16;
1551                 hull->clip_maxs[2] = 18;
1552                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1553         }
1554         else
1555         {
1556                 hull = &loadmodel->hulls[1];
1557                 hull->clipnodes = out;
1558                 hull->firstclipnode = 0;
1559                 hull->lastclipnode = count-1;
1560                 hull->planes = loadmodel->planes;
1561                 hull->clip_mins[0] = -16;
1562                 hull->clip_mins[1] = -16;
1563                 hull->clip_mins[2] = -24;
1564                 hull->clip_maxs[0] = 16;
1565                 hull->clip_maxs[1] = 16;
1566                 hull->clip_maxs[2] = 32;
1567                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1568
1569                 hull = &loadmodel->hulls[2];
1570                 hull->clipnodes = out;
1571                 hull->firstclipnode = 0;
1572                 hull->lastclipnode = count-1;
1573                 hull->planes = loadmodel->planes;
1574                 hull->clip_mins[0] = -32;
1575                 hull->clip_mins[1] = -32;
1576                 hull->clip_mins[2] = -24;
1577                 hull->clip_maxs[0] = 32;
1578                 hull->clip_maxs[1] = 32;
1579                 hull->clip_maxs[2] = 64;
1580                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1581         }
1582
1583         for (i=0 ; i<count ; i++, out++, in++)
1584         {
1585                 out->planenum = LittleLong(in->planenum);
1586                 out->children[0] = LittleShort(in->children[0]);
1587                 out->children[1] = LittleShort(in->children[1]);
1588                 if (out->children[0] >= count || out->children[1] >= count)
1589                         Host_Error("Corrupt clipping hull (out of range child)\n");
1590         }
1591 }
1592
1593 /*
1594 =================
1595 Mod_MakeHull0
1596
1597 Duplicate the drawing hull structure as a clipping hull
1598 =================
1599 */
1600 static void Mod_MakeHull0 (void)
1601 {
1602         mnode_t         *in;
1603         dclipnode_t *out;
1604         int                     i;
1605         hull_t          *hull;
1606
1607         hull = &loadmodel->hulls[0];
1608
1609         in = loadmodel->nodes;
1610         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1611
1612         hull->clipnodes = out;
1613         hull->firstclipnode = 0;
1614         hull->lastclipnode = loadmodel->numnodes - 1;
1615         hull->planes = loadmodel->planes;
1616
1617         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1618         {
1619                 out->planenum = in->plane - loadmodel->planes;
1620                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1621                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1622         }
1623 }
1624
1625 /*
1626 =================
1627 Mod_LoadMarksurfaces
1628 =================
1629 */
1630 static void Mod_LoadMarksurfaces (lump_t *l)
1631 {
1632         int             i, j;
1633         short   *in;
1634
1635         in = (void *)(mod_base + l->fileofs);
1636         if (l->filelen % sizeof(*in))
1637                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1638         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1639         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1640
1641         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1642         {
1643                 j = (unsigned) LittleShort(in[i]);
1644                 if (j >= loadmodel->numsurfaces)
1645                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1646                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1647         }
1648 }
1649
1650 /*
1651 =================
1652 Mod_LoadSurfedges
1653 =================
1654 */
1655 static void Mod_LoadSurfedges (lump_t *l)
1656 {
1657         int             i;
1658         int             *in;
1659
1660         in = (void *)(mod_base + l->fileofs);
1661         if (l->filelen % sizeof(*in))
1662                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1663         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1664         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1665
1666         for (i = 0;i < loadmodel->numsurfedges;i++)
1667                 loadmodel->surfedges[i] = LittleLong (in[i]);
1668 }
1669
1670
1671 /*
1672 =================
1673 Mod_LoadPlanes
1674 =================
1675 */
1676 static void Mod_LoadPlanes (lump_t *l)
1677 {
1678         int                     i;
1679         mplane_t        *out;
1680         dplane_t        *in;
1681
1682         in = (void *)(mod_base + l->fileofs);
1683         if (l->filelen % sizeof(*in))
1684                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1685
1686         loadmodel->numplanes = l->filelen / sizeof(*in);
1687         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1688
1689         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1690         {
1691                 out->normal[0] = LittleFloat (in->normal[0]);
1692                 out->normal[1] = LittleFloat (in->normal[1]);
1693                 out->normal[2] = LittleFloat (in->normal[2]);
1694                 out->dist = LittleFloat (in->dist);
1695
1696                 PlaneClassify(out);
1697         }
1698 }
1699
1700 #define MAX_POINTS_ON_WINDING 64
1701
1702 typedef struct
1703 {
1704         int numpoints;
1705         int padding;
1706         double points[8][3]; // variable sized
1707 }
1708 winding_t;
1709
1710 /*
1711 ==================
1712 NewWinding
1713 ==================
1714 */
1715 static winding_t *NewWinding (int points)
1716 {
1717         winding_t *w;
1718         int size;
1719
1720         if (points > MAX_POINTS_ON_WINDING)
1721                 Sys_Error("NewWinding: too many points\n");
1722
1723         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1724         w = Mem_Alloc(loadmodel->mempool, size);
1725         memset (w, 0, size);
1726
1727         return w;
1728 }
1729
1730 static void FreeWinding (winding_t *w)
1731 {
1732         Mem_Free(w);
1733 }
1734
1735 /*
1736 =================
1737 BaseWindingForPlane
1738 =================
1739 */
1740 static winding_t *BaseWindingForPlane (mplane_t *p)
1741 {
1742         double org[3], vright[3], vup[3], normal[3];
1743         winding_t *w;
1744
1745         VectorCopy(p->normal, normal);
1746         VectorVectorsDouble(normal, vright, vup);
1747
1748         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1749         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1750
1751         // project a really big axis aligned box onto the plane
1752         w = NewWinding (4);
1753
1754         VectorScale (p->normal, p->dist, org);
1755
1756         VectorSubtract (org, vright, w->points[0]);
1757         VectorAdd (w->points[0], vup, w->points[0]);
1758
1759         VectorAdd (org, vright, w->points[1]);
1760         VectorAdd (w->points[1], vup, w->points[1]);
1761
1762         VectorAdd (org, vright, w->points[2]);
1763         VectorSubtract (w->points[2], vup, w->points[2]);
1764
1765         VectorSubtract (org, vright, w->points[3]);
1766         VectorSubtract (w->points[3], vup, w->points[3]);
1767
1768         w->numpoints = 4;
1769
1770         return w;
1771 }
1772
1773 /*
1774 ==================
1775 ClipWinding
1776
1777 Clips the winding to the plane, returning the new winding on the positive side
1778 Frees the input winding.
1779 If keepon is true, an exactly on-plane winding will be saved, otherwise
1780 it will be clipped away.
1781 ==================
1782 */
1783 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1784 {
1785         double  dists[MAX_POINTS_ON_WINDING + 1];
1786         int             sides[MAX_POINTS_ON_WINDING + 1];
1787         int             counts[3];
1788         double  dot;
1789         int             i, j;
1790         double  *p1, *p2;
1791         double  mid[3];
1792         winding_t       *neww;
1793         int             maxpts;
1794
1795         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1796
1797         // determine sides for each point
1798         for (i = 0;i < in->numpoints;i++)
1799         {
1800                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1801                 if (dot > ON_EPSILON)
1802                         sides[i] = SIDE_FRONT;
1803                 else if (dot < -ON_EPSILON)
1804                         sides[i] = SIDE_BACK;
1805                 else
1806                         sides[i] = SIDE_ON;
1807                 counts[sides[i]]++;
1808         }
1809         sides[i] = sides[0];
1810         dists[i] = dists[0];
1811
1812         if (keepon && !counts[0] && !counts[1])
1813                 return in;
1814
1815         if (!counts[0])
1816         {
1817                 FreeWinding (in);
1818                 return NULL;
1819         }
1820         if (!counts[1])
1821                 return in;
1822
1823         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1824         if (maxpts > MAX_POINTS_ON_WINDING)
1825                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1826
1827         neww = NewWinding (maxpts);
1828
1829         for (i = 0;i < in->numpoints;i++)
1830         {
1831                 if (neww->numpoints >= maxpts)
1832                         Sys_Error ("ClipWinding: points exceeded estimate");
1833
1834                 p1 = in->points[i];
1835
1836                 if (sides[i] == SIDE_ON)
1837                 {
1838                         VectorCopy (p1, neww->points[neww->numpoints]);
1839                         neww->numpoints++;
1840                         continue;
1841                 }
1842
1843                 if (sides[i] == SIDE_FRONT)
1844                 {
1845                         VectorCopy (p1, neww->points[neww->numpoints]);
1846                         neww->numpoints++;
1847                 }
1848
1849                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1850                         continue;
1851
1852                 // generate a split point
1853                 p2 = in->points[(i+1)%in->numpoints];
1854
1855                 dot = dists[i] / (dists[i]-dists[i+1]);
1856                 for (j = 0;j < 3;j++)
1857                 {       // avoid round off error when possible
1858                         if (split->normal[j] == 1)
1859                                 mid[j] = split->dist;
1860                         else if (split->normal[j] == -1)
1861                                 mid[j] = -split->dist;
1862                         else
1863                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1864                 }
1865
1866                 VectorCopy (mid, neww->points[neww->numpoints]);
1867                 neww->numpoints++;
1868         }
1869
1870         // free the original winding
1871         FreeWinding (in);
1872
1873         return neww;
1874 }
1875
1876
1877 /*
1878 ==================
1879 DivideWinding
1880
1881 Divides a winding by a plane, producing one or two windings.  The
1882 original winding is not damaged or freed.  If only on one side, the
1883 returned winding will be the input winding.  If on both sides, two
1884 new windings will be created.
1885 ==================
1886 */
1887 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1888 {
1889         double  dists[MAX_POINTS_ON_WINDING + 1];
1890         int             sides[MAX_POINTS_ON_WINDING + 1];
1891         int             counts[3];
1892         double  dot;
1893         int             i, j;
1894         double  *p1, *p2;
1895         double  mid[3];
1896         winding_t       *f, *b;
1897         int             maxpts;
1898
1899         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1900
1901         // determine sides for each point
1902         for (i = 0;i < in->numpoints;i++)
1903         {
1904                 dot = DotProduct (in->points[i], split->normal);
1905                 dot -= split->dist;
1906                 dists[i] = dot;
1907                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1908                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1909                 else sides[i] = SIDE_ON;
1910                 counts[sides[i]]++;
1911         }
1912         sides[i] = sides[0];
1913         dists[i] = dists[0];
1914
1915         *front = *back = NULL;
1916
1917         if (!counts[0])
1918         {
1919                 *back = in;
1920                 return;
1921         }
1922         if (!counts[1])
1923         {
1924                 *front = in;
1925                 return;
1926         }
1927
1928         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1929
1930         if (maxpts > MAX_POINTS_ON_WINDING)
1931                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1932
1933         *front = f = NewWinding (maxpts);
1934         *back = b = NewWinding (maxpts);
1935
1936         for (i = 0;i < in->numpoints;i++)
1937         {
1938                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1939                         Sys_Error ("DivideWinding: points exceeded estimate");
1940
1941                 p1 = in->points[i];
1942
1943                 if (sides[i] == SIDE_ON)
1944                 {
1945                         VectorCopy (p1, f->points[f->numpoints]);
1946                         f->numpoints++;
1947                         VectorCopy (p1, b->points[b->numpoints]);
1948                         b->numpoints++;
1949                         continue;
1950                 }
1951
1952                 if (sides[i] == SIDE_FRONT)
1953                 {
1954                         VectorCopy (p1, f->points[f->numpoints]);
1955                         f->numpoints++;
1956                 }
1957                 else if (sides[i] == SIDE_BACK)
1958                 {
1959                         VectorCopy (p1, b->points[b->numpoints]);
1960                         b->numpoints++;
1961                 }
1962
1963                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1964                         continue;
1965
1966                 // generate a split point
1967                 p2 = in->points[(i+1)%in->numpoints];
1968
1969                 dot = dists[i] / (dists[i]-dists[i+1]);
1970                 for (j = 0;j < 3;j++)
1971                 {       // avoid round off error when possible
1972                         if (split->normal[j] == 1)
1973                                 mid[j] = split->dist;
1974                         else if (split->normal[j] == -1)
1975                                 mid[j] = -split->dist;
1976                         else
1977                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1978                 }
1979
1980                 VectorCopy (mid, f->points[f->numpoints]);
1981                 f->numpoints++;
1982                 VectorCopy (mid, b->points[b->numpoints]);
1983                 b->numpoints++;
1984         }
1985 }
1986
1987 typedef struct portal_s
1988 {
1989         mplane_t plane;
1990         mnode_t *nodes[2];              // [0] = front side of plane
1991         struct portal_s *next[2];
1992         winding_t *winding;
1993         struct portal_s *chain; // all portals are linked into a list
1994 }
1995 portal_t;
1996
1997 static portal_t *portalchain;
1998
1999 /*
2000 ===========
2001 AllocPortal
2002 ===========
2003 */
2004 static portal_t *AllocPortal (void)
2005 {
2006         portal_t *p;
2007         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2008         p->chain = portalchain;
2009         portalchain = p;
2010         return p;
2011 }
2012
2013 static void FreePortal(portal_t *p)
2014 {
2015         Mem_Free(p);
2016 }
2017
2018 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2019 {
2020         // calculate children first
2021         if (node->children[0]->contents >= 0)
2022                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2023         if (node->children[1]->contents >= 0)
2024                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2025
2026         // make combined bounding box from children
2027         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2028         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2029         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2030         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2031         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2032         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2033 }
2034
2035 static void Mod_FinalizePortals(void)
2036 {
2037         int i, j, numportals, numpoints;
2038         portal_t *p, *pnext;
2039         mportal_t *portal;
2040         mvertex_t *point;
2041         mleaf_t *leaf, *endleaf;
2042         winding_t *w;
2043
2044         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2045         leaf = loadmodel->leafs;
2046         endleaf = leaf + loadmodel->numleafs;
2047         for (;leaf < endleaf;leaf++)
2048         {
2049                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2050                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2051         }
2052         p = portalchain;
2053         while(p)
2054         {
2055                 if (p->winding)
2056                 {
2057                         for (i = 0;i < 2;i++)
2058                         {
2059                                 leaf = (mleaf_t *)p->nodes[i];
2060                                 w = p->winding;
2061                                 for (j = 0;j < w->numpoints;j++)
2062                                 {
2063                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2064                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2065                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2066                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2067                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2068                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2069                                 }
2070                         }
2071                 }
2072                 p = p->chain;
2073         }
2074
2075         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2076
2077         // tally up portal and point counts
2078         p = portalchain;
2079         numportals = 0;
2080         numpoints = 0;
2081         while(p)
2082         {
2083                 // note: this check must match the one below or it will usually corrupt memory
2084                 // 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
2085                 if (p->winding && p->nodes[0] != p->nodes[1]
2086                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2087                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2088                 {
2089                         numportals += 2;
2090                         numpoints += p->winding->numpoints * 2;
2091                 }
2092                 p = p->chain;
2093         }
2094         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2095         loadmodel->numportals = numportals;
2096         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2097         loadmodel->numportalpoints = numpoints;
2098         // clear all leaf portal chains
2099         for (i = 0;i < loadmodel->numleafs;i++)
2100                 loadmodel->leafs[i].portals = NULL;
2101         // process all portals in the global portal chain, while freeing them
2102         portal = loadmodel->portals;
2103         point = loadmodel->portalpoints;
2104         p = portalchain;
2105         portalchain = NULL;
2106         while (p)
2107         {
2108                 pnext = p->chain;
2109
2110                 if (p->winding)
2111                 {
2112                         // note: this check must match the one above or it will usually corrupt memory
2113                         // 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
2114                         if (p->nodes[0] != p->nodes[1]
2115                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2116                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2117                         {
2118                                 // first make the back to front portal (forward portal)
2119                                 portal->points = point;
2120                                 portal->numpoints = p->winding->numpoints;
2121                                 portal->plane.dist = p->plane.dist;
2122                                 VectorCopy(p->plane.normal, portal->plane.normal);
2123                                 portal->here = (mleaf_t *)p->nodes[1];
2124                                 portal->past = (mleaf_t *)p->nodes[0];
2125                                 // copy points
2126                                 for (j = 0;j < portal->numpoints;j++)
2127                                 {
2128                                         VectorCopy(p->winding->points[j], point->position);
2129                                         point++;
2130                                 }
2131                                 PlaneClassify(&portal->plane);
2132
2133                                 // link into leaf's portal chain
2134                                 portal->next = portal->here->portals;
2135                                 portal->here->portals = portal;
2136
2137                                 // advance to next portal
2138                                 portal++;
2139
2140                                 // then make the front to back portal (backward portal)
2141                                 portal->points = point;
2142                                 portal->numpoints = p->winding->numpoints;
2143                                 portal->plane.dist = -p->plane.dist;
2144                                 VectorNegate(p->plane.normal, portal->plane.normal);
2145                                 portal->here = (mleaf_t *)p->nodes[0];
2146                                 portal->past = (mleaf_t *)p->nodes[1];
2147                                 // copy points
2148                                 for (j = portal->numpoints - 1;j >= 0;j--)
2149                                 {
2150                                         VectorCopy(p->winding->points[j], point->position);
2151                                         point++;
2152                                 }
2153                                 PlaneClassify(&portal->plane);
2154
2155                                 // link into leaf's portal chain
2156                                 portal->next = portal->here->portals;
2157                                 portal->here->portals = portal;
2158
2159                                 // advance to next portal
2160                                 portal++;
2161                         }
2162                         FreeWinding(p->winding);
2163                 }
2164                 FreePortal(p);
2165                 p = pnext;
2166         }
2167 }
2168
2169 /*
2170 =============
2171 AddPortalToNodes
2172 =============
2173 */
2174 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2175 {
2176         if (!front)
2177                 Host_Error ("AddPortalToNodes: NULL front node");
2178         if (!back)
2179                 Host_Error ("AddPortalToNodes: NULL back node");
2180         if (p->nodes[0] || p->nodes[1])
2181                 Host_Error ("AddPortalToNodes: already included");
2182         // 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
2183
2184         p->nodes[0] = front;
2185         p->next[0] = (portal_t *)front->portals;
2186         front->portals = (mportal_t *)p;
2187
2188         p->nodes[1] = back;
2189         p->next[1] = (portal_t *)back->portals;
2190         back->portals = (mportal_t *)p;
2191 }
2192
2193 /*
2194 =============
2195 RemovePortalFromNode
2196 =============
2197 */
2198 static void RemovePortalFromNodes(portal_t *portal)
2199 {
2200         int i;
2201         mnode_t *node;
2202         void **portalpointer;
2203         portal_t *t;
2204         for (i = 0;i < 2;i++)
2205         {
2206                 node = portal->nodes[i];
2207
2208                 portalpointer = (void **) &node->portals;
2209                 while (1)
2210                 {
2211                         t = *portalpointer;
2212                         if (!t)
2213                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2214
2215                         if (t == portal)
2216                         {
2217                                 if (portal->nodes[0] == node)
2218                                 {
2219                                         *portalpointer = portal->next[0];
2220                                         portal->nodes[0] = NULL;
2221                                 }
2222                                 else if (portal->nodes[1] == node)
2223                                 {
2224                                         *portalpointer = portal->next[1];
2225                                         portal->nodes[1] = NULL;
2226                                 }
2227                                 else
2228                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2229                                 break;
2230                         }
2231
2232                         if (t->nodes[0] == node)
2233                                 portalpointer = (void **) &t->next[0];
2234                         else if (t->nodes[1] == node)
2235                                 portalpointer = (void **) &t->next[1];
2236                         else
2237                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2238                 }
2239         }
2240 }
2241
2242 static void Mod_RecursiveNodePortals (mnode_t *node)
2243 {
2244         int side;
2245         mnode_t *front, *back, *other_node;
2246         mplane_t clipplane, *plane;
2247         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2248         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2249
2250         // if a leaf, we're done
2251         if (node->contents)
2252                 return;
2253
2254         plane = node->plane;
2255
2256         front = node->children[0];
2257         back = node->children[1];
2258         if (front == back)
2259                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2260
2261         // create the new portal by generating a polygon for the node plane,
2262         // and clipping it by all of the other portals (which came from nodes above this one)
2263         nodeportal = AllocPortal ();
2264         nodeportal->plane = *node->plane;
2265
2266         nodeportalwinding = BaseWindingForPlane (node->plane);
2267         side = 0;       // shut up compiler warning
2268         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2269         {
2270                 clipplane = portal->plane;
2271                 if (portal->nodes[0] == portal->nodes[1])
2272                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2273                 if (portal->nodes[0] == node)
2274                         side = 0;
2275                 else if (portal->nodes[1] == node)
2276                 {
2277                         clipplane.dist = -clipplane.dist;
2278                         VectorNegate (clipplane.normal, clipplane.normal);
2279                         side = 1;
2280                 }
2281                 else
2282                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2283
2284                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2285                 if (!nodeportalwinding)
2286                 {
2287                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2288                         break;
2289                 }
2290         }
2291
2292         if (nodeportalwinding)
2293         {
2294                 // if the plane was not clipped on all sides, there was an error
2295                 nodeportal->winding = nodeportalwinding;
2296                 AddPortalToNodes (nodeportal, front, back);
2297         }
2298
2299         // split the portals of this node along this node's plane and assign them to the children of this node
2300         // (migrating the portals downward through the tree)
2301         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2302         {
2303                 if (portal->nodes[0] == portal->nodes[1])
2304                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2305                 if (portal->nodes[0] == node)
2306                         side = 0;
2307                 else if (portal->nodes[1] == node)
2308                         side = 1;
2309                 else
2310                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2311                 nextportal = portal->next[side];
2312
2313                 other_node = portal->nodes[!side];
2314                 RemovePortalFromNodes (portal);
2315
2316                 // cut the portal into two portals, one on each side of the node plane
2317                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2318
2319                 if (!frontwinding)
2320                 {
2321                         if (side == 0)
2322                                 AddPortalToNodes (portal, back, other_node);
2323                         else
2324                                 AddPortalToNodes (portal, other_node, back);
2325                         continue;
2326                 }
2327                 if (!backwinding)
2328                 {
2329                         if (side == 0)
2330                                 AddPortalToNodes (portal, front, other_node);
2331                         else
2332                                 AddPortalToNodes (portal, other_node, front);
2333                         continue;
2334                 }
2335
2336                 // the winding is split
2337                 splitportal = AllocPortal ();
2338                 temp = splitportal->chain;
2339                 *splitportal = *portal;
2340                 splitportal->chain = temp;
2341                 splitportal->winding = backwinding;
2342                 FreeWinding (portal->winding);
2343                 portal->winding = frontwinding;
2344
2345                 if (side == 0)
2346                 {
2347                         AddPortalToNodes (portal, front, other_node);
2348                         AddPortalToNodes (splitportal, back, other_node);
2349                 }
2350                 else
2351                 {
2352                         AddPortalToNodes (portal, other_node, front);
2353                         AddPortalToNodes (splitportal, other_node, back);
2354                 }
2355         }
2356
2357         Mod_RecursiveNodePortals(front);
2358         Mod_RecursiveNodePortals(back);
2359 }
2360
2361
2362 static void Mod_MakePortals(void)
2363 {
2364         portalchain = NULL;
2365         Mod_RecursiveNodePortals (loadmodel->nodes);
2366         Mod_FinalizePortals();
2367 }
2368
2369 /*
2370 =================
2371 Mod_LoadBrushModel
2372 =================
2373 */
2374 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2375 {
2376         int                     i, j;
2377         dheader_t       *header;
2378         dmodel_t        *bm;
2379         mempool_t       *mainmempool;
2380         char            *loadname;
2381
2382         mod->type = mod_brush;
2383
2384         header = (dheader_t *)buffer;
2385
2386         i = LittleLong (header->version);
2387         if (i != BSPVERSION && i != 30)
2388                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2389         mod->ishlbsp = i == 30;
2390         if (loadmodel->isworldmodel)
2391         {
2392                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2393                 // until we get a texture for it...
2394                 R_ResetQuakeSky();
2395         }
2396
2397 // swap all the lumps
2398         mod_base = (qbyte *)header;
2399
2400         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2401                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2402
2403 // load into heap
2404
2405         // store which lightmap format to use
2406         mod->lightmaprgba = r_lightmaprgba.integer;
2407
2408         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2409         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2410         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2411         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2412         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2413         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2414         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2415         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2416         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2417         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2418         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2419         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2420         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2421         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2422         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2423
2424         Mod_MakeHull0 ();
2425         Mod_MakePortals();
2426
2427         mod->numframes = 2;             // regular and alternate animation
2428
2429         mainmempool = mod->mempool;
2430         loadname = mod->name;
2431
2432         Mod_LoadLightList ();
2433
2434 //
2435 // set up the submodels (FIXME: this is confusing)
2436 //
2437         for (i = 0;i < mod->numsubmodels;i++)
2438         {
2439                 int k, l;
2440                 float dist, modelyawradius, modelradius, *vec;
2441                 msurface_t *surf;
2442
2443                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2444                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2445                 modelyawradius = 0;
2446                 modelradius = 0;
2447
2448                 bm = &mod->submodels[i];
2449
2450                 mod->hulls[0].firstclipnode = bm->headnode[0];
2451                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2452                 {
2453                         mod->hulls[j].firstclipnode = bm->headnode[j];
2454                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2455                 }
2456
2457                 mod->firstmodelsurface = bm->firstface;
2458                 mod->nummodelsurfaces = bm->numfaces;
2459
2460                 mod->DrawSky = NULL;
2461                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2462                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2463                 {
2464                         // we only need to have a drawsky function if it is used (usually only on world model)
2465                         if (surf->shader == &Cshader_sky)
2466                                 mod->DrawSky = R_DrawBrushModelSky;
2467                         for (k = 0;k < surf->numedges;k++)
2468                         {
2469                                 l = mod->surfedges[k + surf->firstedge];
2470                                 if (l > 0)
2471                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2472                                 else
2473                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2474                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2475                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2476                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2477                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2478                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2479                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2480                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2481                                 if (modelyawradius < dist)
2482                                         modelyawradius = dist;
2483                                 dist += vec[2]*vec[2];
2484                                 if (modelradius < dist)
2485                                         modelradius = dist;
2486                         }
2487                 }
2488                 modelyawradius = sqrt(modelyawradius);
2489                 modelradius = sqrt(modelradius);
2490                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2491                 mod->yawmins[2] = mod->normalmins[2];
2492                 mod->yawmaxs[2] = mod->normalmaxs[2];
2493                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2494                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2495                 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2496                 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2497                 {
2498                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2499                         VectorClear(mod->normalmins);
2500                         VectorClear(mod->normalmaxs);
2501                         VectorClear(mod->yawmins);
2502                         VectorClear(mod->yawmaxs);
2503                         VectorClear(mod->rotatedmins);
2504                         VectorClear(mod->rotatedmaxs);
2505                 }
2506
2507                 mod->numleafs = bm->visleafs;
2508
2509                 mod->Draw = R_DrawBrushModelNormal;
2510                 mod->DrawShadow = NULL;
2511
2512                 // LordHavoc: only register submodels if it is the world
2513                 // (prevents bsp models from replacing world submodels)
2514                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2515                 {
2516                         char    name[10];
2517                         // duplicate the basic information
2518                         sprintf (name, "*%i", i+1);
2519                         loadmodel = Mod_FindName (name);
2520                         *loadmodel = *mod;
2521                         strcpy (loadmodel->name, name);
2522                         // textures and memory belong to the main model
2523                         loadmodel->texturepool = NULL;
2524                         loadmodel->mempool = NULL;
2525                         mod = loadmodel;
2526                 }
2527         }
2528 }
2529