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_nosurftextures = {0, "r_nosurftextures", "0"};
33 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
35 #define NUM_DETAILTEXTURES 1
36 static rtexture_t *detailtextures[NUM_DETAILTEXTURES];
37 static rtexturepool_t *detailtexturepool;
44 void Mod_BrushInit (void)
46 // Cvar_RegisterVariable(&r_subdivide_size);
47 Cvar_RegisterVariable(&halflifebsp);
48 Cvar_RegisterVariable(&r_novis);
49 Cvar_RegisterVariable(&r_miplightmaps);
50 Cvar_RegisterVariable(&r_lightmaprgba);
51 Cvar_RegisterVariable(&r_nosurftextures);
52 Cvar_RegisterVariable(&r_sortsurfaces);
53 memset(mod_novis, 0xff, sizeof(mod_novis));
56 void Mod_BrushStartup (void)
59 float vc[3], vx[3], vy[3], vn[3], lightdir[3];
60 #define DETAILRESOLUTION 256
61 qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
62 detailtexturepool = R_AllocTexturePool();
66 VectorNormalize(lightdir);
67 for (i = 0;i < NUM_DETAILTEXTURES;i++)
69 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
70 for (y = 0;y < DETAILRESOLUTION;y++)
72 for (x = 0;x < DETAILRESOLUTION;x++)
76 vc[2] = noise[y][x] * (1.0f / 32.0f);
79 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
82 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
83 VectorSubtract(vx, vc, vx);
84 VectorSubtract(vy, vc, vy);
85 CrossProduct(vx, vy, vn);
87 light = 128 - DotProduct(vn, lightdir) * 128;
88 light = bound(0, light, 255);
89 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
93 detailtextures[i] = R_LoadTexture(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE);
97 void Mod_BrushShutdown (void)
100 for (i = 0;i < NUM_DETAILTEXTURES;i++)
101 R_FreeTexture(detailtextures[i]);
102 R_FreeTexturePool(&detailtexturepool);
110 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model)
117 Mod_CheckLoaded(model);
119 // LordHavoc: modified to start at first clip node,
120 // in other words: first node of the (sub)model
121 node = model->nodes + model->hulls[0].firstclipnode;
122 while (node->contents == 0)
123 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
125 return (mleaf_t *)node;
128 int Mod_PointContents (const vec3_t p, model_t *model)
133 return CONTENTS_EMPTY;
135 Mod_CheckLoaded(model);
137 // LordHavoc: modified to start at first clip node,
138 // in other words: first node of the (sub)model
139 node = model->nodes + model->hulls[0].firstclipnode;
140 while (node->contents == 0)
141 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
143 return ((mleaf_t *)node)->contents;
146 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
148 if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
149 pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
150 pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
152 pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
153 pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
155 pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
156 pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
166 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
168 static qbyte decompressed[MAX_MAP_LEAFS/8];
173 row = (model->numleafs+7)>>3;
191 } while (out - decompressed < row);
196 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
198 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
200 return Mod_DecompressVis (leaf->compressed_vis, model);
208 static void Mod_LoadTextures (lump_t *l)
210 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
212 texture_t *tx, *tx2, *anims[10], *altanims[10];
214 qbyte *data, *mtdata, *data2;
217 loadmodel->textures = NULL;
222 m = (dmiptexlump_t *)(mod_base + l->fileofs);
224 m->nummiptex = LittleLong (m->nummiptex);
226 // add two slots for notexture walls and notexture liquids
227 loadmodel->numtextures = m->nummiptex + 2;
228 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(texture_t));
230 // fill out all slots with notexture
231 for (i = 0, tx = loadmodel->textures;i < loadmodel->numtextures;i++, tx++)
235 tx->texture = r_notexture;
236 tx->shader = &Cshader_wall_lightmap;
237 if (i == loadmodel->numtextures - 1)
239 tx->flags = SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
240 tx->shader = &Cshader_water;
244 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
246 // LordHavoc: mostly rewritten map texture loader
247 for (i = 0;i < m->nummiptex;i++)
249 dofs[i] = LittleLong(dofs[i]);
250 if (dofs[i] == -1 || r_nosurftextures.integer)
252 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
254 // make sure name is no more than 15 characters
255 for (j = 0;dmiptex->name[j] && j < 15;j++)
256 name[j] = dmiptex->name[j];
259 mtwidth = LittleLong (dmiptex->width);
260 mtheight = LittleLong (dmiptex->height);
262 j = LittleLong (dmiptex->offsets[0]);
266 if (j < 40 || j + mtwidth * mtheight > l->filelen)
268 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
271 mtdata = (qbyte *)dmiptex + j;
274 if ((mtwidth & 15) || (mtheight & 15))
275 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
277 // LordHavoc: force all names to lowercase
278 for (j = 0;name[j];j++)
279 if (name[j] >= 'A' && name[j] <= 'Z')
280 name[j] += 'a' - 'A';
282 tx = loadmodel->textures + i;
283 strcpy(tx->name, name);
285 tx->height = mtheight;
289 sprintf(tx->name, "unnamed%i", i);
290 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
293 // LordHavoc: HL sky textures are entirely different than quake
294 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
296 if (loadmodel->isworldmodel)
298 data = loadimagepixels(tx->name, false, 0, 0);
301 if (image_width == 256 && image_height == 128)
309 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
311 R_InitSky (mtdata, 1);
314 else if (mtdata != NULL)
315 R_InitSky (mtdata, 1);
318 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
320 tx->fogtexture = image_masktex;
321 strcpy(name, tx->name);
322 strcat(name, "_glow");
323 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
327 if (loadmodel->ishlbsp)
329 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
332 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
333 if (R_TextureHasAlpha(tx->texture))
336 for (j = 0;j < image_width * image_height;j++)
337 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
338 strcpy(name, tx->name);
339 strcat(name, "_fog");
340 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
344 else if ((data = W_GetTexture(tx->name)))
346 // get the size from the wad texture
347 tx->width = image_width;
348 tx->height = image_height;
349 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
350 if (R_TextureHasAlpha(tx->texture))
353 for (j = 0;j < image_width * image_height;j++)
354 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
355 strcpy(name, tx->name);
356 strcat(name, "_fog");
357 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
365 tx->texture = r_notexture;
370 if (mtdata) // texture included
375 if (r_fullbrights.value && tx->name[0] != '*')
377 for (j = 0;j < tx->width*tx->height;j++)
379 if (data[j] >= 224) // fullbright
388 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
389 for (j = 0;j < tx->width*tx->height;j++)
390 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
391 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
392 strcpy(name, tx->name);
393 strcat(name, "_glow");
394 for (j = 0;j < tx->width*tx->height;j++)
395 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
396 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
400 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
402 else // no texture, and no external replacement texture was found
406 tx->texture = r_notexture;
411 if (tx->name[0] == '*')
413 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
414 // LordHavoc: some turbulent textures should be fullbright and solid
415 if (!strncmp(tx->name,"*lava",5)
416 || !strncmp(tx->name,"*teleport",9)
417 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
418 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
419 tx->shader = &Cshader_water;
421 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
423 tx->flags |= SURF_DRAWSKY;
424 tx->shader = &Cshader_sky;
428 tx->flags |= SURF_LIGHTMAP;
429 tx->shader = &Cshader_wall_lightmap;
432 tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
435 // sequence the animations
436 for (i = 0;i < m->nummiptex;i++)
438 tx = loadmodel->textures + i;
439 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
441 if (tx->anim_total[0] || tx->anim_total[1])
442 continue; // already sequenced
444 // find the number of frames in the animation
445 memset (anims, 0, sizeof(anims));
446 memset (altanims, 0, sizeof(altanims));
448 for (j = i;j < m->nummiptex;j++)
450 tx2 = loadmodel->textures + j;
451 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
455 if (num >= '0' && num <= '9')
456 anims[num - '0'] = tx2;
457 else if (num >= 'a' && num <= 'j')
458 altanims[num - 'a'] = tx2;
460 Con_Printf ("Bad animating texture %s\n", tx->name);
464 for (j = 0;j < 10;j++)
471 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
474 for (j = 0;j < max;j++)
478 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
482 for (j = 0;j < altmax;j++)
486 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
495 // if there is no alternate animation, duplicate the primary
496 // animation into the alternate
498 for (k = 0;k < 10;k++)
499 altanims[k] = anims[k];
502 // link together the primary animation
503 for (j = 0;j < max;j++)
506 tx2->animated = true;
507 tx2->anim_total[0] = max;
508 tx2->anim_total[1] = altmax;
509 for (k = 0;k < 10;k++)
511 tx2->anim_frames[0][k] = anims[k];
512 tx2->anim_frames[1][k] = altanims[k];
516 // if there really is an alternate anim...
517 if (anims[0] != altanims[0])
519 // link together the alternate animation
520 for (j = 0;j < altmax;j++)
523 tx2->animated = true;
524 // the primary/alternate are reversed here
525 tx2->anim_total[0] = altmax;
526 tx2->anim_total[1] = max;
527 for (k = 0;k < 10;k++)
529 tx2->anim_frames[0][k] = altanims[k];
530 tx2->anim_frames[1][k] = anims[k];
542 static void Mod_LoadLighting (lump_t *l)
545 qbyte *in, *out, *data, d;
546 char litfilename[1024];
547 loadmodel->lightdata = NULL;
548 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
550 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
551 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
553 else // LordHavoc: bsp version 29 (normal white lighting)
555 // LordHavoc: hope is not lost yet, check for a .lit file to load
556 strcpy(litfilename, loadmodel->name);
557 COM_StripExtension(litfilename, litfilename);
558 strcat(litfilename, ".lit");
559 data = (qbyte*) COM_LoadFile (litfilename, false);
562 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
564 i = LittleLong(((int *)data)[1]);
567 Con_DPrintf("%s loaded", litfilename);
568 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
569 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
575 Con_Printf("Unknown .lit file version (%d)\n", i);
582 Con_Printf("Empty .lit file, ignoring\n");
584 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
588 // LordHavoc: oh well, expand the white lighting data
591 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
592 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
593 out = loadmodel->lightdata;
594 memcpy (in, mod_base + l->fileofs, l->filelen);
595 for (i = 0;i < l->filelen;i++)
605 void Mod_LoadLightList(void)
608 char lightsfilename[1024], *s, *t, *lightsstring;
611 strcpy(lightsfilename, loadmodel->name);
612 COM_StripExtension(lightsfilename, lightsfilename);
613 strcat(lightsfilename, ".lights");
614 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
620 while (*s && *s != '\n')
624 Mem_Free(lightsstring);
625 Host_Error("lights file must end with a newline\n");
630 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
633 while (*s && n < numlights)
636 while (*s && *s != '\n')
640 Mem_Free(lightsstring);
641 Host_Error("misparsed lights file!\n");
643 e = loadmodel->lights + n;
645 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);
649 Mem_Free(lightsstring);
650 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
657 Mem_Free(lightsstring);
658 Host_Error("misparsed lights file!\n");
660 loadmodel->numlights = numlights;
661 Mem_Free(lightsstring);
671 static void Mod_LoadVisibility (lump_t *l)
673 loadmodel->visdata = NULL;
676 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
677 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
680 // used only for HalfLife maps
681 void Mod_ParseWadsFromEntityLump(const char *data)
683 char key[128], value[4096];
688 if (!COM_ParseToken(&data))
690 if (com_token[0] != '{')
694 if (!COM_ParseToken(&data))
696 if (com_token[0] == '}')
697 break; // end of worldspawn
698 if (com_token[0] == '_')
699 strcpy(key, com_token + 1);
701 strcpy(key, com_token);
702 while (key[strlen(key)-1] == ' ') // remove trailing spaces
703 key[strlen(key)-1] = 0;
704 if (!COM_ParseToken(&data))
706 strcpy(value, com_token);
707 if (!strcmp("wad", key)) // for HalfLife maps
709 if (loadmodel->ishlbsp)
712 for (i = 0;i < 4096;i++)
713 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
719 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
720 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
722 else if (value[i] == ';' || value[i] == 0)
726 strcpy(wadname, "textures/");
727 strcat(wadname, &value[j]);
728 W_LoadTextureWadFile (wadname, false);
745 static void Mod_LoadEntities (lump_t *l)
747 loadmodel->entities = NULL;
750 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
751 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
752 if (loadmodel->ishlbsp)
753 Mod_ParseWadsFromEntityLump(loadmodel->entities);
762 static void Mod_LoadVertexes (lump_t *l)
768 in = (void *)(mod_base + l->fileofs);
769 if (l->filelen % sizeof(*in))
770 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
771 count = l->filelen / sizeof(*in);
772 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
774 loadmodel->vertexes = out;
775 loadmodel->numvertexes = count;
777 for ( i=0 ; i<count ; i++, in++, out++)
779 out->position[0] = LittleFloat (in->point[0]);
780 out->position[1] = LittleFloat (in->point[1]);
781 out->position[2] = LittleFloat (in->point[2]);
790 static void Mod_LoadSubmodels (lump_t *l)
796 in = (void *)(mod_base + l->fileofs);
797 if (l->filelen % sizeof(*in))
798 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
799 count = l->filelen / sizeof(*in);
800 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
802 loadmodel->submodels = out;
803 loadmodel->numsubmodels = count;
805 for ( i=0 ; i<count ; i++, in++, out++)
807 for (j=0 ; j<3 ; j++)
809 // spread the mins / maxs by a pixel
810 out->mins[j] = LittleFloat (in->mins[j]) - 1;
811 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
812 out->origin[j] = LittleFloat (in->origin[j]);
814 for (j=0 ; j<MAX_MAP_HULLS ; j++)
815 out->headnode[j] = LittleLong (in->headnode[j]);
816 out->visleafs = LittleLong (in->visleafs);
817 out->firstface = LittleLong (in->firstface);
818 out->numfaces = LittleLong (in->numfaces);
827 static void Mod_LoadEdges (lump_t *l)
833 in = (void *)(mod_base + l->fileofs);
834 if (l->filelen % sizeof(*in))
835 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
836 count = l->filelen / sizeof(*in);
837 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
839 loadmodel->edges = out;
840 loadmodel->numedges = count;
842 for ( i=0 ; i<count ; i++, in++, out++)
844 out->v[0] = (unsigned short)LittleShort(in->v[0]);
845 out->v[1] = (unsigned short)LittleShort(in->v[1]);
854 static void Mod_LoadTexinfo (lump_t *l)
858 int i, j, k, count, miptex;
860 in = (void *)(mod_base + l->fileofs);
861 if (l->filelen % sizeof(*in))
862 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
863 count = l->filelen / sizeof(*in);
864 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
866 loadmodel->texinfo = out;
867 loadmodel->numtexinfo = count;
869 for (i = 0;i < count;i++, in++, out++)
871 for (k = 0;k < 2;k++)
872 for (j = 0;j < 4;j++)
873 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
875 miptex = LittleLong (in->miptex);
876 out->flags = LittleLong (in->flags);
879 if (loadmodel->textures)
881 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
882 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
884 out->texture = loadmodel->textures + miptex;
886 if (out->flags & TEX_SPECIAL)
888 // if texture chosen is NULL or the shader needs a lightmap,
889 // force to notexture water shader
890 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
891 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
895 // if texture chosen is NULL, force to notexture
896 if (out->texture == NULL)
897 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
906 Fills in s->texturemins[] and s->extents[]
909 static void CalcSurfaceExtents (msurface_t *s)
911 float mins[2], maxs[2], val;
915 int bmins[2], bmaxs[2];
917 mins[0] = mins[1] = 999999999;
918 maxs[0] = maxs[1] = -999999999;
922 for (i=0 ; i<s->numedges ; i++)
924 e = loadmodel->surfedges[s->firstedge+i];
926 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
928 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
930 for (j=0 ; j<2 ; j++)
932 val = v->position[0] * tex->vecs[j][0] +
933 v->position[1] * tex->vecs[j][1] +
934 v->position[2] * tex->vecs[j][2] +
943 for (i=0 ; i<2 ; i++)
945 bmins[i] = floor(mins[i]/16);
946 bmaxs[i] = ceil(maxs[i]/16);
948 s->texturemins[i] = bmins[i] * 16;
949 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
954 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
959 mins[0] = mins[1] = mins[2] = 9999;
960 maxs[0] = maxs[1] = maxs[2] = -9999;
962 for (i = 0;i < numverts;i++)
964 for (j = 0;j < 3;j++, v++)
975 #define MAX_SUBDIVPOLYTRIANGLES 4096
976 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
978 static int subdivpolyverts, subdivpolytriangles;
979 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
980 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
982 static int subdivpolylookupvert(vec3_t v)
985 for (i = 0;i < subdivpolyverts;i++)
986 if (subdivpolyvert[i][0] == v[0]
987 && subdivpolyvert[i][1] == v[1]
988 && subdivpolyvert[i][2] == v[2])
990 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
991 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
992 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
993 return subdivpolyverts++;
996 static void SubdividePolygon (int numverts, float *verts)
998 int i, i1, i2, i3, f, b, c, p;
999 vec3_t mins, maxs, front[256], back[256];
1000 float m, *pv, *cv, dist[256], frac;
1003 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1005 BoundPoly (numverts, verts, mins, maxs);
1007 for (i = 0;i < 3;i++)
1009 m = (mins[i] + maxs[i]) * 0.5;
1010 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1011 if (maxs[i] - m < 8)
1013 if (m - mins[i] < 8)
1017 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1018 dist[c] = cv[i] - m;
1021 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1025 VectorCopy (pv, front[f]);
1030 VectorCopy (pv, back[b]);
1033 if (dist[p] == 0 || dist[c] == 0)
1035 if ( (dist[p] > 0) != (dist[c] > 0) )
1038 frac = dist[p] / (dist[p] - dist[c]);
1039 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1040 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1041 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1047 SubdividePolygon (f, front[0]);
1048 SubdividePolygon (b, back[0]);
1052 i1 = subdivpolylookupvert(verts);
1053 i2 = subdivpolylookupvert(verts + 3);
1054 for (i = 2;i < numverts;i++)
1056 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1058 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1062 i3 = subdivpolylookupvert(verts + i * 3);
1063 subdivpolyindex[subdivpolytriangles][0] = i1;
1064 subdivpolyindex[subdivpolytriangles][1] = i2;
1065 subdivpolyindex[subdivpolytriangles][2] = i3;
1067 subdivpolytriangles++;
1073 Mod_GenerateWarpMesh
1075 Breaks a polygon up along axial 64 unit
1076 boundaries so that turbulent and sky warps
1077 can be done reasonably.
1080 void Mod_GenerateWarpMesh (msurface_t *surf)
1086 subdivpolytriangles = 0;
1087 subdivpolyverts = 0;
1088 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1089 if (subdivpolytriangles < 1)
1090 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1092 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1093 mesh->numverts = subdivpolyverts;
1094 mesh->numtriangles = subdivpolytriangles;
1095 mesh->vertex = (surfvertex_t *)(mesh + 1);
1096 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1097 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1099 for (i = 0;i < mesh->numtriangles;i++)
1100 for (j = 0;j < 3;j++)
1101 mesh->index[i*3+j] = subdivpolyindex[i][j];
1103 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1105 VectorCopy(subdivpolyvert[i], v->v);
1106 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1107 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1112 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1114 int i, iu, iv, *index, smax, tmax;
1115 float *in, s, t, u, v, ubase, vbase, uscale, vscale;
1118 smax = surf->extents[0] >> 4;
1119 tmax = surf->extents[1] >> 4;
1123 surf->lightmaptexturestride = 0;
1124 surf->lightmaptexture = NULL;
1132 surf->flags |= SURF_LIGHTMAP;
1133 if (r_miplightmaps.integer)
1135 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1136 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_PRECACHE);
1140 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1141 surf->lightmaptexture = R_LoadTexture(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_PRECACHE);
1143 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1144 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1145 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1148 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * (4 + 2 + 2 + 2 + 1) * sizeof(float));
1149 mesh->numverts = surf->poly_numverts;
1150 mesh->numtriangles = surf->poly_numverts - 2;
1151 mesh->verts = (float *)(mesh + 1);
1152 mesh->st = mesh->verts + mesh->numverts * 4;
1153 mesh->uv = mesh->st + mesh->numverts * 2;
1154 mesh->ab = mesh->uv + mesh->numverts * 2;
1155 mesh->lightmapoffsets = (int *)(mesh->ab + mesh->numverts * 2);
1156 mesh->index = mesh->lightmapoffsets + mesh->numverts;
1158 index = mesh->index;
1159 for (i = 0;i < mesh->numtriangles;i++)
1166 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1168 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1169 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1170 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1171 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1172 // LordHavoc: calc lightmap data offset for vertex lighting to use
1175 iu = bound(0, iu, smax);
1176 iv = bound(0, iv, tmax);
1177 u = u * uscale + ubase;
1178 v = v * vscale + vbase;
1180 mesh->verts[i * 4 + 0] = in[0];
1181 mesh->verts[i * 4 + 1] = in[1];
1182 mesh->verts[i * 4 + 2] = in[2];
1183 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1184 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1185 mesh->uv[i * 2 + 0] = u;
1186 mesh->uv[i * 2 + 1] = v;
1187 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1188 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1189 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1193 void Mod_GenerateVertexMesh (msurface_t *surf)
1199 surf->lightmaptexturestride = 0;
1200 surf->lightmaptexture = NULL;
1202 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * (4 + 2 + 2) * sizeof(float));
1203 mesh->numverts = surf->poly_numverts;
1204 mesh->numtriangles = surf->poly_numverts - 2;
1205 mesh->verts = (float *)(mesh + 1);
1206 mesh->st = mesh->verts + mesh->numverts * 4;
1207 mesh->ab = mesh->st + mesh->numverts * 2;
1208 mesh->index = (int *)(mesh->ab + mesh->numverts * 2);
1210 index = mesh->index;
1211 for (i = 0;i < mesh->numtriangles;i++)
1218 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1220 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1221 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1222 mesh->verts[i * 4 + 0] = in[0];
1223 mesh->verts[i * 4 + 1] = in[1];
1224 mesh->verts[i * 4 + 2] = in[2];
1225 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1226 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1227 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1228 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1232 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1235 float *vec, *vert, mins[3], maxs[3];
1237 // convert edges back to a normal polygon
1238 surf->poly_numverts = surf->numedges;
1239 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1240 for (i = 0;i < surf->numedges;i++)
1242 lindex = loadmodel->surfedges[surf->firstedge + i];
1244 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1246 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1247 VectorCopy (vec, vert);
1250 vert = surf->poly_verts;
1251 VectorCopy(vert, mins);
1252 VectorCopy(vert, maxs);
1254 for (i = 1;i < surf->poly_numverts;i++)
1256 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1257 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1258 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1261 VectorCopy(mins, surf->poly_mins);
1262 VectorCopy(maxs, surf->poly_maxs);
1263 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1264 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1265 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1273 static void Mod_LoadFaces (lump_t *l)
1277 int i, count, surfnum, planenum, ssize, tsize;
1279 in = (void *)(mod_base + l->fileofs);
1280 if (l->filelen % sizeof(*in))
1281 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1282 count = l->filelen / sizeof(*in);
1283 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1285 loadmodel->surfaces = out;
1286 loadmodel->numsurfaces = count;
1287 loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1288 loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1290 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1292 out->number = surfnum;
1293 // FIXME: validate edges, texinfo, etc?
1294 out->firstedge = LittleLong(in->firstedge);
1295 out->numedges = LittleShort(in->numedges);
1296 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1297 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1299 i = LittleShort (in->texinfo);
1300 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1301 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1302 out->texinfo = loadmodel->texinfo + i;
1303 out->flags = out->texinfo->texture->flags;
1305 planenum = LittleShort(in->planenum);
1306 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1307 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1309 if (LittleShort(in->side))
1310 out->flags |= SURF_PLANEBACK;
1312 out->plane = loadmodel->planes + planenum;
1314 // clear lightmap (filled in later)
1315 out->lightmaptexture = NULL;
1317 // force lightmap upload on first time seeing the surface
1318 out->cached_dlight = true;
1319 out->cached_ambient = -1000;
1320 out->cached_lightscalebit = -1000;
1322 CalcSurfaceExtents (out);
1324 ssize = (out->extents[0] >> 4) + 1;
1325 tsize = (out->extents[1] >> 4) + 1;
1328 for (i = 0;i < MAXLIGHTMAPS;i++)
1329 out->styles[i] = in->styles[i];
1330 i = LittleLong(in->lightofs);
1332 out->samples = NULL;
1333 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1334 out->samples = loadmodel->lightdata + i;
1335 else // LordHavoc: white lighting (bsp version 29)
1336 out->samples = loadmodel->lightdata + (i * 3);
1338 Mod_GenerateSurfacePolygon(out);
1339 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
1341 if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1342 Host_Error ("Bad surface extents");
1343 Mod_GenerateWallMesh (out, false);
1344 // stainmap for permanent marks on walls
1345 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1347 memset(out->stainsamples, 255, ssize * tsize * 3);
1350 Mod_GenerateVertexMesh (out);
1359 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1361 node->parent = parent;
1362 if (node->contents < 0)
1364 Mod_SetParent (node->children[0], node);
1365 Mod_SetParent (node->children[1], node);
1373 static void Mod_LoadNodes (lump_t *l)
1379 in = (void *)(mod_base + l->fileofs);
1380 if (l->filelen % sizeof(*in))
1381 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1382 count = l->filelen / sizeof(*in);
1383 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1385 loadmodel->nodes = out;
1386 loadmodel->numnodes = count;
1388 for ( i=0 ; i<count ; i++, in++, out++)
1390 for (j=0 ; j<3 ; j++)
1392 out->mins[j] = LittleShort (in->mins[j]);
1393 out->maxs[j] = LittleShort (in->maxs[j]);
1396 p = LittleLong(in->planenum);
1397 out->plane = loadmodel->planes + p;
1399 out->firstsurface = LittleShort (in->firstface);
1400 out->numsurfaces = LittleShort (in->numfaces);
1402 for (j=0 ; j<2 ; j++)
1404 p = LittleShort (in->children[j]);
1406 out->children[j] = loadmodel->nodes + p;
1408 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1412 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1420 static void Mod_LoadLeafs (lump_t *l)
1426 in = (void *)(mod_base + l->fileofs);
1427 if (l->filelen % sizeof(*in))
1428 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1429 count = l->filelen / sizeof(*in);
1430 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1432 loadmodel->leafs = out;
1433 loadmodel->numleafs = count;
1435 for ( i=0 ; i<count ; i++, in++, out++)
1437 for (j=0 ; j<3 ; j++)
1439 out->mins[j] = LittleShort (in->mins[j]);
1440 out->maxs[j] = LittleShort (in->maxs[j]);
1443 p = LittleLong(in->contents);
1446 out->firstmarksurface = loadmodel->marksurfaces +
1447 LittleShort(in->firstmarksurface);
1448 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1450 p = LittleLong(in->visofs);
1452 out->compressed_vis = NULL;
1454 out->compressed_vis = loadmodel->visdata + p;
1456 for (j=0 ; j<4 ; j++)
1457 out->ambient_sound_level[j] = in->ambient_level[j];
1459 // FIXME: Insert caustics here
1468 static void Mod_LoadClipnodes (lump_t *l)
1470 dclipnode_t *in, *out;
1474 in = (void *)(mod_base + l->fileofs);
1475 if (l->filelen % sizeof(*in))
1476 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1477 count = l->filelen / sizeof(*in);
1478 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1480 loadmodel->clipnodes = out;
1481 loadmodel->numclipnodes = count;
1483 if (loadmodel->ishlbsp)
1485 hull = &loadmodel->hulls[1];
1486 hull->clipnodes = out;
1487 hull->firstclipnode = 0;
1488 hull->lastclipnode = count-1;
1489 hull->planes = loadmodel->planes;
1490 hull->clip_mins[0] = -16;
1491 hull->clip_mins[1] = -16;
1492 hull->clip_mins[2] = -36;
1493 hull->clip_maxs[0] = 16;
1494 hull->clip_maxs[1] = 16;
1495 hull->clip_maxs[2] = 36;
1496 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1498 hull = &loadmodel->hulls[2];
1499 hull->clipnodes = out;
1500 hull->firstclipnode = 0;
1501 hull->lastclipnode = count-1;
1502 hull->planes = loadmodel->planes;
1503 hull->clip_mins[0] = -32;
1504 hull->clip_mins[1] = -32;
1505 hull->clip_mins[2] = -32;
1506 hull->clip_maxs[0] = 32;
1507 hull->clip_maxs[1] = 32;
1508 hull->clip_maxs[2] = 32;
1509 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1511 hull = &loadmodel->hulls[3];
1512 hull->clipnodes = out;
1513 hull->firstclipnode = 0;
1514 hull->lastclipnode = count-1;
1515 hull->planes = loadmodel->planes;
1516 hull->clip_mins[0] = -16;
1517 hull->clip_mins[1] = -16;
1518 hull->clip_mins[2] = -18;
1519 hull->clip_maxs[0] = 16;
1520 hull->clip_maxs[1] = 16;
1521 hull->clip_maxs[2] = 18;
1522 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1526 hull = &loadmodel->hulls[1];
1527 hull->clipnodes = out;
1528 hull->firstclipnode = 0;
1529 hull->lastclipnode = count-1;
1530 hull->planes = loadmodel->planes;
1531 hull->clip_mins[0] = -16;
1532 hull->clip_mins[1] = -16;
1533 hull->clip_mins[2] = -24;
1534 hull->clip_maxs[0] = 16;
1535 hull->clip_maxs[1] = 16;
1536 hull->clip_maxs[2] = 32;
1537 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1539 hull = &loadmodel->hulls[2];
1540 hull->clipnodes = out;
1541 hull->firstclipnode = 0;
1542 hull->lastclipnode = count-1;
1543 hull->planes = loadmodel->planes;
1544 hull->clip_mins[0] = -32;
1545 hull->clip_mins[1] = -32;
1546 hull->clip_mins[2] = -24;
1547 hull->clip_maxs[0] = 32;
1548 hull->clip_maxs[1] = 32;
1549 hull->clip_maxs[2] = 64;
1550 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1553 for (i=0 ; i<count ; i++, out++, in++)
1555 out->planenum = LittleLong(in->planenum);
1556 out->children[0] = LittleShort(in->children[0]);
1557 out->children[1] = LittleShort(in->children[1]);
1558 if (out->children[0] >= count || out->children[1] >= count)
1559 Host_Error("Corrupt clipping hull (out of range child)\n");
1567 Duplicate the drawing hull structure as a clipping hull
1570 static void Mod_MakeHull0 (void)
1577 hull = &loadmodel->hulls[0];
1579 in = loadmodel->nodes;
1580 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1582 hull->clipnodes = out;
1583 hull->firstclipnode = 0;
1584 hull->lastclipnode = loadmodel->numnodes - 1;
1585 hull->planes = loadmodel->planes;
1587 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1589 out->planenum = in->plane - loadmodel->planes;
1590 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1591 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1597 Mod_LoadMarksurfaces
1600 static void Mod_LoadMarksurfaces (lump_t *l)
1605 in = (void *)(mod_base + l->fileofs);
1606 if (l->filelen % sizeof(*in))
1607 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1608 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1609 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1611 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1613 j = (unsigned) LittleShort(in[i]);
1614 if (j >= loadmodel->numsurfaces)
1615 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1616 loadmodel->marksurfaces[i] = j;
1625 static void Mod_LoadSurfedges (lump_t *l)
1630 in = (void *)(mod_base + l->fileofs);
1631 if (l->filelen % sizeof(*in))
1632 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1633 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1634 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1636 for (i = 0;i < loadmodel->numsurfedges;i++)
1637 loadmodel->surfedges[i] = LittleLong (in[i]);
1646 static void Mod_LoadPlanes (lump_t *l)
1652 in = (void *)(mod_base + l->fileofs);
1653 if (l->filelen % sizeof(*in))
1654 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1656 loadmodel->numplanes = l->filelen / sizeof(*in);
1657 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1659 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1661 out->normal[0] = LittleFloat (in->normal[0]);
1662 out->normal[1] = LittleFloat (in->normal[1]);
1663 out->normal[2] = LittleFloat (in->normal[2]);
1664 out->dist = LittleFloat (in->dist);
1670 #define MAX_POINTS_ON_WINDING 64
1676 double points[8][3]; // variable sized
1685 static winding_t *NewWinding (int points)
1690 if (points > MAX_POINTS_ON_WINDING)
1691 Sys_Error("NewWinding: too many points\n");
1693 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1694 w = Mem_Alloc(loadmodel->mempool, size);
1695 memset (w, 0, size);
1700 static void FreeWinding (winding_t *w)
1710 static winding_t *BaseWindingForPlane (mplane_t *p)
1712 double org[3], vright[3], vup[3], normal[3];
1715 VectorCopy(p->normal, normal);
1716 VectorVectorsDouble(normal, vright, vup);
1718 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1719 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1721 // project a really big axis aligned box onto the plane
1724 VectorScale (p->normal, p->dist, org);
1726 VectorSubtract (org, vright, w->points[0]);
1727 VectorAdd (w->points[0], vup, w->points[0]);
1729 VectorAdd (org, vright, w->points[1]);
1730 VectorAdd (w->points[1], vup, w->points[1]);
1732 VectorAdd (org, vright, w->points[2]);
1733 VectorSubtract (w->points[2], vup, w->points[2]);
1735 VectorSubtract (org, vright, w->points[3]);
1736 VectorSubtract (w->points[3], vup, w->points[3]);
1747 Clips the winding to the plane, returning the new winding on the positive side
1748 Frees the input winding.
1749 If keepon is true, an exactly on-plane winding will be saved, otherwise
1750 it will be clipped away.
1753 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1755 double dists[MAX_POINTS_ON_WINDING + 1];
1756 int sides[MAX_POINTS_ON_WINDING + 1];
1765 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1767 // determine sides for each point
1768 for (i = 0;i < in->numpoints;i++)
1770 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1771 if (dot > ON_EPSILON)
1772 sides[i] = SIDE_FRONT;
1773 else if (dot < -ON_EPSILON)
1774 sides[i] = SIDE_BACK;
1779 sides[i] = sides[0];
1780 dists[i] = dists[0];
1782 if (keepon && !counts[0] && !counts[1])
1793 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1794 if (maxpts > MAX_POINTS_ON_WINDING)
1795 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1797 neww = NewWinding (maxpts);
1799 for (i = 0;i < in->numpoints;i++)
1801 if (neww->numpoints >= maxpts)
1802 Sys_Error ("ClipWinding: points exceeded estimate");
1806 if (sides[i] == SIDE_ON)
1808 VectorCopy (p1, neww->points[neww->numpoints]);
1813 if (sides[i] == SIDE_FRONT)
1815 VectorCopy (p1, neww->points[neww->numpoints]);
1819 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1822 // generate a split point
1823 p2 = in->points[(i+1)%in->numpoints];
1825 dot = dists[i] / (dists[i]-dists[i+1]);
1826 for (j = 0;j < 3;j++)
1827 { // avoid round off error when possible
1828 if (split->normal[j] == 1)
1829 mid[j] = split->dist;
1830 else if (split->normal[j] == -1)
1831 mid[j] = -split->dist;
1833 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1836 VectorCopy (mid, neww->points[neww->numpoints]);
1840 // free the original winding
1851 Divides a winding by a plane, producing one or two windings. The
1852 original winding is not damaged or freed. If only on one side, the
1853 returned winding will be the input winding. If on both sides, two
1854 new windings will be created.
1857 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1859 double dists[MAX_POINTS_ON_WINDING + 1];
1860 int sides[MAX_POINTS_ON_WINDING + 1];
1869 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1871 // determine sides for each point
1872 for (i = 0;i < in->numpoints;i++)
1874 dot = DotProduct (in->points[i], split->normal);
1877 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1878 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1879 else sides[i] = SIDE_ON;
1882 sides[i] = sides[0];
1883 dists[i] = dists[0];
1885 *front = *back = NULL;
1898 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1900 if (maxpts > MAX_POINTS_ON_WINDING)
1901 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1903 *front = f = NewWinding (maxpts);
1904 *back = b = NewWinding (maxpts);
1906 for (i = 0;i < in->numpoints;i++)
1908 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1909 Sys_Error ("DivideWinding: points exceeded estimate");
1913 if (sides[i] == SIDE_ON)
1915 VectorCopy (p1, f->points[f->numpoints]);
1917 VectorCopy (p1, b->points[b->numpoints]);
1922 if (sides[i] == SIDE_FRONT)
1924 VectorCopy (p1, f->points[f->numpoints]);
1927 else if (sides[i] == SIDE_BACK)
1929 VectorCopy (p1, b->points[b->numpoints]);
1933 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1936 // generate a split point
1937 p2 = in->points[(i+1)%in->numpoints];
1939 dot = dists[i] / (dists[i]-dists[i+1]);
1940 for (j = 0;j < 3;j++)
1941 { // avoid round off error when possible
1942 if (split->normal[j] == 1)
1943 mid[j] = split->dist;
1944 else if (split->normal[j] == -1)
1945 mid[j] = -split->dist;
1947 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1950 VectorCopy (mid, f->points[f->numpoints]);
1952 VectorCopy (mid, b->points[b->numpoints]);
1957 typedef struct portal_s
1960 mnode_t *nodes[2]; // [0] = front side of plane
1961 struct portal_s *next[2];
1963 struct portal_s *chain; // all portals are linked into a list
1967 static portal_t *portalchain;
1974 static portal_t *AllocPortal (void)
1977 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1978 p->chain = portalchain;
1983 static void FreePortal(portal_t *p)
1988 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1990 // calculate children first
1991 if (node->children[0]->contents >= 0)
1992 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1993 if (node->children[1]->contents >= 0)
1994 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1996 // make combined bounding box from children
1997 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1998 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1999 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2000 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2001 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2002 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2005 static void Mod_FinalizePortals(void)
2007 int i, j, numportals, numpoints;
2008 portal_t *p, *pnext;
2011 mleaf_t *leaf, *endleaf;
2014 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2015 leaf = loadmodel->leafs;
2016 endleaf = leaf + loadmodel->numleafs;
2017 for (;leaf < endleaf;leaf++)
2019 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2020 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2027 for (i = 0;i < 2;i++)
2029 leaf = (mleaf_t *)p->nodes[i];
2031 for (j = 0;j < w->numpoints;j++)
2033 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2034 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2035 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2036 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2037 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2038 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2045 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2047 // tally up portal and point counts
2053 // note: this check must match the one below or it will usually corrupt memory
2054 // 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
2055 if (p->winding && p->nodes[0] != p->nodes[1]
2056 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2057 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2060 numpoints += p->winding->numpoints * 2;
2064 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2065 loadmodel->numportals = numportals;
2066 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2067 loadmodel->numportalpoints = numpoints;
2068 // clear all leaf portal chains
2069 for (i = 0;i < loadmodel->numleafs;i++)
2070 loadmodel->leafs[i].portals = NULL;
2071 // process all portals in the global portal chain, while freeing them
2072 portal = loadmodel->portals;
2073 point = loadmodel->portalpoints;
2082 // note: this check must match the one above or it will usually corrupt memory
2083 // 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
2084 if (p->nodes[0] != p->nodes[1]
2085 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2086 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2088 // first make the back to front portal (forward portal)
2089 portal->points = point;
2090 portal->numpoints = p->winding->numpoints;
2091 portal->plane.dist = p->plane.dist;
2092 VectorCopy(p->plane.normal, portal->plane.normal);
2093 portal->here = (mleaf_t *)p->nodes[1];
2094 portal->past = (mleaf_t *)p->nodes[0];
2096 for (j = 0;j < portal->numpoints;j++)
2098 VectorCopy(p->winding->points[j], point->position);
2101 PlaneClassify(&portal->plane);
2103 // link into leaf's portal chain
2104 portal->next = portal->here->portals;
2105 portal->here->portals = portal;
2107 // advance to next portal
2110 // then make the front to back portal (backward portal)
2111 portal->points = point;
2112 portal->numpoints = p->winding->numpoints;
2113 portal->plane.dist = -p->plane.dist;
2114 VectorNegate(p->plane.normal, portal->plane.normal);
2115 portal->here = (mleaf_t *)p->nodes[0];
2116 portal->past = (mleaf_t *)p->nodes[1];
2118 for (j = portal->numpoints - 1;j >= 0;j--)
2120 VectorCopy(p->winding->points[j], point->position);
2123 PlaneClassify(&portal->plane);
2125 // link into leaf's portal chain
2126 portal->next = portal->here->portals;
2127 portal->here->portals = portal;
2129 // advance to next portal
2132 FreeWinding(p->winding);
2144 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2147 Host_Error ("AddPortalToNodes: NULL front node");
2149 Host_Error ("AddPortalToNodes: NULL back node");
2150 if (p->nodes[0] || p->nodes[1])
2151 Host_Error ("AddPortalToNodes: already included");
2152 // 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
2154 p->nodes[0] = front;
2155 p->next[0] = (portal_t *)front->portals;
2156 front->portals = (mportal_t *)p;
2159 p->next[1] = (portal_t *)back->portals;
2160 back->portals = (mportal_t *)p;
2165 RemovePortalFromNode
2168 static void RemovePortalFromNodes(portal_t *portal)
2172 void **portalpointer;
2174 for (i = 0;i < 2;i++)
2176 node = portal->nodes[i];
2178 portalpointer = (void **) &node->portals;
2183 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2187 if (portal->nodes[0] == node)
2189 *portalpointer = portal->next[0];
2190 portal->nodes[0] = NULL;
2192 else if (portal->nodes[1] == node)
2194 *portalpointer = portal->next[1];
2195 portal->nodes[1] = NULL;
2198 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2202 if (t->nodes[0] == node)
2203 portalpointer = (void **) &t->next[0];
2204 else if (t->nodes[1] == node)
2205 portalpointer = (void **) &t->next[1];
2207 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2212 static void Mod_RecursiveNodePortals (mnode_t *node)
2215 mnode_t *front, *back, *other_node;
2216 mplane_t clipplane, *plane;
2217 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2218 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2220 // if a leaf, we're done
2224 plane = node->plane;
2226 front = node->children[0];
2227 back = node->children[1];
2229 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2231 // create the new portal by generating a polygon for the node plane,
2232 // and clipping it by all of the other portals (which came from nodes above this one)
2233 nodeportal = AllocPortal ();
2234 nodeportal->plane = *node->plane;
2236 nodeportalwinding = BaseWindingForPlane (node->plane);
2237 side = 0; // shut up compiler warning
2238 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2240 clipplane = portal->plane;
2241 if (portal->nodes[0] == portal->nodes[1])
2242 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2243 if (portal->nodes[0] == node)
2245 else if (portal->nodes[1] == node)
2247 clipplane.dist = -clipplane.dist;
2248 VectorNegate (clipplane.normal, clipplane.normal);
2252 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2254 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2255 if (!nodeportalwinding)
2257 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2262 if (nodeportalwinding)
2264 // if the plane was not clipped on all sides, there was an error
2265 nodeportal->winding = nodeportalwinding;
2266 AddPortalToNodes (nodeportal, front, back);
2269 // split the portals of this node along this node's plane and assign them to the children of this node
2270 // (migrating the portals downward through the tree)
2271 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2273 if (portal->nodes[0] == portal->nodes[1])
2274 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2275 if (portal->nodes[0] == node)
2277 else if (portal->nodes[1] == node)
2280 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2281 nextportal = portal->next[side];
2283 other_node = portal->nodes[!side];
2284 RemovePortalFromNodes (portal);
2286 // cut the portal into two portals, one on each side of the node plane
2287 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2292 AddPortalToNodes (portal, back, other_node);
2294 AddPortalToNodes (portal, other_node, back);
2300 AddPortalToNodes (portal, front, other_node);
2302 AddPortalToNodes (portal, other_node, front);
2306 // the winding is split
2307 splitportal = AllocPortal ();
2308 temp = splitportal->chain;
2309 *splitportal = *portal;
2310 splitportal->chain = temp;
2311 splitportal->winding = backwinding;
2312 FreeWinding (portal->winding);
2313 portal->winding = frontwinding;
2317 AddPortalToNodes (portal, front, other_node);
2318 AddPortalToNodes (splitportal, back, other_node);
2322 AddPortalToNodes (portal, other_node, front);
2323 AddPortalToNodes (splitportal, other_node, back);
2327 Mod_RecursiveNodePortals(front);
2328 Mod_RecursiveNodePortals(back);
2332 static void Mod_MakePortals(void)
2335 Mod_RecursiveNodePortals (loadmodel->nodes);
2336 Mod_FinalizePortals();
2344 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2349 mempool_t *mainmempool;
2352 mod->type = mod_brush;
2354 header = (dheader_t *)buffer;
2356 i = LittleLong (header->version);
2357 if (i != BSPVERSION && i != 30)
2358 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2359 mod->ishlbsp = i == 30;
2360 if (loadmodel->isworldmodel)
2362 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2363 // until we get a texture for it...
2367 // swap all the lumps
2368 mod_base = (qbyte *)header;
2370 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2371 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2375 // store which lightmap format to use
2376 mod->lightmaprgba = r_lightmaprgba.integer;
2378 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2379 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2380 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2381 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2382 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2383 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2384 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2385 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2386 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2387 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2388 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2389 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2390 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2391 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2392 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2397 mod->numframes = 2; // regular and alternate animation
2399 mainmempool = mod->mempool;
2400 loadname = mod->name;
2402 Mod_LoadLightList ();
2405 // set up the submodels (FIXME: this is confusing)
2407 for (i = 0;i < mod->numsubmodels;i++)
2410 float dist, modelyawradius, modelradius, *vec;
2413 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2414 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2418 bm = &mod->submodels[i];
2420 mod->hulls[0].firstclipnode = bm->headnode[0];
2421 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2423 mod->hulls[j].firstclipnode = bm->headnode[j];
2424 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2427 mod->firstmodelsurface = bm->firstface;
2428 mod->nummodelsurfaces = bm->numfaces;
2430 mod->DrawSky = NULL;
2431 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2432 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2434 // we only need to have a drawsky function if it is used (usually only on world model)
2435 if (surf->texinfo->texture->shader == &Cshader_sky)
2436 mod->DrawSky = R_DrawBrushModelSky;
2437 for (k = 0;k < surf->numedges;k++)
2439 l = mod->surfedges[k + surf->firstedge];
2441 vec = mod->vertexes[mod->edges[l].v[0]].position;
2443 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2444 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2445 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2446 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2447 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2448 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2449 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2450 dist = vec[0]*vec[0]+vec[1]*vec[1];
2451 if (modelyawradius < dist)
2452 modelyawradius = dist;
2453 dist += vec[2]*vec[2];
2454 if (modelradius < dist)
2458 modelyawradius = sqrt(modelyawradius);
2459 modelradius = sqrt(modelradius);
2460 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2461 mod->yawmins[2] = mod->normalmins[2];
2462 mod->yawmaxs[2] = mod->normalmaxs[2];
2463 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2464 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2465 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2466 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2468 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2469 VectorClear(mod->normalmins);
2470 VectorClear(mod->normalmaxs);
2471 VectorClear(mod->yawmins);
2472 VectorClear(mod->yawmaxs);
2473 VectorClear(mod->rotatedmins);
2474 VectorClear(mod->rotatedmaxs);
2477 mod->numleafs = bm->visleafs;
2479 mod->Draw = R_DrawBrushModelNormal;
2480 mod->DrawShadow = NULL;
2482 // LordHavoc: only register submodels if it is the world
2483 // (prevents bsp models from replacing world submodels)
2484 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2487 // duplicate the basic information
2488 sprintf (name, "*%i", i+1);
2489 loadmodel = Mod_FindName (name);
2491 strcpy (loadmodel->name, name);
2492 // textures and memory belong to the main model
2493 loadmodel->texturepool = NULL;
2494 loadmodel->mempool = NULL;