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(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;
1290 loadmodel->surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1291 loadmodel->surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1293 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1295 out->number = surfnum;
1296 // FIXME: validate edges, texinfo, etc?
1297 out->firstedge = LittleLong(in->firstedge);
1298 out->numedges = LittleShort(in->numedges);
1299 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1300 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1302 i = LittleShort (in->texinfo);
1303 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1304 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1305 out->texinfo = loadmodel->texinfo + i;
1306 out->flags = out->texinfo->texture->flags;
1308 planenum = LittleShort(in->planenum);
1309 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1310 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1312 if (LittleShort(in->side))
1313 out->flags |= SURF_PLANEBACK;
1315 out->plane = loadmodel->planes + planenum;
1317 // clear lightmap (filled in later)
1318 out->lightmaptexture = NULL;
1320 // force lightmap upload on first time seeing the surface
1321 out->cached_dlight = true;
1322 out->cached_ambient = -1000;
1323 out->cached_lightscalebit = -1000;
1325 CalcSurfaceExtents (out);
1327 ssize = (out->extents[0] >> 4) + 1;
1328 tsize = (out->extents[1] >> 4) + 1;
1331 for (i = 0;i < MAXLIGHTMAPS;i++)
1332 out->styles[i] = in->styles[i];
1333 i = LittleLong(in->lightofs);
1335 out->samples = NULL;
1336 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1337 out->samples = loadmodel->lightdata + i;
1338 else // LordHavoc: white lighting (bsp version 29)
1339 out->samples = loadmodel->lightdata + (i * 3);
1341 Mod_GenerateSurfacePolygon(out);
1342 if (out->texinfo->texture->shader == &Cshader_wall_lightmap)
1344 if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1345 Host_Error ("Bad surface extents");
1346 Mod_GenerateWallMesh (out, false);
1347 // stainmap for permanent marks on walls
1348 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1350 memset(out->stainsamples, 255, ssize * tsize * 3);
1353 Mod_GenerateVertexMesh (out);
1362 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1364 node->parent = parent;
1365 if (node->contents < 0)
1367 Mod_SetParent (node->children[0], node);
1368 Mod_SetParent (node->children[1], node);
1376 static void Mod_LoadNodes (lump_t *l)
1382 in = (void *)(mod_base + l->fileofs);
1383 if (l->filelen % sizeof(*in))
1384 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1385 count = l->filelen / sizeof(*in);
1386 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1388 loadmodel->nodes = out;
1389 loadmodel->numnodes = count;
1391 for ( i=0 ; i<count ; i++, in++, out++)
1393 for (j=0 ; j<3 ; j++)
1395 out->mins[j] = LittleShort (in->mins[j]);
1396 out->maxs[j] = LittleShort (in->maxs[j]);
1399 p = LittleLong(in->planenum);
1400 out->plane = loadmodel->planes + p;
1402 out->firstsurface = LittleShort (in->firstface);
1403 out->numsurfaces = LittleShort (in->numfaces);
1405 for (j=0 ; j<2 ; j++)
1407 p = LittleShort (in->children[j]);
1409 out->children[j] = loadmodel->nodes + p;
1411 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1415 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1423 static void Mod_LoadLeafs (lump_t *l)
1429 in = (void *)(mod_base + l->fileofs);
1430 if (l->filelen % sizeof(*in))
1431 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1432 count = l->filelen / sizeof(*in);
1433 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1435 loadmodel->leafs = out;
1436 loadmodel->numleafs = count;
1438 for ( i=0 ; i<count ; i++, in++, out++)
1440 for (j=0 ; j<3 ; j++)
1442 out->mins[j] = LittleShort (in->mins[j]);
1443 out->maxs[j] = LittleShort (in->maxs[j]);
1446 p = LittleLong(in->contents);
1449 out->firstmarksurface = loadmodel->marksurfaces +
1450 LittleShort(in->firstmarksurface);
1451 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1453 p = LittleLong(in->visofs);
1455 out->compressed_vis = NULL;
1457 out->compressed_vis = loadmodel->visdata + p;
1459 for (j=0 ; j<4 ; j++)
1460 out->ambient_sound_level[j] = in->ambient_level[j];
1462 // FIXME: Insert caustics here
1471 static void Mod_LoadClipnodes (lump_t *l)
1473 dclipnode_t *in, *out;
1477 in = (void *)(mod_base + l->fileofs);
1478 if (l->filelen % sizeof(*in))
1479 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1480 count = l->filelen / sizeof(*in);
1481 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1483 loadmodel->clipnodes = out;
1484 loadmodel->numclipnodes = count;
1486 if (loadmodel->ishlbsp)
1488 hull = &loadmodel->hulls[1];
1489 hull->clipnodes = out;
1490 hull->firstclipnode = 0;
1491 hull->lastclipnode = count-1;
1492 hull->planes = loadmodel->planes;
1493 hull->clip_mins[0] = -16;
1494 hull->clip_mins[1] = -16;
1495 hull->clip_mins[2] = -36;
1496 hull->clip_maxs[0] = 16;
1497 hull->clip_maxs[1] = 16;
1498 hull->clip_maxs[2] = 36;
1499 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1501 hull = &loadmodel->hulls[2];
1502 hull->clipnodes = out;
1503 hull->firstclipnode = 0;
1504 hull->lastclipnode = count-1;
1505 hull->planes = loadmodel->planes;
1506 hull->clip_mins[0] = -32;
1507 hull->clip_mins[1] = -32;
1508 hull->clip_mins[2] = -32;
1509 hull->clip_maxs[0] = 32;
1510 hull->clip_maxs[1] = 32;
1511 hull->clip_maxs[2] = 32;
1512 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1514 hull = &loadmodel->hulls[3];
1515 hull->clipnodes = out;
1516 hull->firstclipnode = 0;
1517 hull->lastclipnode = count-1;
1518 hull->planes = loadmodel->planes;
1519 hull->clip_mins[0] = -16;
1520 hull->clip_mins[1] = -16;
1521 hull->clip_mins[2] = -18;
1522 hull->clip_maxs[0] = 16;
1523 hull->clip_maxs[1] = 16;
1524 hull->clip_maxs[2] = 18;
1525 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1529 hull = &loadmodel->hulls[1];
1530 hull->clipnodes = out;
1531 hull->firstclipnode = 0;
1532 hull->lastclipnode = count-1;
1533 hull->planes = loadmodel->planes;
1534 hull->clip_mins[0] = -16;
1535 hull->clip_mins[1] = -16;
1536 hull->clip_mins[2] = -24;
1537 hull->clip_maxs[0] = 16;
1538 hull->clip_maxs[1] = 16;
1539 hull->clip_maxs[2] = 32;
1540 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1542 hull = &loadmodel->hulls[2];
1543 hull->clipnodes = out;
1544 hull->firstclipnode = 0;
1545 hull->lastclipnode = count-1;
1546 hull->planes = loadmodel->planes;
1547 hull->clip_mins[0] = -32;
1548 hull->clip_mins[1] = -32;
1549 hull->clip_mins[2] = -24;
1550 hull->clip_maxs[0] = 32;
1551 hull->clip_maxs[1] = 32;
1552 hull->clip_maxs[2] = 64;
1553 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1556 for (i=0 ; i<count ; i++, out++, in++)
1558 out->planenum = LittleLong(in->planenum);
1559 out->children[0] = LittleShort(in->children[0]);
1560 out->children[1] = LittleShort(in->children[1]);
1561 if (out->children[0] >= count || out->children[1] >= count)
1562 Host_Error("Corrupt clipping hull (out of range child)\n");
1570 Duplicate the drawing hull structure as a clipping hull
1573 static void Mod_MakeHull0 (void)
1580 hull = &loadmodel->hulls[0];
1582 in = loadmodel->nodes;
1583 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1585 hull->clipnodes = out;
1586 hull->firstclipnode = 0;
1587 hull->lastclipnode = loadmodel->numnodes - 1;
1588 hull->planes = loadmodel->planes;
1590 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1592 out->planenum = in->plane - loadmodel->planes;
1593 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1594 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1600 Mod_LoadMarksurfaces
1603 static void Mod_LoadMarksurfaces (lump_t *l)
1608 in = (void *)(mod_base + l->fileofs);
1609 if (l->filelen % sizeof(*in))
1610 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1611 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1612 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(int));
1614 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1616 j = (unsigned) LittleShort(in[i]);
1617 if (j >= loadmodel->numsurfaces)
1618 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1619 loadmodel->marksurfaces[i] = j;
1628 static void Mod_LoadSurfedges (lump_t *l)
1633 in = (void *)(mod_base + l->fileofs);
1634 if (l->filelen % sizeof(*in))
1635 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1636 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1637 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1639 for (i = 0;i < loadmodel->numsurfedges;i++)
1640 loadmodel->surfedges[i] = LittleLong (in[i]);
1649 static void Mod_LoadPlanes (lump_t *l)
1655 in = (void *)(mod_base + l->fileofs);
1656 if (l->filelen % sizeof(*in))
1657 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1659 loadmodel->numplanes = l->filelen / sizeof(*in);
1660 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1662 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1664 out->normal[0] = LittleFloat (in->normal[0]);
1665 out->normal[1] = LittleFloat (in->normal[1]);
1666 out->normal[2] = LittleFloat (in->normal[2]);
1667 out->dist = LittleFloat (in->dist);
1673 #define MAX_POINTS_ON_WINDING 64
1679 double points[8][3]; // variable sized
1688 static winding_t *NewWinding (int points)
1693 if (points > MAX_POINTS_ON_WINDING)
1694 Sys_Error("NewWinding: too many points\n");
1696 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1697 w = Mem_Alloc(loadmodel->mempool, size);
1698 memset (w, 0, size);
1703 static void FreeWinding (winding_t *w)
1713 static winding_t *BaseWindingForPlane (mplane_t *p)
1715 double org[3], vright[3], vup[3], normal[3];
1718 VectorCopy(p->normal, normal);
1719 VectorVectorsDouble(normal, vright, vup);
1721 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1722 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1724 // project a really big axis aligned box onto the plane
1727 VectorScale (p->normal, p->dist, org);
1729 VectorSubtract (org, vright, w->points[0]);
1730 VectorAdd (w->points[0], vup, w->points[0]);
1732 VectorAdd (org, vright, w->points[1]);
1733 VectorAdd (w->points[1], vup, w->points[1]);
1735 VectorAdd (org, vright, w->points[2]);
1736 VectorSubtract (w->points[2], vup, w->points[2]);
1738 VectorSubtract (org, vright, w->points[3]);
1739 VectorSubtract (w->points[3], vup, w->points[3]);
1750 Clips the winding to the plane, returning the new winding on the positive side
1751 Frees the input winding.
1752 If keepon is true, an exactly on-plane winding will be saved, otherwise
1753 it will be clipped away.
1756 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1758 double dists[MAX_POINTS_ON_WINDING + 1];
1759 int sides[MAX_POINTS_ON_WINDING + 1];
1768 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1770 // determine sides for each point
1771 for (i = 0;i < in->numpoints;i++)
1773 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1774 if (dot > ON_EPSILON)
1775 sides[i] = SIDE_FRONT;
1776 else if (dot < -ON_EPSILON)
1777 sides[i] = SIDE_BACK;
1782 sides[i] = sides[0];
1783 dists[i] = dists[0];
1785 if (keepon && !counts[0] && !counts[1])
1796 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1797 if (maxpts > MAX_POINTS_ON_WINDING)
1798 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1800 neww = NewWinding (maxpts);
1802 for (i = 0;i < in->numpoints;i++)
1804 if (neww->numpoints >= maxpts)
1805 Sys_Error ("ClipWinding: points exceeded estimate");
1809 if (sides[i] == SIDE_ON)
1811 VectorCopy (p1, neww->points[neww->numpoints]);
1816 if (sides[i] == SIDE_FRONT)
1818 VectorCopy (p1, neww->points[neww->numpoints]);
1822 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1825 // generate a split point
1826 p2 = in->points[(i+1)%in->numpoints];
1828 dot = dists[i] / (dists[i]-dists[i+1]);
1829 for (j = 0;j < 3;j++)
1830 { // avoid round off error when possible
1831 if (split->normal[j] == 1)
1832 mid[j] = split->dist;
1833 else if (split->normal[j] == -1)
1834 mid[j] = -split->dist;
1836 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1839 VectorCopy (mid, neww->points[neww->numpoints]);
1843 // free the original winding
1854 Divides a winding by a plane, producing one or two windings. The
1855 original winding is not damaged or freed. If only on one side, the
1856 returned winding will be the input winding. If on both sides, two
1857 new windings will be created.
1860 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1862 double dists[MAX_POINTS_ON_WINDING + 1];
1863 int sides[MAX_POINTS_ON_WINDING + 1];
1872 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1874 // determine sides for each point
1875 for (i = 0;i < in->numpoints;i++)
1877 dot = DotProduct (in->points[i], split->normal);
1880 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1881 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1882 else sides[i] = SIDE_ON;
1885 sides[i] = sides[0];
1886 dists[i] = dists[0];
1888 *front = *back = NULL;
1901 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1903 if (maxpts > MAX_POINTS_ON_WINDING)
1904 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1906 *front = f = NewWinding (maxpts);
1907 *back = b = NewWinding (maxpts);
1909 for (i = 0;i < in->numpoints;i++)
1911 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
1912 Sys_Error ("DivideWinding: points exceeded estimate");
1916 if (sides[i] == SIDE_ON)
1918 VectorCopy (p1, f->points[f->numpoints]);
1920 VectorCopy (p1, b->points[b->numpoints]);
1925 if (sides[i] == SIDE_FRONT)
1927 VectorCopy (p1, f->points[f->numpoints]);
1930 else if (sides[i] == SIDE_BACK)
1932 VectorCopy (p1, b->points[b->numpoints]);
1936 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1939 // generate a split point
1940 p2 = in->points[(i+1)%in->numpoints];
1942 dot = dists[i] / (dists[i]-dists[i+1]);
1943 for (j = 0;j < 3;j++)
1944 { // avoid round off error when possible
1945 if (split->normal[j] == 1)
1946 mid[j] = split->dist;
1947 else if (split->normal[j] == -1)
1948 mid[j] = -split->dist;
1950 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1953 VectorCopy (mid, f->points[f->numpoints]);
1955 VectorCopy (mid, b->points[b->numpoints]);
1960 typedef struct portal_s
1963 mnode_t *nodes[2]; // [0] = front side of plane
1964 struct portal_s *next[2];
1966 struct portal_s *chain; // all portals are linked into a list
1970 static portal_t *portalchain;
1977 static portal_t *AllocPortal (void)
1980 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
1981 p->chain = portalchain;
1986 static void FreePortal(portal_t *p)
1991 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
1993 // calculate children first
1994 if (node->children[0]->contents >= 0)
1995 Mod_RecursiveRecalcNodeBBox(node->children[0]);
1996 if (node->children[1]->contents >= 0)
1997 Mod_RecursiveRecalcNodeBBox(node->children[1]);
1999 // make combined bounding box from children
2000 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2001 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2002 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2003 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2004 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2005 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2008 static void Mod_FinalizePortals(void)
2010 int i, j, numportals, numpoints;
2011 portal_t *p, *pnext;
2014 mleaf_t *leaf, *endleaf;
2017 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2018 leaf = loadmodel->leafs;
2019 endleaf = leaf + loadmodel->numleafs;
2020 for (;leaf < endleaf;leaf++)
2022 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2023 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2030 for (i = 0;i < 2;i++)
2032 leaf = (mleaf_t *)p->nodes[i];
2034 for (j = 0;j < w->numpoints;j++)
2036 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2037 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2038 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2039 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2040 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2041 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2048 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2050 // tally up portal and point counts
2056 // note: this check must match the one below or it will usually corrupt memory
2057 // 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
2058 if (p->winding && p->nodes[0] != p->nodes[1]
2059 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2060 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2063 numpoints += p->winding->numpoints * 2;
2067 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2068 loadmodel->numportals = numportals;
2069 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2070 loadmodel->numportalpoints = numpoints;
2071 // clear all leaf portal chains
2072 for (i = 0;i < loadmodel->numleafs;i++)
2073 loadmodel->leafs[i].portals = NULL;
2074 // process all portals in the global portal chain, while freeing them
2075 portal = loadmodel->portals;
2076 point = loadmodel->portalpoints;
2085 // note: this check must match the one above or it will usually corrupt memory
2086 // 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
2087 if (p->nodes[0] != p->nodes[1]
2088 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2089 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2091 // first make the back to front portal (forward portal)
2092 portal->points = point;
2093 portal->numpoints = p->winding->numpoints;
2094 portal->plane.dist = p->plane.dist;
2095 VectorCopy(p->plane.normal, portal->plane.normal);
2096 portal->here = (mleaf_t *)p->nodes[1];
2097 portal->past = (mleaf_t *)p->nodes[0];
2099 for (j = 0;j < portal->numpoints;j++)
2101 VectorCopy(p->winding->points[j], point->position);
2104 PlaneClassify(&portal->plane);
2106 // link into leaf's portal chain
2107 portal->next = portal->here->portals;
2108 portal->here->portals = portal;
2110 // advance to next portal
2113 // then make the front to back portal (backward portal)
2114 portal->points = point;
2115 portal->numpoints = p->winding->numpoints;
2116 portal->plane.dist = -p->plane.dist;
2117 VectorNegate(p->plane.normal, portal->plane.normal);
2118 portal->here = (mleaf_t *)p->nodes[0];
2119 portal->past = (mleaf_t *)p->nodes[1];
2121 for (j = portal->numpoints - 1;j >= 0;j--)
2123 VectorCopy(p->winding->points[j], point->position);
2126 PlaneClassify(&portal->plane);
2128 // link into leaf's portal chain
2129 portal->next = portal->here->portals;
2130 portal->here->portals = portal;
2132 // advance to next portal
2135 FreeWinding(p->winding);
2147 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2150 Host_Error ("AddPortalToNodes: NULL front node");
2152 Host_Error ("AddPortalToNodes: NULL back node");
2153 if (p->nodes[0] || p->nodes[1])
2154 Host_Error ("AddPortalToNodes: already included");
2155 // 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
2157 p->nodes[0] = front;
2158 p->next[0] = (portal_t *)front->portals;
2159 front->portals = (mportal_t *)p;
2162 p->next[1] = (portal_t *)back->portals;
2163 back->portals = (mportal_t *)p;
2168 RemovePortalFromNode
2171 static void RemovePortalFromNodes(portal_t *portal)
2175 void **portalpointer;
2177 for (i = 0;i < 2;i++)
2179 node = portal->nodes[i];
2181 portalpointer = (void **) &node->portals;
2186 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2190 if (portal->nodes[0] == node)
2192 *portalpointer = portal->next[0];
2193 portal->nodes[0] = NULL;
2195 else if (portal->nodes[1] == node)
2197 *portalpointer = portal->next[1];
2198 portal->nodes[1] = NULL;
2201 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2205 if (t->nodes[0] == node)
2206 portalpointer = (void **) &t->next[0];
2207 else if (t->nodes[1] == node)
2208 portalpointer = (void **) &t->next[1];
2210 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2215 static void Mod_RecursiveNodePortals (mnode_t *node)
2218 mnode_t *front, *back, *other_node;
2219 mplane_t clipplane, *plane;
2220 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2221 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2223 // if a leaf, we're done
2227 plane = node->plane;
2229 front = node->children[0];
2230 back = node->children[1];
2232 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2234 // create the new portal by generating a polygon for the node plane,
2235 // and clipping it by all of the other portals (which came from nodes above this one)
2236 nodeportal = AllocPortal ();
2237 nodeportal->plane = *node->plane;
2239 nodeportalwinding = BaseWindingForPlane (node->plane);
2240 side = 0; // shut up compiler warning
2241 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2243 clipplane = portal->plane;
2244 if (portal->nodes[0] == portal->nodes[1])
2245 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2246 if (portal->nodes[0] == node)
2248 else if (portal->nodes[1] == node)
2250 clipplane.dist = -clipplane.dist;
2251 VectorNegate (clipplane.normal, clipplane.normal);
2255 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2257 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2258 if (!nodeportalwinding)
2260 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2265 if (nodeportalwinding)
2267 // if the plane was not clipped on all sides, there was an error
2268 nodeportal->winding = nodeportalwinding;
2269 AddPortalToNodes (nodeportal, front, back);
2272 // split the portals of this node along this node's plane and assign them to the children of this node
2273 // (migrating the portals downward through the tree)
2274 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2276 if (portal->nodes[0] == portal->nodes[1])
2277 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2278 if (portal->nodes[0] == node)
2280 else if (portal->nodes[1] == node)
2283 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2284 nextportal = portal->next[side];
2286 other_node = portal->nodes[!side];
2287 RemovePortalFromNodes (portal);
2289 // cut the portal into two portals, one on each side of the node plane
2290 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2295 AddPortalToNodes (portal, back, other_node);
2297 AddPortalToNodes (portal, other_node, back);
2303 AddPortalToNodes (portal, front, other_node);
2305 AddPortalToNodes (portal, other_node, front);
2309 // the winding is split
2310 splitportal = AllocPortal ();
2311 temp = splitportal->chain;
2312 *splitportal = *portal;
2313 splitportal->chain = temp;
2314 splitportal->winding = backwinding;
2315 FreeWinding (portal->winding);
2316 portal->winding = frontwinding;
2320 AddPortalToNodes (portal, front, other_node);
2321 AddPortalToNodes (splitportal, back, other_node);
2325 AddPortalToNodes (portal, other_node, front);
2326 AddPortalToNodes (splitportal, other_node, back);
2330 Mod_RecursiveNodePortals(front);
2331 Mod_RecursiveNodePortals(back);
2335 static void Mod_MakePortals(void)
2338 Mod_RecursiveNodePortals (loadmodel->nodes);
2339 Mod_FinalizePortals();
2347 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2352 mempool_t *mainmempool;
2355 mod->type = mod_brush;
2357 header = (dheader_t *)buffer;
2359 i = LittleLong (header->version);
2360 if (i != BSPVERSION && i != 30)
2361 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2362 mod->ishlbsp = i == 30;
2363 if (loadmodel->isworldmodel)
2365 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2366 // until we get a texture for it...
2370 // swap all the lumps
2371 mod_base = (qbyte *)header;
2373 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2374 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2378 // store which lightmap format to use
2379 mod->lightmaprgba = r_lightmaprgba.integer;
2381 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2382 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2383 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2384 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2385 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2386 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2387 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2388 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2389 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2390 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2391 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2392 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2393 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2394 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2395 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2400 mod->numframes = 2; // regular and alternate animation
2402 mainmempool = mod->mempool;
2403 loadname = mod->name;
2405 Mod_LoadLightList ();
2408 // set up the submodels (FIXME: this is confusing)
2410 for (i = 0;i < mod->numsubmodels;i++)
2413 float dist, modelyawradius, modelradius, *vec;
2416 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2417 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2421 bm = &mod->submodels[i];
2423 mod->hulls[0].firstclipnode = bm->headnode[0];
2424 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2426 mod->hulls[j].firstclipnode = bm->headnode[j];
2427 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2430 mod->firstmodelsurface = bm->firstface;
2431 mod->nummodelsurfaces = bm->numfaces;
2433 mod->DrawSky = NULL;
2434 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2435 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2437 // we only need to have a drawsky function if it is used (usually only on world model)
2438 if (surf->texinfo->texture->shader == &Cshader_sky)
2439 mod->DrawSky = R_DrawBrushModelSky;
2440 for (k = 0;k < surf->numedges;k++)
2442 l = mod->surfedges[k + surf->firstedge];
2444 vec = mod->vertexes[mod->edges[l].v[0]].position;
2446 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2447 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2448 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2449 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2450 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2451 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2452 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2453 dist = vec[0]*vec[0]+vec[1]*vec[1];
2454 if (modelyawradius < dist)
2455 modelyawradius = dist;
2456 dist += vec[2]*vec[2];
2457 if (modelradius < dist)
2461 modelyawradius = sqrt(modelyawradius);
2462 modelradius = sqrt(modelradius);
2463 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2464 mod->yawmins[2] = mod->normalmins[2];
2465 mod->yawmaxs[2] = mod->normalmaxs[2];
2466 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2467 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2468 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2469 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2471 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2472 VectorClear(mod->normalmins);
2473 VectorClear(mod->normalmaxs);
2474 VectorClear(mod->yawmins);
2475 VectorClear(mod->yawmaxs);
2476 VectorClear(mod->rotatedmins);
2477 VectorClear(mod->rotatedmaxs);
2480 mod->numleafs = bm->visleafs;
2482 mod->Draw = R_DrawBrushModelNormal;
2483 mod->DrawShadow = NULL;
2485 // LordHavoc: only register submodels if it is the world
2486 // (prevents bsp models from replacing world submodels)
2487 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2490 // duplicate the basic information
2491 sprintf (name, "*%i", i+1);
2492 loadmodel = Mod_FindName (name);
2494 strcpy (loadmodel->name, name);
2495 // textures and memory belong to the main model
2496 loadmodel->texturepool = NULL;
2497 loadmodel->mempool = NULL;