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 // note: model_shared.c sets up r_notexture, and r_surf_notexture
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
33 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
34 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
41 void Mod_BrushInit (void)
43 // Cvar_RegisterVariable(&r_subdivide_size);
44 Cvar_RegisterVariable(&halflifebsp);
45 Cvar_RegisterVariable(&r_novis);
46 Cvar_RegisterVariable(&r_miplightmaps);
47 Cvar_RegisterVariable(&r_lightmaprgba);
48 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
49 Cvar_RegisterVariable(&r_nosurftextures);
50 Cvar_RegisterVariable(&r_sortsurfaces);
51 memset(mod_novis, 0xff, sizeof(mod_novis));
59 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
63 Mod_CheckLoaded(model);
65 // LordHavoc: modified to start at first clip node,
66 // in other words: first node of the (sub)model
67 node = model->nodes + model->hulls[0].firstclipnode;
68 while (node->contents == 0)
69 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
71 return (mleaf_t *)node;
74 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
76 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
77 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
78 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
80 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
81 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
83 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
84 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
94 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
96 static qbyte decompressed[MAX_MAP_LEAFS/8];
101 row = (model->numleafs+7)>>3;
119 } while (out - decompressed < row);
124 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
126 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
128 return Mod_DecompressVis (leaf->compressed_vis, model);
136 static void Mod_LoadTextures (lump_t *l)
138 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
140 texture_t *tx, *tx2, *anims[10], *altanims[10];
142 qbyte *data, *mtdata, *data2;
145 loadmodel->textures = NULL;
150 m = (dmiptexlump_t *)(mod_base + l->fileofs);
152 m->nummiptex = LittleLong (m->nummiptex);
154 // add two slots for notexture walls and notexture liquids
155 loadmodel->numtextures = m->nummiptex + 2;
156 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
158 // fill out all slots with notexture
159 for (i = 0;i < loadmodel->numtextures;i++)
161 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
164 tx->texture = r_notexture;
165 if (i == loadmodel->numtextures - 1)
166 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
169 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
171 // LordHavoc: mostly rewritten map texture loader
172 for (i = 0;i < m->nummiptex;i++)
174 dofs[i] = LittleLong(dofs[i]);
175 if (dofs[i] == -1 || r_nosurftextures.integer)
177 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
179 // make sure name is no more than 15 characters
180 for (j = 0;dmiptex->name[j] && j < 15;j++)
181 name[j] = dmiptex->name[j];
184 mtwidth = LittleLong (dmiptex->width);
185 mtheight = LittleLong (dmiptex->height);
187 j = LittleLong (dmiptex->offsets[0]);
191 if (j < 40 || j + mtwidth * mtheight > l->filelen)
193 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
196 mtdata = (qbyte *)dmiptex + j;
199 if ((mtwidth & 15) || (mtheight & 15))
200 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
202 // LordHavoc: force all names to lowercase
203 for (j = 0;name[j];j++)
204 if (name[j] >= 'A' && name[j] <= 'Z')
205 name[j] += 'a' - 'A';
207 tx = loadmodel->textures[i];
208 strcpy(tx->name, name);
210 tx->height = mtheight;
212 tx->glowtexture = NULL;
213 tx->fogtexture = NULL;
217 sprintf(tx->name, "unnamed%i", i);
218 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
221 // LordHavoc: HL sky textures are entirely different than quake
222 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
224 if (loadmodel->isworldmodel)
226 data = loadimagepixels(tx->name, false, 0, 0);
229 if (image_width == 256 && image_height == 128)
237 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
239 R_InitSky (mtdata, 1);
242 else if (mtdata != NULL)
243 R_InitSky (mtdata, 1);
246 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
248 tx->fogtexture = image_masktex;
249 strcpy(name, tx->name);
250 strcat(name, "_glow");
251 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
255 if (loadmodel->ishlbsp)
257 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
260 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
261 if (R_TextureHasAlpha(tx->texture))
264 for (j = 0;j < image_width * image_height;j++)
265 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
266 strcpy(name, tx->name);
267 strcat(name, "_fog");
268 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
272 else if ((data = W_GetTexture(tx->name)))
274 // get the size from the wad texture
275 tx->width = image_width;
276 tx->height = image_height;
277 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
278 if (R_TextureHasAlpha(tx->texture))
281 for (j = 0;j < image_width * image_height;j++)
282 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
283 strcpy(name, tx->name);
284 strcat(name, "_fog");
285 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
293 tx->texture = r_notexture;
298 if (mtdata) // texture included
303 if (r_fullbrights.value && tx->name[0] != '*')
305 for (j = 0;j < tx->width*tx->height;j++)
307 if (data[j] >= 224) // fullbright
316 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
317 for (j = 0;j < tx->width*tx->height;j++)
318 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
319 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
320 strcpy(name, tx->name);
321 strcat(name, "_glow");
322 for (j = 0;j < tx->width*tx->height;j++)
323 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
324 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
328 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
330 else // no texture, and no external replacement texture was found
334 tx->texture = r_notexture;
339 if (tx->name[0] == '*')
341 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
342 // LordHavoc: some turbulent textures should be fullbright and solid
343 if (!strncmp(tx->name,"*lava",5)
344 || !strncmp(tx->name,"*teleport",9)
345 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
346 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
348 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
349 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
352 tx->flags |= SURF_LIGHTMAP;
353 if (!R_TextureHasAlpha(tx->texture))
354 tx->flags |= SURF_CLIPSOLID;
358 // sequence the animations
359 for (i = 0;i < m->nummiptex;i++)
361 tx = loadmodel->textures[i];
362 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
364 if (tx->anim_total[0] || tx->anim_total[1])
365 continue; // already sequenced
367 // find the number of frames in the animation
368 memset (anims, 0, sizeof(anims));
369 memset (altanims, 0, sizeof(altanims));
371 for (j = i;j < m->nummiptex;j++)
373 tx2 = loadmodel->textures[j];
374 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
378 if (num >= '0' && num <= '9')
379 anims[num - '0'] = tx2;
380 else if (num >= 'a' && num <= 'j')
381 altanims[num - 'a'] = tx2;
383 Con_Printf ("Bad animating texture %s\n", tx->name);
387 for (j = 0;j < 10;j++)
394 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
397 for (j = 0;j < max;j++)
401 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
405 for (j = 0;j < altmax;j++)
409 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
418 // if there is no alternate animation, duplicate the primary
419 // animation into the alternate
421 for (k = 0;k < 10;k++)
422 altanims[k] = anims[k];
425 // link together the primary animation
426 for (j = 0;j < max;j++)
429 tx2->animated = true;
430 tx2->anim_total[0] = max;
431 tx2->anim_total[1] = altmax;
432 for (k = 0;k < 10;k++)
434 tx2->anim_frames[0][k] = anims[k];
435 tx2->anim_frames[1][k] = altanims[k];
439 // if there really is an alternate anim...
440 if (anims[0] != altanims[0])
442 // link together the alternate animation
443 for (j = 0;j < altmax;j++)
446 tx2->animated = true;
447 // the primary/alternate are reversed here
448 tx2->anim_total[0] = altmax;
449 tx2->anim_total[1] = max;
450 for (k = 0;k < 10;k++)
452 tx2->anim_frames[0][k] = altanims[k];
453 tx2->anim_frames[1][k] = anims[k];
465 static void Mod_LoadLighting (lump_t *l)
468 qbyte *in, *out, *data, d;
469 char litfilename[1024];
470 loadmodel->lightdata = NULL;
471 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
473 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
474 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
476 else // LordHavoc: bsp version 29 (normal white lighting)
478 // LordHavoc: hope is not lost yet, check for a .lit file to load
479 strcpy(litfilename, loadmodel->name);
480 COM_StripExtension(litfilename, litfilename);
481 strcat(litfilename, ".lit");
482 data = (qbyte*) COM_LoadFile (litfilename, false);
485 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
487 i = LittleLong(((int *)data)[1]);
490 Con_DPrintf("%s loaded", litfilename);
491 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
492 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
498 Con_Printf("Unknown .lit file version (%d)\n", i);
505 Con_Printf("Empty .lit file, ignoring\n");
507 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
511 // LordHavoc: oh well, expand the white lighting data
514 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
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++)
528 void Mod_LoadLightList(void)
531 char lightsfilename[1024], *s, *t, *lightsstring;
534 strcpy(lightsfilename, loadmodel->name);
535 COM_StripExtension(lightsfilename, lightsfilename);
536 strcat(lightsfilename, ".lights");
537 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
543 while (*s && *s != '\n')
547 Mem_Free(lightsstring);
548 Host_Error("lights file must end with a newline\n");
553 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
556 while (*s && n < numlights)
559 while (*s && *s != '\n')
563 Mem_Free(lightsstring);
564 Host_Error("misparsed lights file!\n");
566 e = loadmodel->lights + n;
568 a = sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %d", &e->origin[0], &e->origin[1], &e->origin[2], &e->falloff, &e->light[0], &e->light[1], &e->light[2], &e->subtract, &e->spotdir[0], &e->spotdir[1], &e->spotdir[2], &e->spotcone, &e->distbias, &e->style);
572 Mem_Free(lightsstring);
573 Host_Error("invalid lights file, found %d parameters on line %i, should be 13 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone style)\n", a, n + 1);
580 Mem_Free(lightsstring);
581 Host_Error("misparsed lights file!\n");
583 loadmodel->numlights = numlights;
584 Mem_Free(lightsstring);
594 static void Mod_LoadVisibility (lump_t *l)
596 loadmodel->visdata = NULL;
599 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
600 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
603 // used only for HalfLife maps
604 void Mod_ParseWadsFromEntityLump(char *data)
606 char key[128], value[4096];
611 data = COM_Parse(data);
614 if (com_token[0] != '{')
618 data = COM_Parse(data);
621 if (com_token[0] == '}')
622 break; // end of worldspawn
623 if (com_token[0] == '_')
624 strcpy(key, com_token + 1);
626 strcpy(key, com_token);
627 while (key[strlen(key)-1] == ' ') // remove trailing spaces
628 key[strlen(key)-1] = 0;
629 data = COM_Parse(data);
632 strcpy(value, com_token);
633 if (!strcmp("wad", key)) // for HalfLife maps
635 if (loadmodel->ishlbsp)
638 for (i = 0;i < 4096;i++)
639 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
645 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
646 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
648 else if (value[i] == ';' || value[i] == 0)
652 strcpy(wadname, "textures/");
653 strcat(wadname, &value[j]);
654 W_LoadTextureWadFile (wadname, false);
671 static void Mod_LoadEntities (lump_t *l)
673 loadmodel->entities = NULL;
676 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
677 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
678 if (loadmodel->ishlbsp)
679 Mod_ParseWadsFromEntityLump(loadmodel->entities);
688 static void Mod_LoadVertexes (lump_t *l)
694 in = (void *)(mod_base + l->fileofs);
695 if (l->filelen % sizeof(*in))
696 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
697 count = l->filelen / sizeof(*in);
698 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
700 loadmodel->vertexes = out;
701 loadmodel->numvertexes = count;
703 for ( i=0 ; i<count ; i++, in++, out++)
705 out->position[0] = LittleFloat (in->point[0]);
706 out->position[1] = LittleFloat (in->point[1]);
707 out->position[2] = LittleFloat (in->point[2]);
716 static void Mod_LoadSubmodels (lump_t *l)
722 in = (void *)(mod_base + l->fileofs);
723 if (l->filelen % sizeof(*in))
724 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
725 count = l->filelen / sizeof(*in);
726 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
728 loadmodel->submodels = out;
729 loadmodel->numsubmodels = count;
731 for ( i=0 ; i<count ; i++, in++, out++)
733 for (j=0 ; j<3 ; j++)
735 // spread the mins / maxs by a pixel
736 out->mins[j] = LittleFloat (in->mins[j]) - 1;
737 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
738 out->origin[j] = LittleFloat (in->origin[j]);
740 for (j=0 ; j<MAX_MAP_HULLS ; j++)
741 out->headnode[j] = LittleLong (in->headnode[j]);
742 out->visleafs = LittleLong (in->visleafs);
743 out->firstface = LittleLong (in->firstface);
744 out->numfaces = LittleLong (in->numfaces);
753 static void Mod_LoadEdges (lump_t *l)
759 in = (void *)(mod_base + l->fileofs);
760 if (l->filelen % sizeof(*in))
761 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
762 count = l->filelen / sizeof(*in);
763 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
765 loadmodel->edges = out;
766 loadmodel->numedges = count;
768 for ( i=0 ; i<count ; i++, in++, out++)
770 out->v[0] = (unsigned short)LittleShort(in->v[0]);
771 out->v[1] = (unsigned short)LittleShort(in->v[1]);
780 static void Mod_LoadTexinfo (lump_t *l)
784 int i, j, k, count, miptex;
786 in = (void *)(mod_base + l->fileofs);
787 if (l->filelen % sizeof(*in))
788 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
789 count = l->filelen / sizeof(*in);
790 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
792 loadmodel->texinfo = out;
793 loadmodel->numtexinfo = count;
795 for (i = 0;i < count;i++, in++, out++)
797 for (k = 0;k < 2;k++)
798 for (j = 0;j < 4;j++)
799 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
801 miptex = LittleLong (in->miptex);
802 out->flags = LittleLong (in->flags);
805 if (loadmodel->textures)
807 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
808 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
810 out->texture = loadmodel->textures[miptex];
812 if (out->texture == NULL)
814 // choose either the liquid notexture, or the normal notexture
815 if (out->flags & TEX_SPECIAL)
816 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
818 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
827 Fills in s->texturemins[] and s->extents[]
830 static void CalcSurfaceExtents (msurface_t *s)
832 float mins[2], maxs[2], val;
836 int bmins[2], bmaxs[2];
838 mins[0] = mins[1] = 999999999;
839 maxs[0] = maxs[1] = -999999999;
843 for (i=0 ; i<s->numedges ; i++)
845 e = loadmodel->surfedges[s->firstedge+i];
847 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
849 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
851 for (j=0 ; j<2 ; j++)
853 val = v->position[0] * tex->vecs[j][0] +
854 v->position[1] * tex->vecs[j][1] +
855 v->position[2] * tex->vecs[j][2] +
864 for (i=0 ; i<2 ; i++)
866 bmins[i] = floor(mins[i]/16);
867 bmaxs[i] = ceil(maxs[i]/16);
869 s->texturemins[i] = bmins[i] * 16;
870 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
875 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
880 mins[0] = mins[1] = mins[2] = 9999;
881 maxs[0] = maxs[1] = maxs[2] = -9999;
883 for (i = 0;i < numverts;i++)
885 for (j = 0;j < 3;j++, v++)
896 #define MAX_SUBDIVPOLYTRIANGLES 4096
897 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
899 static int subdivpolyverts, subdivpolytriangles;
900 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
901 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
903 static int subdivpolylookupvert(vec3_t v)
906 for (i = 0;i < subdivpolyverts;i++)
907 if (subdivpolyvert[i][0] == v[0]
908 && subdivpolyvert[i][1] == v[1]
909 && subdivpolyvert[i][2] == v[2])
911 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
912 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
913 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
914 return subdivpolyverts++;
917 static void SubdividePolygon (int numverts, float *verts)
919 int i, i1, i2, i3, f, b, c, p;
920 vec3_t mins, maxs, front[256], back[256];
921 float m, *pv, *cv, dist[256], frac;
924 Host_Error ("SubdividePolygon: ran out of verts in buffer");
926 BoundPoly (numverts, verts, mins, maxs);
928 for (i = 0;i < 3;i++)
930 m = (mins[i] + maxs[i]) * 0.5;
931 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
938 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
942 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
946 VectorCopy (pv, front[f]);
951 VectorCopy (pv, back[b]);
954 if (dist[p] == 0 || dist[c] == 0)
956 if ( (dist[p] > 0) != (dist[c] > 0) )
959 frac = dist[p] / (dist[p] - dist[c]);
960 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
961 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
962 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
968 SubdividePolygon (f, front[0]);
969 SubdividePolygon (b, back[0]);
973 i1 = subdivpolylookupvert(verts);
974 i2 = subdivpolylookupvert(verts + 3);
975 for (i = 2;i < numverts;i++)
977 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
979 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
983 i3 = subdivpolylookupvert(verts + i * 3);
984 subdivpolyindex[subdivpolytriangles][0] = i1;
985 subdivpolyindex[subdivpolytriangles][1] = i2;
986 subdivpolyindex[subdivpolytriangles][2] = i3;
988 subdivpolytriangles++;
996 Breaks a polygon up along axial 64 unit
997 boundaries so that turbulent and sky warps
998 can be done reasonably.
1001 void Mod_GenerateWarpMesh (msurface_t *surf)
1007 subdivpolytriangles = 0;
1008 subdivpolyverts = 0;
1009 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1010 if (subdivpolytriangles < 1)
1011 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1013 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1014 mesh->numverts = subdivpolyverts;
1015 mesh->numtriangles = subdivpolytriangles;
1016 mesh->vertex = (surfvertex_t *)(mesh + 1);
1017 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1018 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1020 for (i = 0;i < mesh->numtriangles;i++)
1021 for (j = 0;j < 3;j++)
1022 mesh->index[i*3+j] = subdivpolyindex[i][j];
1024 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1026 VectorCopy(subdivpolyvert[i], v->v);
1027 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1028 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1033 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1035 int i, is, it, *index, smax, tmax;
1040 smax = surf->extents[0] >> 4;
1041 tmax = surf->extents[1] >> 4;
1042 surf->lightmaptexturestride = 0;
1043 surf->lightmaptexture = NULL;
1045 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1046 mesh->numverts = surf->poly_numverts;
1047 mesh->numtriangles = surf->poly_numverts - 2;
1048 mesh->vertex = (surfvertex_t *)(mesh + 1);
1049 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1050 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1052 index = mesh->index;
1053 for (i = 0;i < mesh->numtriangles;i++)
1060 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1062 VectorCopy (in, out->v);
1064 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1065 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1067 out->st[0] = s / surf->texinfo->texture->width;
1068 out->st[1] = t / surf->texinfo->texture->height;
1070 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1071 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1073 // lightmap coordinates
1077 // LordHavoc: calc lightmap data offset for vertex lighting to use
1080 is = bound(0, is, smax);
1081 it = bound(0, it, tmax);
1082 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1086 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1088 int i, is, it, *index, smax, tmax;
1089 float *in, s, t, xbase, ybase, xscale, yscale;
1093 surf->flags |= SURF_LIGHTMAP;
1094 smax = surf->extents[0] >> 4;
1095 tmax = surf->extents[1] >> 4;
1096 if (r_miplightmaps.integer)
1098 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1099 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE);
1103 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1104 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
1106 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1107 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1108 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1110 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1111 mesh->numverts = surf->poly_numverts;
1112 mesh->numtriangles = surf->poly_numverts - 2;
1113 mesh->vertex = (surfvertex_t *)(mesh + 1);
1114 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1115 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1117 index = mesh->index;
1118 for (i = 0;i < mesh->numtriangles;i++)
1125 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1127 VectorCopy (in, out->v);
1129 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1130 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1132 out->st[0] = s / surf->texinfo->texture->width;
1133 out->st[1] = t / surf->texinfo->texture->height;
1135 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1136 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1138 // lightmap coordinates
1139 out->uv[0] = s * xscale + xbase;
1140 out->uv[1] = t * yscale + ybase;
1142 // LordHavoc: calc lightmap data offset for vertex lighting to use
1145 is = bound(0, is, smax);
1146 it = bound(0, it, tmax);
1147 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1151 void Mod_GenerateVertexMesh (msurface_t *surf)
1158 surf->lightmaptexturestride = 0;
1159 surf->lightmaptexture = NULL;
1161 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1162 mesh->numverts = surf->poly_numverts;
1163 mesh->numtriangles = surf->poly_numverts - 2;
1164 mesh->vertex = (surfvertex_t *)(mesh + 1);
1165 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1166 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1168 index = mesh->index;
1169 for (i = 0;i < mesh->numtriangles;i++)
1176 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1178 VectorCopy (in, out->v);
1179 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1180 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1184 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1191 // convert edges back to a normal polygon
1192 surf->poly_numverts = surf->numedges;
1193 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1194 for (i = 0;i < surf->numedges;i++)
1196 lindex = loadmodel->surfedges[surf->firstedge + i];
1198 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1200 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1201 VectorCopy (vec, vert);
1206 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1208 int j, base, tricount, newvertexcount, *index, *vertexremap;
1209 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1210 if (s->mesh->numtriangles > 1000)
1212 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1217 while (base < s->mesh->numtriangles)
1219 tricount = s->mesh->numtriangles - base;
1220 if (tricount > 1000)
1222 index = s->mesh->index + base * 3;
1226 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1227 for (j = 0;j < tricount * 3;j++)
1228 if (vertexremap[index[j]] < 0)
1229 vertexremap[index[j]] = newvertexcount++;
1231 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1232 newmesh->chain = NULL;
1233 newmesh->numverts = newvertexcount;
1234 newmesh->numtriangles = tricount;
1235 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1236 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1237 for (j = 0;j < tricount * 3;j++)
1239 newmesh->index[j] = vertexremap[index[j]];
1240 // yes this copies the same vertex multiple times in many cases... but that's ok...
1241 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1244 oldmesh->chain = newmesh;
1246 firstmesh = newmesh;
1249 Mem_Free(vertexremap);
1251 s->mesh = firstmesh;
1260 static void Mod_LoadFaces (lump_t *l)
1264 int i, count, surfnum, planenum, ssize, tsize;
1266 in = (void *)(mod_base + l->fileofs);
1267 if (l->filelen % sizeof(*in))
1268 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1269 count = l->filelen / sizeof(*in);
1270 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1272 loadmodel->surfaces = out;
1273 loadmodel->numsurfaces = count;
1275 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1277 // FIXME: validate edges, texinfo, etc?
1278 out->firstedge = LittleLong(in->firstedge);
1279 out->numedges = LittleShort(in->numedges);
1280 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1281 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1283 i = LittleShort (in->texinfo);
1284 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1285 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1286 out->texinfo = loadmodel->texinfo + i;
1287 out->flags = out->texinfo->texture->flags;
1289 planenum = LittleShort(in->planenum);
1290 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1291 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1293 if (LittleShort(in->side))
1294 out->flags |= SURF_PLANEBACK;
1296 out->plane = loadmodel->planes + planenum;
1298 // clear lightmap (filled in later)
1299 out->lightmaptexture = NULL;
1301 // force lightmap upload on first time seeing the surface
1302 out->cached_dlight = true;
1303 out->cached_ambient = -1000;
1304 out->cached_lightscalebit = -1000;
1306 CalcSurfaceExtents (out);
1308 ssize = (out->extents[0] >> 4) + 1;
1309 tsize = (out->extents[1] >> 4) + 1;
1312 for (i = 0;i < MAXLIGHTMAPS;i++)
1313 out->styles[i] = in->styles[i];
1314 i = LittleLong(in->lightofs);
1316 out->samples = NULL;
1317 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1318 out->samples = loadmodel->lightdata + i;
1319 else // LordHavoc: white lighting (bsp version 29)
1320 out->samples = loadmodel->lightdata + (i * 3);
1322 Mod_GenerateSurfacePolygon(out);
1324 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1326 out->shader = &Cshader_sky;
1327 out->samples = NULL;
1328 Mod_GenerateVertexMesh (out);
1330 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1332 out->shader = &Cshader_water;
1333 out->samples = NULL;
1334 Mod_GenerateVertexMesh (out);
1338 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1339 out->flags |= SURF_CLIPSOLID;
1340 if (out->texinfo->flags & TEX_SPECIAL)
1342 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1343 out->shader = &Cshader_water;
1344 out->shader = &Cshader_water;
1345 out->samples = NULL;
1346 Mod_GenerateVertexMesh (out);
1348 else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1350 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1351 out->shader = &Cshader_wall_fullbright;
1352 out->samples = NULL;
1353 Mod_GenerateVertexMesh(out);
1357 // stainmap for permanent marks on walls
1358 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1360 memset(out->stainsamples, 255, ssize * tsize * 3);
1361 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1363 out->shader = &Cshader_wall_vertex;
1364 Mod_GenerateVertexLitMesh(out);
1368 out->shader = &Cshader_wall_lightmap;
1369 Mod_GenerateLightmappedMesh(out);
1373 Mod_SplitSurfMeshIfTooBig(out);
1377 static model_t *sortmodel;
1379 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1381 const msurface_t *a, *b;
1382 a = *((const msurface_t **)voida);
1383 b = *((const msurface_t **)voidb);
1384 if (a->shader != b->shader)
1385 return (qbyte *) a->shader - (qbyte *) b->shader;
1386 if (a->texinfo->texture != b->texinfo->texture);
1387 return a->texinfo->texture - b->texinfo->texture;
1391 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1395 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1396 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1397 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1399 if (r_sortsurfaces.integer)
1400 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1409 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1411 node->parent = parent;
1412 if (node->contents < 0)
1414 Mod_SetParent (node->children[0], node);
1415 Mod_SetParent (node->children[1], node);
1423 static void Mod_LoadNodes (lump_t *l)
1429 in = (void *)(mod_base + l->fileofs);
1430 if (l->filelen % sizeof(*in))
1431 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1432 count = l->filelen / sizeof(*in);
1433 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1435 loadmodel->nodes = out;
1436 loadmodel->numnodes = count;
1438 for ( i=0 ; i<count ; i++, in++, out++)
1440 for (j=0 ; j<3 ; j++)
1442 out->mins[j] = LittleShort (in->mins[j]);
1443 out->maxs[j] = LittleShort (in->maxs[j]);
1446 p = LittleLong(in->planenum);
1447 out->plane = loadmodel->planes + p;
1449 out->firstsurface = LittleShort (in->firstface);
1450 out->numsurfaces = LittleShort (in->numfaces);
1452 for (j=0 ; j<2 ; j++)
1454 p = LittleShort (in->children[j]);
1456 out->children[j] = loadmodel->nodes + p;
1458 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1462 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1470 static void Mod_LoadLeafs (lump_t *l)
1476 in = (void *)(mod_base + l->fileofs);
1477 if (l->filelen % sizeof(*in))
1478 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1479 count = l->filelen / sizeof(*in);
1480 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1482 loadmodel->leafs = out;
1483 loadmodel->numleafs = count;
1485 for ( i=0 ; i<count ; i++, in++, out++)
1487 for (j=0 ; j<3 ; j++)
1489 out->mins[j] = LittleShort (in->mins[j]);
1490 out->maxs[j] = LittleShort (in->maxs[j]);
1493 p = LittleLong(in->contents);
1496 out->firstmarksurface = loadmodel->marksurfaces +
1497 LittleShort(in->firstmarksurface);
1498 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1500 p = LittleLong(in->visofs);
1502 out->compressed_vis = NULL;
1504 out->compressed_vis = loadmodel->visdata + p;
1506 for (j=0 ; j<4 ; j++)
1507 out->ambient_sound_level[j] = in->ambient_level[j];
1509 // FIXME: Insert caustics here
1518 static void Mod_LoadClipnodes (lump_t *l)
1520 dclipnode_t *in, *out;
1524 in = (void *)(mod_base + l->fileofs);
1525 if (l->filelen % sizeof(*in))
1526 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1527 count = l->filelen / sizeof(*in);
1528 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1530 loadmodel->clipnodes = out;
1531 loadmodel->numclipnodes = count;
1533 if (loadmodel->ishlbsp)
1535 hull = &loadmodel->hulls[1];
1536 hull->clipnodes = out;
1537 hull->firstclipnode = 0;
1538 hull->lastclipnode = count-1;
1539 hull->planes = loadmodel->planes;
1540 hull->clip_mins[0] = -16;
1541 hull->clip_mins[1] = -16;
1542 hull->clip_mins[2] = -36;
1543 hull->clip_maxs[0] = 16;
1544 hull->clip_maxs[1] = 16;
1545 hull->clip_maxs[2] = 36;
1546 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1548 hull = &loadmodel->hulls[2];
1549 hull->clipnodes = out;
1550 hull->firstclipnode = 0;
1551 hull->lastclipnode = count-1;
1552 hull->planes = loadmodel->planes;
1553 hull->clip_mins[0] = -32;
1554 hull->clip_mins[1] = -32;
1555 hull->clip_mins[2] = -32;
1556 hull->clip_maxs[0] = 32;
1557 hull->clip_maxs[1] = 32;
1558 hull->clip_maxs[2] = 32;
1559 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1561 hull = &loadmodel->hulls[3];
1562 hull->clipnodes = out;
1563 hull->firstclipnode = 0;
1564 hull->lastclipnode = count-1;
1565 hull->planes = loadmodel->planes;
1566 hull->clip_mins[0] = -16;
1567 hull->clip_mins[1] = -16;
1568 hull->clip_mins[2] = -18;
1569 hull->clip_maxs[0] = 16;
1570 hull->clip_maxs[1] = 16;
1571 hull->clip_maxs[2] = 18;
1572 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1576 hull = &loadmodel->hulls[1];
1577 hull->clipnodes = out;
1578 hull->firstclipnode = 0;
1579 hull->lastclipnode = count-1;
1580 hull->planes = loadmodel->planes;
1581 hull->clip_mins[0] = -16;
1582 hull->clip_mins[1] = -16;
1583 hull->clip_mins[2] = -24;
1584 hull->clip_maxs[0] = 16;
1585 hull->clip_maxs[1] = 16;
1586 hull->clip_maxs[2] = 32;
1587 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1589 hull = &loadmodel->hulls[2];
1590 hull->clipnodes = out;
1591 hull->firstclipnode = 0;
1592 hull->lastclipnode = count-1;
1593 hull->planes = loadmodel->planes;
1594 hull->clip_mins[0] = -32;
1595 hull->clip_mins[1] = -32;
1596 hull->clip_mins[2] = -24;
1597 hull->clip_maxs[0] = 32;
1598 hull->clip_maxs[1] = 32;
1599 hull->clip_maxs[2] = 64;
1600 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1603 for (i=0 ; i<count ; i++, out++, in++)
1605 out->planenum = LittleLong(in->planenum);
1606 out->children[0] = LittleShort(in->children[0]);
1607 out->children[1] = LittleShort(in->children[1]);
1608 if (out->children[0] >= count || out->children[1] >= count)
1609 Host_Error("Corrupt clipping hull (out of range child)\n");
1617 Duplicate the drawing hull structure as a clipping hull
1620 static void Mod_MakeHull0 (void)
1627 hull = &loadmodel->hulls[0];
1629 in = loadmodel->nodes;
1630 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1632 hull->clipnodes = out;
1633 hull->firstclipnode = 0;
1634 hull->lastclipnode = loadmodel->numnodes - 1;
1635 hull->planes = loadmodel->planes;
1637 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1639 out->planenum = in->plane - loadmodel->planes;
1640 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1641 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1647 Mod_LoadMarksurfaces
1650 static void Mod_LoadMarksurfaces (lump_t *l)
1655 in = (void *)(mod_base + l->fileofs);
1656 if (l->filelen % sizeof(*in))
1657 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1658 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1659 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1661 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1663 j = (unsigned) LittleShort(in[i]);
1664 if (j >= loadmodel->numsurfaces)
1665 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1666 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1675 static void Mod_LoadSurfedges (lump_t *l)
1680 in = (void *)(mod_base + l->fileofs);
1681 if (l->filelen % sizeof(*in))
1682 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1683 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1684 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1686 for (i = 0;i < loadmodel->numsurfedges;i++)
1687 loadmodel->surfedges[i] = LittleLong (in[i]);
1696 static void Mod_LoadPlanes (lump_t *l)
1702 in = (void *)(mod_base + l->fileofs);
1703 if (l->filelen % sizeof(*in))
1704 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1706 loadmodel->numplanes = l->filelen / sizeof(*in);
1707 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1709 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1711 out->normal[0] = LittleFloat (in->normal[0]);
1712 out->normal[1] = LittleFloat (in->normal[1]);
1713 out->normal[2] = LittleFloat (in->normal[2]);
1714 out->dist = LittleFloat (in->dist);
1720 #define MAX_POINTS_ON_WINDING 64
1726 double points[8][3]; // variable sized
1735 static winding_t *NewWinding (int points)
1740 if (points > MAX_POINTS_ON_WINDING)
1741 Sys_Error("NewWinding: too many points\n");
1743 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1744 w = Mem_Alloc(loadmodel->mempool, size);
1745 memset (w, 0, size);
1750 static void FreeWinding (winding_t *w)
1760 static winding_t *BaseWindingForPlane (mplane_t *p)
1762 double org[3], vright[3], vup[3], normal[3];
1765 VectorCopy(p->normal, normal);
1766 VectorVectorsDouble(normal, vright, vup);
1768 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1769 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1771 // project a really big axis aligned box onto the plane
1774 VectorScale (p->normal, p->dist, org);
1776 VectorSubtract (org, vright, w->points[0]);
1777 VectorAdd (w->points[0], vup, w->points[0]);
1779 VectorAdd (org, vright, w->points[1]);
1780 VectorAdd (w->points[1], vup, w->points[1]);
1782 VectorAdd (org, vright, w->points[2]);
1783 VectorSubtract (w->points[2], vup, w->points[2]);
1785 VectorSubtract (org, vright, w->points[3]);
1786 VectorSubtract (w->points[3], vup, w->points[3]);
1797 Clips the winding to the plane, returning the new winding on the positive side
1798 Frees the input winding.
1799 If keepon is true, an exactly on-plane winding will be saved, otherwise
1800 it will be clipped away.
1803 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1805 double dists[MAX_POINTS_ON_WINDING + 1];
1806 int sides[MAX_POINTS_ON_WINDING + 1];
1815 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1817 // determine sides for each point
1818 for (i = 0;i < in->numpoints;i++)
1820 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1821 if (dot > ON_EPSILON)
1822 sides[i] = SIDE_FRONT;
1823 else if (dot < -ON_EPSILON)
1824 sides[i] = SIDE_BACK;
1829 sides[i] = sides[0];
1830 dists[i] = dists[0];
1832 if (keepon && !counts[0] && !counts[1])
1843 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1844 if (maxpts > MAX_POINTS_ON_WINDING)
1845 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1847 neww = NewWinding (maxpts);
1849 for (i = 0;i < in->numpoints;i++)
1851 if (neww->numpoints >= maxpts)
1852 Sys_Error ("ClipWinding: points exceeded estimate");
1856 if (sides[i] == SIDE_ON)
1858 VectorCopy (p1, neww->points[neww->numpoints]);
1863 if (sides[i] == SIDE_FRONT)
1865 VectorCopy (p1, neww->points[neww->numpoints]);
1869 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1872 // generate a split point
1873 p2 = in->points[(i+1)%in->numpoints];
1875 dot = dists[i] / (dists[i]-dists[i+1]);
1876 for (j = 0;j < 3;j++)
1877 { // avoid round off error when possible
1878 if (split->normal[j] == 1)
1879 mid[j] = split->dist;
1880 else if (split->normal[j] == -1)
1881 mid[j] = -split->dist;
1883 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1886 VectorCopy (mid, neww->points[neww->numpoints]);
1890 // free the original winding
1901 Divides a winding by a plane, producing one or two windings. The
1902 original winding is not damaged or freed. If only on one side, the
1903 returned winding will be the input winding. If on both sides, two
1904 new windings will be created.
1907 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1909 double dists[MAX_POINTS_ON_WINDING + 1];
1910 int sides[MAX_POINTS_ON_WINDING + 1];
1919 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1921 // determine sides for each point
1922 for (i = 0;i < in->numpoints;i++)
1924 dot = DotProduct (in->points[i], split->normal);
1927 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1928 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1929 else sides[i] = SIDE_ON;
1932 sides[i] = sides[0];
1933 dists[i] = dists[0];
1935 *front = *back = NULL;
1948 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1950 if (maxpts > MAX_POINTS_ON_WINDING)
1951 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1953 *front = f = NewWinding (maxpts);
1954 *back = b = NewWinding (maxpts);
1956 for (i = 0;i < in->numpoints;i++)
1958 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1959 Sys_Error ("DivideWinding: points exceeded estimate");
1963 if (sides[i] == SIDE_ON)
1965 VectorCopy (p1, f->points[f->numpoints]);
1967 VectorCopy (p1, b->points[b->numpoints]);
1972 if (sides[i] == SIDE_FRONT)
1974 VectorCopy (p1, f->points[f->numpoints]);
1977 else if (sides[i] == SIDE_BACK)
1979 VectorCopy (p1, b->points[b->numpoints]);
1983 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1986 // generate a split point
1987 p2 = in->points[(i+1)%in->numpoints];
1989 dot = dists[i] / (dists[i]-dists[i+1]);
1990 for (j = 0;j < 3;j++)
1991 { // avoid round off error when possible
1992 if (split->normal[j] == 1)
1993 mid[j] = split->dist;
1994 else if (split->normal[j] == -1)
1995 mid[j] = -split->dist;
1997 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2000 VectorCopy (mid, f->points[f->numpoints]);
2002 VectorCopy (mid, b->points[b->numpoints]);
2007 typedef struct portal_s
2010 mnode_t *nodes[2]; // [0] = front side of plane
2011 struct portal_s *next[2];
2013 struct portal_s *chain; // all portals are linked into a list
2017 static portal_t *portalchain;
2024 static portal_t *AllocPortal (void)
2027 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2028 p->chain = portalchain;
2033 static void FreePortal(portal_t *p)
2038 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2040 // calculate children first
2041 if (node->children[0]->contents >= 0)
2042 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2043 if (node->children[1]->contents >= 0)
2044 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2046 // make combined bounding box from children
2047 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2048 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2049 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2050 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2051 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2052 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2055 static void Mod_FinalizePortals(void)
2057 int i, j, numportals, numpoints;
2058 portal_t *p, *pnext;
2061 mleaf_t *leaf, *endleaf;
2064 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2065 leaf = loadmodel->leafs;
2066 endleaf = leaf + loadmodel->numleafs;
2067 for (;leaf < endleaf;leaf++)
2069 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2070 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2077 for (i = 0;i < 2;i++)
2079 leaf = (mleaf_t *)p->nodes[i];
2081 for (j = 0;j < w->numpoints;j++)
2083 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2084 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2085 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2086 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2087 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2088 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2095 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2097 // tally up portal and point counts
2103 // note: this check must match the one below or it will usually corrupt memory
2104 // 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
2105 if (p->winding && p->nodes[0] != p->nodes[1]
2106 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2107 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2110 numpoints += p->winding->numpoints * 2;
2114 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2115 loadmodel->numportals = numportals;
2116 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2117 loadmodel->numportalpoints = numpoints;
2118 // clear all leaf portal chains
2119 for (i = 0;i < loadmodel->numleafs;i++)
2120 loadmodel->leafs[i].portals = NULL;
2121 // process all portals in the global portal chain, while freeing them
2122 portal = loadmodel->portals;
2123 point = loadmodel->portalpoints;
2132 // note: this check must match the one above or it will usually corrupt memory
2133 // 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
2134 if (p->nodes[0] != p->nodes[1]
2135 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2136 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2138 // first make the back to front portal (forward portal)
2139 portal->points = point;
2140 portal->numpoints = p->winding->numpoints;
2141 portal->plane.dist = p->plane.dist;
2142 VectorCopy(p->plane.normal, portal->plane.normal);
2143 portal->here = (mleaf_t *)p->nodes[1];
2144 portal->past = (mleaf_t *)p->nodes[0];
2146 for (j = 0;j < portal->numpoints;j++)
2148 VectorCopy(p->winding->points[j], point->position);
2151 PlaneClassify(&portal->plane);
2153 // link into leaf's portal chain
2154 portal->next = portal->here->portals;
2155 portal->here->portals = portal;
2157 // advance to next portal
2160 // then make the front to back portal (backward portal)
2161 portal->points = point;
2162 portal->numpoints = p->winding->numpoints;
2163 portal->plane.dist = -p->plane.dist;
2164 VectorNegate(p->plane.normal, portal->plane.normal);
2165 portal->here = (mleaf_t *)p->nodes[0];
2166 portal->past = (mleaf_t *)p->nodes[1];
2168 for (j = portal->numpoints - 1;j >= 0;j--)
2170 VectorCopy(p->winding->points[j], point->position);
2173 PlaneClassify(&portal->plane);
2175 // link into leaf's portal chain
2176 portal->next = portal->here->portals;
2177 portal->here->portals = portal;
2179 // advance to next portal
2182 FreeWinding(p->winding);
2194 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2197 Host_Error ("AddPortalToNodes: NULL front node");
2199 Host_Error ("AddPortalToNodes: NULL back node");
2200 if (p->nodes[0] || p->nodes[1])
2201 Host_Error ("AddPortalToNodes: already included");
2202 // 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
2204 p->nodes[0] = front;
2205 p->next[0] = (portal_t *)front->portals;
2206 front->portals = (mportal_t *)p;
2209 p->next[1] = (portal_t *)back->portals;
2210 back->portals = (mportal_t *)p;
2215 RemovePortalFromNode
2218 static void RemovePortalFromNodes(portal_t *portal)
2222 void **portalpointer;
2224 for (i = 0;i < 2;i++)
2226 node = portal->nodes[i];
2228 portalpointer = (void **) &node->portals;
2233 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2237 if (portal->nodes[0] == node)
2239 *portalpointer = portal->next[0];
2240 portal->nodes[0] = NULL;
2242 else if (portal->nodes[1] == node)
2244 *portalpointer = portal->next[1];
2245 portal->nodes[1] = NULL;
2248 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2252 if (t->nodes[0] == node)
2253 portalpointer = (void **) &t->next[0];
2254 else if (t->nodes[1] == node)
2255 portalpointer = (void **) &t->next[1];
2257 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2262 static void Mod_RecursiveNodePortals (mnode_t *node)
2265 mnode_t *front, *back, *other_node;
2266 mplane_t clipplane, *plane;
2267 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2268 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2270 // if a leaf, we're done
2274 plane = node->plane;
2276 front = node->children[0];
2277 back = node->children[1];
2279 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2281 // create the new portal by generating a polygon for the node plane,
2282 // and clipping it by all of the other portals (which came from nodes above this one)
2283 nodeportal = AllocPortal ();
2284 nodeportal->plane = *node->plane;
2286 nodeportalwinding = BaseWindingForPlane (node->plane);
2287 side = 0; // shut up compiler warning
2288 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2290 clipplane = portal->plane;
2291 if (portal->nodes[0] == portal->nodes[1])
2292 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2293 if (portal->nodes[0] == node)
2295 else if (portal->nodes[1] == node)
2297 clipplane.dist = -clipplane.dist;
2298 VectorNegate (clipplane.normal, clipplane.normal);
2302 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2304 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2305 if (!nodeportalwinding)
2307 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2312 if (nodeportalwinding)
2314 // if the plane was not clipped on all sides, there was an error
2315 nodeportal->winding = nodeportalwinding;
2316 AddPortalToNodes (nodeportal, front, back);
2319 // split the portals of this node along this node's plane and assign them to the children of this node
2320 // (migrating the portals downward through the tree)
2321 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2323 if (portal->nodes[0] == portal->nodes[1])
2324 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2325 if (portal->nodes[0] == node)
2327 else if (portal->nodes[1] == node)
2330 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2331 nextportal = portal->next[side];
2333 other_node = portal->nodes[!side];
2334 RemovePortalFromNodes (portal);
2336 // cut the portal into two portals, one on each side of the node plane
2337 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2342 AddPortalToNodes (portal, back, other_node);
2344 AddPortalToNodes (portal, other_node, back);
2350 AddPortalToNodes (portal, front, other_node);
2352 AddPortalToNodes (portal, other_node, front);
2356 // the winding is split
2357 splitportal = AllocPortal ();
2358 temp = splitportal->chain;
2359 *splitportal = *portal;
2360 splitportal->chain = temp;
2361 splitportal->winding = backwinding;
2362 FreeWinding (portal->winding);
2363 portal->winding = frontwinding;
2367 AddPortalToNodes (portal, front, other_node);
2368 AddPortalToNodes (splitportal, back, other_node);
2372 AddPortalToNodes (portal, other_node, front);
2373 AddPortalToNodes (splitportal, other_node, back);
2377 Mod_RecursiveNodePortals(front);
2378 Mod_RecursiveNodePortals(back);
2382 static void Mod_MakePortals(void)
2385 Mod_RecursiveNodePortals (loadmodel->nodes);
2386 Mod_FinalizePortals();
2394 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2399 mempool_t *mainmempool;
2402 mod->type = mod_brush;
2404 header = (dheader_t *)buffer;
2406 i = LittleLong (header->version);
2407 if (i != BSPVERSION && i != 30)
2408 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2409 mod->ishlbsp = i == 30;
2410 if (loadmodel->isworldmodel)
2411 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2413 // swap all the lumps
2414 mod_base = (qbyte *)header;
2416 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2417 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2421 // store which lightmap format to use
2422 mod->lightmaprgba = r_lightmaprgba.integer;
2424 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2425 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2426 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2427 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2428 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2429 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2430 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2431 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2432 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2433 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2434 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2435 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2436 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2437 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2438 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2443 mod->numframes = 2; // regular and alternate animation
2445 mainmempool = mod->mempool;
2446 loadname = mod->name;
2448 Mod_LoadLightList ();
2451 // set up the submodels (FIXME: this is confusing)
2453 for (i = 0;i < mod->numsubmodels;i++)
2456 float dist, modelyawradius, modelradius, *vec;
2459 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2460 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2464 bm = &mod->submodels[i];
2466 mod->hulls[0].firstclipnode = bm->headnode[0];
2467 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2469 mod->hulls[j].firstclipnode = bm->headnode[j];
2470 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2473 mod->firstmodelsurface = bm->firstface;
2474 mod->nummodelsurfaces = bm->numfaces;
2476 mod->DrawSky = NULL;
2477 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2478 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2480 // we only need to have a drawsky function if it is used (usually only on world model)
2481 if (surf->shader == &Cshader_sky)
2482 mod->DrawSky = R_DrawBrushModelSky;
2483 for (k = 0;k < surf->numedges;k++)
2485 l = mod->surfedges[k + surf->firstedge];
2487 vec = mod->vertexes[mod->edges[l].v[0]].position;
2489 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2490 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2491 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2492 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2493 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2494 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2495 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2496 dist = vec[0]*vec[0]+vec[1]*vec[1];
2497 if (modelyawradius < dist)
2498 modelyawradius = dist;
2499 dist += vec[2]*vec[2];
2500 if (modelradius < dist)
2504 modelyawradius = sqrt(modelyawradius);
2505 modelradius = sqrt(modelradius);
2506 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2507 mod->yawmins[2] = mod->normalmins[2];
2508 mod->yawmaxs[2] = mod->normalmaxs[2];
2509 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2510 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2511 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2512 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2514 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2515 VectorClear(mod->normalmins);
2516 VectorClear(mod->normalmaxs);
2517 VectorClear(mod->yawmins);
2518 VectorClear(mod->yawmaxs);
2519 VectorClear(mod->rotatedmins);
2520 VectorClear(mod->rotatedmaxs);
2523 mod->numleafs = bm->visleafs;
2525 mod->Draw = R_DrawBrushModelNormal;
2526 mod->DrawShadow = NULL;
2528 Mod_BrushSortedSurfaces(mod, mainmempool);
2530 // LordHavoc: only register submodels if it is the world
2531 // (prevents bsp models from replacing world submodels)
2532 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2535 // duplicate the basic information
2536 sprintf (name, "*%i", i+1);
2537 loadmodel = Mod_FindName (name);
2539 strcpy (loadmodel->name, name);
2540 // textures and memory belong to the main model
2541 loadmodel->texturepool = NULL;
2542 loadmodel->mempool = NULL;