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 | SURF_CLIPSOLID);
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
843 out->flags |= SURF_CLIPSOLID;
853 void Mod_SetParent (mnode_t *node, mnode_t *parent)
855 node->parent = parent;
856 if (node->contents < 0)
858 Mod_SetParent (node->children[0], node);
859 Mod_SetParent (node->children[1], node);
867 void Mod_LoadNodes (lump_t *l)
873 in = (void *)(mod_base + l->fileofs);
874 if (l->filelen % sizeof(*in))
875 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
876 count = l->filelen / sizeof(*in);
877 out = Hunk_AllocName ( count*sizeof(*out), va("%s nodes", loadname));
879 loadmodel->nodes = out;
880 loadmodel->numnodes = count;
882 for ( i=0 ; i<count ; i++, in++, out++)
884 // for (j=0 ; j<3 ; j++)
886 // out->mins[j] = LittleShort (in->mins[j]);
887 // out->maxs[j] = LittleShort (in->maxs[j]);
890 p = LittleLong(in->planenum);
891 out->plane = loadmodel->planes + p;
893 out->firstsurface = LittleShort (in->firstface);
894 out->numsurfaces = LittleShort (in->numfaces);
896 for (j=0 ; j<2 ; j++)
898 p = LittleShort (in->children[j]);
900 out->children[j] = loadmodel->nodes + p;
902 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
906 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
914 void Mod_LoadLeafs (lump_t *l)
920 in = (void *)(mod_base + l->fileofs);
921 if (l->filelen % sizeof(*in))
922 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
923 count = l->filelen / sizeof(*in);
924 out = Hunk_AllocName ( count*sizeof(*out), va("%s leafs", loadname));
926 loadmodel->leafs = out;
927 loadmodel->numleafs = count;
929 for ( i=0 ; i<count ; i++, in++, out++)
931 for (j=0 ; j<3 ; j++)
933 out->mins[j] = LittleShort (in->mins[j]);
934 out->maxs[j] = LittleShort (in->maxs[j]);
937 p = LittleLong(in->contents);
940 out->firstmarksurface = loadmodel->marksurfaces +
941 LittleShort(in->firstmarksurface);
942 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
944 p = LittleLong(in->visofs);
946 out->compressed_vis = NULL;
948 out->compressed_vis = loadmodel->visdata + p;
950 for (j=0 ; j<4 ; j++)
951 out->ambient_sound_level[j] = in->ambient_level[j];
953 // gl underwater warp
954 // LordHavoc: disabled underwater warping
956 if (out->contents != CONTENTS_EMPTY)
958 for (j=0 ; j<out->nummarksurfaces ; j++)
959 out->firstmarksurface[j]->flags |= SURF_UNDERWATER;
970 void Mod_LoadClipnodes (lump_t *l)
972 dclipnode_t *in, *out;
976 in = (void *)(mod_base + l->fileofs);
977 if (l->filelen % sizeof(*in))
978 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
979 count = l->filelen / sizeof(*in);
980 out = Hunk_AllocName ( count*sizeof(*out), va("%s clipnodes", loadname));
982 loadmodel->clipnodes = out;
983 loadmodel->numclipnodes = count;
987 hull = &loadmodel->hulls[1];
988 hull->clipnodes = out;
989 hull->firstclipnode = 0;
990 hull->lastclipnode = count-1;
991 hull->planes = loadmodel->planes;
992 hull->clip_mins[0] = -16;
993 hull->clip_mins[1] = -16;
994 hull->clip_mins[2] = -36;
995 hull->clip_maxs[0] = 16;
996 hull->clip_maxs[1] = 16;
997 hull->clip_maxs[2] = 36;
999 hull = &loadmodel->hulls[2];
1000 hull->clipnodes = out;
1001 hull->firstclipnode = 0;
1002 hull->lastclipnode = count-1;
1003 hull->planes = loadmodel->planes;
1004 hull->clip_mins[0] = -32;
1005 hull->clip_mins[1] = -32;
1006 hull->clip_mins[2] = -32;
1007 hull->clip_maxs[0] = 32;
1008 hull->clip_maxs[1] = 32;
1009 hull->clip_maxs[2] = 32;
1011 hull = &loadmodel->hulls[3];
1012 hull->clipnodes = out;
1013 hull->firstclipnode = 0;
1014 hull->lastclipnode = count-1;
1015 hull->planes = loadmodel->planes;
1016 hull->clip_mins[0] = -16;
1017 hull->clip_mins[1] = -16;
1018 hull->clip_mins[2] = -18;
1019 hull->clip_maxs[0] = 16;
1020 hull->clip_maxs[1] = 16;
1021 hull->clip_maxs[2] = 18;
1025 hull = &loadmodel->hulls[1];
1026 hull->clipnodes = out;
1027 hull->firstclipnode = 0;
1028 hull->lastclipnode = count-1;
1029 hull->planes = loadmodel->planes;
1030 hull->clip_mins[0] = -16;
1031 hull->clip_mins[1] = -16;
1032 hull->clip_mins[2] = -24;
1033 hull->clip_maxs[0] = 16;
1034 hull->clip_maxs[1] = 16;
1035 hull->clip_maxs[2] = 32;
1037 hull = &loadmodel->hulls[2];
1038 hull->clipnodes = out;
1039 hull->firstclipnode = 0;
1040 hull->lastclipnode = count-1;
1041 hull->planes = loadmodel->planes;
1042 hull->clip_mins[0] = -32;
1043 hull->clip_mins[1] = -32;
1044 hull->clip_mins[2] = -24;
1045 hull->clip_maxs[0] = 32;
1046 hull->clip_maxs[1] = 32;
1047 hull->clip_maxs[2] = 64;
1050 for (i=0 ; i<count ; i++, out++, in++)
1052 out->planenum = LittleLong(in->planenum);
1053 out->children[0] = LittleShort(in->children[0]);
1054 out->children[1] = LittleShort(in->children[1]);
1055 if (out->children[0] >= count || out->children[1] >= count)
1056 Host_Error("Corrupt clipping hull (out of range child)\n");
1064 Duplicate the drawing hull structure as a clipping hull
1067 void Mod_MakeHull0 (void)
1074 hull = &loadmodel->hulls[0];
1076 in = loadmodel->nodes;
1077 count = loadmodel->numnodes;
1078 out = Hunk_AllocName ( count*sizeof(*out), va("%s hull0", loadname));
1080 hull->clipnodes = out;
1081 hull->firstclipnode = 0;
1082 hull->lastclipnode = count - 1;
1083 hull->planes = loadmodel->planes;
1085 for (i = 0;i < count;i++, out++, in++)
1087 out->planenum = in->plane - loadmodel->planes;
1088 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1089 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1095 Mod_LoadMarksurfaces
1098 void Mod_LoadMarksurfaces (lump_t *l)
1104 in = (void *)(mod_base + l->fileofs);
1105 if (l->filelen % sizeof(*in))
1106 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1107 count = l->filelen / sizeof(*in);
1108 out = Hunk_AllocName ( count*sizeof(*out), va("%s marksurfaces", loadname));
1110 loadmodel->marksurfaces = out;
1111 loadmodel->nummarksurfaces = count;
1113 for ( i=0 ; i<count ; i++)
1115 j = LittleShort(in[i]);
1116 if (j >= loadmodel->numsurfaces)
1117 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1118 out[i] = loadmodel->surfaces + j;
1127 void Mod_LoadSurfedges (lump_t *l)
1132 in = (void *)(mod_base + l->fileofs);
1133 if (l->filelen % sizeof(*in))
1134 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1135 count = l->filelen / sizeof(*in);
1136 out = Hunk_AllocName ( count*sizeof(*out), va("%s surfedges", loadname));
1138 loadmodel->surfedges = out;
1139 loadmodel->numsurfedges = count;
1141 for ( i=0 ; i<count ; i++)
1142 out[i] = LittleLong (in[i]);
1151 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++)
1169 for (j=0 ; j<3 ; j++)
1170 out->normal[j] = LittleFloat (in->normal[j]);
1172 out->dist = LittleFloat (in->dist);
1173 // LordHavoc: recalculated by PlaneClassify, FIXME: validate type and report error if type does not match normal?
1174 // out->type = LittleLong (in->type);
1179 #define MAX_POINTS_ON_WINDING 64
1184 vec3_t points[8]; // variable sized
1193 winding_t *NewWinding (int points)
1198 if (points > MAX_POINTS_ON_WINDING)
1199 Host_Error("NewWinding: too many points\n");
1201 size = (int)((winding_t *)0)->points[points];
1203 memset (w, 0, size);
1208 void FreeWinding (winding_t *w)
1218 winding_t *BaseWindingForPlane (mplane_t *p)
1220 vec3_t org, vright, vup;
1223 VectorVectors(p->normal, vright, vup);
1225 VectorScale (vup, 65536, vup);
1226 VectorScale (vright, 65536, vright);
1228 // project a really big axis aligned box onto the plane
1231 VectorScale (p->normal, p->dist, org);
1233 VectorSubtract (org, vright, w->points[0]);
1234 VectorAdd (w->points[0], vup, w->points[0]);
1236 VectorAdd (org, vright, w->points[1]);
1237 VectorAdd (w->points[1], vup, w->points[1]);
1239 VectorAdd (org, vright, w->points[2]);
1240 VectorSubtract (w->points[2], vup, w->points[2]);
1242 VectorSubtract (org, vright, w->points[3]);
1243 VectorSubtract (w->points[3], vup, w->points[3]);
1254 Clips the winding to the plane, returning the new winding on the positive side
1255 Frees the input winding.
1256 If keepon is true, an exactly on-plane winding will be saved, otherwise
1257 it will be clipped away.
1260 winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1262 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1263 int sides[MAX_POINTS_ON_WINDING + 1];
1272 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1274 // determine sides for each point
1275 for (i = 0;i < in->numpoints;i++)
1277 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1278 if (dot > ON_EPSILON)
1279 sides[i] = SIDE_FRONT;
1280 else if (dot < -ON_EPSILON)
1281 sides[i] = SIDE_BACK;
1286 sides[i] = sides[0];
1287 dists[i] = dists[0];
1289 if (keepon && !counts[0] && !counts[1])
1300 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1301 neww = NewWinding (maxpts);
1303 for (i = 0;i < in->numpoints;i++)
1307 if (sides[i] == SIDE_ON)
1309 VectorCopy (p1, neww->points[neww->numpoints]);
1314 if (sides[i] == SIDE_FRONT)
1316 VectorCopy (p1, neww->points[neww->numpoints]);
1320 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1323 // generate a split point
1324 p2 = in->points[(i+1)%in->numpoints];
1326 dot = dists[i] / (dists[i]-dists[i+1]);
1327 for (j = 0;j < 3;j++)
1328 { // avoid round off error when possible
1329 if (split->normal[j] == 1)
1330 mid[j] = split->dist;
1331 else if (split->normal[j] == -1)
1332 mid[j] = -split->dist;
1334 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1337 VectorCopy (mid, neww->points[neww->numpoints]);
1341 if (neww->numpoints > maxpts)
1342 Host_Error ("ClipWinding: points exceeded estimate");
1344 // free the original winding
1355 Divides a winding by a plane, producing one or two windings. The
1356 original winding is not damaged or freed. If only on one side, the
1357 returned winding will be the input winding. If on both sides, two
1358 new windings will be created.
1361 void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1363 vec_t dists[MAX_POINTS_ON_WINDING + 1];
1364 int sides[MAX_POINTS_ON_WINDING + 1];
1373 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1375 // determine sides for each point
1376 for (i = 0;i < in->numpoints;i++)
1378 dot = DotProduct (in->points[i], split->normal);
1381 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1382 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1383 else sides[i] = SIDE_ON;
1386 sides[i] = sides[0];
1387 dists[i] = dists[0];
1389 *front = *back = NULL;
1402 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1404 *front = f = NewWinding (maxpts);
1405 *back = b = NewWinding (maxpts);
1407 for (i = 0;i < in->numpoints;i++)
1411 if (sides[i] == SIDE_ON)
1413 VectorCopy (p1, f->points[f->numpoints]);
1415 VectorCopy (p1, b->points[b->numpoints]);
1420 if (sides[i] == SIDE_FRONT)
1422 VectorCopy (p1, f->points[f->numpoints]);
1425 else if (sides[i] == SIDE_BACK)
1427 VectorCopy (p1, b->points[b->numpoints]);
1431 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1434 // generate a split point
1435 p2 = in->points[(i+1)%in->numpoints];
1437 dot = dists[i] / (dists[i]-dists[i+1]);
1438 for (j = 0;j < 3;j++)
1439 { // avoid round off error when possible
1440 if (split->normal[j] == 1)
1441 mid[j] = split->dist;
1442 else if (split->normal[j] == -1)
1443 mid[j] = -split->dist;
1445 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1448 VectorCopy (mid, f->points[f->numpoints]);
1450 VectorCopy (mid, b->points[b->numpoints]);
1454 if (f->numpoints > maxpts || b->numpoints > maxpts)
1455 Host_Error ("DivideWinding: points exceeded estimate");
1458 typedef struct portal_s
1461 mnode_t *nodes[2]; // [0] = front side of plane
1462 struct portal_s *next[2];
1464 struct portal_s *chain; // all portals are linked into a list
1468 static portal_t *portalchain;
1475 portal_t *AllocPortal (void)
1478 p = qmalloc(sizeof(portal_t));
1479 memset(p, 0, sizeof(portal_t));
1480 p->chain = portalchain;
1485 void Mod_FinalizePortals(void)
1487 int i, j, numportals, numpoints;
1488 portal_t *p, *pnext;
1491 mleaf_t *leaf, *endleaf;
1494 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
1495 leaf = loadmodel->leafs;
1496 endleaf = leaf + loadmodel->numleafs;
1497 for (;leaf < endleaf;leaf++)
1499 VectorSet( 2000000000, 2000000000, 2000000000, leaf->mins);
1500 VectorSet(-2000000000, -2000000000, -2000000000, leaf->maxs);
1507 for (i = 0;i < 2;i++)
1509 leaf = (mleaf_t *)p->nodes[i];
1511 for (j = 0;j < w->numpoints;j++)
1513 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
1514 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
1515 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
1516 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
1517 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
1518 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
1525 // tally up portal and point counts
1531 if (p->winding && p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1534 numpoints += p->winding->numpoints * 2;
1538 loadmodel->portals = Hunk_AllocName(numportals * sizeof(mportal_t), va("%s portals", loadmodel->name));
1539 loadmodel->numportals = numportals;
1540 loadmodel->portalpoints = Hunk_AllocName(numpoints * sizeof(mvertex_t), va("%s portals", loadmodel->name));
1541 loadmodel->numportalpoints = numpoints;
1542 // clear all leaf portal chains
1543 for (i = 0;i < loadmodel->numleafs;i++)
1544 loadmodel->leafs[i].portals = NULL;
1545 // process all portals in the global portal chain, while freeing them
1546 portal = loadmodel->portals;
1547 point = loadmodel->portalpoints;
1556 // 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
1557 if (p->nodes[0] != p->nodes[1] && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID)
1559 // first make the back to front portal (forward portal)
1560 portal->points = point;
1561 portal->numpoints = p->winding->numpoints;
1562 portal->plane.dist = p->plane.dist;
1563 VectorCopy(p->plane.normal, portal->plane.normal);
1564 portal->here = (mleaf_t *)p->nodes[1];
1565 portal->past = (mleaf_t *)p->nodes[0];
1567 for (j = 0;j < portal->numpoints;j++)
1569 VectorCopy(p->winding->points[j], point->position);
1572 PlaneClassify(&portal->plane);
1574 // link into leaf's portal chain
1575 portal->next = portal->here->portals;
1576 portal->here->portals = portal;
1578 // advance to next portal
1581 // then make the front to back portal (backward portal)
1582 portal->points = point;
1583 portal->numpoints = p->winding->numpoints;
1584 portal->plane.dist = -p->plane.dist;
1585 VectorNegate(p->plane.normal, portal->plane.normal);
1586 portal->here = (mleaf_t *)p->nodes[0];
1587 portal->past = (mleaf_t *)p->nodes[1];
1589 for (j = portal->numpoints - 1;j >= 0;j--)
1591 VectorCopy(p->winding->points[j], point->position);
1594 PlaneClassify(&portal->plane);
1596 // link into leaf's portal chain
1597 portal->next = portal->here->portals;
1598 portal->here->portals = portal;
1600 // advance to next portal
1603 FreeWinding(p->winding);
1615 void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
1618 Host_Error ("AddPortalToNodes: NULL front node");
1620 Host_Error ("AddPortalToNodes: NULL back node");
1621 if (p->nodes[0] || p->nodes[1])
1622 Host_Error ("AddPortalToNodes: already included");
1623 // 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
1625 p->nodes[0] = front;
1626 p->next[0] = (portal_t *)front->portals;
1627 front->portals = (mportal_t *)p;
1630 p->next[1] = (portal_t *)back->portals;
1631 back->portals = (mportal_t *)p;
1636 RemovePortalFromNode
1639 void RemovePortalFromNodes(portal_t *portal)
1643 void **portalpointer;
1645 for (i = 0;i < 2;i++)
1647 node = portal->nodes[i];
1649 portalpointer = (void **) &node->portals;
1654 Host_Error ("RemovePortalFromNodes: portal not in leaf");
1658 if (portal->nodes[0] == node)
1660 *portalpointer = portal->next[0];
1661 portal->nodes[0] = NULL;
1663 else if (portal->nodes[1] == node)
1665 *portalpointer = portal->next[1];
1666 portal->nodes[1] = NULL;
1669 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1673 if (t->nodes[0] == node)
1674 portalpointer = (void **) &t->next[0];
1675 else if (t->nodes[1] == node)
1676 portalpointer = (void **) &t->next[1];
1678 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
1683 void Mod_RecursiveNodePortals (mnode_t *node)
1686 mnode_t *front, *back, *other_node;
1687 mplane_t clipplane, *plane;
1688 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
1689 winding_t *nodeportalwinding, *frontwinding, *backwinding;
1691 // CheckLeafPortalConsistancy (node);
1693 // if a leaf, we're done
1697 plane = node->plane;
1699 front = node->children[0];
1700 back = node->children[1];
1702 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
1704 // create the new portal by generating a polygon for the node plane,
1705 // and clipping it by all of the other portals (which came from nodes above this one)
1706 nodeportal = AllocPortal ();
1707 nodeportal->plane = *node->plane;
1709 nodeportalwinding = BaseWindingForPlane (node->plane);
1710 side = 0; // shut up compiler warning
1711 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
1713 clipplane = portal->plane;
1714 if (portal->nodes[0] == portal->nodes[1])
1715 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
1716 if (portal->nodes[0] == node)
1718 else if (portal->nodes[1] == node)
1720 clipplane.dist = -clipplane.dist;
1721 VectorNegate (clipplane.normal, clipplane.normal);
1725 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1727 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
1728 if (!nodeportalwinding)
1730 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
1735 if (nodeportalwinding)
1737 // if the plane was not clipped on all sides, there was an error
1738 nodeportal->winding = nodeportalwinding;
1739 AddPortalToNodes (nodeportal, front, back);
1742 // split the portals of this node along this node's plane and assign them to the children of this node
1743 // (migrating the portals downward through the tree)
1744 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
1746 if (portal->nodes[0] == portal->nodes[1])
1747 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
1748 if (portal->nodes[0] == node)
1750 else if (portal->nodes[1] == node)
1753 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
1754 nextportal = portal->next[side];
1756 other_node = portal->nodes[!side];
1757 RemovePortalFromNodes (portal);
1759 // cut the portal into two portals, one on each side of the node plane
1760 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
1765 AddPortalToNodes (portal, back, other_node);
1767 AddPortalToNodes (portal, other_node, back);
1773 AddPortalToNodes (portal, front, other_node);
1775 AddPortalToNodes (portal, other_node, front);
1779 // the winding is split
1780 splitportal = AllocPortal ();
1781 temp = splitportal->chain;
1782 *splitportal = *portal;
1783 splitportal->chain = temp;
1784 splitportal->winding = backwinding;
1785 FreeWinding (portal->winding);
1786 portal->winding = frontwinding;
1790 AddPortalToNodes (portal, front, other_node);
1791 AddPortalToNodes (splitportal, back, other_node);
1795 AddPortalToNodes (portal, other_node, front);
1796 AddPortalToNodes (splitportal, other_node, back);
1800 Mod_RecursiveNodePortals(front);
1801 Mod_RecursiveNodePortals(back);
1805 void Mod_MakeOutsidePortals(mnode_t *node)
1808 portal_t *p, *portals[6];
1809 mnode_t *outside_node;
1811 outside_node = Hunk_AllocName(sizeof(mnode_t), loadmodel->name);
1812 outside_node->contents = CONTENTS_SOLID;
1813 outside_node->portals = NULL;
1815 for (i = 0;i < 3;i++)
1817 for (j = 0;j < 2;j++)
1819 portals[j*3 + i] = p = AllocPortal ();
1820 memset (&p->plane, 0, sizeof(mplane_t));
1821 p->plane.normal[i] = j ? -1 : 1;
1822 p->plane.dist = -65536;
1823 p->winding = BaseWindingForPlane (&p->plane);
1825 AddPortalToNodes (p, outside_node, node);
1827 AddPortalToNodes (p, node, outside_node);
1831 // clip the basewindings by all the other planes
1832 for (i = 0;i < 6;i++)
1834 for (j = 0;j < 6;j++)
1838 portals[i]->winding = ClipWinding (portals[i]->winding, &portals[j]->plane, true);
1844 void Mod_MakePortals(void)
1846 // Con_Printf("building portals for %s\n", loadmodel->name);
1849 // Mod_MakeOutsidePortals (loadmodel->nodes);
1850 Mod_RecursiveNodePortals (loadmodel->nodes);
1851 Mod_FinalizePortals();
1859 void Mod_LoadBrushModel (model_t *mod, void *buffer)
1865 loadmodel->type = mod_brush;
1867 header = (dheader_t *)buffer;
1869 i = LittleLong (header->version);
1870 if (i != BSPVERSION && i != 30)
1871 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i or 30 (HalfLife))", mod->name, i, BSPVERSION);
1873 halflifebsp.value = hlbsp;
1875 // swap all the lumps
1876 mod_base = (byte *)header;
1878 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
1879 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
1883 // LordHavoc: had to move entity loading above everything to allow parsing various settings from worldspawn
1884 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1886 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
1887 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
1888 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
1889 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
1890 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
1891 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
1892 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
1893 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
1894 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
1895 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
1896 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
1897 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
1898 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
1899 // Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
1900 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
1906 mod->numframes = 2; // regular and alternate animation
1909 // set up the submodels (FIXME: this is confusing)
1911 for (i = 0;i < mod->numsubmodels;i++)
1913 bm = &mod->submodels[i];
1915 mod->hulls[0].firstclipnode = bm->headnode[0];
1916 for (j=1 ; j<MAX_MAP_HULLS ; j++)
1918 mod->hulls[j].firstclipnode = bm->headnode[j];
1919 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
1922 mod->firstmodelsurface = bm->firstface;
1923 mod->nummodelsurfaces = bm->numfaces;
1925 VectorCopy (bm->maxs, mod->maxs);
1926 VectorCopy (bm->mins, mod->mins);
1928 mod->radius = RadiusFromBounds (mod->mins, mod->maxs);
1930 mod->numleafs = bm->visleafs;
1932 if (isworldmodel && i < (mod->numsubmodels - 1)) // LordHavoc: only register submodels if it is the world (prevents bsp models from replacing world submodels)
1933 { // duplicate the basic information
1936 sprintf (name, "*%i", i+1);
1937 loadmodel = Mod_FindName (name);
1939 strcpy (loadmodel->name, name);