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;
143 texture_t *tx, *tx2, *anims[10], *altanims[10];
145 qbyte *data, *mtdata, *data2;
148 loadmodel->textures = NULL;
153 m = (dmiptexlump_t *)(mod_base + l->fileofs);
155 m->nummiptex = LittleLong (m->nummiptex);
157 // add two slots for notexture walls and notexture liquids
158 loadmodel->numtextures = m->nummiptex + 2;
159 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
161 // fill out all slots with notexture
162 for (i = 0;i < loadmodel->numtextures;i++)
164 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
167 tx->texture = r_notexture;
168 if (i == loadmodel->numtextures - 1)
169 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
172 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
174 // LordHavoc: mostly rewritten map texture loader
175 for (i = 0;i < m->nummiptex;i++)
177 dofs[i] = LittleLong(dofs[i]);
178 if (dofs[i] == -1 || r_nosurftextures.integer)
180 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
182 // make sure name is no more than 15 characters
183 for (j = 0;dmiptex->name[j] && j < 15;j++)
184 name[j] = dmiptex->name[j];
187 mtwidth = LittleLong (dmiptex->width);
188 mtheight = LittleLong (dmiptex->height);
190 j = LittleLong (dmiptex->offsets[0]);
194 if (j < 40 || j + mtwidth * mtheight > l->filelen)
196 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
199 mtdata = (qbyte *)dmiptex + j;
202 if ((mtwidth & 15) || (mtheight & 15))
203 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
205 // LordHavoc: force all names to lowercase
206 for (j = 0;name[j];j++)
207 if (name[j] >= 'A' && name[j] <= 'Z')
208 name[j] += 'a' - 'A';
210 tx = loadmodel->textures[i];
211 strcpy(tx->name, name);
213 tx->height = mtheight;
215 tx->glowtexture = NULL;
216 tx->fogtexture = NULL;
220 sprintf(tx->name, "unnamed%i", i);
221 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
224 // LordHavoc: HL sky textures are entirely different than quake
225 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
227 data = loadimagepixels(tx->name, false, 0, 0);
230 if (image_width == 256 && image_height == 128)
232 if (loadmodel->isworldmodel)
239 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
242 else if (loadmodel->isworldmodel)
243 R_InitSky (mtdata, 1);
245 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
247 tx->fogtexture = image_masktex;
248 strcpy(name, tx->name);
249 strcat(name, "_glow");
250 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
254 if (loadmodel->ishlbsp)
256 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
259 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
260 if (R_TextureHasAlpha(tx->texture))
263 for (j = 0;j < image_width * image_height;j++)
264 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
265 strcpy(name, tx->name);
266 strcat(name, "_fog");
267 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
271 else if ((data = W_GetTexture(tx->name)))
273 // get the size from the wad texture
274 tx->width = image_width;
275 tx->height = image_height;
276 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
277 if (R_TextureHasAlpha(tx->texture))
280 for (j = 0;j < image_width * image_height;j++)
281 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
282 strcpy(name, tx->name);
283 strcat(name, "_fog");
284 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
292 tx->texture = r_notexture;
297 if (mtdata) // texture included
302 if (r_fullbrights.value && tx->name[0] != '*')
304 for (j = 0;j < tx->width*tx->height;j++)
306 if (data[j] >= 224) // fullbright
315 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
316 for (j = 0;j < tx->width*tx->height;j++)
317 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
318 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
319 strcpy(name, tx->name);
320 strcat(name, "_glow");
321 for (j = 0;j < tx->width*tx->height;j++)
322 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
323 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
327 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
329 else // no texture, and no external replacement texture was found
333 tx->texture = r_notexture;
338 if (tx->name[0] == '*')
340 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
341 // LordHavoc: some turbulent textures should be fullbright and solid
342 if (!strncmp(tx->name,"*lava",5)
343 || !strncmp(tx->name,"*teleport",9)
344 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
345 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
347 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
348 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
351 tx->flags |= SURF_LIGHTMAP;
352 if (!R_TextureHasAlpha(tx->texture))
353 tx->flags |= SURF_CLIPSOLID;
357 // sequence the animations
358 for (i = 0;i < m->nummiptex;i++)
360 tx = loadmodel->textures[i];
361 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
364 continue; // already sequenced
366 // find the number of frames in the animation
367 memset (anims, 0, sizeof(anims));
368 memset (altanims, 0, sizeof(altanims));
370 for (j = i;j < m->nummiptex;j++)
372 tx2 = loadmodel->textures[j];
373 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
377 if (num >= '0' && num <= '9')
378 anims[num - '0'] = tx2;
379 else if (num >= 'a' && num <= 'j')
380 altanims[num - 'a'] = tx2;
382 Host_Error ("Bad animating texture %s", tx->name);
386 for (j = 0;j < 10;j++)
388 if (anims[j] != NULL)
390 if (altanims[j] != NULL)
395 // if there is no alternate animation, duplicate the primary
396 // animation into the alternate
398 for (k = 0;k < 10;k++)
399 anims[k] = altanims[k];
402 // link together the primary animation
403 for (j = 0;j < max;j++)
407 Host_Error ("Missing frame %i of %s", j, tx->name);
408 tx2->animated = true;
409 tx2->anim_total[0] = max;
410 tx2->anim_total[1] = altmax;
411 for (k = 0;k < 10;k++)
413 tx2->anim_frames[0][k] = anims[k];
414 tx2->anim_frames[1][k] = altanims[k];
418 // link together the alternate animation
419 for (j = 0;j < altmax;j++)
423 Host_Error ("Missing frame %i of %s", j, tx->name);
424 tx2->animated = true;
425 // the primary/alternate are reversed here
426 tx2->anim_total[0] = altmax;
427 tx2->anim_total[1] = max;
428 for (k = 0;k < 10;k++)
430 tx2->anim_frames[0][k] = altanims[k];
431 tx2->anim_frames[1][k] = anims[k];
442 static void Mod_LoadLighting (lump_t *l)
445 qbyte *in, *out, *data, d;
446 char litfilename[1024];
447 loadmodel->lightdata = NULL;
448 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
450 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
451 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
453 else // LordHavoc: bsp version 29 (normal white lighting)
455 // LordHavoc: hope is not lost yet, check for a .lit file to load
456 strcpy(litfilename, loadmodel->name);
457 COM_StripExtension(litfilename, litfilename);
458 strcat(litfilename, ".lit");
459 data = (qbyte*) COM_LoadFile (litfilename, false);
462 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
464 i = LittleLong(((int *)data)[1]);
467 Con_DPrintf("%s loaded", litfilename);
468 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
469 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
475 Con_Printf("Unknown .lit file version (%d)\n", i);
482 Con_Printf("Empty .lit file, ignoring\n");
484 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
488 // LordHavoc: oh well, expand the white lighting data
491 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
492 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
493 out = loadmodel->lightdata;
494 memcpy (in, mod_base + l->fileofs, l->filelen);
495 for (i = 0;i < l->filelen;i++)
505 void Mod_LoadLightList(void)
508 char lightsfilename[1024], *s, *t, *lightsstring;
511 strcpy(lightsfilename, loadmodel->name);
512 COM_StripExtension(lightsfilename, lightsfilename);
513 strcat(lightsfilename, ".lights");
514 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
520 while (*s && *s != '\n')
524 Mem_Free(lightsstring);
525 Host_Error("lights file must end with a newline\n");
530 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
533 while (*s && n < numlights)
536 while (*s && *s != '\n')
540 Mem_Free(lightsstring);
541 Host_Error("misparsed lights file!\n");
543 e = loadmodel->lights + n;
545 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);
549 Mem_Free(lightsstring);
550 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);
557 Mem_Free(lightsstring);
558 Host_Error("misparsed lights file!\n");
560 loadmodel->numlights = numlights;
561 Mem_Free(lightsstring);
571 static void Mod_LoadVisibility (lump_t *l)
573 loadmodel->visdata = NULL;
576 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
577 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
580 // used only for HalfLife maps
581 void Mod_ParseWadsFromEntityLump(char *data)
583 char key[128], value[4096];
588 data = COM_Parse(data);
591 if (com_token[0] != '{')
595 data = COM_Parse(data);
598 if (com_token[0] == '}')
599 break; // end of worldspawn
600 if (com_token[0] == '_')
601 strcpy(key, com_token + 1);
603 strcpy(key, com_token);
604 while (key[strlen(key)-1] == ' ') // remove trailing spaces
605 key[strlen(key)-1] = 0;
606 data = COM_Parse(data);
609 strcpy(value, com_token);
610 if (!strcmp("wad", key)) // for HalfLife maps
612 if (loadmodel->ishlbsp)
615 for (i = 0;i < 4096;i++)
616 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
622 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
623 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
625 else if (value[i] == ';' || value[i] == 0)
629 strcpy(wadname, "textures/");
630 strcat(wadname, &value[j]);
631 W_LoadTextureWadFile (wadname, false);
648 static void Mod_LoadEntities (lump_t *l)
650 loadmodel->entities = NULL;
653 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
654 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
655 if (loadmodel->ishlbsp)
656 Mod_ParseWadsFromEntityLump(loadmodel->entities);
665 static void Mod_LoadVertexes (lump_t *l)
671 in = (void *)(mod_base + l->fileofs);
672 if (l->filelen % sizeof(*in))
673 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
674 count = l->filelen / sizeof(*in);
675 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
677 loadmodel->vertexes = out;
678 loadmodel->numvertexes = count;
680 for ( i=0 ; i<count ; i++, in++, out++)
682 out->position[0] = LittleFloat (in->point[0]);
683 out->position[1] = LittleFloat (in->point[1]);
684 out->position[2] = LittleFloat (in->point[2]);
693 static void Mod_LoadSubmodels (lump_t *l)
699 in = (void *)(mod_base + l->fileofs);
700 if (l->filelen % sizeof(*in))
701 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
702 count = l->filelen / sizeof(*in);
703 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
705 loadmodel->submodels = out;
706 loadmodel->numsubmodels = count;
708 for ( i=0 ; i<count ; i++, in++, out++)
710 for (j=0 ; j<3 ; j++)
712 // spread the mins / maxs by a pixel
713 out->mins[j] = LittleFloat (in->mins[j]) - 1;
714 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
715 out->origin[j] = LittleFloat (in->origin[j]);
717 for (j=0 ; j<MAX_MAP_HULLS ; j++)
718 out->headnode[j] = LittleLong (in->headnode[j]);
719 out->visleafs = LittleLong (in->visleafs);
720 out->firstface = LittleLong (in->firstface);
721 out->numfaces = LittleLong (in->numfaces);
730 static void Mod_LoadEdges (lump_t *l)
736 in = (void *)(mod_base + l->fileofs);
737 if (l->filelen % sizeof(*in))
738 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
739 count = l->filelen / sizeof(*in);
740 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
742 loadmodel->edges = out;
743 loadmodel->numedges = count;
745 for ( i=0 ; i<count ; i++, in++, out++)
747 out->v[0] = (unsigned short)LittleShort(in->v[0]);
748 out->v[1] = (unsigned short)LittleShort(in->v[1]);
757 static void Mod_LoadTexinfo (lump_t *l)
761 int i, j, k, count, miptex;
763 in = (void *)(mod_base + l->fileofs);
764 if (l->filelen % sizeof(*in))
765 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
766 count = l->filelen / sizeof(*in);
767 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
769 loadmodel->texinfo = out;
770 loadmodel->numtexinfo = count;
772 for (i = 0;i < count;i++, in++, out++)
774 for (k = 0;k < 2;k++)
775 for (j = 0;j < 4;j++)
776 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
778 miptex = LittleLong (in->miptex);
779 out->flags = LittleLong (in->flags);
782 if (loadmodel->textures)
784 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
785 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
787 out->texture = loadmodel->textures[miptex];
789 if (out->texture == NULL)
791 // choose either the liquid notexture, or the normal notexture
792 if (out->flags & TEX_SPECIAL)
793 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
795 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
804 Fills in s->texturemins[] and s->extents[]
807 static void CalcSurfaceExtents (msurface_t *s)
809 float mins[2], maxs[2], val;
813 int bmins[2], bmaxs[2];
815 mins[0] = mins[1] = 999999999;
816 maxs[0] = maxs[1] = -999999999;
820 for (i=0 ; i<s->numedges ; i++)
822 e = loadmodel->surfedges[s->firstedge+i];
824 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
826 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
828 for (j=0 ; j<2 ; j++)
830 val = v->position[0] * tex->vecs[j][0] +
831 v->position[1] * tex->vecs[j][1] +
832 v->position[2] * tex->vecs[j][2] +
841 for (i=0 ; i<2 ; i++)
843 bmins[i] = floor(mins[i]/16);
844 bmaxs[i] = ceil(maxs[i]/16);
846 s->texturemins[i] = bmins[i] * 16;
847 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
852 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
857 mins[0] = mins[1] = mins[2] = 9999;
858 maxs[0] = maxs[1] = maxs[2] = -9999;
860 for (i = 0;i < numverts;i++)
862 for (j = 0;j < 3;j++, v++)
872 #define MAX_SUBDIVPOLYTRIANGLES 4096
873 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
875 static int subdivpolyverts, subdivpolytriangles;
876 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
877 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
879 static int subdivpolylookupvert(vec3_t v)
882 for (i = 0;i < subdivpolyverts;i++)
883 if (subdivpolyvert[i][0] == v[0]
884 && subdivpolyvert[i][1] == v[1]
885 && subdivpolyvert[i][2] == v[2])
887 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
888 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
889 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
890 return subdivpolyverts++;
893 static void SubdividePolygon (int numverts, float *verts)
895 int i, i1, i2, i3, f, b, c, p;
896 vec3_t mins, maxs, front[256], back[256];
897 float m, *pv, *cv, dist[256], frac;
900 Host_Error ("SubdividePolygon: ran out of verts in buffer");
902 BoundPoly (numverts, verts, mins, maxs);
904 for (i = 0;i < 3;i++)
906 m = (mins[i] + maxs[i]) * 0.5;
907 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
914 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
918 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
922 VectorCopy (pv, front[f]);
927 VectorCopy (pv, back[b]);
930 if (dist[p] == 0 || dist[c] == 0)
932 if ( (dist[p] > 0) != (dist[c] > 0) )
935 frac = dist[p] / (dist[p] - dist[c]);
936 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
937 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
938 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
944 SubdividePolygon (f, front[0]);
945 SubdividePolygon (b, back[0]);
949 i1 = subdivpolylookupvert(verts);
950 i2 = subdivpolylookupvert(verts + 3);
951 for (i = 2;i < numverts;i++)
953 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
955 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
959 i3 = subdivpolylookupvert(verts + i * 3);
960 subdivpolyindex[subdivpolytriangles][0] = i1;
961 subdivpolyindex[subdivpolytriangles][1] = i2;
962 subdivpolyindex[subdivpolytriangles][2] = i3;
964 subdivpolytriangles++;
972 Breaks a polygon up along axial 64 unit
973 boundaries so that turbulent and sky warps
974 can be done reasonably.
977 void Mod_GenerateWarpMesh (msurface_t *surf)
983 subdivpolytriangles = 0;
985 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
986 if (subdivpolytriangles < 1)
987 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
989 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
990 mesh->numverts = subdivpolyverts;
991 mesh->numtriangles = subdivpolytriangles;
992 mesh->vertex = (surfvertex_t *)(mesh + 1);
993 mesh->index = (int *)(mesh->vertex + mesh->numverts);
994 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
996 for (i = 0;i < mesh->numtriangles;i++)
997 for (j = 0;j < 3;j++)
998 mesh->index[i*3+j] = subdivpolyindex[i][j];
1000 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1002 VectorCopy(subdivpolyvert[i], v->v);
1003 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1004 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1008 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1010 int i, is, it, *index, smax, tmax;
1015 smax = surf->extents[0] >> 4;
1016 tmax = surf->extents[1] >> 4;
1017 surf->lightmaptexturestride = 0;
1018 surf->lightmaptexture = NULL;
1020 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1021 mesh->numverts = surf->poly_numverts;
1022 mesh->numtriangles = surf->poly_numverts - 2;
1023 mesh->vertex = (surfvertex_t *)(mesh + 1);
1024 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1025 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1027 index = mesh->index;
1028 for (i = 0;i < mesh->numtriangles;i++)
1035 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1037 VectorCopy (in, out->v);
1039 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1040 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1042 out->st[0] = s / surf->texinfo->texture->width;
1043 out->st[1] = t / surf->texinfo->texture->height;
1045 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1046 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1048 // lightmap coordinates
1052 // LordHavoc: calc lightmap data offset for vertex lighting to use
1055 is = bound(0, is, smax);
1056 it = bound(0, it, tmax);
1057 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1061 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1063 int i, is, it, *index, smax, tmax;
1064 float *in, s, t, xbase, ybase, xscale, yscale;
1068 surf->flags |= SURF_LIGHTMAP;
1069 smax = surf->extents[0] >> 4;
1070 tmax = surf->extents[1] >> 4;
1071 if (r_miplightmaps.integer)
1073 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1074 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);
1078 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1079 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);
1081 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1082 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1083 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1085 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1086 mesh->numverts = surf->poly_numverts;
1087 mesh->numtriangles = surf->poly_numverts - 2;
1088 mesh->vertex = (surfvertex_t *)(mesh + 1);
1089 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1090 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1092 index = mesh->index;
1093 for (i = 0;i < mesh->numtriangles;i++)
1100 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1102 VectorCopy (in, out->v);
1104 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1105 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1107 out->st[0] = s / surf->texinfo->texture->width;
1108 out->st[1] = t / surf->texinfo->texture->height;
1110 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1111 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1113 // lightmap coordinates
1114 out->uv[0] = s * xscale + xbase;
1115 out->uv[1] = t * yscale + ybase;
1117 // LordHavoc: calc lightmap data offset for vertex lighting to use
1120 is = bound(0, is, smax);
1121 it = bound(0, it, tmax);
1122 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1126 void Mod_GenerateVertexMesh (msurface_t *surf)
1133 surf->lightmaptexturestride = 0;
1134 surf->lightmaptexture = NULL;
1136 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1137 mesh->numverts = surf->poly_numverts;
1138 mesh->numtriangles = surf->poly_numverts - 2;
1139 mesh->vertex = (surfvertex_t *)(mesh + 1);
1140 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1141 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1143 index = mesh->index;
1144 for (i = 0;i < mesh->numtriangles;i++)
1151 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1153 VectorCopy (in, out->v);
1154 out->st[0] = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) / surf->texinfo->texture->width;
1155 out->st[1] = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) / surf->texinfo->texture->height;
1159 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1166 // convert edges back to a normal polygon
1167 surf->poly_numverts = surf->numedges;
1168 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1169 for (i = 0;i < surf->numedges;i++)
1171 lindex = loadmodel->surfedges[surf->firstedge + i];
1173 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1175 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1176 VectorCopy (vec, vert);
1181 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1183 int j, base, tricount, newvertexcount, *index, *vertexremap;
1184 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1185 if (s->mesh->numtriangles > 1000)
1187 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1192 while (base < s->mesh->numtriangles)
1194 tricount = s->mesh->numtriangles - base;
1195 if (tricount > 1000)
1197 index = s->mesh->index + base * 3;
1201 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1202 for (j = 0;j < tricount * 3;j++)
1203 if (vertexremap[index[j]] < 0)
1204 vertexremap[index[j]] = newvertexcount++;
1206 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1207 newmesh->chain = NULL;
1208 newmesh->numverts = newvertexcount;
1209 newmesh->numtriangles = tricount;
1210 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1211 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1212 for (j = 0;j < tricount * 3;j++)
1214 newmesh->index[j] = vertexremap[index[j]];
1215 // yes this copies the same vertex multiple times in many cases... but that's ok...
1216 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1219 oldmesh->chain = newmesh;
1221 firstmesh = newmesh;
1224 Mem_Free(vertexremap);
1226 s->mesh = firstmesh;
1235 static void Mod_LoadFaces (lump_t *l)
1239 int i, count, surfnum, planenum, ssize, tsize;
1241 in = (void *)(mod_base + l->fileofs);
1242 if (l->filelen % sizeof(*in))
1243 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1244 count = l->filelen / sizeof(*in);
1245 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1247 loadmodel->surfaces = out;
1248 loadmodel->numsurfaces = count;
1250 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1252 // FIXME: validate edges, texinfo, etc?
1253 out->firstedge = LittleLong(in->firstedge);
1254 out->numedges = LittleShort(in->numedges);
1255 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1256 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1258 i = LittleShort (in->texinfo);
1259 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1260 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1261 out->texinfo = loadmodel->texinfo + i;
1262 out->flags = out->texinfo->texture->flags;
1264 planenum = LittleShort(in->planenum);
1265 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1266 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1268 if (LittleShort(in->side))
1269 out->flags |= SURF_PLANEBACK;
1271 out->plane = loadmodel->planes + planenum;
1273 // clear lightmap (filled in later)
1274 out->lightmaptexture = NULL;
1276 // force lightmap upload on first time seeing the surface
1277 out->cached_dlight = true;
1278 out->cached_ambient = -1000;
1279 out->cached_lightscalebit = -1000;
1281 CalcSurfaceExtents (out);
1283 ssize = (out->extents[0] >> 4) + 1;
1284 tsize = (out->extents[1] >> 4) + 1;
1287 for (i = 0;i < MAXLIGHTMAPS;i++)
1288 out->styles[i] = in->styles[i];
1289 i = LittleLong(in->lightofs);
1291 out->samples = NULL;
1292 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1293 out->samples = loadmodel->lightdata + i;
1294 else // LordHavoc: white lighting (bsp version 29)
1295 out->samples = loadmodel->lightdata + (i * 3);
1297 Mod_GenerateSurfacePolygon(out);
1299 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1301 out->shader = &Cshader_sky;
1302 out->samples = NULL;
1303 Mod_GenerateWarpMesh (out);
1305 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1307 out->shader = &Cshader_water;
1308 out->samples = NULL;
1309 Mod_GenerateWarpMesh (out);
1313 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1314 out->flags |= SURF_CLIPSOLID;
1315 if (out->texinfo->flags & TEX_SPECIAL)
1317 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1318 out->shader = &Cshader_water;
1319 out->shader = &Cshader_water;
1320 out->samples = NULL;
1321 Mod_GenerateWarpMesh (out);
1323 else if ((out->extents[0]+1) > (256*16) || (out->extents[1]+1) > (256*16))
1325 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1326 out->shader = &Cshader_wall_fullbright;
1327 out->samples = NULL;
1328 Mod_GenerateVertexMesh(out);
1332 // stainmap for permanent marks on walls
1333 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1335 memset(out->stainsamples, 255, ssize * tsize * 3);
1336 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1338 out->shader = &Cshader_wall_vertex;
1339 Mod_GenerateVertexLitMesh(out);
1343 out->shader = &Cshader_wall_lightmap;
1344 Mod_GenerateLightmappedMesh(out);
1348 Mod_SplitSurfMeshIfTooBig(out);
1352 static model_t *sortmodel;
1354 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1356 const msurface_t *a, *b;
1357 a = *((const msurface_t **)voida);
1358 b = *((const msurface_t **)voidb);
1359 if (a->shader != b->shader)
1360 return (qbyte *) a->shader - (qbyte *) b->shader;
1361 if (a->texinfo->texture != b->texinfo->texture);
1362 return a->texinfo->texture - b->texinfo->texture;
1366 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1370 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1371 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1372 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1374 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1383 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1385 node->parent = parent;
1386 if (node->contents < 0)
1388 Mod_SetParent (node->children[0], node);
1389 Mod_SetParent (node->children[1], node);
1397 static void Mod_LoadNodes (lump_t *l)
1403 in = (void *)(mod_base + l->fileofs);
1404 if (l->filelen % sizeof(*in))
1405 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1406 count = l->filelen / sizeof(*in);
1407 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1409 loadmodel->nodes = out;
1410 loadmodel->numnodes = count;
1412 for ( i=0 ; i<count ; i++, in++, out++)
1414 for (j=0 ; j<3 ; j++)
1416 out->mins[j] = LittleShort (in->mins[j]);
1417 out->maxs[j] = LittleShort (in->maxs[j]);
1420 p = LittleLong(in->planenum);
1421 out->plane = loadmodel->planes + p;
1423 out->firstsurface = LittleShort (in->firstface);
1424 out->numsurfaces = LittleShort (in->numfaces);
1426 for (j=0 ; j<2 ; j++)
1428 p = LittleShort (in->children[j]);
1430 out->children[j] = loadmodel->nodes + p;
1432 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1436 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1444 static void Mod_LoadLeafs (lump_t *l)
1450 in = (void *)(mod_base + l->fileofs);
1451 if (l->filelen % sizeof(*in))
1452 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1453 count = l->filelen / sizeof(*in);
1454 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1456 loadmodel->leafs = out;
1457 loadmodel->numleafs = count;
1459 for ( i=0 ; i<count ; i++, in++, out++)
1461 for (j=0 ; j<3 ; j++)
1463 out->mins[j] = LittleShort (in->mins[j]);
1464 out->maxs[j] = LittleShort (in->maxs[j]);
1467 p = LittleLong(in->contents);
1470 out->firstmarksurface = loadmodel->marksurfaces +
1471 LittleShort(in->firstmarksurface);
1472 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1474 p = LittleLong(in->visofs);
1476 out->compressed_vis = NULL;
1478 out->compressed_vis = loadmodel->visdata + p;
1480 for (j=0 ; j<4 ; j++)
1481 out->ambient_sound_level[j] = in->ambient_level[j];
1483 // FIXME: Insert caustics here
1492 static void Mod_LoadClipnodes (lump_t *l)
1494 dclipnode_t *in, *out;
1498 in = (void *)(mod_base + l->fileofs);
1499 if (l->filelen % sizeof(*in))
1500 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1501 count = l->filelen / sizeof(*in);
1502 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1504 loadmodel->clipnodes = out;
1505 loadmodel->numclipnodes = count;
1507 if (loadmodel->ishlbsp)
1509 hull = &loadmodel->hulls[1];
1510 hull->clipnodes = out;
1511 hull->firstclipnode = 0;
1512 hull->lastclipnode = count-1;
1513 hull->planes = loadmodel->planes;
1514 hull->clip_mins[0] = -16;
1515 hull->clip_mins[1] = -16;
1516 hull->clip_mins[2] = -36;
1517 hull->clip_maxs[0] = 16;
1518 hull->clip_maxs[1] = 16;
1519 hull->clip_maxs[2] = 36;
1520 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1522 hull = &loadmodel->hulls[2];
1523 hull->clipnodes = out;
1524 hull->firstclipnode = 0;
1525 hull->lastclipnode = count-1;
1526 hull->planes = loadmodel->planes;
1527 hull->clip_mins[0] = -32;
1528 hull->clip_mins[1] = -32;
1529 hull->clip_mins[2] = -32;
1530 hull->clip_maxs[0] = 32;
1531 hull->clip_maxs[1] = 32;
1532 hull->clip_maxs[2] = 32;
1533 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1535 hull = &loadmodel->hulls[3];
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] = -18;
1543 hull->clip_maxs[0] = 16;
1544 hull->clip_maxs[1] = 16;
1545 hull->clip_maxs[2] = 18;
1546 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1550 hull = &loadmodel->hulls[1];
1551 hull->clipnodes = out;
1552 hull->firstclipnode = 0;
1553 hull->lastclipnode = count-1;
1554 hull->planes = loadmodel->planes;
1555 hull->clip_mins[0] = -16;
1556 hull->clip_mins[1] = -16;
1557 hull->clip_mins[2] = -24;
1558 hull->clip_maxs[0] = 16;
1559 hull->clip_maxs[1] = 16;
1560 hull->clip_maxs[2] = 32;
1561 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1563 hull = &loadmodel->hulls[2];
1564 hull->clipnodes = out;
1565 hull->firstclipnode = 0;
1566 hull->lastclipnode = count-1;
1567 hull->planes = loadmodel->planes;
1568 hull->clip_mins[0] = -32;
1569 hull->clip_mins[1] = -32;
1570 hull->clip_mins[2] = -24;
1571 hull->clip_maxs[0] = 32;
1572 hull->clip_maxs[1] = 32;
1573 hull->clip_maxs[2] = 64;
1574 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1577 for (i=0 ; i<count ; i++, out++, in++)
1579 out->planenum = LittleLong(in->planenum);
1580 out->children[0] = LittleShort(in->children[0]);
1581 out->children[1] = LittleShort(in->children[1]);
1582 if (out->children[0] >= count || out->children[1] >= count)
1583 Host_Error("Corrupt clipping hull (out of range child)\n");
1591 Duplicate the drawing hull structure as a clipping hull
1594 static void Mod_MakeHull0 (void)
1601 hull = &loadmodel->hulls[0];
1603 in = loadmodel->nodes;
1604 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1606 hull->clipnodes = out;
1607 hull->firstclipnode = 0;
1608 hull->lastclipnode = loadmodel->numnodes - 1;
1609 hull->planes = loadmodel->planes;
1611 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1613 out->planenum = in->plane - loadmodel->planes;
1614 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1615 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1621 Mod_LoadMarksurfaces
1624 static void Mod_LoadMarksurfaces (lump_t *l)
1629 in = (void *)(mod_base + l->fileofs);
1630 if (l->filelen % sizeof(*in))
1631 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1632 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1633 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1635 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1637 j = (unsigned) LittleShort(in[i]);
1638 if (j >= loadmodel->numsurfaces)
1639 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1640 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1649 static void Mod_LoadSurfedges (lump_t *l)
1654 in = (void *)(mod_base + l->fileofs);
1655 if (l->filelen % sizeof(*in))
1656 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1657 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1658 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1660 for (i = 0;i < loadmodel->numsurfedges;i++)
1661 loadmodel->surfedges[i] = LittleLong (in[i]);
1670 static void Mod_LoadPlanes (lump_t *l)
1676 in = (void *)(mod_base + l->fileofs);
1677 if (l->filelen % sizeof(*in))
1678 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1680 loadmodel->numplanes = l->filelen / sizeof(*in);
1681 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1683 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1685 out->normal[0] = LittleFloat (in->normal[0]);
1686 out->normal[1] = LittleFloat (in->normal[1]);
1687 out->normal[2] = LittleFloat (in->normal[2]);
1688 out->dist = LittleFloat (in->dist);
1694 #define MAX_POINTS_ON_WINDING 64
1700 double points[8][3]; // variable sized
1709 static winding_t *NewWinding (int points)
1714 if (points > MAX_POINTS_ON_WINDING)
1715 Sys_Error("NewWinding: too many points\n");
1717 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1718 w = Mem_Alloc(loadmodel->mempool, size);
1719 memset (w, 0, size);
1724 static void FreeWinding (winding_t *w)
1734 static winding_t *BaseWindingForPlane (mplane_t *p)
1736 double org[3], vright[3], vup[3], normal[3];
1739 VectorCopy(p->normal, normal);
1740 VectorVectorsDouble(normal, vright, vup);
1742 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1743 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1745 // project a really big axis aligned box onto the plane
1748 VectorScale (p->normal, p->dist, org);
1750 VectorSubtract (org, vright, w->points[0]);
1751 VectorAdd (w->points[0], vup, w->points[0]);
1753 VectorAdd (org, vright, w->points[1]);
1754 VectorAdd (w->points[1], vup, w->points[1]);
1756 VectorAdd (org, vright, w->points[2]);
1757 VectorSubtract (w->points[2], vup, w->points[2]);
1759 VectorSubtract (org, vright, w->points[3]);
1760 VectorSubtract (w->points[3], vup, w->points[3]);
1771 Clips the winding to the plane, returning the new winding on the positive side
1772 Frees the input winding.
1773 If keepon is true, an exactly on-plane winding will be saved, otherwise
1774 it will be clipped away.
1777 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1779 double dists[MAX_POINTS_ON_WINDING + 1];
1780 int sides[MAX_POINTS_ON_WINDING + 1];
1789 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1791 // determine sides for each point
1792 for (i = 0;i < in->numpoints;i++)
1794 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1795 if (dot > ON_EPSILON)
1796 sides[i] = SIDE_FRONT;
1797 else if (dot < -ON_EPSILON)
1798 sides[i] = SIDE_BACK;
1803 sides[i] = sides[0];
1804 dists[i] = dists[0];
1806 if (keepon && !counts[0] && !counts[1])
1817 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1818 if (maxpts > MAX_POINTS_ON_WINDING)
1819 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1821 neww = NewWinding (maxpts);
1823 for (i = 0;i < in->numpoints;i++)
1825 if (neww->numpoints >= maxpts)
1826 Sys_Error ("ClipWinding: points exceeded estimate");
1830 if (sides[i] == SIDE_ON)
1832 VectorCopy (p1, neww->points[neww->numpoints]);
1837 if (sides[i] == SIDE_FRONT)
1839 VectorCopy (p1, neww->points[neww->numpoints]);
1843 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1846 // generate a split point
1847 p2 = in->points[(i+1)%in->numpoints];
1849 dot = dists[i] / (dists[i]-dists[i+1]);
1850 for (j = 0;j < 3;j++)
1851 { // avoid round off error when possible
1852 if (split->normal[j] == 1)
1853 mid[j] = split->dist;
1854 else if (split->normal[j] == -1)
1855 mid[j] = -split->dist;
1857 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1860 VectorCopy (mid, neww->points[neww->numpoints]);
1864 // free the original winding
1875 Divides a winding by a plane, producing one or two windings. The
1876 original winding is not damaged or freed. If only on one side, the
1877 returned winding will be the input winding. If on both sides, two
1878 new windings will be created.
1881 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1883 double dists[MAX_POINTS_ON_WINDING + 1];
1884 int sides[MAX_POINTS_ON_WINDING + 1];
1893 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1895 // determine sides for each point
1896 for (i = 0;i < in->numpoints;i++)
1898 dot = DotProduct (in->points[i], split->normal);
1901 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1902 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1903 else sides[i] = SIDE_ON;
1906 sides[i] = sides[0];
1907 dists[i] = dists[0];
1909 *front = *back = NULL;
1922 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1924 if (maxpts > MAX_POINTS_ON_WINDING)
1925 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1927 *front = f = NewWinding (maxpts);
1928 *back = b = NewWinding (maxpts);
1930 for (i = 0;i < in->numpoints;i++)
1932 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1933 Sys_Error ("DivideWinding: points exceeded estimate");
1937 if (sides[i] == SIDE_ON)
1939 VectorCopy (p1, f->points[f->numpoints]);
1941 VectorCopy (p1, b->points[b->numpoints]);
1946 if (sides[i] == SIDE_FRONT)
1948 VectorCopy (p1, f->points[f->numpoints]);
1951 else if (sides[i] == SIDE_BACK)
1953 VectorCopy (p1, b->points[b->numpoints]);
1957 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1960 // generate a split point
1961 p2 = in->points[(i+1)%in->numpoints];
1963 dot = dists[i] / (dists[i]-dists[i+1]);
1964 for (j = 0;j < 3;j++)
1965 { // avoid round off error when possible
1966 if (split->normal[j] == 1)
1967 mid[j] = split->dist;
1968 else if (split->normal[j] == -1)
1969 mid[j] = -split->dist;
1971 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1974 VectorCopy (mid, f->points[f->numpoints]);
1976 VectorCopy (mid, b->points[b->numpoints]);
1981 typedef struct portal_s
1984 mnode_t *nodes[2]; // [0] = front side of plane
1985 struct portal_s *next[2];
1987 struct portal_s *chain; // all portals are linked into a list
1991 static portal_t *portalchain;
1998 static portal_t *AllocPortal (void)
2001 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2002 p->chain = portalchain;
2007 static void FreePortal(portal_t *p)
2012 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2014 // calculate children first
2015 if (node->children[0]->contents >= 0)
2016 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2017 if (node->children[1]->contents >= 0)
2018 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2020 // make combined bounding box from children
2021 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2022 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2023 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2024 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2025 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2026 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2029 static void Mod_FinalizePortals(void)
2031 int i, j, numportals, numpoints;
2032 portal_t *p, *pnext;
2035 mleaf_t *leaf, *endleaf;
2038 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2039 leaf = loadmodel->leafs;
2040 endleaf = leaf + loadmodel->numleafs;
2041 for (;leaf < endleaf;leaf++)
2043 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2044 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2051 for (i = 0;i < 2;i++)
2053 leaf = (mleaf_t *)p->nodes[i];
2055 for (j = 0;j < w->numpoints;j++)
2057 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2058 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2059 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2060 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2061 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2062 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2069 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2071 // tally up portal and point counts
2077 // note: this check must match the one below or it will usually corrupt memory
2078 // 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
2079 if (p->winding && p->nodes[0] != p->nodes[1]
2080 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2081 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2084 numpoints += p->winding->numpoints * 2;
2088 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2089 loadmodel->numportals = numportals;
2090 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2091 loadmodel->numportalpoints = numpoints;
2092 // clear all leaf portal chains
2093 for (i = 0;i < loadmodel->numleafs;i++)
2094 loadmodel->leafs[i].portals = NULL;
2095 // process all portals in the global portal chain, while freeing them
2096 portal = loadmodel->portals;
2097 point = loadmodel->portalpoints;
2106 // note: this check must match the one above or it will usually corrupt memory
2107 // 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
2108 if (p->nodes[0] != p->nodes[1]
2109 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2110 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2112 // first make the back to front portal (forward portal)
2113 portal->points = point;
2114 portal->numpoints = p->winding->numpoints;
2115 portal->plane.dist = p->plane.dist;
2116 VectorCopy(p->plane.normal, portal->plane.normal);
2117 portal->here = (mleaf_t *)p->nodes[1];
2118 portal->past = (mleaf_t *)p->nodes[0];
2120 for (j = 0;j < portal->numpoints;j++)
2122 VectorCopy(p->winding->points[j], point->position);
2125 PlaneClassify(&portal->plane);
2127 // link into leaf's portal chain
2128 portal->next = portal->here->portals;
2129 portal->here->portals = portal;
2131 // advance to next portal
2134 // then make the front to back portal (backward portal)
2135 portal->points = point;
2136 portal->numpoints = p->winding->numpoints;
2137 portal->plane.dist = -p->plane.dist;
2138 VectorNegate(p->plane.normal, portal->plane.normal);
2139 portal->here = (mleaf_t *)p->nodes[0];
2140 portal->past = (mleaf_t *)p->nodes[1];
2142 for (j = portal->numpoints - 1;j >= 0;j--)
2144 VectorCopy(p->winding->points[j], point->position);
2147 PlaneClassify(&portal->plane);
2149 // link into leaf's portal chain
2150 portal->next = portal->here->portals;
2151 portal->here->portals = portal;
2153 // advance to next portal
2156 FreeWinding(p->winding);
2168 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2171 Host_Error ("AddPortalToNodes: NULL front node");
2173 Host_Error ("AddPortalToNodes: NULL back node");
2174 if (p->nodes[0] || p->nodes[1])
2175 Host_Error ("AddPortalToNodes: already included");
2176 // 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
2178 p->nodes[0] = front;
2179 p->next[0] = (portal_t *)front->portals;
2180 front->portals = (mportal_t *)p;
2183 p->next[1] = (portal_t *)back->portals;
2184 back->portals = (mportal_t *)p;
2189 RemovePortalFromNode
2192 static void RemovePortalFromNodes(portal_t *portal)
2196 void **portalpointer;
2198 for (i = 0;i < 2;i++)
2200 node = portal->nodes[i];
2202 portalpointer = (void **) &node->portals;
2207 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2211 if (portal->nodes[0] == node)
2213 *portalpointer = portal->next[0];
2214 portal->nodes[0] = NULL;
2216 else if (portal->nodes[1] == node)
2218 *portalpointer = portal->next[1];
2219 portal->nodes[1] = NULL;
2222 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2226 if (t->nodes[0] == node)
2227 portalpointer = (void **) &t->next[0];
2228 else if (t->nodes[1] == node)
2229 portalpointer = (void **) &t->next[1];
2231 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2236 static void Mod_RecursiveNodePortals (mnode_t *node)
2239 mnode_t *front, *back, *other_node;
2240 mplane_t clipplane, *plane;
2241 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2242 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2244 // if a leaf, we're done
2248 plane = node->plane;
2250 front = node->children[0];
2251 back = node->children[1];
2253 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2255 // create the new portal by generating a polygon for the node plane,
2256 // and clipping it by all of the other portals (which came from nodes above this one)
2257 nodeportal = AllocPortal ();
2258 nodeportal->plane = *node->plane;
2260 nodeportalwinding = BaseWindingForPlane (node->plane);
2261 side = 0; // shut up compiler warning
2262 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2264 clipplane = portal->plane;
2265 if (portal->nodes[0] == portal->nodes[1])
2266 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2267 if (portal->nodes[0] == node)
2269 else if (portal->nodes[1] == node)
2271 clipplane.dist = -clipplane.dist;
2272 VectorNegate (clipplane.normal, clipplane.normal);
2276 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2278 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2279 if (!nodeportalwinding)
2281 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2286 if (nodeportalwinding)
2288 // if the plane was not clipped on all sides, there was an error
2289 nodeportal->winding = nodeportalwinding;
2290 AddPortalToNodes (nodeportal, front, back);
2293 // split the portals of this node along this node's plane and assign them to the children of this node
2294 // (migrating the portals downward through the tree)
2295 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2297 if (portal->nodes[0] == portal->nodes[1])
2298 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2299 if (portal->nodes[0] == node)
2301 else if (portal->nodes[1] == node)
2304 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2305 nextportal = portal->next[side];
2307 other_node = portal->nodes[!side];
2308 RemovePortalFromNodes (portal);
2310 // cut the portal into two portals, one on each side of the node plane
2311 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2316 AddPortalToNodes (portal, back, other_node);
2318 AddPortalToNodes (portal, other_node, back);
2324 AddPortalToNodes (portal, front, other_node);
2326 AddPortalToNodes (portal, other_node, front);
2330 // the winding is split
2331 splitportal = AllocPortal ();
2332 temp = splitportal->chain;
2333 *splitportal = *portal;
2334 splitportal->chain = temp;
2335 splitportal->winding = backwinding;
2336 FreeWinding (portal->winding);
2337 portal->winding = frontwinding;
2341 AddPortalToNodes (portal, front, other_node);
2342 AddPortalToNodes (splitportal, back, other_node);
2346 AddPortalToNodes (portal, other_node, front);
2347 AddPortalToNodes (splitportal, other_node, back);
2351 Mod_RecursiveNodePortals(front);
2352 Mod_RecursiveNodePortals(back);
2356 static void Mod_MakePortals(void)
2359 Mod_RecursiveNodePortals (loadmodel->nodes);
2360 Mod_FinalizePortals();
2368 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2373 mempool_t *mainmempool;
2376 mod->type = mod_brush;
2378 header = (dheader_t *)buffer;
2380 i = LittleLong (header->version);
2381 if (i != BSPVERSION && i != 30)
2382 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2383 mod->ishlbsp = i == 30;
2384 if (loadmodel->isworldmodel)
2385 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2387 // swap all the lumps
2388 mod_base = (qbyte *)header;
2390 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2391 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2395 // store which lightmap format to use
2396 mod->lightmaprgba = r_lightmaprgba.integer;
2398 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2399 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2400 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2401 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2402 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2403 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2404 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2405 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2406 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2407 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2408 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2409 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2410 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2411 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2412 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2417 mod->numframes = 2; // regular and alternate animation
2419 mainmempool = mod->mempool;
2420 loadname = mod->name;
2422 Mod_LoadLightList ();
2425 // set up the submodels (FIXME: this is confusing)
2427 for (i = 0;i < mod->numsubmodels;i++)
2430 float dist, modelyawradius, modelradius, *vec;
2433 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2434 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2438 bm = &mod->submodels[i];
2440 mod->hulls[0].firstclipnode = bm->headnode[0];
2441 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2443 mod->hulls[j].firstclipnode = bm->headnode[j];
2444 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2447 mod->firstmodelsurface = bm->firstface;
2448 mod->nummodelsurfaces = bm->numfaces;
2450 mod->DrawSky = NULL;
2451 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2452 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2454 // we only need to have a drawsky function if it is used (usually only on world model)
2455 if (surf->shader == &Cshader_sky)
2456 mod->DrawSky = R_DrawBrushModelSky;
2457 for (k = 0;k < surf->numedges;k++)
2459 l = mod->surfedges[k + surf->firstedge];
2461 vec = mod->vertexes[mod->edges[l].v[0]].position;
2463 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2464 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2465 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2466 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2467 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2468 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2469 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2470 dist = vec[0]*vec[0]+vec[1]*vec[1];
2471 if (modelyawradius < dist)
2472 modelyawradius = dist;
2473 dist += vec[2]*vec[2];
2474 if (modelradius < dist)
2478 modelyawradius = sqrt(modelyawradius);
2479 modelradius = sqrt(modelradius);
2480 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2481 mod->yawmins[2] = mod->normalmins[2];
2482 mod->yawmaxs[2] = mod->normalmaxs[2];
2483 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2484 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2485 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2486 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2488 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2489 VectorClear(mod->normalmins);
2490 VectorClear(mod->normalmaxs);
2491 VectorClear(mod->yawmins);
2492 VectorClear(mod->yawmaxs);
2493 VectorClear(mod->rotatedmins);
2494 VectorClear(mod->rotatedmaxs);
2497 mod->numleafs = bm->visleafs;
2499 mod->SERAddEntity = Mod_Brush_SERAddEntity;
2500 mod->Draw = R_DrawBrushModelNormal;
2501 mod->DrawShadow = NULL;
2503 Mod_BrushSortedSurfaces(mod, mainmempool);
2505 // LordHavoc: only register submodels if it is the world
2506 // (prevents bsp models from replacing world submodels)
2507 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2510 // duplicate the basic information
2511 sprintf (name, "*%i", i+1);
2512 loadmodel = Mod_FindName (name);
2514 strcpy (loadmodel->name, name);
2515 // textures and memory belong to the main model
2516 loadmodel->texturepool = NULL;
2517 loadmodel->mempool = NULL;