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