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++)
895 #define MAX_SUBDIVPOLYTRIANGLES 4096
896 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
898 static int subdivpolyverts, subdivpolytriangles;
899 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
900 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
902 static int subdivpolylookupvert(vec3_t v)
905 for (i = 0;i < subdivpolyverts;i++)
906 if (subdivpolyvert[i][0] == v[0]
907 && subdivpolyvert[i][1] == v[1]
908 && subdivpolyvert[i][2] == v[2])
910 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
911 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
912 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
913 return subdivpolyverts++;
916 static void SubdividePolygon (int numverts, float *verts)
918 int i, i1, i2, i3, f, b, c, p;
919 vec3_t mins, maxs, front[256], back[256];
920 float m, *pv, *cv, dist[256], frac;
923 Host_Error ("SubdividePolygon: ran out of verts in buffer");
925 BoundPoly (numverts, verts, mins, maxs);
927 for (i = 0;i < 3;i++)
929 m = (mins[i] + maxs[i]) * 0.5;
930 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
937 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
941 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
945 VectorCopy (pv, front[f]);
950 VectorCopy (pv, back[b]);
953 if (dist[p] == 0 || dist[c] == 0)
955 if ( (dist[p] > 0) != (dist[c] > 0) )
958 frac = dist[p] / (dist[p] - dist[c]);
959 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
960 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
961 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
967 SubdividePolygon (f, front[0]);
968 SubdividePolygon (b, back[0]);
972 i1 = subdivpolylookupvert(verts);
973 i2 = subdivpolylookupvert(verts + 3);
974 for (i = 2;i < numverts;i++)
976 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
978 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
982 i3 = subdivpolylookupvert(verts + i * 3);
983 subdivpolyindex[subdivpolytriangles][0] = i1;
984 subdivpolyindex[subdivpolytriangles][1] = i2;
985 subdivpolyindex[subdivpolytriangles][2] = i3;
987 subdivpolytriangles++;
995 Breaks a polygon up along axial 64 unit
996 boundaries so that turbulent and sky warps
997 can be done reasonably.
1000 void Mod_GenerateWarpMesh (msurface_t *surf)
1006 subdivpolytriangles = 0;
1007 subdivpolyverts = 0;
1008 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1009 if (subdivpolytriangles < 1)
1010 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1012 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1013 mesh->numverts = subdivpolyverts;
1014 mesh->numtriangles = subdivpolytriangles;
1015 mesh->vertex = (surfvertex_t *)(mesh + 1);
1016 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1017 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1019 for (i = 0;i < mesh->numtriangles;i++)
1020 for (j = 0;j < 3;j++)
1021 mesh->index[i*3+j] = subdivpolyindex[i][j];
1023 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1025 VectorCopy(subdivpolyvert[i], v->v);
1026 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1027 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1031 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1033 int i, is, it, *index, smax, tmax;
1038 smax = surf->extents[0] >> 4;
1039 tmax = surf->extents[1] >> 4;
1040 surf->lightmaptexturestride = 0;
1041 surf->lightmaptexture = NULL;
1043 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1044 mesh->numverts = surf->poly_numverts;
1045 mesh->numtriangles = surf->poly_numverts - 2;
1046 mesh->vertex = (surfvertex_t *)(mesh + 1);
1047 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1048 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1050 index = mesh->index;
1051 for (i = 0;i < mesh->numtriangles;i++)
1058 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1060 VectorCopy (in, out->v);
1062 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1063 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1065 out->st[0] = s / surf->texinfo->texture->width;
1066 out->st[1] = t / surf->texinfo->texture->height;
1068 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1069 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1071 // lightmap coordinates
1075 // LordHavoc: calc lightmap data offset for vertex lighting to use
1078 is = bound(0, is, smax);
1079 it = bound(0, it, tmax);
1080 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1084 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1086 int i, is, it, *index, smax, tmax;
1087 float *in, s, t, xbase, ybase, xscale, yscale;
1091 surf->flags |= SURF_LIGHTMAP;
1092 smax = surf->extents[0] >> 4;
1093 tmax = surf->extents[1] >> 4;
1094 if (r_miplightmaps.integer)
1096 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1097 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);
1101 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1102 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);
1104 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1105 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1106 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1108 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1109 mesh->numverts = surf->poly_numverts;
1110 mesh->numtriangles = surf->poly_numverts - 2;
1111 mesh->vertex = (surfvertex_t *)(mesh + 1);
1112 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1113 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1115 index = mesh->index;
1116 for (i = 0;i < mesh->numtriangles;i++)
1123 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1125 VectorCopy (in, out->v);
1127 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1128 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1130 out->st[0] = s / surf->texinfo->texture->width;
1131 out->st[1] = t / surf->texinfo->texture->height;
1133 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1134 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1136 // lightmap coordinates
1137 out->uv[0] = s * xscale + xbase;
1138 out->uv[1] = t * yscale + ybase;
1140 // LordHavoc: calc lightmap data offset for vertex lighting to use
1143 is = bound(0, is, smax);
1144 it = bound(0, it, tmax);
1145 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1149 void Mod_GenerateVertexMesh (msurface_t *surf)
1156 surf->lightmaptexturestride = 0;
1157 surf->lightmaptexture = NULL;
1159 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1160 mesh->numverts = surf->poly_numverts;
1161 mesh->numtriangles = surf->poly_numverts - 2;
1162 mesh->vertex = (surfvertex_t *)(mesh + 1);
1163 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1164 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1166 index = mesh->index;
1167 for (i = 0;i < mesh->numtriangles;i++)
1174 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1176 VectorCopy (in, out->v);
1177 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1178 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1182 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1189 // convert edges back to a normal polygon
1190 surf->poly_numverts = surf->numedges;
1191 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1192 for (i = 0;i < surf->numedges;i++)
1194 lindex = loadmodel->surfedges[surf->firstedge + i];
1196 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1198 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1199 VectorCopy (vec, vert);
1204 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1206 int j, base, tricount, newvertexcount, *index, *vertexremap;
1207 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1208 if (s->mesh->numtriangles > 1000)
1210 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1215 while (base < s->mesh->numtriangles)
1217 tricount = s->mesh->numtriangles - base;
1218 if (tricount > 1000)
1220 index = s->mesh->index + base * 3;
1224 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1225 for (j = 0;j < tricount * 3;j++)
1226 if (vertexremap[index[j]] < 0)
1227 vertexremap[index[j]] = newvertexcount++;
1229 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1230 newmesh->chain = NULL;
1231 newmesh->numverts = newvertexcount;
1232 newmesh->numtriangles = tricount;
1233 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1234 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1235 for (j = 0;j < tricount * 3;j++)
1237 newmesh->index[j] = vertexremap[index[j]];
1238 // yes this copies the same vertex multiple times in many cases... but that's ok...
1239 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1242 oldmesh->chain = newmesh;
1244 firstmesh = newmesh;
1247 Mem_Free(vertexremap);
1249 s->mesh = firstmesh;
1258 static void Mod_LoadFaces (lump_t *l)
1262 int i, count, surfnum, planenum, ssize, tsize;
1264 in = (void *)(mod_base + l->fileofs);
1265 if (l->filelen % sizeof(*in))
1266 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1267 count = l->filelen / sizeof(*in);
1268 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1270 loadmodel->surfaces = out;
1271 loadmodel->numsurfaces = count;
1273 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1275 // FIXME: validate edges, texinfo, etc?
1276 out->firstedge = LittleLong(in->firstedge);
1277 out->numedges = LittleShort(in->numedges);
1278 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1279 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1281 i = LittleShort (in->texinfo);
1282 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1283 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1284 out->texinfo = loadmodel->texinfo + i;
1285 out->flags = out->texinfo->texture->flags;
1287 planenum = LittleShort(in->planenum);
1288 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1289 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1291 if (LittleShort(in->side))
1292 out->flags |= SURF_PLANEBACK;
1294 out->plane = loadmodel->planes + planenum;
1296 // clear lightmap (filled in later)
1297 out->lightmaptexture = NULL;
1299 // force lightmap upload on first time seeing the surface
1300 out->cached_dlight = true;
1301 out->cached_ambient = -1000;
1302 out->cached_lightscalebit = -1000;
1304 CalcSurfaceExtents (out);
1306 ssize = (out->extents[0] >> 4) + 1;
1307 tsize = (out->extents[1] >> 4) + 1;
1310 for (i = 0;i < MAXLIGHTMAPS;i++)
1311 out->styles[i] = in->styles[i];
1312 i = LittleLong(in->lightofs);
1314 out->samples = NULL;
1315 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1316 out->samples = loadmodel->lightdata + i;
1317 else // LordHavoc: white lighting (bsp version 29)
1318 out->samples = loadmodel->lightdata + (i * 3);
1320 Mod_GenerateSurfacePolygon(out);
1322 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1324 out->shader = &Cshader_sky;
1325 out->samples = NULL;
1326 Mod_GenerateWarpMesh (out);
1328 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1330 out->shader = &Cshader_water;
1331 out->samples = NULL;
1332 Mod_GenerateWarpMesh (out);
1336 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1337 out->flags |= SURF_CLIPSOLID;
1338 if (out->texinfo->flags & TEX_SPECIAL)
1340 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1341 out->shader = &Cshader_water;
1342 out->shader = &Cshader_water;
1343 out->samples = NULL;
1344 Mod_GenerateWarpMesh (out);
1346 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1348 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1349 out->shader = &Cshader_wall_fullbright;
1350 out->samples = NULL;
1351 Mod_GenerateVertexMesh(out);
1355 // stainmap for permanent marks on walls
1356 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1358 memset(out->stainsamples, 255, ssize * tsize * 3);
1359 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1361 out->shader = &Cshader_wall_vertex;
1362 Mod_GenerateVertexLitMesh(out);
1366 out->shader = &Cshader_wall_lightmap;
1367 Mod_GenerateLightmappedMesh(out);
1371 Mod_SplitSurfMeshIfTooBig(out);
1375 static model_t *sortmodel;
1377 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1379 const msurface_t *a, *b;
1380 a = *((const msurface_t **)voida);
1381 b = *((const msurface_t **)voidb);
1382 if (a->shader != b->shader)
1383 return (qbyte *) a->shader - (qbyte *) b->shader;
1384 if (a->texinfo->texture != b->texinfo->texture);
1385 return a->texinfo->texture - b->texinfo->texture;
1389 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1393 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1394 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1395 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1397 if (r_sortsurfaces.integer)
1398 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1407 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1409 node->parent = parent;
1410 if (node->contents < 0)
1412 Mod_SetParent (node->children[0], node);
1413 Mod_SetParent (node->children[1], node);
1421 static void Mod_LoadNodes (lump_t *l)
1427 in = (void *)(mod_base + l->fileofs);
1428 if (l->filelen % sizeof(*in))
1429 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1430 count = l->filelen / sizeof(*in);
1431 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1433 loadmodel->nodes = out;
1434 loadmodel->numnodes = count;
1436 for ( i=0 ; i<count ; i++, in++, out++)
1438 for (j=0 ; j<3 ; j++)
1440 out->mins[j] = LittleShort (in->mins[j]);
1441 out->maxs[j] = LittleShort (in->maxs[j]);
1444 p = LittleLong(in->planenum);
1445 out->plane = loadmodel->planes + p;
1447 out->firstsurface = LittleShort (in->firstface);
1448 out->numsurfaces = LittleShort (in->numfaces);
1450 for (j=0 ; j<2 ; j++)
1452 p = LittleShort (in->children[j]);
1454 out->children[j] = loadmodel->nodes + p;
1456 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1460 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1468 static void Mod_LoadLeafs (lump_t *l)
1474 in = (void *)(mod_base + l->fileofs);
1475 if (l->filelen % sizeof(*in))
1476 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1477 count = l->filelen / sizeof(*in);
1478 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1480 loadmodel->leafs = out;
1481 loadmodel->numleafs = count;
1483 for ( i=0 ; i<count ; i++, in++, out++)
1485 for (j=0 ; j<3 ; j++)
1487 out->mins[j] = LittleShort (in->mins[j]);
1488 out->maxs[j] = LittleShort (in->maxs[j]);
1491 p = LittleLong(in->contents);
1494 out->firstmarksurface = loadmodel->marksurfaces +
1495 LittleShort(in->firstmarksurface);
1496 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1498 p = LittleLong(in->visofs);
1500 out->compressed_vis = NULL;
1502 out->compressed_vis = loadmodel->visdata + p;
1504 for (j=0 ; j<4 ; j++)
1505 out->ambient_sound_level[j] = in->ambient_level[j];
1507 // FIXME: Insert caustics here
1516 static void Mod_LoadClipnodes (lump_t *l)
1518 dclipnode_t *in, *out;
1522 in = (void *)(mod_base + l->fileofs);
1523 if (l->filelen % sizeof(*in))
1524 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1525 count = l->filelen / sizeof(*in);
1526 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1528 loadmodel->clipnodes = out;
1529 loadmodel->numclipnodes = count;
1531 if (loadmodel->ishlbsp)
1533 hull = &loadmodel->hulls[1];
1534 hull->clipnodes = out;
1535 hull->firstclipnode = 0;
1536 hull->lastclipnode = count-1;
1537 hull->planes = loadmodel->planes;
1538 hull->clip_mins[0] = -16;
1539 hull->clip_mins[1] = -16;
1540 hull->clip_mins[2] = -36;
1541 hull->clip_maxs[0] = 16;
1542 hull->clip_maxs[1] = 16;
1543 hull->clip_maxs[2] = 36;
1544 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1546 hull = &loadmodel->hulls[2];
1547 hull->clipnodes = out;
1548 hull->firstclipnode = 0;
1549 hull->lastclipnode = count-1;
1550 hull->planes = loadmodel->planes;
1551 hull->clip_mins[0] = -32;
1552 hull->clip_mins[1] = -32;
1553 hull->clip_mins[2] = -32;
1554 hull->clip_maxs[0] = 32;
1555 hull->clip_maxs[1] = 32;
1556 hull->clip_maxs[2] = 32;
1557 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1559 hull = &loadmodel->hulls[3];
1560 hull->clipnodes = out;
1561 hull->firstclipnode = 0;
1562 hull->lastclipnode = count-1;
1563 hull->planes = loadmodel->planes;
1564 hull->clip_mins[0] = -16;
1565 hull->clip_mins[1] = -16;
1566 hull->clip_mins[2] = -18;
1567 hull->clip_maxs[0] = 16;
1568 hull->clip_maxs[1] = 16;
1569 hull->clip_maxs[2] = 18;
1570 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1574 hull = &loadmodel->hulls[1];
1575 hull->clipnodes = out;
1576 hull->firstclipnode = 0;
1577 hull->lastclipnode = count-1;
1578 hull->planes = loadmodel->planes;
1579 hull->clip_mins[0] = -16;
1580 hull->clip_mins[1] = -16;
1581 hull->clip_mins[2] = -24;
1582 hull->clip_maxs[0] = 16;
1583 hull->clip_maxs[1] = 16;
1584 hull->clip_maxs[2] = 32;
1585 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1587 hull = &loadmodel->hulls[2];
1588 hull->clipnodes = out;
1589 hull->firstclipnode = 0;
1590 hull->lastclipnode = count-1;
1591 hull->planes = loadmodel->planes;
1592 hull->clip_mins[0] = -32;
1593 hull->clip_mins[1] = -32;
1594 hull->clip_mins[2] = -24;
1595 hull->clip_maxs[0] = 32;
1596 hull->clip_maxs[1] = 32;
1597 hull->clip_maxs[2] = 64;
1598 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1601 for (i=0 ; i<count ; i++, out++, in++)
1603 out->planenum = LittleLong(in->planenum);
1604 out->children[0] = LittleShort(in->children[0]);
1605 out->children[1] = LittleShort(in->children[1]);
1606 if (out->children[0] >= count || out->children[1] >= count)
1607 Host_Error("Corrupt clipping hull (out of range child)\n");
1615 Duplicate the drawing hull structure as a clipping hull
1618 static void Mod_MakeHull0 (void)
1625 hull = &loadmodel->hulls[0];
1627 in = loadmodel->nodes;
1628 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1630 hull->clipnodes = out;
1631 hull->firstclipnode = 0;
1632 hull->lastclipnode = loadmodel->numnodes - 1;
1633 hull->planes = loadmodel->planes;
1635 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1637 out->planenum = in->plane - loadmodel->planes;
1638 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1639 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1645 Mod_LoadMarksurfaces
1648 static void Mod_LoadMarksurfaces (lump_t *l)
1653 in = (void *)(mod_base + l->fileofs);
1654 if (l->filelen % sizeof(*in))
1655 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1656 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1657 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1659 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1661 j = (unsigned) LittleShort(in[i]);
1662 if (j >= loadmodel->numsurfaces)
1663 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1664 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1673 static void Mod_LoadSurfedges (lump_t *l)
1678 in = (void *)(mod_base + l->fileofs);
1679 if (l->filelen % sizeof(*in))
1680 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1681 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1682 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1684 for (i = 0;i < loadmodel->numsurfedges;i++)
1685 loadmodel->surfedges[i] = LittleLong (in[i]);
1694 static void Mod_LoadPlanes (lump_t *l)
1700 in = (void *)(mod_base + l->fileofs);
1701 if (l->filelen % sizeof(*in))
1702 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1704 loadmodel->numplanes = l->filelen / sizeof(*in);
1705 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1707 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1709 out->normal[0] = LittleFloat (in->normal[0]);
1710 out->normal[1] = LittleFloat (in->normal[1]);
1711 out->normal[2] = LittleFloat (in->normal[2]);
1712 out->dist = LittleFloat (in->dist);
1718 #define MAX_POINTS_ON_WINDING 64
1724 double points[8][3]; // variable sized
1733 static winding_t *NewWinding (int points)
1738 if (points > MAX_POINTS_ON_WINDING)
1739 Sys_Error("NewWinding: too many points\n");
1741 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1742 w = Mem_Alloc(loadmodel->mempool, size);
1743 memset (w, 0, size);
1748 static void FreeWinding (winding_t *w)
1758 static winding_t *BaseWindingForPlane (mplane_t *p)
1760 double org[3], vright[3], vup[3], normal[3];
1763 VectorCopy(p->normal, normal);
1764 VectorVectorsDouble(normal, vright, vup);
1766 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1767 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1769 // project a really big axis aligned box onto the plane
1772 VectorScale (p->normal, p->dist, org);
1774 VectorSubtract (org, vright, w->points[0]);
1775 VectorAdd (w->points[0], vup, w->points[0]);
1777 VectorAdd (org, vright, w->points[1]);
1778 VectorAdd (w->points[1], vup, w->points[1]);
1780 VectorAdd (org, vright, w->points[2]);
1781 VectorSubtract (w->points[2], vup, w->points[2]);
1783 VectorSubtract (org, vright, w->points[3]);
1784 VectorSubtract (w->points[3], vup, w->points[3]);
1795 Clips the winding to the plane, returning the new winding on the positive side
1796 Frees the input winding.
1797 If keepon is true, an exactly on-plane winding will be saved, otherwise
1798 it will be clipped away.
1801 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1803 double dists[MAX_POINTS_ON_WINDING + 1];
1804 int sides[MAX_POINTS_ON_WINDING + 1];
1813 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1815 // determine sides for each point
1816 for (i = 0;i < in->numpoints;i++)
1818 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1819 if (dot > ON_EPSILON)
1820 sides[i] = SIDE_FRONT;
1821 else if (dot < -ON_EPSILON)
1822 sides[i] = SIDE_BACK;
1827 sides[i] = sides[0];
1828 dists[i] = dists[0];
1830 if (keepon && !counts[0] && !counts[1])
1841 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1842 if (maxpts > MAX_POINTS_ON_WINDING)
1843 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1845 neww = NewWinding (maxpts);
1847 for (i = 0;i < in->numpoints;i++)
1849 if (neww->numpoints >= maxpts)
1850 Sys_Error ("ClipWinding: points exceeded estimate");
1854 if (sides[i] == SIDE_ON)
1856 VectorCopy (p1, neww->points[neww->numpoints]);
1861 if (sides[i] == SIDE_FRONT)
1863 VectorCopy (p1, neww->points[neww->numpoints]);
1867 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1870 // generate a split point
1871 p2 = in->points[(i+1)%in->numpoints];
1873 dot = dists[i] / (dists[i]-dists[i+1]);
1874 for (j = 0;j < 3;j++)
1875 { // avoid round off error when possible
1876 if (split->normal[j] == 1)
1877 mid[j] = split->dist;
1878 else if (split->normal[j] == -1)
1879 mid[j] = -split->dist;
1881 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1884 VectorCopy (mid, neww->points[neww->numpoints]);
1888 // free the original winding
1899 Divides a winding by a plane, producing one or two windings. The
1900 original winding is not damaged or freed. If only on one side, the
1901 returned winding will be the input winding. If on both sides, two
1902 new windings will be created.
1905 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1907 double dists[MAX_POINTS_ON_WINDING + 1];
1908 int sides[MAX_POINTS_ON_WINDING + 1];
1917 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1919 // determine sides for each point
1920 for (i = 0;i < in->numpoints;i++)
1922 dot = DotProduct (in->points[i], split->normal);
1925 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1926 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1927 else sides[i] = SIDE_ON;
1930 sides[i] = sides[0];
1931 dists[i] = dists[0];
1933 *front = *back = NULL;
1946 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1948 if (maxpts > MAX_POINTS_ON_WINDING)
1949 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1951 *front = f = NewWinding (maxpts);
1952 *back = b = NewWinding (maxpts);
1954 for (i = 0;i < in->numpoints;i++)
1956 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1957 Sys_Error ("DivideWinding: points exceeded estimate");
1961 if (sides[i] == SIDE_ON)
1963 VectorCopy (p1, f->points[f->numpoints]);
1965 VectorCopy (p1, b->points[b->numpoints]);
1970 if (sides[i] == SIDE_FRONT)
1972 VectorCopy (p1, f->points[f->numpoints]);
1975 else if (sides[i] == SIDE_BACK)
1977 VectorCopy (p1, b->points[b->numpoints]);
1981 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1984 // generate a split point
1985 p2 = in->points[(i+1)%in->numpoints];
1987 dot = dists[i] / (dists[i]-dists[i+1]);
1988 for (j = 0;j < 3;j++)
1989 { // avoid round off error when possible
1990 if (split->normal[j] == 1)
1991 mid[j] = split->dist;
1992 else if (split->normal[j] == -1)
1993 mid[j] = -split->dist;
1995 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1998 VectorCopy (mid, f->points[f->numpoints]);
2000 VectorCopy (mid, b->points[b->numpoints]);
2005 typedef struct portal_s
2008 mnode_t *nodes[2]; // [0] = front side of plane
2009 struct portal_s *next[2];
2011 struct portal_s *chain; // all portals are linked into a list
2015 static portal_t *portalchain;
2022 static portal_t *AllocPortal (void)
2025 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2026 p->chain = portalchain;
2031 static void FreePortal(portal_t *p)
2036 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2038 // calculate children first
2039 if (node->children[0]->contents >= 0)
2040 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2041 if (node->children[1]->contents >= 0)
2042 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2044 // make combined bounding box from children
2045 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2046 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2047 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2048 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2049 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2050 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2053 static void Mod_FinalizePortals(void)
2055 int i, j, numportals, numpoints;
2056 portal_t *p, *pnext;
2059 mleaf_t *leaf, *endleaf;
2062 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2063 leaf = loadmodel->leafs;
2064 endleaf = leaf + loadmodel->numleafs;
2065 for (;leaf < endleaf;leaf++)
2067 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2068 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2075 for (i = 0;i < 2;i++)
2077 leaf = (mleaf_t *)p->nodes[i];
2079 for (j = 0;j < w->numpoints;j++)
2081 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2082 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2083 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2084 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2085 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2086 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2093 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2095 // tally up portal and point counts
2101 // note: this check must match the one below or it will usually corrupt memory
2102 // 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
2103 if (p->winding && p->nodes[0] != p->nodes[1]
2104 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2105 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2108 numpoints += p->winding->numpoints * 2;
2112 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2113 loadmodel->numportals = numportals;
2114 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2115 loadmodel->numportalpoints = numpoints;
2116 // clear all leaf portal chains
2117 for (i = 0;i < loadmodel->numleafs;i++)
2118 loadmodel->leafs[i].portals = NULL;
2119 // process all portals in the global portal chain, while freeing them
2120 portal = loadmodel->portals;
2121 point = loadmodel->portalpoints;
2130 // note: this check must match the one above or it will usually corrupt memory
2131 // 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
2132 if (p->nodes[0] != p->nodes[1]
2133 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2134 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2136 // first make the back to front portal (forward portal)
2137 portal->points = point;
2138 portal->numpoints = p->winding->numpoints;
2139 portal->plane.dist = p->plane.dist;
2140 VectorCopy(p->plane.normal, portal->plane.normal);
2141 portal->here = (mleaf_t *)p->nodes[1];
2142 portal->past = (mleaf_t *)p->nodes[0];
2144 for (j = 0;j < portal->numpoints;j++)
2146 VectorCopy(p->winding->points[j], point->position);
2149 PlaneClassify(&portal->plane);
2151 // link into leaf's portal chain
2152 portal->next = portal->here->portals;
2153 portal->here->portals = portal;
2155 // advance to next portal
2158 // then make the front to back portal (backward portal)
2159 portal->points = point;
2160 portal->numpoints = p->winding->numpoints;
2161 portal->plane.dist = -p->plane.dist;
2162 VectorNegate(p->plane.normal, portal->plane.normal);
2163 portal->here = (mleaf_t *)p->nodes[0];
2164 portal->past = (mleaf_t *)p->nodes[1];
2166 for (j = portal->numpoints - 1;j >= 0;j--)
2168 VectorCopy(p->winding->points[j], point->position);
2171 PlaneClassify(&portal->plane);
2173 // link into leaf's portal chain
2174 portal->next = portal->here->portals;
2175 portal->here->portals = portal;
2177 // advance to next portal
2180 FreeWinding(p->winding);
2192 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2195 Host_Error ("AddPortalToNodes: NULL front node");
2197 Host_Error ("AddPortalToNodes: NULL back node");
2198 if (p->nodes[0] || p->nodes[1])
2199 Host_Error ("AddPortalToNodes: already included");
2200 // 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
2202 p->nodes[0] = front;
2203 p->next[0] = (portal_t *)front->portals;
2204 front->portals = (mportal_t *)p;
2207 p->next[1] = (portal_t *)back->portals;
2208 back->portals = (mportal_t *)p;
2213 RemovePortalFromNode
2216 static void RemovePortalFromNodes(portal_t *portal)
2220 void **portalpointer;
2222 for (i = 0;i < 2;i++)
2224 node = portal->nodes[i];
2226 portalpointer = (void **) &node->portals;
2231 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2235 if (portal->nodes[0] == node)
2237 *portalpointer = portal->next[0];
2238 portal->nodes[0] = NULL;
2240 else if (portal->nodes[1] == node)
2242 *portalpointer = portal->next[1];
2243 portal->nodes[1] = NULL;
2246 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2250 if (t->nodes[0] == node)
2251 portalpointer = (void **) &t->next[0];
2252 else if (t->nodes[1] == node)
2253 portalpointer = (void **) &t->next[1];
2255 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2260 static void Mod_RecursiveNodePortals (mnode_t *node)
2263 mnode_t *front, *back, *other_node;
2264 mplane_t clipplane, *plane;
2265 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2266 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2268 // if a leaf, we're done
2272 plane = node->plane;
2274 front = node->children[0];
2275 back = node->children[1];
2277 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2279 // create the new portal by generating a polygon for the node plane,
2280 // and clipping it by all of the other portals (which came from nodes above this one)
2281 nodeportal = AllocPortal ();
2282 nodeportal->plane = *node->plane;
2284 nodeportalwinding = BaseWindingForPlane (node->plane);
2285 side = 0; // shut up compiler warning
2286 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2288 clipplane = portal->plane;
2289 if (portal->nodes[0] == portal->nodes[1])
2290 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2291 if (portal->nodes[0] == node)
2293 else if (portal->nodes[1] == node)
2295 clipplane.dist = -clipplane.dist;
2296 VectorNegate (clipplane.normal, clipplane.normal);
2300 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2302 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2303 if (!nodeportalwinding)
2305 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2310 if (nodeportalwinding)
2312 // if the plane was not clipped on all sides, there was an error
2313 nodeportal->winding = nodeportalwinding;
2314 AddPortalToNodes (nodeportal, front, back);
2317 // split the portals of this node along this node's plane and assign them to the children of this node
2318 // (migrating the portals downward through the tree)
2319 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2321 if (portal->nodes[0] == portal->nodes[1])
2322 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2323 if (portal->nodes[0] == node)
2325 else if (portal->nodes[1] == node)
2328 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2329 nextportal = portal->next[side];
2331 other_node = portal->nodes[!side];
2332 RemovePortalFromNodes (portal);
2334 // cut the portal into two portals, one on each side of the node plane
2335 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2340 AddPortalToNodes (portal, back, other_node);
2342 AddPortalToNodes (portal, other_node, back);
2348 AddPortalToNodes (portal, front, other_node);
2350 AddPortalToNodes (portal, other_node, front);
2354 // the winding is split
2355 splitportal = AllocPortal ();
2356 temp = splitportal->chain;
2357 *splitportal = *portal;
2358 splitportal->chain = temp;
2359 splitportal->winding = backwinding;
2360 FreeWinding (portal->winding);
2361 portal->winding = frontwinding;
2365 AddPortalToNodes (portal, front, other_node);
2366 AddPortalToNodes (splitportal, back, other_node);
2370 AddPortalToNodes (portal, other_node, front);
2371 AddPortalToNodes (splitportal, other_node, back);
2375 Mod_RecursiveNodePortals(front);
2376 Mod_RecursiveNodePortals(back);
2380 static void Mod_MakePortals(void)
2383 Mod_RecursiveNodePortals (loadmodel->nodes);
2384 Mod_FinalizePortals();
2392 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2397 mempool_t *mainmempool;
2400 mod->type = mod_brush;
2402 header = (dheader_t *)buffer;
2404 i = LittleLong (header->version);
2405 if (i != BSPVERSION && i != 30)
2406 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2407 mod->ishlbsp = i == 30;
2408 if (loadmodel->isworldmodel)
2409 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2411 // swap all the lumps
2412 mod_base = (qbyte *)header;
2414 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2415 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2419 // store which lightmap format to use
2420 mod->lightmaprgba = r_lightmaprgba.integer;
2422 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2423 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2424 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2425 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2426 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2427 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2428 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2429 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2430 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2431 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2432 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2433 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2434 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2435 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2436 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2441 mod->numframes = 2; // regular and alternate animation
2443 mainmempool = mod->mempool;
2444 loadname = mod->name;
2446 Mod_LoadLightList ();
2449 // set up the submodels (FIXME: this is confusing)
2451 for (i = 0;i < mod->numsubmodels;i++)
2454 float dist, modelyawradius, modelradius, *vec;
2457 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2458 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2462 bm = &mod->submodels[i];
2464 mod->hulls[0].firstclipnode = bm->headnode[0];
2465 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2467 mod->hulls[j].firstclipnode = bm->headnode[j];
2468 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2471 mod->firstmodelsurface = bm->firstface;
2472 mod->nummodelsurfaces = bm->numfaces;
2474 mod->DrawSky = NULL;
2475 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2476 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2478 // we only need to have a drawsky function if it is used (usually only on world model)
2479 if (surf->shader == &Cshader_sky)
2480 mod->DrawSky = R_DrawBrushModelSky;
2481 for (k = 0;k < surf->numedges;k++)
2483 l = mod->surfedges[k + surf->firstedge];
2485 vec = mod->vertexes[mod->edges[l].v[0]].position;
2487 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2488 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2489 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2490 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2491 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2492 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2493 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2494 dist = vec[0]*vec[0]+vec[1]*vec[1];
2495 if (modelyawradius < dist)
2496 modelyawradius = dist;
2497 dist += vec[2]*vec[2];
2498 if (modelradius < dist)
2502 modelyawradius = sqrt(modelyawradius);
2503 modelradius = sqrt(modelradius);
2504 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2505 mod->yawmins[2] = mod->normalmins[2];
2506 mod->yawmaxs[2] = mod->normalmaxs[2];
2507 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2508 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2509 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2510 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2512 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2513 VectorClear(mod->normalmins);
2514 VectorClear(mod->normalmaxs);
2515 VectorClear(mod->yawmins);
2516 VectorClear(mod->yawmaxs);
2517 VectorClear(mod->rotatedmins);
2518 VectorClear(mod->rotatedmaxs);
2521 mod->numleafs = bm->visleafs;
2523 mod->Draw = R_DrawBrushModelNormal;
2524 mod->DrawShadow = NULL;
2526 Mod_BrushSortedSurfaces(mod, mainmempool);
2528 // LordHavoc: only register submodels if it is the world
2529 // (prevents bsp models from replacing world submodels)
2530 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2533 // duplicate the basic information
2534 sprintf (name, "*%i", i+1);
2535 loadmodel = Mod_FindName (name);
2537 strcpy (loadmodel->name, name);
2538 // textures and memory belong to the main model
2539 loadmodel->texturepool = NULL;
2540 loadmodel->mempool = NULL;