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 if (loadmodel->isworldmodel)
229 data = loadimagepixels(tx->name, false, 0, 0);
232 if (image_width == 256 && image_height == 128)
240 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
242 R_InitSky (mtdata, 1);
245 else if (mtdata != NULL)
246 R_InitSky (mtdata, 1);
249 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
251 tx->fogtexture = image_masktex;
252 strcpy(name, tx->name);
253 strcat(name, "_glow");
254 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
258 if (loadmodel->ishlbsp)
260 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
263 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
264 if (R_TextureHasAlpha(tx->texture))
267 for (j = 0;j < image_width * image_height;j++)
268 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
269 strcpy(name, tx->name);
270 strcat(name, "_fog");
271 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
275 else if ((data = W_GetTexture(tx->name)))
277 // get the size from the wad texture
278 tx->width = image_width;
279 tx->height = image_height;
280 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
281 if (R_TextureHasAlpha(tx->texture))
284 for (j = 0;j < image_width * image_height;j++)
285 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
286 strcpy(name, tx->name);
287 strcat(name, "_fog");
288 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
296 tx->texture = r_notexture;
301 if (mtdata) // texture included
306 if (r_fullbrights.value && tx->name[0] != '*')
308 for (j = 0;j < tx->width*tx->height;j++)
310 if (data[j] >= 224) // fullbright
319 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
320 for (j = 0;j < tx->width*tx->height;j++)
321 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
322 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
323 strcpy(name, tx->name);
324 strcat(name, "_glow");
325 for (j = 0;j < tx->width*tx->height;j++)
326 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
327 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
331 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
333 else // no texture, and no external replacement texture was found
337 tx->texture = r_notexture;
342 if (tx->name[0] == '*')
344 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
345 // LordHavoc: some turbulent textures should be fullbright and solid
346 if (!strncmp(tx->name,"*lava",5)
347 || !strncmp(tx->name,"*teleport",9)
348 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
349 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
351 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
352 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
355 tx->flags |= SURF_LIGHTMAP;
356 if (!R_TextureHasAlpha(tx->texture))
357 tx->flags |= SURF_CLIPSOLID;
361 // sequence the animations
362 for (i = 0;i < m->nummiptex;i++)
364 tx = loadmodel->textures[i];
365 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
367 if (tx->anim_total[0] || tx->anim_total[1])
368 continue; // already sequenced
370 // find the number of frames in the animation
371 memset (anims, 0, sizeof(anims));
372 memset (altanims, 0, sizeof(altanims));
374 for (j = i;j < m->nummiptex;j++)
376 tx2 = loadmodel->textures[j];
377 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
381 if (num >= '0' && num <= '9')
382 anims[num - '0'] = tx2;
383 else if (num >= 'a' && num <= 'j')
384 altanims[num - 'a'] = tx2;
386 Con_Printf ("Bad animating texture %s\n", tx->name);
390 for (j = 0;j < 10;j++)
397 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
400 for (j = 0;j < max;j++)
404 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
408 for (j = 0;j < altmax;j++)
412 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
421 // if there is no alternate animation, duplicate the primary
422 // animation into the alternate
424 for (k = 0;k < 10;k++)
425 altanims[k] = anims[k];
428 // link together the primary animation
429 for (j = 0;j < max;j++)
432 tx2->animated = true;
433 tx2->anim_total[0] = max;
434 tx2->anim_total[1] = altmax;
435 for (k = 0;k < 10;k++)
437 tx2->anim_frames[0][k] = anims[k];
438 tx2->anim_frames[1][k] = altanims[k];
442 // if there really is an alternate anim...
443 if (anims[0] != altanims[0])
445 // link together the alternate animation
446 for (j = 0;j < altmax;j++)
449 tx2->animated = true;
450 // the primary/alternate are reversed here
451 tx2->anim_total[0] = altmax;
452 tx2->anim_total[1] = max;
453 for (k = 0;k < 10;k++)
455 tx2->anim_frames[0][k] = altanims[k];
456 tx2->anim_frames[1][k] = anims[k];
468 static void Mod_LoadLighting (lump_t *l)
471 qbyte *in, *out, *data, d;
472 char litfilename[1024];
473 loadmodel->lightdata = NULL;
474 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
476 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
477 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
479 else // LordHavoc: bsp version 29 (normal white lighting)
481 // LordHavoc: hope is not lost yet, check for a .lit file to load
482 strcpy(litfilename, loadmodel->name);
483 COM_StripExtension(litfilename, litfilename);
484 strcat(litfilename, ".lit");
485 data = (qbyte*) COM_LoadFile (litfilename, false);
488 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
490 i = LittleLong(((int *)data)[1]);
493 Con_DPrintf("%s loaded", litfilename);
494 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
495 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
501 Con_Printf("Unknown .lit file version (%d)\n", i);
508 Con_Printf("Empty .lit file, ignoring\n");
510 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
514 // LordHavoc: oh well, expand the white lighting data
517 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
518 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
519 out = loadmodel->lightdata;
520 memcpy (in, mod_base + l->fileofs, l->filelen);
521 for (i = 0;i < l->filelen;i++)
531 void Mod_LoadLightList(void)
534 char lightsfilename[1024], *s, *t, *lightsstring;
537 strcpy(lightsfilename, loadmodel->name);
538 COM_StripExtension(lightsfilename, lightsfilename);
539 strcat(lightsfilename, ".lights");
540 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
546 while (*s && *s != '\n')
550 Mem_Free(lightsstring);
551 Host_Error("lights file must end with a newline\n");
556 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
559 while (*s && n < numlights)
562 while (*s && *s != '\n')
566 Mem_Free(lightsstring);
567 Host_Error("misparsed lights file!\n");
569 e = loadmodel->lights + n;
571 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);
575 Mem_Free(lightsstring);
576 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);
583 Mem_Free(lightsstring);
584 Host_Error("misparsed lights file!\n");
586 loadmodel->numlights = numlights;
587 Mem_Free(lightsstring);
597 static void Mod_LoadVisibility (lump_t *l)
599 loadmodel->visdata = NULL;
602 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
603 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
606 // used only for HalfLife maps
607 void Mod_ParseWadsFromEntityLump(char *data)
609 char key[128], value[4096];
614 data = COM_Parse(data);
617 if (com_token[0] != '{')
621 data = COM_Parse(data);
624 if (com_token[0] == '}')
625 break; // end of worldspawn
626 if (com_token[0] == '_')
627 strcpy(key, com_token + 1);
629 strcpy(key, com_token);
630 while (key[strlen(key)-1] == ' ') // remove trailing spaces
631 key[strlen(key)-1] = 0;
632 data = COM_Parse(data);
635 strcpy(value, com_token);
636 if (!strcmp("wad", key)) // for HalfLife maps
638 if (loadmodel->ishlbsp)
641 for (i = 0;i < 4096;i++)
642 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
648 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
649 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
651 else if (value[i] == ';' || value[i] == 0)
655 strcpy(wadname, "textures/");
656 strcat(wadname, &value[j]);
657 W_LoadTextureWadFile (wadname, false);
674 static void Mod_LoadEntities (lump_t *l)
676 loadmodel->entities = NULL;
679 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
680 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
681 if (loadmodel->ishlbsp)
682 Mod_ParseWadsFromEntityLump(loadmodel->entities);
691 static void Mod_LoadVertexes (lump_t *l)
697 in = (void *)(mod_base + l->fileofs);
698 if (l->filelen % sizeof(*in))
699 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
700 count = l->filelen / sizeof(*in);
701 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
703 loadmodel->vertexes = out;
704 loadmodel->numvertexes = count;
706 for ( i=0 ; i<count ; i++, in++, out++)
708 out->position[0] = LittleFloat (in->point[0]);
709 out->position[1] = LittleFloat (in->point[1]);
710 out->position[2] = LittleFloat (in->point[2]);
719 static void Mod_LoadSubmodels (lump_t *l)
725 in = (void *)(mod_base + l->fileofs);
726 if (l->filelen % sizeof(*in))
727 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
728 count = l->filelen / sizeof(*in);
729 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
731 loadmodel->submodels = out;
732 loadmodel->numsubmodels = count;
734 for ( i=0 ; i<count ; i++, in++, out++)
736 for (j=0 ; j<3 ; j++)
738 // spread the mins / maxs by a pixel
739 out->mins[j] = LittleFloat (in->mins[j]) - 1;
740 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
741 out->origin[j] = LittleFloat (in->origin[j]);
743 for (j=0 ; j<MAX_MAP_HULLS ; j++)
744 out->headnode[j] = LittleLong (in->headnode[j]);
745 out->visleafs = LittleLong (in->visleafs);
746 out->firstface = LittleLong (in->firstface);
747 out->numfaces = LittleLong (in->numfaces);
756 static void Mod_LoadEdges (lump_t *l)
762 in = (void *)(mod_base + l->fileofs);
763 if (l->filelen % sizeof(*in))
764 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
765 count = l->filelen / sizeof(*in);
766 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
768 loadmodel->edges = out;
769 loadmodel->numedges = count;
771 for ( i=0 ; i<count ; i++, in++, out++)
773 out->v[0] = (unsigned short)LittleShort(in->v[0]);
774 out->v[1] = (unsigned short)LittleShort(in->v[1]);
783 static void Mod_LoadTexinfo (lump_t *l)
787 int i, j, k, count, miptex;
789 in = (void *)(mod_base + l->fileofs);
790 if (l->filelen % sizeof(*in))
791 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
792 count = l->filelen / sizeof(*in);
793 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
795 loadmodel->texinfo = out;
796 loadmodel->numtexinfo = count;
798 for (i = 0;i < count;i++, in++, out++)
800 for (k = 0;k < 2;k++)
801 for (j = 0;j < 4;j++)
802 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
804 miptex = LittleLong (in->miptex);
805 out->flags = LittleLong (in->flags);
808 if (loadmodel->textures)
810 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
811 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
813 out->texture = loadmodel->textures[miptex];
815 if (out->texture == NULL)
817 // choose either the liquid notexture, or the normal notexture
818 if (out->flags & TEX_SPECIAL)
819 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
821 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
830 Fills in s->texturemins[] and s->extents[]
833 static void CalcSurfaceExtents (msurface_t *s)
835 float mins[2], maxs[2], val;
839 int bmins[2], bmaxs[2];
841 mins[0] = mins[1] = 999999999;
842 maxs[0] = maxs[1] = -999999999;
846 for (i=0 ; i<s->numedges ; i++)
848 e = loadmodel->surfedges[s->firstedge+i];
850 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
852 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
854 for (j=0 ; j<2 ; j++)
856 val = v->position[0] * tex->vecs[j][0] +
857 v->position[1] * tex->vecs[j][1] +
858 v->position[2] * tex->vecs[j][2] +
867 for (i=0 ; i<2 ; i++)
869 bmins[i] = floor(mins[i]/16);
870 bmaxs[i] = ceil(maxs[i]/16);
872 s->texturemins[i] = bmins[i] * 16;
873 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
878 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
883 mins[0] = mins[1] = mins[2] = 9999;
884 maxs[0] = maxs[1] = maxs[2] = -9999;
886 for (i = 0;i < numverts;i++)
888 for (j = 0;j < 3;j++, v++)
898 #define MAX_SUBDIVPOLYTRIANGLES 4096
899 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
901 static int subdivpolyverts, subdivpolytriangles;
902 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
903 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
905 static int subdivpolylookupvert(vec3_t v)
908 for (i = 0;i < subdivpolyverts;i++)
909 if (subdivpolyvert[i][0] == v[0]
910 && subdivpolyvert[i][1] == v[1]
911 && subdivpolyvert[i][2] == v[2])
913 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
914 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
915 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
916 return subdivpolyverts++;
919 static void SubdividePolygon (int numverts, float *verts)
921 int i, i1, i2, i3, f, b, c, p;
922 vec3_t mins, maxs, front[256], back[256];
923 float m, *pv, *cv, dist[256], frac;
926 Host_Error ("SubdividePolygon: ran out of verts in buffer");
928 BoundPoly (numverts, verts, mins, maxs);
930 for (i = 0;i < 3;i++)
932 m = (mins[i] + maxs[i]) * 0.5;
933 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
940 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
944 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
948 VectorCopy (pv, front[f]);
953 VectorCopy (pv, back[b]);
956 if (dist[p] == 0 || dist[c] == 0)
958 if ( (dist[p] > 0) != (dist[c] > 0) )
961 frac = dist[p] / (dist[p] - dist[c]);
962 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
963 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
964 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
970 SubdividePolygon (f, front[0]);
971 SubdividePolygon (b, back[0]);
975 i1 = subdivpolylookupvert(verts);
976 i2 = subdivpolylookupvert(verts + 3);
977 for (i = 2;i < numverts;i++)
979 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
981 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
985 i3 = subdivpolylookupvert(verts + i * 3);
986 subdivpolyindex[subdivpolytriangles][0] = i1;
987 subdivpolyindex[subdivpolytriangles][1] = i2;
988 subdivpolyindex[subdivpolytriangles][2] = i3;
990 subdivpolytriangles++;
998 Breaks a polygon up along axial 64 unit
999 boundaries so that turbulent and sky warps
1000 can be done reasonably.
1003 void Mod_GenerateWarpMesh (msurface_t *surf)
1009 subdivpolytriangles = 0;
1010 subdivpolyverts = 0;
1011 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1012 if (subdivpolytriangles < 1)
1013 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1015 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1016 mesh->numverts = subdivpolyverts;
1017 mesh->numtriangles = subdivpolytriangles;
1018 mesh->vertex = (surfvertex_t *)(mesh + 1);
1019 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1020 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1022 for (i = 0;i < mesh->numtriangles;i++)
1023 for (j = 0;j < 3;j++)
1024 mesh->index[i*3+j] = subdivpolyindex[i][j];
1026 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1028 VectorCopy(subdivpolyvert[i], v->v);
1029 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1030 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1034 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1036 int i, is, it, *index, smax, tmax;
1041 smax = surf->extents[0] >> 4;
1042 tmax = surf->extents[1] >> 4;
1043 surf->lightmaptexturestride = 0;
1044 surf->lightmaptexture = NULL;
1046 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1047 mesh->numverts = surf->poly_numverts;
1048 mesh->numtriangles = surf->poly_numverts - 2;
1049 mesh->vertex = (surfvertex_t *)(mesh + 1);
1050 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1051 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1053 index = mesh->index;
1054 for (i = 0;i < mesh->numtriangles;i++)
1061 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1063 VectorCopy (in, out->v);
1065 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1066 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1068 out->st[0] = s / surf->texinfo->texture->width;
1069 out->st[1] = t / surf->texinfo->texture->height;
1071 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1072 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1074 // lightmap coordinates
1078 // LordHavoc: calc lightmap data offset for vertex lighting to use
1081 is = bound(0, is, smax);
1082 it = bound(0, it, tmax);
1083 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1087 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1089 int i, is, it, *index, smax, tmax;
1090 float *in, s, t, xbase, ybase, xscale, yscale;
1094 surf->flags |= SURF_LIGHTMAP;
1095 smax = surf->extents[0] >> 4;
1096 tmax = surf->extents[1] >> 4;
1097 if (r_miplightmaps.integer)
1099 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1100 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);
1104 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1105 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);
1107 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1108 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1109 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1111 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1112 mesh->numverts = surf->poly_numverts;
1113 mesh->numtriangles = surf->poly_numverts - 2;
1114 mesh->vertex = (surfvertex_t *)(mesh + 1);
1115 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1116 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1118 index = mesh->index;
1119 for (i = 0;i < mesh->numtriangles;i++)
1126 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1128 VectorCopy (in, out->v);
1130 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1131 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1133 out->st[0] = s / surf->texinfo->texture->width;
1134 out->st[1] = t / surf->texinfo->texture->height;
1136 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1137 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1139 // lightmap coordinates
1140 out->uv[0] = s * xscale + xbase;
1141 out->uv[1] = t * yscale + ybase;
1143 // LordHavoc: calc lightmap data offset for vertex lighting to use
1146 is = bound(0, is, smax);
1147 it = bound(0, it, tmax);
1148 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1152 void Mod_GenerateVertexMesh (msurface_t *surf)
1159 surf->lightmaptexturestride = 0;
1160 surf->lightmaptexture = NULL;
1162 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1163 mesh->numverts = surf->poly_numverts;
1164 mesh->numtriangles = surf->poly_numverts - 2;
1165 mesh->vertex = (surfvertex_t *)(mesh + 1);
1166 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1167 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1169 index = mesh->index;
1170 for (i = 0;i < mesh->numtriangles;i++)
1177 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1179 VectorCopy (in, out->v);
1180 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1181 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1185 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1192 // convert edges back to a normal polygon
1193 surf->poly_numverts = surf->numedges;
1194 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1195 for (i = 0;i < surf->numedges;i++)
1197 lindex = loadmodel->surfedges[surf->firstedge + i];
1199 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1201 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1202 VectorCopy (vec, vert);
1207 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1209 int j, base, tricount, newvertexcount, *index, *vertexremap;
1210 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1211 if (s->mesh->numtriangles > 1000)
1213 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1218 while (base < s->mesh->numtriangles)
1220 tricount = s->mesh->numtriangles - base;
1221 if (tricount > 1000)
1223 index = s->mesh->index + base * 3;
1227 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1228 for (j = 0;j < tricount * 3;j++)
1229 if (vertexremap[index[j]] < 0)
1230 vertexremap[index[j]] = newvertexcount++;
1232 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1233 newmesh->chain = NULL;
1234 newmesh->numverts = newvertexcount;
1235 newmesh->numtriangles = tricount;
1236 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1237 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1238 for (j = 0;j < tricount * 3;j++)
1240 newmesh->index[j] = vertexremap[index[j]];
1241 // yes this copies the same vertex multiple times in many cases... but that's ok...
1242 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1245 oldmesh->chain = newmesh;
1247 firstmesh = newmesh;
1250 Mem_Free(vertexremap);
1252 s->mesh = firstmesh;
1261 static void Mod_LoadFaces (lump_t *l)
1265 int i, count, surfnum, planenum, ssize, tsize;
1267 in = (void *)(mod_base + l->fileofs);
1268 if (l->filelen % sizeof(*in))
1269 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1270 count = l->filelen / sizeof(*in);
1271 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1273 loadmodel->surfaces = out;
1274 loadmodel->numsurfaces = count;
1276 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1278 // FIXME: validate edges, texinfo, etc?
1279 out->firstedge = LittleLong(in->firstedge);
1280 out->numedges = LittleShort(in->numedges);
1281 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1282 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1284 i = LittleShort (in->texinfo);
1285 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1286 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1287 out->texinfo = loadmodel->texinfo + i;
1288 out->flags = out->texinfo->texture->flags;
1290 planenum = LittleShort(in->planenum);
1291 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1292 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1294 if (LittleShort(in->side))
1295 out->flags |= SURF_PLANEBACK;
1297 out->plane = loadmodel->planes + planenum;
1299 // clear lightmap (filled in later)
1300 out->lightmaptexture = NULL;
1302 // force lightmap upload on first time seeing the surface
1303 out->cached_dlight = true;
1304 out->cached_ambient = -1000;
1305 out->cached_lightscalebit = -1000;
1307 CalcSurfaceExtents (out);
1309 ssize = (out->extents[0] >> 4) + 1;
1310 tsize = (out->extents[1] >> 4) + 1;
1313 for (i = 0;i < MAXLIGHTMAPS;i++)
1314 out->styles[i] = in->styles[i];
1315 i = LittleLong(in->lightofs);
1317 out->samples = NULL;
1318 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1319 out->samples = loadmodel->lightdata + i;
1320 else // LordHavoc: white lighting (bsp version 29)
1321 out->samples = loadmodel->lightdata + (i * 3);
1323 Mod_GenerateSurfacePolygon(out);
1325 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1327 out->shader = &Cshader_sky;
1328 out->samples = NULL;
1329 Mod_GenerateWarpMesh (out);
1331 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1333 out->shader = &Cshader_water;
1334 out->samples = NULL;
1335 Mod_GenerateWarpMesh (out);
1339 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1340 out->flags |= SURF_CLIPSOLID;
1341 if (out->texinfo->flags & TEX_SPECIAL)
1343 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1344 out->shader = &Cshader_water;
1345 out->shader = &Cshader_water;
1346 out->samples = NULL;
1347 Mod_GenerateWarpMesh (out);
1349 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1351 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1352 out->shader = &Cshader_wall_fullbright;
1353 out->samples = NULL;
1354 Mod_GenerateVertexMesh(out);
1358 // stainmap for permanent marks on walls
1359 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1361 memset(out->stainsamples, 255, ssize * tsize * 3);
1362 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1364 out->shader = &Cshader_wall_vertex;
1365 Mod_GenerateVertexLitMesh(out);
1369 out->shader = &Cshader_wall_lightmap;
1370 Mod_GenerateLightmappedMesh(out);
1374 Mod_SplitSurfMeshIfTooBig(out);
1378 static model_t *sortmodel;
1380 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1382 const msurface_t *a, *b;
1383 a = *((const msurface_t **)voida);
1384 b = *((const msurface_t **)voidb);
1385 if (a->shader != b->shader)
1386 return (qbyte *) a->shader - (qbyte *) b->shader;
1387 if (a->texinfo->texture != b->texinfo->texture);
1388 return a->texinfo->texture - b->texinfo->texture;
1392 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1396 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1397 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1398 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1400 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1409 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1411 node->parent = parent;
1412 if (node->contents < 0)
1414 Mod_SetParent (node->children[0], node);
1415 Mod_SetParent (node->children[1], node);
1423 static void Mod_LoadNodes (lump_t *l)
1429 in = (void *)(mod_base + l->fileofs);
1430 if (l->filelen % sizeof(*in))
1431 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1432 count = l->filelen / sizeof(*in);
1433 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1435 loadmodel->nodes = out;
1436 loadmodel->numnodes = count;
1438 for ( i=0 ; i<count ; i++, in++, out++)
1440 for (j=0 ; j<3 ; j++)
1442 out->mins[j] = LittleShort (in->mins[j]);
1443 out->maxs[j] = LittleShort (in->maxs[j]);
1446 p = LittleLong(in->planenum);
1447 out->plane = loadmodel->planes + p;
1449 out->firstsurface = LittleShort (in->firstface);
1450 out->numsurfaces = LittleShort (in->numfaces);
1452 for (j=0 ; j<2 ; j++)
1454 p = LittleShort (in->children[j]);
1456 out->children[j] = loadmodel->nodes + p;
1458 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1462 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1470 static void Mod_LoadLeafs (lump_t *l)
1476 in = (void *)(mod_base + l->fileofs);
1477 if (l->filelen % sizeof(*in))
1478 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1479 count = l->filelen / sizeof(*in);
1480 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1482 loadmodel->leafs = out;
1483 loadmodel->numleafs = count;
1485 for ( i=0 ; i<count ; i++, in++, out++)
1487 for (j=0 ; j<3 ; j++)
1489 out->mins[j] = LittleShort (in->mins[j]);
1490 out->maxs[j] = LittleShort (in->maxs[j]);
1493 p = LittleLong(in->contents);
1496 out->firstmarksurface = loadmodel->marksurfaces +
1497 LittleShort(in->firstmarksurface);
1498 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1500 p = LittleLong(in->visofs);
1502 out->compressed_vis = NULL;
1504 out->compressed_vis = loadmodel->visdata + p;
1506 for (j=0 ; j<4 ; j++)
1507 out->ambient_sound_level[j] = in->ambient_level[j];
1509 // FIXME: Insert caustics here
1518 static void Mod_LoadClipnodes (lump_t *l)
1520 dclipnode_t *in, *out;
1524 in = (void *)(mod_base + l->fileofs);
1525 if (l->filelen % sizeof(*in))
1526 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1527 count = l->filelen / sizeof(*in);
1528 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1530 loadmodel->clipnodes = out;
1531 loadmodel->numclipnodes = count;
1533 if (loadmodel->ishlbsp)
1535 hull = &loadmodel->hulls[1];
1536 hull->clipnodes = out;
1537 hull->firstclipnode = 0;
1538 hull->lastclipnode = count-1;
1539 hull->planes = loadmodel->planes;
1540 hull->clip_mins[0] = -16;
1541 hull->clip_mins[1] = -16;
1542 hull->clip_mins[2] = -36;
1543 hull->clip_maxs[0] = 16;
1544 hull->clip_maxs[1] = 16;
1545 hull->clip_maxs[2] = 36;
1546 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1548 hull = &loadmodel->hulls[2];
1549 hull->clipnodes = out;
1550 hull->firstclipnode = 0;
1551 hull->lastclipnode = count-1;
1552 hull->planes = loadmodel->planes;
1553 hull->clip_mins[0] = -32;
1554 hull->clip_mins[1] = -32;
1555 hull->clip_mins[2] = -32;
1556 hull->clip_maxs[0] = 32;
1557 hull->clip_maxs[1] = 32;
1558 hull->clip_maxs[2] = 32;
1559 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1561 hull = &loadmodel->hulls[3];
1562 hull->clipnodes = out;
1563 hull->firstclipnode = 0;
1564 hull->lastclipnode = count-1;
1565 hull->planes = loadmodel->planes;
1566 hull->clip_mins[0] = -16;
1567 hull->clip_mins[1] = -16;
1568 hull->clip_mins[2] = -18;
1569 hull->clip_maxs[0] = 16;
1570 hull->clip_maxs[1] = 16;
1571 hull->clip_maxs[2] = 18;
1572 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1576 hull = &loadmodel->hulls[1];
1577 hull->clipnodes = out;
1578 hull->firstclipnode = 0;
1579 hull->lastclipnode = count-1;
1580 hull->planes = loadmodel->planes;
1581 hull->clip_mins[0] = -16;
1582 hull->clip_mins[1] = -16;
1583 hull->clip_mins[2] = -24;
1584 hull->clip_maxs[0] = 16;
1585 hull->clip_maxs[1] = 16;
1586 hull->clip_maxs[2] = 32;
1587 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1589 hull = &loadmodel->hulls[2];
1590 hull->clipnodes = out;
1591 hull->firstclipnode = 0;
1592 hull->lastclipnode = count-1;
1593 hull->planes = loadmodel->planes;
1594 hull->clip_mins[0] = -32;
1595 hull->clip_mins[1] = -32;
1596 hull->clip_mins[2] = -24;
1597 hull->clip_maxs[0] = 32;
1598 hull->clip_maxs[1] = 32;
1599 hull->clip_maxs[2] = 64;
1600 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1603 for (i=0 ; i<count ; i++, out++, in++)
1605 out->planenum = LittleLong(in->planenum);
1606 out->children[0] = LittleShort(in->children[0]);
1607 out->children[1] = LittleShort(in->children[1]);
1608 if (out->children[0] >= count || out->children[1] >= count)
1609 Host_Error("Corrupt clipping hull (out of range child)\n");
1617 Duplicate the drawing hull structure as a clipping hull
1620 static void Mod_MakeHull0 (void)
1627 hull = &loadmodel->hulls[0];
1629 in = loadmodel->nodes;
1630 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1632 hull->clipnodes = out;
1633 hull->firstclipnode = 0;
1634 hull->lastclipnode = loadmodel->numnodes - 1;
1635 hull->planes = loadmodel->planes;
1637 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1639 out->planenum = in->plane - loadmodel->planes;
1640 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1641 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1647 Mod_LoadMarksurfaces
1650 static void Mod_LoadMarksurfaces (lump_t *l)
1655 in = (void *)(mod_base + l->fileofs);
1656 if (l->filelen % sizeof(*in))
1657 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1658 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1659 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1661 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1663 j = (unsigned) LittleShort(in[i]);
1664 if (j >= loadmodel->numsurfaces)
1665 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1666 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1675 static void Mod_LoadSurfedges (lump_t *l)
1680 in = (void *)(mod_base + l->fileofs);
1681 if (l->filelen % sizeof(*in))
1682 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1683 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1684 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1686 for (i = 0;i < loadmodel->numsurfedges;i++)
1687 loadmodel->surfedges[i] = LittleLong (in[i]);
1696 static void Mod_LoadPlanes (lump_t *l)
1702 in = (void *)(mod_base + l->fileofs);
1703 if (l->filelen % sizeof(*in))
1704 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1706 loadmodel->numplanes = l->filelen / sizeof(*in);
1707 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1709 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1711 out->normal[0] = LittleFloat (in->normal[0]);
1712 out->normal[1] = LittleFloat (in->normal[1]);
1713 out->normal[2] = LittleFloat (in->normal[2]);
1714 out->dist = LittleFloat (in->dist);
1720 #define MAX_POINTS_ON_WINDING 64
1726 double points[8][3]; // variable sized
1735 static winding_t *NewWinding (int points)
1740 if (points > MAX_POINTS_ON_WINDING)
1741 Sys_Error("NewWinding: too many points\n");
1743 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1744 w = Mem_Alloc(loadmodel->mempool, size);
1745 memset (w, 0, size);
1750 static void FreeWinding (winding_t *w)
1760 static winding_t *BaseWindingForPlane (mplane_t *p)
1762 double org[3], vright[3], vup[3], normal[3];
1765 VectorCopy(p->normal, normal);
1766 VectorVectorsDouble(normal, vright, vup);
1768 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1769 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1771 // project a really big axis aligned box onto the plane
1774 VectorScale (p->normal, p->dist, org);
1776 VectorSubtract (org, vright, w->points[0]);
1777 VectorAdd (w->points[0], vup, w->points[0]);
1779 VectorAdd (org, vright, w->points[1]);
1780 VectorAdd (w->points[1], vup, w->points[1]);
1782 VectorAdd (org, vright, w->points[2]);
1783 VectorSubtract (w->points[2], vup, w->points[2]);
1785 VectorSubtract (org, vright, w->points[3]);
1786 VectorSubtract (w->points[3], vup, w->points[3]);
1797 Clips the winding to the plane, returning the new winding on the positive side
1798 Frees the input winding.
1799 If keepon is true, an exactly on-plane winding will be saved, otherwise
1800 it will be clipped away.
1803 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1805 double dists[MAX_POINTS_ON_WINDING + 1];
1806 int sides[MAX_POINTS_ON_WINDING + 1];
1815 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1817 // determine sides for each point
1818 for (i = 0;i < in->numpoints;i++)
1820 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1821 if (dot > ON_EPSILON)
1822 sides[i] = SIDE_FRONT;
1823 else if (dot < -ON_EPSILON)
1824 sides[i] = SIDE_BACK;
1829 sides[i] = sides[0];
1830 dists[i] = dists[0];
1832 if (keepon && !counts[0] && !counts[1])
1843 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1844 if (maxpts > MAX_POINTS_ON_WINDING)
1845 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1847 neww = NewWinding (maxpts);
1849 for (i = 0;i < in->numpoints;i++)
1851 if (neww->numpoints >= maxpts)
1852 Sys_Error ("ClipWinding: points exceeded estimate");
1856 if (sides[i] == SIDE_ON)
1858 VectorCopy (p1, neww->points[neww->numpoints]);
1863 if (sides[i] == SIDE_FRONT)
1865 VectorCopy (p1, neww->points[neww->numpoints]);
1869 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1872 // generate a split point
1873 p2 = in->points[(i+1)%in->numpoints];
1875 dot = dists[i] / (dists[i]-dists[i+1]);
1876 for (j = 0;j < 3;j++)
1877 { // avoid round off error when possible
1878 if (split->normal[j] == 1)
1879 mid[j] = split->dist;
1880 else if (split->normal[j] == -1)
1881 mid[j] = -split->dist;
1883 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1886 VectorCopy (mid, neww->points[neww->numpoints]);
1890 // free the original winding
1901 Divides a winding by a plane, producing one or two windings. The
1902 original winding is not damaged or freed. If only on one side, the
1903 returned winding will be the input winding. If on both sides, two
1904 new windings will be created.
1907 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1909 double dists[MAX_POINTS_ON_WINDING + 1];
1910 int sides[MAX_POINTS_ON_WINDING + 1];
1919 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1921 // determine sides for each point
1922 for (i = 0;i < in->numpoints;i++)
1924 dot = DotProduct (in->points[i], split->normal);
1927 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1928 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1929 else sides[i] = SIDE_ON;
1932 sides[i] = sides[0];
1933 dists[i] = dists[0];
1935 *front = *back = NULL;
1948 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1950 if (maxpts > MAX_POINTS_ON_WINDING)
1951 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1953 *front = f = NewWinding (maxpts);
1954 *back = b = NewWinding (maxpts);
1956 for (i = 0;i < in->numpoints;i++)
1958 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1959 Sys_Error ("DivideWinding: points exceeded estimate");
1963 if (sides[i] == SIDE_ON)
1965 VectorCopy (p1, f->points[f->numpoints]);
1967 VectorCopy (p1, b->points[b->numpoints]);
1972 if (sides[i] == SIDE_FRONT)
1974 VectorCopy (p1, f->points[f->numpoints]);
1977 else if (sides[i] == SIDE_BACK)
1979 VectorCopy (p1, b->points[b->numpoints]);
1983 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1986 // generate a split point
1987 p2 = in->points[(i+1)%in->numpoints];
1989 dot = dists[i] / (dists[i]-dists[i+1]);
1990 for (j = 0;j < 3;j++)
1991 { // avoid round off error when possible
1992 if (split->normal[j] == 1)
1993 mid[j] = split->dist;
1994 else if (split->normal[j] == -1)
1995 mid[j] = -split->dist;
1997 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2000 VectorCopy (mid, f->points[f->numpoints]);
2002 VectorCopy (mid, b->points[b->numpoints]);
2007 typedef struct portal_s
2010 mnode_t *nodes[2]; // [0] = front side of plane
2011 struct portal_s *next[2];
2013 struct portal_s *chain; // all portals are linked into a list
2017 static portal_t *portalchain;
2024 static portal_t *AllocPortal (void)
2027 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2028 p->chain = portalchain;
2033 static void FreePortal(portal_t *p)
2038 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2040 // calculate children first
2041 if (node->children[0]->contents >= 0)
2042 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2043 if (node->children[1]->contents >= 0)
2044 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2046 // make combined bounding box from children
2047 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2048 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2049 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2050 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2051 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2052 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2055 static void Mod_FinalizePortals(void)
2057 int i, j, numportals, numpoints;
2058 portal_t *p, *pnext;
2061 mleaf_t *leaf, *endleaf;
2064 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2065 leaf = loadmodel->leafs;
2066 endleaf = leaf + loadmodel->numleafs;
2067 for (;leaf < endleaf;leaf++)
2069 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2070 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2077 for (i = 0;i < 2;i++)
2079 leaf = (mleaf_t *)p->nodes[i];
2081 for (j = 0;j < w->numpoints;j++)
2083 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2084 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2085 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2086 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2087 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2088 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2095 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2097 // tally up portal and point counts
2103 // note: this check must match the one below or it will usually corrupt memory
2104 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2105 if (p->winding && p->nodes[0] != p->nodes[1]
2106 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2107 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2110 numpoints += p->winding->numpoints * 2;
2114 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2115 loadmodel->numportals = numportals;
2116 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2117 loadmodel->numportalpoints = numpoints;
2118 // clear all leaf portal chains
2119 for (i = 0;i < loadmodel->numleafs;i++)
2120 loadmodel->leafs[i].portals = NULL;
2121 // process all portals in the global portal chain, while freeing them
2122 portal = loadmodel->portals;
2123 point = loadmodel->portalpoints;
2132 // note: this check must match the one above or it will usually corrupt memory
2133 // the nodes[0] != nodes[1] check is because leaf 0 is the shared solid leaf, it can have many portals inside with leaf 0 on both sides
2134 if (p->nodes[0] != p->nodes[1]
2135 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2136 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2138 // first make the back to front portal (forward portal)
2139 portal->points = point;
2140 portal->numpoints = p->winding->numpoints;
2141 portal->plane.dist = p->plane.dist;
2142 VectorCopy(p->plane.normal, portal->plane.normal);
2143 portal->here = (mleaf_t *)p->nodes[1];
2144 portal->past = (mleaf_t *)p->nodes[0];
2146 for (j = 0;j < portal->numpoints;j++)
2148 VectorCopy(p->winding->points[j], point->position);
2151 PlaneClassify(&portal->plane);
2153 // link into leaf's portal chain
2154 portal->next = portal->here->portals;
2155 portal->here->portals = portal;
2157 // advance to next portal
2160 // then make the front to back portal (backward portal)
2161 portal->points = point;
2162 portal->numpoints = p->winding->numpoints;
2163 portal->plane.dist = -p->plane.dist;
2164 VectorNegate(p->plane.normal, portal->plane.normal);
2165 portal->here = (mleaf_t *)p->nodes[0];
2166 portal->past = (mleaf_t *)p->nodes[1];
2168 for (j = portal->numpoints - 1;j >= 0;j--)
2170 VectorCopy(p->winding->points[j], point->position);
2173 PlaneClassify(&portal->plane);
2175 // link into leaf's portal chain
2176 portal->next = portal->here->portals;
2177 portal->here->portals = portal;
2179 // advance to next portal
2182 FreeWinding(p->winding);
2194 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2197 Host_Error ("AddPortalToNodes: NULL front node");
2199 Host_Error ("AddPortalToNodes: NULL back node");
2200 if (p->nodes[0] || p->nodes[1])
2201 Host_Error ("AddPortalToNodes: already included");
2202 // note: front == back is handled gracefully, because leaf 0 is the shared solid leaf, it can often have portals with the same leaf on both sides
2204 p->nodes[0] = front;
2205 p->next[0] = (portal_t *)front->portals;
2206 front->portals = (mportal_t *)p;
2209 p->next[1] = (portal_t *)back->portals;
2210 back->portals = (mportal_t *)p;
2215 RemovePortalFromNode
2218 static void RemovePortalFromNodes(portal_t *portal)
2222 void **portalpointer;
2224 for (i = 0;i < 2;i++)
2226 node = portal->nodes[i];
2228 portalpointer = (void **) &node->portals;
2233 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2237 if (portal->nodes[0] == node)
2239 *portalpointer = portal->next[0];
2240 portal->nodes[0] = NULL;
2242 else if (portal->nodes[1] == node)
2244 *portalpointer = portal->next[1];
2245 portal->nodes[1] = NULL;
2248 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2252 if (t->nodes[0] == node)
2253 portalpointer = (void **) &t->next[0];
2254 else if (t->nodes[1] == node)
2255 portalpointer = (void **) &t->next[1];
2257 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2262 static void Mod_RecursiveNodePortals (mnode_t *node)
2265 mnode_t *front, *back, *other_node;
2266 mplane_t clipplane, *plane;
2267 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2268 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2270 // if a leaf, we're done
2274 plane = node->plane;
2276 front = node->children[0];
2277 back = node->children[1];
2279 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2281 // create the new portal by generating a polygon for the node plane,
2282 // and clipping it by all of the other portals (which came from nodes above this one)
2283 nodeportal = AllocPortal ();
2284 nodeportal->plane = *node->plane;
2286 nodeportalwinding = BaseWindingForPlane (node->plane);
2287 side = 0; // shut up compiler warning
2288 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2290 clipplane = portal->plane;
2291 if (portal->nodes[0] == portal->nodes[1])
2292 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2293 if (portal->nodes[0] == node)
2295 else if (portal->nodes[1] == node)
2297 clipplane.dist = -clipplane.dist;
2298 VectorNegate (clipplane.normal, clipplane.normal);
2302 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2304 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2305 if (!nodeportalwinding)
2307 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2312 if (nodeportalwinding)
2314 // if the plane was not clipped on all sides, there was an error
2315 nodeportal->winding = nodeportalwinding;
2316 AddPortalToNodes (nodeportal, front, back);
2319 // split the portals of this node along this node's plane and assign them to the children of this node
2320 // (migrating the portals downward through the tree)
2321 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2323 if (portal->nodes[0] == portal->nodes[1])
2324 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2325 if (portal->nodes[0] == node)
2327 else if (portal->nodes[1] == node)
2330 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2331 nextportal = portal->next[side];
2333 other_node = portal->nodes[!side];
2334 RemovePortalFromNodes (portal);
2336 // cut the portal into two portals, one on each side of the node plane
2337 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2342 AddPortalToNodes (portal, back, other_node);
2344 AddPortalToNodes (portal, other_node, back);
2350 AddPortalToNodes (portal, front, other_node);
2352 AddPortalToNodes (portal, other_node, front);
2356 // the winding is split
2357 splitportal = AllocPortal ();
2358 temp = splitportal->chain;
2359 *splitportal = *portal;
2360 splitportal->chain = temp;
2361 splitportal->winding = backwinding;
2362 FreeWinding (portal->winding);
2363 portal->winding = frontwinding;
2367 AddPortalToNodes (portal, front, other_node);
2368 AddPortalToNodes (splitportal, back, other_node);
2372 AddPortalToNodes (portal, other_node, front);
2373 AddPortalToNodes (splitportal, other_node, back);
2377 Mod_RecursiveNodePortals(front);
2378 Mod_RecursiveNodePortals(back);
2382 static void Mod_MakePortals(void)
2385 Mod_RecursiveNodePortals (loadmodel->nodes);
2386 Mod_FinalizePortals();
2394 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2399 mempool_t *mainmempool;
2402 mod->type = mod_brush;
2404 header = (dheader_t *)buffer;
2406 i = LittleLong (header->version);
2407 if (i != BSPVERSION && i != 30)
2408 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2409 mod->ishlbsp = i == 30;
2410 if (loadmodel->isworldmodel)
2411 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2413 // swap all the lumps
2414 mod_base = (qbyte *)header;
2416 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2417 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2421 // store which lightmap format to use
2422 mod->lightmaprgba = r_lightmaprgba.integer;
2424 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2425 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2426 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2427 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2428 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2429 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2430 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2431 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2432 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2433 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2434 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2435 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2436 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2437 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2438 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2443 mod->numframes = 2; // regular and alternate animation
2445 mainmempool = mod->mempool;
2446 loadname = mod->name;
2448 Mod_LoadLightList ();
2451 // set up the submodels (FIXME: this is confusing)
2453 for (i = 0;i < mod->numsubmodels;i++)
2456 float dist, modelyawradius, modelradius, *vec;
2459 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2460 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2464 bm = &mod->submodels[i];
2466 mod->hulls[0].firstclipnode = bm->headnode[0];
2467 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2469 mod->hulls[j].firstclipnode = bm->headnode[j];
2470 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2473 mod->firstmodelsurface = bm->firstface;
2474 mod->nummodelsurfaces = bm->numfaces;
2476 mod->DrawSky = NULL;
2477 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2478 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2480 // we only need to have a drawsky function if it is used (usually only on world model)
2481 if (surf->shader == &Cshader_sky)
2482 mod->DrawSky = R_DrawBrushModelSky;
2483 for (k = 0;k < surf->numedges;k++)
2485 l = mod->surfedges[k + surf->firstedge];
2487 vec = mod->vertexes[mod->edges[l].v[0]].position;
2489 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2490 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2491 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2492 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2493 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2494 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2495 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2496 dist = vec[0]*vec[0]+vec[1]*vec[1];
2497 if (modelyawradius < dist)
2498 modelyawradius = dist;
2499 dist += vec[2]*vec[2];
2500 if (modelradius < dist)
2504 modelyawradius = sqrt(modelyawradius);
2505 modelradius = sqrt(modelradius);
2506 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2507 mod->yawmins[2] = mod->normalmins[2];
2508 mod->yawmaxs[2] = mod->normalmaxs[2];
2509 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2510 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2511 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2512 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2514 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2515 VectorClear(mod->normalmins);
2516 VectorClear(mod->normalmaxs);
2517 VectorClear(mod->yawmins);
2518 VectorClear(mod->yawmaxs);
2519 VectorClear(mod->rotatedmins);
2520 VectorClear(mod->rotatedmaxs);
2523 mod->numleafs = bm->visleafs;
2525 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2526 mod->Draw = R_DrawBrushModelNormal;
2527 mod->DrawShadow = NULL;
2529 Mod_BrushSortedSurfaces(mod, mainmempool);
2531 // LordHavoc: only register submodels if it is the world
2532 // (prevents bsp models from replacing world submodels)
2533 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2536 // duplicate the basic information
2537 sprintf (name, "*%i", i+1);
2538 loadmodel = Mod_FindName (name);
2540 strcpy (loadmodel->name, name);
2541 // textures and memory belong to the main model
2542 loadmodel->texturepool = NULL;
2543 loadmodel->mempool = NULL;