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;
959 for (j=0 ; j<4 ; j++)
960 out->ambient_sound_level[j] = in->ambient_level[j];
962 // gl underwater warp
963 // LordHavoc: disabled underwater warping
965 if (out->contents != CONTENTS_EMPTY)
967 for (j=0 ; j<out->nummarksurfaces ; j++)
968 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
979 void Mod_LoadClipnodes (lump_t *l)
981 dclipnode_t *in, *out;
985 in = (void *)(mod_base + l->fileofs);
986 if (l->filelen % sizeof(*in))
987 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
988 count = l->filelen / sizeof(*in);
989 out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname));
991 loadmodel->clipnodes = out;
992 loadmodel->numclipnodes = count;
996 hull = &loadmodel->hulls[1];
997 hull->clipnodes = out;
998 hull->firstclipnode = 0;
999 hull->lastclipnode = count-1;
1000 hull->planes = loadmodel->planes;
1001 hull->clip_mins[0] = -16;
1002 hull->clip_mins[1] = -16;
1003 hull->clip_mins[2] = -36;
1004 hull->clip_maxs[0] = 16;
1005 hull->clip_maxs[1] = 16;
1006 hull->clip_maxs[2] = 36;
1008 hull = &loadmodel->hulls[2];
1009 hull->clipnodes = out;
1010 hull->firstclipnode = 0;
1011 hull->lastclipnode = count-1;
1012 hull->planes = loadmodel->planes;
1013 hull->clip_mins[0] = -32;
1014 hull->clip_mins[1] = -32;
1015 hull->clip_mins[2] = -32;
1016 hull->clip_maxs[0] = 32;
1017 hull->clip_maxs[1] = 32;
1018 hull->clip_maxs[2] = 32;
1020 hull = &loadmodel->hulls[3];
1021 hull->clipnodes = out;
1022 hull->firstclipnode = 0;
1023 hull->lastclipnode = count-1;
1024 hull->planes = loadmodel->planes;
1025 hull->clip_mins[0] = -16;
1026 hull->clip_mins[1] = -16;
1027 hull->clip_mins[2] = -18;
1028 hull->clip_maxs[0] = 16;
1029 hull->clip_maxs[1] = 16;
1030 hull->clip_maxs[2] = 18;
1034 hull = &loadmodel->hulls[1];
1035 hull->clipnodes = out;
1036 hull->firstclipnode = 0;
1037 hull->lastclipnode = count-1;
1038 hull->planes = loadmodel->planes;
1039 hull->clip_mins[0] = -16;
1040 hull->clip_mins[1] = -16;
1041 hull->clip_mins[2] = -24;
1042 hull->clip_maxs[0] = 16;
1043 hull->clip_maxs[1] = 16;
1044 hull->clip_maxs[2] = 32;
1046 hull = &loadmodel->hulls[2];
1047 hull->clipnodes = out;
1048 hull->firstclipnode = 0;
1049 hull->lastclipnode = count-1;
1050 hull->planes = loadmodel->planes;
1051 hull->clip_mins[0] = -32;
1052 hull->clip_mins[1] = -32;
1053 hull->clip_mins[2] = -24;
1054 hull->clip_maxs[0] = 32;
1055 hull->clip_maxs[1] = 32;
1056 hull->clip_maxs[2] = 64;
1059 for (i=0 ; i<count ; i++, out++, in++)
1061 out->planenum = LittleLong(in->planenum);
1062 out->children[0] = LittleShort(in->children[0]);
1063 out->children[1] = LittleShort(in->children[1]);
1064 if (out->children[0] >= count || out->children[1] >= count)
1065 Host_Error("Corrupt clipping hull (out of range child)\n");
1073 Duplicate the drawing hull structure as a clipping hull
1076 void Mod_MakeHull0 (void)
1083 hull = &loadmodel->hulls[0];
1085 in = loadmodel->nodes;
1086 count = loadmodel->numnodes;
1087 out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname));
1089 hull->clipnodes = out;
1090 hull->firstclipnode = 0;
1091 hull->lastclipnode = count - 1;
1092 hull->planes = loadmodel->planes;
1094 for (i = 0;i < count;i++, out++, in++)
1096 out->planenum = in->plane - loadmodel->planes;
1097 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1098 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1104 Mod_LoadMarksurfaces
1107 void Mod_LoadMarksurfaces (lump_t *l)
1113 in = (void *)(mod_base + l->fileofs);
1114 if (l->filelen % sizeof(*in))
1115 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1116 count = l->filelen / sizeof(*in);
1117 out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname));
1119 loadmodel->marksurfaces = out;
1120 loadmodel->nummarksurfaces = count;
1122 for ( i=0 ; i<count ; i++)
1124 j = LittleShort(in[i]);
1125 if (j >= loadmodel->numsurfaces)
1126 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1127 out[i] = loadmodel->surfaces + j;
1136 void Mod_LoadSurfedges (lump_t *l)
1141 in = (void *)(mod_base + l->fileofs);
1142 if (l->filelen % sizeof(*in))
1143 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1144 count = l->filelen / sizeof(*in);
1145 out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname));
1147 loadmodel->surfedges = out;
1148 loadmodel->numsurfedges = count;
1150 for ( i=0 ; i<count ; i++)
1151 out[i] = LittleLong (in[i]);
1160 void Mod_LoadPlanes (lump_t *l)
1168 in = (void *)(mod_base + l->fileofs);
1169 if (l->filelen % sizeof(*in))
1170 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1171 count = l->filelen / sizeof(*in);
1172 out = Hunk_AllocName ( count*2*sizeof(*out), va("%s planes", loadname));
1174 loadmodel->planes = out;
1175 loadmodel->numplanes = count;
1177 for ( i=0 ; i<count ; i++, in++, out++)
1180 for (j=0 ; j<3 ; j++)
1182 out->normal[j] = LittleFloat (in->normal[j]);
1183 // if (out->normal[j] < 0)
1187 out->dist = LittleFloat (in->dist);
1188 out->type = LittleLong (in->type);
1189 // out->signbits = bits;
1190 BoxOnPlaneSideClassify(out);
1194 #define MAX_POINTS_ON_WINDING 64
1199 vec3_t points[8]; // variable sized
1208 winding_t *NewWinding (int points)
1213 if (points > MAX_POINTS_ON_WINDING)
1214 Host_Error("NewWinding: too many points\n");
1216 size = (int)((winding_t *)0)->points[points];
1218 memset (w, 0, size);
1223 void FreeWinding (winding_t *w)
1233 winding_t *BaseWindingForPlane (mplane_t *p)
1235 vec3_t org, vright, vup;
1238 VectorVectors(p->normal, vright, vup);
1240 VectorScale (vup, 65536, vup);
1241 VectorScale (vright, 65536, vright);
1243 // project a really big axis aligned box onto the plane
1246 VectorScale (p->normal, p->dist, org);
1248 VectorSubtract (org, vright, w->points[0]);
1249 VectorAdd (w->points[0], vup, w->points[0]);
1251 VectorAdd (org, vright, w->points[1]);
1252 VectorAdd (w->points[1], vup, w->points[1]);
1254 VectorAdd (org, vright, w->points[2]);
1255 VectorSubtract (w->points[2], vup, w->points[2]);
1257 VectorSubtract (org, vright, w->points[3]);
1258 VectorSubtract (w->points[3], vup, w->points[3]);
1269 Clips the winding to the plane, returning the new winding on the positive side
1270 Frees the input winding.
1271 If keepon is true, an exactly on-plane winding will be saved, otherwise
1272 it will be clipped away.
1275 winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1277 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1278 int sides[MAX_POINTS_ON_WINDING + 1];
1287 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1289 // determine sides for each point
1290 for (i = 0;i < in->numpoints;i++)
1292 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1293 if (dot > ON_EPSILON)
1294 sides[i] = SIDE_FRONT;
1295 else if (dot < -ON_EPSILON)
1296 sides[i] = SIDE_BACK;
1301 sides[i] = sides[0];
1302 dists[i] = dists[0];
1304 if (keepon && !counts[0] && !counts[1])
1315 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1316 neww = NewWinding (maxpts);
1318 for (i = 0;i < in->numpoints;i++)
1322 if (sides[i] == SIDE_ON)
1324 VectorCopy (p1, neww->points[neww->numpoints]);
1329 if (sides[i] == SIDE_FRONT)
1331 VectorCopy (p1, neww->points[neww->numpoints]);
1335 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1338 // generate a split point
1339 p2 = in->points[(i+1)%in->numpoints];
1341 dot = dists[i] / (dists[i]-dists[i+1]);
1342 for (j = 0;j < 3;j++)
1343 { // avoid round off error when possible
1344 if (split->normal[j] == 1)
1345 mid[j] = split->dist;
1346 else if (split->normal[j] == -1)
1347 mid[j] = -split->dist;
1349 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1352 VectorCopy (mid, neww->points[neww->numpoints]);
1356 if (neww->numpoints > maxpts)
1357 Host_Error ("ClipWinding: points exceeded estimate");
1359 // free the original winding
1370 Divides a winding by a plane, producing one or two windings. The
1371 original winding is not damaged or freed. If only on one side, the
1372 returned winding will be the input winding. If on both sides, two
1373 new windings will be created.
1376 void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1378 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1379 int sides[MAX_POINTS_ON_WINDING + 1];
1388 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1390 // determine sides for each point
1391 for (i = 0;i < in->numpoints;i++)
1393 dot = DotProduct (in->points[i], split->normal);
1396 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1397 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1398 else sides[i] = SIDE_ON;
1401 sides[i] = sides[0];
1402 dists[i] = dists[0];
1404 *front = *back = NULL;
1417 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1419 *front = f = NewWinding (maxpts);
1420 *back = b = NewWinding (maxpts);
1422 for (i = 0;i < in->numpoints;i++)
1426 if (sides[i] == SIDE_ON)
1428 VectorCopy (p1, f->points[f->numpoints]);
1430 VectorCopy (p1, b->points[b->numpoints]);
1435 if (sides[i] == SIDE_FRONT)
1437 VectorCopy (p1, f->points[f->numpoints]);
1440 else if (sides[i] == SIDE_BACK)
1442 VectorCopy (p1, b->points[b->numpoints]);
1446 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1449 // generate a split point
1450 p2 = in->points[(i+1)%in->numpoints];
1452 dot = dists[i] / (dists[i]-dists[i+1]);
1453 for (j = 0;j < 3;j++)
1454 { // avoid round off error when possible
1455 if (split->normal[j] == 1)
1456 mid[j] = split->dist;
1457 else if (split->normal[j] == -1)
1458 mid[j] = -split->dist;
1460 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1463 VectorCopy (mid, f->points[f->numpoints]);
1465 VectorCopy (mid, b->points[b->numpoints]);
1469 if (f->numpoints > maxpts || b->numpoints > maxpts)
1470 Host_Error ("DivideWinding: points exceeded estimate");
1473 typedef struct portal_s
1476 mnode_t *nodes[2]; // [0] = front side of plane
1477 struct portal_s *next[2];
1479 struct portal_s *chain; // all portals are linked into a list
1483 static portal_t *portalchain;
1490 portal_t *AllocPortal (void)
1493 p = malloc(sizeof(portal_t));
1494 memset(p, 0, sizeof(portal_t));
1495 p->chain = portalchain;
1500 void Mod_FinalizePortals()
1502 int i, j, numportals, numpoints;
1503 portal_t *p, *pnext;
1506 mleaf_t *leaf, *endleaf;
1509 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1510 leaf = loadmodel->leafs;
1511 endleaf = leaf + loadmodel->numleafs;
1512 for (;leaf < endleaf;leaf++)
1514 VectorSet( 2000000000, 2000000000, 2000000000, leaf->mins);
1515 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1522 for (i = 0;i < 2;i++)
1524 leaf = (mleaf_t *)p->nodes[i];
1526 for (j = 0;j < w->numpoints;j++)
1528 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
1529 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
1530 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
1531 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
1532 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
1533 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
1540 // tally up portal and point counts
1546 if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1549 numpoints += p->winding->numpoints * 2;
1553 loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name));
1554 loadmodel->numportals = numportals;
1555 loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name));
1556 loadmodel->numportalpoints = numpoints;
1557 // clear all leaf portal chains
1558 for (i = 0;i < loadmodel->numleafs;i++)
1559 loadmodel->leafs[i].portals = NULL;
1560 // process all portals in the global portal chain, while freeing them
1561 portal = loadmodel->portals;
1562 point = loadmodel->portalpoints;
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] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1574 // first make the back to front portal (forward portal)
1575 portal->points = point;
1576 portal->numpoints = p->winding->numpoints;
1577 portal->plane.dist = p->plane.dist;
1578 VectorCopy(p->plane.normal, portal->plane.normal);
1579 portal->here = (mleaf_t *)p->nodes[1];
1580 portal->past = (mleaf_t *)p->nodes[0];
1582 for (j = 0;j < portal->numpoints;j++)
1584 VectorCopy(p->winding->points[j], point->position);
1588 // link into leaf's portal chain
1589 portal->next = portal->here->portals;
1590 portal->here->portals = portal;
1592 // advance to next portal
1595 // then make the front to back portal (backward portal)
1596 portal->points = point;
1597 portal->numpoints = p->winding->numpoints;
1598 portal->plane.dist = -p->plane.dist;
1599 VectorNegate(p->plane.normal, portal->plane.normal);
1600 portal->here = (mleaf_t *)p->nodes[0];
1601 portal->past = (mleaf_t *)p->nodes[1];
1603 for (j = portal->numpoints - 1;j >= 0;j--)
1605 VectorCopy(p->winding->points[j], point->position);
1609 // link into leaf's portal chain
1610 portal->next = portal->here->portals;
1611 portal->here->portals = portal;
1613 // advance to next portal
1616 FreeWinding(p->winding);
1628 void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
1631 Host_Error ("AddPortalToNodes: NULL front node");
1633 Host_Error ("AddPortalToNodes: NULL back node");
1634 if (p->nodes[0] || p->nodes[1])
1635 Host_Error ("AddPortalToNodes: already included");
1636 // 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
1638 p->nodes[0] = front;
1639 p->next[0] = (portal_t *)front->portals;
1640 front->portals = (mportal_t *)p;
1643 p->next[1] = (portal_t *)back->portals;
1644 back->portals = (mportal_t *)p;
1649 RemovePortalFromNode
1652 void RemovePortalFromNodes(portal_t *portal)
1656 void **portalpointer;
1658 for (i = 0;i < 2;i++)
1660 node = portal->nodes[i];
1662 portalpointer = (void **) &node->portals;
1667 Host_Error ("RemovePortalFromNodes: portal not in leaf");
1671 if (portal->nodes[0] == node)
1673 *portalpointer = portal->next[0];
1674 portal->nodes[0] = NULL;
1676 else if (portal->nodes[1] == node)
1678 *portalpointer = portal->next[1];
1679 portal->nodes[1] = NULL;
1682 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1686 if (t->nodes[0] == node)
1687 portalpointer = (void **) &t->next[0];
1688 else if (t->nodes[1] == node)
1689 portalpointer = (void **) &t->next[1];
1691 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1696 void Mod_RecursiveNodePortals (mnode_t *node)
1699 mnode_t *front, *back, *other_node;
1700 mplane_t clipplane, *plane;
1701 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
1702 winding_t *nodeportalwinding, *frontwinding, *backwinding;
1704 // CheckLeafPortalConsistancy (node);
1706 // if a leaf, we're done
1710 plane = node->plane;
1712 front = node->children[0];
1713 back = node->children[1];
1715 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
1717 // create the new portal by generating a polygon for the node plane,
1718 // and clipping it by all of the other portals (which came from nodes above this one)
1719 nodeportal = AllocPortal ();
1720 nodeportal->plane = *node->plane;
1722 nodeportalwinding = BaseWindingForPlane (node->plane);
1723 side = 0; // shut up compiler warning
1724 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
1726 clipplane = portal->plane;
1727 if (portal->nodes[0] == portal->nodes[1])
1728 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
1729 if (portal->nodes[0] == node)
1731 else if (portal->nodes[1] == node)
1733 clipplane.dist = -clipplane.dist;
1734 VectorNegate (clipplane.normal, clipplane.normal);
1738 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1740 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
1741 if (!nodeportalwinding)
1743 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
1748 if (nodeportalwinding)
1750 // if the plane was not clipped on all sides, there was an error
1751 nodeportal->winding = nodeportalwinding;
1752 AddPortalToNodes (nodeportal, front, back);
1755 // split the portals of this node along this node's plane and assign them to the children of this node
1756 // (migrating the portals downward through the tree)
1757 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
1759 if (portal->nodes[0] == portal->nodes[1])
1760 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
1761 if (portal->nodes[0] == node)
1763 else if (portal->nodes[1] == node)
1766 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1767 nextportal = portal->next[side];
1769 other_node = portal->nodes[!side];
1770 RemovePortalFromNodes (portal);
1772 // cut the portal into two portals, one on each side of the node plane
1773 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
1778 AddPortalToNodes (portal, back, other_node);
1780 AddPortalToNodes (portal, other_node, back);
1786 AddPortalToNodes (portal, front, other_node);
1788 AddPortalToNodes (portal, other_node, front);
1792 // the winding is split
1793 splitportal = AllocPortal ();
1794 temp = splitportal->chain;
1795 *splitportal = *portal;
1796 splitportal->chain = temp;
1797 splitportal->winding = backwinding;
1798 FreeWinding (portal->winding);
1799 portal->winding = frontwinding;
1803 AddPortalToNodes (portal, front, other_node);
1804 AddPortalToNodes (splitportal, back, other_node);
1808 AddPortalToNodes (portal, other_node, front);
1809 AddPortalToNodes (splitportal, other_node, back);
1813 Mod_RecursiveNodePortals(front);
1814 Mod_RecursiveNodePortals(back);
1818 void Mod_MakeOutsidePortals(mnode_t *node)
1821 portal_t *p, *portals[6];
1822 mnode_t *outside_node;
1824 outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name);
1825 outside_node->contents = CONTENTS_SOLID;
1826 outside_node->portals = NULL;
1828 for (i = 0;i < 3;i++)
1830 for (j = 0;j < 2;j++)
1832 portals[j*3 + i] = p = AllocPortal ();
1833 memset (&p->plane, 0, sizeof(mplane_t));
1834 p->plane.normal[i] = j ? -1 : 1;
1835 p->plane.dist = -65536;
1836 p->winding = BaseWindingForPlane (&p->plane);
1838 AddPortalToNodes (p, outside_node, node);
1840 AddPortalToNodes (p, node, outside_node);
1844 // clip the basewindings by all the other planes
1845 for (i = 0;i < 6;i++)
1847 for (j = 0;j < 6;j++)
1851 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
1857 void Mod_MakePortals()
1859 // Con_Printf("building portals for %s\n", loadmodel->name);
1862 // Mod_MakeOutsidePortals (loadmodel->nodes);
1863 Mod_RecursiveNodePortals (loadmodel->nodes);
1864 Mod_FinalizePortals();
1872 void Mod_LoadBrushModel (model_t *mod, void *buffer)
1878 loadmodel->type = mod_brush;
1880 header = (dheader_t *)buffer;
1882 i = LittleLong (header->version);
1883 if (i != BSPVERSION && i != 30)
1884 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
1886 halflifebsp.value = hlbsp;
1888 // swap all the lumps
1889 mod_base = (byte *)header;
1891 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
1892 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
1896 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
1897 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1899 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
1900 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
1901 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
1902 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
1903 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
1904 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
1905 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
1906 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
1907 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
1908 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
1909 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
1910 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
1911 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
1912 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1913 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
1919 mod->numframes = 2; // regular and alternate animation
1922 // set up the submodels (FIXME: this is confusing)
1924 for (i = 0;i < mod->numsubmodels;i++)
1926 bm = &mod->submodels[i];
1928 mod->hulls[0].firstclipnode = bm->headnode[0];
1929 for (j=1 ; j<MAX_MAP_HULLS ; j++)
1931 mod->hulls[j].firstclipnode = bm->headnode[j];
1932 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
1935 mod->firstmodelsurface = bm->firstface;
1936 mod->nummodelsurfaces = bm->numfaces;
1938 VectorCopy (bm->maxs, mod->maxs);
1939 VectorCopy (bm->mins, mod->mins);
1941 mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
1943 mod->numleafs = bm->visleafs;
1945 if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
1946 { // duplicate the basic information
1949 sprintf (name, "*%i", i+1);
1950 loadmodel = Mod_FindName (name);
1952 strcpy (loadmodel->name, name);