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 rtexture_t *r_notexture;
145 texture_t r_notexture_mip;
147 void Mod_SetupNoTexture(void)
152 // create a simple checkerboard texture for the default
153 // LordHavoc: redesigned this to remove reliance on the palette and texture_t
154 for (y = 0;y < 16;y++)
156 for (x = 0;x < 16;x++)
158 if ((y < 8) ^ (x < 8))
175 r_notexture = R_LoadTexture("notexture", 16, 16, &pix[0][0][0], TEXF_MIPMAP | TEXF_RGBA);
177 strcpy(r_notexture_mip.name, "notexture");
178 r_notexture_mip.width = 16;
179 r_notexture_mip.height = 16;
180 r_notexture_mip.transparent = false;
181 r_notexture_mip.texture = r_notexture;
182 r_notexture_mip.glowtexture = NULL;
190 void Mod_LoadTextures (lump_t *l)
192 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs;
194 texture_t *tx, *tx2, *anims[10], *altanims[10];
200 loadmodel->textures = NULL;
204 m = (dmiptexlump_t *)(mod_base + l->fileofs);
206 m->nummiptex = LittleLong (m->nummiptex);
208 loadmodel->numtextures = m->nummiptex;
209 loadmodel->textures = Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures), va("%s texture headers", loadname));
211 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
213 for (i = 0;i < m->nummiptex;i++)
215 dofs[i] = LittleLong(dofs[i]);
218 dmiptex = (miptex_t *)((byte *)m + dofs[i]);
219 mtwidth = LittleLong (dmiptex->width);
220 mtheight = LittleLong (dmiptex->height);
222 j = LittleLong (dmiptex->offsets[0]);
226 if (j < 40 || j + mtwidth * mtheight > l->filelen)
227 Host_Error ("Texture %s is corrupt or incomplete\n", dmiptex->name);
228 mtdata = (byte *)dmiptex + j;
231 if ((mtwidth & 15) || (mtheight & 15))
232 Host_Error ("Texture %s is not 16 aligned", dmiptex->name);
233 // LordHavoc: rewriting the map texture loader for GLQuake
234 tx = Hunk_AllocName (sizeof(texture_t), va("%s textures", loadname));
235 loadmodel->textures[i] = tx;
237 // LordHavoc: force all names to lowercase and make sure they are terminated while copying
238 for (j = 0;dmiptex->name[j] && j < 15;j++)
240 if (dmiptex->name[j] >= 'A' && dmiptex->name[j] <= 'Z')
241 tx->name[j] = dmiptex->name[j] + ('a' - 'A');
243 tx->name[j] = dmiptex->name[j];
250 Con_Printf("warning: unnamed texture in %s\n", loadname);
251 sprintf(tx->name, "unnamed%i", i);
254 tx->transparent = false;
255 data = loadimagepixels(tx->name, false, 0, 0);
258 if (!hlbsp && !strncmp(tx->name,"sky",3) && image_width == 256 && image_height == 128) // LordHavoc: HL sky textures are entirely unrelated
262 tx->transparent = false;
264 tx->glowtexture = NULL;
270 tx->height = mtheight;
271 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
272 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
273 tx->glowtexture = NULL;
281 if (mtdata) // texture included
283 data = W_ConvertWAD3Texture(dmiptex);
287 tx->height = mtheight;
288 tx->transparent = Image_CheckAlpha(data, mtwidth * mtheight, true);
289 tx->texture = R_LoadTexture (tx->name, mtwidth, mtheight, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
290 tx->glowtexture = NULL;
296 data = W_GetTexture(tx->name);
297 // get the size from the wad texture
300 tx->width = image_width;
301 tx->height = image_height;
302 tx->transparent = Image_CheckAlpha(data, image_width * image_height, true);
303 tx->texture = R_LoadTexture (tx->name, image_width, image_height, data, TEXF_MIPMAP | (tx->transparent ? TEXF_ALPHA : 0) | TEXF_RGBA | TEXF_PRECACHE);
304 tx->glowtexture = NULL;
312 tx->transparent = false;
313 tx->texture = r_notexture;
314 tx->glowtexture = NULL;
319 if (!strncmp(tx->name,"sky",3) && mtwidth == 256 && mtheight == 128)
322 tx->height = mtheight;
323 tx->transparent = false;
325 tx->glowtexture = NULL;
326 R_InitSky (mtdata, 1);
330 if (mtdata) // texture included
335 tx->height = mtheight;
336 tx->transparent = false;
338 if (r_fullbrights.value && tx->name[0] != '*')
340 for (j = 0;j < tx->width*tx->height;j++)
342 if (data[j] >= 224) // fullbright
353 data2 = qmalloc(tx->width*tx->height);
354 for (j = 0;j < tx->width*tx->height;j++)
355 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
356 tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
357 strcpy(name, tx->name);
358 strcat(name, "_glow");
359 for (j = 0;j < tx->width*tx->height;j++)
360 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
361 tx->glowtexture = R_LoadTexture (name, tx->width, tx->height, data2, TEXF_MIPMAP | TEXF_PRECACHE);
366 tx->texture = R_LoadTexture (tx->name, tx->width, tx->height, data, TEXF_MIPMAP | TEXF_PRECACHE);
367 tx->glowtexture = NULL;
370 else // no texture, and no external replacement texture was found
374 tx->transparent = false;
375 tx->texture = r_notexture;
376 tx->glowtexture = NULL;
384 // sequence the animations
386 for (i = 0;i < m->nummiptex;i++)
388 tx = loadmodel->textures[i];
389 if (!tx || tx->name[0] != '+')
392 continue; // already sequenced
394 // find the number of frames in the animation
395 memset (anims, 0, sizeof(anims));
396 memset (altanims, 0, sizeof(altanims));
400 if (max >= '0' && max <= '9')
407 else if (max >= 'a' && max <= 'j')
411 altanims[altmax] = tx;
415 Host_Error ("Bad animating texture %s", tx->name);
417 for (j = i + 1;j < m->nummiptex;j++)
419 tx2 = loadmodel->textures[j];
420 if (!tx2 || tx2->name[0] != '+')
422 if (strcmp (tx2->name+2, tx->name+2))
426 if (num >= '0' && num <= '9')
433 else if (num >= 'a' && num <= 'j')
441 Host_Error ("Bad animating texture %s", tx->name);
444 // link them all together
445 for (j = 0;j < max;j++)
449 Host_Error ("Missing frame %i of %s", j, tx->name);
450 tx2->anim_total = max;
452 tx2->alternate_anims = altanims[0];
453 for (k = 0;k < 10;k++)
454 tx2->anim_frames[k] = anims[j];
456 for (j = 0;j < altmax;j++)
460 Host_Error ("Missing frame %i of %s", j, tx->name);
461 tx2->anim_total = altmax;
463 tx2->alternate_anims = anims[0];
464 for (k = 0;k < 10;k++)
465 tx2->anim_frames[k] = altanims[j];
475 void Mod_LoadLighting (lump_t *l)
478 byte *in, *out, *data;
480 char litfilename[1024];
481 loadmodel->lightdata = NULL;
482 if (hlbsp) // LordHavoc: load the colored lighting data straight
484 loadmodel->lightdata = Hunk_AllocName ( l->filelen, va("%s lightmaps", loadname));
485 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
487 else // LordHavoc: bsp version 29 (normal white lighting)
489 // LordHavoc: hope is not lost yet, check for a .lit file to load
490 strcpy(litfilename, loadmodel->name);
491 COM_StripExtension(litfilename, litfilename);
492 strcat(litfilename, ".lit");
493 data = (byte*) COM_LoadHunkFile (litfilename, false);
496 if (data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
498 i = LittleLong(((int *)data)[1]);
501 Con_DPrintf("%s loaded", litfilename);
502 loadmodel->lightdata = data + 8;
506 Con_Printf("Unknown .lit file version (%d)\n", i);
509 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
511 // LordHavoc: oh well, expand the white lighting data
514 loadmodel->lightdata = Hunk_AllocName ( l->filelen*3, va("%s lightmaps", loadname));
515 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
516 out = loadmodel->lightdata;
517 memcpy (in, mod_base + l->fileofs, l->filelen);
518 for (i = 0;i < l->filelen;i++)
534 void Mod_LoadVisibility (lump_t *l)
538 loadmodel->visdata = NULL;
541 loadmodel->visdata = Hunk_AllocName ( l->filelen, va("%s visdata", loadname));
542 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
550 void Mod_LoadEntities (lump_t *l)
554 loadmodel->entities = NULL;
557 loadmodel->entities = Hunk_AllocName ( l->filelen, va("%s entities", loadname));
558 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
561 CL_ParseEntityLump(loadmodel->entities);
570 void Mod_LoadVertexes (lump_t *l)
576 in = (void *)(mod_base + l->fileofs);
577 if (l->filelen % sizeof(*in))
578 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
579 count = l->filelen / sizeof(*in);
580 out = Hunk_AllocName ( count*sizeof(*out), va("%s vertices", loadname));
582 loadmodel->vertexes = out;
583 loadmodel->numvertexes = count;
585 for ( i=0 ; i<count ; i++, in++, out++)
587 out->position[0] = LittleFloat (in->point[0]);
588 out->position[1] = LittleFloat (in->point[1]);
589 out->position[2] = LittleFloat (in->point[2]);
598 void Mod_LoadSubmodels (lump_t *l)
604 in = (void *)(mod_base + l->fileofs);
605 if (l->filelen % sizeof(*in))
606 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
607 count = l->filelen / sizeof(*in);
608 out = Hunk_AllocName ( count*sizeof(*out), va("%s submodels", loadname));
610 loadmodel->submodels = out;
611 loadmodel->numsubmodels = count;
613 for ( i=0 ; i<count ; i++, in++, out++)
615 for (j=0 ; j<3 ; j++)
616 { // spread the mins / maxs by a pixel
617 out->mins[j] = LittleFloat (in->mins[j]) - 1;
618 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
619 out->origin[j] = LittleFloat (in->origin[j]);
621 for (j=0 ; j<MAX_MAP_HULLS ; j++)
622 out->headnode[j] = LittleLong (in->headnode[j]);
623 out->visleafs = LittleLong (in->visleafs);
624 out->firstface = LittleLong (in->firstface);
625 out->numfaces = LittleLong (in->numfaces);
634 void Mod_LoadEdges (lump_t *l)
640 in = (void *)(mod_base + l->fileofs);
641 if (l->filelen % sizeof(*in))
642 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
643 count = l->filelen / sizeof(*in);
644 out = Hunk_AllocName ( (count + 1) * sizeof(*out), va("%s edges", loadname));
646 loadmodel->edges = out;
647 loadmodel->numedges = count;
649 for ( i=0 ; i<count ; i++, in++, out++)
651 out->v[0] = (unsigned short)LittleShort(in->v[0]);
652 out->v[1] = (unsigned short)LittleShort(in->v[1]);
661 void Mod_LoadTexinfo (lump_t *l)
668 in = (void *)(mod_base + l->fileofs);
669 if (l->filelen % sizeof(*in))
670 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
671 count = l->filelen / sizeof(*in);
672 out = Hunk_AllocName ( count*sizeof(*out), va("%s texinfo", loadname));
674 loadmodel->texinfo = out;
675 loadmodel->numtexinfo = count;
677 for ( i=0 ; i<count ; i++, in++, out++)
679 for (k=0 ; k<2 ; k++)
680 for (j=0 ; j<4 ; j++)
681 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
683 miptex = LittleLong (in->miptex);
684 out->flags = LittleLong (in->flags);
686 if (!loadmodel->textures)
688 out->texture = &r_notexture_mip; // checkerboard texture
693 if (miptex >= loadmodel->numtextures)
694 Host_Error ("miptex >= loadmodel->numtextures");
695 out->texture = loadmodel->textures[miptex];
698 out->texture = &r_notexture_mip; // checkerboard texture
709 Fills in s->texturemins[] and s->extents[]
712 void CalcSurfaceExtents (msurface_t *s)
714 float mins[2], maxs[2], val;
718 int bmins[2], bmaxs[2];
720 mins[0] = mins[1] = 999999;
721 maxs[0] = maxs[1] = -99999;
725 for (i=0 ; i<s->numedges ; i++)
727 e = loadmodel->surfedges[s->firstedge+i];
729 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
731 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
733 for (j=0 ; j<2 ; j++)
735 val = v->position[0] * tex->vecs[j][0] +
736 v->position[1] * tex->vecs[j][1] +
737 v->position[2] * tex->vecs[j][2] +
746 for (i=0 ; i<2 ; i++)
748 bmins[i] = floor(mins[i]/16);
749 bmaxs[i] = ceil(maxs[i]/16);
751 s->texturemins[i] = bmins[i] * 16;
752 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
753 // if ( !(tex->flags & TEX_SPECIAL) && s->extents[i] > 512)
754 if ((tex->flags & TEX_SPECIAL) == 0 && (s->extents[i]+1) > (256*16))
755 Host_Error ("Bad surface extents");
759 void GL_SubdivideSurface (msurface_t *fa);
766 void Mod_LoadFaces (lump_t *l)
770 int i, count, surfnum;
773 in = (void *)(mod_base + l->fileofs);
774 if (l->filelen % sizeof(*in))
775 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
776 count = l->filelen / sizeof(*in);
777 out = Hunk_AllocName ( count*sizeof(*out), va("%s faces", loadname));
779 loadmodel->surfaces = out;
780 loadmodel->numsurfaces = count;
782 for ( surfnum=0 ; surfnum<count ; surfnum++, in++, out++)
784 out->firstedge = LittleLong(in->firstedge);
785 out->numedges = LittleShort(in->numedges);
788 planenum = LittleShort(in->planenum);
789 side = LittleShort(in->side);
791 out->flags |= SURF_PLANEBACK;
793 out->plane = loadmodel->planes + planenum;
795 out->texinfo = loadmodel->texinfo + LittleShort (in->texinfo);
797 CalcSurfaceExtents (out);
801 for (i=0 ; i<MAXLIGHTMAPS ; i++)
802 out->styles[i] = in->styles[i];
803 i = LittleLong(in->lightofs);
806 else if (hlbsp) // LordHavoc: HalfLife map (bsp version 30)
807 out->samples = loadmodel->lightdata + i;
808 else // LordHavoc: white lighting (bsp version 29)
809 out->samples = loadmodel->lightdata + (i * 3);
811 // set the drawing flags flag
813 // if (!strncmp(out->texinfo->texture->name,"sky",3)) // sky
814 // LordHavoc: faster check
815 if ((out->texinfo->texture->name[0] == 's' || out->texinfo->texture->name[0] == 'S')
816 && (out->texinfo->texture->name[1] == 'k' || out->texinfo->texture->name[1] == 'K')
817 && (out->texinfo->texture->name[2] == 'y' || out->texinfo->texture->name[2] == 'Y'))
819 // LordHavoc: for consistency reasons, mark sky as fullbright and solid as well
820 out->flags |= (SURF_DRAWSKY | SURF_DRAWTILED | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
821 GL_SubdivideSurface (out); // cut up polygon for warps
825 // if (!strncmp(out->texinfo->texture->name,"*",1)) // turbulent
826 if (out->texinfo->texture->name[0] == '*') // LordHavoc: faster check
828 out->flags |= (SURF_DRAWTURB | SURF_DRAWTILED | SURF_LIGHTBOTHSIDES);
829 // LordHavoc: some turbulent textures should be fullbright and solid
830 if (!strncmp(out->texinfo->texture->name,"*lava",5)
831 || !strncmp(out->texinfo->texture->name,"*teleport",9)
832 || !strncmp(out->texinfo->texture->name,"*rift",5)) // Scourge of Armagon texture
833 out->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA);
834 for (i=0 ; i<2 ; i++)
836 out->extents[i] = 16384;
837 out->texturemins[i] = -8192;
839 GL_SubdivideSurface (out); // cut up polygon for warps
852 void Mod_SetParent (mnode_t *node, mnode_t *parent)
854 node->parent = parent;
855 if (node->contents < 0)
857 Mod_SetParent (node->children[0], node);
858 Mod_SetParent (node->children[1], node);
866 void Mod_LoadNodes (lump_t *l)
872 in = (void *)(mod_base + l->fileofs);
873 if (l->filelen % sizeof(*in))
874 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
875 count = l->filelen / sizeof(*in);
876 out = Hunk_AllocName ( count*sizeof(*out), va("%s nodes", loadname));
878 loadmodel->nodes = out;
879 loadmodel->numnodes = count;
881 for ( i=0 ; i<count ; i++, in++, out++)
883 // for (j=0 ; j<3 ; j++)
885 // out->mins[j] = LittleShort (in->mins[j]);
886 // out->maxs[j] = LittleShort (in->maxs[j]);
889 p = LittleLong(in->planenum);
890 out->plane = loadmodel->planes + p;
892 out->firstsurface = LittleShort (in->firstface);
893 out->numsurfaces = LittleShort (in->numfaces);
895 for (j=0 ; j<2 ; j++)
897 p = LittleShort (in->children[j]);
899 out->children[j] = loadmodel->nodes + p;
901 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
905 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
913 void Mod_LoadLeafs (lump_t *l)
919 in = (void *)(mod_base + l->fileofs);
920 if (l->filelen % sizeof(*in))
921 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
922 count = l->filelen / sizeof(*in);
923 out = Hunk_AllocName ( count*sizeof(*out), va("%s leafs", loadname));
925 loadmodel->leafs = out;
926 loadmodel->numleafs = count;
928 for ( i=0 ; i<count ; i++, in++, out++)
930 for (j=0 ; j<3 ; j++)
932 out->mins[j] = LittleShort (in->mins[j]);
933 out->maxs[j] = LittleShort (in->maxs[j]);
936 p = LittleLong(in->contents);
939 out->firstmarksurface = loadmodel->marksurfaces +
940 LittleShort(in->firstmarksurface);
941 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
943 p = LittleLong(in->visofs);
945 out->compressed_vis = NULL;
947 out->compressed_vis = loadmodel->visdata + p;
949 for (j=0 ; j<4 ; j++)
950 out->ambient_sound_level[j] = in->ambient_level[j];
952 // gl underwater warp
953 // LordHavoc: disabled underwater warping
955 if (out->contents != CONTENTS_EMPTY)
957 for (j=0 ; j<out->nummarksurfaces ; j++)
958 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
969 void Mod_LoadClipnodes (lump_t *l)
971 dclipnode_t *in, *out;
975 in = (void *)(mod_base + l->fileofs);
976 if (l->filelen % sizeof(*in))
977 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
978 count = l->filelen / sizeof(*in);
979 out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname));
981 loadmodel->clipnodes = out;
982 loadmodel->numclipnodes = count;
986 hull = &loadmodel->hulls[1];
987 hull->clipnodes = out;
988 hull->firstclipnode = 0;
989 hull->lastclipnode = count-1;
990 hull->planes = loadmodel->planes;
991 hull->clip_mins[0] = -16;
992 hull->clip_mins[1] = -16;
993 hull->clip_mins[2] = -36;
994 hull->clip_maxs[0] = 16;
995 hull->clip_maxs[1] = 16;
996 hull->clip_maxs[2] = 36;
998 hull = &loadmodel->hulls[2];
999 hull->clipnodes = out;
1000 hull->firstclipnode = 0;
1001 hull->lastclipnode = count-1;
1002 hull->planes = loadmodel->planes;
1003 hull->clip_mins[0] = -32;
1004 hull->clip_mins[1] = -32;
1005 hull->clip_mins[2] = -32;
1006 hull->clip_maxs[0] = 32;
1007 hull->clip_maxs[1] = 32;
1008 hull->clip_maxs[2] = 32;
1010 hull = &loadmodel->hulls[3];
1011 hull->clipnodes = out;
1012 hull->firstclipnode = 0;
1013 hull->lastclipnode = count-1;
1014 hull->planes = loadmodel->planes;
1015 hull->clip_mins[0] = -16;
1016 hull->clip_mins[1] = -16;
1017 hull->clip_mins[2] = -18;
1018 hull->clip_maxs[0] = 16;
1019 hull->clip_maxs[1] = 16;
1020 hull->clip_maxs[2] = 18;
1024 hull = &loadmodel->hulls[1];
1025 hull->clipnodes = out;
1026 hull->firstclipnode = 0;
1027 hull->lastclipnode = count-1;
1028 hull->planes = loadmodel->planes;
1029 hull->clip_mins[0] = -16;
1030 hull->clip_mins[1] = -16;
1031 hull->clip_mins[2] = -24;
1032 hull->clip_maxs[0] = 16;
1033 hull->clip_maxs[1] = 16;
1034 hull->clip_maxs[2] = 32;
1036 hull = &loadmodel->hulls[2];
1037 hull->clipnodes = out;
1038 hull->firstclipnode = 0;
1039 hull->lastclipnode = count-1;
1040 hull->planes = loadmodel->planes;
1041 hull->clip_mins[0] = -32;
1042 hull->clip_mins[1] = -32;
1043 hull->clip_mins[2] = -24;
1044 hull->clip_maxs[0] = 32;
1045 hull->clip_maxs[1] = 32;
1046 hull->clip_maxs[2] = 64;
1049 for (i=0 ; i<count ; i++, out++, in++)
1051 out->planenum = LittleLong(in->planenum);
1052 out->children[0] = LittleShort(in->children[0]);
1053 out->children[1] = LittleShort(in->children[1]);
1054 if (out->children[0] >= count || out->children[1] >= count)
1055 Host_Error("Corrupt clipping hull (out of range child)\n");
1063 Duplicate the drawing hull structure as a clipping hull
1066 void Mod_MakeHull0 (void)
1073 hull = &loadmodel->hulls[0];
1075 in = loadmodel->nodes;
1076 count = loadmodel->numnodes;
1077 out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname));
1079 hull->clipnodes = out;
1080 hull->firstclipnode = 0;
1081 hull->lastclipnode = count - 1;
1082 hull->planes = loadmodel->planes;
1084 for (i = 0;i < count;i++, out++, in++)
1086 out->planenum = in->plane - loadmodel->planes;
1087 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1088 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1094 Mod_LoadMarksurfaces
1097 void Mod_LoadMarksurfaces (lump_t *l)
1103 in = (void *)(mod_base + l->fileofs);
1104 if (l->filelen % sizeof(*in))
1105 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1106 count = l->filelen / sizeof(*in);
1107 out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname));
1109 loadmodel->marksurfaces = out;
1110 loadmodel->nummarksurfaces = count;
1112 for ( i=0 ; i<count ; i++)
1114 j = LittleShort(in[i]);
1115 if (j >= loadmodel->numsurfaces)
1116 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1117 out[i] = loadmodel->surfaces + j;
1126 void Mod_LoadSurfedges (lump_t *l)
1131 in = (void *)(mod_base + l->fileofs);
1132 if (l->filelen % sizeof(*in))
1133 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1134 count = l->filelen / sizeof(*in);
1135 out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname));
1137 loadmodel->surfedges = out;
1138 loadmodel->numsurfedges = count;
1140 for ( i=0 ; i<count ; i++)
1141 out[i] = LittleLong (in[i]);
1150 void Mod_LoadPlanes (lump_t *l)
1158 in = (void *)(mod_base + l->fileofs);
1159 if (l->filelen % sizeof(*in))
1160 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1161 count = l->filelen / sizeof(*in);
1162 out = Hunk_AllocName ( count*2*sizeof(*out), va("%s planes", loadname));
1164 loadmodel->planes = out;
1165 loadmodel->numplanes = count;
1167 for ( i=0 ; i<count ; i++, in++, out++)
1170 for (j=0 ; j<3 ; j++)
1172 out->normal[j] = LittleFloat (in->normal[j]);
1173 // if (out->normal[j] < 0)
1177 out->dist = LittleFloat (in->dist);
1178 out->type = LittleLong (in->type);
1179 // out->signbits = bits;
1180 BoxOnPlaneSideClassify(out);
1184 #define MAX_POINTS_ON_WINDING 64
1189 vec3_t points[8]; // variable sized
1198 winding_t *NewWinding (int points)
1203 if (points > MAX_POINTS_ON_WINDING)
1204 Host_Error("NewWinding: too many points\n");
1206 size = (int)((winding_t *)0)->points[points];
1208 memset (w, 0, size);
1213 void FreeWinding (winding_t *w)
1223 winding_t *BaseWindingForPlane (mplane_t *p)
1225 vec3_t org, vright, vup;
1228 VectorVectors(p->normal, vright, vup);
1230 VectorScale (vup, 65536, vup);
1231 VectorScale (vright, 65536, vright);
1233 // project a really big axis aligned box onto the plane
1236 VectorScale (p->normal, p->dist, org);
1238 VectorSubtract (org, vright, w->points[0]);
1239 VectorAdd (w->points[0], vup, w->points[0]);
1241 VectorAdd (org, vright, w->points[1]);
1242 VectorAdd (w->points[1], vup, w->points[1]);
1244 VectorAdd (org, vright, w->points[2]);
1245 VectorSubtract (w->points[2], vup, w->points[2]);
1247 VectorSubtract (org, vright, w->points[3]);
1248 VectorSubtract (w->points[3], vup, w->points[3]);
1259 Clips the winding to the plane, returning the new winding on the positive side
1260 Frees the input winding.
1261 If keepon is true, an exactly on-plane winding will be saved, otherwise
1262 it will be clipped away.
1265 winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1267 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1268 int sides[MAX_POINTS_ON_WINDING + 1];
1277 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1279 // determine sides for each point
1280 for (i = 0;i < in->numpoints;i++)
1282 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1283 if (dot > ON_EPSILON)
1284 sides[i] = SIDE_FRONT;
1285 else if (dot < -ON_EPSILON)
1286 sides[i] = SIDE_BACK;
1291 sides[i] = sides[0];
1292 dists[i] = dists[0];
1294 if (keepon && !counts[0] && !counts[1])
1305 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1306 neww = NewWinding (maxpts);
1308 for (i = 0;i < in->numpoints;i++)
1312 if (sides[i] == SIDE_ON)
1314 VectorCopy (p1, neww->points[neww->numpoints]);
1319 if (sides[i] == SIDE_FRONT)
1321 VectorCopy (p1, neww->points[neww->numpoints]);
1325 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1328 // generate a split point
1329 p2 = in->points[(i+1)%in->numpoints];
1331 dot = dists[i] / (dists[i]-dists[i+1]);
1332 for (j = 0;j < 3;j++)
1333 { // avoid round off error when possible
1334 if (split->normal[j] == 1)
1335 mid[j] = split->dist;
1336 else if (split->normal[j] == -1)
1337 mid[j] = -split->dist;
1339 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1342 VectorCopy (mid, neww->points[neww->numpoints]);
1346 if (neww->numpoints > maxpts)
1347 Host_Error ("ClipWinding: points exceeded estimate");
1349 // free the original winding
1360 Divides a winding by a plane, producing one or two windings. The
1361 original winding is not damaged or freed. If only on one side, the
1362 returned winding will be the input winding. If on both sides, two
1363 new windings will be created.
1366 void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1368 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1369 int sides[MAX_POINTS_ON_WINDING + 1];
1378 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1380 // determine sides for each point
1381 for (i = 0;i < in->numpoints;i++)
1383 dot = DotProduct (in->points[i], split->normal);
1386 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1387 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1388 else sides[i] = SIDE_ON;
1391 sides[i] = sides[0];
1392 dists[i] = dists[0];
1394 *front = *back = NULL;
1407 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1409 *front = f = NewWinding (maxpts);
1410 *back = b = NewWinding (maxpts);
1412 for (i = 0;i < in->numpoints;i++)
1416 if (sides[i] == SIDE_ON)
1418 VectorCopy (p1, f->points[f->numpoints]);
1420 VectorCopy (p1, b->points[b->numpoints]);
1425 if (sides[i] == SIDE_FRONT)
1427 VectorCopy (p1, f->points[f->numpoints]);
1430 else if (sides[i] == SIDE_BACK)
1432 VectorCopy (p1, b->points[b->numpoints]);
1436 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1439 // generate a split point
1440 p2 = in->points[(i+1)%in->numpoints];
1442 dot = dists[i] / (dists[i]-dists[i+1]);
1443 for (j = 0;j < 3;j++)
1444 { // avoid round off error when possible
1445 if (split->normal[j] == 1)
1446 mid[j] = split->dist;
1447 else if (split->normal[j] == -1)
1448 mid[j] = -split->dist;
1450 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1453 VectorCopy (mid, f->points[f->numpoints]);
1455 VectorCopy (mid, b->points[b->numpoints]);
1459 if (f->numpoints > maxpts || b->numpoints > maxpts)
1460 Host_Error ("DivideWinding: points exceeded estimate");
1463 typedef struct portal_s
1466 mnode_t *nodes[2]; // [0] = front side of plane
1467 struct portal_s *next[2];
1469 struct portal_s *chain; // all portals are linked into a list
1473 static portal_t *portalchain;
1480 portal_t *AllocPortal (void)
1483 p = malloc(sizeof(portal_t));
1484 memset(p, 0, sizeof(portal_t));
1485 p->chain = portalchain;
1490 void Mod_FinalizePortals(void)
1492 int i, j, numportals, numpoints;
1493 portal_t *p, *pnext;
1496 mleaf_t *leaf, *endleaf;
1499 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1500 leaf = loadmodel->leafs;
1501 endleaf = leaf + loadmodel->numleafs;
1502 for (;leaf < endleaf;leaf++)
1504 VectorSet( 2000000000, 2000000000, 2000000000, leaf->mins);
1505 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1512 for (i = 0;i < 2;i++)
1514 leaf = (mleaf_t *)p->nodes[i];
1516 for (j = 0;j < w->numpoints;j++)
1518 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
1519 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
1520 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
1521 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
1522 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
1523 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
1530 // tally up portal and point counts
1536 if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1539 numpoints += p->winding->numpoints * 2;
1543 loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name));
1544 loadmodel->numportals = numportals;
1545 loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name));
1546 loadmodel->numportalpoints = numpoints;
1547 // clear all leaf portal chains
1548 for (i = 0;i < loadmodel->numleafs;i++)
1549 loadmodel->leafs[i].portals = NULL;
1550 // process all portals in the global portal chain, while freeing them
1551 portal = loadmodel->portals;
1552 point = loadmodel->portalpoints;
1561 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
1562 if (p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1564 // first make the back to front portal (forward portal)
1565 portal->points = point;
1566 portal->numpoints = p->winding->numpoints;
1567 portal->plane.dist = p->plane.dist;
1568 VectorCopy(p->plane.normal, portal->plane.normal);
1569 portal->here = (mleaf_t *)p->nodes[1];
1570 portal->past = (mleaf_t *)p->nodes[0];
1572 for (j = 0;j < portal->numpoints;j++)
1574 VectorCopy(p->winding->points[j], point->position);
1578 // link into leaf's portal chain
1579 portal->next = portal->here->portals;
1580 portal->here->portals = portal;
1582 // advance to next portal
1585 // then make the front to back portal (backward portal)
1586 portal->points = point;
1587 portal->numpoints = p->winding->numpoints;
1588 portal->plane.dist = -p->plane.dist;
1589 VectorNegate(p->plane.normal, portal->plane.normal);
1590 portal->here = (mleaf_t *)p->nodes[0];
1591 portal->past = (mleaf_t *)p->nodes[1];
1593 for (j = portal->numpoints - 1;j >= 0;j--)
1595 VectorCopy(p->winding->points[j], point->position);
1599 // link into leaf's portal chain
1600 portal->next = portal->here->portals;
1601 portal->here->portals = portal;
1603 // advance to next portal
1606 FreeWinding(p->winding);
1618 void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
1621 Host_Error ("AddPortalToNodes: NULL front node");
1623 Host_Error ("AddPortalToNodes: NULL back node");
1624 if (p->nodes[0] || p->nodes[1])
1625 Host_Error ("AddPortalToNodes: already included");
1626 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
1628 p->nodes[0] = front;
1629 p->next[0] = (portal_t *)front->portals;
1630 front->portals = (mportal_t *)p;
1633 p->next[1] = (portal_t *)back->portals;
1634 back->portals = (mportal_t *)p;
1639 RemovePortalFromNode
1642 void RemovePortalFromNodes(portal_t *portal)
1646 void **portalpointer;
1648 for (i = 0;i < 2;i++)
1650 node = portal->nodes[i];
1652 portalpointer = (void **) &node->portals;
1657 Host_Error ("RemovePortalFromNodes: portal not in leaf");
1661 if (portal->nodes[0] == node)
1663 *portalpointer = portal->next[0];
1664 portal->nodes[0] = NULL;
1666 else if (portal->nodes[1] == node)
1668 *portalpointer = portal->next[1];
1669 portal->nodes[1] = NULL;
1672 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1676 if (t->nodes[0] == node)
1677 portalpointer = (void **) &t->next[0];
1678 else if (t->nodes[1] == node)
1679 portalpointer = (void **) &t->next[1];
1681 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1686 void Mod_RecursiveNodePortals (mnode_t *node)
1689 mnode_t *front, *back, *other_node;
1690 mplane_t clipplane, *plane;
1691 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
1692 winding_t *nodeportalwinding, *frontwinding, *backwinding;
1694 // CheckLeafPortalConsistancy (node);
1696 // if a leaf, we're done
1700 plane = node->plane;
1702 front = node->children[0];
1703 back = node->children[1];
1705 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
1707 // create the new portal by generating a polygon for the node plane,
1708 // and clipping it by all of the other portals (which came from nodes above this one)
1709 nodeportal = AllocPortal ();
1710 nodeportal->plane = *node->plane;
1712 nodeportalwinding = BaseWindingForPlane (node->plane);
1713 side = 0; // shut up compiler warning
1714 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
1716 clipplane = portal->plane;
1717 if (portal->nodes[0] == portal->nodes[1])
1718 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
1719 if (portal->nodes[0] == node)
1721 else if (portal->nodes[1] == node)
1723 clipplane.dist = -clipplane.dist;
1724 VectorNegate (clipplane.normal, clipplane.normal);
1728 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1730 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
1731 if (!nodeportalwinding)
1733 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
1738 if (nodeportalwinding)
1740 // if the plane was not clipped on all sides, there was an error
1741 nodeportal->winding = nodeportalwinding;
1742 AddPortalToNodes (nodeportal, front, back);
1745 // split the portals of this node along this node's plane and assign them to the children of this node
1746 // (migrating the portals downward through the tree)
1747 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
1749 if (portal->nodes[0] == portal->nodes[1])
1750 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
1751 if (portal->nodes[0] == node)
1753 else if (portal->nodes[1] == node)
1756 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1757 nextportal = portal->next[side];
1759 other_node = portal->nodes[!side];
1760 RemovePortalFromNodes (portal);
1762 // cut the portal into two portals, one on each side of the node plane
1763 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
1768 AddPortalToNodes (portal, back, other_node);
1770 AddPortalToNodes (portal, other_node, back);
1776 AddPortalToNodes (portal, front, other_node);
1778 AddPortalToNodes (portal, other_node, front);
1782 // the winding is split
1783 splitportal = AllocPortal ();
1784 temp = splitportal->chain;
1785 *splitportal = *portal;
1786 splitportal->chain = temp;
1787 splitportal->winding = backwinding;
1788 FreeWinding (portal->winding);
1789 portal->winding = frontwinding;
1793 AddPortalToNodes (portal, front, other_node);
1794 AddPortalToNodes (splitportal, back, other_node);
1798 AddPortalToNodes (portal, other_node, front);
1799 AddPortalToNodes (splitportal, other_node, back);
1803 Mod_RecursiveNodePortals(front);
1804 Mod_RecursiveNodePortals(back);
1808 void Mod_MakeOutsidePortals(mnode_t *node)
1811 portal_t *p, *portals[6];
1812 mnode_t *outside_node;
1814 outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name);
1815 outside_node->contents = CONTENTS_SOLID;
1816 outside_node->portals = NULL;
1818 for (i = 0;i < 3;i++)
1820 for (j = 0;j < 2;j++)
1822 portals[j*3 + i] = p = AllocPortal ();
1823 memset (&p->plane, 0, sizeof(mplane_t));
1824 p->plane.normal[i] = j ? -1 : 1;
1825 p->plane.dist = -65536;
1826 p->winding = BaseWindingForPlane (&p->plane);
1828 AddPortalToNodes (p, outside_node, node);
1830 AddPortalToNodes (p, node, outside_node);
1834 // clip the basewindings by all the other planes
1835 for (i = 0;i < 6;i++)
1837 for (j = 0;j < 6;j++)
1841 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
1847 void Mod_MakePortals(void)
1849 // Con_Printf("building portals for %s\n", loadmodel->name);
1852 // Mod_MakeOutsidePortals (loadmodel->nodes);
1853 Mod_RecursiveNodePortals (loadmodel->nodes);
1854 Mod_FinalizePortals();
1862 void Mod_LoadBrushModel (model_t *mod, void *buffer)
1868 loadmodel->type = mod_brush;
1870 header = (dheader_t *)buffer;
1872 i = LittleLong (header->version);
1873 if (i != BSPVERSION && i != 30)
1874 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
1876 halflifebsp.value = hlbsp;
1878 // swap all the lumps
1879 mod_base = (byte *)header;
1881 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
1882 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
1886 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
1887 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1889 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
1890 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
1891 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
1892 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
1893 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
1894 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
1895 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
1896 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
1897 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
1898 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
1899 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
1900 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
1901 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
1902 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1903 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
1909 mod->numframes = 2; // regular and alternate animation
1912 // set up the submodels (FIXME: this is confusing)
1914 for (i = 0;i < mod->numsubmodels;i++)
1916 bm = &mod->submodels[i];
1918 mod->hulls[0].firstclipnode = bm->headnode[0];
1919 for (j=1 ; j<MAX_MAP_HULLS ; j++)
1921 mod->hulls[j].firstclipnode = bm->headnode[j];
1922 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
1925 mod->firstmodelsurface = bm->firstface;
1926 mod->nummodelsurfaces = bm->numfaces;
1928 VectorCopy (bm->maxs, mod->maxs);
1929 VectorCopy (bm->mins, mod->mins);
1931 mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
1933 mod->numleafs = bm->visleafs;
1935 if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
1936 { // duplicate the basic information
1939 sprintf (name, "*%i", i+1);
1940 loadmodel = Mod_FindName (name);
1942 strcpy (loadmodel->name, name);