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