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