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