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 13 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone style)\n", a, n + 1);
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(char *data)
683 char key[128], value[4096];
688 data = COM_Parse(data);
691 if (com_token[0] != '{')
695 data = COM_Parse(data);
698 if (com_token[0] == '}')
699 break; // end of worldspawn
700 if (com_token[0] == '_')
701 strcpy(key, com_token + 1);
703 strcpy(key, com_token);
704 while (key[strlen(key)-1] == ' ') // remove trailing spaces
705 key[strlen(key)-1] = 0;
706 data = COM_Parse(data);
709 strcpy(value, com_token);
710 if (!strcmp("wad", key)) // for HalfLife maps
712 if (loadmodel->ishlbsp)
715 for (i = 0;i < 4096;i++)
716 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
722 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
723 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
725 else if (value[i] == ';' || value[i] == 0)
729 strcpy(wadname, "textures/");
730 strcat(wadname, &value[j]);
731 W_LoadTextureWadFile (wadname, false);
748 static void Mod_LoadEntities (lump_t *l)
750 loadmodel->entities = NULL;
753 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
754 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
755 if (loadmodel->ishlbsp)
756 Mod_ParseWadsFromEntityLump(loadmodel->entities);
765 static void Mod_LoadVertexes (lump_t *l)
771 in = (void *)(mod_base + l->fileofs);
772 if (l->filelen % sizeof(*in))
773 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
774 count = l->filelen / sizeof(*in);
775 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
777 loadmodel->vertexes = out;
778 loadmodel->numvertexes = count;
780 for ( i=0 ; i<count ; i++, in++, out++)
782 out->position[0] = LittleFloat (in->point[0]);
783 out->position[1] = LittleFloat (in->point[1]);
784 out->position[2] = LittleFloat (in->point[2]);
793 static void Mod_LoadSubmodels (lump_t *l)
799 in = (void *)(mod_base + l->fileofs);
800 if (l->filelen % sizeof(*in))
801 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
802 count = l->filelen / sizeof(*in);
803 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
805 loadmodel->submodels = out;
806 loadmodel->numsubmodels = count;
808 for ( i=0 ; i<count ; i++, in++, out++)
810 for (j=0 ; j<3 ; j++)
812 // spread the mins / maxs by a pixel
813 out->mins[j] = LittleFloat (in->mins[j]) - 1;
814 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
815 out->origin[j] = LittleFloat (in->origin[j]);
817 for (j=0 ; j<MAX_MAP_HULLS ; j++)
818 out->headnode[j] = LittleLong (in->headnode[j]);
819 out->visleafs = LittleLong (in->visleafs);
820 out->firstface = LittleLong (in->firstface);
821 out->numfaces = LittleLong (in->numfaces);
830 static void Mod_LoadEdges (lump_t *l)
836 in = (void *)(mod_base + l->fileofs);
837 if (l->filelen % sizeof(*in))
838 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
839 count = l->filelen / sizeof(*in);
840 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
842 loadmodel->edges = out;
843 loadmodel->numedges = count;
845 for ( i=0 ; i<count ; i++, in++, out++)
847 out->v[0] = (unsigned short)LittleShort(in->v[0]);
848 out->v[1] = (unsigned short)LittleShort(in->v[1]);
857 static void Mod_LoadTexinfo (lump_t *l)
861 int i, j, k, count, miptex;
863 in = (void *)(mod_base + l->fileofs);
864 if (l->filelen % sizeof(*in))
865 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
866 count = l->filelen / sizeof(*in);
867 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
869 loadmodel->texinfo = out;
870 loadmodel->numtexinfo = count;
872 for (i = 0;i < count;i++, in++, out++)
874 for (k = 0;k < 2;k++)
875 for (j = 0;j < 4;j++)
876 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
878 miptex = LittleLong (in->miptex);
879 out->flags = LittleLong (in->flags);
882 if (loadmodel->textures)
884 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
885 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
887 out->texture = loadmodel->textures + miptex;
889 if (out->flags & TEX_SPECIAL)
891 // if texture chosen is NULL or the shader needs a lightmap,
892 // force to notexture water shader
893 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
894 out->texture = loadmodel->textures + (loadmodel->numtextures - 1);
898 // if texture chosen is NULL, force to notexture
899 if (out->texture == NULL)
900 out->texture = loadmodel->textures + (loadmodel->numtextures - 2);
909 Fills in s->texturemins[] and s->extents[]
912 static void CalcSurfaceExtents (msurface_t *s)
914 float mins[2], maxs[2], val;
918 int bmins[2], bmaxs[2];
920 mins[0] = mins[1] = 999999999;
921 maxs[0] = maxs[1] = -999999999;
925 for (i=0 ; i<s->numedges ; i++)
927 e = loadmodel->surfedges[s->firstedge+i];
929 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
931 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
933 for (j=0 ; j<2 ; j++)
935 val = v->position[0] * tex->vecs[j][0] +
936 v->position[1] * tex->vecs[j][1] +
937 v->position[2] * tex->vecs[j][2] +
946 for (i=0 ; i<2 ; i++)
948 bmins[i] = floor(mins[i]/16);
949 bmaxs[i] = ceil(maxs[i]/16);
951 s->texturemins[i] = bmins[i] * 16;
952 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
957 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
962 mins[0] = mins[1] = mins[2] = 9999;
963 maxs[0] = maxs[1] = maxs[2] = -9999;
965 for (i = 0;i < numverts;i++)
967 for (j = 0;j < 3;j++, v++)
978 #define MAX_SUBDIVPOLYTRIANGLES 4096
979 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
981 static int subdivpolyverts, subdivpolytriangles;
982 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
983 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
985 static int subdivpolylookupvert(vec3_t v)
988 for (i = 0;i < subdivpolyverts;i++)
989 if (subdivpolyvert[i][0] == v[0]
990 && subdivpolyvert[i][1] == v[1]
991 && subdivpolyvert[i][2] == v[2])
993 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
994 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
995 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
996 return subdivpolyverts++;
999 static void SubdividePolygon (int numverts, float *verts)
1001 int i, i1, i2, i3, f, b, c, p;
1002 vec3_t mins, maxs, front[256], back[256];
1003 float m, *pv, *cv, dist[256], frac;
1006 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1008 BoundPoly (numverts, verts, mins, maxs);
1010 for (i = 0;i < 3;i++)
1012 m = (mins[i] + maxs[i]) * 0.5;
1013 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1014 if (maxs[i] - m < 8)
1016 if (m - mins[i] < 8)
1020 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1021 dist[c] = cv[i] - m;
1024 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1028 VectorCopy (pv, front[f]);
1033 VectorCopy (pv, back[b]);
1036 if (dist[p] == 0 || dist[c] == 0)
1038 if ( (dist[p] > 0) != (dist[c] > 0) )
1041 frac = dist[p] / (dist[p] - dist[c]);
1042 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1043 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1044 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1050 SubdividePolygon (f, front[0]);
1051 SubdividePolygon (b, back[0]);
1055 i1 = subdivpolylookupvert(verts);
1056 i2 = subdivpolylookupvert(verts + 3);
1057 for (i = 2;i < numverts;i++)
1059 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1061 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1065 i3 = subdivpolylookupvert(verts + i * 3);
1066 subdivpolyindex[subdivpolytriangles][0] = i1;
1067 subdivpolyindex[subdivpolytriangles][1] = i2;
1068 subdivpolyindex[subdivpolytriangles][2] = i3;
1070 subdivpolytriangles++;
1076 Mod_GenerateWarpMesh
1078 Breaks a polygon up along axial 64 unit
1079 boundaries so that turbulent and sky warps
1080 can be done reasonably.
1083 void Mod_GenerateWarpMesh (msurface_t *surf)
1089 subdivpolytriangles = 0;
1090 subdivpolyverts = 0;
1091 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1092 if (subdivpolytriangles < 1)
1093 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1095 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1096 mesh->numverts = subdivpolyverts;
1097 mesh->numtriangles = subdivpolytriangles;
1098 mesh->vertex = (surfvertex_t *)(mesh + 1);
1099 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1100 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1102 for (i = 0;i < mesh->numtriangles;i++)
1103 for (j = 0;j < 3;j++)
1104 mesh->index[i*3+j] = subdivpolyindex[i][j];
1106 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1108 VectorCopy(subdivpolyvert[i], v->v);
1109 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1110 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1115 void Mod_GenerateWallMesh (msurface_t *surf, int vertexonly)
1117 int i, iu, iv, *index, smax, tmax;
1118 float *in, s, t, u, v, ubase, vbase, uscale, vscale;
1121 smax = surf->extents[0] >> 4;
1122 tmax = surf->extents[1] >> 4;
1126 surf->lightmaptexturestride = 0;
1127 surf->lightmaptexture = NULL;
1135 surf->flags |= SURF_LIGHTMAP;
1136 if (r_miplightmaps.integer)
1138 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1139 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);
1143 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1144 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);
1146 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1147 uscale = (uscale - ubase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1148 vscale = (vscale - vbase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1151 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));
1152 mesh->numverts = surf->poly_numverts;
1153 mesh->numtriangles = surf->poly_numverts - 2;
1154 mesh->verts = (float *)(mesh + 1);
1155 mesh->st = mesh->verts + mesh->numverts * 4;
1156 mesh->uv = mesh->st + mesh->numverts * 2;
1157 mesh->ab = mesh->uv + mesh->numverts * 2;
1158 mesh->lightmapoffsets = (int *)(mesh->ab + mesh->numverts * 2);
1159 mesh->index = mesh->lightmapoffsets + mesh->numverts;
1161 index = mesh->index;
1162 for (i = 0;i < mesh->numtriangles;i++)
1169 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1171 s = DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1172 t = DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1173 u = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1174 v = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1175 // LordHavoc: calc lightmap data offset for vertex lighting to use
1178 iu = bound(0, iu, smax);
1179 iv = bound(0, iv, tmax);
1180 u = u * uscale + ubase;
1181 v = v * vscale + vbase;
1183 mesh->verts[i * 4 + 0] = in[0];
1184 mesh->verts[i * 4 + 1] = in[1];
1185 mesh->verts[i * 4 + 2] = in[2];
1186 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1187 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1188 mesh->uv[i * 2 + 0] = u;
1189 mesh->uv[i * 2 + 1] = v;
1190 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1191 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1192 mesh->lightmapoffsets[i] = ((iv * (smax+1) + iu) * 3);
1196 void Mod_GenerateVertexMesh (msurface_t *surf)
1202 surf->lightmaptexturestride = 0;
1203 surf->lightmaptexture = NULL;
1205 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));
1206 mesh->numverts = surf->poly_numverts;
1207 mesh->numtriangles = surf->poly_numverts - 2;
1208 mesh->verts = (float *)(mesh + 1);
1209 mesh->st = mesh->verts + mesh->numverts * 4;
1210 mesh->ab = mesh->st + mesh->numverts * 2;
1211 mesh->index = (int *)(mesh->ab + mesh->numverts * 2);
1213 index = mesh->index;
1214 for (i = 0;i < mesh->numtriangles;i++)
1221 for (i = 0, in = surf->poly_verts;i < mesh->numverts;i++, in += 3)
1223 s = (DotProduct (in, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1224 t = (DotProduct (in, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1225 mesh->verts[i * 4 + 0] = in[0];
1226 mesh->verts[i * 4 + 1] = in[1];
1227 mesh->verts[i * 4 + 2] = in[2];
1228 mesh->st[i * 2 + 0] = s / surf->texinfo->texture->width;
1229 mesh->st[i * 2 + 1] = t / surf->texinfo->texture->height;
1230 mesh->ab[i * 2 + 0] = s * (1.0f / 16.0f);
1231 mesh->ab[i * 2 + 1] = t * (1.0f / 16.0f);
1235 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1238 float *vec, *vert, mins[3], maxs[3];
1240 // convert edges back to a normal polygon
1241 surf->poly_numverts = surf->numedges;
1242 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1243 for (i = 0;i < surf->numedges;i++)
1245 lindex = loadmodel->surfedges[surf->firstedge + i];
1247 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1249 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1250 VectorCopy (vec, vert);
1253 vert = surf->poly_verts;
1254 VectorCopy(vert, mins);
1255 VectorCopy(vert, maxs);
1257 for (i = 1;i < surf->poly_numverts;i++)
1259 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1260 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1261 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1264 VectorCopy(mins, surf->poly_mins);
1265 VectorCopy(maxs, surf->poly_maxs);
1266 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1267 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1268 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1276 static void Mod_LoadFaces (lump_t *l)
1280 int i, count, surfnum, planenum, ssize, tsize;
1282 in = (void *)(mod_base + l->fileofs);
1283 if (l->filelen % sizeof(*in))
1284 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1285 count = l->filelen / sizeof(*in);
1286 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1288 loadmodel->surfaces = out;
1289 loadmodel->numsurfaces = count;
1291 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
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(msurface_t *));
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] = loadmodel->surfaces + 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;