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;
1320 CalcSurfaceExtents (out);
1322 ssize = (out->extents[0] >> 4) + 1;
1323 tsize = (out->extents[1] >> 4) + 1;
1326 for (i = 0;i < MAXLIGHTMAPS;i++)
1327 out->styles[i] = in->styles[i];
1328 i = LittleLong(in->lightofs);
1330 out->samples = NULL;
1331 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1332 out->samples = loadmodel->lightdata + i;
1333 else // LordHavoc: white lighting (bsp version 29)
1334 out->samples = loadmodel->lightdata + (i * 3);
1336 Mod_GenerateSurfacePolygon(out);
1337 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
1339 if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1340 Host_Error ("Bad surface extents");
1341 Mod_GenerateWallMesh (out, false);
1342 // stainmap for permanent marks on walls
1343 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1345 memset(out->stainsamples, 255, ssize * tsize * 3);
1348 Mod_GenerateVertexMesh (out);
1357 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1359 node->parent = parent;
1360 if (node->contents < 0)
1362 Mod_SetParent (node->children[0], node);
1363 Mod_SetParent (node->children[1], node);
1371 static void Mod_LoadNodes (lump_t *l)
1377 in = (void *)(mod_base + l->fileofs);
1378 if (l->filelen % sizeof(*in))
1379 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1380 count = l->filelen / sizeof(*in);
1381 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1383 loadmodel->nodes = out;
1384 loadmodel->numnodes = count;
1386 for ( i=0 ; i<count ; i++, in++, out++)
1388 for (j=0 ; j<3 ; j++)
1390 out->mins[j] = LittleShort (in->mins[j]);
1391 out->maxs[j] = LittleShort (in->maxs[j]);
1394 p = LittleLong(in->planenum);
1395 out->plane = loadmodel->planes + p;
1397 out->firstsurface = LittleShort (in->firstface);
1398 out->numsurfaces = LittleShort (in->numfaces);
1400 for (j=0 ; j<2 ; j++)
1402 p = LittleShort (in->children[j]);
1404 out->children[j] = loadmodel->nodes + p;
1406 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1410 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1418 static void Mod_LoadLeafs (lump_t *l)
1424 in = (void *)(mod_base + l->fileofs);
1425 if (l->filelen % sizeof(*in))
1426 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1427 count = l->filelen / sizeof(*in);
1428 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1430 loadmodel->leafs = out;
1431 loadmodel->numleafs = count;
1433 for ( i=0 ; i<count ; i++, in++, out++)
1435 for (j=0 ; j<3 ; j++)
1437 out->mins[j] = LittleShort (in->mins[j]);
1438 out->maxs[j] = LittleShort (in->maxs[j]);
1441 p = LittleLong(in->contents);
1444 out->firstmarksurface = loadmodel->marksurfaces +
1445 LittleShort(in->firstmarksurface);
1446 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1448 p = LittleLong(in->visofs);
1450 out->compressed_vis = NULL;
1452 out->compressed_vis = loadmodel->visdata + p;
1454 for (j=0 ; j<4 ; j++)
1455 out->ambient_sound_level[j] = in->ambient_level[j];
1457 // FIXME: Insert caustics here
1466 static void Mod_LoadClipnodes (lump_t *l)
1468 dclipnode_t *in, *out;
1472 in = (void *)(mod_base + l->fileofs);
1473 if (l->filelen % sizeof(*in))
1474 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1475 count = l->filelen / sizeof(*in);
1476 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1478 loadmodel->clipnodes = out;
1479 loadmodel->numclipnodes = count;
1481 if (loadmodel->ishlbsp)
1483 hull = &loadmodel->hulls[1];
1484 hull->clipnodes = out;
1485 hull->firstclipnode = 0;
1486 hull->lastclipnode = count-1;
1487 hull->planes = loadmodel->planes;
1488 hull->clip_mins[0] = -16;
1489 hull->clip_mins[1] = -16;
1490 hull->clip_mins[2] = -36;
1491 hull->clip_maxs[0] = 16;
1492 hull->clip_maxs[1] = 16;
1493 hull->clip_maxs[2] = 36;
1494 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1496 hull = &loadmodel->hulls[2];
1497 hull->clipnodes = out;
1498 hull->firstclipnode = 0;
1499 hull->lastclipnode = count-1;
1500 hull->planes = loadmodel->planes;
1501 hull->clip_mins[0] = -32;
1502 hull->clip_mins[1] = -32;
1503 hull->clip_mins[2] = -32;
1504 hull->clip_maxs[0] = 32;
1505 hull->clip_maxs[1] = 32;
1506 hull->clip_maxs[2] = 32;
1507 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1509 hull = &loadmodel->hulls[3];
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] = -18;
1517 hull->clip_maxs[0] = 16;
1518 hull->clip_maxs[1] = 16;
1519 hull->clip_maxs[2] = 18;
1520 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1524 hull = &loadmodel->hulls[1];
1525 hull->clipnodes = out;
1526 hull->firstclipnode = 0;
1527 hull->lastclipnode = count-1;
1528 hull->planes = loadmodel->planes;
1529 hull->clip_mins[0] = -16;
1530 hull->clip_mins[1] = -16;
1531 hull->clip_mins[2] = -24;
1532 hull->clip_maxs[0] = 16;
1533 hull->clip_maxs[1] = 16;
1534 hull->clip_maxs[2] = 32;
1535 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1537 hull = &loadmodel->hulls[2];
1538 hull->clipnodes = out;
1539 hull->firstclipnode = 0;
1540 hull->lastclipnode = count-1;
1541 hull->planes = loadmodel->planes;
1542 hull->clip_mins[0] = -32;
1543 hull->clip_mins[1] = -32;
1544 hull->clip_mins[2] = -24;
1545 hull->clip_maxs[0] = 32;
1546 hull->clip_maxs[1] = 32;
1547 hull->clip_maxs[2] = 64;
1548 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1551 for (i=0 ; i<count ; i++, out++, in++)
1553 out->planenum = LittleLong(in->planenum);
1554 out->children[0] = LittleShort(in->children[0]);
1555 out->children[1] = LittleShort(in->children[1]);
1556 if (out->children[0] >= count || out->children[1] >= count)
1557 Host_Error("Corrupt clipping hull (out of range child)\n");
1565 Duplicate the drawing hull structure as a clipping hull
1568 static void Mod_MakeHull0 (void)
1575 hull = &loadmodel->hulls[0];
1577 in = loadmodel->nodes;
1578 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1580 hull->clipnodes = out;
1581 hull->firstclipnode = 0;
1582 hull->lastclipnode = loadmodel->numnodes - 1;
1583 hull->planes = loadmodel->planes;
1585 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1587 out->planenum = in->plane - loadmodel->planes;
1588 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1589 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1595 Mod_LoadMarksurfaces
1598 static void Mod_LoadMarksurfaces (lump_t *l)
1603 in = (void *)(mod_base + l->fileofs);
1604 if (l->filelen % sizeof(*in))
1605 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1606 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1607 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1609 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1611 j = (unsigned) LittleShort(in[i]);
1612 if (j >= loadmodel->numsurfaces)
1613 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1614 loadmodel->marksurfaces[i] = j;
1623 static void Mod_LoadSurfedges (lump_t *l)
1628 in = (void *)(mod_base + l->fileofs);
1629 if (l->filelen % sizeof(*in))
1630 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1631 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1632 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1634 for (i = 0;i < loadmodel->numsurfedges;i++)
1635 loadmodel->surfedges[i] = LittleLong (in[i]);
1644 static void Mod_LoadPlanes (lump_t *l)
1650 in = (void *)(mod_base + l->fileofs);
1651 if (l->filelen % sizeof(*in))
1652 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1654 loadmodel->numplanes = l->filelen / sizeof(*in);
1655 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1657 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1659 out->normal[0] = LittleFloat (in->normal[0]);
1660 out->normal[1] = LittleFloat (in->normal[1]);
1661 out->normal[2] = LittleFloat (in->normal[2]);
1662 out->dist = LittleFloat (in->dist);
1668 #define MAX_POINTS_ON_WINDING 64
1674 double points[8][3]; // variable sized
1683 static winding_t *NewWinding (int points)
1688 if (points > MAX_POINTS_ON_WINDING)
1689 Sys_Error("NewWinding: too many points\n");
1691 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1692 w = Mem_Alloc(loadmodel->mempool, size);
1693 memset (w, 0, size);
1698 static void FreeWinding (winding_t *w)
1708 static winding_t *BaseWindingForPlane (mplane_t *p)
1710 double org[3], vright[3], vup[3], normal[3];
1713 VectorCopy(p->normal, normal);
1714 VectorVectorsDouble(normal, vright, vup);
1716 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1717 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1719 // project a really big axis aligned box onto the plane
1722 VectorScale (p->normal, p->dist, org);
1724 VectorSubtract (org, vright, w->points[0]);
1725 VectorAdd (w->points[0], vup, w->points[0]);
1727 VectorAdd (org, vright, w->points[1]);
1728 VectorAdd (w->points[1], vup, w->points[1]);
1730 VectorAdd (org, vright, w->points[2]);
1731 VectorSubtract (w->points[2], vup, w->points[2]);
1733 VectorSubtract (org, vright, w->points[3]);
1734 VectorSubtract (w->points[3], vup, w->points[3]);
1745 Clips the winding to the plane, returning the new winding on the positive side
1746 Frees the input winding.
1747 If keepon is true, an exactly on-plane winding will be saved, otherwise
1748 it will be clipped away.
1751 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1753 double dists[MAX_POINTS_ON_WINDING + 1];
1754 int sides[MAX_POINTS_ON_WINDING + 1];
1763 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1765 // determine sides for each point
1766 for (i = 0;i < in->numpoints;i++)
1768 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1769 if (dot > ON_EPSILON)
1770 sides[i] = SIDE_FRONT;
1771 else if (dot < -ON_EPSILON)
1772 sides[i] = SIDE_BACK;
1777 sides[i] = sides[0];
1778 dists[i] = dists[0];
1780 if (keepon && !counts[0] && !counts[1])
1791 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1792 if (maxpts > MAX_POINTS_ON_WINDING)
1793 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1795 neww = NewWinding (maxpts);
1797 for (i = 0;i < in->numpoints;i++)
1799 if (neww->numpoints >= maxpts)
1800 Sys_Error ("ClipWinding: points exceeded estimate");
1804 if (sides[i] == SIDE_ON)
1806 VectorCopy (p1, neww->points[neww->numpoints]);
1811 if (sides[i] == SIDE_FRONT)
1813 VectorCopy (p1, neww->points[neww->numpoints]);
1817 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1820 // generate a split point
1821 p2 = in->points[(i+1)%in->numpoints];
1823 dot = dists[i] / (dists[i]-dists[i+1]);
1824 for (j = 0;j < 3;j++)
1825 { // avoid round off error when possible
1826 if (split->normal[j] == 1)
1827 mid[j] = split->dist;
1828 else if (split->normal[j] == -1)
1829 mid[j] = -split->dist;
1831 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1834 VectorCopy (mid, neww->points[neww->numpoints]);
1838 // free the original winding
1849 Divides a winding by a plane, producing one or two windings. The
1850 original winding is not damaged or freed. If only on one side, the
1851 returned winding will be the input winding. If on both sides, two
1852 new windings will be created.
1855 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1857 double dists[MAX_POINTS_ON_WINDING + 1];
1858 int sides[MAX_POINTS_ON_WINDING + 1];
1867 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1869 // determine sides for each point
1870 for (i = 0;i < in->numpoints;i++)
1872 dot = DotProduct (in->points[i], split->normal);
1875 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1876 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1877 else sides[i] = SIDE_ON;
1880 sides[i] = sides[0];
1881 dists[i] = dists[0];
1883 *front = *back = NULL;
1896 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1898 if (maxpts > MAX_POINTS_ON_WINDING)
1899 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1901 *front = f = NewWinding (maxpts);
1902 *back = b = NewWinding (maxpts);
1904 for (i = 0;i < in->numpoints;i++)
1906 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1907 Sys_Error ("DivideWinding: points exceeded estimate");
1911 if (sides[i] == SIDE_ON)
1913 VectorCopy (p1, f->points[f->numpoints]);
1915 VectorCopy (p1, b->points[b->numpoints]);
1920 if (sides[i] == SIDE_FRONT)
1922 VectorCopy (p1, f->points[f->numpoints]);
1925 else if (sides[i] == SIDE_BACK)
1927 VectorCopy (p1, b->points[b->numpoints]);
1931 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1934 // generate a split point
1935 p2 = in->points[(i+1)%in->numpoints];
1937 dot = dists[i] / (dists[i]-dists[i+1]);
1938 for (j = 0;j < 3;j++)
1939 { // avoid round off error when possible
1940 if (split->normal[j] == 1)
1941 mid[j] = split->dist;
1942 else if (split->normal[j] == -1)
1943 mid[j] = -split->dist;
1945 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1948 VectorCopy (mid, f->points[f->numpoints]);
1950 VectorCopy (mid, b->points[b->numpoints]);
1955 typedef struct portal_s
1958 mnode_t *nodes[2]; // [0] = front side of plane
1959 struct portal_s *next[2];
1961 struct portal_s *chain; // all portals are linked into a list
1965 static portal_t *portalchain;
1972 static portal_t *AllocPortal (void)
1975 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1976 p->chain = portalchain;
1981 static void FreePortal(portal_t *p)
1986 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1988 // calculate children first
1989 if (node->children[0]->contents >= 0)
1990 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1991 if (node->children[1]->contents >= 0)
1992 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1994 // make combined bounding box from children
1995 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
1996 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
1997 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
1998 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
1999 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2000 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2003 static void Mod_FinalizePortals(void)
2005 int i, j, numportals, numpoints;
2006 portal_t *p, *pnext;
2009 mleaf_t *leaf, *endleaf;
2012 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2013 leaf = loadmodel->leafs;
2014 endleaf = leaf + loadmodel->numleafs;
2015 for (;leaf < endleaf;leaf++)
2017 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2018 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2025 for (i = 0;i < 2;i++)
2027 leaf = (mleaf_t *)p->nodes[i];
2029 for (j = 0;j < w->numpoints;j++)
2031 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2032 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2033 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2034 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2035 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2036 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2043 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2045 // tally up portal and point counts
2051 // note: this check must match the one below or it will usually corrupt memory
2052 // 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
2053 if (p->winding && p->nodes[0] != p->nodes[1]
2054 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2055 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2058 numpoints += p->winding->numpoints * 2;
2062 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2063 loadmodel->numportals = numportals;
2064 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2065 loadmodel->numportalpoints = numpoints;
2066 // clear all leaf portal chains
2067 for (i = 0;i < loadmodel->numleafs;i++)
2068 loadmodel->leafs[i].portals = NULL;
2069 // process all portals in the global portal chain, while freeing them
2070 portal = loadmodel->portals;
2071 point = loadmodel->portalpoints;
2080 // note: this check must match the one above or it will usually corrupt memory
2081 // 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
2082 if (p->nodes[0] != p->nodes[1]
2083 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2084 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2086 // first make the back to front portal (forward portal)
2087 portal->points = point;
2088 portal->numpoints = p->winding->numpoints;
2089 portal->plane.dist = p->plane.dist;
2090 VectorCopy(p->plane.normal, portal->plane.normal);
2091 portal->here = (mleaf_t *)p->nodes[1];
2092 portal->past = (mleaf_t *)p->nodes[0];
2094 for (j = 0;j < portal->numpoints;j++)
2096 VectorCopy(p->winding->points[j], point->position);
2099 PlaneClassify(&portal->plane);
2101 // link into leaf's portal chain
2102 portal->next = portal->here->portals;
2103 portal->here->portals = portal;
2105 // advance to next portal
2108 // then make the front to back portal (backward portal)
2109 portal->points = point;
2110 portal->numpoints = p->winding->numpoints;
2111 portal->plane.dist = -p->plane.dist;
2112 VectorNegate(p->plane.normal, portal->plane.normal);
2113 portal->here = (mleaf_t *)p->nodes[0];
2114 portal->past = (mleaf_t *)p->nodes[1];
2116 for (j = portal->numpoints - 1;j >= 0;j--)
2118 VectorCopy(p->winding->points[j], point->position);
2121 PlaneClassify(&portal->plane);
2123 // link into leaf's portal chain
2124 portal->next = portal->here->portals;
2125 portal->here->portals = portal;
2127 // advance to next portal
2130 FreeWinding(p->winding);
2142 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2145 Host_Error ("AddPortalToNodes: NULL front node");
2147 Host_Error ("AddPortalToNodes: NULL back node");
2148 if (p->nodes[0] || p->nodes[1])
2149 Host_Error ("AddPortalToNodes: already included");
2150 // 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
2152 p->nodes[0] = front;
2153 p->next[0] = (portal_t *)front->portals;
2154 front->portals = (mportal_t *)p;
2157 p->next[1] = (portal_t *)back->portals;
2158 back->portals = (mportal_t *)p;
2163 RemovePortalFromNode
2166 static void RemovePortalFromNodes(portal_t *portal)
2170 void **portalpointer;
2172 for (i = 0;i < 2;i++)
2174 node = portal->nodes[i];
2176 portalpointer = (void **) &node->portals;
2181 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2185 if (portal->nodes[0] == node)
2187 *portalpointer = portal->next[0];
2188 portal->nodes[0] = NULL;
2190 else if (portal->nodes[1] == node)
2192 *portalpointer = portal->next[1];
2193 portal->nodes[1] = NULL;
2196 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2200 if (t->nodes[0] == node)
2201 portalpointer = (void **) &t->next[0];
2202 else if (t->nodes[1] == node)
2203 portalpointer = (void **) &t->next[1];
2205 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2210 static void Mod_RecursiveNodePortals (mnode_t *node)
2213 mnode_t *front, *back, *other_node;
2214 mplane_t clipplane, *plane;
2215 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2216 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2218 // if a leaf, we're done
2222 plane = node->plane;
2224 front = node->children[0];
2225 back = node->children[1];
2227 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2229 // create the new portal by generating a polygon for the node plane,
2230 // and clipping it by all of the other portals (which came from nodes above this one)
2231 nodeportal = AllocPortal ();
2232 nodeportal->plane = *node->plane;
2234 nodeportalwinding = BaseWindingForPlane (node->plane);
2235 side = 0; // shut up compiler warning
2236 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2238 clipplane = portal->plane;
2239 if (portal->nodes[0] == portal->nodes[1])
2240 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2241 if (portal->nodes[0] == node)
2243 else if (portal->nodes[1] == node)
2245 clipplane.dist = -clipplane.dist;
2246 VectorNegate (clipplane.normal, clipplane.normal);
2250 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2252 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2253 if (!nodeportalwinding)
2255 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2260 if (nodeportalwinding)
2262 // if the plane was not clipped on all sides, there was an error
2263 nodeportal->winding = nodeportalwinding;
2264 AddPortalToNodes (nodeportal, front, back);
2267 // split the portals of this node along this node's plane and assign them to the children of this node
2268 // (migrating the portals downward through the tree)
2269 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2271 if (portal->nodes[0] == portal->nodes[1])
2272 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2273 if (portal->nodes[0] == node)
2275 else if (portal->nodes[1] == node)
2278 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2279 nextportal = portal->next[side];
2281 other_node = portal->nodes[!side];
2282 RemovePortalFromNodes (portal);
2284 // cut the portal into two portals, one on each side of the node plane
2285 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2290 AddPortalToNodes (portal, back, other_node);
2292 AddPortalToNodes (portal, other_node, back);
2298 AddPortalToNodes (portal, front, other_node);
2300 AddPortalToNodes (portal, other_node, front);
2304 // the winding is split
2305 splitportal = AllocPortal ();
2306 temp = splitportal->chain;
2307 *splitportal = *portal;
2308 splitportal->chain = temp;
2309 splitportal->winding = backwinding;
2310 FreeWinding (portal->winding);
2311 portal->winding = frontwinding;
2315 AddPortalToNodes (portal, front, other_node);
2316 AddPortalToNodes (splitportal, back, other_node);
2320 AddPortalToNodes (portal, other_node, front);
2321 AddPortalToNodes (splitportal, other_node, back);
2325 Mod_RecursiveNodePortals(front);
2326 Mod_RecursiveNodePortals(back);
2330 static void Mod_MakePortals(void)
2333 Mod_RecursiveNodePortals (loadmodel->nodes);
2334 Mod_FinalizePortals();
2342 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2347 mempool_t *mainmempool;
2350 mod->type = mod_brush;
2352 header = (dheader_t *)buffer;
2354 i = LittleLong (header->version);
2355 if (i != BSPVERSION && i != 30)
2356 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2357 mod->ishlbsp = i == 30;
2358 if (loadmodel->isworldmodel)
2360 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2361 // until we get a texture for it...
2365 // swap all the lumps
2366 mod_base = (qbyte *)header;
2368 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2369 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2373 // store which lightmap format to use
2374 mod->lightmaprgba = r_lightmaprgba.integer;
2376 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2377 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2378 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2379 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2380 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2381 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2382 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2383 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2384 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2385 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2386 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2387 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2388 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2389 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2390 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2395 mod->numframes = 2; // regular and alternate animation
2397 mainmempool = mod->mempool;
2398 loadname = mod->name;
2400 Mod_LoadLightList ();
2403 // set up the submodels (FIXME: this is confusing)
2405 for (i = 0;i < mod->numsubmodels;i++)
2408 float dist, modelyawradius, modelradius, *vec;
2411 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2412 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2416 bm = &mod->submodels[i];
2418 mod->hulls[0].firstclipnode = bm->headnode[0];
2419 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2421 mod->hulls[j].firstclipnode = bm->headnode[j];
2422 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2425 mod->firstmodelsurface = bm->firstface;
2426 mod->nummodelsurfaces = bm->numfaces;
2428 mod->DrawSky = NULL;
2429 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2430 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2432 // we only need to have a drawsky function if it is used (usually only on world model)
2433 if (surf->texinfo->texture->shader == &Cshader_sky)
2434 mod->DrawSky = R_DrawBrushModelSky;
2435 for (k = 0;k < surf->numedges;k++)
2437 l = mod->surfedges[k + surf->firstedge];
2439 vec = mod->vertexes[mod->edges[l].v[0]].position;
2441 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2442 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2443 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2444 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2445 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2446 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2447 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2448 dist = vec[0]*vec[0]+vec[1]*vec[1];
2449 if (modelyawradius < dist)
2450 modelyawradius = dist;
2451 dist += vec[2]*vec[2];
2452 if (modelradius < dist)
2456 modelyawradius = sqrt(modelyawradius);
2457 modelradius = sqrt(modelradius);
2458 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2459 mod->yawmins[2] = mod->normalmins[2];
2460 mod->yawmaxs[2] = mod->normalmaxs[2];
2461 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2462 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2463 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2464 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2466 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2467 VectorClear(mod->normalmins);
2468 VectorClear(mod->normalmaxs);
2469 VectorClear(mod->yawmins);
2470 VectorClear(mod->yawmaxs);
2471 VectorClear(mod->rotatedmins);
2472 VectorClear(mod->rotatedmaxs);
2475 mod->numleafs = bm->visleafs;
2477 mod->Draw = R_DrawBrushModelNormal;
2478 mod->DrawShadow = NULL;
2480 // LordHavoc: only register submodels if it is the world
2481 // (prevents bsp models from replacing world submodels)
2482 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2485 // duplicate the basic information
2486 sprintf (name, "*%i", i+1);
2487 loadmodel = Mod_FindName (name);
2489 strcpy (loadmodel->name, name);
2490 // textures and memory belong to the main model
2491 loadmodel->texturepool = NULL;
2492 loadmodel->mempool = NULL;