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"};
40 void Mod_BrushInit (void)
42 Cvar_RegisterVariable(&r_subdivide_size);
43 Cvar_RegisterVariable(&halflifebsp);
44 Cvar_RegisterVariable(&r_novis);
45 Cvar_RegisterVariable(&r_miplightmaps);
46 Cvar_RegisterVariable(&r_lightmaprgba);
47 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
48 Cvar_RegisterVariable(&r_nosurftextures);
49 memset(mod_novis, 0xff, sizeof(mod_novis));
52 void Mod_Brush_SERAddEntity(void)
54 R_Clip_AddBox(currentrenderentity->mins, currentrenderentity->maxs, R_Entity_Callback, currentrenderentity, NULL);
62 mleaf_t *Mod_PointInLeaf (vec3_t p, model_t *model)
66 Mod_CheckLoaded(model);
68 // LordHavoc: modified to start at first clip node,
69 // in other words: first node of the (sub)model
70 node = model->nodes + model->hulls[0].firstclipnode;
71 while (node->contents == 0)
72 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
74 return (mleaf_t *)node;
77 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
79 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
80 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
81 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
83 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
84 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
86 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
87 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
97 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
99 static qbyte decompressed[MAX_MAP_LEAFS/8];
104 row = (model->numleafs+7)>>3;
122 } while (out - decompressed < row);
127 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
129 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
131 return Mod_DecompressVis (leaf->compressed_vis, model);
139 static void Mod_LoadTextures (lump_t *l)
141 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
143 texture_t *tx, *tx2, *anims[10], *altanims[10];
145 qbyte *data, *mtdata, *data2;
148 loadmodel->textures = NULL;
153 m = (dmiptexlump_t *)(mod_base + l->fileofs);
155 m->nummiptex = LittleLong (m->nummiptex);
157 // add two slots for notexture walls and notexture liquids
158 loadmodel->numtextures = m->nummiptex + 2;
159 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
161 // fill out all slots with notexture
162 for (i = 0;i < loadmodel->numtextures;i++)
164 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
167 tx->texture = r_notexture;
168 if (i == loadmodel->numtextures - 1)
169 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
172 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
174 // LordHavoc: mostly rewritten map texture loader
175 for (i = 0;i < m->nummiptex;i++)
177 dofs[i] = LittleLong(dofs[i]);
178 if (dofs[i] == -1 || r_nosurftextures.integer)
180 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
182 // make sure name is no more than 15 characters
183 for (j = 0;dmiptex->name[j] && j < 15;j++)
184 name[j] = dmiptex->name[j];
187 mtwidth = LittleLong (dmiptex->width);
188 mtheight = LittleLong (dmiptex->height);
190 j = LittleLong (dmiptex->offsets[0]);
194 if (j < 40 || j + mtwidth * mtheight > l->filelen)
196 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
199 mtdata = (qbyte *)dmiptex + j;
202 if ((mtwidth & 15) || (mtheight & 15))
203 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
205 // LordHavoc: force all names to lowercase
206 for (j = 0;name[j];j++)
207 if (name[j] >= 'A' && name[j] <= 'Z')
208 name[j] += 'a' - 'A';
210 tx = loadmodel->textures[i];
211 strcpy(tx->name, name);
213 tx->height = mtheight;
215 tx->glowtexture = NULL;
216 tx->fogtexture = NULL;
220 sprintf(tx->name, "unnamed%i", i);
221 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
224 // LordHavoc: HL sky textures are entirely different than quake
225 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
227 data = loadimagepixels(tx->name, false, 0, 0);
230 if (image_width == 256 && image_height == 128)
232 if (loadmodel->isworldmodel)
239 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
242 else if (loadmodel->isworldmodel)
243 R_InitSky (mtdata, 1);
245 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
247 tx->fogtexture = image_masktex;
248 strcpy(name, tx->name);
249 strcat(name, "_glow");
250 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
254 if (loadmodel->ishlbsp)
256 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
259 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
260 if (R_TextureHasAlpha(tx->texture))
263 for (j = 0;j < image_width * image_height;j++)
264 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
265 strcpy(name, tx->name);
266 strcat(name, "_fog");
267 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
271 else if ((data = W_GetTexture(tx->name)))
273 // get the size from the wad texture
274 tx->width = image_width;
275 tx->height = image_height;
276 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
277 if (R_TextureHasAlpha(tx->texture))
280 for (j = 0;j < image_width * image_height;j++)
281 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
282 strcpy(name, tx->name);
283 strcat(name, "_fog");
284 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
292 tx->texture = r_notexture;
297 if (mtdata) // texture included
302 if (r_fullbrights.value && tx->name[0] != '*')
304 for (j = 0;j < tx->width*tx->height;j++)
306 if (data[j] >= 224) // fullbright
315 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
316 for (j = 0;j < tx->width*tx->height;j++)
317 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
318 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
319 strcpy(name, tx->name);
320 strcat(name, "_glow");
321 for (j = 0;j < tx->width*tx->height;j++)
322 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
323 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
327 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
329 else // no texture, and no external replacement texture was found
333 tx->texture = r_notexture;
338 if (tx->name[0] == '*')
340 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
341 // LordHavoc: some turbulent textures should be fullbright and solid
342 if (!strncmp(tx->name,"*lava",5)
343 || !strncmp(tx->name,"*teleport",9)
344 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
345 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
347 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
348 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
351 tx->flags |= SURF_LIGHTMAP;
352 if (!R_TextureHasAlpha(tx->texture))
353 tx->flags |= SURF_CLIPSOLID;
357 // sequence the animations
358 for (i = 0;i < m->nummiptex;i++)
360 tx = loadmodel->textures[i];
361 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
363 if (tx->anim_total[0] || tx->anim_total[1])
364 continue; // already sequenced
366 // find the number of frames in the animation
367 memset (anims, 0, sizeof(anims));
368 memset (altanims, 0, sizeof(altanims));
370 for (j = i;j < m->nummiptex;j++)
372 tx2 = loadmodel->textures[j];
373 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
377 if (num >= '0' && num <= '9')
378 anims[num - '0'] = tx2;
379 else if (num >= 'a' && num <= 'j')
380 altanims[num - 'a'] = tx2;
382 Con_Printf ("Bad animating texture %s\n", tx->name);
386 for (j = 0;j < 10;j++)
393 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
396 for (j = 0;j < max;j++)
400 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
404 for (j = 0;j < altmax;j++)
408 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
417 // if there is no alternate animation, duplicate the primary
418 // animation into the alternate
420 for (k = 0;k < 10;k++)
421 altanims[k] = anims[k];
424 // link together the primary animation
425 for (j = 0;j < max;j++)
428 tx2->animated = true;
429 tx2->anim_total[0] = max;
430 tx2->anim_total[1] = altmax;
431 for (k = 0;k < 10;k++)
433 tx2->anim_frames[0][k] = anims[k];
434 tx2->anim_frames[1][k] = altanims[k];
438 // if there really is an alternate anim...
439 if (anims[0] != altanims[0])
441 // link together the alternate animation
442 for (j = 0;j < altmax;j++)
445 tx2->animated = true;
446 // the primary/alternate are reversed here
447 tx2->anim_total[0] = altmax;
448 tx2->anim_total[1] = max;
449 for (k = 0;k < 10;k++)
451 tx2->anim_frames[0][k] = altanims[k];
452 tx2->anim_frames[1][k] = anims[k];
464 static void Mod_LoadLighting (lump_t *l)
467 qbyte *in, *out, *data, d;
468 char litfilename[1024];
469 loadmodel->lightdata = NULL;
470 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
472 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
473 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
475 else // LordHavoc: bsp version 29 (normal white lighting)
477 // LordHavoc: hope is not lost yet, check for a .lit file to load
478 strcpy(litfilename, loadmodel->name);
479 COM_StripExtension(litfilename, litfilename);
480 strcat(litfilename, ".lit");
481 data = (qbyte*) COM_LoadFile (litfilename, false);
484 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
486 i = LittleLong(((int *)data)[1]);
489 Con_DPrintf("%s loaded", litfilename);
490 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
491 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
497 Con_Printf("Unknown .lit file version (%d)\n", i);
504 Con_Printf("Empty .lit file, ignoring\n");
506 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
510 // LordHavoc: oh well, expand the white lighting data
513 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
514 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
515 out = loadmodel->lightdata;
516 memcpy (in, mod_base + l->fileofs, l->filelen);
517 for (i = 0;i < l->filelen;i++)
527 void Mod_LoadLightList(void)
530 char lightsfilename[1024], *s, *t, *lightsstring;
533 strcpy(lightsfilename, loadmodel->name);
534 COM_StripExtension(lightsfilename, lightsfilename);
535 strcat(lightsfilename, ".lights");
536 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
542 while (*s && *s != '\n')
546 Mem_Free(lightsstring);
547 Host_Error("lights file must end with a newline\n");
552 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
555 while (*s && n < numlights)
558 while (*s && *s != '\n')
562 Mem_Free(lightsstring);
563 Host_Error("misparsed lights file!\n");
565 e = loadmodel->lights + n;
567 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);
571 Mem_Free(lightsstring);
572 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);
579 Mem_Free(lightsstring);
580 Host_Error("misparsed lights file!\n");
582 loadmodel->numlights = numlights;
583 Mem_Free(lightsstring);
593 static void Mod_LoadVisibility (lump_t *l)
595 loadmodel->visdata = NULL;
598 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
599 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
602 // used only for HalfLife maps
603 void Mod_ParseWadsFromEntityLump(char *data)
605 char key[128], value[4096];
610 data = COM_Parse(data);
613 if (com_token[0] != '{')
617 data = COM_Parse(data);
620 if (com_token[0] == '}')
621 break; // end of worldspawn
622 if (com_token[0] == '_')
623 strcpy(key, com_token + 1);
625 strcpy(key, com_token);
626 while (key[strlen(key)-1] == ' ') // remove trailing spaces
627 key[strlen(key)-1] = 0;
628 data = COM_Parse(data);
631 strcpy(value, com_token);
632 if (!strcmp("wad", key)) // for HalfLife maps
634 if (loadmodel->ishlbsp)
637 for (i = 0;i < 4096;i++)
638 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
644 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
645 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
647 else if (value[i] == ';' || value[i] == 0)
651 strcpy(wadname, "textures/");
652 strcat(wadname, &value[j]);
653 W_LoadTextureWadFile (wadname, false);
670 static void Mod_LoadEntities (lump_t *l)
672 loadmodel->entities = NULL;
675 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
676 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
677 if (loadmodel->ishlbsp)
678 Mod_ParseWadsFromEntityLump(loadmodel->entities);
687 static void Mod_LoadVertexes (lump_t *l)
693 in = (void *)(mod_base + l->fileofs);
694 if (l->filelen % sizeof(*in))
695 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
696 count = l->filelen / sizeof(*in);
697 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
699 loadmodel->vertexes = out;
700 loadmodel->numvertexes = count;
702 for ( i=0 ; i<count ; i++, in++, out++)
704 out->position[0] = LittleFloat (in->point[0]);
705 out->position[1] = LittleFloat (in->point[1]);
706 out->position[2] = LittleFloat (in->point[2]);
715 static void Mod_LoadSubmodels (lump_t *l)
721 in = (void *)(mod_base + l->fileofs);
722 if (l->filelen % sizeof(*in))
723 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
724 count = l->filelen / sizeof(*in);
725 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
727 loadmodel->submodels = out;
728 loadmodel->numsubmodels = count;
730 for ( i=0 ; i<count ; i++, in++, out++)
732 for (j=0 ; j<3 ; j++)
734 // spread the mins / maxs by a pixel
735 out->mins[j] = LittleFloat (in->mins[j]) - 1;
736 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
737 out->origin[j] = LittleFloat (in->origin[j]);
739 for (j=0 ; j<MAX_MAP_HULLS ; j++)
740 out->headnode[j] = LittleLong (in->headnode[j]);
741 out->visleafs = LittleLong (in->visleafs);
742 out->firstface = LittleLong (in->firstface);
743 out->numfaces = LittleLong (in->numfaces);
752 static void Mod_LoadEdges (lump_t *l)
758 in = (void *)(mod_base + l->fileofs);
759 if (l->filelen % sizeof(*in))
760 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
761 count = l->filelen / sizeof(*in);
762 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
764 loadmodel->edges = out;
765 loadmodel->numedges = count;
767 for ( i=0 ; i<count ; i++, in++, out++)
769 out->v[0] = (unsigned short)LittleShort(in->v[0]);
770 out->v[1] = (unsigned short)LittleShort(in->v[1]);
779 static void Mod_LoadTexinfo (lump_t *l)
783 int i, j, k, count, miptex;
785 in = (void *)(mod_base + l->fileofs);
786 if (l->filelen % sizeof(*in))
787 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
788 count = l->filelen / sizeof(*in);
789 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
791 loadmodel->texinfo = out;
792 loadmodel->numtexinfo = count;
794 for (i = 0;i < count;i++, in++, out++)
796 for (k = 0;k < 2;k++)
797 for (j = 0;j < 4;j++)
798 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
800 miptex = LittleLong (in->miptex);
801 out->flags = LittleLong (in->flags);
804 if (loadmodel->textures)
806 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
807 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
809 out->texture = loadmodel->textures[miptex];
811 if (out->texture == NULL)
813 // choose either the liquid notexture, or the normal notexture
814 if (out->flags & TEX_SPECIAL)
815 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
817 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
826 Fills in s->texturemins[] and s->extents[]
829 static void CalcSurfaceExtents (msurface_t *s)
831 float mins[2], maxs[2], val;
835 int bmins[2], bmaxs[2];
837 mins[0] = mins[1] = 999999999;
838 maxs[0] = maxs[1] = -999999999;
842 for (i=0 ; i<s->numedges ; i++)
844 e = loadmodel->surfedges[s->firstedge+i];
846 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
848 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
850 for (j=0 ; j<2 ; j++)
852 val = v->position[0] * tex->vecs[j][0] +
853 v->position[1] * tex->vecs[j][1] +
854 v->position[2] * tex->vecs[j][2] +
863 for (i=0 ; i<2 ; i++)
865 bmins[i] = floor(mins[i]/16);
866 bmaxs[i] = ceil(maxs[i]/16);
868 s->texturemins[i] = bmins[i] * 16;
869 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
874 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
879 mins[0] = mins[1] = mins[2] = 9999;
880 maxs[0] = maxs[1] = maxs[2] = -9999;
882 for (i = 0;i < numverts;i++)
884 for (j = 0;j < 3;j++, v++)
894 #define MAX_SUBDIVPOLYTRIANGLES 4096
895 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
897 static int subdivpolyverts, subdivpolytriangles;
898 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
899 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
901 static int subdivpolylookupvert(vec3_t v)
904 for (i = 0;i < subdivpolyverts;i++)
905 if (subdivpolyvert[i][0] == v[0]
906 && subdivpolyvert[i][1] == v[1]
907 && subdivpolyvert[i][2] == v[2])
909 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
910 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
911 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
912 return subdivpolyverts++;
915 static void SubdividePolygon (int numverts, float *verts)
917 int i, i1, i2, i3, f, b, c, p;
918 vec3_t mins, maxs, front[256], back[256];
919 float m, *pv, *cv, dist[256], frac;
922 Host_Error ("SubdividePolygon: ran out of verts in buffer");
924 BoundPoly (numverts, verts, mins, maxs);
926 for (i = 0;i < 3;i++)
928 m = (mins[i] + maxs[i]) * 0.5;
929 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
936 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
940 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
944 VectorCopy (pv, front[f]);
949 VectorCopy (pv, back[b]);
952 if (dist[p] == 0 || dist[c] == 0)
954 if ( (dist[p] > 0) != (dist[c] > 0) )
957 frac = dist[p] / (dist[p] - dist[c]);
958 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
959 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
960 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
966 SubdividePolygon (f, front[0]);
967 SubdividePolygon (b, back[0]);
971 i1 = subdivpolylookupvert(verts);
972 i2 = subdivpolylookupvert(verts + 3);
973 for (i = 2;i < numverts;i++)
975 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
977 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
981 i3 = subdivpolylookupvert(verts + i * 3);
982 subdivpolyindex[subdivpolytriangles][0] = i1;
983 subdivpolyindex[subdivpolytriangles][1] = i2;
984 subdivpolyindex[subdivpolytriangles][2] = i3;
986 subdivpolytriangles++;
994 Breaks a polygon up along axial 64 unit
995 boundaries so that turbulent and sky warps
996 can be done reasonably.
999 void Mod_GenerateWarpMesh (msurface_t *surf)
1005 subdivpolytriangles = 0;
1006 subdivpolyverts = 0;
1007 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1008 if (subdivpolytriangles < 1)
1009 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1011 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1012 mesh->numverts = subdivpolyverts;
1013 mesh->numtriangles = subdivpolytriangles;
1014 mesh->vertex = (surfvertex_t *)(mesh + 1);
1015 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1016 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1018 for (i = 0;i < mesh->numtriangles;i++)
1019 for (j = 0;j < 3;j++)
1020 mesh->index[i*3+j] = subdivpolyindex[i][j];
1022 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1024 VectorCopy(subdivpolyvert[i], v->v);
1025 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1026 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1030 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1032 int i, is, it, *index, smax, tmax;
1037 smax = surf->extents[0] >> 4;
1038 tmax = surf->extents[1] >> 4;
1039 surf->lightmaptexturestride = 0;
1040 surf->lightmaptexture = NULL;
1042 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1043 mesh->numverts = surf->poly_numverts;
1044 mesh->numtriangles = surf->poly_numverts - 2;
1045 mesh->vertex = (surfvertex_t *)(mesh + 1);
1046 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1047 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1049 index = mesh->index;
1050 for (i = 0;i < mesh->numtriangles;i++)
1057 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1059 VectorCopy (in, out->v);
1061 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1062 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1064 out->st[0] = s / surf->texinfo->texture->width;
1065 out->st[1] = t / surf->texinfo->texture->height;
1067 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1068 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1070 // lightmap coordinates
1074 // LordHavoc: calc lightmap data offset for vertex lighting to use
1077 is = bound(0, is, smax);
1078 it = bound(0, it, tmax);
1079 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1083 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1085 int i, is, it, *index, smax, tmax;
1086 float *in, s, t, xbase, ybase, xscale, yscale;
1090 surf->flags |= SURF_LIGHTMAP;
1091 smax = surf->extents[0] >> 4;
1092 tmax = surf->extents[1] >> 4;
1093 if (r_miplightmaps.integer)
1095 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1096 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE, NULL, NULL, 0);
1100 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1101 surf->lightmaptexture = R_ProceduralTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE, NULL, NULL, 0);
1103 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1104 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1105 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1107 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1108 mesh->numverts = surf->poly_numverts;
1109 mesh->numtriangles = surf->poly_numverts - 2;
1110 mesh->vertex = (surfvertex_t *)(mesh + 1);
1111 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1112 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1114 index = mesh->index;
1115 for (i = 0;i < mesh->numtriangles;i++)
1122 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1124 VectorCopy (in, out->v);
1126 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1127 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1129 out->st[0] = s / surf->texinfo->texture->width;
1130 out->st[1] = t / surf->texinfo->texture->height;
1132 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1133 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1135 // lightmap coordinates
1136 out->uv[0] = s * xscale + xbase;
1137 out->uv[1] = t * yscale + ybase;
1139 // LordHavoc: calc lightmap data offset for vertex lighting to use
1142 is = bound(0, is, smax);
1143 it = bound(0, it, tmax);
1144 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1148 void Mod_GenerateVertexMesh (msurface_t *surf)
1155 surf->lightmaptexturestride = 0;
1156 surf->lightmaptexture = NULL;
1158 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1159 mesh->numverts = surf->poly_numverts;
1160 mesh->numtriangles = surf->poly_numverts - 2;
1161 mesh->vertex = (surfvertex_t *)(mesh + 1);
1162 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1163 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1165 index = mesh->index;
1166 for (i = 0;i < mesh->numtriangles;i++)
1173 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1175 VectorCopy (in, out->v);
1176 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1177 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1181 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1188 // convert edges back to a normal polygon
1189 surf->poly_numverts = surf->numedges;
1190 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1191 for (i = 0;i < surf->numedges;i++)
1193 lindex = loadmodel->surfedges[surf->firstedge + i];
1195 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1197 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1198 VectorCopy (vec, vert);
1203 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1205 int j, base, tricount, newvertexcount, *index, *vertexremap;
1206 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1207 if (s->mesh->numtriangles > 1000)
1209 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1214 while (base < s->mesh->numtriangles)
1216 tricount = s->mesh->numtriangles - base;
1217 if (tricount > 1000)
1219 index = s->mesh->index + base * 3;
1223 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1224 for (j = 0;j < tricount * 3;j++)
1225 if (vertexremap[index[j]] < 0)
1226 vertexremap[index[j]] = newvertexcount++;
1228 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1229 newmesh->chain = NULL;
1230 newmesh->numverts = newvertexcount;
1231 newmesh->numtriangles = tricount;
1232 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1233 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1234 for (j = 0;j < tricount * 3;j++)
1236 newmesh->index[j] = vertexremap[index[j]];
1237 // yes this copies the same vertex multiple times in many cases... but that's ok...
1238 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1241 oldmesh->chain = newmesh;
1243 firstmesh = newmesh;
1246 Mem_Free(vertexremap);
1248 s->mesh = firstmesh;
1257 static void Mod_LoadFaces (lump_t *l)
1261 int i, count, surfnum, planenum, ssize, tsize;
1263 in = (void *)(mod_base + l->fileofs);
1264 if (l->filelen % sizeof(*in))
1265 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1266 count = l->filelen / sizeof(*in);
1267 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1269 loadmodel->surfaces = out;
1270 loadmodel->numsurfaces = count;
1272 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1274 // FIXME: validate edges, texinfo, etc?
1275 out->firstedge = LittleLong(in->firstedge);
1276 out->numedges = LittleShort(in->numedges);
1277 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1278 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1280 i = LittleShort (in->texinfo);
1281 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1282 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1283 out->texinfo = loadmodel->texinfo + i;
1284 out->flags = out->texinfo->texture->flags;
1286 planenum = LittleShort(in->planenum);
1287 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1288 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1290 if (LittleShort(in->side))
1291 out->flags |= SURF_PLANEBACK;
1293 out->plane = loadmodel->planes + planenum;
1295 // clear lightmap (filled in later)
1296 out->lightmaptexture = NULL;
1298 // force lightmap upload on first time seeing the surface
1299 out->cached_dlight = true;
1300 out->cached_ambient = -1000;
1301 out->cached_lightscalebit = -1000;
1303 CalcSurfaceExtents (out);
1305 ssize = (out->extents[0] >> 4) + 1;
1306 tsize = (out->extents[1] >> 4) + 1;
1309 for (i = 0;i < MAXLIGHTMAPS;i++)
1310 out->styles[i] = in->styles[i];
1311 i = LittleLong(in->lightofs);
1313 out->samples = NULL;
1314 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1315 out->samples = loadmodel->lightdata + i;
1316 else // LordHavoc: white lighting (bsp version 29)
1317 out->samples = loadmodel->lightdata + (i * 3);
1319 Mod_GenerateSurfacePolygon(out);
1321 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1323 out->shader = &Cshader_sky;
1324 out->samples = NULL;
1325 Mod_GenerateWarpMesh (out);
1327 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1329 out->shader = &Cshader_water;
1330 out->samples = NULL;
1331 Mod_GenerateWarpMesh (out);
1335 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1336 out->flags |= SURF_CLIPSOLID;
1337 if (out->texinfo->flags & TEX_SPECIAL)
1339 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1340 out->shader = &Cshader_water;
1341 out->shader = &Cshader_water;
1342 out->samples = NULL;
1343 Mod_GenerateWarpMesh (out);
1345 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1347 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1348 out->shader = &Cshader_wall_fullbright;
1349 out->samples = NULL;
1350 Mod_GenerateVertexMesh(out);
1354 // stainmap for permanent marks on walls
1355 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1357 memset(out->stainsamples, 255, ssize * tsize * 3);
1358 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1360 out->shader = &Cshader_wall_vertex;
1361 Mod_GenerateVertexLitMesh(out);
1365 out->shader = &Cshader_wall_lightmap;
1366 Mod_GenerateLightmappedMesh(out);
1370 Mod_SplitSurfMeshIfTooBig(out);
1374 static model_t *sortmodel;
1376 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1378 const msurface_t *a, *b;
1379 a = *((const msurface_t **)voida);
1380 b = *((const msurface_t **)voidb);
1381 if (a->shader != b->shader)
1382 return (qbyte *) a->shader - (qbyte *) b->shader;
1383 if (a->texinfo->texture != b->texinfo->texture);
1384 return a->texinfo->texture - b->texinfo->texture;
1388 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1392 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1393 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1394 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1396 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1405 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1407 node->parent = parent;
1408 if (node->contents < 0)
1410 Mod_SetParent (node->children[0], node);
1411 Mod_SetParent (node->children[1], node);
1419 static void Mod_LoadNodes (lump_t *l)
1425 in = (void *)(mod_base + l->fileofs);
1426 if (l->filelen % sizeof(*in))
1427 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1428 count = l->filelen / sizeof(*in);
1429 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1431 loadmodel->nodes = out;
1432 loadmodel->numnodes = count;
1434 for ( i=0 ; i<count ; i++, in++, out++)
1436 for (j=0 ; j<3 ; j++)
1438 out->mins[j] = LittleShort (in->mins[j]);
1439 out->maxs[j] = LittleShort (in->maxs[j]);
1442 p = LittleLong(in->planenum);
1443 out->plane = loadmodel->planes + p;
1445 out->firstsurface = LittleShort (in->firstface);
1446 out->numsurfaces = LittleShort (in->numfaces);
1448 for (j=0 ; j<2 ; j++)
1450 p = LittleShort (in->children[j]);
1452 out->children[j] = loadmodel->nodes + p;
1454 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1458 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1466 static void Mod_LoadLeafs (lump_t *l)
1472 in = (void *)(mod_base + l->fileofs);
1473 if (l->filelen % sizeof(*in))
1474 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1475 count = l->filelen / sizeof(*in);
1476 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1478 loadmodel->leafs = out;
1479 loadmodel->numleafs = count;
1481 for ( i=0 ; i<count ; i++, in++, out++)
1483 for (j=0 ; j<3 ; j++)
1485 out->mins[j] = LittleShort (in->mins[j]);
1486 out->maxs[j] = LittleShort (in->maxs[j]);
1489 p = LittleLong(in->contents);
1492 out->firstmarksurface = loadmodel->marksurfaces +
1493 LittleShort(in->firstmarksurface);
1494 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1496 p = LittleLong(in->visofs);
1498 out->compressed_vis = NULL;
1500 out->compressed_vis = loadmodel->visdata + p;
1502 for (j=0 ; j<4 ; j++)
1503 out->ambient_sound_level[j] = in->ambient_level[j];
1505 // FIXME: Insert caustics here
1514 static void Mod_LoadClipnodes (lump_t *l)
1516 dclipnode_t *in, *out;
1520 in = (void *)(mod_base + l->fileofs);
1521 if (l->filelen % sizeof(*in))
1522 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1523 count = l->filelen / sizeof(*in);
1524 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1526 loadmodel->clipnodes = out;
1527 loadmodel->numclipnodes = count;
1529 if (loadmodel->ishlbsp)
1531 hull = &loadmodel->hulls[1];
1532 hull->clipnodes = out;
1533 hull->firstclipnode = 0;
1534 hull->lastclipnode = count-1;
1535 hull->planes = loadmodel->planes;
1536 hull->clip_mins[0] = -16;
1537 hull->clip_mins[1] = -16;
1538 hull->clip_mins[2] = -36;
1539 hull->clip_maxs[0] = 16;
1540 hull->clip_maxs[1] = 16;
1541 hull->clip_maxs[2] = 36;
1542 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1544 hull = &loadmodel->hulls[2];
1545 hull->clipnodes = out;
1546 hull->firstclipnode = 0;
1547 hull->lastclipnode = count-1;
1548 hull->planes = loadmodel->planes;
1549 hull->clip_mins[0] = -32;
1550 hull->clip_mins[1] = -32;
1551 hull->clip_mins[2] = -32;
1552 hull->clip_maxs[0] = 32;
1553 hull->clip_maxs[1] = 32;
1554 hull->clip_maxs[2] = 32;
1555 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1557 hull = &loadmodel->hulls[3];
1558 hull->clipnodes = out;
1559 hull->firstclipnode = 0;
1560 hull->lastclipnode = count-1;
1561 hull->planes = loadmodel->planes;
1562 hull->clip_mins[0] = -16;
1563 hull->clip_mins[1] = -16;
1564 hull->clip_mins[2] = -18;
1565 hull->clip_maxs[0] = 16;
1566 hull->clip_maxs[1] = 16;
1567 hull->clip_maxs[2] = 18;
1568 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1572 hull = &loadmodel->hulls[1];
1573 hull->clipnodes = out;
1574 hull->firstclipnode = 0;
1575 hull->lastclipnode = count-1;
1576 hull->planes = loadmodel->planes;
1577 hull->clip_mins[0] = -16;
1578 hull->clip_mins[1] = -16;
1579 hull->clip_mins[2] = -24;
1580 hull->clip_maxs[0] = 16;
1581 hull->clip_maxs[1] = 16;
1582 hull->clip_maxs[2] = 32;
1583 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1585 hull = &loadmodel->hulls[2];
1586 hull->clipnodes = out;
1587 hull->firstclipnode = 0;
1588 hull->lastclipnode = count-1;
1589 hull->planes = loadmodel->planes;
1590 hull->clip_mins[0] = -32;
1591 hull->clip_mins[1] = -32;
1592 hull->clip_mins[2] = -24;
1593 hull->clip_maxs[0] = 32;
1594 hull->clip_maxs[1] = 32;
1595 hull->clip_maxs[2] = 64;
1596 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1599 for (i=0 ; i<count ; i++, out++, in++)
1601 out->planenum = LittleLong(in->planenum);
1602 out->children[0] = LittleShort(in->children[0]);
1603 out->children[1] = LittleShort(in->children[1]);
1604 if (out->children[0] >= count || out->children[1] >= count)
1605 Host_Error("Corrupt clipping hull (out of range child)\n");
1613 Duplicate the drawing hull structure as a clipping hull
1616 static void Mod_MakeHull0 (void)
1623 hull = &loadmodel->hulls[0];
1625 in = loadmodel->nodes;
1626 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1628 hull->clipnodes = out;
1629 hull->firstclipnode = 0;
1630 hull->lastclipnode = loadmodel->numnodes - 1;
1631 hull->planes = loadmodel->planes;
1633 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1635 out->planenum = in->plane - loadmodel->planes;
1636 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1637 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1643 Mod_LoadMarksurfaces
1646 static void Mod_LoadMarksurfaces (lump_t *l)
1651 in = (void *)(mod_base + l->fileofs);
1652 if (l->filelen % sizeof(*in))
1653 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1654 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1655 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1657 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1659 j = (unsigned) LittleShort(in[i]);
1660 if (j >= loadmodel->numsurfaces)
1661 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1662 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1671 static void Mod_LoadSurfedges (lump_t *l)
1676 in = (void *)(mod_base + l->fileofs);
1677 if (l->filelen % sizeof(*in))
1678 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1679 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1680 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1682 for (i = 0;i < loadmodel->numsurfedges;i++)
1683 loadmodel->surfedges[i] = LittleLong (in[i]);
1692 static void Mod_LoadPlanes (lump_t *l)
1698 in = (void *)(mod_base + l->fileofs);
1699 if (l->filelen % sizeof(*in))
1700 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1702 loadmodel->numplanes = l->filelen / sizeof(*in);
1703 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1705 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1707 out->normal[0] = LittleFloat (in->normal[0]);
1708 out->normal[1] = LittleFloat (in->normal[1]);
1709 out->normal[2] = LittleFloat (in->normal[2]);
1710 out->dist = LittleFloat (in->dist);
1716 #define MAX_POINTS_ON_WINDING 64
1722 double points[8][3]; // variable sized
1731 static winding_t *NewWinding (int points)
1736 if (points > MAX_POINTS_ON_WINDING)
1737 Sys_Error("NewWinding: too many points\n");
1739 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1740 w = Mem_Alloc(loadmodel->mempool, size);
1741 memset (w, 0, size);
1746 static void FreeWinding (winding_t *w)
1756 static winding_t *BaseWindingForPlane (mplane_t *p)
1758 double org[3], vright[3], vup[3], normal[3];
1761 VectorCopy(p->normal, normal);
1762 VectorVectorsDouble(normal, vright, vup);
1764 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1765 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1767 // project a really big axis aligned box onto the plane
1770 VectorScale (p->normal, p->dist, org);
1772 VectorSubtract (org, vright, w->points[0]);
1773 VectorAdd (w->points[0], vup, w->points[0]);
1775 VectorAdd (org, vright, w->points[1]);
1776 VectorAdd (w->points[1], vup, w->points[1]);
1778 VectorAdd (org, vright, w->points[2]);
1779 VectorSubtract (w->points[2], vup, w->points[2]);
1781 VectorSubtract (org, vright, w->points[3]);
1782 VectorSubtract (w->points[3], vup, w->points[3]);
1793 Clips the winding to the plane, returning the new winding on the positive side
1794 Frees the input winding.
1795 If keepon is true, an exactly on-plane winding will be saved, otherwise
1796 it will be clipped away.
1799 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1801 double dists[MAX_POINTS_ON_WINDING + 1];
1802 int sides[MAX_POINTS_ON_WINDING + 1];
1811 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1813 // determine sides for each point
1814 for (i = 0;i < in->numpoints;i++)
1816 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1817 if (dot > ON_EPSILON)
1818 sides[i] = SIDE_FRONT;
1819 else if (dot < -ON_EPSILON)
1820 sides[i] = SIDE_BACK;
1825 sides[i] = sides[0];
1826 dists[i] = dists[0];
1828 if (keepon && !counts[0] && !counts[1])
1839 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1840 if (maxpts > MAX_POINTS_ON_WINDING)
1841 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1843 neww = NewWinding (maxpts);
1845 for (i = 0;i < in->numpoints;i++)
1847 if (neww->numpoints >= maxpts)
1848 Sys_Error ("ClipWinding: points exceeded estimate");
1852 if (sides[i] == SIDE_ON)
1854 VectorCopy (p1, neww->points[neww->numpoints]);
1859 if (sides[i] == SIDE_FRONT)
1861 VectorCopy (p1, neww->points[neww->numpoints]);
1865 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1868 // generate a split point
1869 p2 = in->points[(i+1)%in->numpoints];
1871 dot = dists[i] / (dists[i]-dists[i+1]);
1872 for (j = 0;j < 3;j++)
1873 { // avoid round off error when possible
1874 if (split->normal[j] == 1)
1875 mid[j] = split->dist;
1876 else if (split->normal[j] == -1)
1877 mid[j] = -split->dist;
1879 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1882 VectorCopy (mid, neww->points[neww->numpoints]);
1886 // free the original winding
1897 Divides a winding by a plane, producing one or two windings. The
1898 original winding is not damaged or freed. If only on one side, the
1899 returned winding will be the input winding. If on both sides, two
1900 new windings will be created.
1903 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1905 double dists[MAX_POINTS_ON_WINDING + 1];
1906 int sides[MAX_POINTS_ON_WINDING + 1];
1915 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1917 // determine sides for each point
1918 for (i = 0;i < in->numpoints;i++)
1920 dot = DotProduct (in->points[i], split->normal);
1923 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1924 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1925 else sides[i] = SIDE_ON;
1928 sides[i] = sides[0];
1929 dists[i] = dists[0];
1931 *front = *back = NULL;
1944 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1946 if (maxpts > MAX_POINTS_ON_WINDING)
1947 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1949 *front = f = NewWinding (maxpts);
1950 *back = b = NewWinding (maxpts);
1952 for (i = 0;i < in->numpoints;i++)
1954 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1955 Sys_Error ("DivideWinding: points exceeded estimate");
1959 if (sides[i] == SIDE_ON)
1961 VectorCopy (p1, f->points[f->numpoints]);
1963 VectorCopy (p1, b->points[b->numpoints]);
1968 if (sides[i] == SIDE_FRONT)
1970 VectorCopy (p1, f->points[f->numpoints]);
1973 else if (sides[i] == SIDE_BACK)
1975 VectorCopy (p1, b->points[b->numpoints]);
1979 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1982 // generate a split point
1983 p2 = in->points[(i+1)%in->numpoints];
1985 dot = dists[i] / (dists[i]-dists[i+1]);
1986 for (j = 0;j < 3;j++)
1987 { // avoid round off error when possible
1988 if (split->normal[j] == 1)
1989 mid[j] = split->dist;
1990 else if (split->normal[j] == -1)
1991 mid[j] = -split->dist;
1993 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1996 VectorCopy (mid, f->points[f->numpoints]);
1998 VectorCopy (mid, b->points[b->numpoints]);
2003 typedef struct portal_s
2006 mnode_t *nodes[2]; // [0] = front side of plane
2007 struct portal_s *next[2];
2009 struct portal_s *chain; // all portals are linked into a list
2013 static portal_t *portalchain;
2020 static portal_t *AllocPortal (void)
2023 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2024 p->chain = portalchain;
2029 static void FreePortal(portal_t *p)
2034 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2036 // calculate children first
2037 if (node->children[0]->contents >= 0)
2038 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2039 if (node->children[1]->contents >= 0)
2040 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2042 // make combined bounding box from children
2043 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2044 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2045 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2046 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2047 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2048 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2051 static void Mod_FinalizePortals(void)
2053 int i, j, numportals, numpoints;
2054 portal_t *p, *pnext;
2057 mleaf_t *leaf, *endleaf;
2060 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2061 leaf = loadmodel->leafs;
2062 endleaf = leaf + loadmodel->numleafs;
2063 for (;leaf < endleaf;leaf++)
2065 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2066 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2073 for (i = 0;i < 2;i++)
2075 leaf = (mleaf_t *)p->nodes[i];
2077 for (j = 0;j < w->numpoints;j++)
2079 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2080 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2081 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2082 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2083 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2084 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2091 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2093 // tally up portal and point counts
2099 // note: this check must match the one below or it will usually corrupt memory
2100 // 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
2101 if (p->winding && p->nodes[0] != p->nodes[1]
2102 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2103 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2106 numpoints += p->winding->numpoints * 2;
2110 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2111 loadmodel->numportals = numportals;
2112 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2113 loadmodel->numportalpoints = numpoints;
2114 // clear all leaf portal chains
2115 for (i = 0;i < loadmodel->numleafs;i++)
2116 loadmodel->leafs[i].portals = NULL;
2117 // process all portals in the global portal chain, while freeing them
2118 portal = loadmodel->portals;
2119 point = loadmodel->portalpoints;
2128 // note: this check must match the one above or it will usually corrupt memory
2129 // 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
2130 if (p->nodes[0] != p->nodes[1]
2131 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2132 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2134 // first make the back to front portal (forward portal)
2135 portal->points = point;
2136 portal->numpoints = p->winding->numpoints;
2137 portal->plane.dist = p->plane.dist;
2138 VectorCopy(p->plane.normal, portal->plane.normal);
2139 portal->here = (mleaf_t *)p->nodes[1];
2140 portal->past = (mleaf_t *)p->nodes[0];
2142 for (j = 0;j < portal->numpoints;j++)
2144 VectorCopy(p->winding->points[j], point->position);
2147 PlaneClassify(&portal->plane);
2149 // link into leaf's portal chain
2150 portal->next = portal->here->portals;
2151 portal->here->portals = portal;
2153 // advance to next portal
2156 // then make the front to back portal (backward portal)
2157 portal->points = point;
2158 portal->numpoints = p->winding->numpoints;
2159 portal->plane.dist = -p->plane.dist;
2160 VectorNegate(p->plane.normal, portal->plane.normal);
2161 portal->here = (mleaf_t *)p->nodes[0];
2162 portal->past = (mleaf_t *)p->nodes[1];
2164 for (j = portal->numpoints - 1;j >= 0;j--)
2166 VectorCopy(p->winding->points[j], point->position);
2169 PlaneClassify(&portal->plane);
2171 // link into leaf's portal chain
2172 portal->next = portal->here->portals;
2173 portal->here->portals = portal;
2175 // advance to next portal
2178 FreeWinding(p->winding);
2190 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2193 Host_Error ("AddPortalToNodes: NULL front node");
2195 Host_Error ("AddPortalToNodes: NULL back node");
2196 if (p->nodes[0] || p->nodes[1])
2197 Host_Error ("AddPortalToNodes: already included");
2198 // 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
2200 p->nodes[0] = front;
2201 p->next[0] = (portal_t *)front->portals;
2202 front->portals = (mportal_t *)p;
2205 p->next[1] = (portal_t *)back->portals;
2206 back->portals = (mportal_t *)p;
2211 RemovePortalFromNode
2214 static void RemovePortalFromNodes(portal_t *portal)
2218 void **portalpointer;
2220 for (i = 0;i < 2;i++)
2222 node = portal->nodes[i];
2224 portalpointer = (void **) &node->portals;
2229 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2233 if (portal->nodes[0] == node)
2235 *portalpointer = portal->next[0];
2236 portal->nodes[0] = NULL;
2238 else if (portal->nodes[1] == node)
2240 *portalpointer = portal->next[1];
2241 portal->nodes[1] = NULL;
2244 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2248 if (t->nodes[0] == node)
2249 portalpointer = (void **) &t->next[0];
2250 else if (t->nodes[1] == node)
2251 portalpointer = (void **) &t->next[1];
2253 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2258 static void Mod_RecursiveNodePortals (mnode_t *node)
2261 mnode_t *front, *back, *other_node;
2262 mplane_t clipplane, *plane;
2263 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2264 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2266 // if a leaf, we're done
2270 plane = node->plane;
2272 front = node->children[0];
2273 back = node->children[1];
2275 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2277 // create the new portal by generating a polygon for the node plane,
2278 // and clipping it by all of the other portals (which came from nodes above this one)
2279 nodeportal = AllocPortal ();
2280 nodeportal->plane = *node->plane;
2282 nodeportalwinding = BaseWindingForPlane (node->plane);
2283 side = 0; // shut up compiler warning
2284 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2286 clipplane = portal->plane;
2287 if (portal->nodes[0] == portal->nodes[1])
2288 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2289 if (portal->nodes[0] == node)
2291 else if (portal->nodes[1] == node)
2293 clipplane.dist = -clipplane.dist;
2294 VectorNegate (clipplane.normal, clipplane.normal);
2298 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2300 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2301 if (!nodeportalwinding)
2303 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2308 if (nodeportalwinding)
2310 // if the plane was not clipped on all sides, there was an error
2311 nodeportal->winding = nodeportalwinding;
2312 AddPortalToNodes (nodeportal, front, back);
2315 // split the portals of this node along this node's plane and assign them to the children of this node
2316 // (migrating the portals downward through the tree)
2317 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2319 if (portal->nodes[0] == portal->nodes[1])
2320 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2321 if (portal->nodes[0] == node)
2323 else if (portal->nodes[1] == node)
2326 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2327 nextportal = portal->next[side];
2329 other_node = portal->nodes[!side];
2330 RemovePortalFromNodes (portal);
2332 // cut the portal into two portals, one on each side of the node plane
2333 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2338 AddPortalToNodes (portal, back, other_node);
2340 AddPortalToNodes (portal, other_node, back);
2346 AddPortalToNodes (portal, front, other_node);
2348 AddPortalToNodes (portal, other_node, front);
2352 // the winding is split
2353 splitportal = AllocPortal ();
2354 temp = splitportal->chain;
2355 *splitportal = *portal;
2356 splitportal->chain = temp;
2357 splitportal->winding = backwinding;
2358 FreeWinding (portal->winding);
2359 portal->winding = frontwinding;
2363 AddPortalToNodes (portal, front, other_node);
2364 AddPortalToNodes (splitportal, back, other_node);
2368 AddPortalToNodes (portal, other_node, front);
2369 AddPortalToNodes (splitportal, other_node, back);
2373 Mod_RecursiveNodePortals(front);
2374 Mod_RecursiveNodePortals(back);
2378 static void Mod_MakePortals(void)
2381 Mod_RecursiveNodePortals (loadmodel->nodes);
2382 Mod_FinalizePortals();
2390 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2395 mempool_t *mainmempool;
2398 mod->type = mod_brush;
2400 header = (dheader_t *)buffer;
2402 i = LittleLong (header->version);
2403 if (i != BSPVERSION && i != 30)
2404 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2405 mod->ishlbsp = i == 30;
2406 if (loadmodel->isworldmodel)
2407 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2409 // swap all the lumps
2410 mod_base = (qbyte *)header;
2412 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2413 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2417 // store which lightmap format to use
2418 mod->lightmaprgba = r_lightmaprgba.integer;
2420 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2421 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2422 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2423 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2424 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2425 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2426 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2427 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2428 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2429 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2430 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2431 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2432 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2433 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2434 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2439 mod->numframes = 2; // regular and alternate animation
2441 mainmempool = mod->mempool;
2442 loadname = mod->name;
2444 Mod_LoadLightList ();
2447 // set up the submodels (FIXME: this is confusing)
2449 for (i = 0;i < mod->numsubmodels;i++)
2452 float dist, modelyawradius, modelradius, *vec;
2455 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2456 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2460 bm = &mod->submodels[i];
2462 mod->hulls[0].firstclipnode = bm->headnode[0];
2463 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2465 mod->hulls[j].firstclipnode = bm->headnode[j];
2466 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2469 mod->firstmodelsurface = bm->firstface;
2470 mod->nummodelsurfaces = bm->numfaces;
2472 mod->DrawSky = NULL;
2473 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2474 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2476 // we only need to have a drawsky function if it is used (usually only on world model)
2477 if (surf->shader == &Cshader_sky)
2478 mod->DrawSky = R_DrawBrushModelSky;
2479 for (k = 0;k < surf->numedges;k++)
2481 l = mod->surfedges[k + surf->firstedge];
2483 vec = mod->vertexes[mod->edges[l].v[0]].position;
2485 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2486 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2487 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2488 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2489 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2490 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2491 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2492 dist = vec[0]*vec[0]+vec[1]*vec[1];
2493 if (modelyawradius < dist)
2494 modelyawradius = dist;
2495 dist += vec[2]*vec[2];
2496 if (modelradius < dist)
2500 modelyawradius = sqrt(modelyawradius);
2501 modelradius = sqrt(modelradius);
2502 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2503 mod->yawmins[2] = mod->normalmins[2];
2504 mod->yawmaxs[2] = mod->normalmaxs[2];
2505 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2506 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2507 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2508 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2510 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2511 VectorClear(mod->normalmins);
2512 VectorClear(mod->normalmaxs);
2513 VectorClear(mod->yawmins);
2514 VectorClear(mod->yawmaxs);
2515 VectorClear(mod->rotatedmins);
2516 VectorClear(mod->rotatedmaxs);
2519 mod->numleafs = bm->visleafs;
2521 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2522 mod->Draw = R_DrawBrushModelNormal;
2523 mod->DrawShadow = NULL;
2525 Mod_BrushSortedSurfaces(mod, mainmempool);
2527 // LordHavoc: only register submodels if it is the world
2528 // (prevents bsp models from replacing world submodels)
2529 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2532 // duplicate the basic information
2533 sprintf (name, "*%i", i+1);
2534 loadmodel = Mod_FindName (name);
2536 strcpy (loadmodel->name, name);
2537 // textures and memory belong to the main model
2538 loadmodel->texturepool = NULL;
2539 loadmodel->mempool = NULL;