]> icculus.org git repositories - divverent/darkplaces.git/blob - model_brush.c
texcoord arrays are now 3 component (padded to 4 floats)
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
24
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
26
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
33 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
34
35 #define NUM_DETAILTEXTURES 1
36 static rtexture_t *detailtextures[NUM_DETAILTEXTURES];
37 static rtexturepool_t *detailtexturepool;
38
39 /*
40 ===============
41 Mod_BrushInit
42 ===============
43 */
44 void Mod_BrushInit (void)
45 {
46 //      Cvar_RegisterVariable(&r_subdivide_size);
47         Cvar_RegisterVariable(&halflifebsp);
48         Cvar_RegisterVariable(&r_novis);
49         Cvar_RegisterVariable(&r_miplightmaps);
50         Cvar_RegisterVariable(&r_lightmaprgba);
51         Cvar_RegisterVariable(&r_nosurftextures);
52         Cvar_RegisterVariable(&r_sortsurfaces);
53         memset(mod_novis, 0xff, sizeof(mod_novis));
54 }
55
56 void Mod_BrushStartup (void)
57 {
58         int i, x, y, light;
59         float vc[3], vx[3], vy[3], vn[3], lightdir[3];
60 #define DETAILRESOLUTION 256
61         qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
62         detailtexturepool = R_AllocTexturePool();
63         lightdir[0] = 0.5;
64         lightdir[1] = 1;
65         lightdir[2] = -0.25;
66         VectorNormalize(lightdir);
67         for (i = 0;i < NUM_DETAILTEXTURES;i++)
68         {
69                 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
70                 for (y = 0;y < DETAILRESOLUTION;y++)
71                 {
72                         for (x = 0;x < DETAILRESOLUTION;x++)
73                         {
74                                 vc[0] = x;
75                                 vc[1] = y;
76                                 vc[2] = noise[y][x] * (1.0f / 32.0f);
77                                 vx[0] = x + 1;
78                                 vx[1] = y;
79                                 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
80                                 vy[0] = x;
81                                 vy[1] = y + 1;
82                                 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
83                                 VectorSubtract(vx, vc, vx);
84                                 VectorSubtract(vy, vc, vy);
85                                 CrossProduct(vx, vy, vn);
86                                 VectorNormalize(vn);
87                                 light = 128 - DotProduct(vn, lightdir) * 128;
88                                 light = bound(0, light, 255);
89                                 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
90                                 data[y][x][3] = 255;
91                         }
92                 }
93                 detailtextures[i] = R_LoadTexture(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE);
94         }
95 }
96
97 void Mod_BrushShutdown (void)
98 {
99         int i;
100         for (i = 0;i < NUM_DETAILTEXTURES;i++)
101                 R_FreeTexture(detailtextures[i]);
102         R_FreeTexturePool(&detailtexturepool);
103 }
104
105 /*
106 ===============
107 Mod_PointInLeaf
108 ===============
109 */
110 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model)
111 {
112         mnode_t *node;
113
114         if (model == NULL)
115                 return NULL;
116
117         Mod_CheckLoaded(model);
118
119         // LordHavoc: modified to start at first clip node,
120         // in other words: first node of the (sub)model
121         node = model->nodes + model->hulls[0].firstclipnode;
122         while (node->contents == 0)
123                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
124
125         return (mleaf_t *)node;
126 }
127
128 int Mod_PointContents (const vec3_t p, model_t *model)
129 {
130         mnode_t *node;
131
132         if (model == NULL)
133                 return CONTENTS_EMPTY;
134
135         Mod_CheckLoaded(model);
136
137         // LordHavoc: modified to start at first clip node,
138         // in other words: first node of the (sub)model
139         node = model->nodes + model->hulls[0].firstclipnode;
140         while (node->contents == 0)
141                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
142
143         return ((mleaf_t *)node)->contents;
144 }
145
146 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
147 {
148         if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
149         pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
150         pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
151         pos[0]-=1;
152         pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
153         pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
154         pos[1]-=1;
155         pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
156         pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
157         pos[2]-=1;
158 }
159
160
161 /*
162 ===================
163 Mod_DecompressVis
164 ===================
165 */
166 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
167 {
168         static qbyte decompressed[MAX_MAP_LEAFS/8];
169         int c;
170         qbyte *out;
171         int row;
172
173         row = (model->numleafs+7)>>3;
174         out = decompressed;
175
176         do
177         {
178                 if (*in)
179                 {
180                         *out++ = *in++;
181                         continue;
182                 }
183
184                 c = in[1];
185                 in += 2;
186                 while (c)
187                 {
188                         *out++ = 0;
189                         c--;
190                 }
191         } while (out - decompressed < row);
192
193         return decompressed;
194 }
195
196 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
197 {
198         if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
199                 return mod_novis;
200         return Mod_DecompressVis (leaf->compressed_vis, model);
201 }
202
203 /*
204 =================
205 Mod_LoadTextures
206 =================
207 */
208 static void Mod_LoadTextures (lump_t *l)
209 {
210         int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
211         miptex_t *dmiptex;
212         texture_t *tx, *tx2, *anims[10], *altanims[10];
213         dmiptexlump_t *m;
214         qbyte *data, *mtdata, *data2;
215         char name[256];
216
217         loadmodel->textures = NULL;
218
219         if (!l->filelen)
220                 return;
221
222         m = (dmiptexlump_t *)(mod_base + l->fileofs);
223
224         m->nummiptex = LittleLong (m->nummiptex);
225
226         // add two slots for notexture walls and notexture liquids
227         loadmodel->numtextures = m->nummiptex + 2;
228         loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(texture_t));
229
230         // fill out all slots with notexture
231         for (i = 0, tx = loadmodel->textures;i < loadmodel->numtextures;i++, tx++)
232         {
233                 tx->width = 16;
234                 tx->height = 16;
235                 tx->texture = r_notexture;
236                 tx->shader = &Cshader_wall_lightmap;
237                 if (i == loadmodel->numtextures - 1)
238                 {
239                         tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
240                         tx->shader = &Cshader_water;
241                 }
242         }
243
244         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
245         dofs = m->dataofs;
246         // LordHavoc: mostly rewritten map texture loader
247         for (i = 0;i < m->nummiptex;i++)
248         {
249                 dofs[i] = LittleLong(dofs[i]);
250                 if (dofs[i] == -1 || r_nosurftextures.integer)
251                         continue;
252                 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
253
254                 // make sure name is no more than 15 characters
255                 for (j = 0;dmiptex->name[j] && j < 15;j++)
256                         name[j] = dmiptex->name[j];
257                 name[j] = 0;
258
259                 mtwidth = LittleLong (dmiptex->width);
260                 mtheight = LittleLong (dmiptex->height);
261                 mtdata = NULL;
262                 j = LittleLong (dmiptex->offsets[0]);
263                 if (j)
264                 {
265                         // texture included
266                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
267                         {
268                                 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
269                                 continue;
270                         }
271                         mtdata = (qbyte *)dmiptex + j;
272                 }
273
274                 if ((mtwidth & 15) || (mtheight & 15))
275                         Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
276
277                 // LordHavoc: force all names to lowercase
278                 for (j = 0;name[j];j++)
279                         if (name[j] >= 'A' && name[j] <= 'Z')
280                                 name[j] += 'a' - 'A';
281
282                 tx = loadmodel->textures + i;
283                 strcpy(tx->name, name);
284                 tx->width = mtwidth;
285                 tx->height = mtheight;
286
287                 if (!tx->name[0])
288                 {
289                         sprintf(tx->name, "unnamed%i", i);
290                         Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
291                 }
292
293                 // LordHavoc: HL sky textures are entirely different than quake
294                 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
295                 {
296                         if (loadmodel->isworldmodel)
297                         {
298                                 data = loadimagepixels(tx->name, false, 0, 0);
299                                 if (data)
300                                 {
301                                         if (image_width == 256 && image_height == 128)
302                                         {
303                                                 R_InitSky (data, 4);
304                                                 Mem_Free(data);
305                                         }
306                                         else
307                                         {
308                                                 Mem_Free(data);
309                                                 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
310                                                 if (mtdata != NULL)
311                                                         R_InitSky (mtdata, 1);
312                                         }
313                                 }
314                                 else if (mtdata != NULL)
315                                         R_InitSky (mtdata, 1);
316                         }
317                 }
318                 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
319                 {
320                         tx->fogtexture = image_masktex;
321                         strcpy(name, tx->name);
322                         strcat(name, "_glow");
323                         tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
324                 }
325                 else
326                 {
327                         if (loadmodel->ishlbsp)
328                         {
329                                 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
330                                 {
331                                         // texture included
332                                         tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
333                                         if (R_TextureHasAlpha(tx->texture))
334                                         {
335                                                 // make mask texture
336                                                 for (j = 0;j < image_width * image_height;j++)
337                                                         data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
338                                                 strcpy(name, tx->name);
339                                                 strcat(name, "_fog");
340                                                 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
341                                         }
342                                         Mem_Free(data);
343                                 }
344                                 else if ((data = W_GetTexture(tx->name)))
345                                 {
346                                         // get the size from the wad texture
347                                         tx->width = image_width;
348                                         tx->height = image_height;
349                                         tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
350                                         if (R_TextureHasAlpha(tx->texture))
351                                         {
352                                                 // make mask texture
353                                                 for (j = 0;j < image_width * image_height;j++)
354                                                         data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
355                                                 strcpy(name, tx->name);
356                                                 strcat(name, "_fog");
357                                                 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
358                                         }
359                                         Mem_Free(data);
360                                 }
361                                 else
362                                 {
363                                         tx->width = 16;
364                                         tx->height = 16;
365                                         tx->texture = r_notexture;
366                                 }
367                         }
368                         else
369                         {
370                                 if (mtdata) // texture included
371                                 {
372                                         int fullbrights;
373                                         data = mtdata;
374                                         fullbrights = false;
375                                         if (r_fullbrights.value && tx->name[0] != '*')
376                                         {
377                                                 for (j = 0;j < tx->width*tx->height;j++)
378                                                 {
379                                                         if (data[j] >= 224) // fullbright
380                                                         {
381                                                                 fullbrights = true;
382                                                                 break;
383                                                         }
384                                                 }
385                                         }
386                                         if (fullbrights)
387                                         {
388                                                 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
389                                                 for (j = 0;j < tx->width*tx->height;j++)
390                                                         data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
391                                                 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
392                                                 strcpy(name, tx->name);
393                                                 strcat(name, "_glow");
394                                                 for (j = 0;j < tx->width*tx->height;j++)
395                                                         data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
396                                                 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
397                                                 Mem_Free(data2);
398                                         }
399                                         else
400                                                 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
401                                 }
402                                 else // no texture, and no external replacement texture was found
403                                 {
404                                         tx->width = 16;
405                                         tx->height = 16;
406                                         tx->texture = r_notexture;
407                                 }
408                         }
409                 }
410
411                 if (tx->name[0] == '*')
412                 {
413                         tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
414                         // LordHavoc: some turbulent textures should be fullbright and solid
415                         if (!strncmp(tx->name,"*lava",5)
416                          || !strncmp(tx->name,"*teleport",9)
417                          || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
418                                 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
419                         tx->shader = &Cshader_water;
420                 }
421                 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
422                 {
423                         tx->flags |= SURF_DRAWSKY;
424                         tx->shader = &Cshader_sky;
425                 }
426                 else
427                 {
428                         tx->flags |= SURF_LIGHTMAP;
429                         if (!tx->fogtexture)
430                                 tx->flags |= SURF_CLIPSOLID;
431                         tx->shader = &Cshader_wall_lightmap;
432                 }
433
434                 tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
435         }
436
437         // sequence the animations
438         for (i = 0;i < m->nummiptex;i++)
439         {
440                 tx = loadmodel->textures + i;
441                 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
442                         continue;
443                 if (tx->anim_total[0] || tx->anim_total[1])
444                         continue;       // already sequenced
445
446                 // find the number of frames in the animation
447                 memset (anims, 0, sizeof(anims));
448                 memset (altanims, 0, sizeof(altanims));
449
450                 for (j = i;j < m->nummiptex;j++)
451                 {
452                         tx2 = loadmodel->textures + j;
453                         if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
454                                 continue;
455
456                         num = tx2->name[1];
457                         if (num >= '0' && num <= '9')
458                                 anims[num - '0'] = tx2;
459                         else if (num >= 'a' && num <= 'j')
460                                 altanims[num - 'a'] = tx2;
461                         else
462                                 Con_Printf ("Bad animating texture %s\n", tx->name);
463                 }
464
465                 max = altmax = 0;
466                 for (j = 0;j < 10;j++)
467                 {
468                         if (anims[j])
469                                 max = j + 1;
470                         if (altanims[j])
471                                 altmax = j + 1;
472                 }
473                 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
474
475                 incomplete = false;
476                 for (j = 0;j < max;j++)
477                 {
478                         if (!anims[j])
479                         {
480                                 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
481                                 incomplete = true;
482                         }
483                 }
484                 for (j = 0;j < altmax;j++)
485                 {
486                         if (!altanims[j])
487                         {
488                                 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
489                                 incomplete = true;
490                         }
491                 }
492                 if (incomplete)
493                         continue;
494
495                 if (altmax < 1)
496                 {
497                         // if there is no alternate animation, duplicate the primary
498                         // animation into the alternate
499                         altmax = max;
500                         for (k = 0;k < 10;k++)
501                                 altanims[k] = anims[k];
502                 }
503
504                 // link together the primary animation
505                 for (j = 0;j < max;j++)
506                 {
507                         tx2 = anims[j];
508                         tx2->animated = true;
509                         tx2->anim_total[0] = max;
510                         tx2->anim_total[1] = altmax;
511                         for (k = 0;k < 10;k++)
512                         {
513                                 tx2->anim_frames[0][k] = anims[k];
514                                 tx2->anim_frames[1][k] = altanims[k];
515                         }
516                 }
517
518                 // if there really is an alternate anim...
519                 if (anims[0] != altanims[0])
520                 {
521                         // link together the alternate animation
522                         for (j = 0;j < altmax;j++)
523                         {
524                                 tx2 = altanims[j];
525                                 tx2->animated = true;
526                                 // the primary/alternate are reversed here
527                                 tx2->anim_total[0] = altmax;
528                                 tx2->anim_total[1] = max;
529                                 for (k = 0;k < 10;k++)
530                                 {
531                                         tx2->anim_frames[0][k] = altanims[k];
532                                         tx2->anim_frames[1][k] = anims[k];
533                                 }
534                         }
535                 }
536         }
537 }
538
539 /*
540 =================
541 Mod_LoadLighting
542 =================
543 */
544 static void Mod_LoadLighting (lump_t *l)
545 {
546         int i;
547         qbyte *in, *out, *data, d;
548         char litfilename[1024];
549         loadmodel->lightdata = NULL;
550         if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
551         {
552                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
553                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
554         }
555         else // LordHavoc: bsp version 29 (normal white lighting)
556         {
557                 // LordHavoc: hope is not lost yet, check for a .lit file to load
558                 strcpy(litfilename, loadmodel->name);
559                 COM_StripExtension(litfilename, litfilename);
560                 strcat(litfilename, ".lit");
561                 data = (qbyte*) COM_LoadFile (litfilename, false);
562                 if (data)
563                 {
564                         if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
565                         {
566                                 i = LittleLong(((int *)data)[1]);
567                                 if (i == 1)
568                                 {
569                                         Con_DPrintf("%s loaded", litfilename);
570                                         loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
571                                         memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
572                                         Mem_Free(data);
573                                         return;
574                                 }
575                                 else
576                                 {
577                                         Con_Printf("Unknown .lit file version (%d)\n", i);
578                                         Mem_Free(data);
579                                 }
580                         }
581                         else
582                         {
583                                 if (loadsize == 8)
584                                         Con_Printf("Empty .lit file, ignoring\n");
585                                 else
586                                         Con_Printf("Corrupt .lit file (old version?), ignoring\n");
587                                 Mem_Free(data);
588                         }
589                 }
590                 // LordHavoc: oh well, expand the white lighting data
591                 if (!l->filelen)
592                         return;
593                 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
594                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
595                 out = loadmodel->lightdata;
596                 memcpy (in, mod_base + l->fileofs, l->filelen);
597                 for (i = 0;i < l->filelen;i++)
598                 {
599                         d = *in++;
600                         *out++ = d;
601                         *out++ = d;
602                         *out++ = d;
603                 }
604         }
605 }
606
607 void Mod_LoadLightList(void)
608 {
609         int a, n, numlights;
610         char lightsfilename[1024], *s, *t, *lightsstring;
611         mlight_t *e;
612
613         strcpy(lightsfilename, loadmodel->name);
614         COM_StripExtension(lightsfilename, lightsfilename);
615         strcat(lightsfilename, ".lights");
616         s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
617         if (s)
618         {
619                 numlights = 0;
620                 while (*s)
621                 {
622                         while (*s && *s != '\n')
623                                 s++;
624                         if (!*s)
625                         {
626                                 Mem_Free(lightsstring);
627                                 Host_Error("lights file must end with a newline\n");
628                         }
629                         s++;
630                         numlights++;
631                 }
632                 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
633                 s = lightsstring;
634                 n = 0;
635                 while (*s && n < numlights)
636                 {
637                         t = s;
638                         while (*s && *s != '\n')
639                                 s++;
640                         if (!*s)
641                         {
642                                 Mem_Free(lightsstring);
643                                 Host_Error("misparsed lights file!\n");
644                         }
645                         e = loadmodel->lights + n;
646                         *s = 0;
647                         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);
648                         *s = '\n';
649                         if (a != 14)
650                         {
651                                 Mem_Free(lightsstring);
652                                 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);
653                         }
654                         s++;
655                         n++;
656                 }
657                 if (*s)
658                 {
659                         Mem_Free(lightsstring);
660                         Host_Error("misparsed lights file!\n");
661                 }
662                 loadmodel->numlights = numlights;
663                 Mem_Free(lightsstring);
664         }
665 }
666
667
668
669 /*
670 // svbspmesh_t is in model_brush.h
671
672 typedef struct svbsppolygon_s
673 {
674         struct svbsppolygon_s *next;
675         int numverts;
676         float *verts;
677         float normal[3], dist;
678 }
679 svbsppolygon_t;
680
681 typedef struct svbspnode_s
682 {
683         // true if this is a leaf (has no children), not a node
684         int isleaf;
685         // (shared) parent node
686         struct svbspnode_s *parent;
687         // (leaf) dark or lit leaf
688         int dark;
689         // (leaf) polygons bounding this leaf
690         svbsppolygon_t *polygons;
691         // (node) children
692         struct svbspnode_s *children[2];
693         // (node) splitting plane
694         float normal[3], dist;
695 }
696 svbspnode_t;
697
698 svbspnode_t *Mod_SVBSP_AllocNode(svbspnode_t *parent, svbspnode_t *child0, svbspnode_t *child1, float *normal, float dist)
699 {
700         svbspnode_t *node;
701         node = Mem_Alloc(loadmodel->mempool, sizeof(svbspnode_t));
702         node->parent = parent;
703         node->children[0] = child0;
704         node->children[1] = child1;
705         VectorCopy(normal, node->normal);
706         node->dist = dist;
707         return node;
708 }
709
710 svbspnode_t *Mod_SVBSP_AllocLeaf(svbspnode_t *parent, int dark)
711 {
712         svbspnode_t *leaf;
713         leaf = Mem_Alloc(loadmodel->mempool, sizeof(svbspnode_t));
714         leaf->isleaf = true;
715         leaf->parent = parent;
716         leaf->dark = dark;
717         return leaf;
718 }
719
720 svbspnode_t *Mod_SVBSP_NewTree(void)
721 {
722         return Mod_SVBSP_AllocLeaf(NULL, false);
723 }
724
725 void Mod_SVBSP_FreeTree(svbspnode_t *node)
726 {
727         if (!node->isleaf)
728         {
729                 Mod_SVBSP_FreeTree(node->children[0]);
730                 Mod_SVBSP_FreeTree(node->children[1]);
731         }
732         Mem_Free(node);
733 }
734
735 void Mod_SVBSP_RecursiveAddPolygon(svbspnode_t *node, int numverts, float *verts, float *normal, float dist, int constructmode)
736 {
737         int i, j, numvertsfront, numvertsback, maxverts, counts[3];
738         float *vertsfront, *vertsback, *v, d, temp[3];
739         float dists[4096];
740         qbyte sides[4096];
741         svbsppolygon_t *poly;
742         if (node->isleaf)
743         {
744                 if (constructmode == 0)
745                 {
746                         // construct tree structure
747                         node->isleaf = false;
748                         node->children[0] = Mod_SVBSP_AllocLeaf(node, false);
749                         node->children[1] = Mod_SVBSP_AllocLeaf(node, false);
750                         VectorCopy(normal, node->normal);
751                         node->dist = dist;
752                 }
753                 else if (constructmode == 1)
754                 {
755                         // mark dark leafs
756                         node->dark = true;
757                 }
758                 else
759                 {
760                         // link polygons into lit leafs only (this is the optimization)
761                         if (!node->dark)
762                         {
763                                 poly = Mem_Alloc(loadmodel->mempool, sizeof(svbsppolygon_t) + numverts * sizeof(float[3]));
764                                 poly->numverts = numverts;
765                                 poly->verts = (float *)(poly + 1);
766                                 VectorCopy(normal, poly->normal);
767                                 poly->dist = dist;
768                                 memcpy(poly->verts, verts, numverts * sizeof(float[3]));
769                                 poly->next = node->polygons;
770                                 node->polygons = poly;
771                         }
772                 }
773         }
774         else
775         {
776                 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
777                 for (i = 0, v = verts;i < numverts;i++, v += 3)
778                 {
779                         dists[i] = DotProduct(v, node->normal) - node->dist;
780                         if (dists[i] >= 0.1)
781                                 sides[i] = SIDE_FRONT;
782                         else if (dists[i] <= -0.1)
783                                 sides[i] = SIDE_BACK;
784                         else
785                                 sides[i] = SIDE_ON;
786                         counts[sides[i]]++;
787                 }
788                 if (counts[SIDE_FRONT] && counts[SIDE_BACK])
789                 {
790                         // some front, some back...  sliced
791                         numvertsfront = 0;
792                         numvertsback = 0;
793                         // this is excessive, but nice for safety...
794                         maxverts = numverts + 4;
795                         vertsfront = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
796                         vertsback = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
797                         for (i = 0, j = numverts - 1;i < numverts;j = i, i++)
798                         {
799                                 if (sides[j] == SIDE_FRONT)
800                                 {
801                                         VectorCopy(&verts[j * 3], &vertsfront[numvertsfront * 3]);
802                                         numvertsfront++;
803                                         if (sides[i] == SIDE_BACK)
804                                         {
805                                                 d = dists[j] / (dists[j] - dists[i]);
806                                                 VectorSubtract(&verts[i * 3], &verts[j * 3], temp);
807                                                 VectorMA(&verts[j * 3], d, temp, temp);
808                                                 VectorCopy(temp, &vertsfront[numvertsfront * 3]);
809                                                 VectorCopy(temp, &vertsback[numvertsback * 3]);
810                                                 numvertsfront++;
811                                                 numvertsback++;
812                                         }
813                                 }
814                                 else if (sides[j] == SIDE_BACK)
815                                 {
816                                         VectorCopy(&verts[j * 3], &vertsback[numvertsback * 3]);
817                                         numvertsback++;
818                                         if (sides[i] == SIDE_FRONT)
819                                         {
820                                                 d = dists[j] / (dists[j] - dists[i]);
821                                                 VectorSubtract(&verts[i * 3], &verts[j * 3], temp);
822                                                 VectorMA(&verts[j * 3], d, temp, temp);
823                                                 VectorCopy(temp, &vertsfront[numvertsfront * 3]);
824                                                 VectorCopy(temp, &vertsback[numvertsback * 3]);
825                                                 numvertsfront++;
826                                                 numvertsback++;
827                                         }
828                                 }
829                                 else
830                                 {
831                                         VectorCopy(&verts[j * 3], &vertsfront[numvertsfront * 3]);
832                                         VectorCopy(&verts[j * 3], &vertsback[numvertsback * 3]);
833                                         numvertsfront++;
834                                         numvertsback++;
835                                 }
836                         }
837                         Mod_SVBSP_RecursiveAddPolygon(node->children[1], numvertsfront, vertsfront, normal, dist, constructmode);
838                         Mod_SVBSP_RecursiveAddPolygon(node->children[0], numvertsback, vertsback, normal, dist, constructmode);
839                         Mem_Free(vertsfront);
840                         Mem_Free(vertsback);
841                 }
842                 else if (counts[SIDE_BACK])
843                         Mod_SVBSP_RecursiveAddPolygon(node->children[0], numverts, verts, normal, dist, constructmode);
844                 else if (counts[SIDE_FRONT])
845                         Mod_SVBSP_RecursiveAddPolygon(node->children[1], numverts, verts, normal, dist, constructmode);
846                 else
847                 {
848                         // mode 0 is constructing tree, don't make unnecessary splits
849                         if (constructmode == 1)
850                         {
851                                 // marking dark leafs
852                                 // send it down the side it is not facing
853                                 Mod_SVBSP_RecursiveAddPolygon(node->children[DotProduct(node->normal, normal) < 0], numverts, verts, normal, dist, constructmode);
854                         }
855                         else if (constructmode == 2)
856                         {
857                                 // linking polygons into lit leafs only
858                                 // send it down the side it is facing
859                                 Mod_SVBSP_RecursiveAddPolygon(node->children[DotProduct(node->normal, normal) >= 0], numverts, verts, normal, dist, constructmode);
860                         }
861                 }
862         }
863 }
864
865 int svbsp_count_nodes;
866 int svbsp_count_leafs;
867 int svbsp_count_polygons;
868 int svbsp_count_darkleafs;
869 int svbsp_count_originalpolygons;
870 int svbsp_count_meshs;
871 int svbsp_count_triangles;
872 int svbsp_count_vertices;
873
874 void Mod_SVBSP_AddPolygon(svbspnode_t *root, int numverts, float *verts, int constructmode, float *test, int linenumber)
875 {
876         int i;
877         float normal[3], dist, dir0[3], dir1[3], *v0, *v1, *v2;
878         svbsp_count_originalpolygons++;
879         for (i = 0, v0 = verts + (numverts - 2) * 3, v1 = verts + (numverts - 1) * 3, v2 = verts;i < numverts;i++, v0 = v1, v1 = v2, v2 += 3)
880         {
881                 VectorSubtract(v0, v1, dir0);
882                 VectorSubtract(v2, v1, dir1);
883                 CrossProduct(dir0, dir1, normal);
884                 if (DotProduct(normal, normal) >= 0.1)
885                         break;
886         }
887         if (i == numverts)
888                 return;
889         VectorNormalize(normal);
890         dist = DotProduct(verts, normal);
891         if (test && DotProduct(test, normal) > dist + 0.1)
892                 Con_Printf("%i %f %f %f %f : %f %f %f %f\n", linenumber, normal[0], normal[1], normal[2], dist, test[0], test[1], test[2], DotProduct(test, normal));
893         Mod_SVBSP_RecursiveAddPolygon(root, numverts, verts, normal, dist, constructmode);
894 }
895
896 void Mod_SVBSP_RecursiveGatherStats(svbspnode_t *node)
897 {
898         svbsppolygon_t *poly;
899         for (poly = node->polygons;poly;poly = poly->next)
900                 svbsp_count_polygons++;
901         if (node->isleaf)
902         {
903                 svbsp_count_leafs++;
904                 if (node->dark)
905                         svbsp_count_darkleafs++;
906         }
907         else
908         {
909                 svbsp_count_nodes++;
910                 Mod_SVBSP_RecursiveGatherStats(node->children[0]);
911                 Mod_SVBSP_RecursiveGatherStats(node->children[1]);
912         }
913 }
914
915 svbspmesh_t *Mod_SVBSP_AllocMesh(int maxverts)
916 {
917         svbspmesh_t *mesh;
918         mesh = Mem_Alloc(loadmodel->mempool, sizeof(svbspmesh_t) + maxverts * sizeof(float[4]) + maxverts * sizeof(int[3]));
919         mesh->maxverts = maxverts;
920         mesh->maxtriangles = maxverts;
921         mesh->numverts = 0;
922         mesh->numtriangles = 0;
923         mesh->verts = (float *)(mesh + 1);
924         mesh->elements = (int *)(mesh->verts + mesh->maxverts * 4);
925         return mesh;
926 }
927
928 svbspmesh_t *Mod_SVBSP_ReAllocMesh(svbspmesh_t *oldmesh)
929 {
930         svbspmesh_t *newmesh;
931         newmesh = Mem_Alloc(loadmodel->mempool, sizeof(svbspmesh_t) + oldmesh->numverts * sizeof(float[4]) + oldmesh->numtriangles * sizeof(int[3]));
932         newmesh->maxverts = newmesh->numverts = oldmesh->numverts;
933         newmesh->maxtriangles = newmesh->numtriangles = oldmesh->numtriangles;
934         newmesh->verts = (float *)(newmesh + 1);
935         newmesh->elements = (int *)(newmesh->verts + newmesh->maxverts * 4);
936         memcpy(newmesh->verts, oldmesh->verts, newmesh->numverts * sizeof(float[4]));
937         memcpy(newmesh->elements, oldmesh->elements, newmesh->numtriangles * sizeof(int[3]));
938         return newmesh;
939 }
940
941 void Mod_SVBSP_RecursiveBuildTriangleMeshs(svbspmesh_t *firstmesh, svbspnode_t *node)
942 {
943         svbsppolygon_t *poly;
944         svbspmesh_t *mesh;
945         int i, j, k;
946         float *v, *m, temp[3];
947         if (node->isleaf)
948         {
949                 for (poly = node->polygons;poly;poly = poly->next)
950                 {
951                         mesh = firstmesh;
952                         while (poly->numverts + mesh->numverts > mesh->maxverts || (poly->numverts - 2) + mesh->numtriangles > mesh->maxtriangles)
953                         {
954                                 if (mesh->next == NULL)
955                                         mesh->next = Mod_SVBSP_AllocMesh(max(1000, poly->numverts));
956                                 mesh = mesh->next;
957                         }
958                         for (i = 0, v = poly->verts;i < poly->numverts - 2;i++, v += 3)
959                         {
960                                 for (k = 0;k < 3;k++)
961                                 {
962                                         if (k == 0)
963                                                 v = poly->verts;
964                                         else if (k == 1)
965                                                 v = poly->verts + (i + 1) * 3;
966                                         else if (k == 2)
967                                                 v = poly->verts + (i + 2) * 3;
968                                         for (j = 0, m = mesh->verts;j < mesh->numverts;j++, m += 4)
969                                         {
970                                                 VectorSubtract(v, m, temp);
971                                                 if (DotProduct(temp, temp) < 0.1)
972                                                         break;
973                                         }
974                                         if (j == mesh->numverts)
975                                         {
976                                                 mesh->numverts++;
977                                                 VectorCopy(v, m);
978                                         }
979                                         mesh->elements[mesh->numtriangles * 3 + k] = j;
980                                 }
981                                 mesh->numtriangles++;
982                         }
983                 }
984         }
985         else
986         {
987                 Mod_SVBSP_RecursiveBuildTriangleMeshs(firstmesh, node->children[0]);
988                 Mod_SVBSP_RecursiveBuildTriangleMeshs(firstmesh, node->children[1]);
989         }
990 }
991
992 svbspmesh_t *Mod_SVBSP_BuildTriangleMeshs(svbspnode_t *root, vec3_t mins, vec3_t maxs)
993 {
994         svbspmesh_t *firstmesh, *mesh, *newmesh, *nextmesh;
995         int i;
996         float *v;
997         firstmesh = Mod_SVBSP_AllocMesh(1000);
998         Mod_SVBSP_RecursiveBuildTriangleMeshs(firstmesh, root);
999         // reallocate meshs to conserve space
1000         for (mesh = firstmesh, firstmesh = NULL;mesh;mesh = nextmesh)
1001         {
1002                 svbsp_count_meshs++;
1003                 svbsp_count_triangles += mesh->numtriangles;
1004                 svbsp_count_vertices += mesh->numverts;
1005
1006                 // calculate bbox
1007                 if (firstmesh == NULL)
1008                 {
1009                         VectorCopy(mesh->verts, mins);
1010                         VectorCopy(mesh->verts, maxs);
1011                 }
1012                 for (i = 0, v = mesh->verts;i < mesh->numverts;i++, v += 4)
1013                 {
1014                         if (mins[0] > v[0]) mins[0] = v[0];if (maxs[0] < v[0]) maxs[0] = v[0];
1015                         if (mins[1] > v[1]) mins[1] = v[1];if (maxs[1] < v[1]) maxs[1] = v[1];
1016                         if (mins[2] > v[2]) mins[2] = v[2];if (maxs[2] < v[2]) maxs[2] = v[2];
1017                 }
1018
1019                 nextmesh = mesh->next;
1020                 newmesh = Mod_SVBSP_ReAllocMesh(mesh);
1021                 newmesh->next = firstmesh;
1022                 firstmesh = newmesh;
1023                 Mem_Free(mesh);
1024         }
1025         return firstmesh;
1026 }
1027
1028 void Mod_SVBSP_FreeTriangleMeshs(svbspmesh_t *mesh)
1029 {
1030         svbspmesh_t *nextmesh;
1031         for (;mesh;mesh = nextmesh)
1032         {
1033                 nextmesh = mesh->next;
1034                 Mem_Free(mesh);
1035         }
1036 }
1037 */
1038
1039 typedef struct svpolygon_s
1040 {
1041         struct svpolygon_s *next;
1042         int maxverts;
1043         int numverts;
1044         float *verts;
1045         float normal[3], dist;
1046 }
1047 svpolygon_t;
1048
1049 typedef struct svbrush_s
1050 {
1051         struct svbrush_s *next;
1052         svpolygon_t *polygons;
1053         vec3_t mins, maxs;
1054 }
1055 svbrush_t;
1056
1057 typedef struct svworld_s
1058 {
1059         svbrush_t *brushs;
1060 }
1061 svworld_t;
1062
1063 svworld_t *Mod_ShadowBrush_NewWorld(mempool_t *mempool)
1064 {
1065         return Mem_Alloc(mempool, sizeof(svworld_t));
1066 }
1067
1068 void Mod_ShadowBrush_FreeWorld(svworld_t *world)
1069 {
1070         svbrush_t *brush, *brushnext;
1071         svpolygon_t *poly, *polynext;
1072         for (brush = world->brushs;brush;brush = brushnext)
1073         {
1074                 brushnext = brush->next;
1075                 for (poly = brush->polygons;poly;poly = polynext)
1076                 {
1077                         polynext = poly->next;
1078                         Mem_Free(poly);
1079                 }
1080                 Mem_Free(brush);
1081         }
1082         Mem_Free(world);
1083 }
1084
1085 svbrush_t *Mod_ShadowBrush_BeginBrush(mempool_t *mempool)
1086 {
1087         return Mem_Alloc(mempool, sizeof(svbrush_t));
1088 }
1089
1090 void Mod_ShadowBrush_AddPolygon(mempool_t *mempool, svbrush_t *brush, int numverts, float *verts)
1091 {
1092         int i;
1093         float normal[3], dist, dir0[3], dir1[3], *v0, *v1, *v2;
1094         svpolygon_t *poly;
1095         for (i = 0, v0 = verts + (numverts - 2) * 3, v1 = verts + (numverts - 1) * 3, v2 = verts;i < numverts;i++, v0 = v1, v1 = v2, v2 += 3)
1096         {
1097                 VectorSubtract(v0, v1, dir0);
1098                 VectorSubtract(v2, v1, dir1);
1099                 CrossProduct(dir0, dir1, normal);
1100                 if (DotProduct(normal, normal) >= 0.1)
1101                         break;
1102         }
1103         if (i == numverts)
1104                 return;
1105         VectorNormalize(normal);
1106         dist = DotProduct(verts, normal);
1107
1108         poly = Mem_Alloc(mempool, sizeof(svpolygon_t) + numverts * sizeof(float[3]));
1109         poly->numverts = numverts;
1110         poly->verts = (float *)(poly + 1);
1111         VectorCopy(normal, poly->normal);
1112         poly->dist = dist;
1113         poly->next = brush->polygons;
1114         brush->polygons = poly;
1115         memcpy(poly->verts, verts, numverts * sizeof(float[3]));
1116 }
1117
1118 void Mod_ShadowBrush_EndBrush(svworld_t *world, svbrush_t *brush)
1119 {
1120         int i;
1121         float *v;
1122         svpolygon_t *poly;
1123         if (!brush->polygons)
1124         {
1125                 Mem_Free(brush);
1126                 return;
1127         }
1128         brush->next = world->brushs;
1129         world->brushs = brush;
1130         VectorCopy(brush->polygons->verts, brush->mins);
1131         VectorCopy(brush->polygons->verts, brush->maxs);
1132         for (poly = brush->polygons;poly;poly = poly->next)
1133         {
1134                 for (i = 0, v = poly->verts;i < poly->numverts;i++, v += 3)
1135                 {
1136                         if (brush->mins[0] > v[0]) brush->mins[0] = v[0];if (brush->maxs[0] < v[0]) brush->maxs[0] = v[0];
1137                         if (brush->mins[1] > v[1]) brush->mins[1] = v[1];if (brush->maxs[1] < v[1]) brush->maxs[1] = v[1];
1138                         if (brush->mins[2] > v[2]) brush->mins[2] = v[2];if (brush->maxs[2] < v[2]) brush->maxs[2] = v[2];
1139                 }
1140         }
1141 }
1142
1143 void Mod_ShadowBrush_ProcessWorld(mempool_t *mempool, svworld_t *world)
1144 {
1145         /*
1146         for (clipbrush = world->brushs;clipbrush;clipbrush = clipbrush->next)
1147         {
1148                 for (brush = world->brushs;brush;brush = brush->next)
1149                 {
1150                         if (brush != clipbrush
1151                          && brush->mins[0] <= clipbrush->maxs[0]
1152                          && brush->maxs[0] >= clipbrush->mins[0]
1153                          && brush->mins[1] <= clipbrush->maxs[1]
1154                          && brush->maxs[1] >= clipbrush->mins[1]
1155                          && brush->mins[2] <= clipbrush->maxs[2]
1156                          && brush->maxs[2] >= clipbrush->mins[2])
1157                                 continue;
1158                         for (poly = brush->polygons;poly;poly = poly->next)
1159                         {
1160
1161                         }
1162                 }
1163         }
1164         */
1165 }
1166
1167 shadowmesh_t *Mod_ShadowBrush_BuildMeshs(mempool_t *mempool, svworld_t *world)
1168 {
1169         shadowmesh_t *mesh;
1170         svbrush_t *brush;
1171         svpolygon_t *poly;
1172         mesh = Mod_ShadowMesh_Begin(mempool);
1173         for (brush = world->brushs;brush;brush = brush->next)
1174                 for (poly = brush->polygons;poly;poly = poly->next)
1175                         Mod_ShadowMesh_AddPolygon(mempool, mesh, poly->numverts, poly->verts);
1176         mesh = Mod_ShadowMesh_Finish(mempool, mesh);
1177         return mesh;
1178 }
1179
1180 void Mod_ProcessLightList(void)
1181 {
1182         int j, k, *mark, lnum;
1183         mlight_t *e;
1184         msurface_t *surf;
1185         float dist;
1186         mleaf_t *leaf;
1187         qbyte *pvs;
1188         for (lnum = 0, e = loadmodel->lights;lnum < loadmodel->numlights;lnum++, e++)
1189         {
1190                 e->cullradius2 = DotProduct(e->light, e->light) * (1.0f / (8192.0f * 8192.0f)) / (e->falloff * e->falloff) + 4096.0f;
1191                 if (e->cullradius2 > 4096.0f * 4096.0f)
1192                         e->cullradius2 = 4096.0f * 4096.0f;
1193                 e->cullradius = sqrt(e->cullradius2);
1194                 leaf = Mod_PointInLeaf(e->origin, loadmodel);
1195                 if (leaf->compressed_vis)
1196                         pvs = Mod_DecompressVis (leaf->compressed_vis, loadmodel);
1197                 else
1198                         pvs = mod_novis;
1199                 for (j = 0;j < loadmodel->numsurfaces;j++)
1200                         loadmodel->surfacevisframes[j] = -1;
1201                 for (j = 0, leaf = loadmodel->leafs + 1;j < loadmodel->numleafs - 1;j++, leaf++)
1202                 {
1203                         if (pvs[j >> 3] & (1 << (j & 7)))
1204                         {
1205                                 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
1206                                 {
1207                                         surf = loadmodel->surfaces + *mark;
1208                                         if (surf->number != *mark)
1209                                                 Con_Printf("%d != %d\n", surf->number, *mark);
1210                                         dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1211                                         if (surf->flags & SURF_PLANEBACK)
1212                                                 dist = -dist;
1213                                         if (dist > 0 && dist < e->cullradius)
1214                                                 loadmodel->surfacevisframes[*mark] = -2;
1215                                 }
1216                         }
1217                 }
1218                 // build list of light receiving surfaces
1219                 e->numsurfaces = 0;
1220                 for (j = 0;j < loadmodel->numsurfaces;j++)
1221                         if (loadmodel->surfacevisframes[j] == -2)
1222                                 e->numsurfaces++;
1223                 e->surfaces = NULL;
1224                 if (e->numsurfaces > 0)
1225                 {
1226                         e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
1227                         e->numsurfaces = 0;
1228                         for (j = 0;j < loadmodel->numsurfaces;j++)
1229                                 if (loadmodel->surfacevisframes[j] == -2)
1230                                         e->surfaces[e->numsurfaces++] = loadmodel->surfaces + j;
1231                 }
1232                 {
1233                 // find bounding box and sphere of lit surfaces
1234                 // (these will be used for creating a shape to clip the light)
1235                 float *v, temp[3], radius2;
1236                 radius2 = 0;
1237                 for (j = 0;j < e->numsurfaces;j++)
1238                 {
1239                         surf = e->surfaces[j];
1240                         if (j == 0)
1241                         {
1242                                 VectorCopy(surf->poly_verts, e->mins);
1243                                 VectorCopy(surf->poly_verts, e->maxs);
1244                         }
1245                         for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
1246                         {
1247                                 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
1248                                 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
1249                                 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1250                                 VectorSubtract(v, e->origin, temp);
1251                                 dist = DotProduct(temp, temp);
1252                                 if (radius2 < dist)
1253                                         radius2 = dist;
1254                         }
1255                 }
1256                 /*
1257                 if (e->cullradius2 > radius2)
1258                 {
1259                         e->cullradius2 = radius2;
1260                         e->cullradius = sqrt(e->cullradius2);
1261                 }
1262                 */
1263                 }
1264 #if 1
1265                 // clip shadow volumes against eachother to remove unnecessary
1266                 // polygons (and sections of polygons)
1267                 {
1268                         vec3_t temp, outermins, outermaxs, innermins, innermaxs;
1269                         int maxverts = 4;
1270                         float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1271                         float f, *v0, *v1, projectdistance;
1272                         svworld_t *svworld;
1273                         svbrush_t *svbrush;
1274
1275                         innermins[0] = e->mins[0] - 1;
1276                         innermins[1] = e->mins[1] - 1;
1277                         innermins[2] = e->mins[2] - 1;
1278                         innermaxs[0] = e->maxs[0] + 1;
1279                         innermaxs[1] = e->maxs[1] + 1;
1280                         innermaxs[2] = e->maxs[2] + 1;
1281                         outermins[0] = loadmodel->normalmins[0] - 1;
1282                         outermins[1] = loadmodel->normalmins[1] - 1;
1283                         outermins[2] = loadmodel->normalmins[2] - 1;
1284                         outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1285                         outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1286                         outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1287                         svworld = Mod_ShadowBrush_NewWorld(loadmodel->mempool);
1288                         for (j = 0, surf = loadmodel->surfaces + loadmodel->firstmodelsurface;j < loadmodel->nummodelsurfaces;j++, surf++)
1289                         {
1290                                 if (!(surf->flags & SURF_CLIPSOLID))
1291                                         continue;
1292                                 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1293                                 if (surf->flags & SURF_PLANEBACK)
1294                                         f = -f;
1295                                 projectdistance = e->cullradius + f;
1296                                 if (projectdistance < 0.1 || projectdistance > e->cullradius)
1297                                         continue;
1298                                 VectorSubtract(e->origin, surf->poly_center, temp);
1299                                 if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2))
1300                                         continue;
1301                                 if (maxverts < surf->poly_numverts)
1302                                 {
1303                                         maxverts = surf->poly_numverts;
1304                                         if (verts)
1305                                                 Mem_Free(verts);
1306                                         verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1307                                 }
1308                                 svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1309                                 // copy the original polygon, reversed, for the front cap of the volume
1310                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1311                                         VectorCopy(v0, v1);
1312                                 Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, surf->poly_numverts, verts);
1313                                 // project the original polygon, for the back cap of the volume
1314                                 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1315                                 {
1316                                         VectorSubtract(v0, e->origin, temp);
1317                                         VectorNormalize(temp);
1318                                         VectorMA(v0, projectdistance, temp, v1);
1319                                 }
1320                                 Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, surf->poly_numverts, verts);
1321                                 // project the shadow volume sides
1322                                 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3)
1323                                 {
1324                                         VectorCopy(v0, &verts[0]);
1325                                         VectorCopy(v1, &verts[3]);
1326                                         VectorCopy(v1, &verts[6]);
1327                                         VectorCopy(v0, &verts[9]);
1328                                         VectorSubtract(&verts[6], e->origin, temp);
1329                                         VectorNormalize(temp);
1330                                         VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1331                                         VectorSubtract(&verts[9], e->origin, temp);
1332                                         VectorNormalize(temp);
1333                                         VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1334                                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1335                                 }
1336                                 Mod_ShadowBrush_EndBrush(svworld, svbrush);
1337                         }
1338                         // add bounding box around the whole shadow volume set,
1339                         // facing inward to limit light area, with an outer bounding box
1340                         // facing outward (this is needed by the shadow rendering method)
1341                         // X major
1342                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1343                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1344                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1345                         verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1346                         verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1347                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1348                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1349                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1350                         verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1351                         verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1352                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1353                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1354                         // X minor
1355                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1356                         verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1357                         verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1358                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1359                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1360                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1361                         verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1362                         verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1363                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1364                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1365                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1366                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1367                         // Y major
1368                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1369                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1370                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1371                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1372                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1373                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1374                         verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1375                         verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1376                         verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1377                         verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1378                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1379                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1380                         // Y minor
1381                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1382                         verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1383                         verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1384                         verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1385                         verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1386                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1387                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1388                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1389                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1390                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1391                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1392                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1393                         // Z major
1394                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1395                         verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1396                         verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1397                         verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1398                         verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1399                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1400                         verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1401                         verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1402                         verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1403                         verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1404                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1405                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1406                         // Z minor
1407                         svbrush = Mod_ShadowBrush_BeginBrush(loadmodel->mempool);
1408                         verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1409                         verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1410                         verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1411                         verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1412                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1413                         verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1414                         verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1415                         verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1416                         verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1417                         Mod_ShadowBrush_AddPolygon(loadmodel->mempool, svbrush, 4, verts);
1418                         Mod_ShadowBrush_EndBrush(svworld, svbrush);
1419                         // clip away hidden polygons
1420                         Mod_ShadowBrush_ProcessWorld(loadmodel->mempool, svworld);
1421                         // build the triangle mesh
1422                         e->shadowvolume = Mod_ShadowBrush_BuildMeshs(loadmodel->mempool, svworld);
1423                         Mod_ShadowBrush_FreeWorld(svworld);
1424                 }
1425 #elif 0
1426                 // build svbsp (shadow volume bsp)
1427                 {
1428                         int maxverts = 0, constructmode;
1429                         float *verts = NULL, projectdistance, *v0, *v1, f, temp[3];
1430                         svbspnode_t *svbsproot;
1431                         svbsproot = Mod_SVBSP_NewTree();
1432                         // we do this in three stages:
1433                         // 1. construct the svbsp structure
1434                         // 2. mark which leafs are dark (shadow)
1435                         // 3. link polygons into only leafs that are not dark
1436                         // this results in polygons that are only on the outside of the
1437                         // shadow volume, removing polygons that are inside the shadow
1438                         // volume (which waste time)
1439                         for (constructmode = 0;constructmode < 3;constructmode++)
1440                         {
1441                                 svbsp_count_originalpolygons = 0;
1442 #if 1
1443                                 for (j = 0, surf = loadmodel->surfaces + loadmodel->firstmodelsurface;j < loadmodel->nummodelsurfaces;j++, surf++)
1444                                 {
1445                                         if (!(surf->flags & SURF_CLIPSOLID))
1446                                                 continue;
1447                                         /*
1448                                         if (surf->poly_maxs[0] < e->mins[0]
1449                                          || surf->poly_mins[0] > e->maxs[0]
1450                                          || surf->poly_maxs[1] < e->mins[1]
1451                                          || surf->poly_mins[1] > e->maxs[1]
1452                                          || surf->poly_maxs[2] < e->mins[2]
1453                                          || surf->poly_mins[2] > e->maxs[2])
1454                                                 continue;
1455                                         */
1456                                         f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1457                                         if (surf->flags & SURF_PLANEBACK)
1458                                                 f = -f;
1459                                         projectdistance = e->cullradius + f;
1460                                         if (projectdistance < 0.1 || projectdistance > e->cullradius)
1461                                                 continue;
1462                                         /*
1463                                         // find the nearest vertex of the projected volume
1464                                         for (k = 0, v0 = surf->poly_verts;k < surf->poly_numverts;k++, v0 += 3)
1465                                         {
1466                                                 VectorSubtract(v0, e->origin, temp);
1467                                                 VectorNormalize(temp);
1468                                                 if (maxdist00 > v0[0] - e->origin[0]) maxdist00 = v0[0] - e->origin[0];
1469                                                 if (maxdist01 < e->origin[0] - v0[0]) maxdist01 = e->origin[0] - v0[0];
1470                                                 if (maxdist10 > v0[1] - e->origin[1]) maxdist10 = v0[1] - e->origin[1];
1471                                                 if (maxdist11 < e->origin[1] - v0[1]) maxdist11 = e->origin[1] - v0[1];
1472                                                 if (maxdist20 > v0[2] - e->origin[2]) maxdist20 = v0[2] - e->origin[2];
1473                                                 if (maxdist21 < e->origin[2] - v0[2]) maxdist21 = e->origin[2] - v0[2];
1474                                                 dist =
1475
1476                                                 dist = DotProduct(temp, temp);
1477                                                 if (bestdist > dist)
1478                                                 {
1479                                                         bestdist = dist;
1480                                                         VectorCopy(temp, bestvec);
1481                                                 }
1482                                         }
1483                                         projectdistance = e->cullradius - sqrt(bestdist);
1484                                         if (projectdistance < 0.1)
1485                                                 continue;
1486                                         for (k = 0, v0 = surf->poly_verts;k < surf->poly_numverts;k++, v0 += 3)
1487                                         {
1488                                                 VectorNormalize(temp);
1489                                                 if (temp[0] > 0)
1490                                                 {
1491                                                         dist = (e->maxs[0] - e->origin[0]) / temp[0];
1492                                                         if (maxdist >
1493                                                 }
1494                                                 else if (temp[0] < 0)
1495                                                         dist = (e->mins[0] - e->origin[0]) / temp[0];
1496                                                 dist =
1497                                                 VectorMA(v0, projectdistance, temp, temp);
1498                                                 dist = (temp[0]
1499                                                 VectorSubtract(temp, e->origin,
1500                                         }
1501                                         */
1502                                         VectorSubtract(e->origin, surf->poly_center, temp);
1503                                         if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2))
1504                                                 continue;
1505                                         if (maxverts < surf->poly_numverts)
1506                                         {
1507                                                 maxverts = surf->poly_numverts;
1508                                                 if (verts)
1509                                                         Mem_Free(verts);
1510                                                 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1511                                         }
1512                                         // copy the original polygon, reversed, for the front cap of the volume
1513                                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1514                                                 VectorCopy(v0, v1);
1515                                         Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__);
1516                                         // project the original polygon, for the back cap of the volume
1517                                         for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1518                                         {
1519                                                 VectorSubtract(v0, e->origin, temp);
1520                                                 VectorNormalize(temp);
1521                                                 VectorMA(v0, projectdistance, temp, v1);
1522                                         }
1523                                         Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__);
1524                                         // project the shadow volume sides
1525                                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3)
1526                                         {
1527                                                 VectorCopy(v0, &verts[0]);
1528                                                 VectorCopy(v1, &verts[3]);
1529                                                 VectorCopy(v1, &verts[6]);
1530                                                 VectorCopy(v0, &verts[9]);
1531                                                 VectorSubtract(&verts[6], e->origin, temp);
1532                                                 VectorNormalize(temp);
1533                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1534                                                 VectorSubtract(&verts[9], e->origin, temp);
1535                                                 VectorNormalize(temp);
1536                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1537                                                 Mod_SVBSP_AddPolygon(svbsproot, 4, verts, constructmode, surf->poly_center, __LINE__);
1538                                         }
1539                                 }
1540 #else
1541                                 for (j = 0;j < e->numsurfaces;j++)
1542                                 {
1543                                         surf = e->surfaces[j];
1544                                         if (!(surf->flags & SURF_CLIPSOLID))
1545                                                 continue;
1546                                         f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1547                                         if (surf->flags & SURF_PLANEBACK)
1548                                                 f = -f;
1549                                         projectdistance = e->cullradius - f;
1550                                         if (projectdistance < 0.1 || projectdistance > e->cullradius)
1551                                                 continue;
1552                                         VectorSubtract(e->origin, surf->poly_center, temp);
1553                                         if (DotProduct(temp, temp) > (surf->poly_radius2 + e->cullradius2))
1554                                                 continue;
1555                                         if (maxverts < surf->poly_numverts)
1556                                         {
1557                                                 maxverts = surf->poly_numverts;
1558                                                 if (verts)
1559                                                         Mem_Free(verts);
1560                                                 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1561                                         }
1562                                         // copy the original polygon, for the front cap of the volume
1563                                         for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1564                                                 VectorCopy(v0, v1);
1565                                         Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__);
1566                                         // project the original polygon, reversed, for the back cap of the volume
1567                                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1568                                         {
1569                                                 VectorSubtract(v0, e->origin, temp);
1570                                                 VectorNormalize(temp);
1571                                                 VectorMA(v0, projectdistance, temp, v1);
1572                                         }
1573                                         Mod_SVBSP_AddPolygon(svbsproot, surf->poly_numverts, verts, constructmode, surf->poly_center, __LINE__);
1574                                         // project the shadow volume sides
1575                                         for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;k++, v0 = v1, v1 += 3)
1576                                         {
1577                                                 VectorCopy(v1, &verts[0]);
1578                                                 VectorCopy(v0, &verts[3]);
1579                                                 VectorCopy(v0, &verts[6]);
1580                                                 VectorCopy(v1, &verts[9]);
1581                                                 VectorSubtract(&verts[6], e->origin, temp);
1582                                                 VectorNormalize(temp);
1583                                                 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1584                                                 VectorSubtract(&verts[9], e->origin, temp);
1585                                                 VectorNormalize(temp);
1586                                                 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1587                                                 Mod_SVBSP_AddPolygon(svbsproot, 4, verts, constructmode, surf->poly_center, __LINE__);
1588                                         }
1589                                 }
1590 #endif
1591                         }
1592                         if (verts)
1593                                 Mem_Free(verts);
1594
1595                         svbsp_count_nodes = 0;
1596                         svbsp_count_leafs = 0;
1597                         svbsp_count_polygons = 0;
1598                         svbsp_count_darkleafs = 0;
1599                         svbsp_count_meshs = 0;
1600                         svbsp_count_triangles = 0;
1601                         svbsp_count_vertices = 0;
1602                         e->shadowvolume = Mod_SVBSP_BuildTriangleMeshs(svbsproot, e->shadowvolumemins, e->shadowvolumemaxs);
1603                         Mod_SVBSP_RecursiveGatherStats(svbsproot);
1604                         Mod_SVBSP_FreeTree(svbsproot);
1605                         Con_Printf("light %d (radius %d) has %d surfaces, svbsp contains %d nodes, %d leafs, %d are dark (%d%%), %d original polygons, %d polygons stored (%d%%), %d meshs %d vertices %d triangles\n", lnum, (int)e->cullradius, e->numsurfaces, svbsp_count_nodes, svbsp_count_leafs, svbsp_count_darkleafs, svbsp_count_leafs ? (100 * svbsp_count_darkleafs / svbsp_count_leafs) : 0, svbsp_count_originalpolygons, svbsp_count_polygons, svbsp_count_originalpolygons ? (100 * svbsp_count_polygons / svbsp_count_originalpolygons) : 0, svbsp_count_meshs, svbsp_count_triangles, svbsp_count_vertices);
1606                 }
1607 #endif
1608         }
1609 }
1610
1611
1612 /*
1613 =================
1614 Mod_LoadVisibility
1615 =================
1616 */
1617 static void Mod_LoadVisibility (lump_t *l)
1618 {
1619         loadmodel->visdata = NULL;
1620         if (!l->filelen)
1621                 return;
1622         loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1623         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
1624 }
1625
1626 // used only for HalfLife maps
1627 void Mod_ParseWadsFromEntityLump(const char *data)
1628 {
1629         char key[128], value[4096];
1630         char wadname[128];
1631         int i, j, k;
1632         if (!data)
1633                 return;
1634         if (!COM_ParseToken(&data))
1635                 return; // error
1636         if (com_token[0] != '{')
1637                 return; // error
1638         while (1)
1639         {
1640                 if (!COM_ParseToken(&data))
1641                         return; // error
1642                 if (com_token[0] == '}')
1643                         break; // end of worldspawn
1644                 if (com_token[0] == '_')
1645                         strcpy(key, com_token + 1);
1646                 else
1647                         strcpy(key, com_token);
1648                 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1649                         key[strlen(key)-1] = 0;
1650                 if (!COM_ParseToken(&data))
1651                         return; // error
1652                 strcpy(value, com_token);
1653                 if (!strcmp("wad", key)) // for HalfLife maps
1654                 {
1655                         if (loadmodel->ishlbsp)
1656                         {
1657                                 j = 0;
1658                                 for (i = 0;i < 4096;i++)
1659                                         if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1660                                                 break;
1661                                 if (value[i])
1662                                 {
1663                                         for (;i < 4096;i++)
1664                                         {
1665                                                 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1666                                                 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1667                                                         j = i+1;
1668                                                 else if (value[i] == ';' || value[i] == 0)
1669                                                 {
1670                                                         k = value[i];
1671                                                         value[i] = 0;
1672                                                         strcpy(wadname, "textures/");
1673                                                         strcat(wadname, &value[j]);
1674                                                         W_LoadTextureWadFile (wadname, false);
1675                                                         j = i+1;
1676                                                         if (!k)
1677                                                                 break;
1678                                                 }
1679                                         }
1680                                 }
1681                         }
1682                 }
1683         }
1684 }
1685
1686 /*
1687 =================
1688 Mod_LoadEntities
1689 =================
1690 */
1691 static void Mod_LoadEntities (lump_t *l)
1692 {
1693         loadmodel->entities = NULL;
1694         if (!l->filelen)
1695                 return;
1696         loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1697         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
1698         if (loadmodel->ishlbsp)
1699                 Mod_ParseWadsFromEntityLump(loadmodel->entities);
1700 }
1701
1702
1703 /*
1704 =================
1705 Mod_LoadVertexes
1706 =================
1707 */
1708 static void Mod_LoadVertexes (lump_t *l)
1709 {
1710         dvertex_t       *in;
1711         mvertex_t       *out;
1712         int                     i, count;
1713
1714         in = (void *)(mod_base + l->fileofs);
1715         if (l->filelen % sizeof(*in))
1716                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1717         count = l->filelen / sizeof(*in);
1718         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1719
1720         loadmodel->vertexes = out;
1721         loadmodel->numvertexes = count;
1722
1723         for ( i=0 ; i<count ; i++, in++, out++)
1724         {
1725                 out->position[0] = LittleFloat (in->point[0]);
1726                 out->position[1] = LittleFloat (in->point[1]);
1727                 out->position[2] = LittleFloat (in->point[2]);
1728         }
1729 }
1730
1731 /*
1732 =================
1733 Mod_LoadSubmodels
1734 =================
1735 */
1736 static void Mod_LoadSubmodels (lump_t *l)
1737 {
1738         dmodel_t        *in;
1739         dmodel_t        *out;
1740         int                     i, j, count;
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         count = l->filelen / sizeof(*in);
1746         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1747
1748         loadmodel->submodels = out;
1749         loadmodel->numsubmodels = count;
1750
1751         for ( i=0 ; i<count ; i++, in++, out++)
1752         {
1753                 for (j=0 ; j<3 ; j++)
1754                 {
1755                         // spread the mins / maxs by a pixel
1756                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
1757                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
1758                         out->origin[j] = LittleFloat (in->origin[j]);
1759                 }
1760                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1761                         out->headnode[j] = LittleLong (in->headnode[j]);
1762                 out->visleafs = LittleLong (in->visleafs);
1763                 out->firstface = LittleLong (in->firstface);
1764                 out->numfaces = LittleLong (in->numfaces);
1765         }
1766 }
1767
1768 /*
1769 =================
1770 Mod_LoadEdges
1771 =================
1772 */
1773 static void Mod_LoadEdges (lump_t *l)
1774 {
1775         dedge_t *in;
1776         medge_t *out;
1777         int     i, count;
1778
1779         in = (void *)(mod_base + l->fileofs);
1780         if (l->filelen % sizeof(*in))
1781                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1782         count = l->filelen / sizeof(*in);
1783         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1784
1785         loadmodel->edges = out;
1786         loadmodel->numedges = count;
1787
1788         for ( i=0 ; i<count ; i++, in++, out++)
1789         {
1790                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1791                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1792         }
1793 }
1794
1795 /*
1796 =================
1797 Mod_LoadTexinfo
1798 =================
1799 */
1800 static void Mod_LoadTexinfo (lump_t *l)
1801 {
1802         texinfo_t *in;
1803         mtexinfo_t *out;
1804         int i, j, k, count, miptex;
1805
1806         in = (void *)(mod_base + l->fileofs);
1807         if (l->filelen % sizeof(*in))
1808                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1809         count = l->filelen / sizeof(*in);
1810         out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1811
1812         loadmodel->texinfo = out;
1813         loadmodel->numtexinfo = count;
1814
1815         for (i = 0;i < count;i++, in++, out++)
1816         {
1817                 for (k = 0;k < 2;k++)
1818                         for (j = 0;j < 4;j++)
1819                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
1820
1821                 miptex = LittleLong (in->miptex);
1822                 out->flags = LittleLong (in->flags);
1823
1824                 out->texture = NULL;
1825                 if (loadmodel->textures)
1826                 {
1827                         if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
1828                                 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
1829                         else
1830                                 out->texture = loadmodel->textures + miptex;
1831                 }
1832                 if (out->flags & TEX_SPECIAL)
1833                 {
1834                         // if texture chosen is NULL or the shader needs a lightmap,
1835                         // force to notexture water shader
1836                         if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1837                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
1838                 }
1839                 else
1840                 {
1841                         // if texture chosen is NULL, force to notexture
1842                         if (out->texture == NULL)
1843                                 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
1844                 }
1845         }
1846 }
1847
1848 /*
1849 ================
1850 CalcSurfaceExtents
1851
1852 Fills in s->texturemins[] and s->extents[]
1853 ================
1854 */
1855 static void CalcSurfaceExtents (msurface_t *s)
1856 {
1857         float   mins[2], maxs[2], val;
1858         int             i,j, e;
1859         mvertex_t       *v;
1860         mtexinfo_t      *tex;
1861         int             bmins[2], bmaxs[2];
1862
1863         mins[0] = mins[1] = 999999999;
1864         maxs[0] = maxs[1] = -999999999;
1865
1866         tex = s->texinfo;
1867
1868         for (i=0 ; i<s->numedges ; i++)
1869         {
1870                 e = loadmodel->surfedges[s->firstedge+i];
1871                 if (e >= 0)
1872                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
1873                 else
1874                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
1875
1876                 for (j=0 ; j<2 ; j++)
1877                 {
1878                         val = v->position[0] * tex->vecs[j][0] +
1879                                 v->position[1] * tex->vecs[j][1] +
1880                                 v->position[2] * tex->vecs[j][2] +
1881                                 tex->vecs[j][3];
1882                         if (val < mins[j])
1883                                 mins[j] = val;
1884                         if (val > maxs[j])
1885                                 maxs[j] = val;
1886                 }
1887         }
1888
1889         for (i=0 ; i<2 ; i++)
1890         {
1891                 bmins[i] = floor(mins[i]/16);
1892                 bmaxs[i] = ceil(maxs[i]/16);
1893
1894                 s->texturemins[i] = bmins[i] * 16;
1895                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
1896         }
1897 }
1898
1899
1900 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
1901 {
1902         int             i, j;
1903         float   *v;
1904
1905         mins[0] = mins[1] = mins[2] = 9999;
1906         maxs[0] = maxs[1] = maxs[2] = -9999;
1907         v = verts;
1908         for (i = 0;i < numverts;i++)
1909         {
1910                 for (j = 0;j < 3;j++, v++)
1911                 {
1912                         if (*v < mins[j])
1913                                 mins[j] = *v;
1914                         if (*v > maxs[j])
1915                                 maxs[j] = *v;
1916                 }
1917         }
1918 }
1919
1920 #if 0
1921 #define MAX_SUBDIVPOLYTRIANGLES 4096
1922 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
1923
1924 static int subdivpolyverts, subdivpolytriangles;
1925 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1926 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1927
1928 static int subdivpolylookupvert(vec3_t v)
1929 {
1930         int i;
1931         for (i = 0;i < subdivpolyverts;i++)
1932                 if (subdivpolyvert[i][0] == v[0]
1933                  && subdivpolyvert[i][1] == v[1]
1934                  && subdivpolyvert[i][2] == v[2])
1935                         return i;
1936         if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1937                 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1938         VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1939         return subdivpolyverts++;
1940 }
1941
1942 static void SubdividePolygon (int numverts, float *verts)
1943 {
1944         int             i, i1, i2, i3, f, b, c, p;
1945         vec3_t  mins, maxs, front[256], back[256];
1946         float   m, *pv, *cv, dist[256], frac;
1947
1948         if (numverts > 250)
1949                 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1950
1951         BoundPoly (numverts, verts, mins, maxs);
1952
1953         for (i = 0;i < 3;i++)
1954         {
1955                 m = (mins[i] + maxs[i]) * 0.5;
1956                 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1957                 if (maxs[i] - m < 8)
1958                         continue;
1959                 if (m - mins[i] < 8)
1960                         continue;
1961
1962                 // cut it
1963                 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1964                         dist[c] = cv[i] - m;
1965
1966                 f = b = 0;
1967                 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1968                 {
1969                         if (dist[p] >= 0)
1970                         {
1971                                 VectorCopy (pv, front[f]);
1972                                 f++;
1973                         }
1974                         if (dist[p] <= 0)
1975                         {
1976                                 VectorCopy (pv, back[b]);
1977                                 b++;
1978                         }
1979                         if (dist[p] == 0 || dist[c] == 0)
1980                                 continue;
1981                         if ( (dist[p] > 0) != (dist[c] > 0) )
1982                         {
1983                                 // clip point
1984                                 frac = dist[p] / (dist[p] - dist[c]);
1985                                 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1986                                 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1987                                 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1988                                 f++;
1989                                 b++;
1990                         }
1991                 }
1992
1993                 SubdividePolygon (f, front[0]);
1994                 SubdividePolygon (b, back[0]);
1995                 return;
1996         }
1997
1998         i1 = subdivpolylookupvert(verts);
1999         i2 = subdivpolylookupvert(verts + 3);
2000         for (i = 2;i < numverts;i++)
2001         {
2002                 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
2003                 {
2004                         Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
2005                         return;
2006                 }
2007
2008                 i3 = subdivpolylookupvert(verts + i * 3);
2009                 subdivpolyindex[subdivpolytriangles][0] = i1;
2010                 subdivpolyindex[subdivpolytriangles][1] = i2;
2011                 subdivpolyindex[subdivpolytriangles][2] = i3;
2012                 i2 = i3;
2013                 subdivpolytriangles++;
2014         }
2015 }
2016
2017 /*
2018 ================
2019 Mod_GenerateWarpMesh
2020
2021 Breaks a polygon up along axial 64 unit
2022 boundaries so that turbulent and sky warps
2023 can be done reasonably.
2024 ================
2025 */
2026 void Mod_GenerateWarpMesh (msurface_t *surf)
2027 {
2028         int i, j;
2029         surfvertex_t *v;
2030         surfmesh_t *mesh;
2031
2032         subdivpolytriangles = 0;
2033         subdivpolyverts = 0;
2034         SubdividePolygon (surf->poly_numverts, surf->poly_verts);
2035         if (subdivpolytriangles < 1)
2036                 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
2037
2038         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
2039         mesh->numverts = subdivpolyverts;
2040         mesh->numtriangles = subdivpolytriangles;
2041         mesh->vertex = (surfvertex_t *)(mesh + 1);
2042         mesh->index = (int *)(mesh->vertex + mesh->numverts);
2043         memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
2044
2045         for (i = 0;i < mesh->numtriangles;i++)
2046                 for (j = 0;j < 3;j++)
2047                         mesh->index[i*3+j] = subdivpolyindex[i][j];
2048
2049         for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
2050         {
2051                 VectorCopy(subdivpolyvert[i], v->v);
2052                 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
2053                 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
2054         }
2055 }
2056 #endif
2057
2058 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
2059 {
2060         int i, iu, iv, *index, smax, tmax;
2061         float *in, s, t, u, v, ubase, vbase, uscale, vscale, normal[3];
2062         surfmesh_t *mesh;
2063
2064         smax = surf->extents[0] >> 4;
2065         tmax = surf->extents[1] >> 4;
2066
2067         if (vertexonly)
2068         {
2069                 surf->lightmaptexturestride = 0;
2070                 surf->lightmaptexture = NULL;
2071                 uscale = 0;
2072                 vscale = 0;
2073                 ubase = 0;
2074                 vbase = 0;
2075         }
2076         else
2077         {
2078                 surf->flags |= SURF_LIGHTMAP;
2079                 if (r_miplightmaps.integer)
2080                 {
2081                         surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
2082                         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);
2083                 }
2084                 else
2085                 {
2086                         surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
2087                         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);
2088                 }
2089                 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
2090                 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
2091                 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
2092         }
2093
2094         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[6]) + surf->poly_numverts * (4 + 4 + 4 + 4 + 1 + 3) * sizeof(float));
2095         mesh->numverts = surf->poly_numverts;
2096         mesh->numtriangles = surf->poly_numverts - 2;
2097         mesh->verts = (float *)(mesh + 1);
2098         mesh->str = mesh->verts + mesh->numverts * 4;
2099         mesh->uvw = mesh->str + mesh->numverts * 4;
2100         mesh->abc = mesh->uvw + mesh->numverts * 4;
2101         mesh->lightmapoffsets = (int *)(mesh->abc + mesh->numverts * 4);
2102         mesh->index = mesh->lightmapoffsets + mesh->numverts;
2103         mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3;
2104         mesh->normals = (float *)(mesh->triangleneighbors + mesh->numtriangles * 3);
2105
2106         index = mesh->index;
2107         for (i = 0;i < mesh->numtriangles;i++)
2108         {
2109                 *index++ = 0;
2110                 *index++ = i + 1;
2111                 *index++ = i + 2;
2112         }
2113         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
2114
2115         VectorCopy(surf->plane->normal, normal);
2116         if (surf->flags & SURF_PLANEBACK)
2117                 VectorNegate(normal, normal);
2118         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
2119         {
2120                 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
2121                 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
2122                 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
2123                 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
2124                 // LordHavoc: calc lightmap data offset for vertex lighting to use
2125                 iu = (int) u;
2126                 iv = (int) v;
2127                 iu = bound(0, iu, smax);
2128                 iv = bound(0, iv, tmax);
2129                 u = u * uscale + ubase;
2130                 v = v * vscale + vbase;
2131
2132                 mesh->verts[i * 4 + 0] = in[0];
2133                 mesh->verts[i * 4 + 1] = in[1];
2134                 mesh->verts[i * 4 + 2] = in[2];
2135                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
2136                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
2137                 mesh->uvw[i * 4 + 0] = u;
2138                 mesh->uvw[i * 4 + 1] = v;
2139                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
2140                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
2141                 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
2142                 mesh->normals[i * 3 + 0] = normal[0];
2143                 mesh->normals[i * 3 + 1] = normal[1];
2144                 mesh->normals[i * 3 + 2] = normal[2];
2145         }
2146 }
2147
2148 void Mod_GenerateVertexMesh (msurface_t *surf)
2149 {
2150         int i, *index;
2151         float *in, s, t, normal[3];
2152         surfmesh_t *mesh;
2153
2154         surf->lightmaptexturestride = 0;
2155         surf->lightmaptexture = NULL;
2156
2157         surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[6]) + surf->poly_numverts * (4 + 4 + 4 + 3) * sizeof(float));
2158         mesh->numverts = surf->poly_numverts;
2159         mesh->numtriangles = surf->poly_numverts - 2;
2160         mesh->verts = (float *)(mesh + 1);
2161         mesh->str = mesh->verts + mesh->numverts * 4;
2162         mesh->abc = mesh->str + mesh->numverts * 4;
2163         mesh->index = (int *)(mesh->abc + mesh->numverts * 4);
2164         mesh->triangleneighbors = mesh->index + mesh->numtriangles * 3;
2165         mesh->normals = (float *)(mesh->triangleneighbors + mesh->numtriangles * 3);
2166
2167         index = mesh->index;
2168         for (i = 0;i < mesh->numtriangles;i++)
2169         {
2170                 *index++ = 0;
2171                 *index++ = i + 1;
2172                 *index++ = i + 2;
2173         }
2174         Mod_BuildTriangleNeighbors(mesh->triangleneighbors, mesh->index, mesh->numtriangles);
2175
2176         VectorCopy(surf->plane->normal, normal);
2177         if (surf->flags & SURF_PLANEBACK)
2178                 VectorNegate(normal, normal);
2179         for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
2180         {
2181                 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
2182                 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
2183                 mesh->verts[i * 4 + 0] = in[0];
2184                 mesh->verts[i * 4 + 1] = in[1];
2185                 mesh->verts[i * 4 + 2] = in[2];
2186                 mesh->str[i * 4 + 0] = s / surf->texinfo->texture->width;
2187                 mesh->str[i * 4 + 1] = t / surf->texinfo->texture->height;
2188                 mesh->abc[i * 4 + 0] = s * (1.0f / 16.0f);
2189                 mesh->abc[i * 4 + 1] = t * (1.0f / 16.0f);
2190                 mesh->normals[i * 3 + 0] = normal[0];
2191                 mesh->normals[i * 3 + 1] = normal[1];
2192                 mesh->normals[i * 3 + 2] = normal[2];
2193         }
2194 }
2195
2196 void Mod_GenerateSurfacePolygon (msurface_t *surf)
2197 {
2198         int i, lindex;
2199         float *vec, *vert, mins[3], maxs[3], temp[3], dist;
2200
2201         // convert edges back to a normal polygon
2202         surf->poly_numverts = surf->numedges;
2203         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
2204         for (i = 0;i < surf->numedges;i++)
2205         {
2206                 lindex = loadmodel->surfedges[surf->firstedge + i];
2207                 if (lindex > 0)
2208                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
2209                 else
2210                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
2211                 VectorCopy (vec, vert);
2212                 vert += 3;
2213         }
2214         vert = surf->poly_verts;
2215         VectorCopy(vert, mins);
2216         VectorCopy(vert, maxs);
2217         vert += 3;
2218         for (i = 1;i < surf->poly_numverts;i++)
2219         {
2220                 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
2221                 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
2222                 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
2223                 vert += 3;
2224         }
2225         VectorCopy(mins, surf->poly_mins);
2226         VectorCopy(maxs, surf->poly_maxs);
2227         surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
2228         surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
2229         surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
2230         surf->poly_radius2 = 0;
2231         vert = surf->poly_verts;
2232         for (i = 0;i < surf->poly_numverts;i++)
2233         {
2234                 VectorSubtract(vert, surf->poly_center, temp);
2235                 dist = DotProduct(temp, temp);
2236                 if (surf->poly_radius2 < dist)
2237                         surf->poly_radius2 = dist;
2238                 vert += 3;
2239         }
2240         surf->poly_radius = sqrt(surf->poly_radius2);
2241 }
2242
2243 /*
2244 =================
2245 Mod_LoadFaces
2246 =================
2247 */
2248 static void Mod_LoadFaces (lump_t *l)
2249 {
2250         dface_t *in;
2251         msurface_t      *out;
2252         int i, count, surfnum, planenum, ssize, tsize;
2253
2254         in = (void *)(mod_base + l->fileofs);
2255         if (l->filelen % sizeof(*in))
2256                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2257         count = l->filelen / sizeof(*in);
2258         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2259
2260         loadmodel->surfaces = out;
2261         loadmodel->numsurfaces = count;
2262         loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
2263         loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
2264
2265         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
2266         {
2267                 out->number = surfnum;
2268                 // FIXME: validate edges, texinfo, etc?
2269                 out->firstedge = LittleLong(in->firstedge);
2270                 out->numedges = LittleShort(in->numedges);
2271                 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
2272                         Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
2273
2274                 i = LittleShort (in->texinfo);
2275                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
2276                         Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
2277                 out->texinfo = loadmodel->texinfo + i;
2278                 out->flags = out->texinfo->texture->flags;
2279
2280                 planenum = LittleShort(in->planenum);
2281                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
2282                         Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
2283
2284                 if (LittleShort(in->side))
2285                         out->flags |= SURF_PLANEBACK;
2286
2287                 out->plane = loadmodel->planes + planenum;
2288
2289                 // clear lightmap (filled in later)
2290                 out->lightmaptexture = NULL;
2291
2292                 // force lightmap upload on first time seeing the surface
2293                 out->cached_dlight = true;
2294
2295                 CalcSurfaceExtents (out);
2296
2297                 ssize = (out->extents[0] >> 4) + 1;
2298                 tsize = (out->extents[1] >> 4) + 1;
2299
2300                 // lighting info
2301                 for (i = 0;i < MAXLIGHTMAPS;i++)
2302                         out->styles[i] = in->styles[i];
2303                 i = LittleLong(in->lightofs);
2304                 if (i == -1)
2305                         out->samples = NULL;
2306                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
2307                         out->samples = loadmodel->lightdata + i;
2308                 else // LordHavoc: white lighting (bsp version 29)
2309                         out->samples = loadmodel->lightdata + (i * 3);
2310
2311                 Mod_GenerateSurfacePolygon(out);
2312                 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
2313                 {
2314                         if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
2315                                 Host_Error ("Bad surface extents");
2316                         Mod_GenerateWallMesh (out, false);
2317                         // stainmap for permanent marks on walls
2318                         out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
2319                         // clear to white
2320                         memset(out->stainsamples, 255, ssize * tsize * 3);
2321                 }
2322                 else
2323                         Mod_GenerateVertexMesh (out);
2324         }
2325 }
2326
2327 /*
2328 =================
2329 Mod_SetParent
2330 =================
2331 */
2332 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
2333 {
2334         node->parent = parent;
2335         if (node->contents < 0)
2336                 return;
2337         Mod_SetParent (node->children[0], node);
2338         Mod_SetParent (node->children[1], node);
2339 }
2340
2341 /*
2342 =================
2343 Mod_LoadNodes
2344 =================
2345 */
2346 static void Mod_LoadNodes (lump_t *l)
2347 {
2348         int                     i, j, count, p;
2349         dnode_t         *in;
2350         mnode_t         *out;
2351
2352         in = (void *)(mod_base + l->fileofs);
2353         if (l->filelen % sizeof(*in))
2354                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2355         count = l->filelen / sizeof(*in);
2356         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2357
2358         loadmodel->nodes = out;
2359         loadmodel->numnodes = count;
2360
2361         for ( i=0 ; i<count ; i++, in++, out++)
2362         {
2363                 for (j=0 ; j<3 ; j++)
2364                 {
2365                         out->mins[j] = LittleShort (in->mins[j]);
2366                         out->maxs[j] = LittleShort (in->maxs[j]);
2367                 }
2368
2369                 p = LittleLong(in->planenum);
2370                 out->plane = loadmodel->planes + p;
2371
2372                 out->firstsurface = LittleShort (in->firstface);
2373                 out->numsurfaces = LittleShort (in->numfaces);
2374
2375                 for (j=0 ; j<2 ; j++)
2376                 {
2377                         p = LittleShort (in->children[j]);
2378                         if (p >= 0)
2379                                 out->children[j] = loadmodel->nodes + p;
2380                         else
2381                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
2382                 }
2383         }
2384
2385         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
2386 }
2387
2388 /*
2389 =================
2390 Mod_LoadLeafs
2391 =================
2392 */
2393 static void Mod_LoadLeafs (lump_t *l)
2394 {
2395         dleaf_t         *in;
2396         mleaf_t         *out;
2397         int                     i, j, count, p;
2398
2399         in = (void *)(mod_base + l->fileofs);
2400         if (l->filelen % sizeof(*in))
2401                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2402         count = l->filelen / sizeof(*in);
2403         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2404
2405         loadmodel->leafs = out;
2406         loadmodel->numleafs = count;
2407
2408         for ( i=0 ; i<count ; i++, in++, out++)
2409         {
2410                 for (j=0 ; j<3 ; j++)
2411                 {
2412                         out->mins[j] = LittleShort (in->mins[j]);
2413                         out->maxs[j] = LittleShort (in->maxs[j]);
2414                 }
2415
2416                 p = LittleLong(in->contents);
2417                 out->contents = p;
2418
2419                 out->firstmarksurface = loadmodel->marksurfaces +
2420                         LittleShort(in->firstmarksurface);
2421                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
2422
2423                 p = LittleLong(in->visofs);
2424                 if (p == -1)
2425                         out->compressed_vis = NULL;
2426                 else
2427                         out->compressed_vis = loadmodel->visdata + p;
2428
2429                 for (j=0 ; j<4 ; j++)
2430                         out->ambient_sound_level[j] = in->ambient_level[j];
2431
2432                 // FIXME: Insert caustics here
2433         }
2434 }
2435
2436 /*
2437 =================
2438 Mod_LoadClipnodes
2439 =================
2440 */
2441 static void Mod_LoadClipnodes (lump_t *l)
2442 {
2443         dclipnode_t *in, *out;
2444         int                     i, count;
2445         hull_t          *hull;
2446
2447         in = (void *)(mod_base + l->fileofs);
2448         if (l->filelen % sizeof(*in))
2449                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2450         count = l->filelen / sizeof(*in);
2451         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
2452
2453         loadmodel->clipnodes = out;
2454         loadmodel->numclipnodes = count;
2455
2456         if (loadmodel->ishlbsp)
2457         {
2458                 hull = &loadmodel->hulls[1];
2459                 hull->clipnodes = out;
2460                 hull->firstclipnode = 0;
2461                 hull->lastclipnode = count-1;
2462                 hull->planes = loadmodel->planes;
2463                 hull->clip_mins[0] = -16;
2464                 hull->clip_mins[1] = -16;
2465                 hull->clip_mins[2] = -36;
2466                 hull->clip_maxs[0] = 16;
2467                 hull->clip_maxs[1] = 16;
2468                 hull->clip_maxs[2] = 36;
2469                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2470
2471                 hull = &loadmodel->hulls[2];
2472                 hull->clipnodes = out;
2473                 hull->firstclipnode = 0;
2474                 hull->lastclipnode = count-1;
2475                 hull->planes = loadmodel->planes;
2476                 hull->clip_mins[0] = -32;
2477                 hull->clip_mins[1] = -32;
2478                 hull->clip_mins[2] = -32;
2479                 hull->clip_maxs[0] = 32;
2480                 hull->clip_maxs[1] = 32;
2481                 hull->clip_maxs[2] = 32;
2482                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2483
2484                 hull = &loadmodel->hulls[3];
2485                 hull->clipnodes = out;
2486                 hull->firstclipnode = 0;
2487                 hull->lastclipnode = count-1;
2488                 hull->planes = loadmodel->planes;
2489                 hull->clip_mins[0] = -16;
2490                 hull->clip_mins[1] = -16;
2491                 hull->clip_mins[2] = -18;
2492                 hull->clip_maxs[0] = 16;
2493                 hull->clip_maxs[1] = 16;
2494                 hull->clip_maxs[2] = 18;
2495                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2496         }
2497         else
2498         {
2499                 hull = &loadmodel->hulls[1];
2500                 hull->clipnodes = out;
2501                 hull->firstclipnode = 0;
2502                 hull->lastclipnode = count-1;
2503                 hull->planes = loadmodel->planes;
2504                 hull->clip_mins[0] = -16;
2505                 hull->clip_mins[1] = -16;
2506                 hull->clip_mins[2] = -24;
2507                 hull->clip_maxs[0] = 16;
2508                 hull->clip_maxs[1] = 16;
2509                 hull->clip_maxs[2] = 32;
2510                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2511
2512                 hull = &loadmodel->hulls[2];
2513                 hull->clipnodes = out;
2514                 hull->firstclipnode = 0;
2515                 hull->lastclipnode = count-1;
2516                 hull->planes = loadmodel->planes;
2517                 hull->clip_mins[0] = -32;
2518                 hull->clip_mins[1] = -32;
2519                 hull->clip_mins[2] = -24;
2520                 hull->clip_maxs[0] = 32;
2521                 hull->clip_maxs[1] = 32;
2522                 hull->clip_maxs[2] = 64;
2523                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
2524         }
2525
2526         for (i=0 ; i<count ; i++, out++, in++)
2527         {
2528                 out->planenum = LittleLong(in->planenum);
2529                 out->children[0] = LittleShort(in->children[0]);
2530                 out->children[1] = LittleShort(in->children[1]);
2531                 if (out->children[0] >= count || out->children[1] >= count)
2532                         Host_Error("Corrupt clipping hull (out of range child)\n");
2533         }
2534 }
2535
2536 /*
2537 =================
2538 Mod_MakeHull0
2539
2540 Duplicate the drawing hull structure as a clipping hull
2541 =================
2542 */
2543 static void Mod_MakeHull0 (void)
2544 {
2545         mnode_t         *in;
2546         dclipnode_t *out;
2547         int                     i;
2548         hull_t          *hull;
2549
2550         hull = &loadmodel->hulls[0];
2551
2552         in = loadmodel->nodes;
2553         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
2554
2555         hull->clipnodes = out;
2556         hull->firstclipnode = 0;
2557         hull->lastclipnode = loadmodel->numnodes - 1;
2558         hull->planes = loadmodel->planes;
2559
2560         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
2561         {
2562                 out->planenum = in->plane - loadmodel->planes;
2563                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
2564                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
2565         }
2566 }
2567
2568 /*
2569 =================
2570 Mod_LoadMarksurfaces
2571 =================
2572 */
2573 static void Mod_LoadMarksurfaces (lump_t *l)
2574 {
2575         int i, j;
2576         short *in;
2577
2578         in = (void *)(mod_base + l->fileofs);
2579         if (l->filelen % sizeof(*in))
2580                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2581         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
2582         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
2583
2584         for (i = 0;i < loadmodel->nummarksurfaces;i++)
2585         {
2586                 j = (unsigned) LittleShort(in[i]);
2587                 if (j >= loadmodel->numsurfaces)
2588                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
2589                 loadmodel->marksurfaces[i] = j;
2590         }
2591 }
2592
2593 /*
2594 =================
2595 Mod_LoadSurfedges
2596 =================
2597 */
2598 static void Mod_LoadSurfedges (lump_t *l)
2599 {
2600         int             i;
2601         int             *in;
2602
2603         in = (void *)(mod_base + l->fileofs);
2604         if (l->filelen % sizeof(*in))
2605                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
2606         loadmodel->numsurfedges = l->filelen / sizeof(*in);
2607         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
2608
2609         for (i = 0;i < loadmodel->numsurfedges;i++)
2610                 loadmodel->surfedges[i] = LittleLong (in[i]);
2611 }
2612
2613
2614 /*
2615 =================
2616 Mod_LoadPlanes
2617 =================
2618 */
2619 static void Mod_LoadPlanes (lump_t *l)
2620 {
2621         int                     i;
2622         mplane_t        *out;
2623         dplane_t        *in;
2624
2625         in = (void *)(mod_base + l->fileofs);
2626         if (l->filelen % sizeof(*in))
2627                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
2628
2629         loadmodel->numplanes = l->filelen / sizeof(*in);
2630         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
2631
2632         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
2633         {
2634                 out->normal[0] = LittleFloat (in->normal[0]);
2635                 out->normal[1] = LittleFloat (in->normal[1]);
2636                 out->normal[2] = LittleFloat (in->normal[2]);
2637                 out->dist = LittleFloat (in->dist);
2638
2639                 PlaneClassify(out);
2640         }
2641 }
2642
2643 #define MAX_POINTS_ON_WINDING 64
2644
2645 typedef struct
2646 {
2647         int numpoints;
2648         int padding;
2649         double points[8][3]; // variable sized
2650 }
2651 winding_t;
2652
2653 /*
2654 ==================
2655 NewWinding
2656 ==================
2657 */
2658 static winding_t *NewWinding (int points)
2659 {
2660         winding_t *w;
2661         int size;
2662
2663         if (points > MAX_POINTS_ON_WINDING)
2664                 Sys_Error("NewWinding: too many points\n");
2665
2666         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2667         w = Mem_Alloc(loadmodel->mempool, size);
2668         memset (w, 0, size);
2669
2670         return w;
2671 }
2672
2673 static void FreeWinding (winding_t *w)
2674 {
2675         Mem_Free(w);
2676 }
2677
2678 /*
2679 =================
2680 BaseWindingForPlane
2681 =================
2682 */
2683 static winding_t *BaseWindingForPlane (mplane_t *p)
2684 {
2685         double org[3], vright[3], vup[3], normal[3];
2686         winding_t *w;
2687
2688         VectorCopy(p->normal, normal);
2689         VectorVectorsDouble(normal, vright, vup);
2690
2691         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
2692         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
2693
2694         // project a really big axis aligned box onto the plane
2695         w = NewWinding (4);
2696
2697         VectorScale (p->normal, p->dist, org);
2698
2699         VectorSubtract (org, vright, w->points[0]);
2700         VectorAdd (w->points[0], vup, w->points[0]);
2701
2702         VectorAdd (org, vright, w->points[1]);
2703         VectorAdd (w->points[1], vup, w->points[1]);
2704
2705         VectorAdd (org, vright, w->points[2]);
2706         VectorSubtract (w->points[2], vup, w->points[2]);
2707
2708         VectorSubtract (org, vright, w->points[3]);
2709         VectorSubtract (w->points[3], vup, w->points[3]);
2710
2711         w->numpoints = 4;
2712
2713         return w;
2714 }
2715
2716 /*
2717 ==================
2718 ClipWinding
2719
2720 Clips the winding to the plane, returning the new winding on the positive side
2721 Frees the input winding.
2722 If keepon is true, an exactly on-plane winding will be saved, otherwise
2723 it will be clipped away.
2724 ==================
2725 */
2726 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
2727 {
2728         double  dists[MAX_POINTS_ON_WINDING + 1];
2729         int             sides[MAX_POINTS_ON_WINDING + 1];
2730         int             counts[3];
2731         double  dot;
2732         int             i, j;
2733         double  *p1, *p2;
2734         double  mid[3];
2735         winding_t       *neww;
2736         int             maxpts;
2737
2738         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2739
2740         // determine sides for each point
2741         for (i = 0;i < in->numpoints;i++)
2742         {
2743                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
2744                 if (dot > ON_EPSILON)
2745                         sides[i] = SIDE_FRONT;
2746                 else if (dot < -ON_EPSILON)
2747                         sides[i] = SIDE_BACK;
2748                 else
2749                         sides[i] = SIDE_ON;
2750                 counts[sides[i]]++;
2751         }
2752         sides[i] = sides[0];
2753         dists[i] = dists[0];
2754
2755         if (keepon && !counts[0] && !counts[1])
2756                 return in;
2757
2758         if (!counts[0])
2759         {
2760                 FreeWinding (in);
2761                 return NULL;
2762         }
2763         if (!counts[1])
2764                 return in;
2765
2766         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2767         if (maxpts > MAX_POINTS_ON_WINDING)
2768                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2769
2770         neww = NewWinding (maxpts);
2771
2772         for (i = 0;i < in->numpoints;i++)
2773         {
2774                 if (neww->numpoints >= maxpts)
2775                         Sys_Error ("ClipWinding: points exceeded estimate");
2776
2777                 p1 = in->points[i];
2778
2779                 if (sides[i] == SIDE_ON)
2780                 {
2781                         VectorCopy (p1, neww->points[neww->numpoints]);
2782                         neww->numpoints++;
2783                         continue;
2784                 }
2785
2786                 if (sides[i] == SIDE_FRONT)
2787                 {
2788                         VectorCopy (p1, neww->points[neww->numpoints]);
2789                         neww->numpoints++;
2790                 }
2791
2792                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2793                         continue;
2794
2795                 // generate a split point
2796                 p2 = in->points[(i+1)%in->numpoints];
2797
2798                 dot = dists[i] / (dists[i]-dists[i+1]);
2799                 for (j = 0;j < 3;j++)
2800                 {       // avoid round off error when possible
2801                         if (split->normal[j] == 1)
2802                                 mid[j] = split->dist;
2803                         else if (split->normal[j] == -1)
2804                                 mid[j] = -split->dist;
2805                         else
2806                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2807                 }
2808
2809                 VectorCopy (mid, neww->points[neww->numpoints]);
2810                 neww->numpoints++;
2811         }
2812
2813         // free the original winding
2814         FreeWinding (in);
2815
2816         return neww;
2817 }
2818
2819
2820 /*
2821 ==================
2822 DivideWinding
2823
2824 Divides a winding by a plane, producing one or two windings.  The
2825 original winding is not damaged or freed.  If only on one side, the
2826 returned winding will be the input winding.  If on both sides, two
2827 new windings will be created.
2828 ==================
2829 */
2830 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2831 {
2832         double  dists[MAX_POINTS_ON_WINDING + 1];
2833         int             sides[MAX_POINTS_ON_WINDING + 1];
2834         int             counts[3];
2835         double  dot;
2836         int             i, j;
2837         double  *p1, *p2;
2838         double  mid[3];
2839         winding_t       *f, *b;
2840         int             maxpts;
2841
2842         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2843
2844         // determine sides for each point
2845         for (i = 0;i < in->numpoints;i++)
2846         {
2847                 dot = DotProduct (in->points[i], split->normal);
2848                 dot -= split->dist;
2849                 dists[i] = dot;
2850                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2851                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2852                 else sides[i] = SIDE_ON;
2853                 counts[sides[i]]++;
2854         }
2855         sides[i] = sides[0];
2856         dists[i] = dists[0];
2857
2858         *front = *back = NULL;
2859
2860         if (!counts[0])
2861         {
2862                 *back = in;
2863                 return;
2864         }
2865         if (!counts[1])
2866         {
2867                 *front = in;
2868                 return;
2869         }
2870
2871         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2872
2873         if (maxpts > MAX_POINTS_ON_WINDING)
2874                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2875
2876         *front = f = NewWinding (maxpts);
2877         *back = b = NewWinding (maxpts);
2878
2879         for (i = 0;i < in->numpoints;i++)
2880         {
2881                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2882                         Sys_Error ("DivideWinding: points exceeded estimate");
2883
2884                 p1 = in->points[i];
2885
2886                 if (sides[i] == SIDE_ON)
2887                 {
2888                         VectorCopy (p1, f->points[f->numpoints]);
2889                         f->numpoints++;
2890                         VectorCopy (p1, b->points[b->numpoints]);
2891                         b->numpoints++;
2892                         continue;
2893                 }
2894
2895                 if (sides[i] == SIDE_FRONT)
2896                 {
2897                         VectorCopy (p1, f->points[f->numpoints]);
2898                         f->numpoints++;
2899                 }
2900                 else if (sides[i] == SIDE_BACK)
2901                 {
2902                         VectorCopy (p1, b->points[b->numpoints]);
2903                         b->numpoints++;
2904                 }
2905
2906                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2907                         continue;
2908
2909                 // generate a split point
2910                 p2 = in->points[(i+1)%in->numpoints];
2911
2912                 dot = dists[i] / (dists[i]-dists[i+1]);
2913                 for (j = 0;j < 3;j++)
2914                 {       // avoid round off error when possible
2915                         if (split->normal[j] == 1)
2916                                 mid[j] = split->dist;
2917                         else if (split->normal[j] == -1)
2918                                 mid[j] = -split->dist;
2919                         else
2920                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2921                 }
2922
2923                 VectorCopy (mid, f->points[f->numpoints]);
2924                 f->numpoints++;
2925                 VectorCopy (mid, b->points[b->numpoints]);
2926                 b->numpoints++;
2927         }
2928 }
2929
2930 typedef struct portal_s
2931 {
2932         mplane_t plane;
2933         mnode_t *nodes[2];              // [0] = front side of plane
2934         struct portal_s *next[2];
2935         winding_t *winding;
2936         struct portal_s *chain; // all portals are linked into a list
2937 }
2938 portal_t;
2939
2940 static portal_t *portalchain;
2941
2942 /*
2943 ===========
2944 AllocPortal
2945 ===========
2946 */
2947 static portal_t *AllocPortal (void)
2948 {
2949         portal_t *p;
2950         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2951         p->chain = portalchain;
2952         portalchain = p;
2953         return p;
2954 }
2955
2956 static void FreePortal(portal_t *p)
2957 {
2958         Mem_Free(p);
2959 }
2960
2961 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2962 {
2963         // calculate children first
2964         if (node->children[0]->contents >= 0)
2965                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2966         if (node->children[1]->contents >= 0)
2967                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2968
2969         // make combined bounding box from children
2970         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2971         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2972         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2973         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2974         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2975         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2976 }
2977
2978 static void Mod_FinalizePortals(void)
2979 {
2980         int i, j, numportals, numpoints;
2981         portal_t *p, *pnext;
2982         mportal_t *portal;
2983         mvertex_t *point;
2984         mleaf_t *leaf, *endleaf;
2985         winding_t *w;
2986
2987         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2988         leaf = loadmodel->leafs;
2989         endleaf = leaf + loadmodel->numleafs;
2990         for (;leaf < endleaf;leaf++)
2991         {
2992                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2993                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2994         }
2995         p = portalchain;
2996         while(p)
2997         {
2998                 if (p->winding)
2999                 {
3000                         for (i = 0;i < 2;i++)
3001                         {
3002                                 leaf = (mleaf_t *)p->nodes[i];
3003                                 w = p->winding;
3004                                 for (j = 0;j < w->numpoints;j++)
3005                                 {
3006                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
3007                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
3008                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
3009                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
3010                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
3011                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
3012                                 }
3013                         }
3014                 }
3015                 p = p->chain;
3016         }
3017
3018         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
3019
3020         // tally up portal and point counts
3021         p = portalchain;
3022         numportals = 0;
3023         numpoints = 0;
3024         while(p)
3025         {
3026                 // note: this check must match the one below or it will usually corrupt memory
3027                 // 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
3028                 if (p->winding && p->nodes[0] != p->nodes[1]
3029                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
3030                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
3031                 {
3032                         numportals += 2;
3033                         numpoints += p->winding->numpoints * 2;
3034                 }
3035                 p = p->chain;
3036         }
3037         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
3038         loadmodel->numportals = numportals;
3039         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
3040         loadmodel->numportalpoints = numpoints;
3041         // clear all leaf portal chains
3042         for (i = 0;i < loadmodel->numleafs;i++)
3043                 loadmodel->leafs[i].portals = NULL;
3044         // process all portals in the global portal chain, while freeing them
3045         portal = loadmodel->portals;
3046         point = loadmodel->portalpoints;
3047         p = portalchain;
3048         portalchain = NULL;
3049         while (p)
3050         {
3051                 pnext = p->chain;
3052
3053                 if (p->winding)
3054                 {
3055                         // note: this check must match the one above or it will usually corrupt memory
3056                         // 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
3057                         if (p->nodes[0] != p->nodes[1]
3058                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
3059                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
3060                         {
3061                                 // first make the back to front portal (forward portal)
3062                                 portal->points = point;
3063                                 portal->numpoints = p->winding->numpoints;
3064                                 portal->plane.dist = p->plane.dist;
3065                                 VectorCopy(p->plane.normal, portal->plane.normal);
3066                                 portal->here = (mleaf_t *)p->nodes[1];
3067                                 portal->past = (mleaf_t *)p->nodes[0];
3068                                 // copy points
3069                                 for (j = 0;j < portal->numpoints;j++)
3070                                 {
3071                                         VectorCopy(p->winding->points[j], point->position);
3072                                         point++;
3073                                 }
3074                                 PlaneClassify(&portal->plane);
3075
3076                                 // link into leaf's portal chain
3077                                 portal->next = portal->here->portals;
3078                                 portal->here->portals = portal;
3079
3080                                 // advance to next portal
3081                                 portal++;
3082
3083                                 // then make the front to back portal (backward portal)
3084                                 portal->points = point;
3085                                 portal->numpoints = p->winding->numpoints;
3086                                 portal->plane.dist = -p->plane.dist;
3087                                 VectorNegate(p->plane.normal, portal->plane.normal);
3088                                 portal->here = (mleaf_t *)p->nodes[0];
3089                                 portal->past = (mleaf_t *)p->nodes[1];
3090                                 // copy points
3091                                 for (j = portal->numpoints - 1;j >= 0;j--)
3092                                 {
3093                                         VectorCopy(p->winding->points[j], point->position);
3094                                         point++;
3095                                 }
3096                                 PlaneClassify(&portal->plane);
3097
3098                                 // link into leaf's portal chain
3099                                 portal->next = portal->here->portals;
3100                                 portal->here->portals = portal;
3101
3102                                 // advance to next portal
3103                                 portal++;
3104                         }
3105                         FreeWinding(p->winding);
3106                 }
3107                 FreePortal(p);
3108                 p = pnext;
3109         }
3110 }
3111
3112 /*
3113 =============
3114 AddPortalToNodes
3115 =============
3116 */
3117 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
3118 {
3119         if (!front)
3120                 Host_Error ("AddPortalToNodes: NULL front node");
3121         if (!back)
3122                 Host_Error ("AddPortalToNodes: NULL back node");
3123         if (p->nodes[0] || p->nodes[1])
3124                 Host_Error ("AddPortalToNodes: already included");
3125         // 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
3126
3127         p->nodes[0] = front;
3128         p->next[0] = (portal_t *)front->portals;
3129         front->portals = (mportal_t *)p;
3130
3131         p->nodes[1] = back;
3132         p->next[1] = (portal_t *)back->portals;
3133         back->portals = (mportal_t *)p;
3134 }
3135
3136 /*
3137 =============
3138 RemovePortalFromNode
3139 =============
3140 */
3141 static void RemovePortalFromNodes(portal_t *portal)
3142 {
3143         int i;
3144         mnode_t *node;
3145         void **portalpointer;
3146         portal_t *t;
3147         for (i = 0;i < 2;i++)
3148         {
3149                 node = portal->nodes[i];
3150
3151                 portalpointer = (void **) &node->portals;
3152                 while (1)
3153                 {
3154                         t = *portalpointer;
3155                         if (!t)
3156                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
3157
3158                         if (t == portal)
3159                         {
3160                                 if (portal->nodes[0] == node)
3161                                 {
3162                                         *portalpointer = portal->next[0];
3163                                         portal->nodes[0] = NULL;
3164                                 }
3165                                 else if (portal->nodes[1] == node)
3166                                 {
3167                                         *portalpointer = portal->next[1];
3168                                         portal->nodes[1] = NULL;
3169                                 }
3170                                 else
3171                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
3172                                 break;
3173                         }
3174
3175                         if (t->nodes[0] == node)
3176                                 portalpointer = (void **) &t->next[0];
3177                         else if (t->nodes[1] == node)
3178                                 portalpointer = (void **) &t->next[1];
3179                         else
3180                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
3181                 }
3182         }
3183 }
3184
3185 static void Mod_RecursiveNodePortals (mnode_t *node)
3186 {
3187         int side;
3188         mnode_t *front, *back, *other_node;
3189         mplane_t clipplane, *plane;
3190         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
3191         winding_t *nodeportalwinding, *frontwinding, *backwinding;
3192
3193         // if a leaf, we're done
3194         if (node->contents)
3195                 return;
3196
3197         plane = node->plane;
3198
3199         front = node->children[0];
3200         back = node->children[1];
3201         if (front == back)
3202                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
3203
3204         // create the new portal by generating a polygon for the node plane,
3205         // and clipping it by all of the other portals (which came from nodes above this one)
3206         nodeportal = AllocPortal ();
3207         nodeportal->plane = *node->plane;
3208
3209         nodeportalwinding = BaseWindingForPlane (node->plane);
3210         side = 0;       // shut up compiler warning
3211         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
3212         {
3213                 clipplane = portal->plane;
3214                 if (portal->nodes[0] == portal->nodes[1])
3215                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
3216                 if (portal->nodes[0] == node)
3217                         side = 0;
3218                 else if (portal->nodes[1] == node)
3219                 {
3220                         clipplane.dist = -clipplane.dist;
3221                         VectorNegate (clipplane.normal, clipplane.normal);
3222                         side = 1;
3223                 }
3224                 else
3225                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
3226
3227                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
3228                 if (!nodeportalwinding)
3229                 {
3230                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
3231                         break;
3232                 }
3233         }
3234
3235         if (nodeportalwinding)
3236         {
3237                 // if the plane was not clipped on all sides, there was an error
3238                 nodeportal->winding = nodeportalwinding;
3239                 AddPortalToNodes (nodeportal, front, back);
3240         }
3241
3242         // split the portals of this node along this node's plane and assign them to the children of this node
3243         // (migrating the portals downward through the tree)
3244         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
3245         {
3246                 if (portal->nodes[0] == portal->nodes[1])
3247                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
3248                 if (portal->nodes[0] == node)
3249                         side = 0;
3250                 else if (portal->nodes[1] == node)
3251                         side = 1;
3252                 else
3253                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
3254                 nextportal = portal->next[side];
3255
3256                 other_node = portal->nodes[!side];
3257                 RemovePortalFromNodes (portal);
3258
3259                 // cut the portal into two portals, one on each side of the node plane
3260                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
3261
3262                 if (!frontwinding)
3263                 {
3264                         if (side == 0)
3265                                 AddPortalToNodes (portal, back, other_node);
3266                         else
3267                                 AddPortalToNodes (portal, other_node, back);
3268                         continue;
3269                 }
3270                 if (!backwinding)
3271                 {
3272                         if (side == 0)
3273                                 AddPortalToNodes (portal, front, other_node);
3274                         else
3275                                 AddPortalToNodes (portal, other_node, front);
3276                         continue;
3277                 }
3278
3279                 // the winding is split
3280                 splitportal = AllocPortal ();
3281                 temp = splitportal->chain;
3282                 *splitportal = *portal;
3283                 splitportal->chain = temp;
3284                 splitportal->winding = backwinding;
3285                 FreeWinding (portal->winding);
3286                 portal->winding = frontwinding;
3287
3288                 if (side == 0)
3289                 {
3290                         AddPortalToNodes (portal, front, other_node);
3291                         AddPortalToNodes (splitportal, back, other_node);
3292                 }
3293                 else
3294                 {
3295                         AddPortalToNodes (portal, other_node, front);
3296                         AddPortalToNodes (splitportal, other_node, back);
3297                 }
3298         }
3299
3300         Mod_RecursiveNodePortals(front);
3301         Mod_RecursiveNodePortals(back);
3302 }
3303
3304
3305 static void Mod_MakePortals(void)
3306 {
3307         portalchain = NULL;
3308         Mod_RecursiveNodePortals (loadmodel->nodes);
3309         Mod_FinalizePortals();
3310 }
3311
3312 /*
3313 =================
3314 Mod_LoadBrushModel
3315 =================
3316 */
3317 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
3318 extern void R_Model_Brush_Draw(entity_render_t *ent);
3319 //extern void R_Model_Brush_DrawFakeShadow(entity_render_t *ent);
3320 extern void R_Model_Brush_DrawDepth(entity_render_t *ent);
3321 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius, int visiblevolume);
3322 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, float lightradius2, float lightdistbias, float lightsubtract, float *lightcolor);
3323 extern void R_Model_Brush_DrawOntoLight(entity_render_t *ent);
3324 void Mod_LoadBrushModel (model_t *mod, void *buffer)
3325 {
3326         int                     i, j;
3327         dheader_t       *header;
3328         dmodel_t        *bm;
3329         mempool_t       *mainmempool;
3330         char            *loadname;
3331         model_t         *originalloadmodel;
3332
3333         mod->type = mod_brush;
3334
3335         header = (dheader_t *)buffer;
3336
3337         i = LittleLong (header->version);
3338         if (i != BSPVERSION && i != 30)
3339                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
3340         mod->ishlbsp = i == 30;
3341         if (loadmodel->isworldmodel)
3342         {
3343                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
3344                 // until we get a texture for it...
3345                 R_ResetQuakeSky();
3346         }
3347
3348 // swap all the lumps
3349         mod_base = (qbyte *)header;
3350
3351         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
3352                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
3353
3354 // load into heap
3355
3356         // store which lightmap format to use
3357         mod->lightmaprgba = r_lightmaprgba.integer;
3358
3359         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
3360         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
3361         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
3362         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
3363         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
3364         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
3365         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
3366         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
3367         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
3368         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
3369         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
3370         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
3371         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
3372         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
3373         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
3374
3375         Mod_MakeHull0 ();
3376         Mod_MakePortals();
3377
3378         mod->numframes = 2;             // regular and alternate animation
3379
3380         mainmempool = mod->mempool;
3381         loadname = mod->name;
3382
3383         Mod_LoadLightList ();
3384         originalloadmodel = loadmodel;
3385
3386 //
3387 // set up the submodels (FIXME: this is confusing)
3388 //
3389         for (i = 0;i < mod->numsubmodels;i++)
3390         {
3391                 int k, l;
3392                 float dist, modelyawradius, modelradius, *vec;
3393                 msurface_t *surf;
3394
3395                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
3396                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
3397                 modelyawradius = 0;
3398                 modelradius = 0;
3399
3400                 bm = &mod->submodels[i];
3401
3402                 mod->hulls[0].firstclipnode = bm->headnode[0];
3403                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
3404                 {
3405                         mod->hulls[j].firstclipnode = bm->headnode[j];
3406                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
3407                 }
3408
3409                 mod->firstmodelsurface = bm->firstface;
3410                 mod->nummodelsurfaces = bm->numfaces;
3411
3412                 // this gets altered below if sky is used
3413                 mod->DrawSky = NULL;
3414                 mod->Draw = R_Model_Brush_Draw;
3415                 mod->DrawFakeShadow = NULL;
3416                 mod->DrawDepth = R_Model_Brush_DrawDepth;
3417                 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
3418                 mod->DrawLight = R_Model_Brush_DrawLight;
3419                 mod->DrawOntoLight = R_Model_Brush_DrawOntoLight;
3420                 if (mod->nummodelsurfaces)
3421                 {
3422                         // LordHavoc: calculate bmodel bounding box rather than trusting what it says
3423                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3424                         {
3425                                 // we only need to have a drawsky function if it is used (usually only on world model)
3426                                 if (surf->texinfo->texture->shader == &Cshader_sky)
3427                                         mod->DrawSky = R_Model_Brush_DrawSky;
3428                                 for (k = 0;k < surf->numedges;k++)
3429                                 {
3430                                         l = mod->surfedges[k + surf->firstedge];
3431                                         if (l > 0)
3432                                                 vec = mod->vertexes[mod->edges[l].v[0]].position;
3433                                         else
3434                                                 vec = mod->vertexes[mod->edges[-l].v[1]].position;
3435                                         if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3436                                         if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3437                                         if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3438                                         if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3439                                         if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3440                                         if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3441                                         dist = vec[0]*vec[0]+vec[1]*vec[1];
3442                                         if (modelyawradius < dist)
3443                                                 modelyawradius = dist;
3444                                         dist += vec[2]*vec[2];
3445                                         if (modelradius < dist)
3446                                                 modelradius = dist;
3447                                 }
3448                         }
3449                         modelyawradius = sqrt(modelyawradius);
3450                         modelradius = sqrt(modelradius);
3451                         mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3452                         mod->yawmins[2] = mod->normalmins[2];
3453                         mod->yawmaxs[2] = mod->normalmaxs[2];
3454                         mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3455                         mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3456                         mod->radius = modelradius;
3457                         mod->radius2 = modelradius * modelradius;
3458                         // LordHavoc: build triangle meshs for entire model's geometry
3459                         // (only used for shadow volumes)
3460                         mod->shadowmesh = Mod_ShadowMesh_Begin(originalloadmodel->mempool);
3461                         for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
3462                                 if (surf->flags & SURF_CLIPSOLID)
3463                                         Mod_ShadowMesh_AddPolygon(originalloadmodel->mempool, mod->shadowmesh, surf->poly_numverts, surf->poly_verts);
3464                         mod->shadowmesh = Mod_ShadowMesh_Finish(originalloadmodel->mempool, mod->shadowmesh);
3465                         Mod_ShadowMesh_CalcBBox(mod->shadowmesh, mod->shadowmesh_mins, mod->shadowmesh_maxs, mod->shadowmesh_center, &mod->shadowmesh_radius);
3466                 }
3467                 else
3468                 {
3469                         // LordHavoc: empty submodel (lacrima.bsp has such a glitch)
3470                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3471                         VectorClear(mod->normalmins);
3472                         VectorClear(mod->normalmaxs);
3473                         VectorClear(mod->yawmins);
3474                         VectorClear(mod->yawmaxs);
3475                         VectorClear(mod->rotatedmins);
3476                         VectorClear(mod->rotatedmaxs);
3477                         mod->radius = 0;
3478                         mod->radius2 = 0;
3479                         mod->shadowmesh = NULL;
3480                 }
3481
3482                 mod->numleafs = bm->visleafs;
3483
3484                 // LordHavoc: only register submodels if it is the world
3485                 // (prevents bsp models from replacing world submodels)
3486                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
3487                 {
3488                         char    name[10];
3489                         // duplicate the basic information
3490                         sprintf (name, "*%i", i+1);
3491                         loadmodel = Mod_FindName (name);
3492                         *loadmodel = *mod;
3493                         strcpy (loadmodel->name, name);
3494                         // textures and memory belong to the main model
3495                         loadmodel->texturepool = NULL;
3496                         loadmodel->mempool = NULL;
3497                         mod = loadmodel;
3498                 }
3499         }
3500
3501         loadmodel = originalloadmodel;
3502         Mod_ProcessLightList ();
3503 }
3504