cleaned up nearly all of the externs in .c files (moved to appropriate .h files)
[divverent/darkplaces.git] / model_brush.c
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
12
13 See the GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18
19 */
20
21 #include "quakedef.h"
22
23 byte    mod_novis[MAX_MAP_LEAFS/8];
24
25 qboolean        hlbsp; // LordHavoc: true if it is a HalfLife BSP file (version 30)
26
27 cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", true};
28 cvar_t halflifebsp = {"halflifebsp", "0"};
29 cvar_t r_novis = {"r_novis", "0"};
30
31 /*
32 ===============
33 Mod_BrushInit
34 ===============
35 */
36 void Mod_BrushInit (void)
37 {
38         Cvar_RegisterVariable (&gl_subdivide_size);
39         Cvar_RegisterVariable (&halflifebsp);
40         Cvar_RegisterVariable (&r_novis);
41         memset (mod_novis, 0xff, sizeof(mod_novis));
42 }
43
44 /*
45 ===============
46 Mod_PointInLeaf
47 ===============
48 */
49 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
50 {
51         mnode_t         *node;
52         
53 //      if (!model || !model->nodes)
54 //              Sys_Error ("Mod_PointInLeaf: bad model");
55
56         node = model->nodes;
57         do
58                 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
59         while (node->contents == 0);
60
61         return (mleaf_t *)node;
62 }
63 /*
64 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
65 {
66         mnode_t         *node;
67         float           d;
68         mplane_t        *plane;
69         
70         if (!model || !model->nodes)
71                 Sys_Error ("Mod_PointInLeaf: bad model");
72
73         node = model->nodes;
74         while (1)
75         {
76                 if (node->contents < 0)
77                         return (mleaf_t *)node;
78                 plane = node->plane;
79                 d = DotProduct (p,plane->normal) - plane->dist;
80                 if (d > 0)
81                         node = node->children[0];
82                 else
83                         node = node->children[1];
84         }
85         
86         return NULL;    // never reached
87 }
88 */
89
90 /*
91 ===================
92 Mod_DecompressVis
93 ===================
94 */
95 byte *Mod_DecompressVis (byte *in, model_t *model)
96 {
97         static byte     decompressed[MAX_MAP_LEAFS/8];
98         int             c;
99         byte    *out;
100         int             row;
101
102         row = (model->numleafs+7)>>3;
103         out = decompressed;
104
105         /*
106         if (!in)
107         {       // no vis info, so make all visible
108                 while (row)
109                 {
110                         *out++ = 0xff;
111                         row--;
112                 }
113                 return decompressed;            
114         }
115         */
116
117         do
118         {
119                 if (*in)
120                 {
121                         *out++ = *in++;
122                         continue;
123                 }
124         
125                 c = in[1];
126                 in += 2;
127                 while (c)
128                 {
129                         *out++ = 0;
130                         c--;
131                 }
132         } while (out - decompressed < row);
133         
134         return decompressed;
135 }
136
137 byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
138 {
139         if (r_novis.value || leaf == model->leafs || leaf->compressed_vis == NULL)
140                 return mod_novis;
141         return Mod_DecompressVis (leaf->compressed_vis, model);
142 }
143
144 rtexture_t *r_notexture;
145 texture_t r_notexture_mip;
146
147 void Mod_SetupNoTexture(void)
148 {
149         int             x, y;
150         byte    pix[16][16][4];
151
152         // create a simple checkerboard texture for the default
153         // LordHavoc: redesigned this to remove reliance on the palette and texture_t
154         for (y = 0;y < 16;y++)
155         {
156                 for (x = 0;x < 16;x++)
157                 {
158                         if ((y < 8) ^ (x < 8))
159                         {
160                                 pix[y][x][0] = 128;
161                                 pix[y][x][1] = 128;
162                                 pix[y][x][2] = 128;
163                                 pix[y][x][3] = 255;
164                         }
165                         else
166                         {
167                                 pix[y][x][0] = 64;
168                                 pix[y][x][1] = 64;
169                                 pix[y][x][2] = 64;
170                                 pix[y][x][3] = 255;
171                         }
172                 }
173         }
174
175         r_notexture = R_LoadTexture("notexture", 16, 16, &pix[0][0][0], TEXF_MIPMAP | TEXF_RGBA);
176
177         strcpy(r_notexture_mip.name, "notexture");
178         r_notexture_mip.width = 16;
179         r_notexture_mip.height = 16;
180         r_notexture_mip.transparent = false;
181         r_notexture_mip.texture = r_notexture;
182         r_notexture_mip.glowtexture = NULL;
183 }
184
185 /*
186 =================
187 Mod_LoadTextures
188 =================
189 */
190 void Mod_LoadTextures (lump_t *l)
191 {
192         int                             i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
193         miptex_t                *dmiptex;
194         texture_t               *tx, *tx2, *anims[10], *altanims[10];
195         dmiptexlump_t   *m;
196         byte                    *data, *mtdata;
197
198         if (!l->filelen)
199         {
200                 loadmodel->textures = NULL;
201                 return;
202         }
203
204         m = (dmiptexlump_t *)(mod_base + l->fileofs);
205         
206         m->nummiptex = LittleLong (m->nummiptex);
207         
208         loadmodel->numtextures = m->nummiptex;
209         loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), va("%s texture headers", loadname));
210
211         // just to work around bounds checking when debugging with it (array index out of bounds error thing)
212         dofs = m->dataofs;
213         for (i = 0;i < m->nummiptex;i++)
214         {
215                 dofs[i] = LittleLong(dofs[i]);
216                 if (dofs[i] == -1)
217                         continue;
218                 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
219                 mtwidth = LittleLong (dmiptex->width);
220                 mtheight = LittleLong (dmiptex->height);
221                 mtdata = NULL;
222                 j = LittleLong (dmiptex->offsets[0]);
223                 if (j)
224                 {
225                         // texture included
226                         if (j < 40 || j + mtwidth * mtheight > l->filelen)
227                                 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
228                         mtdata = (byte *)dmiptex + j;
229                 }
230                 
231                 if ((mtwidth & 15) || (mtheight & 15))
232                         Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
233                 // LordHavoc: rewriting the map texture loader for GLQuake
234                 tx = Hunk_AllocName (sizeof(texture_t), va("%s textures", loadname));
235                 loadmodel->textures[i] = tx;
236
237                 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
238                 for (j = 0;dmiptex->name[j] && j < 15;j++)
239                 {
240                         if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
241                                 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
242                         else
243                                 tx->name[j] = dmiptex->name[j];
244                 }
245                 for (;j < 16;j++)
246                         tx->name[j] = 0;
247
248                 if (!tx->name[0])
249                 {
250                         Con_Printf("warning: unnamed texture in %s\n", loadname);
251                         sprintf(tx->name, "unnamed%i", i);
252                 }
253
254                 tx->transparent = false;
255                 data = loadimagepixels(tx->name, false, 0, 0);
256                 if (data)
257                 {
258                         if (!hlbsp && !strncmp(tx->name,"sky",3) && image_width == 256 && image_height == 128) // LordHavoc: HL sky textures are entirely unrelated
259                         {
260                                 tx->width = 0;
261                                 tx->height = 0;
262                                 tx->transparent = false;
263                                 tx->texture = NULL;
264                                 tx->glowtexture = NULL;
265                                 R_InitSky (data, 4);
266                         }
267                         else
268                         {
269                                 tx->width = mtwidth;
270                                 tx->height = mtheight;
271                                 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
272                                 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
273                                 tx->glowtexture = NULL;
274                         }
275                         qfree(data);
276                 }
277                 else
278                 {
279                         if (hlbsp)
280                         {
281                                 if (mtdata) // texture included
282                                 {
283                                         data = W_ConvertWAD3Texture(dmiptex);
284                                         if (data)
285                                         {
286                                                 tx->width = mtwidth;
287                                                 tx->height = mtheight;
288                                                 tx->transparent = Image_CheckAlpha(data, mtwidth * mtheight, true);
289                                                 tx->texture = R_LoadTexture (tx->name, mtwidth, mtheight, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
290                                                 tx->glowtexture = NULL;
291                                                 qfree(data);
292                                         }
293                                 }
294                                 if (!data)
295                                 {
296                                         data = W_GetTexture(tx->name);
297                                         // get the size from the wad texture
298                                         if (data)
299                                         {
300                                                 tx->width = image_width;
301                                                 tx->height = image_height;
302                                                 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
303                                                 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
304                                                 tx->glowtexture = NULL;
305                                                 qfree(data);
306                                         }
307                                 }
308                                 if (!data)
309                                 {
310                                         tx->width = 16;
311                                         tx->height = 16;
312                                         tx->transparent = false;
313                                         tx->texture = r_notexture;
314                                         tx->glowtexture = NULL;
315                                 }
316                         }
317                         else
318                         {
319                                 if (!strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128)
320                                 {
321                                         tx->width = mtwidth;
322                                         tx->height = mtheight;
323                                         tx->transparent = false;
324                                         tx->texture = NULL;
325                                         tx->glowtexture = NULL;
326                                         R_InitSky (mtdata, 1);
327                                 }
328                                 else
329                                 {
330                                         if (mtdata) // texture included
331                                         {
332                                                 int fullbrights;
333                                                 data = mtdata;
334                                                 tx->width = mtwidth;
335                                                 tx->height = mtheight;
336                                                 tx->transparent = false;
337                                                 fullbrights = false;
338                                                 if (r_fullbrights.value && tx->name[0] != '*')
339                                                 {
340                                                         for (j = 0;j < tx->width*tx->height;j++)
341                                                         {
342                                                                 if (data[j] >= 224) // fullbright
343                                                                 {
344                                                                         fullbrights = true;
345                                                                         break;
346                                                                 }
347                                                         }
348                                                 }
349                                                 if (fullbrights)
350                                                 {
351                                                         char name[64];
352                                                         byte *data2;
353                                                         data2 = qmalloc(tx->width*tx->height);
354                                                         for (j = 0;j < tx->width*tx->height;j++)
355                                                                 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
356                                                         tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
357                                                         strcpy(name, tx->name);
358                                                         strcat(name, "_glow");
359                                                         for (j = 0;j < tx->width*tx->height;j++)
360                                                                 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
361                                                         tx->glowtexture = R_LoadTexture (name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
362                                                         qfree(data2);
363                                                 }
364                                                 else
365                                                 {
366                                                         tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data, TEXF_MIPMAP | TEXF_PRECACHE);
367                                                         tx->glowtexture = NULL;
368                                                 }
369                                         }
370                                         else // no texture, and no external replacement texture was found
371                                         {
372                                                 tx->width = 16;
373                                                 tx->height = 16;
374                                                 tx->transparent = false;
375                                                 tx->texture = r_notexture;
376                                                 tx->glowtexture = NULL;
377                                         }
378                                 }
379                         }
380                 }
381         }
382
383 //
384 // sequence the animations
385 //
386         for (i = 0;i < m->nummiptex;i++)
387         {
388                 tx = loadmodel->textures[i];
389                 if (!tx || tx->name[0] != '+')
390                         continue;
391                 if (tx->anim_total)
392                         continue;       // already sequenced
393
394                 // find the number of frames in the animation
395                 memset (anims, 0, sizeof(anims));
396                 memset (altanims, 0, sizeof(altanims));
397
398                 max = tx->name[1];
399                 altmax = 0;
400                 if (max >= '0' && max <= '9')
401                 {
402                         max -= '0';
403                         altmax = 0;
404                         anims[max] = tx;
405                         max++;
406                 }
407                 else if (max >= 'a' && max <= 'j')
408                 {
409                         altmax = max - 'a';
410                         max = 0;
411                         altanims[altmax] = tx;
412                         altmax++;
413                 }
414                 else
415                         Host_Error ("Bad animating texture %s", tx->name);
416
417                 for (j = i + 1;j < m->nummiptex;j++)
418                 {
419                         tx2 = loadmodel->textures[j];
420                         if (!tx2 || tx2->name[0] != '+')
421                                 continue;
422                         if (strcmp (tx2->name+2, tx->name+2))
423                                 continue;
424
425                         num = tx2->name[1];
426                         if (num >= '0' && num <= '9')
427                         {
428                                 num -= '0';
429                                 anims[num] = tx2;
430                                 if (num+1 > max)
431                                         max = num + 1;
432                         }
433                         else if (num >= 'a' && num <= 'j')
434                         {
435                                 num = num - 'a';
436                                 altanims[num] = tx2;
437                                 if (num+1 > altmax)
438                                         altmax = num+1;
439                         }
440                         else
441                                 Host_Error ("Bad animating texture %s", tx->name);
442                 }
443
444                 // link them all together
445                 for (j = 0;j < max;j++)
446                 {
447                         tx2 = anims[j];
448                         if (!tx2)
449                                 Host_Error ("Missing frame %i of %s", j, tx->name);
450                         tx2->anim_total = max;
451                         if (altmax)
452                                 tx2->alternate_anims = altanims[0];
453                         for (k = 0;k < 10;k++)
454                                 tx2->anim_frames[k] = anims[j];
455                 }
456                 for (j = 0;j < altmax;j++)
457                 {
458                         tx2 = altanims[j];
459                         if (!tx2)
460                                 Host_Error ("Missing frame %i of %s", j, tx->name);
461                         tx2->anim_total = altmax;
462                         if (max)
463                                 tx2->alternate_anims = anims[0];
464                         for (k = 0;k < 10;k++)
465                                 tx2->anim_frames[k] = altanims[j];
466                 }
467         }
468 }
469
470 /*
471 =================
472 Mod_LoadLighting
473 =================
474 */
475 void Mod_LoadLighting (lump_t *l)
476 {
477         int i;
478         byte *in, *out, *data;
479         byte d;
480         char litfilename[1024];
481         loadmodel->lightdata = NULL;
482         if (hlbsp) // LordHavoc: load the colored lighting data straight
483         {
484                 loadmodel->lightdata = Hunk_AllocName ( l->filelen, va("%s lightmaps", loadname));
485                 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
486         }
487         else // LordHavoc: bsp version 29 (normal white lighting)
488         {
489                 // LordHavoc: hope is not lost yet, check for a .lit file to load
490                 strcpy(litfilename, loadmodel->name);
491                 COM_StripExtension(litfilename, litfilename);
492                 strcat(litfilename, ".lit");
493                 data = (byte*) COM_LoadHunkFile (litfilename, false);
494                 if (data)
495                 {
496                         if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
497                         {
498                                 i = LittleLong(((int *)data)[1]);
499                                 if (i == 1)
500                                 {
501                                         Con_DPrintf("%s loaded", litfilename);
502                                         loadmodel->lightdata = data + 8;
503                                         return;
504                                 }
505                                 else
506                                         Con_Printf("Unknown .lit file version (%d)\n", i);
507                         }
508                         else
509                                 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
510                 }
511                 // LordHavoc: oh well, expand the white lighting data
512                 if (!l->filelen)
513                         return;
514                 loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, va("%s lightmaps", loadname));
515                 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
516                 out = loadmodel->lightdata;
517                 memcpy (in, mod_base + l->fileofs, l->filelen);
518                 for (i = 0;i < l->filelen;i++)
519                 {
520                         d = *in++;
521                         *out++ = d;
522                         *out++ = d;
523                         *out++ = d;
524                 }
525         }
526 }
527
528
529 /*
530 =================
531 Mod_LoadVisibility
532 =================
533 */
534 void Mod_LoadVisibility (lump_t *l)
535 {
536         if (!l->filelen)
537         {
538                 loadmodel->visdata = NULL;
539                 return;
540         }
541         loadmodel->visdata = Hunk_AllocName ( l->filelen, va("%s visdata", loadname));
542         memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
543 }
544
545 /*
546 =================
547 Mod_LoadEntities
548 =================
549 */
550 void Mod_LoadEntities (lump_t *l)
551 {
552         if (!l->filelen)
553         {
554                 loadmodel->entities = NULL;
555                 return;
556         }
557         loadmodel->entities = Hunk_AllocName ( l->filelen, va("%s entities", loadname));
558         memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
559
560         if (isworldmodel)
561                 CL_ParseEntityLump(loadmodel->entities);
562 }
563
564
565 /*
566 =================
567 Mod_LoadVertexes
568 =================
569 */
570 void Mod_LoadVertexes (lump_t *l)
571 {
572         dvertex_t       *in;
573         mvertex_t       *out;
574         int                     i, count;
575
576         in = (void *)(mod_base + l->fileofs);
577         if (l->filelen % sizeof(*in))
578                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
579         count = l->filelen / sizeof(*in);
580         out = Hunk_AllocName ( count*sizeof(*out), va("%s vertices", loadname));
581
582         loadmodel->vertexes = out;
583         loadmodel->numvertexes = count;
584
585         for ( i=0 ; i<count ; i++, in++, out++)
586         {
587                 out->position[0] = LittleFloat (in->point[0]);
588                 out->position[1] = LittleFloat (in->point[1]);
589                 out->position[2] = LittleFloat (in->point[2]);
590         }
591 }
592
593 /*
594 =================
595 Mod_LoadSubmodels
596 =================
597 */
598 void Mod_LoadSubmodels (lump_t *l)
599 {
600         dmodel_t        *in;
601         dmodel_t        *out;
602         int                     i, j, count;
603
604         in = (void *)(mod_base + l->fileofs);
605         if (l->filelen % sizeof(*in))
606                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
607         count = l->filelen / sizeof(*in);
608         out = Hunk_AllocName ( count*sizeof(*out), va("%s submodels", loadname));
609
610         loadmodel->submodels = out;
611         loadmodel->numsubmodels = count;
612
613         for ( i=0 ; i<count ; i++, in++, out++)
614         {
615                 for (j=0 ; j<3 ; j++)
616                 {       // spread the mins / maxs by a pixel
617                         out->mins[j] = LittleFloat (in->mins[j]) - 1;
618                         out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
619                         out->origin[j] = LittleFloat (in->origin[j]);
620                 }
621                 for (j=0 ; j<MAX_MAP_HULLS ; j++)
622                         out->headnode[j] = LittleLong (in->headnode[j]);
623                 out->visleafs = LittleLong (in->visleafs);
624                 out->firstface = LittleLong (in->firstface);
625                 out->numfaces = LittleLong (in->numfaces);
626         }
627 }
628
629 /*
630 =================
631 Mod_LoadEdges
632 =================
633 */
634 void Mod_LoadEdges (lump_t *l)
635 {
636         dedge_t *in;
637         medge_t *out;
638         int     i, count;
639
640         in = (void *)(mod_base + l->fileofs);
641         if (l->filelen % sizeof(*in))
642                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
643         count = l->filelen / sizeof(*in);
644         out = Hunk_AllocName ( (count + 1) * sizeof(*out), va("%s edges", loadname));
645
646         loadmodel->edges = out;
647         loadmodel->numedges = count;
648
649         for ( i=0 ; i<count ; i++, in++, out++)
650         {
651                 out->v[0] = (unsigned short)LittleShort(in->v[0]);
652                 out->v[1] = (unsigned short)LittleShort(in->v[1]);
653         }
654 }
655
656 /*
657 =================
658 Mod_LoadTexinfo
659 =================
660 */
661 void Mod_LoadTexinfo (lump_t *l)
662 {
663         texinfo_t *in;
664         mtexinfo_t *out;
665         int     i, j, k, count;
666         int             miptex;
667
668         in = (void *)(mod_base + l->fileofs);
669         if (l->filelen % sizeof(*in))
670                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
671         count = l->filelen / sizeof(*in);
672         out = Hunk_AllocName ( count*sizeof(*out), va("%s texinfo", loadname));
673
674         loadmodel->texinfo = out;
675         loadmodel->numtexinfo = count;
676
677         for ( i=0 ; i<count ; i++, in++, out++)
678         {
679                 for (k=0 ; k<2 ; k++)
680                         for (j=0 ; j<4 ; j++)
681                                 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
682
683                 miptex = LittleLong (in->miptex);
684                 out->flags = LittleLong (in->flags);
685         
686                 if (!loadmodel->textures)
687                 {
688                         out->texture = &r_notexture_mip;        // checkerboard texture
689                         out->flags = 0;
690                 }
691                 else
692                 {
693                         if (miptex >= loadmodel->numtextures)
694                                 Host_Error ("miptex >= loadmodel->numtextures");
695                         out->texture = loadmodel->textures[miptex];
696                         if (!out->texture)
697                         {
698                                 out->texture = &r_notexture_mip; // checkerboard texture
699                                 out->flags = 0;
700                         }
701                 }
702         }
703 }
704
705 /*
706 ================
707 CalcSurfaceExtents
708
709 Fills in s->texturemins[] and s->extents[]
710 ================
711 */
712 void CalcSurfaceExtents (msurface_t *s)
713 {
714         float   mins[2], maxs[2], val;
715         int             i,j, e;
716         mvertex_t       *v;
717         mtexinfo_t      *tex;
718         int             bmins[2], bmaxs[2];
719
720         mins[0] = mins[1] = 999999;
721         maxs[0] = maxs[1] = -99999;
722
723         tex = s->texinfo;
724         
725         for (i=0 ; i<s->numedges ; i++)
726         {
727                 e = loadmodel->surfedges[s->firstedge+i];
728                 if (e >= 0)
729                         v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
730                 else
731                         v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
732                 
733                 for (j=0 ; j<2 ; j++)
734                 {
735                         val = v->position[0] * tex->vecs[j][0] + 
736                                 v->position[1] * tex->vecs[j][1] +
737                                 v->position[2] * tex->vecs[j][2] +
738                                 tex->vecs[j][3];
739                         if (val < mins[j])
740                                 mins[j] = val;
741                         if (val > maxs[j])
742                                 maxs[j] = val;
743                 }
744         }
745
746         for (i=0 ; i<2 ; i++)
747         {       
748                 bmins[i] = floor(mins[i]/16);
749                 bmaxs[i] = ceil(maxs[i]/16);
750
751                 s->texturemins[i] = bmins[i] * 16;
752                 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
753 //              if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512)
754                 if ((tex->flags & TEX_SPECIAL) == 0 && (s->extents[i]+1) > (256*16))
755                         Host_Error ("Bad surface extents");
756         }
757 }
758
759 void GL_SubdivideSurface (msurface_t *fa);
760
761 /*
762 =================
763 Mod_LoadFaces
764 =================
765 */
766 void Mod_LoadFaces (lump_t *l)
767 {
768         dface_t         *in;
769         msurface_t      *out;
770         int                     i, count, surfnum;
771         int                     planenum, side;
772
773         in = (void *)(mod_base + l->fileofs);
774         if (l->filelen % sizeof(*in))
775                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
776         count = l->filelen / sizeof(*in);
777         out = Hunk_AllocName ( count*sizeof(*out), va("%s faces", loadname));
778
779         loadmodel->surfaces = out;
780         loadmodel->numsurfaces = count;
781
782         for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
783         {
784                 out->firstedge = LittleLong(in->firstedge);
785                 out->numedges = LittleShort(in->numedges);              
786                 out->flags = 0;
787
788                 planenum = LittleShort(in->planenum);
789                 side = LittleShort(in->side);
790                 if (side)
791                         out->flags |= SURF_PLANEBACK;                   
792
793                 out->plane = loadmodel->planes + planenum;
794
795                 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
796
797                 CalcSurfaceExtents (out);
798                                 
799         // lighting info
800
801                 for (i=0 ; i<MAXLIGHTMAPS ; i++)
802                         out->styles[i] = in->styles[i];
803                 i = LittleLong(in->lightofs);
804                 if (i == -1)
805                         out->samples = NULL;
806                 else if (hlbsp) // LordHavoc: HalfLife map (bsp version 30)
807                         out->samples = loadmodel->lightdata + i;
808                 else // LordHavoc: white lighting (bsp version 29)
809                         out->samples = loadmodel->lightdata + (i * 3); 
810                 
811         // set the drawing flags flag
812                 
813 //              if (!strncmp(out->texinfo->texture->name,"sky",3))      // sky
814                 // LordHavoc: faster check
815                 if ((out->texinfo->texture->name[0] == 's' || out->texinfo->texture->name[0] == 'S')
816                  && (out->texinfo->texture->name[1] == 'k' || out->texinfo->texture->name[1] == 'K')
817                  && (out->texinfo->texture->name[2] == 'y' || out->texinfo->texture->name[2] == 'Y'))
818                 {
819                         // LordHavoc: for consistency reasons, mark sky as fullbright and solid as well
820                         out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
821                         GL_SubdivideSurface (out);      // cut up polygon for warps
822                         continue;
823                 }
824                 
825 //              if (!strncmp(out->texinfo->texture->name,"*",1))                // turbulent
826                 if (out->texinfo->texture->name[0] == '*') // LordHavoc: faster check
827                 {
828                         out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED | SURF_LIGHTBOTHSIDES);
829                         // LordHavoc: some turbulent textures should be fullbright and solid
830                         if (!strncmp(out->texinfo->texture->name,"*lava",5)
831                          || !strncmp(out->texinfo->texture->name,"*teleport",9)
832                          || !strncmp(out->texinfo->texture->name,"*rift",5)) // Scourge of Armagon texture
833                                 out->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
834                         for (i=0 ; i<2 ; i++)
835                         {
836                                 out->extents[i] = 16384;
837                                 out->texturemins[i] = -8192;
838                         }
839                         GL_SubdivideSurface (out);      // cut up polygon for warps
840                         continue;
841                 }
842
843         }
844 }
845
846
847 /*
848 =================
849 Mod_SetParent
850 =================
851 */
852 void Mod_SetParent (mnode_t *node, mnode_t *parent)
853 {
854         node->parent = parent;
855         if (node->contents < 0)
856                 return;
857         Mod_SetParent (node->children[0], node);
858         Mod_SetParent (node->children[1], node);
859 }
860
861 /*
862 =================
863 Mod_LoadNodes
864 =================
865 */
866 void Mod_LoadNodes (lump_t *l)
867 {
868         int                     i, j, count, p;
869         dnode_t         *in;
870         mnode_t         *out;
871
872         in = (void *)(mod_base + l->fileofs);
873         if (l->filelen % sizeof(*in))
874                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
875         count = l->filelen / sizeof(*in);
876         out = Hunk_AllocName ( count*sizeof(*out), va("%s nodes", loadname));
877
878         loadmodel->nodes = out;
879         loadmodel->numnodes = count;
880
881         for ( i=0 ; i<count ; i++, in++, out++)
882         {
883 //              for (j=0 ; j<3 ; j++)
884 //              {
885 //                      out->mins[j] = LittleShort (in->mins[j]);
886 //                      out->maxs[j] = LittleShort (in->maxs[j]);
887 //              }
888         
889                 p = LittleLong(in->planenum);
890                 out->plane = loadmodel->planes + p;
891
892                 out->firstsurface = LittleShort (in->firstface);
893                 out->numsurfaces = LittleShort (in->numfaces);
894                 
895                 for (j=0 ; j<2 ; j++)
896                 {
897                         p = LittleShort (in->children[j]);
898                         if (p >= 0)
899                                 out->children[j] = loadmodel->nodes + p;
900                         else
901                                 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
902                 }
903         }
904         
905         Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
906 }
907
908 /*
909 =================
910 Mod_LoadLeafs
911 =================
912 */
913 void Mod_LoadLeafs (lump_t *l)
914 {
915         dleaf_t         *in;
916         mleaf_t         *out;
917         int                     i, j, count, p;
918
919         in = (void *)(mod_base + l->fileofs);
920         if (l->filelen % sizeof(*in))
921                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
922         count = l->filelen / sizeof(*in);
923         out = Hunk_AllocName ( count*sizeof(*out), va("%s leafs", loadname));
924
925         loadmodel->leafs = out;
926         loadmodel->numleafs = count;
927
928         for ( i=0 ; i<count ; i++, in++, out++)
929         {
930                 for (j=0 ; j<3 ; j++)
931                 {
932                         out->mins[j] = LittleShort (in->mins[j]);
933                         out->maxs[j] = LittleShort (in->maxs[j]);
934                 }
935
936                 p = LittleLong(in->contents);
937                 out->contents = p;
938
939                 out->firstmarksurface = loadmodel->marksurfaces +
940                         LittleShort(in->firstmarksurface);
941                 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
942                 
943                 p = LittleLong(in->visofs);
944                 if (p == -1)
945                         out->compressed_vis = NULL;
946                 else
947                         out->compressed_vis = loadmodel->visdata + p;
948                 
949                 for (j=0 ; j<4 ; j++)
950                         out->ambient_sound_level[j] = in->ambient_level[j];
951
952                 // gl underwater warp
953                 // LordHavoc: disabled underwater warping
954                 /*
955                 if (out->contents != CONTENTS_EMPTY)
956                 {
957                         for (j=0 ; j<out->nummarksurfaces ; j++)
958                                 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
959                 }
960                 */
961         }       
962 }
963
964 /*
965 =================
966 Mod_LoadClipnodes
967 =================
968 */
969 void Mod_LoadClipnodes (lump_t *l)
970 {
971         dclipnode_t *in, *out;
972         int                     i, count;
973         hull_t          *hull;
974
975         in = (void *)(mod_base + l->fileofs);
976         if (l->filelen % sizeof(*in))
977                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
978         count = l->filelen / sizeof(*in);
979         out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname));
980
981         loadmodel->clipnodes = out;
982         loadmodel->numclipnodes = count;
983
984         if (hlbsp)
985         {
986                 hull = &loadmodel->hulls[1];
987                 hull->clipnodes = out;
988                 hull->firstclipnode = 0;
989                 hull->lastclipnode = count-1;
990                 hull->planes = loadmodel->planes;
991                 hull->clip_mins[0] = -16;
992                 hull->clip_mins[1] = -16;
993                 hull->clip_mins[2] = -36;
994                 hull->clip_maxs[0] = 16;
995                 hull->clip_maxs[1] = 16;
996                 hull->clip_maxs[2] = 36;
997
998                 hull = &loadmodel->hulls[2];
999                 hull->clipnodes = out;
1000                 hull->firstclipnode = 0;
1001                 hull->lastclipnode = count-1;
1002                 hull->planes = loadmodel->planes;
1003                 hull->clip_mins[0] = -32;
1004                 hull->clip_mins[1] = -32;
1005                 hull->clip_mins[2] = -32;
1006                 hull->clip_maxs[0] = 32;
1007                 hull->clip_maxs[1] = 32;
1008                 hull->clip_maxs[2] = 32;
1009
1010                 hull = &loadmodel->hulls[3];
1011                 hull->clipnodes = out;
1012                 hull->firstclipnode = 0;
1013                 hull->lastclipnode = count-1;
1014                 hull->planes = loadmodel->planes;
1015                 hull->clip_mins[0] = -16;
1016                 hull->clip_mins[1] = -16;
1017                 hull->clip_mins[2] = -18;
1018                 hull->clip_maxs[0] = 16;
1019                 hull->clip_maxs[1] = 16;
1020                 hull->clip_maxs[2] = 18;
1021         }
1022         else
1023         {
1024                 hull = &loadmodel->hulls[1];
1025                 hull->clipnodes = out;
1026                 hull->firstclipnode = 0;
1027                 hull->lastclipnode = count-1;
1028                 hull->planes = loadmodel->planes;
1029                 hull->clip_mins[0] = -16;
1030                 hull->clip_mins[1] = -16;
1031                 hull->clip_mins[2] = -24;
1032                 hull->clip_maxs[0] = 16;
1033                 hull->clip_maxs[1] = 16;
1034                 hull->clip_maxs[2] = 32;
1035
1036                 hull = &loadmodel->hulls[2];
1037                 hull->clipnodes = out;
1038                 hull->firstclipnode = 0;
1039                 hull->lastclipnode = count-1;
1040                 hull->planes = loadmodel->planes;
1041                 hull->clip_mins[0] = -32;
1042                 hull->clip_mins[1] = -32;
1043                 hull->clip_mins[2] = -24;
1044                 hull->clip_maxs[0] = 32;
1045                 hull->clip_maxs[1] = 32;
1046                 hull->clip_maxs[2] = 64;
1047         }
1048
1049         for (i=0 ; i<count ; i++, out++, in++)
1050         {
1051                 out->planenum = LittleLong(in->planenum);
1052                 out->children[0] = LittleShort(in->children[0]);
1053                 out->children[1] = LittleShort(in->children[1]);
1054                 if (out->children[0] >= count || out->children[1] >= count)
1055                         Host_Error("Corrupt clipping hull (out of range child)\n");
1056         }
1057 }
1058
1059 /*
1060 =================
1061 Mod_MakeHull0
1062
1063 Duplicate the drawing hull structure as a clipping hull
1064 =================
1065 */
1066 void Mod_MakeHull0 (void)
1067 {
1068         mnode_t         *in;
1069         dclipnode_t *out;
1070         int                     i, count;
1071         hull_t          *hull;
1072         
1073         hull = &loadmodel->hulls[0];    
1074         
1075         in = loadmodel->nodes;
1076         count = loadmodel->numnodes;
1077         out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname));
1078
1079         hull->clipnodes = out;
1080         hull->firstclipnode = 0;
1081         hull->lastclipnode = count - 1;
1082         hull->planes = loadmodel->planes;
1083
1084         for (i = 0;i < count;i++, out++, in++)
1085         {
1086                 out->planenum = in->plane - loadmodel->planes;
1087                 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1088                 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1089         }
1090 }
1091
1092 /*
1093 =================
1094 Mod_LoadMarksurfaces
1095 =================
1096 */
1097 void Mod_LoadMarksurfaces (lump_t *l)
1098 {       
1099         int             i, j, count;
1100         short           *in;
1101         msurface_t **out;
1102         
1103         in = (void *)(mod_base + l->fileofs);
1104         if (l->filelen % sizeof(*in))
1105                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1106         count = l->filelen / sizeof(*in);
1107         out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname));
1108
1109         loadmodel->marksurfaces = out;
1110         loadmodel->nummarksurfaces = count;
1111
1112         for ( i=0 ; i<count ; i++)
1113         {
1114                 j = LittleShort(in[i]);
1115                 if (j >= loadmodel->numsurfaces)
1116                         Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1117                 out[i] = loadmodel->surfaces + j;
1118         }
1119 }
1120
1121 /*
1122 =================
1123 Mod_LoadSurfedges
1124 =================
1125 */
1126 void Mod_LoadSurfedges (lump_t *l)
1127 {       
1128         int             i, count;
1129         int             *in, *out;
1130         
1131         in = (void *)(mod_base + l->fileofs);
1132         if (l->filelen % sizeof(*in))
1133                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1134         count = l->filelen / sizeof(*in);
1135         out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname));
1136
1137         loadmodel->surfedges = out;
1138         loadmodel->numsurfedges = count;
1139
1140         for ( i=0 ; i<count ; i++)
1141                 out[i] = LittleLong (in[i]);
1142 }
1143
1144
1145 /*
1146 =================
1147 Mod_LoadPlanes
1148 =================
1149 */
1150 void Mod_LoadPlanes (lump_t *l)
1151 {
1152         int                     i, j;
1153         mplane_t        *out;
1154         dplane_t        *in;
1155         int                     count;
1156         int                     bits;
1157         
1158         in = (void *)(mod_base + l->fileofs);
1159         if (l->filelen % sizeof(*in))
1160                 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1161         count = l->filelen / sizeof(*in);
1162         out = Hunk_AllocName ( count*2*sizeof(*out), va("%s planes", loadname));
1163
1164         loadmodel->planes = out;
1165         loadmodel->numplanes = count;
1166
1167         for ( i=0 ; i<count ; i++, in++, out++)
1168         {
1169                 bits = 0;
1170                 for (j=0 ; j<3 ; j++)
1171                 {
1172                         out->normal[j] = LittleFloat (in->normal[j]);
1173 //                      if (out->normal[j] < 0)
1174 //                              bits |= 1<<j;
1175                 }
1176
1177                 out->dist = LittleFloat (in->dist);
1178                 out->type = LittleLong (in->type);
1179 //              out->signbits = bits;
1180                 BoxOnPlaneSideClassify(out);
1181         }
1182 }
1183
1184 #define MAX_POINTS_ON_WINDING 64
1185
1186 typedef struct
1187 {
1188         int numpoints;
1189         vec3_t points[8]; // variable sized
1190 }
1191 winding_t;
1192
1193 /*
1194 ==================
1195 NewWinding
1196 ==================
1197 */
1198 winding_t *NewWinding (int points)
1199 {
1200         winding_t *w;
1201         int size;
1202
1203         if (points > MAX_POINTS_ON_WINDING)
1204                 Host_Error("NewWinding: too many points\n");
1205
1206         size = (int)((winding_t *)0)->points[points];
1207         w = malloc (size);
1208         memset (w, 0, size);
1209
1210         return w;
1211 }
1212
1213 void FreeWinding (winding_t *w)
1214 {
1215         free (w);
1216 }
1217
1218 /*
1219 =================
1220 BaseWindingForPlane
1221 =================
1222 */
1223 winding_t *BaseWindingForPlane (mplane_t *p)
1224 {
1225         vec3_t  org, vright, vup;
1226         winding_t       *w;
1227
1228         VectorVectors(p->normal, vright, vup);
1229
1230         VectorScale (vup, 65536, vup);
1231         VectorScale (vright, 65536, vright);
1232
1233         // project a really big axis aligned box onto the plane
1234         w = NewWinding (4);
1235
1236         VectorScale (p->normal, p->dist, org);
1237
1238         VectorSubtract (org, vright, w->points[0]);
1239         VectorAdd (w->points[0], vup, w->points[0]);
1240
1241         VectorAdd (org, vright, w->points[1]);
1242         VectorAdd (w->points[1], vup, w->points[1]);
1243
1244         VectorAdd (org, vright, w->points[2]);
1245         VectorSubtract (w->points[2], vup, w->points[2]);
1246
1247         VectorSubtract (org, vright, w->points[3]);
1248         VectorSubtract (w->points[3], vup, w->points[3]);
1249
1250         w->numpoints = 4;
1251
1252         return w;       
1253 }
1254
1255 /*
1256 ==================
1257 ClipWinding
1258
1259 Clips the winding to the plane, returning the new winding on the positive side
1260 Frees the input winding.
1261 If keepon is true, an exactly on-plane winding will be saved, otherwise
1262 it will be clipped away.
1263 ==================
1264 */
1265 winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1266 {
1267         vec_t   dists[MAX_POINTS_ON_WINDING + 1];
1268         int             sides[MAX_POINTS_ON_WINDING + 1];
1269         int             counts[3];
1270         vec_t   dot;
1271         int             i, j;
1272         vec_t   *p1, *p2;
1273         vec3_t  mid;
1274         winding_t       *neww;
1275         int             maxpts;
1276
1277         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1278
1279         // determine sides for each point
1280         for (i = 0;i < in->numpoints;i++)
1281         {
1282                 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1283                 if (dot > ON_EPSILON)
1284                         sides[i] = SIDE_FRONT;
1285                 else if (dot < -ON_EPSILON)
1286                         sides[i] = SIDE_BACK;
1287                 else
1288                         sides[i] = SIDE_ON;
1289                 counts[sides[i]]++;
1290         }
1291         sides[i] = sides[0];
1292         dists[i] = dists[0];
1293
1294         if (keepon && !counts[0] && !counts[1])
1295                 return in;
1296
1297         if (!counts[0])
1298         {
1299                 FreeWinding (in);
1300                 return NULL;
1301         }
1302         if (!counts[1])
1303                 return in;
1304
1305         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1306         neww = NewWinding (maxpts);
1307
1308         for (i = 0;i < in->numpoints;i++)
1309         {
1310                 p1 = in->points[i];
1311
1312                 if (sides[i] == SIDE_ON)
1313                 {
1314                         VectorCopy (p1, neww->points[neww->numpoints]);
1315                         neww->numpoints++;
1316                         continue;
1317                 }
1318
1319                 if (sides[i] == SIDE_FRONT)
1320                 {
1321                         VectorCopy (p1, neww->points[neww->numpoints]);
1322                         neww->numpoints++;
1323                 }
1324
1325                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1326                         continue;
1327
1328                 // generate a split point
1329                 p2 = in->points[(i+1)%in->numpoints];
1330
1331                 dot = dists[i] / (dists[i]-dists[i+1]);
1332                 for (j = 0;j < 3;j++)
1333                 {       // avoid round off error when possible
1334                         if (split->normal[j] == 1)
1335                                 mid[j] = split->dist;
1336                         else if (split->normal[j] == -1)
1337                                 mid[j] = -split->dist;
1338                         else
1339                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1340                 }
1341
1342                 VectorCopy (mid, neww->points[neww->numpoints]);
1343                 neww->numpoints++;
1344         }
1345
1346         if (neww->numpoints > maxpts)
1347                 Host_Error ("ClipWinding: points exceeded estimate");
1348
1349         // free the original winding
1350         FreeWinding (in);
1351
1352         return neww;
1353 }
1354
1355
1356 /*
1357 ==================
1358 DivideWinding
1359
1360 Divides a winding by a plane, producing one or two windings.  The
1361 original winding is not damaged or freed.  If only on one side, the
1362 returned winding will be the input winding.  If on both sides, two
1363 new windings will be created.
1364 ==================
1365 */
1366 void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1367 {
1368         vec_t   dists[MAX_POINTS_ON_WINDING + 1];
1369         int             sides[MAX_POINTS_ON_WINDING + 1];
1370         int             counts[3];
1371         vec_t   dot;
1372         int             i, j;
1373         vec_t   *p1, *p2;
1374         vec3_t  mid;
1375         winding_t       *f, *b;
1376         int             maxpts;
1377
1378         counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1379
1380         // determine sides for each point
1381         for (i = 0;i < in->numpoints;i++)
1382         {
1383                 dot = DotProduct (in->points[i], split->normal);
1384                 dot -= split->dist;
1385                 dists[i] = dot;
1386                 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1387                 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1388                 else sides[i] = SIDE_ON;
1389                 counts[sides[i]]++;
1390         }
1391         sides[i] = sides[0];
1392         dists[i] = dists[0];
1393
1394         *front = *back = NULL;
1395
1396         if (!counts[0])
1397         {
1398                 *back = in;
1399                 return;
1400         }
1401         if (!counts[1])
1402         {
1403                 *front = in;
1404                 return;
1405         }
1406
1407         maxpts = in->numpoints+4;       // can't use counts[0]+2 because of fp grouping errors
1408
1409         *front = f = NewWinding (maxpts);
1410         *back = b = NewWinding (maxpts);
1411
1412         for (i = 0;i < in->numpoints;i++)
1413         {
1414                 p1 = in->points[i];
1415
1416                 if (sides[i] == SIDE_ON)
1417                 {
1418                         VectorCopy (p1, f->points[f->numpoints]);
1419                         f->numpoints++;
1420                         VectorCopy (p1, b->points[b->numpoints]);
1421                         b->numpoints++;
1422                         continue;
1423                 }
1424
1425                 if (sides[i] == SIDE_FRONT)
1426                 {
1427                         VectorCopy (p1, f->points[f->numpoints]);
1428                         f->numpoints++;
1429                 }
1430                 else if (sides[i] == SIDE_BACK)
1431                 {
1432                         VectorCopy (p1, b->points[b->numpoints]);
1433                         b->numpoints++;
1434                 }
1435
1436                 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1437                         continue;
1438
1439                 // generate a split point
1440                 p2 = in->points[(i+1)%in->numpoints];
1441
1442                 dot = dists[i] / (dists[i]-dists[i+1]);
1443                 for (j = 0;j < 3;j++)
1444                 {       // avoid round off error when possible
1445                         if (split->normal[j] == 1)
1446                                 mid[j] = split->dist;
1447                         else if (split->normal[j] == -1)
1448                                 mid[j] = -split->dist;
1449                         else
1450                                 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1451                 }
1452
1453                 VectorCopy (mid, f->points[f->numpoints]);
1454                 f->numpoints++;
1455                 VectorCopy (mid, b->points[b->numpoints]);
1456                 b->numpoints++;
1457         }
1458
1459         if (f->numpoints > maxpts || b->numpoints > maxpts)
1460                 Host_Error ("DivideWinding: points exceeded estimate");
1461 }
1462
1463 typedef struct portal_s
1464 {
1465         mplane_t plane;
1466         mnode_t *nodes[2];              // [0] = front side of plane
1467         struct portal_s *next[2];       
1468         winding_t *winding;
1469         struct portal_s *chain; // all portals are linked into a list
1470 }
1471 portal_t;
1472
1473 static portal_t *portalchain;
1474
1475 /*
1476 ===========
1477 AllocPortal
1478 ===========
1479 */
1480 portal_t *AllocPortal (void)
1481 {
1482         portal_t *p;
1483         p = malloc(sizeof(portal_t));
1484         memset(p, 0, sizeof(portal_t));
1485         p->chain = portalchain;
1486         portalchain = p;
1487         return p;
1488 }
1489
1490 void Mod_FinalizePortals(void)
1491 {
1492         int i, j, numportals, numpoints;
1493         portal_t *p, *pnext;
1494         mportal_t *portal;
1495         mvertex_t *point;
1496         mleaf_t *leaf, *endleaf;
1497         winding_t *w;
1498
1499         // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1500         leaf = loadmodel->leafs;
1501         endleaf = leaf + loadmodel->numleafs;
1502         for (;leaf < endleaf;leaf++)
1503         {
1504                 VectorSet( 2000000000,  2000000000,  2000000000, leaf->mins);
1505                 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1506         }
1507         p = portalchain;
1508         while(p)
1509         {
1510                 if (p->winding)
1511                 {
1512                         for (i = 0;i < 2;i++)
1513                         {
1514                                 leaf = (mleaf_t *)p->nodes[i];
1515                                 w = p->winding;
1516                                 for (j = 0;j < w->numpoints;j++)
1517                                 {
1518                                         if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
1519                                         if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
1520                                         if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
1521                                         if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
1522                                         if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
1523                                         if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
1524                                 }
1525                         }
1526                 }
1527                 p = p->chain;
1528         }
1529
1530         // tally up portal and point counts
1531         p = portalchain;
1532         numportals = 0;
1533         numpoints = 0;
1534         while(p)
1535         {
1536                 if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1537                 {
1538                         numportals += 2;
1539                         numpoints += p->winding->numpoints * 2;
1540                 }
1541                 p = p->chain;
1542         }
1543         loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name));
1544         loadmodel->numportals = numportals;
1545         loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name));
1546         loadmodel->numportalpoints = numpoints;
1547         // clear all leaf portal chains
1548         for (i = 0;i < loadmodel->numleafs;i++)
1549                 loadmodel->leafs[i].portals = NULL;
1550         // process all portals in the global portal chain, while freeing them
1551         portal = loadmodel->portals;
1552         point = loadmodel->portalpoints;
1553         p = portalchain;
1554         portalchain = NULL;
1555         while (p)
1556         {
1557                 pnext = p->chain;
1558
1559                 if (p->winding)
1560                 {
1561                         // 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
1562                         if (p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1563                         {
1564                                 // first make the back to front portal (forward portal)
1565                                 portal->points = point;
1566                                 portal->numpoints = p->winding->numpoints;
1567                                 portal->plane.dist = p->plane.dist;
1568                                 VectorCopy(p->plane.normal, portal->plane.normal);
1569                                 portal->here = (mleaf_t *)p->nodes[1];
1570                                 portal->past = (mleaf_t *)p->nodes[0];
1571                                 // copy points
1572                                 for (j = 0;j < portal->numpoints;j++)
1573                                 {
1574                                         VectorCopy(p->winding->points[j], point->position);
1575                                         point++;
1576                                 }
1577
1578                                 // link into leaf's portal chain
1579                                 portal->next = portal->here->portals;
1580                                 portal->here->portals = portal;
1581
1582                                 // advance to next portal
1583                                 portal++;
1584
1585                                 // then make the front to back portal (backward portal)
1586                                 portal->points = point;
1587                                 portal->numpoints = p->winding->numpoints;
1588                                 portal->plane.dist = -p->plane.dist;
1589                                 VectorNegate(p->plane.normal, portal->plane.normal);
1590                                 portal->here = (mleaf_t *)p->nodes[0];
1591                                 portal->past = (mleaf_t *)p->nodes[1];
1592                                 // copy points
1593                                 for (j = portal->numpoints - 1;j >= 0;j--)
1594                                 {
1595                                         VectorCopy(p->winding->points[j], point->position);
1596                                         point++;
1597                                 }
1598
1599                                 // link into leaf's portal chain
1600                                 portal->next = portal->here->portals;
1601                                 portal->here->portals = portal;
1602
1603                                 // advance to next portal
1604                                 portal++;
1605                         }
1606                         FreeWinding(p->winding);
1607                 }
1608                 free(p);
1609                 p = pnext;
1610         }
1611 }
1612
1613 /*
1614 =============
1615 AddPortalToNodes
1616 =============
1617 */
1618 void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
1619 {
1620         if (!front)
1621                 Host_Error ("AddPortalToNodes: NULL front node");
1622         if (!back)
1623                 Host_Error ("AddPortalToNodes: NULL back node");
1624         if (p->nodes[0] || p->nodes[1])
1625                 Host_Error ("AddPortalToNodes: already included");
1626         // 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
1627
1628         p->nodes[0] = front;
1629         p->next[0] = (portal_t *)front->portals;
1630         front->portals = (mportal_t *)p;
1631
1632         p->nodes[1] = back;
1633         p->next[1] = (portal_t *)back->portals;
1634         back->portals = (mportal_t *)p;
1635 }
1636
1637 /*
1638 =============
1639 RemovePortalFromNode
1640 =============
1641 */
1642 void RemovePortalFromNodes(portal_t *portal)
1643 {
1644         int i;
1645         mnode_t *node;
1646         void **portalpointer;
1647         portal_t *t;
1648         for (i = 0;i < 2;i++)
1649         {
1650                 node = portal->nodes[i];
1651
1652                 portalpointer = (void **) &node->portals;
1653                 while (1)
1654                 {
1655                         t = *portalpointer;
1656                         if (!t)
1657                                 Host_Error ("RemovePortalFromNodes: portal not in leaf");
1658
1659                         if (t == portal)
1660                         {
1661                                 if (portal->nodes[0] == node)
1662                                 {
1663                                         *portalpointer = portal->next[0];
1664                                         portal->nodes[0] = NULL;
1665                                 }
1666                                 else if (portal->nodes[1] == node)
1667                                 {
1668                                         *portalpointer = portal->next[1];       
1669                                         portal->nodes[1] = NULL;
1670                                 }
1671                                 else
1672                                         Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1673                                 break;
1674                         }
1675
1676                         if (t->nodes[0] == node)
1677                                 portalpointer = (void **) &t->next[0];
1678                         else if (t->nodes[1] == node)
1679                                 portalpointer = (void **) &t->next[1];
1680                         else
1681                                 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1682                 }
1683         }
1684 }
1685
1686 void Mod_RecursiveNodePortals (mnode_t *node)
1687 {
1688         int side;
1689         mnode_t *front, *back, *other_node;
1690         mplane_t clipplane, *plane;
1691         portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
1692         winding_t *nodeportalwinding, *frontwinding, *backwinding;
1693
1694         //      CheckLeafPortalConsistancy (node);
1695
1696         // if a leaf, we're done
1697         if (node->contents)
1698                 return;
1699
1700         plane = node->plane;
1701
1702         front = node->children[0];
1703         back = node->children[1];
1704         if (front == back)
1705                 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
1706
1707         // create the new portal by generating a polygon for the node plane,
1708         // and clipping it by all of the other portals (which came from nodes above this one)
1709         nodeportal = AllocPortal ();
1710         nodeportal->plane = *node->plane;
1711
1712         nodeportalwinding = BaseWindingForPlane (node->plane);
1713         side = 0;       // shut up compiler warning
1714         for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])     
1715         {
1716                 clipplane = portal->plane;
1717                 if (portal->nodes[0] == portal->nodes[1])
1718                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
1719                 if (portal->nodes[0] == node)
1720                         side = 0;
1721                 else if (portal->nodes[1] == node)
1722                 {
1723                         clipplane.dist = -clipplane.dist;
1724                         VectorNegate (clipplane.normal, clipplane.normal);
1725                         side = 1;
1726                 }
1727                 else
1728                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1729
1730                 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
1731                 if (!nodeportalwinding)
1732                 {
1733                         printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
1734                         break;
1735                 }
1736         }
1737
1738         if (nodeportalwinding)
1739         {
1740                 // if the plane was not clipped on all sides, there was an error
1741                 nodeportal->winding = nodeportalwinding;
1742                 AddPortalToNodes (nodeportal, front, back);
1743         }
1744
1745         // split the portals of this node along this node's plane and assign them to the children of this node
1746         // (migrating the portals downward through the tree)
1747         for (portal = (portal_t *)node->portals;portal;portal = nextportal)
1748         {
1749                 if (portal->nodes[0] == portal->nodes[1])
1750                         Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
1751                 if (portal->nodes[0] == node)
1752                         side = 0;
1753                 else if (portal->nodes[1] == node)
1754                         side = 1;
1755                 else
1756                         Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1757                 nextportal = portal->next[side];
1758
1759                 other_node = portal->nodes[!side];
1760                 RemovePortalFromNodes (portal);
1761
1762                 // cut the portal into two portals, one on each side of the node plane
1763                 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
1764
1765                 if (!frontwinding)
1766                 {
1767                         if (side == 0)
1768                                 AddPortalToNodes (portal, back, other_node);
1769                         else
1770                                 AddPortalToNodes (portal, other_node, back);
1771                         continue;
1772                 }
1773                 if (!backwinding)
1774                 {
1775                         if (side == 0)
1776                                 AddPortalToNodes (portal, front, other_node);
1777                         else
1778                                 AddPortalToNodes (portal, other_node, front);
1779                         continue;
1780                 }
1781
1782                 // the winding is split
1783                 splitportal = AllocPortal ();
1784                 temp = splitportal->chain;
1785                 *splitportal = *portal;
1786                 splitportal->chain = temp;
1787                 splitportal->winding = backwinding;
1788                 FreeWinding (portal->winding);
1789                 portal->winding = frontwinding;
1790
1791                 if (side == 0)
1792                 {
1793                         AddPortalToNodes (portal, front, other_node);
1794                         AddPortalToNodes (splitportal, back, other_node);
1795                 }
1796                 else
1797                 {
1798                         AddPortalToNodes (portal, other_node, front);
1799                         AddPortalToNodes (splitportal, other_node, back);
1800                 }
1801         }
1802
1803         Mod_RecursiveNodePortals(front);
1804         Mod_RecursiveNodePortals(back);
1805 }
1806
1807 /*
1808 void Mod_MakeOutsidePortals(mnode_t *node)
1809 {
1810         int                     i, j;
1811         portal_t        *p, *portals[6];
1812         mnode_t         *outside_node;
1813
1814         outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name);
1815         outside_node->contents = CONTENTS_SOLID;
1816         outside_node->portals = NULL;
1817
1818         for (i = 0;i < 3;i++)
1819         {
1820                 for (j = 0;j < 2;j++)
1821                 {
1822                         portals[j*3 + i] = p = AllocPortal ();
1823                         memset (&p->plane, 0, sizeof(mplane_t));
1824                         p->plane.normal[i] = j ? -1 : 1;
1825                         p->plane.dist = -65536;
1826                         p->winding = BaseWindingForPlane (&p->plane);
1827                         if (j)
1828                                 AddPortalToNodes (p, outside_node, node);
1829                         else
1830                                 AddPortalToNodes (p, node, outside_node);
1831                 }
1832         }
1833
1834         // clip the basewindings by all the other planes
1835         for (i = 0;i < 6;i++)
1836         {
1837                 for (j = 0;j < 6;j++)
1838                 {
1839                         if (j == i)
1840                                 continue;
1841                         portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
1842                 }
1843         }
1844 }
1845 */
1846
1847 void Mod_MakePortals(void)
1848 {
1849 //      Con_Printf("building portals for %s\n", loadmodel->name);
1850
1851         portalchain = NULL;
1852 //      Mod_MakeOutsidePortals (loadmodel->nodes);
1853         Mod_RecursiveNodePortals (loadmodel->nodes);
1854         Mod_FinalizePortals();
1855 }
1856
1857 /*
1858 =================
1859 Mod_LoadBrushModel
1860 =================
1861 */
1862 void Mod_LoadBrushModel (model_t *mod, void *buffer)
1863 {
1864         int                     i, j;
1865         dheader_t       *header;
1866         dmodel_t        *bm;
1867         
1868         loadmodel->type = mod_brush;
1869         
1870         header = (dheader_t *)buffer;
1871
1872         i = LittleLong (header->version);
1873         if (i != BSPVERSION && i != 30)
1874                 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
1875         hlbsp = i == 30;
1876         halflifebsp.value = hlbsp;
1877
1878 // swap all the lumps
1879         mod_base = (byte *)header;
1880
1881         for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
1882                 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
1883
1884 // load into heap
1885         
1886         // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
1887         Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1888
1889         Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
1890         Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
1891         Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
1892         Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
1893         Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
1894         Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
1895         Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
1896         Mod_LoadFaces (&header->lumps[LUMP_FACES]);
1897         Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
1898         Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
1899         Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
1900         Mod_LoadNodes (&header->lumps[LUMP_NODES]);
1901         Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
1902 //      Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1903         Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
1904
1905         Mod_MakeHull0 ();
1906
1907         Mod_MakePortals();
1908         
1909         mod->numframes = 2;             // regular and alternate animation
1910         
1911 //
1912 // set up the submodels (FIXME: this is confusing)
1913 //
1914         for (i = 0;i < mod->numsubmodels;i++)
1915         {
1916                 bm = &mod->submodels[i];
1917
1918                 mod->hulls[0].firstclipnode = bm->headnode[0];
1919                 for (j=1 ; j<MAX_MAP_HULLS ; j++)
1920                 {
1921                         mod->hulls[j].firstclipnode = bm->headnode[j];
1922                         mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
1923                 }
1924                 
1925                 mod->firstmodelsurface = bm->firstface;
1926                 mod->nummodelsurfaces = bm->numfaces;
1927                 
1928                 VectorCopy (bm->maxs, mod->maxs);
1929                 VectorCopy (bm->mins, mod->mins);
1930
1931                 mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
1932
1933                 mod->numleafs = bm->visleafs;
1934
1935                 if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
1936                 {       // duplicate the basic information
1937                         char    name[10];
1938
1939                         sprintf (name, "*%i", i+1);
1940                         loadmodel = Mod_FindName (name);
1941                         *loadmodel = *mod;
1942                         strcpy (loadmodel->name, name);
1943                         mod = loadmodel;
1944                 }
1945         }
1946 }