2 Copyright (C) 1996-1997 Id Software, Inc.
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.
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.
13 See the GNU General Public License for more details.
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.
23 byte mod_novis[MAX_MAP_LEAFS/8];
25 qboolean hlbsp; // LordHavoc: true if it is a HalfLife BSP file (version 30)
27 cvar_t gl_subdivide_size = {"gl_subdivide_size", "128", true};
28 cvar_t halflifebsp = {"halflifebsp", "0"};
29 cvar_t r_novis = {"r_novis", "0"};
36 void Mod_BrushInit (void)
38 Cvar_RegisterVariable (&gl_subdivide_size);
39 Cvar_RegisterVariable (&halflifebsp);
40 Cvar_RegisterVariable (&r_novis);
41 memset (mod_novis, 0xff, sizeof(mod_novis));
49 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
53 // if (!model || !model->nodes)
54 // Sys_Error ("Mod_PointInLeaf: bad model");
58 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
59 while (node->contents == 0);
61 return (mleaf_t *)node;
64 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
70 if (!model || !model->nodes)
71 Sys_Error ("Mod_PointInLeaf: bad model");
76 if (node->contents < 0)
77 return (mleaf_t *)node;
79 d = DotProduct (p,plane->normal) - plane->dist;
81 node = node->children[0];
83 node = node->children[1];
86 return NULL; // never reached
95 byte *Mod_DecompressVis (byte *in, model_t *model)
97 static byte decompressed[MAX_MAP_LEAFS/8];
102 row = (model->numleafs+7)>>3;
107 { // no vis info, so make all visible
132 } while (out - decompressed < row);
137 byte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
139 if (r_novis.value || leaf == model->leafs || leaf->compressed_vis == NULL)
141 return Mod_DecompressVis (leaf->compressed_vis, model);
144 extern byte *mod_base;
146 extern cvar_t r_fullbrights;
148 rtexture_t *r_notexture;
149 texture_t r_notexture_mip;
151 void Mod_SetupNoTexture()
156 // create a simple checkerboard texture for the default
157 // LordHavoc: redesigned this to remove reliance on the palette and texture_t
158 for (y = 0;y < 16;y++)
160 for (x = 0;x < 16;x++)
162 if ((y < 8) ^ (x < 8))
179 r_notexture = R_LoadTexture("notexture", 16, 16, &pix[0][0][0], TEXF_MIPMAP | TEXF_RGBA);
181 strcpy(r_notexture_mip.name, "notexture");
182 r_notexture_mip.width = 16;
183 r_notexture_mip.height = 16;
184 r_notexture_mip.transparent = false;
185 r_notexture_mip.texture = r_notexture;
186 r_notexture_mip.glowtexture = NULL;
194 void Mod_LoadTextures (lump_t *l)
196 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
198 texture_t *tx, *tx2, *anims[10], *altanims[10];
204 loadmodel->textures = NULL;
208 m = (dmiptexlump_t *)(mod_base + l->fileofs);
210 m->nummiptex = LittleLong (m->nummiptex);
212 loadmodel->numtextures = m->nummiptex;
213 loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), va("%s texture headers", loadname));
215 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
217 for (i = 0;i < m->nummiptex;i++)
219 dofs[i] = LittleLong(dofs[i]);
222 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
223 mtwidth = LittleLong (dmiptex->width);
224 mtheight = LittleLong (dmiptex->height);
226 j = LittleLong (dmiptex->offsets[0]);
230 if (j < 40 || j + mtwidth * mtheight > l->filelen)
231 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
232 mtdata = (byte *)dmiptex + j;
235 if ((mtwidth & 15) || (mtheight & 15))
236 Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
237 // LordHavoc: rewriting the map texture loader for GLQuake
238 tx = Hunk_AllocName (sizeof(texture_t), va("%s textures", loadname));
239 loadmodel->textures[i] = tx;
241 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
242 for (j = 0;dmiptex->name[j] && j < 15;j++)
244 if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
245 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
247 tx->name[j] = dmiptex->name[j];
254 Con_Printf("warning: unnamed texture in %s\n", loadname);
255 sprintf(tx->name, "unnamed%i", i);
258 tx->transparent = false;
259 data = loadimagepixels(tx->name, false, 0, 0);
262 if (!hlbsp && !strncmp(tx->name,"sky",3) && image_width == 256 && image_height == 128) // LordHavoc: HL sky textures are entirely unrelated
266 tx->transparent = false;
268 tx->glowtexture = NULL;
274 tx->height = mtheight;
275 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
276 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
277 tx->glowtexture = NULL;
285 if (mtdata) // texture included
287 data = W_ConvertWAD3Texture(dmiptex);
291 tx->height = mtheight;
292 tx->transparent = Image_CheckAlpha(data, mtwidth * mtheight, true);
293 tx->texture = R_LoadTexture (tx->name, mtwidth, mtheight, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
294 tx->glowtexture = NULL;
300 data = W_GetTexture(tx->name);
301 // get the size from the wad texture
304 tx->width = image_width;
305 tx->height = image_height;
306 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
307 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
308 tx->glowtexture = NULL;
316 tx->transparent = false;
317 tx->texture = r_notexture;
318 tx->glowtexture = NULL;
323 if (!strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128)
326 tx->height = mtheight;
327 tx->transparent = false;
329 tx->glowtexture = NULL;
330 R_InitSky (mtdata, 1);
334 if (mtdata) // texture included
339 tx->height = mtheight;
340 tx->transparent = false;
342 if (r_fullbrights.value && tx->name[0] != '*')
344 for (j = 0;j < tx->width*tx->height;j++)
346 if (data[j] >= 224) // fullbright
357 data2 = qmalloc(tx->width*tx->height);
358 for (j = 0;j < tx->width*tx->height;j++)
359 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
360 tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
361 strcpy(name, tx->name);
362 strcat(name, "_glow");
363 for (j = 0;j < tx->width*tx->height;j++)
364 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
365 tx->glowtexture = R_LoadTexture (name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
370 tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data, TEXF_MIPMAP | TEXF_PRECACHE);
371 tx->glowtexture = NULL;
374 else // no texture, and no external replacement texture was found
378 tx->transparent = false;
379 tx->texture = r_notexture;
380 tx->glowtexture = NULL;
388 // sequence the animations
390 for (i = 0;i < m->nummiptex;i++)
392 tx = loadmodel->textures[i];
393 if (!tx || tx->name[0] != '+')
396 continue; // already sequenced
398 // find the number of frames in the animation
399 memset (anims, 0, sizeof(anims));
400 memset (altanims, 0, sizeof(altanims));
404 if (max >= '0' && max <= '9')
411 else if (max >= 'a' && max <= 'j')
415 altanims[altmax] = tx;
419 Host_Error ("Bad animating texture %s", tx->name);
421 for (j = i + 1;j < m->nummiptex;j++)
423 tx2 = loadmodel->textures[j];
424 if (!tx2 || tx2->name[0] != '+')
426 if (strcmp (tx2->name+2, tx->name+2))
430 if (num >= '0' && num <= '9')
437 else if (num >= 'a' && num <= 'j')
445 Host_Error ("Bad animating texture %s", tx->name);
448 // link them all together
449 for (j = 0;j < max;j++)
453 Host_Error ("Missing frame %i of %s", j, tx->name);
454 tx2->anim_total = max;
456 tx2->alternate_anims = altanims[0];
457 for (k = 0;k < 10;k++)
458 tx2->anim_frames[k] = anims[j];
460 for (j = 0;j < altmax;j++)
464 Host_Error ("Missing frame %i of %s", j, tx->name);
465 tx2->anim_total = altmax;
467 tx2->alternate_anims = anims[0];
468 for (k = 0;k < 10;k++)
469 tx2->anim_frames[k] = altanims[j];
479 void Mod_LoadLighting (lump_t *l)
482 byte *in, *out, *data;
484 char litfilename[1024];
485 loadmodel->lightdata = NULL;
486 if (hlbsp) // LordHavoc: load the colored lighting data straight
488 loadmodel->lightdata = Hunk_AllocName ( l->filelen, va("%s lightmaps", loadname));
489 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
491 else // LordHavoc: bsp version 29 (normal white lighting)
493 // LordHavoc: hope is not lost yet, check for a .lit file to load
494 strcpy(litfilename, loadmodel->name);
495 COM_StripExtension(litfilename, litfilename);
496 strcat(litfilename, ".lit");
497 data = (byte*) COM_LoadHunkFile (litfilename, false);
500 if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
502 i = LittleLong(((int *)data)[1]);
505 Con_DPrintf("%s loaded", litfilename);
506 loadmodel->lightdata = data + 8;
510 Con_Printf("Unknown .lit file version (%d)\n", i);
513 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
515 // LordHavoc: oh well, expand the white lighting data
518 loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, va("%s lightmaps", loadname));
519 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
520 out = loadmodel->lightdata;
521 memcpy (in, mod_base + l->fileofs, l->filelen);
522 for (i = 0;i < l->filelen;i++)
538 void Mod_LoadVisibility (lump_t *l)
542 loadmodel->visdata = NULL;
545 loadmodel->visdata = Hunk_AllocName ( l->filelen, va("%s visdata", loadname));
546 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
549 void CL_ParseEntityLump(char *entdata);
551 extern qboolean isworldmodel;
558 void Mod_LoadEntities (lump_t *l)
562 loadmodel->entities = NULL;
565 loadmodel->entities = Hunk_AllocName ( l->filelen, va("%s entities", loadname));
566 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
569 CL_ParseEntityLump(loadmodel->entities);
578 void Mod_LoadVertexes (lump_t *l)
584 in = (void *)(mod_base + l->fileofs);
585 if (l->filelen % sizeof(*in))
586 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
587 count = l->filelen / sizeof(*in);
588 out = Hunk_AllocName ( count*sizeof(*out), va("%s vertices", loadname));
590 loadmodel->vertexes = out;
591 loadmodel->numvertexes = count;
593 for ( i=0 ; i<count ; i++, in++, out++)
595 out->position[0] = LittleFloat (in->point[0]);
596 out->position[1] = LittleFloat (in->point[1]);
597 out->position[2] = LittleFloat (in->point[2]);
606 void Mod_LoadSubmodels (lump_t *l)
612 in = (void *)(mod_base + l->fileofs);
613 if (l->filelen % sizeof(*in))
614 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
615 count = l->filelen / sizeof(*in);
616 out = Hunk_AllocName ( count*sizeof(*out), va("%s submodels", loadname));
618 loadmodel->submodels = out;
619 loadmodel->numsubmodels = count;
621 for ( i=0 ; i<count ; i++, in++, out++)
623 for (j=0 ; j<3 ; j++)
624 { // spread the mins / maxs by a pixel
625 out->mins[j] = LittleFloat (in->mins[j]) - 1;
626 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
627 out->origin[j] = LittleFloat (in->origin[j]);
629 for (j=0 ; j<MAX_MAP_HULLS ; j++)
630 out->headnode[j] = LittleLong (in->headnode[j]);
631 out->visleafs = LittleLong (in->visleafs);
632 out->firstface = LittleLong (in->firstface);
633 out->numfaces = LittleLong (in->numfaces);
642 void Mod_LoadEdges (lump_t *l)
648 in = (void *)(mod_base + l->fileofs);
649 if (l->filelen % sizeof(*in))
650 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
651 count = l->filelen / sizeof(*in);
652 out = Hunk_AllocName ( (count + 1) * sizeof(*out), va("%s edges", loadname));
654 loadmodel->edges = out;
655 loadmodel->numedges = count;
657 for ( i=0 ; i<count ; i++, in++, out++)
659 out->v[0] = (unsigned short)LittleShort(in->v[0]);
660 out->v[1] = (unsigned short)LittleShort(in->v[1]);
669 void Mod_LoadTexinfo (lump_t *l)
676 in = (void *)(mod_base + l->fileofs);
677 if (l->filelen % sizeof(*in))
678 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
679 count = l->filelen / sizeof(*in);
680 out = Hunk_AllocName ( count*sizeof(*out), va("%s texinfo", loadname));
682 loadmodel->texinfo = out;
683 loadmodel->numtexinfo = count;
685 for ( i=0 ; i<count ; i++, in++, out++)
687 for (k=0 ; k<2 ; k++)
688 for (j=0 ; j<4 ; j++)
689 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
691 miptex = LittleLong (in->miptex);
692 out->flags = LittleLong (in->flags);
694 if (!loadmodel->textures)
696 out->texture = &r_notexture_mip; // checkerboard texture
701 if (miptex >= loadmodel->numtextures)
702 Host_Error ("miptex >= loadmodel->numtextures");
703 out->texture = loadmodel->textures[miptex];
706 out->texture = &r_notexture_mip; // checkerboard texture
717 Fills in s->texturemins[] and s->extents[]
720 void CalcSurfaceExtents (msurface_t *s)
722 float mins[2], maxs[2], val;
726 int bmins[2], bmaxs[2];
728 mins[0] = mins[1] = 999999;
729 maxs[0] = maxs[1] = -99999;
733 for (i=0 ; i<s->numedges ; i++)
735 e = loadmodel->surfedges[s->firstedge+i];
737 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
739 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
741 for (j=0 ; j<2 ; j++)
743 val = v->position[0] * tex->vecs[j][0] +
744 v->position[1] * tex->vecs[j][1] +
745 v->position[2] * tex->vecs[j][2] +
754 for (i=0 ; i<2 ; i++)
756 bmins[i] = floor(mins[i]/16);
757 bmaxs[i] = ceil(maxs[i]/16);
759 s->texturemins[i] = bmins[i] * 16;
760 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
761 // if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512)
762 if ((tex->flags & TEX_SPECIAL) == 0 && (s->extents[i]+1) > (256*16))
763 Host_Error ("Bad surface extents");
767 void GL_SubdivideSurface (msurface_t *fa);
769 extern char skyname[];
776 void Mod_LoadFaces (lump_t *l)
780 int i, count, surfnum;
783 in = (void *)(mod_base + l->fileofs);
784 if (l->filelen % sizeof(*in))
785 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
786 count = l->filelen / sizeof(*in);
787 out = Hunk_AllocName ( count*sizeof(*out), va("%s faces", loadname));
789 loadmodel->surfaces = out;
790 loadmodel->numsurfaces = count;
792 for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
794 out->firstedge = LittleLong(in->firstedge);
795 out->numedges = LittleShort(in->numedges);
798 planenum = LittleShort(in->planenum);
799 side = LittleShort(in->side);
801 out->flags |= SURF_PLANEBACK;
803 out->plane = loadmodel->planes + planenum;
805 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
807 CalcSurfaceExtents (out);
811 for (i=0 ; i<MAXLIGHTMAPS ; i++)
812 out->styles[i] = in->styles[i];
813 i = LittleLong(in->lightofs);
816 else if (hlbsp) // LordHavoc: HalfLife map (bsp version 30)
817 out->samples = loadmodel->lightdata + i;
818 else // LordHavoc: white lighting (bsp version 29)
819 out->samples = loadmodel->lightdata + (i * 3);
821 // set the drawing flags flag
823 // if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky
824 // LordHavoc: faster check
825 if ((out->texinfo->texture->name[0] == 's' || out->texinfo->texture->name[0] == 'S')
826 && (out->texinfo->texture->name[1] == 'k' || out->texinfo->texture->name[1] == 'K')
827 && (out->texinfo->texture->name[2] == 'y' || out->texinfo->texture->name[2] == 'Y'))
829 // LordHavoc: for consistency reasons, mark sky as fullbright and solid as well
830 out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
831 GL_SubdivideSurface (out); // cut up polygon for warps
835 // if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent
836 if (out->texinfo->texture->name[0] == '*') // LordHavoc: faster check
838 out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED | SURF_LIGHTBOTHSIDES);
839 // LordHavoc: some turbulent textures should be fullbright and solid
840 if (!strncmp(out->texinfo->texture->name,"*lava",5)
841 || !strncmp(out->texinfo->texture->name,"*teleport",9)
842 || !strncmp(out->texinfo->texture->name,"*rift",5)) // Scourge of Armagon texture
843 out->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
844 for (i=0 ; i<2 ; i++)
846 out->extents[i] = 16384;
847 out->texturemins[i] = -8192;
849 GL_SubdivideSurface (out); // cut up polygon for warps
862 void Mod_SetParent (mnode_t *node, mnode_t *parent)
864 node->parent = parent;
865 if (node->contents < 0)
867 Mod_SetParent (node->children[0], node);
868 Mod_SetParent (node->children[1], node);
876 void Mod_LoadNodes (lump_t *l)
882 in = (void *)(mod_base + l->fileofs);
883 if (l->filelen % sizeof(*in))
884 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
885 count = l->filelen / sizeof(*in);
886 out = Hunk_AllocName ( count*sizeof(*out), va("%s nodes", loadname));
888 loadmodel->nodes = out;
889 loadmodel->numnodes = count;
891 for ( i=0 ; i<count ; i++, in++, out++)
893 // for (j=0 ; j<3 ; j++)
895 // out->mins[j] = LittleShort (in->mins[j]);
896 // out->maxs[j] = LittleShort (in->maxs[j]);
899 p = LittleLong(in->planenum);
900 out->plane = loadmodel->planes + p;
902 out->firstsurface = LittleShort (in->firstface);
903 out->numsurfaces = LittleShort (in->numfaces);
905 for (j=0 ; j<2 ; j++)
907 p = LittleShort (in->children[j]);
909 out->children[j] = loadmodel->nodes + p;
911 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
915 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
923 void Mod_LoadLeafs (lump_t *l)
929 in = (void *)(mod_base + l->fileofs);
930 if (l->filelen % sizeof(*in))
931 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
932 count = l->filelen / sizeof(*in);
933 out = Hunk_AllocName ( count*sizeof(*out), va("%s leafs", loadname));
935 loadmodel->leafs = out;
936 loadmodel->numleafs = count;
938 for ( i=0 ; i<count ; i++, in++, out++)
940 for (j=0 ; j<3 ; j++)
942 out->mins[j] = LittleShort (in->mins[j]);
943 out->maxs[j] = LittleShort (in->maxs[j]);
946 p = LittleLong(in->contents);
949 out->firstmarksurface = loadmodel->marksurfaces +
950 LittleShort(in->firstmarksurface);
951 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
953 p = LittleLong(in->visofs);
955 out->compressed_vis = NULL;
957 out->compressed_vis = loadmodel->visdata + p;
958 // out->efrags = NULL;
960 for (j=0 ; j<4 ; j++)
961 out->ambient_sound_level[j] = in->ambient_level[j];
963 // gl underwater warp
964 // LordHavoc: disabled underwater warping
966 if (out->contents != CONTENTS_EMPTY)
968 for (j=0 ; j<out->nummarksurfaces ; j++)
969 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
980 void Mod_LoadClipnodes (lump_t *l)
982 dclipnode_t *in, *out;
986 in = (void *)(mod_base + l->fileofs);
987 if (l->filelen % sizeof(*in))
988 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
989 count = l->filelen / sizeof(*in);
990 out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname));
992 loadmodel->clipnodes = out;
993 loadmodel->numclipnodes = count;
997 hull = &loadmodel->hulls[1];
998 hull->clipnodes = out;
999 hull->firstclipnode = 0;
1000 hull->lastclipnode = count-1;
1001 hull->planes = loadmodel->planes;
1002 hull->clip_mins[0] = -16;
1003 hull->clip_mins[1] = -16;
1004 hull->clip_mins[2] = -36;
1005 hull->clip_maxs[0] = 16;
1006 hull->clip_maxs[1] = 16;
1007 hull->clip_maxs[2] = 36;
1009 hull = &loadmodel->hulls[2];
1010 hull->clipnodes = out;
1011 hull->firstclipnode = 0;
1012 hull->lastclipnode = count-1;
1013 hull->planes = loadmodel->planes;
1014 hull->clip_mins[0] = -32;
1015 hull->clip_mins[1] = -32;
1016 hull->clip_mins[2] = -32;
1017 hull->clip_maxs[0] = 32;
1018 hull->clip_maxs[1] = 32;
1019 hull->clip_maxs[2] = 32;
1021 hull = &loadmodel->hulls[3];
1022 hull->clipnodes = out;
1023 hull->firstclipnode = 0;
1024 hull->lastclipnode = count-1;
1025 hull->planes = loadmodel->planes;
1026 hull->clip_mins[0] = -16;
1027 hull->clip_mins[1] = -16;
1028 hull->clip_mins[2] = -18;
1029 hull->clip_maxs[0] = 16;
1030 hull->clip_maxs[1] = 16;
1031 hull->clip_maxs[2] = 18;
1035 hull = &loadmodel->hulls[1];
1036 hull->clipnodes = out;
1037 hull->firstclipnode = 0;
1038 hull->lastclipnode = count-1;
1039 hull->planes = loadmodel->planes;
1040 hull->clip_mins[0] = -16;
1041 hull->clip_mins[1] = -16;
1042 hull->clip_mins[2] = -24;
1043 hull->clip_maxs[0] = 16;
1044 hull->clip_maxs[1] = 16;
1045 hull->clip_maxs[2] = 32;
1047 hull = &loadmodel->hulls[2];
1048 hull->clipnodes = out;
1049 hull->firstclipnode = 0;
1050 hull->lastclipnode = count-1;
1051 hull->planes = loadmodel->planes;
1052 hull->clip_mins[0] = -32;
1053 hull->clip_mins[1] = -32;
1054 hull->clip_mins[2] = -24;
1055 hull->clip_maxs[0] = 32;
1056 hull->clip_maxs[1] = 32;
1057 hull->clip_maxs[2] = 64;
1060 for (i=0 ; i<count ; i++, out++, in++)
1062 out->planenum = LittleLong(in->planenum);
1063 out->children[0] = LittleShort(in->children[0]);
1064 out->children[1] = LittleShort(in->children[1]);
1065 if (out->children[0] >= count || out->children[1] >= count)
1066 Host_Error("Corrupt clipping hull (out of range child)\n");
1074 Duplicate the drawing hull structure as a clipping hull
1077 void Mod_MakeHull0 (void)
1084 hull = &loadmodel->hulls[0];
1086 in = loadmodel->nodes;
1087 count = loadmodel->numnodes;
1088 out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname));
1090 hull->clipnodes = out;
1091 hull->firstclipnode = 0;
1092 hull->lastclipnode = count - 1;
1093 hull->planes = loadmodel->planes;
1095 for (i = 0;i < count;i++, out++, in++)
1097 out->planenum = in->plane - loadmodel->planes;
1098 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1099 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1105 Mod_LoadMarksurfaces
1108 void Mod_LoadMarksurfaces (lump_t *l)
1114 in = (void *)(mod_base + l->fileofs);
1115 if (l->filelen % sizeof(*in))
1116 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1117 count = l->filelen / sizeof(*in);
1118 out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname));
1120 loadmodel->marksurfaces = out;
1121 loadmodel->nummarksurfaces = count;
1123 for ( i=0 ; i<count ; i++)
1125 j = LittleShort(in[i]);
1126 if (j >= loadmodel->numsurfaces)
1127 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1128 out[i] = loadmodel->surfaces + j;
1137 void Mod_LoadSurfedges (lump_t *l)
1142 in = (void *)(mod_base + l->fileofs);
1143 if (l->filelen % sizeof(*in))
1144 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1145 count = l->filelen / sizeof(*in);
1146 out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname));
1148 loadmodel->surfedges = out;
1149 loadmodel->numsurfedges = count;
1151 for ( i=0 ; i<count ; i++)
1152 out[i] = LittleLong (in[i]);
1161 void Mod_LoadPlanes (lump_t *l)
1169 in = (void *)(mod_base + l->fileofs);
1170 if (l->filelen % sizeof(*in))
1171 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1172 count = l->filelen / sizeof(*in);
1173 out = Hunk_AllocName ( count*2*sizeof(*out), va("%s planes", loadname));
1175 loadmodel->planes = out;
1176 loadmodel->numplanes = count;
1178 for ( i=0 ; i<count ; i++, in++, out++)
1181 for (j=0 ; j<3 ; j++)
1183 out->normal[j] = LittleFloat (in->normal[j]);
1184 // if (out->normal[j] < 0)
1188 out->dist = LittleFloat (in->dist);
1189 out->type = LittleLong (in->type);
1190 // out->signbits = bits;
1191 BoxOnPlaneSideClassify(out);
1195 #define MAX_POINTS_ON_WINDING 64
1200 vec3_t points[8]; // variable sized
1209 winding_t *NewWinding (int points)
1214 if (points > MAX_POINTS_ON_WINDING)
1215 Host_Error("NewWinding: too many points\n");
1217 size = (int)((winding_t *)0)->points[points];
1219 memset (w, 0, size);
1224 void FreeWinding (winding_t *w)
1234 winding_t *BaseWindingForPlane (mplane_t *p)
1236 vec3_t org, vright, vup;
1239 VectorVectors(p->normal, vright, vup);
1241 VectorScale (vup, 65536, vup);
1242 VectorScale (vright, 65536, vright);
1244 // project a really big axis aligned box onto the plane
1247 VectorScale (p->normal, p->dist, org);
1249 VectorSubtract (org, vright, w->points[0]);
1250 VectorAdd (w->points[0], vup, w->points[0]);
1252 VectorAdd (org, vright, w->points[1]);
1253 VectorAdd (w->points[1], vup, w->points[1]);
1255 VectorAdd (org, vright, w->points[2]);
1256 VectorSubtract (w->points[2], vup, w->points[2]);
1258 VectorSubtract (org, vright, w->points[3]);
1259 VectorSubtract (w->points[3], vup, w->points[3]);
1270 Clips the winding to the plane, returning the new winding on the positive side
1271 Frees the input winding.
1272 If keepon is true, an exactly on-plane winding will be saved, otherwise
1273 it will be clipped away.
1276 winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1278 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1279 int sides[MAX_POINTS_ON_WINDING + 1];
1288 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1290 // determine sides for each point
1291 for (i = 0;i < in->numpoints;i++)
1293 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1294 if (dot > ON_EPSILON)
1295 sides[i] = SIDE_FRONT;
1296 else if (dot < -ON_EPSILON)
1297 sides[i] = SIDE_BACK;
1302 sides[i] = sides[0];
1303 dists[i] = dists[0];
1305 if (keepon && !counts[0] && !counts[1])
1316 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1317 neww = NewWinding (maxpts);
1319 for (i = 0;i < in->numpoints;i++)
1323 if (sides[i] == SIDE_ON)
1325 VectorCopy (p1, neww->points[neww->numpoints]);
1330 if (sides[i] == SIDE_FRONT)
1332 VectorCopy (p1, neww->points[neww->numpoints]);
1336 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1339 // generate a split point
1340 p2 = in->points[(i+1)%in->numpoints];
1342 dot = dists[i] / (dists[i]-dists[i+1]);
1343 for (j = 0;j < 3;j++)
1344 { // avoid round off error when possible
1345 if (split->normal[j] == 1)
1346 mid[j] = split->dist;
1347 else if (split->normal[j] == -1)
1348 mid[j] = -split->dist;
1350 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1353 VectorCopy (mid, neww->points[neww->numpoints]);
1357 if (neww->numpoints > maxpts)
1358 Host_Error ("ClipWinding: points exceeded estimate");
1360 // free the original winding
1371 Divides a winding by a plane, producing one or two windings. The
1372 original winding is not damaged or freed. If only on one side, the
1373 returned winding will be the input winding. If on both sides, two
1374 new windings will be created.
1377 void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1379 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1380 int sides[MAX_POINTS_ON_WINDING + 1];
1389 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1391 // determine sides for each point
1392 for (i = 0;i < in->numpoints;i++)
1394 dot = DotProduct (in->points[i], split->normal);
1397 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1398 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1399 else sides[i] = SIDE_ON;
1402 sides[i] = sides[0];
1403 dists[i] = dists[0];
1405 *front = *back = NULL;
1418 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1420 *front = f = NewWinding (maxpts);
1421 *back = b = NewWinding (maxpts);
1423 for (i = 0;i < in->numpoints;i++)
1427 if (sides[i] == SIDE_ON)
1429 VectorCopy (p1, f->points[f->numpoints]);
1431 VectorCopy (p1, b->points[b->numpoints]);
1436 if (sides[i] == SIDE_FRONT)
1438 VectorCopy (p1, f->points[f->numpoints]);
1441 else if (sides[i] == SIDE_BACK)
1443 VectorCopy (p1, b->points[b->numpoints]);
1447 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1450 // generate a split point
1451 p2 = in->points[(i+1)%in->numpoints];
1453 dot = dists[i] / (dists[i]-dists[i+1]);
1454 for (j = 0;j < 3;j++)
1455 { // avoid round off error when possible
1456 if (split->normal[j] == 1)
1457 mid[j] = split->dist;
1458 else if (split->normal[j] == -1)
1459 mid[j] = -split->dist;
1461 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1464 VectorCopy (mid, f->points[f->numpoints]);
1466 VectorCopy (mid, b->points[b->numpoints]);
1470 if (f->numpoints > maxpts || b->numpoints > maxpts)
1471 Host_Error ("DivideWinding: points exceeded estimate");
1474 typedef struct portal_s
1477 mnode_t *nodes[2]; // [0] = front side of plane
1478 struct portal_s *next[2];
1480 struct portal_s *chain; // all portals are linked into a list
1484 static portal_t *portalchain;
1491 portal_t *AllocPortal (void)
1494 p = malloc(sizeof(portal_t));
1495 memset(p, 0, sizeof(portal_t));
1496 p->chain = portalchain;
1501 void Mod_FinalizePortals()
1503 int i, j, numportals, numpoints;
1504 portal_t *p, *pnext;
1507 mleaf_t *leaf, *endleaf;
1510 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1511 leaf = loadmodel->leafs;
1512 endleaf = leaf + loadmodel->numleafs;
1513 for (;leaf < endleaf;leaf++)
1515 VectorSet( 2000000000, 2000000000, 2000000000, leaf->mins);
1516 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1523 for (i = 0;i < 2;i++)
1525 leaf = (mleaf_t *)p->nodes[i];
1527 for (j = 0;j < w->numpoints;j++)
1529 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
1530 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
1531 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
1532 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
1533 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
1534 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
1541 // tally up portal and point counts
1547 if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1550 numpoints += p->winding->numpoints * 2;
1554 loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name));
1555 loadmodel->numportals = numportals;
1556 loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name));
1557 loadmodel->numportalpoints = numpoints;
1558 // clear all leaf portal chains
1559 for (i = 0;i < loadmodel->numleafs;i++)
1560 loadmodel->leafs[i].portals = NULL;
1561 // process all portals in the global portal chain, while freeing them
1562 portal = loadmodel->portals;
1563 point = loadmodel->portalpoints;
1572 // 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
1573 if (p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1575 // first make the back to front portal (forward portal)
1576 portal->points = point;
1577 portal->numpoints = p->winding->numpoints;
1578 portal->plane.dist = p->plane.dist;
1579 VectorCopy(p->plane.normal, portal->plane.normal);
1580 portal->here = (mleaf_t *)p->nodes[1];
1581 portal->past = (mleaf_t *)p->nodes[0];
1583 for (j = 0;j < portal->numpoints;j++)
1585 VectorCopy(p->winding->points[j], point->position);
1589 // link into leaf's portal chain
1590 portal->next = portal->here->portals;
1591 portal->here->portals = portal;
1593 // advance to next portal
1596 // then make the front to back portal (backward portal)
1597 portal->points = point;
1598 portal->numpoints = p->winding->numpoints;
1599 portal->plane.dist = -p->plane.dist;
1600 VectorNegate(p->plane.normal, portal->plane.normal);
1601 portal->here = (mleaf_t *)p->nodes[0];
1602 portal->past = (mleaf_t *)p->nodes[1];
1604 for (j = portal->numpoints - 1;j >= 0;j--)
1606 VectorCopy(p->winding->points[j], point->position);
1610 // link into leaf's portal chain
1611 portal->next = portal->here->portals;
1612 portal->here->portals = portal;
1614 // advance to next portal
1617 FreeWinding(p->winding);
1629 void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
1632 Host_Error ("AddPortalToNodes: NULL front node");
1634 Host_Error ("AddPortalToNodes: NULL back node");
1635 if (p->nodes[0] || p->nodes[1])
1636 Host_Error ("AddPortalToNodes: already included");
1637 // 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
1639 p->nodes[0] = front;
1640 p->next[0] = (portal_t *)front->portals;
1641 front->portals = (mportal_t *)p;
1644 p->next[1] = (portal_t *)back->portals;
1645 back->portals = (mportal_t *)p;
1650 RemovePortalFromNode
1653 void RemovePortalFromNodes(portal_t *portal)
1657 void **portalpointer;
1659 for (i = 0;i < 2;i++)
1661 node = portal->nodes[i];
1663 portalpointer = (void **) &node->portals;
1668 Host_Error ("RemovePortalFromNodes: portal not in leaf");
1672 if (portal->nodes[0] == node)
1674 *portalpointer = portal->next[0];
1675 portal->nodes[0] = NULL;
1677 else if (portal->nodes[1] == node)
1679 *portalpointer = portal->next[1];
1680 portal->nodes[1] = NULL;
1683 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1687 if (t->nodes[0] == node)
1688 portalpointer = (void **) &t->next[0];
1689 else if (t->nodes[1] == node)
1690 portalpointer = (void **) &t->next[1];
1692 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1697 void Mod_RecursiveNodePortals (mnode_t *node)
1700 mnode_t *front, *back, *other_node;
1701 mplane_t clipplane, *plane;
1702 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
1703 winding_t *nodeportalwinding, *frontwinding, *backwinding;
1705 // CheckLeafPortalConsistancy (node);
1707 // if a leaf, we're done
1711 plane = node->plane;
1713 front = node->children[0];
1714 back = node->children[1];
1716 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
1718 // create the new portal by generating a polygon for the node plane,
1719 // and clipping it by all of the other portals (which came from nodes above this one)
1720 nodeportal = AllocPortal ();
1721 nodeportal->plane = *node->plane;
1723 nodeportalwinding = BaseWindingForPlane (node->plane);
1724 side = 0; // shut up compiler warning
1725 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
1727 clipplane = portal->plane;
1728 if (portal->nodes[0] == portal->nodes[1])
1729 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
1730 if (portal->nodes[0] == node)
1732 else if (portal->nodes[1] == node)
1734 clipplane.dist = -clipplane.dist;
1735 VectorNegate (clipplane.normal, clipplane.normal);
1739 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1741 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
1742 if (!nodeportalwinding)
1744 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
1749 if (nodeportalwinding)
1751 // if the plane was not clipped on all sides, there was an error
1752 nodeportal->winding = nodeportalwinding;
1753 AddPortalToNodes (nodeportal, front, back);
1756 // split the portals of this node along this node's plane and assign them to the children of this node
1757 // (migrating the portals downward through the tree)
1758 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
1760 if (portal->nodes[0] == portal->nodes[1])
1761 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
1762 if (portal->nodes[0] == node)
1764 else if (portal->nodes[1] == node)
1767 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1768 nextportal = portal->next[side];
1770 other_node = portal->nodes[!side];
1771 RemovePortalFromNodes (portal);
1773 // cut the portal into two portals, one on each side of the node plane
1774 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
1779 AddPortalToNodes (portal, back, other_node);
1781 AddPortalToNodes (portal, other_node, back);
1787 AddPortalToNodes (portal, front, other_node);
1789 AddPortalToNodes (portal, other_node, front);
1793 // the winding is split
1794 splitportal = AllocPortal ();
1795 temp = splitportal->chain;
1796 *splitportal = *portal;
1797 splitportal->chain = temp;
1798 splitportal->winding = backwinding;
1799 FreeWinding (portal->winding);
1800 portal->winding = frontwinding;
1804 AddPortalToNodes (portal, front, other_node);
1805 AddPortalToNodes (splitportal, back, other_node);
1809 AddPortalToNodes (portal, other_node, front);
1810 AddPortalToNodes (splitportal, other_node, back);
1814 Mod_RecursiveNodePortals(front);
1815 Mod_RecursiveNodePortals(back);
1819 void Mod_MakeOutsidePortals(mnode_t *node)
1822 portal_t *p, *portals[6];
1823 mnode_t *outside_node;
1825 outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name);
1826 outside_node->contents = CONTENTS_SOLID;
1827 outside_node->portals = NULL;
1829 for (i = 0;i < 3;i++)
1831 for (j = 0;j < 2;j++)
1833 portals[j*3 + i] = p = AllocPortal ();
1834 memset (&p->plane, 0, sizeof(mplane_t));
1835 p->plane.normal[i] = j ? -1 : 1;
1836 p->plane.dist = -65536;
1837 p->winding = BaseWindingForPlane (&p->plane);
1839 AddPortalToNodes (p, outside_node, node);
1841 AddPortalToNodes (p, node, outside_node);
1845 // clip the basewindings by all the other planes
1846 for (i = 0;i < 6;i++)
1848 for (j = 0;j < 6;j++)
1852 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
1858 void Mod_MakePortals()
1860 // Con_Printf("building portals for %s\n", loadmodel->name);
1863 // Mod_MakeOutsidePortals (loadmodel->nodes);
1864 Mod_RecursiveNodePortals (loadmodel->nodes);
1865 Mod_FinalizePortals();
1873 void Mod_LoadBrushModel (model_t *mod, void *buffer)
1879 loadmodel->type = mod_brush;
1881 header = (dheader_t *)buffer;
1883 i = LittleLong (header->version);
1884 if (i != BSPVERSION && i != 30)
1885 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
1887 halflifebsp.value = hlbsp;
1889 // swap all the lumps
1890 mod_base = (byte *)header;
1892 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
1893 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
1897 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
1898 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1900 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
1901 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
1902 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
1903 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
1904 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
1905 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
1906 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
1907 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
1908 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
1909 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
1910 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
1911 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
1912 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
1913 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1914 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
1920 mod->numframes = 2; // regular and alternate animation
1923 // set up the submodels (FIXME: this is confusing)
1925 for (i = 0;i < mod->numsubmodels;i++)
1927 bm = &mod->submodels[i];
1929 mod->hulls[0].firstclipnode = bm->headnode[0];
1930 for (j=1 ; j<MAX_MAP_HULLS ; j++)
1932 mod->hulls[j].firstclipnode = bm->headnode[j];
1933 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
1936 mod->firstmodelsurface = bm->firstface;
1937 mod->nummodelsurfaces = bm->numfaces;
1939 VectorCopy (bm->maxs, mod->maxs);
1940 VectorCopy (bm->mins, mod->mins);
1942 mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
1944 mod->numleafs = bm->visleafs;
1946 if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
1947 { // duplicate the basic information
1950 sprintf (name, "*%i", i+1);
1951 loadmodel = Mod_FindName (name);
1953 strcpy (loadmodel->name, name);