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