]> de.git.xonotic.org Git - xonotic/darkplaces.git/blob - model_brush.c
got rid of R_Mesh_Draw and R_Mesh_DrawPolygon, now all 3D geometry is drawn using...
[xonotic/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
24
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
26
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_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         float           *vert;
1252         int                     i;
1253         int                     lindex;
1254         float           *vec;
1255
1256         // convert edges back to a normal polygon
1257         surf->poly_numverts = surf->numedges;
1258         vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1259         for (i = 0;i < surf->numedges;i++)
1260         {
1261                 lindex = loadmodel->surfedges[surf->firstedge + i];
1262                 if (lindex > 0)
1263                         vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1264                 else
1265                         vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1266                 VectorCopy (vec, vert);
1267                 vert += 3;
1268         }
1269 }
1270
1271 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1272 {
1273         int j, base, tricount, newvertexcount, *index, *vertexremap;
1274         surfmesh_t *newmesh, *oldmesh, *firstmesh;
1275         if (s->mesh->numtriangles > 1000)
1276         {
1277                 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1278                 base = 0;
1279                 oldmesh = NULL;
1280                 firstmesh = NULL;
1281                 newmesh = NULL;
1282                 while (base < s->mesh->numtriangles)
1283                 {
1284                         tricount = s->mesh->numtriangles - base;
1285                         if (tricount > 1000)
1286                                 tricount = 1000;
1287                         index = s->mesh->index + base * 3;
1288                         base += tricount;
1289
1290                         newvertexcount = 0;
1291                         memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1292                         for (j = 0;j < tricount * 3;j++)
1293                                 if (vertexremap[index[j]] < 0)
1294                                         vertexremap[index[j]] = newvertexcount++;
1295
1296                         newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1297                         newmesh->chain = NULL;
1298                         newmesh->numverts = newvertexcount;
1299                         newmesh->numtriangles = tricount;
1300                         newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1301                         newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1302                         for (j = 0;j < tricount * 3;j++)
1303                         {
1304                                 newmesh->index[j] = vertexremap[index[j]];
1305                                 // yes this copies the same vertex multiple times in many cases...  but that's ok...
1306                                 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1307                         }
1308                         if (oldmesh)
1309                                 oldmesh->chain = newmesh;
1310                         else
1311                                 firstmesh = newmesh;
1312                         oldmesh = newmesh;
1313                 }
1314                 Mem_Free(vertexremap);
1315                 Mem_Free(s->mesh);
1316                 s->mesh = firstmesh;
1317         }
1318 }
1319
1320 /*
1321 =================
1322 Mod_LoadFaces
1323 =================
1324 */
1325 static void Mod_LoadFaces (lump_t *l)
1326 {
1327         dface_t *in;
1328         msurface_t      *out;
1329         int i, count, surfnum, planenum, ssize, tsize;
1330
1331         in = (void *)(mod_base + l->fileofs);
1332         if (l->filelen % sizeof(*in))
1333                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1334         count = l->filelen / sizeof(*in);
1335         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1336
1337         loadmodel->surfaces = out;
1338         loadmodel->numsurfaces = count;
1339
1340         for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1341         {
1342                 // FIXME: validate edges, texinfo, etc?
1343                 out->firstedge = LittleLong(in->firstedge);
1344                 out->numedges = LittleShort(in->numedges);
1345                 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1346                         Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1347
1348                 i = LittleShort (in->texinfo);
1349                 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1350                         Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1351                 out->texinfo = loadmodel->texinfo + i;
1352                 out->flags = out->texinfo->texture->flags;
1353
1354                 planenum = LittleShort(in->planenum);
1355                 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1356                         Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1357
1358                 if (LittleShort(in->side))
1359                         out->flags |= SURF_PLANEBACK;
1360
1361                 out->plane = loadmodel->planes + planenum;
1362
1363                 // clear lightmap (filled in later)
1364                 out->lightmaptexture = NULL;
1365
1366                 // force lightmap upload on first time seeing the surface
1367                 out->cached_dlight = true;
1368                 out->cached_ambient = -1000;
1369                 out->cached_lightscalebit = -1000;
1370
1371                 CalcSurfaceExtents (out);
1372
1373                 ssize = (out->extents[0] >> 4) + 1;
1374                 tsize = (out->extents[1] >> 4) + 1;
1375
1376                 // lighting info
1377                 for (i = 0;i < MAXLIGHTMAPS;i++)
1378                         out->styles[i] = in->styles[i];
1379                 i = LittleLong(in->lightofs);
1380                 if (i == -1)
1381                         out->samples = NULL;
1382                 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1383                         out->samples = loadmodel->lightdata + i;
1384                 else // LordHavoc: white lighting (bsp version 29)
1385                         out->samples = loadmodel->lightdata + (i * 3);
1386
1387                 Mod_GenerateSurfacePolygon(out);
1388
1389                 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1390                 {
1391                         out->shader = &Cshader_sky;
1392                         out->samples = NULL;
1393                         Mod_GenerateVertexMesh (out);
1394                 }
1395                 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1396                 {
1397                         out->shader = &Cshader_water;
1398                         out->samples = NULL;
1399                         Mod_GenerateVertexMesh (out);
1400                 }
1401                 else
1402                 {
1403                         if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1404                                 out->flags |= SURF_CLIPSOLID;
1405                         if (out->texinfo->flags & TEX_SPECIAL)
1406                         {
1407                                 // qbsp couldn't find the texture for this surface, but it was either turb or sky...  assume turb
1408                                 out->shader = &Cshader_water;
1409                                 out->shader = &Cshader_water;
1410                                 out->samples = NULL;
1411                                 Mod_GenerateVertexMesh (out);
1412                         }
1413                         else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1414                         {
1415                                 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1416                                 out->shader = &Cshader_wall_fullbright;
1417                                 out->samples = NULL;
1418                                 Mod_GenerateVertexMesh(out);
1419                         }
1420                         else
1421                         {
1422                                 // stainmap for permanent marks on walls
1423                                 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1424                                 // clear to white
1425                                 memset(out->stainsamples, 255, ssize * tsize * 3);
1426                                 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1427                                 {
1428                                         out->shader = &Cshader_wall_vertex;
1429                                         Mod_GenerateVertexLitMesh(out);
1430                                 }
1431                                 else
1432                                 {
1433                                         out->shader = &Cshader_wall_lightmap;
1434                                         Mod_GenerateLightmappedMesh(out);
1435                                 }
1436                         }
1437                 }
1438                 Mod_SplitSurfMeshIfTooBig(out);
1439         }
1440 }
1441
1442 static model_t *sortmodel;
1443
1444 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1445 {
1446         const msurface_t *a, *b;
1447         a = *((const msurface_t **)voida);
1448         b = *((const msurface_t **)voidb);
1449         if (a->shader != b->shader)
1450                 return (qbyte *) a->shader - (qbyte *) b->shader;
1451         if (a->texinfo->texture != b->texinfo->texture);
1452                 return a->texinfo->texture - b->texinfo->texture;
1453         return 0;
1454 }
1455
1456 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1457 {
1458         int surfnum;
1459         sortmodel = model;
1460         sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1461         for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1462                 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1463
1464         if (r_sortsurfaces.integer)
1465                 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1466 }
1467
1468
1469 /*
1470 =================
1471 Mod_SetParent
1472 =================
1473 */
1474 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1475 {
1476         node->parent = parent;
1477         if (node->contents < 0)
1478                 return;
1479         Mod_SetParent (node->children[0], node);
1480         Mod_SetParent (node->children[1], node);
1481 }
1482
1483 /*
1484 =================
1485 Mod_LoadNodes
1486 =================
1487 */
1488 static void Mod_LoadNodes (lump_t *l)
1489 {
1490         int                     i, j, count, p;
1491         dnode_t         *in;
1492         mnode_t         *out;
1493
1494         in = (void *)(mod_base + l->fileofs);
1495         if (l->filelen % sizeof(*in))
1496                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1497         count = l->filelen / sizeof(*in);
1498         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1499
1500         loadmodel->nodes = out;
1501         loadmodel->numnodes = count;
1502
1503         for ( i=0 ; i<count ; i++, in++, out++)
1504         {
1505                 for (j=0 ; j<3 ; j++)
1506                 {
1507                         out->mins[j] = LittleShort (in->mins[j]);
1508                         out->maxs[j] = LittleShort (in->maxs[j]);
1509                 }
1510
1511                 p = LittleLong(in->planenum);
1512                 out->plane = loadmodel->planes + p;
1513
1514                 out->firstsurface = LittleShort (in->firstface);
1515                 out->numsurfaces = LittleShort (in->numfaces);
1516
1517                 for (j=0 ; j<2 ; j++)
1518                 {
1519                         p = LittleShort (in->children[j]);
1520                         if (p >= 0)
1521                                 out->children[j] = loadmodel->nodes + p;
1522                         else
1523                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1524                 }
1525         }
1526
1527         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1528 }
1529
1530 /*
1531 =================
1532 Mod_LoadLeafs
1533 =================
1534 */
1535 static void Mod_LoadLeafs (lump_t *l)
1536 {
1537         dleaf_t         *in;
1538         mleaf_t         *out;
1539         int                     i, j, count, p;
1540
1541         in = (void *)(mod_base + l->fileofs);
1542         if (l->filelen % sizeof(*in))
1543                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1544         count = l->filelen / sizeof(*in);
1545         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1546
1547         loadmodel->leafs = out;
1548         loadmodel->numleafs = count;
1549
1550         for ( i=0 ; i<count ; i++, in++, out++)
1551         {
1552                 for (j=0 ; j<3 ; j++)
1553                 {
1554                         out->mins[j] = LittleShort (in->mins[j]);
1555                         out->maxs[j] = LittleShort (in->maxs[j]);
1556                 }
1557
1558                 p = LittleLong(in->contents);
1559                 out->contents = p;
1560
1561                 out->firstmarksurface = loadmodel->marksurfaces +
1562                         LittleShort(in->firstmarksurface);
1563                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1564
1565                 p = LittleLong(in->visofs);
1566                 if (p == -1)
1567                         out->compressed_vis = NULL;
1568                 else
1569                         out->compressed_vis = loadmodel->visdata + p;
1570
1571                 for (j=0 ; j<4 ; j++)
1572                         out->ambient_sound_level[j] = in->ambient_level[j];
1573
1574                 // FIXME: Insert caustics here
1575         }
1576 }
1577
1578 /*
1579 =================
1580 Mod_LoadClipnodes
1581 =================
1582 */
1583 static void Mod_LoadClipnodes (lump_t *l)
1584 {
1585         dclipnode_t *in, *out;
1586         int                     i, count;
1587         hull_t          *hull;
1588
1589         in = (void *)(mod_base + l->fileofs);
1590         if (l->filelen % sizeof(*in))
1591                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1592         count = l->filelen / sizeof(*in);
1593         out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1594
1595         loadmodel->clipnodes = out;
1596         loadmodel->numclipnodes = count;
1597
1598         if (loadmodel->ishlbsp)
1599         {
1600                 hull = &loadmodel->hulls[1];
1601                 hull->clipnodes = out;
1602                 hull->firstclipnode = 0;
1603                 hull->lastclipnode = count-1;
1604                 hull->planes = loadmodel->planes;
1605                 hull->clip_mins[0] = -16;
1606                 hull->clip_mins[1] = -16;
1607                 hull->clip_mins[2] = -36;
1608                 hull->clip_maxs[0] = 16;
1609                 hull->clip_maxs[1] = 16;
1610                 hull->clip_maxs[2] = 36;
1611                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1612
1613                 hull = &loadmodel->hulls[2];
1614                 hull->clipnodes = out;
1615                 hull->firstclipnode = 0;
1616                 hull->lastclipnode = count-1;
1617                 hull->planes = loadmodel->planes;
1618                 hull->clip_mins[0] = -32;
1619                 hull->clip_mins[1] = -32;
1620                 hull->clip_mins[2] = -32;
1621                 hull->clip_maxs[0] = 32;
1622                 hull->clip_maxs[1] = 32;
1623                 hull->clip_maxs[2] = 32;
1624                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1625
1626                 hull = &loadmodel->hulls[3];
1627                 hull->clipnodes = out;
1628                 hull->firstclipnode = 0;
1629                 hull->lastclipnode = count-1;
1630                 hull->planes = loadmodel->planes;
1631                 hull->clip_mins[0] = -16;
1632                 hull->clip_mins[1] = -16;
1633                 hull->clip_mins[2] = -18;
1634                 hull->clip_maxs[0] = 16;
1635                 hull->clip_maxs[1] = 16;
1636                 hull->clip_maxs[2] = 18;
1637                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1638         }
1639         else
1640         {
1641                 hull = &loadmodel->hulls[1];
1642                 hull->clipnodes = out;
1643                 hull->firstclipnode = 0;
1644                 hull->lastclipnode = count-1;
1645                 hull->planes = loadmodel->planes;
1646                 hull->clip_mins[0] = -16;
1647                 hull->clip_mins[1] = -16;
1648                 hull->clip_mins[2] = -24;
1649                 hull->clip_maxs[0] = 16;
1650                 hull->clip_maxs[1] = 16;
1651                 hull->clip_maxs[2] = 32;
1652                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1653
1654                 hull = &loadmodel->hulls[2];
1655                 hull->clipnodes = out;
1656                 hull->firstclipnode = 0;
1657                 hull->lastclipnode = count-1;
1658                 hull->planes = loadmodel->planes;
1659                 hull->clip_mins[0] = -32;
1660                 hull->clip_mins[1] = -32;
1661                 hull->clip_mins[2] = -24;
1662                 hull->clip_maxs[0] = 32;
1663                 hull->clip_maxs[1] = 32;
1664                 hull->clip_maxs[2] = 64;
1665                 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1666         }
1667
1668         for (i=0 ; i<count ; i++, out++, in++)
1669         {
1670                 out->planenum = LittleLong(in->planenum);
1671                 out->children[0] = LittleShort(in->children[0]);
1672                 out->children[1] = LittleShort(in->children[1]);
1673                 if (out->children[0] >= count || out->children[1] >= count)
1674                         Host_Error("Corrupt clipping hull (out of range child)\n");
1675         }
1676 }
1677
1678 /*
1679 =================
1680 Mod_MakeHull0
1681
1682 Duplicate the drawing hull structure as a clipping hull
1683 =================
1684 */
1685 static void Mod_MakeHull0 (void)
1686 {
1687         mnode_t         *in;
1688         dclipnode_t *out;
1689         int                     i;
1690         hull_t          *hull;
1691
1692         hull = &loadmodel->hulls[0];
1693
1694         in = loadmodel->nodes;
1695         out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1696
1697         hull->clipnodes = out;
1698         hull->firstclipnode = 0;
1699         hull->lastclipnode = loadmodel->numnodes - 1;
1700         hull->planes = loadmodel->planes;
1701
1702         for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1703         {
1704                 out->planenum = in->plane - loadmodel->planes;
1705                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1706                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1707         }
1708 }
1709
1710 /*
1711 =================
1712 Mod_LoadMarksurfaces
1713 =================
1714 */
1715 static void Mod_LoadMarksurfaces (lump_t *l)
1716 {
1717         int             i, j;
1718         short   *in;
1719
1720         in = (void *)(mod_base + l->fileofs);
1721         if (l->filelen % sizeof(*in))
1722                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1723         loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1724         loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1725
1726         for (i = 0;i < loadmodel->nummarksurfaces;i++)
1727         {
1728                 j = (unsigned) LittleShort(in[i]);
1729                 if (j >= loadmodel->numsurfaces)
1730                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1731                 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1732         }
1733 }
1734
1735 /*
1736 =================
1737 Mod_LoadSurfedges
1738 =================
1739 */
1740 static void Mod_LoadSurfedges (lump_t *l)
1741 {
1742         int             i;
1743         int             *in;
1744
1745         in = (void *)(mod_base + l->fileofs);
1746         if (l->filelen % sizeof(*in))
1747                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1748         loadmodel->numsurfedges = l->filelen / sizeof(*in);
1749         loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1750
1751         for (i = 0;i < loadmodel->numsurfedges;i++)
1752                 loadmodel->surfedges[i] = LittleLong (in[i]);
1753 }
1754
1755
1756 /*
1757 =================
1758 Mod_LoadPlanes
1759 =================
1760 */
1761 static void Mod_LoadPlanes (lump_t *l)
1762 {
1763         int                     i;
1764         mplane_t        *out;
1765         dplane_t        *in;
1766
1767         in = (void *)(mod_base + l->fileofs);
1768         if (l->filelen % sizeof(*in))
1769                 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1770
1771         loadmodel->numplanes = l->filelen / sizeof(*in);
1772         loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1773
1774         for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1775         {
1776                 out->normal[0] = LittleFloat (in->normal[0]);
1777                 out->normal[1] = LittleFloat (in->normal[1]);
1778                 out->normal[2] = LittleFloat (in->normal[2]);
1779                 out->dist = LittleFloat (in->dist);
1780
1781                 PlaneClassify(out);
1782         }
1783 }
1784
1785 #define MAX_POINTS_ON_WINDING 64
1786
1787 typedef struct
1788 {
1789         int numpoints;
1790         int padding;
1791         double points[8][3]; // variable sized
1792 }
1793 winding_t;
1794
1795 /*
1796 ==================
1797 NewWinding
1798 ==================
1799 */
1800 static winding_t *NewWinding (int points)
1801 {
1802         winding_t *w;
1803         int size;
1804
1805         if (points > MAX_POINTS_ON_WINDING)
1806                 Sys_Error("NewWinding: too many points\n");
1807
1808         size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1809         w = Mem_Alloc(loadmodel->mempool, size);
1810         memset (w, 0, size);
1811
1812         return w;
1813 }
1814
1815 static void FreeWinding (winding_t *w)
1816 {
1817         Mem_Free(w);
1818 }
1819
1820 /*
1821 =================
1822 BaseWindingForPlane
1823 =================
1824 */
1825 static winding_t *BaseWindingForPlane (mplane_t *p)
1826 {
1827         double org[3], vright[3], vup[3], normal[3];
1828         winding_t *w;
1829
1830         VectorCopy(p->normal, normal);
1831         VectorVectorsDouble(normal, vright, vup);
1832
1833         VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1834         VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1835
1836         // project a really big axis aligned box onto the plane
1837         w = NewWinding (4);
1838
1839         VectorScale (p->normal, p->dist, org);
1840
1841         VectorSubtract (org, vright, w->points[0]);
1842         VectorAdd (w->points[0], vup, w->points[0]);
1843
1844         VectorAdd (org, vright, w->points[1]);
1845         VectorAdd (w->points[1], vup, w->points[1]);
1846
1847         VectorAdd (org, vright, w->points[2]);
1848         VectorSubtract (w->points[2], vup, w->points[2]);
1849
1850         VectorSubtract (org, vright, w->points[3]);
1851         VectorSubtract (w->points[3], vup, w->points[3]);
1852
1853         w->numpoints = 4;
1854
1855         return w;
1856 }
1857
1858 /*
1859 ==================
1860 ClipWinding
1861
1862 Clips the winding to the plane, returning the new winding on the positive side
1863 Frees the input winding.
1864 If keepon is true, an exactly on-plane winding will be saved, otherwise
1865 it will be clipped away.
1866 ==================
1867 */
1868 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1869 {
1870         double  dists[MAX_POINTS_ON_WINDING + 1];
1871         int             sides[MAX_POINTS_ON_WINDING + 1];
1872         int             counts[3];
1873         double  dot;
1874         int             i, j;
1875         double  *p1, *p2;
1876         double  mid[3];
1877         winding_t       *neww;
1878         int             maxpts;
1879
1880         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1881
1882         // determine sides for each point
1883         for (i = 0;i < in->numpoints;i++)
1884         {
1885                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1886                 if (dot > ON_EPSILON)
1887                         sides[i] = SIDE_FRONT;
1888                 else if (dot < -ON_EPSILON)
1889                         sides[i] = SIDE_BACK;
1890                 else
1891                         sides[i] = SIDE_ON;
1892                 counts[sides[i]]++;
1893         }
1894         sides[i] = sides[0];
1895         dists[i] = dists[0];
1896
1897         if (keepon && !counts[0] && !counts[1])
1898                 return in;
1899
1900         if (!counts[0])
1901         {
1902                 FreeWinding (in);
1903                 return NULL;
1904         }
1905         if (!counts[1])
1906                 return in;
1907
1908         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1909         if (maxpts > MAX_POINTS_ON_WINDING)
1910                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1911
1912         neww = NewWinding (maxpts);
1913
1914         for (i = 0;i < in->numpoints;i++)
1915         {
1916                 if (neww->numpoints >= maxpts)
1917                         Sys_Error ("ClipWinding: points exceeded estimate");
1918
1919                 p1 = in->points[i];
1920
1921                 if (sides[i] == SIDE_ON)
1922                 {
1923                         VectorCopy (p1, neww->points[neww->numpoints]);
1924                         neww->numpoints++;
1925                         continue;
1926                 }
1927
1928                 if (sides[i] == SIDE_FRONT)
1929                 {
1930                         VectorCopy (p1, neww->points[neww->numpoints]);
1931                         neww->numpoints++;
1932                 }
1933
1934                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1935                         continue;
1936
1937                 // generate a split point
1938                 p2 = in->points[(i+1)%in->numpoints];
1939
1940                 dot = dists[i] / (dists[i]-dists[i+1]);
1941                 for (j = 0;j < 3;j++)
1942                 {       // avoid round off error when possible
1943                         if (split->normal[j] == 1)
1944                                 mid[j] = split->dist;
1945                         else if (split->normal[j] == -1)
1946                                 mid[j] = -split->dist;
1947                         else
1948                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1949                 }
1950
1951                 VectorCopy (mid, neww->points[neww->numpoints]);
1952                 neww->numpoints++;
1953         }
1954
1955         // free the original winding
1956         FreeWinding (in);
1957
1958         return neww;
1959 }
1960
1961
1962 /*
1963 ==================
1964 DivideWinding
1965
1966 Divides a winding by a plane, producing one or two windings.  The
1967 original winding is not damaged or freed.  If only on one side, the
1968 returned winding will be the input winding.  If on both sides, two
1969 new windings will be created.
1970 ==================
1971 */
1972 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1973 {
1974         double  dists[MAX_POINTS_ON_WINDING + 1];
1975         int             sides[MAX_POINTS_ON_WINDING + 1];
1976         int             counts[3];
1977         double  dot;
1978         int             i, j;
1979         double  *p1, *p2;
1980         double  mid[3];
1981         winding_t       *f, *b;
1982         int             maxpts;
1983
1984         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1985
1986         // determine sides for each point
1987         for (i = 0;i < in->numpoints;i++)
1988         {
1989                 dot = DotProduct (in->points[i], split->normal);
1990                 dot -= split->dist;
1991                 dists[i] = dot;
1992                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1993                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1994                 else sides[i] = SIDE_ON;
1995                 counts[sides[i]]++;
1996         }
1997         sides[i] = sides[0];
1998         dists[i] = dists[0];
1999
2000         *front = *back = NULL;
2001
2002         if (!counts[0])
2003         {
2004                 *back = in;
2005                 return;
2006         }
2007         if (!counts[1])
2008         {
2009                 *front = in;
2010                 return;
2011         }
2012
2013         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
2014
2015         if (maxpts > MAX_POINTS_ON_WINDING)
2016                 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2017
2018         *front = f = NewWinding (maxpts);
2019         *back = b = NewWinding (maxpts);
2020
2021         for (i = 0;i < in->numpoints;i++)
2022         {
2023                 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2024                         Sys_Error ("DivideWinding: points exceeded estimate");
2025
2026                 p1 = in->points[i];
2027
2028                 if (sides[i] == SIDE_ON)
2029                 {
2030                         VectorCopy (p1, f->points[f->numpoints]);
2031                         f->numpoints++;
2032                         VectorCopy (p1, b->points[b->numpoints]);
2033                         b->numpoints++;
2034                         continue;
2035                 }
2036
2037                 if (sides[i] == SIDE_FRONT)
2038                 {
2039                         VectorCopy (p1, f->points[f->numpoints]);
2040                         f->numpoints++;
2041                 }
2042                 else if (sides[i] == SIDE_BACK)
2043                 {
2044                         VectorCopy (p1, b->points[b->numpoints]);
2045                         b->numpoints++;
2046                 }
2047
2048                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2049                         continue;
2050
2051                 // generate a split point
2052                 p2 = in->points[(i+1)%in->numpoints];
2053
2054                 dot = dists[i] / (dists[i]-dists[i+1]);
2055                 for (j = 0;j < 3;j++)
2056                 {       // avoid round off error when possible
2057                         if (split->normal[j] == 1)
2058                                 mid[j] = split->dist;
2059                         else if (split->normal[j] == -1)
2060                                 mid[j] = -split->dist;
2061                         else
2062                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2063                 }
2064
2065                 VectorCopy (mid, f->points[f->numpoints]);
2066                 f->numpoints++;
2067                 VectorCopy (mid, b->points[b->numpoints]);
2068                 b->numpoints++;
2069         }
2070 }
2071
2072 typedef struct portal_s
2073 {
2074         mplane_t plane;
2075         mnode_t *nodes[2];              // [0] = front side of plane
2076         struct portal_s *next[2];
2077         winding_t *winding;
2078         struct portal_s *chain; // all portals are linked into a list
2079 }
2080 portal_t;
2081
2082 static portal_t *portalchain;
2083
2084 /*
2085 ===========
2086 AllocPortal
2087 ===========
2088 */
2089 static portal_t *AllocPortal (void)
2090 {
2091         portal_t *p;
2092         p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2093         p->chain = portalchain;
2094         portalchain = p;
2095         return p;
2096 }
2097
2098 static void FreePortal(portal_t *p)
2099 {
2100         Mem_Free(p);
2101 }
2102
2103 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2104 {
2105         // calculate children first
2106         if (node->children[0]->contents >= 0)
2107                 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2108         if (node->children[1]->contents >= 0)
2109                 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2110
2111         // make combined bounding box from children
2112         node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2113         node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2114         node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2115         node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2116         node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2117         node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2118 }
2119
2120 static void Mod_FinalizePortals(void)
2121 {
2122         int i, j, numportals, numpoints;
2123         portal_t *p, *pnext;
2124         mportal_t *portal;
2125         mvertex_t *point;
2126         mleaf_t *leaf, *endleaf;
2127         winding_t *w;
2128
2129         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2130         leaf = loadmodel->leafs;
2131         endleaf = leaf + loadmodel->numleafs;
2132         for (;leaf < endleaf;leaf++)
2133         {
2134                 VectorSet(leaf->mins,  2000000000,  2000000000,  2000000000);
2135                 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2136         }
2137         p = portalchain;
2138         while(p)
2139         {
2140                 if (p->winding)
2141                 {
2142                         for (i = 0;i < 2;i++)
2143                         {
2144                                 leaf = (mleaf_t *)p->nodes[i];
2145                                 w = p->winding;
2146                                 for (j = 0;j < w->numpoints;j++)
2147                                 {
2148                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2149                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2150                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2151                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2152                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2153                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2154                                 }
2155                         }
2156                 }
2157                 p = p->chain;
2158         }
2159
2160         Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2161
2162         // tally up portal and point counts
2163         p = portalchain;
2164         numportals = 0;
2165         numpoints = 0;
2166         while(p)
2167         {
2168                 // note: this check must match the one below or it will usually corrupt memory
2169                 // 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
2170                 if (p->winding && p->nodes[0] != p->nodes[1]
2171                  && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2172                  && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2173                 {
2174                         numportals += 2;
2175                         numpoints += p->winding->numpoints * 2;
2176                 }
2177                 p = p->chain;
2178         }
2179         loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2180         loadmodel->numportals = numportals;
2181         loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2182         loadmodel->numportalpoints = numpoints;
2183         // clear all leaf portal chains
2184         for (i = 0;i < loadmodel->numleafs;i++)
2185                 loadmodel->leafs[i].portals = NULL;
2186         // process all portals in the global portal chain, while freeing them
2187         portal = loadmodel->portals;
2188         point = loadmodel->portalpoints;
2189         p = portalchain;
2190         portalchain = NULL;
2191         while (p)
2192         {
2193                 pnext = p->chain;
2194
2195                 if (p->winding)
2196                 {
2197                         // note: this check must match the one above or it will usually corrupt memory
2198                         // 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
2199                         if (p->nodes[0] != p->nodes[1]
2200                          && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2201                          && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2202                         {
2203                                 // first make the back to front portal (forward portal)
2204                                 portal->points = point;
2205                                 portal->numpoints = p->winding->numpoints;
2206                                 portal->plane.dist = p->plane.dist;
2207                                 VectorCopy(p->plane.normal, portal->plane.normal);
2208                                 portal->here = (mleaf_t *)p->nodes[1];
2209                                 portal->past = (mleaf_t *)p->nodes[0];
2210                                 // copy points
2211                                 for (j = 0;j < portal->numpoints;j++)
2212                                 {
2213                                         VectorCopy(p->winding->points[j], point->position);
2214                                         point++;
2215                                 }
2216                                 PlaneClassify(&portal->plane);
2217
2218                                 // link into leaf's portal chain
2219                                 portal->next = portal->here->portals;
2220                                 portal->here->portals = portal;
2221
2222                                 // advance to next portal
2223                                 portal++;
2224
2225                                 // then make the front to back portal (backward portal)
2226                                 portal->points = point;
2227                                 portal->numpoints = p->winding->numpoints;
2228                                 portal->plane.dist = -p->plane.dist;
2229                                 VectorNegate(p->plane.normal, portal->plane.normal);
2230                                 portal->here = (mleaf_t *)p->nodes[0];
2231                                 portal->past = (mleaf_t *)p->nodes[1];
2232                                 // copy points
2233                                 for (j = portal->numpoints - 1;j >= 0;j--)
2234                                 {
2235                                         VectorCopy(p->winding->points[j], point->position);
2236                                         point++;
2237                                 }
2238                                 PlaneClassify(&portal->plane);
2239
2240                                 // link into leaf's portal chain
2241                                 portal->next = portal->here->portals;
2242                                 portal->here->portals = portal;
2243
2244                                 // advance to next portal
2245                                 portal++;
2246                         }
2247                         FreeWinding(p->winding);
2248                 }
2249                 FreePortal(p);
2250                 p = pnext;
2251         }
2252 }
2253
2254 /*
2255 =============
2256 AddPortalToNodes
2257 =============
2258 */
2259 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2260 {
2261         if (!front)
2262                 Host_Error ("AddPortalToNodes: NULL front node");
2263         if (!back)
2264                 Host_Error ("AddPortalToNodes: NULL back node");
2265         if (p->nodes[0] || p->nodes[1])
2266                 Host_Error ("AddPortalToNodes: already included");
2267         // 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
2268
2269         p->nodes[0] = front;
2270         p->next[0] = (portal_t *)front->portals;
2271         front->portals = (mportal_t *)p;
2272
2273         p->nodes[1] = back;
2274         p->next[1] = (portal_t *)back->portals;
2275         back->portals = (mportal_t *)p;
2276 }
2277
2278 /*
2279 =============
2280 RemovePortalFromNode
2281 =============
2282 */
2283 static void RemovePortalFromNodes(portal_t *portal)
2284 {
2285         int i;
2286         mnode_t *node;
2287         void **portalpointer;
2288         portal_t *t;
2289         for (i = 0;i < 2;i++)
2290         {
2291                 node = portal->nodes[i];
2292
2293                 portalpointer = (void **) &node->portals;
2294                 while (1)
2295                 {
2296                         t = *portalpointer;
2297                         if (!t)
2298                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2299
2300                         if (t == portal)
2301                         {
2302                                 if (portal->nodes[0] == node)
2303                                 {
2304                                         *portalpointer = portal->next[0];
2305                                         portal->nodes[0] = NULL;
2306                                 }
2307                                 else if (portal->nodes[1] == node)
2308                                 {
2309                                         *portalpointer = portal->next[1];
2310                                         portal->nodes[1] = NULL;
2311                                 }
2312                                 else
2313                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2314                                 break;
2315                         }
2316
2317                         if (t->nodes[0] == node)
2318                                 portalpointer = (void **) &t->next[0];
2319                         else if (t->nodes[1] == node)
2320                                 portalpointer = (void **) &t->next[1];
2321                         else
2322                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2323                 }
2324         }
2325 }
2326
2327 static void Mod_RecursiveNodePortals (mnode_t *node)
2328 {
2329         int side;
2330         mnode_t *front, *back, *other_node;
2331         mplane_t clipplane, *plane;
2332         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2333         winding_t *nodeportalwinding, *frontwinding, *backwinding;
2334
2335         // if a leaf, we're done
2336         if (node->contents)
2337                 return;
2338
2339         plane = node->plane;
2340
2341         front = node->children[0];
2342         back = node->children[1];
2343         if (front == back)
2344                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2345
2346         // create the new portal by generating a polygon for the node plane,
2347         // and clipping it by all of the other portals (which came from nodes above this one)
2348         nodeportal = AllocPortal ();
2349         nodeportal->plane = *node->plane;
2350
2351         nodeportalwinding = BaseWindingForPlane (node->plane);
2352         side = 0;       // shut up compiler warning
2353         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2354         {
2355                 clipplane = portal->plane;
2356                 if (portal->nodes[0] == portal->nodes[1])
2357                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2358                 if (portal->nodes[0] == node)
2359                         side = 0;
2360                 else if (portal->nodes[1] == node)
2361                 {
2362                         clipplane.dist = -clipplane.dist;
2363                         VectorNegate (clipplane.normal, clipplane.normal);
2364                         side = 1;
2365                 }
2366                 else
2367                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2368
2369                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2370                 if (!nodeportalwinding)
2371                 {
2372                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2373                         break;
2374                 }
2375         }
2376
2377         if (nodeportalwinding)
2378         {
2379                 // if the plane was not clipped on all sides, there was an error
2380                 nodeportal->winding = nodeportalwinding;
2381                 AddPortalToNodes (nodeportal, front, back);
2382         }
2383
2384         // split the portals of this node along this node's plane and assign them to the children of this node
2385         // (migrating the portals downward through the tree)
2386         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2387         {
2388                 if (portal->nodes[0] == portal->nodes[1])
2389                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2390                 if (portal->nodes[0] == node)
2391                         side = 0;
2392                 else if (portal->nodes[1] == node)
2393                         side = 1;
2394                 else
2395                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2396                 nextportal = portal->next[side];
2397
2398                 other_node = portal->nodes[!side];
2399                 RemovePortalFromNodes (portal);
2400
2401                 // cut the portal into two portals, one on each side of the node plane
2402                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2403
2404                 if (!frontwinding)
2405                 {
2406                         if (side == 0)
2407                                 AddPortalToNodes (portal, back, other_node);
2408                         else
2409                                 AddPortalToNodes (portal, other_node, back);
2410                         continue;
2411                 }
2412                 if (!backwinding)
2413                 {
2414                         if (side == 0)
2415                                 AddPortalToNodes (portal, front, other_node);
2416                         else
2417                                 AddPortalToNodes (portal, other_node, front);
2418                         continue;
2419                 }
2420
2421                 // the winding is split
2422                 splitportal = AllocPortal ();
2423                 temp = splitportal->chain;
2424                 *splitportal = *portal;
2425                 splitportal->chain = temp;
2426                 splitportal->winding = backwinding;
2427                 FreeWinding (portal->winding);
2428                 portal->winding = frontwinding;
2429
2430                 if (side == 0)
2431                 {
2432                         AddPortalToNodes (portal, front, other_node);
2433                         AddPortalToNodes (splitportal, back, other_node);
2434                 }
2435                 else
2436                 {
2437                         AddPortalToNodes (portal, other_node, front);
2438                         AddPortalToNodes (splitportal, other_node, back);
2439                 }
2440         }
2441
2442         Mod_RecursiveNodePortals(front);
2443         Mod_RecursiveNodePortals(back);
2444 }
2445
2446
2447 static void Mod_MakePortals(void)
2448 {
2449         portalchain = NULL;
2450         Mod_RecursiveNodePortals (loadmodel->nodes);
2451         Mod_FinalizePortals();
2452 }
2453
2454 /*
2455 =================
2456 Mod_LoadBrushModel
2457 =================
2458 */
2459 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2460 {
2461         int                     i, j;
2462         dheader_t       *header;
2463         dmodel_t        *bm;
2464         mempool_t       *mainmempool;
2465         char            *loadname;
2466
2467         mod->type = mod_brush;
2468
2469         header = (dheader_t *)buffer;
2470
2471         i = LittleLong (header->version);
2472         if (i != BSPVERSION && i != 30)
2473                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2474         mod->ishlbsp = i == 30;
2475         if (loadmodel->isworldmodel)
2476                 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2477
2478 // swap all the lumps
2479         mod_base = (qbyte *)header;
2480
2481         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2482                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2483
2484 // load into heap
2485
2486         // store which lightmap format to use
2487         mod->lightmaprgba = r_lightmaprgba.integer;
2488
2489         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2490         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2491         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2492         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2493         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2494         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2495         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2496         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2497         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2498         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2499         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2500         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2501         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2502         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2503         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2504
2505         Mod_MakeHull0 ();
2506         Mod_MakePortals();
2507
2508         mod->numframes = 2;             // regular and alternate animation
2509
2510         mainmempool = mod->mempool;
2511         loadname = mod->name;
2512
2513         Mod_LoadLightList ();
2514
2515 //
2516 // set up the submodels (FIXME: this is confusing)
2517 //
2518         for (i = 0;i < mod->numsubmodels;i++)
2519         {
2520                 int k, l;
2521                 float dist, modelyawradius, modelradius, *vec;
2522                 msurface_t *surf;
2523
2524                 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2525                 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2526                 modelyawradius = 0;
2527                 modelradius = 0;
2528
2529                 bm = &mod->submodels[i];
2530
2531                 mod->hulls[0].firstclipnode = bm->headnode[0];
2532                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2533                 {
2534                         mod->hulls[j].firstclipnode = bm->headnode[j];
2535                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2536                 }
2537
2538                 mod->firstmodelsurface = bm->firstface;
2539                 mod->nummodelsurfaces = bm->numfaces;
2540
2541                 mod->DrawSky = NULL;
2542                 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2543                 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2544                 {
2545                         // we only need to have a drawsky function if it is used (usually only on world model)
2546                         if (surf->shader == &Cshader_sky)
2547                                 mod->DrawSky = R_DrawBrushModelSky;
2548                         for (k = 0;k < surf->numedges;k++)
2549                         {
2550                                 l = mod->surfedges[k + surf->firstedge];
2551                                 if (l > 0)
2552                                         vec = mod->vertexes[mod->edges[l].v[0]].position;
2553                                 else
2554                                         vec = mod->vertexes[mod->edges[-l].v[1]].position;
2555                                 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2556                                 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2557                                 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2558                                 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2559                                 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2560                                 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2561                                 dist = vec[0]*vec[0]+vec[1]*vec[1];
2562                                 if (modelyawradius < dist)
2563                                         modelyawradius = dist;
2564                                 dist += vec[2]*vec[2];
2565                                 if (modelradius < dist)
2566                                         modelradius = dist;
2567                         }
2568                 }
2569                 modelyawradius = sqrt(modelyawradius);
2570                 modelradius = sqrt(modelradius);
2571                 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2572                 mod->yawmins[2] = mod->normalmins[2];
2573                 mod->yawmaxs[2] = mod->normalmaxs[2];
2574                 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2575                 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2576                 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2577                 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2578                 {
2579                         Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2580                         VectorClear(mod->normalmins);
2581                         VectorClear(mod->normalmaxs);
2582                         VectorClear(mod->yawmins);
2583                         VectorClear(mod->yawmaxs);
2584                         VectorClear(mod->rotatedmins);
2585                         VectorClear(mod->rotatedmaxs);
2586                 }
2587
2588                 mod->numleafs = bm->visleafs;
2589
2590                 mod->Draw = R_DrawBrushModelNormal;
2591                 mod->DrawShadow = NULL;
2592
2593                 Mod_BrushSortedSurfaces(mod, mainmempool);
2594
2595                 // LordHavoc: only register submodels if it is the world
2596                 // (prevents bsp models from replacing world submodels)
2597                 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2598                 {
2599                         char    name[10];
2600                         // duplicate the basic information
2601                         sprintf (name, "*%i", i+1);
2602                         loadmodel = Mod_FindName (name);
2603                         *loadmodel = *mod;
2604                         strcpy (loadmodel->name, name);
2605                         // textures and memory belong to the main model
2606                         loadmodel->texturepool = NULL;
2607                         loadmodel->mempool = NULL;
2608                         mod = loadmodel;
2609                 }
2610         }
2611 }
2612