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