2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 // note: model_shared.c sets up r_notexture, and r_surf_notexture
25 qbyte mod_novis[(MAX_MAP_LEAFS + 7)/ 8];
27 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
28 cvar_t halflifebsp = {0, "halflifebsp", "0"};
29 cvar_t r_novis = {0, "r_novis", "0"};
30 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
31 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
32 cvar_t r_vertexsurfacesthreshold = {CVAR_SAVE, "r_vertexsurfacesthreshold", "0"};
33 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
34 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
36 #define NUM_DETAILTEXTURES 1
37 static rtexture_t *detailtextures[NUM_DETAILTEXTURES];
38 static rtexturepool_t *detailtexturepool;
45 void Mod_BrushInit (void)
47 // Cvar_RegisterVariable(&r_subdivide_size);
48 Cvar_RegisterVariable(&halflifebsp);
49 Cvar_RegisterVariable(&r_novis);
50 Cvar_RegisterVariable(&r_miplightmaps);
51 Cvar_RegisterVariable(&r_lightmaprgba);
52 Cvar_RegisterVariable(&r_vertexsurfacesthreshold);
53 Cvar_RegisterVariable(&r_nosurftextures);
54 Cvar_RegisterVariable(&r_sortsurfaces);
55 memset(mod_novis, 0xff, sizeof(mod_novis));
58 void Mod_BrushStartup (void)
61 float vc[3], vx[3], vy[3], vn[3], lightdir[3];
62 #define DETAILRESOLUTION 256
63 qbyte data[DETAILRESOLUTION][DETAILRESOLUTION][4], noise[DETAILRESOLUTION][DETAILRESOLUTION];
64 detailtexturepool = R_AllocTexturePool();
68 VectorNormalize(lightdir);
69 for (i = 0;i < NUM_DETAILTEXTURES;i++)
71 fractalnoise(&noise[0][0], DETAILRESOLUTION, DETAILRESOLUTION >> 4);
72 for (y = 0;y < DETAILRESOLUTION;y++)
74 for (x = 0;x < DETAILRESOLUTION;x++)
78 vc[2] = noise[y][x] * (1.0f / 32.0f);
81 vx[2] = noise[y][(x + 1) % DETAILRESOLUTION] * (1.0f / 32.0f);
84 vy[2] = noise[(y + 1) % DETAILRESOLUTION][x] * (1.0f / 32.0f);
85 VectorSubtract(vx, vc, vx);
86 VectorSubtract(vy, vc, vy);
87 CrossProduct(vx, vy, vn);
89 light = 128 - DotProduct(vn, lightdir) * 128;
90 light = bound(0, light, 255);
91 data[y][x][0] = data[y][x][1] = data[y][x][2] = light;
95 detailtextures[i] = R_LoadTexture(detailtexturepool, va("detailtexture%i", i), DETAILRESOLUTION, DETAILRESOLUTION, &data[0][0][0], TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_PRECACHE);
99 void Mod_BrushShutdown (void)
102 for (i = 0;i < NUM_DETAILTEXTURES;i++)
103 R_FreeTexture(detailtextures[i]);
104 R_FreeTexturePool(&detailtexturepool);
112 mleaf_t *Mod_PointInLeaf (const vec3_t p, model_t *model)
119 Mod_CheckLoaded(model);
121 // LordHavoc: modified to start at first clip node,
122 // in other words: first node of the (sub)model
123 node = model->nodes + model->hulls[0].firstclipnode;
124 while (node->contents == 0)
125 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
127 return (mleaf_t *)node;
130 int Mod_PointContents (const vec3_t p, model_t *model)
135 return CONTENTS_EMPTY;
137 Mod_CheckLoaded(model);
139 // LordHavoc: modified to start at first clip node,
140 // in other words: first node of the (sub)model
141 node = model->nodes + model->hulls[0].firstclipnode;
142 while (node->contents == 0)
143 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
145 return ((mleaf_t *)node)->contents;
148 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
150 if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
151 pos[0]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
152 pos[0]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
154 pos[1]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
155 pos[1]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
157 pos[2]-=1;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
158 pos[2]+=2;if (Mod_PointContents(pos, mod) != CONTENTS_SOLID) return;
168 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
170 static qbyte decompressed[MAX_MAP_LEAFS/8];
175 row = (model->numleafs+7)>>3;
193 } while (out - decompressed < row);
198 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
200 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
202 return Mod_DecompressVis (leaf->compressed_vis, model);
210 static void Mod_LoadTextures (lump_t *l)
212 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
214 texture_t *tx, *tx2, *anims[10], *altanims[10];
216 qbyte *data, *mtdata, *data2;
219 loadmodel->textures = NULL;
224 m = (dmiptexlump_t *)(mod_base + l->fileofs);
226 m->nummiptex = LittleLong (m->nummiptex);
228 // add two slots for notexture walls and notexture liquids
229 loadmodel->numtextures = m->nummiptex + 2;
230 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
232 // fill out all slots with notexture
233 for (i = 0;i < loadmodel->numtextures;i++)
235 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
238 tx->texture = r_notexture;
239 if (i == loadmodel->numtextures - 1)
240 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
243 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
245 // LordHavoc: mostly rewritten map texture loader
246 for (i = 0;i < m->nummiptex;i++)
248 dofs[i] = LittleLong(dofs[i]);
249 if (dofs[i] == -1 || r_nosurftextures.integer)
251 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
253 // make sure name is no more than 15 characters
254 for (j = 0;dmiptex->name[j] && j < 15;j++)
255 name[j] = dmiptex->name[j];
258 mtwidth = LittleLong (dmiptex->width);
259 mtheight = LittleLong (dmiptex->height);
261 j = LittleLong (dmiptex->offsets[0]);
265 if (j < 40 || j + mtwidth * mtheight > l->filelen)
267 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
270 mtdata = (qbyte *)dmiptex + j;
273 if ((mtwidth & 15) || (mtheight & 15))
274 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
276 // LordHavoc: force all names to lowercase
277 for (j = 0;name[j];j++)
278 if (name[j] >= 'A' && name[j] <= 'Z')
279 name[j] += 'a' - 'A';
281 tx = loadmodel->textures[i];
282 strcpy(tx->name, name);
284 tx->height = mtheight;
286 tx->glowtexture = NULL;
287 tx->fogtexture = NULL;
291 sprintf(tx->name, "unnamed%i", i);
292 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
295 // LordHavoc: HL sky textures are entirely different than quake
296 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
298 if (loadmodel->isworldmodel)
300 data = loadimagepixels(tx->name, false, 0, 0);
303 if (image_width == 256 && image_height == 128)
311 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
313 R_InitSky (mtdata, 1);
316 else if (mtdata != NULL)
317 R_InitSky (mtdata, 1);
320 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
322 tx->fogtexture = image_masktex;
323 strcpy(name, tx->name);
324 strcat(name, "_glow");
325 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
329 if (loadmodel->ishlbsp)
331 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
334 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
335 if (R_TextureHasAlpha(tx->texture))
338 for (j = 0;j < image_width * image_height;j++)
339 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
340 strcpy(name, tx->name);
341 strcat(name, "_fog");
342 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
346 else if ((data = W_GetTexture(tx->name)))
348 // get the size from the wad texture
349 tx->width = image_width;
350 tx->height = image_height;
351 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
352 if (R_TextureHasAlpha(tx->texture))
355 for (j = 0;j < image_width * image_height;j++)
356 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
357 strcpy(name, tx->name);
358 strcat(name, "_fog");
359 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
367 tx->texture = r_notexture;
372 if (mtdata) // texture included
377 if (r_fullbrights.value && tx->name[0] != '*')
379 for (j = 0;j < tx->width*tx->height;j++)
381 if (data[j] >= 224) // fullbright
390 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
391 for (j = 0;j < tx->width*tx->height;j++)
392 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
393 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
394 strcpy(name, tx->name);
395 strcat(name, "_glow");
396 for (j = 0;j < tx->width*tx->height;j++)
397 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
398 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
402 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
404 else // no texture, and no external replacement texture was found
408 tx->texture = r_notexture;
413 if (tx->name[0] == '*')
415 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
416 // LordHavoc: some turbulent textures should be fullbright and solid
417 if (!strncmp(tx->name,"*lava",5)
418 || !strncmp(tx->name,"*teleport",9)
419 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
420 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
422 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
423 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
426 tx->flags |= SURF_LIGHTMAP;
427 if (!R_TextureHasAlpha(tx->texture))
428 tx->flags |= SURF_CLIPSOLID;
431 tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
434 // sequence the animations
435 for (i = 0;i < m->nummiptex;i++)
437 tx = loadmodel->textures[i];
438 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
440 if (tx->anim_total[0] || tx->anim_total[1])
441 continue; // already sequenced
443 // find the number of frames in the animation
444 memset (anims, 0, sizeof(anims));
445 memset (altanims, 0, sizeof(altanims));
447 for (j = i;j < m->nummiptex;j++)
449 tx2 = loadmodel->textures[j];
450 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
454 if (num >= '0' && num <= '9')
455 anims[num - '0'] = tx2;
456 else if (num >= 'a' && num <= 'j')
457 altanims[num - 'a'] = tx2;
459 Con_Printf ("Bad animating texture %s\n", tx->name);
463 for (j = 0;j < 10;j++)
470 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
473 for (j = 0;j < max;j++)
477 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
481 for (j = 0;j < altmax;j++)
485 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
494 // if there is no alternate animation, duplicate the primary
495 // animation into the alternate
497 for (k = 0;k < 10;k++)
498 altanims[k] = anims[k];
501 // link together the primary animation
502 for (j = 0;j < max;j++)
505 tx2->animated = true;
506 tx2->anim_total[0] = max;
507 tx2->anim_total[1] = altmax;
508 for (k = 0;k < 10;k++)
510 tx2->anim_frames[0][k] = anims[k];
511 tx2->anim_frames[1][k] = altanims[k];
515 // if there really is an alternate anim...
516 if (anims[0] != altanims[0])
518 // link together the alternate animation
519 for (j = 0;j < altmax;j++)
522 tx2->animated = true;
523 // the primary/alternate are reversed here
524 tx2->anim_total[0] = altmax;
525 tx2->anim_total[1] = max;
526 for (k = 0;k < 10;k++)
528 tx2->anim_frames[0][k] = altanims[k];
529 tx2->anim_frames[1][k] = anims[k];
541 static void Mod_LoadLighting (lump_t *l)
544 qbyte *in, *out, *data, d;
545 char litfilename[1024];
546 loadmodel->lightdata = NULL;
547 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
549 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
550 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
552 else // LordHavoc: bsp version 29 (normal white lighting)
554 // LordHavoc: hope is not lost yet, check for a .lit file to load
555 strcpy(litfilename, loadmodel->name);
556 COM_StripExtension(litfilename, litfilename);
557 strcat(litfilename, ".lit");
558 data = (qbyte*) COM_LoadFile (litfilename, false);
561 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
563 i = LittleLong(((int *)data)[1]);
566 Con_DPrintf("%s loaded", litfilename);
567 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
568 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
574 Con_Printf("Unknown .lit file version (%d)\n", i);
581 Con_Printf("Empty .lit file, ignoring\n");
583 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
587 // LordHavoc: oh well, expand the white lighting data
590 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
591 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
592 out = loadmodel->lightdata;
593 memcpy (in, mod_base + l->fileofs, l->filelen);
594 for (i = 0;i < l->filelen;i++)
604 void Mod_LoadLightList(void)
607 char lightsfilename[1024], *s, *t, *lightsstring;
610 strcpy(lightsfilename, loadmodel->name);
611 COM_StripExtension(lightsfilename, lightsfilename);
612 strcat(lightsfilename, ".lights");
613 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
619 while (*s && *s != '\n')
623 Mem_Free(lightsstring);
624 Host_Error("lights file must end with a newline\n");
629 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
632 while (*s && n < numlights)
635 while (*s && *s != '\n')
639 Mem_Free(lightsstring);
640 Host_Error("misparsed lights file!\n");
642 e = loadmodel->lights + n;
644 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);
648 Mem_Free(lightsstring);
649 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);
656 Mem_Free(lightsstring);
657 Host_Error("misparsed lights file!\n");
659 loadmodel->numlights = numlights;
660 Mem_Free(lightsstring);
670 static void Mod_LoadVisibility (lump_t *l)
672 loadmodel->visdata = NULL;
675 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
676 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
679 // used only for HalfLife maps
680 void Mod_ParseWadsFromEntityLump(char *data)
682 char key[128], value[4096];
687 data = COM_Parse(data);
690 if (com_token[0] != '{')
694 data = COM_Parse(data);
697 if (com_token[0] == '}')
698 break; // end of worldspawn
699 if (com_token[0] == '_')
700 strcpy(key, com_token + 1);
702 strcpy(key, com_token);
703 while (key[strlen(key)-1] == ' ') // remove trailing spaces
704 key[strlen(key)-1] = 0;
705 data = COM_Parse(data);
708 strcpy(value, com_token);
709 if (!strcmp("wad", key)) // for HalfLife maps
711 if (loadmodel->ishlbsp)
714 for (i = 0;i < 4096;i++)
715 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
721 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
722 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
724 else if (value[i] == ';' || value[i] == 0)
728 strcpy(wadname, "textures/");
729 strcat(wadname, &value[j]);
730 W_LoadTextureWadFile (wadname, false);
747 static void Mod_LoadEntities (lump_t *l)
749 loadmodel->entities = NULL;
752 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
753 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
754 if (loadmodel->ishlbsp)
755 Mod_ParseWadsFromEntityLump(loadmodel->entities);
764 static void Mod_LoadVertexes (lump_t *l)
770 in = (void *)(mod_base + l->fileofs);
771 if (l->filelen % sizeof(*in))
772 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
773 count = l->filelen / sizeof(*in);
774 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
776 loadmodel->vertexes = out;
777 loadmodel->numvertexes = count;
779 for ( i=0 ; i<count ; i++, in++, out++)
781 out->position[0] = LittleFloat (in->point[0]);
782 out->position[1] = LittleFloat (in->point[1]);
783 out->position[2] = LittleFloat (in->point[2]);
792 static void Mod_LoadSubmodels (lump_t *l)
798 in = (void *)(mod_base + l->fileofs);
799 if (l->filelen % sizeof(*in))
800 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
801 count = l->filelen / sizeof(*in);
802 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
804 loadmodel->submodels = out;
805 loadmodel->numsubmodels = count;
807 for ( i=0 ; i<count ; i++, in++, out++)
809 for (j=0 ; j<3 ; j++)
811 // spread the mins / maxs by a pixel
812 out->mins[j] = LittleFloat (in->mins[j]) - 1;
813 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
814 out->origin[j] = LittleFloat (in->origin[j]);
816 for (j=0 ; j<MAX_MAP_HULLS ; j++)
817 out->headnode[j] = LittleLong (in->headnode[j]);
818 out->visleafs = LittleLong (in->visleafs);
819 out->firstface = LittleLong (in->firstface);
820 out->numfaces = LittleLong (in->numfaces);
829 static void Mod_LoadEdges (lump_t *l)
835 in = (void *)(mod_base + l->fileofs);
836 if (l->filelen % sizeof(*in))
837 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
838 count = l->filelen / sizeof(*in);
839 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
841 loadmodel->edges = out;
842 loadmodel->numedges = count;
844 for ( i=0 ; i<count ; i++, in++, out++)
846 out->v[0] = (unsigned short)LittleShort(in->v[0]);
847 out->v[1] = (unsigned short)LittleShort(in->v[1]);
856 static void Mod_LoadTexinfo (lump_t *l)
860 int i, j, k, count, miptex;
862 in = (void *)(mod_base + l->fileofs);
863 if (l->filelen % sizeof(*in))
864 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
865 count = l->filelen / sizeof(*in);
866 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
868 loadmodel->texinfo = out;
869 loadmodel->numtexinfo = count;
871 for (i = 0;i < count;i++, in++, out++)
873 for (k = 0;k < 2;k++)
874 for (j = 0;j < 4;j++)
875 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
877 miptex = LittleLong (in->miptex);
878 out->flags = LittleLong (in->flags);
881 if (loadmodel->textures)
883 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
884 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
886 out->texture = loadmodel->textures[miptex];
888 if (out->texture == NULL)
890 // choose either the liquid notexture, or the normal notexture
891 if (out->flags & TEX_SPECIAL)
892 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
894 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
903 Fills in s->texturemins[] and s->extents[]
906 static void CalcSurfaceExtents (msurface_t *s)
908 float mins[2], maxs[2], val;
912 int bmins[2], bmaxs[2];
914 mins[0] = mins[1] = 999999999;
915 maxs[0] = maxs[1] = -999999999;
919 for (i=0 ; i<s->numedges ; i++)
921 e = loadmodel->surfedges[s->firstedge+i];
923 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
925 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
927 for (j=0 ; j<2 ; j++)
929 val = v->position[0] * tex->vecs[j][0] +
930 v->position[1] * tex->vecs[j][1] +
931 v->position[2] * tex->vecs[j][2] +
940 for (i=0 ; i<2 ; i++)
942 bmins[i] = floor(mins[i]/16);
943 bmaxs[i] = ceil(maxs[i]/16);
945 s->texturemins[i] = bmins[i] * 16;
946 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
951 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
956 mins[0] = mins[1] = mins[2] = 9999;
957 maxs[0] = maxs[1] = maxs[2] = -9999;
959 for (i = 0;i < numverts;i++)
961 for (j = 0;j < 3;j++, v++)
972 #define MAX_SUBDIVPOLYTRIANGLES 4096
973 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
975 static int subdivpolyverts, subdivpolytriangles;
976 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
977 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
979 static int subdivpolylookupvert(vec3_t v)
982 for (i = 0;i < subdivpolyverts;i++)
983 if (subdivpolyvert[i][0] == v[0]
984 && subdivpolyvert[i][1] == v[1]
985 && subdivpolyvert[i][2] == v[2])
987 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
988 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
989 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
990 return subdivpolyverts++;
993 static void SubdividePolygon (int numverts, float *verts)
995 int i, i1, i2, i3, f, b, c, p;
996 vec3_t mins, maxs, front[256], back[256];
997 float m, *pv, *cv, dist[256], frac;
1000 Host_Error ("SubdividePolygon: ran out of verts in buffer");
1002 BoundPoly (numverts, verts, mins, maxs);
1004 for (i = 0;i < 3;i++)
1006 m = (mins[i] + maxs[i]) * 0.5;
1007 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
1008 if (maxs[i] - m < 8)
1010 if (m - mins[i] < 8)
1014 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1015 dist[c] = cv[i] - m;
1018 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1022 VectorCopy (pv, front[f]);
1027 VectorCopy (pv, back[b]);
1030 if (dist[p] == 0 || dist[c] == 0)
1032 if ( (dist[p] > 0) != (dist[c] > 0) )
1035 frac = dist[p] / (dist[p] - dist[c]);
1036 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1037 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1038 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1044 SubdividePolygon (f, front[0]);
1045 SubdividePolygon (b, back[0]);
1049 i1 = subdivpolylookupvert(verts);
1050 i2 = subdivpolylookupvert(verts + 3);
1051 for (i = 2;i < numverts;i++)
1053 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1055 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1059 i3 = subdivpolylookupvert(verts + i * 3);
1060 subdivpolyindex[subdivpolytriangles][0] = i1;
1061 subdivpolyindex[subdivpolytriangles][1] = i2;
1062 subdivpolyindex[subdivpolytriangles][2] = i3;
1064 subdivpolytriangles++;
1070 Mod_GenerateWarpMesh
1072 Breaks a polygon up along axial 64 unit
1073 boundaries so that turbulent and sky warps
1074 can be done reasonably.
1077 void Mod_GenerateWarpMesh (msurface_t *surf)
1083 subdivpolytriangles = 0;
1084 subdivpolyverts = 0;
1085 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1086 if (subdivpolytriangles < 1)
1087 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1089 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1090 mesh->numverts = subdivpolyverts;
1091 mesh->numtriangles = subdivpolytriangles;
1092 mesh->vertex = (surfvertex_t *)(mesh + 1);
1093 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1094 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1096 for (i = 0;i < mesh->numtriangles;i++)
1097 for (j = 0;j < 3;j++)
1098 mesh->index[i*3+j] = subdivpolyindex[i][j];
1100 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1102 VectorCopy(subdivpolyvert[i], v->v);
1103 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1104 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1109 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1111 int i, is, it, *index, smax, tmax;
1116 smax = surf->extents[0] >> 4;
1117 tmax = surf->extents[1] >> 4;
1118 surf->lightmaptexturestride = 0;
1119 surf->lightmaptexture = NULL;
1121 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1122 mesh->numverts = surf->poly_numverts;
1123 mesh->numtriangles = surf->poly_numverts - 2;
1124 mesh->vertex = (surfvertex_t *)(mesh + 1);
1125 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1126 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1128 index = mesh->index;
1129 for (i = 0;i < mesh->numtriangles;i++)
1136 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1138 VectorCopy (in, out->v);
1140 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1141 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1143 out->ab[0] = s * (1.0f / 16.0f);
1144 out->ab[1] = t * (1.0f / 16.0f);
1146 out->st[0] = s / surf->texinfo->texture->width;
1147 out->st[1] = t / surf->texinfo->texture->height;
1149 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1150 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1152 // lightmap coordinates
1156 // LordHavoc: calc lightmap data offset for vertex lighting to use
1159 is = bound(0, is, smax);
1160 it = bound(0, it, tmax);
1161 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1165 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1167 int i, is, it, *index, smax, tmax;
1168 float *in, s, t, xbase, ybase, xscale, yscale;
1172 surf->flags |= SURF_LIGHTMAP;
1173 smax = surf->extents[0] >> 4;
1174 tmax = surf->extents[1] >> 4;
1175 if (r_miplightmaps.integer)
1177 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1178 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);
1182 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1183 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);
1185 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1186 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1187 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1189 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1190 mesh->numverts = surf->poly_numverts;
1191 mesh->numtriangles = surf->poly_numverts - 2;
1192 mesh->vertex = (surfvertex_t *)(mesh + 1);
1193 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1194 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1196 index = mesh->index;
1197 for (i = 0;i < mesh->numtriangles;i++)
1204 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1206 VectorCopy (in, out->v);
1208 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1209 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1211 out->ab[0] = s * (1.0f / 16.0f);
1212 out->ab[1] = t * (1.0f / 16.0f);
1214 out->st[0] = s / surf->texinfo->texture->width;
1215 out->st[1] = t / surf->texinfo->texture->height;
1217 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1218 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1220 // lightmap coordinates
1221 out->uv[0] = s * xscale + xbase;
1222 out->uv[1] = t * yscale + ybase;
1224 // LordHavoc: calc lightmap data offset for vertex lighting to use
1227 is = bound(0, is, smax);
1228 it = bound(0, it, tmax);
1229 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1233 void Mod_GenerateVertexMesh (msurface_t *surf)
1240 surf->lightmaptexturestride = 0;
1241 surf->lightmaptexture = NULL;
1243 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1244 mesh->numverts = surf->poly_numverts;
1245 mesh->numtriangles = surf->poly_numverts - 2;
1246 mesh->vertex = (surfvertex_t *)(mesh + 1);
1247 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1248 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1250 index = mesh->index;
1251 for (i = 0;i < mesh->numtriangles;i++)
1258 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1260 VectorCopy (in, out->v);
1261 s = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1262 t = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1263 out->st[0] = s / surf->texinfo->texture->width;
1264 out->st[1] = t / surf->texinfo->texture->height;
1265 out->ab[0] = s * (1.0f / 16.0f);
1266 out->ab[1] = t * (1.0f / 16.0f);
1270 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1273 float *vec, *vert, mins[3], maxs[3];
1275 // convert edges back to a normal polygon
1276 surf->poly_numverts = surf->numedges;
1277 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1278 for (i = 0;i < surf->numedges;i++)
1280 lindex = loadmodel->surfedges[surf->firstedge + i];
1282 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1284 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1285 VectorCopy (vec, vert);
1288 vert = surf->poly_verts;
1289 VectorCopy(vert, mins);
1290 VectorCopy(vert, maxs);
1292 for (i = 1;i < surf->poly_numverts;i++)
1294 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1295 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1296 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1299 VectorCopy(mins, surf->poly_mins);
1300 VectorCopy(maxs, surf->poly_maxs);
1301 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1302 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1303 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1306 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1308 int j, base, tricount, newvertexcount, *index, *vertexremap;
1309 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1310 if (s->mesh->numtriangles > 1000)
1312 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1317 while (base < s->mesh->numtriangles)
1319 tricount = s->mesh->numtriangles - base;
1320 if (tricount > 1000)
1322 index = s->mesh->index + base * 3;
1326 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1327 for (j = 0;j < tricount * 3;j++)
1328 if (vertexremap[index[j]] < 0)
1329 vertexremap[index[j]] = newvertexcount++;
1331 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1332 newmesh->chain = NULL;
1333 newmesh->numverts = newvertexcount;
1334 newmesh->numtriangles = tricount;
1335 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1336 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1337 for (j = 0;j < tricount * 3;j++)
1339 newmesh->index[j] = vertexremap[index[j]];
1340 // yes this copies the same vertex multiple times in many cases... but that's ok...
1341 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1344 oldmesh->chain = newmesh;
1346 firstmesh = newmesh;
1349 Mem_Free(vertexremap);
1351 s->mesh = firstmesh;
1360 static void Mod_LoadFaces (lump_t *l)
1364 int i, count, surfnum, planenum, ssize, tsize;
1366 in = (void *)(mod_base + l->fileofs);
1367 if (l->filelen % sizeof(*in))
1368 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1369 count = l->filelen / sizeof(*in);
1370 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1372 loadmodel->surfaces = out;
1373 loadmodel->numsurfaces = count;
1375 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1377 // FIXME: validate edges, texinfo, etc?
1378 out->firstedge = LittleLong(in->firstedge);
1379 out->numedges = LittleShort(in->numedges);
1380 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1381 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1383 i = LittleShort (in->texinfo);
1384 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1385 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1386 out->texinfo = loadmodel->texinfo + i;
1387 out->flags = out->texinfo->texture->flags;
1389 planenum = LittleShort(in->planenum);
1390 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1391 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1393 if (LittleShort(in->side))
1394 out->flags |= SURF_PLANEBACK;
1396 out->plane = loadmodel->planes + planenum;
1398 // clear lightmap (filled in later)
1399 out->lightmaptexture = NULL;
1401 // force lightmap upload on first time seeing the surface
1402 out->cached_dlight = true;
1403 out->cached_ambient = -1000;
1404 out->cached_lightscalebit = -1000;
1406 CalcSurfaceExtents (out);
1408 ssize = (out->extents[0] >> 4) + 1;
1409 tsize = (out->extents[1] >> 4) + 1;
1412 for (i = 0;i < MAXLIGHTMAPS;i++)
1413 out->styles[i] = in->styles[i];
1414 i = LittleLong(in->lightofs);
1416 out->samples = NULL;
1417 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1418 out->samples = loadmodel->lightdata + i;
1419 else // LordHavoc: white lighting (bsp version 29)
1420 out->samples = loadmodel->lightdata + (i * 3);
1422 Mod_GenerateSurfacePolygon(out);
1424 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1426 out->shader = &Cshader_sky;
1427 out->samples = NULL;
1428 Mod_GenerateVertexMesh (out);
1430 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1432 out->shader = &Cshader_water;
1433 out->samples = NULL;
1434 Mod_GenerateVertexMesh (out);
1438 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1439 out->flags |= SURF_CLIPSOLID;
1440 if (out->texinfo->flags & TEX_SPECIAL)
1442 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1443 out->shader = &Cshader_water;
1444 out->shader = &Cshader_water;
1445 out->samples = NULL;
1446 Mod_GenerateVertexMesh (out);
1448 else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1450 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1451 out->shader = &Cshader_wall_fullbright;
1452 out->samples = NULL;
1453 Mod_GenerateVertexMesh(out);
1457 // stainmap for permanent marks on walls
1458 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1460 memset(out->stainsamples, 255, ssize * tsize * 3);
1461 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1463 out->shader = &Cshader_wall_vertex;
1464 Mod_GenerateVertexLitMesh(out);
1468 out->shader = &Cshader_wall_lightmap;
1469 Mod_GenerateLightmappedMesh(out);
1473 Mod_SplitSurfMeshIfTooBig(out);
1477 static model_t *sortmodel;
1479 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1481 const msurface_t *a, *b;
1482 a = *((const msurface_t **)voida);
1483 b = *((const msurface_t **)voidb);
1484 if (a->shader != b->shader)
1485 return (qbyte *) a->shader - (qbyte *) b->shader;
1486 if (a->texinfo->texture != b->texinfo->texture);
1487 return a->texinfo->texture - b->texinfo->texture;
1491 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1495 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1496 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1497 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1499 if (r_sortsurfaces.integer)
1500 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1509 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1511 node->parent = parent;
1512 if (node->contents < 0)
1514 Mod_SetParent (node->children[0], node);
1515 Mod_SetParent (node->children[1], node);
1523 static void Mod_LoadNodes (lump_t *l)
1529 in = (void *)(mod_base + l->fileofs);
1530 if (l->filelen % sizeof(*in))
1531 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1532 count = l->filelen / sizeof(*in);
1533 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1535 loadmodel->nodes = out;
1536 loadmodel->numnodes = count;
1538 for ( i=0 ; i<count ; i++, in++, out++)
1540 for (j=0 ; j<3 ; j++)
1542 out->mins[j] = LittleShort (in->mins[j]);
1543 out->maxs[j] = LittleShort (in->maxs[j]);
1546 p = LittleLong(in->planenum);
1547 out->plane = loadmodel->planes + p;
1549 out->firstsurface = LittleShort (in->firstface);
1550 out->numsurfaces = LittleShort (in->numfaces);
1552 for (j=0 ; j<2 ; j++)
1554 p = LittleShort (in->children[j]);
1556 out->children[j] = loadmodel->nodes + p;
1558 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1562 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1570 static void Mod_LoadLeafs (lump_t *l)
1576 in = (void *)(mod_base + l->fileofs);
1577 if (l->filelen % sizeof(*in))
1578 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1579 count = l->filelen / sizeof(*in);
1580 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1582 loadmodel->leafs = out;
1583 loadmodel->numleafs = count;
1585 for ( i=0 ; i<count ; i++, in++, out++)
1587 for (j=0 ; j<3 ; j++)
1589 out->mins[j] = LittleShort (in->mins[j]);
1590 out->maxs[j] = LittleShort (in->maxs[j]);
1593 p = LittleLong(in->contents);
1596 out->firstmarksurface = loadmodel->marksurfaces +
1597 LittleShort(in->firstmarksurface);
1598 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1600 p = LittleLong(in->visofs);
1602 out->compressed_vis = NULL;
1604 out->compressed_vis = loadmodel->visdata + p;
1606 for (j=0 ; j<4 ; j++)
1607 out->ambient_sound_level[j] = in->ambient_level[j];
1609 // FIXME: Insert caustics here
1618 static void Mod_LoadClipnodes (lump_t *l)
1620 dclipnode_t *in, *out;
1624 in = (void *)(mod_base + l->fileofs);
1625 if (l->filelen % sizeof(*in))
1626 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1627 count = l->filelen / sizeof(*in);
1628 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1630 loadmodel->clipnodes = out;
1631 loadmodel->numclipnodes = count;
1633 if (loadmodel->ishlbsp)
1635 hull = &loadmodel->hulls[1];
1636 hull->clipnodes = out;
1637 hull->firstclipnode = 0;
1638 hull->lastclipnode = count-1;
1639 hull->planes = loadmodel->planes;
1640 hull->clip_mins[0] = -16;
1641 hull->clip_mins[1] = -16;
1642 hull->clip_mins[2] = -36;
1643 hull->clip_maxs[0] = 16;
1644 hull->clip_maxs[1] = 16;
1645 hull->clip_maxs[2] = 36;
1646 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1648 hull = &loadmodel->hulls[2];
1649 hull->clipnodes = out;
1650 hull->firstclipnode = 0;
1651 hull->lastclipnode = count-1;
1652 hull->planes = loadmodel->planes;
1653 hull->clip_mins[0] = -32;
1654 hull->clip_mins[1] = -32;
1655 hull->clip_mins[2] = -32;
1656 hull->clip_maxs[0] = 32;
1657 hull->clip_maxs[1] = 32;
1658 hull->clip_maxs[2] = 32;
1659 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1661 hull = &loadmodel->hulls[3];
1662 hull->clipnodes = out;
1663 hull->firstclipnode = 0;
1664 hull->lastclipnode = count-1;
1665 hull->planes = loadmodel->planes;
1666 hull->clip_mins[0] = -16;
1667 hull->clip_mins[1] = -16;
1668 hull->clip_mins[2] = -18;
1669 hull->clip_maxs[0] = 16;
1670 hull->clip_maxs[1] = 16;
1671 hull->clip_maxs[2] = 18;
1672 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1676 hull = &loadmodel->hulls[1];
1677 hull->clipnodes = out;
1678 hull->firstclipnode = 0;
1679 hull->lastclipnode = count-1;
1680 hull->planes = loadmodel->planes;
1681 hull->clip_mins[0] = -16;
1682 hull->clip_mins[1] = -16;
1683 hull->clip_mins[2] = -24;
1684 hull->clip_maxs[0] = 16;
1685 hull->clip_maxs[1] = 16;
1686 hull->clip_maxs[2] = 32;
1687 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1689 hull = &loadmodel->hulls[2];
1690 hull->clipnodes = out;
1691 hull->firstclipnode = 0;
1692 hull->lastclipnode = count-1;
1693 hull->planes = loadmodel->planes;
1694 hull->clip_mins[0] = -32;
1695 hull->clip_mins[1] = -32;
1696 hull->clip_mins[2] = -24;
1697 hull->clip_maxs[0] = 32;
1698 hull->clip_maxs[1] = 32;
1699 hull->clip_maxs[2] = 64;
1700 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1703 for (i=0 ; i<count ; i++, out++, in++)
1705 out->planenum = LittleLong(in->planenum);
1706 out->children[0] = LittleShort(in->children[0]);
1707 out->children[1] = LittleShort(in->children[1]);
1708 if (out->children[0] >= count || out->children[1] >= count)
1709 Host_Error("Corrupt clipping hull (out of range child)\n");
1717 Duplicate the drawing hull structure as a clipping hull
1720 static void Mod_MakeHull0 (void)
1727 hull = &loadmodel->hulls[0];
1729 in = loadmodel->nodes;
1730 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1732 hull->clipnodes = out;
1733 hull->firstclipnode = 0;
1734 hull->lastclipnode = loadmodel->numnodes - 1;
1735 hull->planes = loadmodel->planes;
1737 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1739 out->planenum = in->plane - loadmodel->planes;
1740 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1741 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1747 Mod_LoadMarksurfaces
1750 static void Mod_LoadMarksurfaces (lump_t *l)
1755 in = (void *)(mod_base + l->fileofs);
1756 if (l->filelen % sizeof(*in))
1757 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1758 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1759 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1761 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1763 j = (unsigned) LittleShort(in[i]);
1764 if (j >= loadmodel->numsurfaces)
1765 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1766 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1775 static void Mod_LoadSurfedges (lump_t *l)
1780 in = (void *)(mod_base + l->fileofs);
1781 if (l->filelen % sizeof(*in))
1782 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1783 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1784 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1786 for (i = 0;i < loadmodel->numsurfedges;i++)
1787 loadmodel->surfedges[i] = LittleLong (in[i]);
1796 static void Mod_LoadPlanes (lump_t *l)
1802 in = (void *)(mod_base + l->fileofs);
1803 if (l->filelen % sizeof(*in))
1804 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1806 loadmodel->numplanes = l->filelen / sizeof(*in);
1807 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1809 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1811 out->normal[0] = LittleFloat (in->normal[0]);
1812 out->normal[1] = LittleFloat (in->normal[1]);
1813 out->normal[2] = LittleFloat (in->normal[2]);
1814 out->dist = LittleFloat (in->dist);
1820 #define MAX_POINTS_ON_WINDING 64
1826 double points[8][3]; // variable sized
1835 static winding_t *NewWinding (int points)
1840 if (points > MAX_POINTS_ON_WINDING)
1841 Sys_Error("NewWinding: too many points\n");
1843 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1844 w = Mem_Alloc(loadmodel->mempool, size);
1845 memset (w, 0, size);
1850 static void FreeWinding (winding_t *w)
1860 static winding_t *BaseWindingForPlane (mplane_t *p)
1862 double org[3], vright[3], vup[3], normal[3];
1865 VectorCopy(p->normal, normal);
1866 VectorVectorsDouble(normal, vright, vup);
1868 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1869 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1871 // project a really big axis aligned box onto the plane
1874 VectorScale (p->normal, p->dist, org);
1876 VectorSubtract (org, vright, w->points[0]);
1877 VectorAdd (w->points[0], vup, w->points[0]);
1879 VectorAdd (org, vright, w->points[1]);
1880 VectorAdd (w->points[1], vup, w->points[1]);
1882 VectorAdd (org, vright, w->points[2]);
1883 VectorSubtract (w->points[2], vup, w->points[2]);
1885 VectorSubtract (org, vright, w->points[3]);
1886 VectorSubtract (w->points[3], vup, w->points[3]);
1897 Clips the winding to the plane, returning the new winding on the positive side
1898 Frees the input winding.
1899 If keepon is true, an exactly on-plane winding will be saved, otherwise
1900 it will be clipped away.
1903 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1905 double dists[MAX_POINTS_ON_WINDING + 1];
1906 int sides[MAX_POINTS_ON_WINDING + 1];
1915 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1917 // determine sides for each point
1918 for (i = 0;i < in->numpoints;i++)
1920 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1921 if (dot > ON_EPSILON)
1922 sides[i] = SIDE_FRONT;
1923 else if (dot < -ON_EPSILON)
1924 sides[i] = SIDE_BACK;
1929 sides[i] = sides[0];
1930 dists[i] = dists[0];
1932 if (keepon && !counts[0] && !counts[1])
1943 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1944 if (maxpts > MAX_POINTS_ON_WINDING)
1945 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1947 neww = NewWinding (maxpts);
1949 for (i = 0;i < in->numpoints;i++)
1951 if (neww->numpoints >= maxpts)
1952 Sys_Error ("ClipWinding: points exceeded estimate");
1956 if (sides[i] == SIDE_ON)
1958 VectorCopy (p1, neww->points[neww->numpoints]);
1963 if (sides[i] == SIDE_FRONT)
1965 VectorCopy (p1, neww->points[neww->numpoints]);
1969 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1972 // generate a split point
1973 p2 = in->points[(i+1)%in->numpoints];
1975 dot = dists[i] / (dists[i]-dists[i+1]);
1976 for (j = 0;j < 3;j++)
1977 { // avoid round off error when possible
1978 if (split->normal[j] == 1)
1979 mid[j] = split->dist;
1980 else if (split->normal[j] == -1)
1981 mid[j] = -split->dist;
1983 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1986 VectorCopy (mid, neww->points[neww->numpoints]);
1990 // free the original winding
2001 Divides a winding by a plane, producing one or two windings. The
2002 original winding is not damaged or freed. If only on one side, the
2003 returned winding will be the input winding. If on both sides, two
2004 new windings will be created.
2007 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2009 double dists[MAX_POINTS_ON_WINDING + 1];
2010 int sides[MAX_POINTS_ON_WINDING + 1];
2019 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2021 // determine sides for each point
2022 for (i = 0;i < in->numpoints;i++)
2024 dot = DotProduct (in->points[i], split->normal);
2027 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2028 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2029 else sides[i] = SIDE_ON;
2032 sides[i] = sides[0];
2033 dists[i] = dists[0];
2035 *front = *back = NULL;
2048 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2050 if (maxpts > MAX_POINTS_ON_WINDING)
2051 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2053 *front = f = NewWinding (maxpts);
2054 *back = b = NewWinding (maxpts);
2056 for (i = 0;i < in->numpoints;i++)
2058 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2059 Sys_Error ("DivideWinding: points exceeded estimate");
2063 if (sides[i] == SIDE_ON)
2065 VectorCopy (p1, f->points[f->numpoints]);
2067 VectorCopy (p1, b->points[b->numpoints]);
2072 if (sides[i] == SIDE_FRONT)
2074 VectorCopy (p1, f->points[f->numpoints]);
2077 else if (sides[i] == SIDE_BACK)
2079 VectorCopy (p1, b->points[b->numpoints]);
2083 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2086 // generate a split point
2087 p2 = in->points[(i+1)%in->numpoints];
2089 dot = dists[i] / (dists[i]-dists[i+1]);
2090 for (j = 0;j < 3;j++)
2091 { // avoid round off error when possible
2092 if (split->normal[j] == 1)
2093 mid[j] = split->dist;
2094 else if (split->normal[j] == -1)
2095 mid[j] = -split->dist;
2097 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2100 VectorCopy (mid, f->points[f->numpoints]);
2102 VectorCopy (mid, b->points[b->numpoints]);
2107 typedef struct portal_s
2110 mnode_t *nodes[2]; // [0] = front side of plane
2111 struct portal_s *next[2];
2113 struct portal_s *chain; // all portals are linked into a list
2117 static portal_t *portalchain;
2124 static portal_t *AllocPortal (void)
2127 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2128 p->chain = portalchain;
2133 static void FreePortal(portal_t *p)
2138 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2140 // calculate children first
2141 if (node->children[0]->contents >= 0)
2142 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2143 if (node->children[1]->contents >= 0)
2144 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2146 // make combined bounding box from children
2147 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2148 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2149 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2150 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2151 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2152 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2155 static void Mod_FinalizePortals(void)
2157 int i, j, numportals, numpoints;
2158 portal_t *p, *pnext;
2161 mleaf_t *leaf, *endleaf;
2164 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2165 leaf = loadmodel->leafs;
2166 endleaf = leaf + loadmodel->numleafs;
2167 for (;leaf < endleaf;leaf++)
2169 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2170 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2177 for (i = 0;i < 2;i++)
2179 leaf = (mleaf_t *)p->nodes[i];
2181 for (j = 0;j < w->numpoints;j++)
2183 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2184 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2185 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2186 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2187 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2188 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2195 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2197 // tally up portal and point counts
2203 // note: this check must match the one below or it will usually corrupt memory
2204 // 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
2205 if (p->winding && p->nodes[0] != p->nodes[1]
2206 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2207 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2210 numpoints += p->winding->numpoints * 2;
2214 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2215 loadmodel->numportals = numportals;
2216 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2217 loadmodel->numportalpoints = numpoints;
2218 // clear all leaf portal chains
2219 for (i = 0;i < loadmodel->numleafs;i++)
2220 loadmodel->leafs[i].portals = NULL;
2221 // process all portals in the global portal chain, while freeing them
2222 portal = loadmodel->portals;
2223 point = loadmodel->portalpoints;
2232 // note: this check must match the one above or it will usually corrupt memory
2233 // 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
2234 if (p->nodes[0] != p->nodes[1]
2235 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2236 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2238 // first make the back to front portal (forward portal)
2239 portal->points = point;
2240 portal->numpoints = p->winding->numpoints;
2241 portal->plane.dist = p->plane.dist;
2242 VectorCopy(p->plane.normal, portal->plane.normal);
2243 portal->here = (mleaf_t *)p->nodes[1];
2244 portal->past = (mleaf_t *)p->nodes[0];
2246 for (j = 0;j < portal->numpoints;j++)
2248 VectorCopy(p->winding->points[j], point->position);
2251 PlaneClassify(&portal->plane);
2253 // link into leaf's portal chain
2254 portal->next = portal->here->portals;
2255 portal->here->portals = portal;
2257 // advance to next portal
2260 // then make the front to back portal (backward portal)
2261 portal->points = point;
2262 portal->numpoints = p->winding->numpoints;
2263 portal->plane.dist = -p->plane.dist;
2264 VectorNegate(p->plane.normal, portal->plane.normal);
2265 portal->here = (mleaf_t *)p->nodes[0];
2266 portal->past = (mleaf_t *)p->nodes[1];
2268 for (j = portal->numpoints - 1;j >= 0;j--)
2270 VectorCopy(p->winding->points[j], point->position);
2273 PlaneClassify(&portal->plane);
2275 // link into leaf's portal chain
2276 portal->next = portal->here->portals;
2277 portal->here->portals = portal;
2279 // advance to next portal
2282 FreeWinding(p->winding);
2294 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2297 Host_Error ("AddPortalToNodes: NULL front node");
2299 Host_Error ("AddPortalToNodes: NULL back node");
2300 if (p->nodes[0] || p->nodes[1])
2301 Host_Error ("AddPortalToNodes: already included");
2302 // 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
2304 p->nodes[0] = front;
2305 p->next[0] = (portal_t *)front->portals;
2306 front->portals = (mportal_t *)p;
2309 p->next[1] = (portal_t *)back->portals;
2310 back->portals = (mportal_t *)p;
2315 RemovePortalFromNode
2318 static void RemovePortalFromNodes(portal_t *portal)
2322 void **portalpointer;
2324 for (i = 0;i < 2;i++)
2326 node = portal->nodes[i];
2328 portalpointer = (void **) &node->portals;
2333 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2337 if (portal->nodes[0] == node)
2339 *portalpointer = portal->next[0];
2340 portal->nodes[0] = NULL;
2342 else if (portal->nodes[1] == node)
2344 *portalpointer = portal->next[1];
2345 portal->nodes[1] = NULL;
2348 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2352 if (t->nodes[0] == node)
2353 portalpointer = (void **) &t->next[0];
2354 else if (t->nodes[1] == node)
2355 portalpointer = (void **) &t->next[1];
2357 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2362 static void Mod_RecursiveNodePortals (mnode_t *node)
2365 mnode_t *front, *back, *other_node;
2366 mplane_t clipplane, *plane;
2367 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2368 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2370 // if a leaf, we're done
2374 plane = node->plane;
2376 front = node->children[0];
2377 back = node->children[1];
2379 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2381 // create the new portal by generating a polygon for the node plane,
2382 // and clipping it by all of the other portals (which came from nodes above this one)
2383 nodeportal = AllocPortal ();
2384 nodeportal->plane = *node->plane;
2386 nodeportalwinding = BaseWindingForPlane (node->plane);
2387 side = 0; // shut up compiler warning
2388 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2390 clipplane = portal->plane;
2391 if (portal->nodes[0] == portal->nodes[1])
2392 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2393 if (portal->nodes[0] == node)
2395 else if (portal->nodes[1] == node)
2397 clipplane.dist = -clipplane.dist;
2398 VectorNegate (clipplane.normal, clipplane.normal);
2402 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2404 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2405 if (!nodeportalwinding)
2407 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2412 if (nodeportalwinding)
2414 // if the plane was not clipped on all sides, there was an error
2415 nodeportal->winding = nodeportalwinding;
2416 AddPortalToNodes (nodeportal, front, back);
2419 // split the portals of this node along this node's plane and assign them to the children of this node
2420 // (migrating the portals downward through the tree)
2421 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2423 if (portal->nodes[0] == portal->nodes[1])
2424 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2425 if (portal->nodes[0] == node)
2427 else if (portal->nodes[1] == node)
2430 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2431 nextportal = portal->next[side];
2433 other_node = portal->nodes[!side];
2434 RemovePortalFromNodes (portal);
2436 // cut the portal into two portals, one on each side of the node plane
2437 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2442 AddPortalToNodes (portal, back, other_node);
2444 AddPortalToNodes (portal, other_node, back);
2450 AddPortalToNodes (portal, front, other_node);
2452 AddPortalToNodes (portal, other_node, front);
2456 // the winding is split
2457 splitportal = AllocPortal ();
2458 temp = splitportal->chain;
2459 *splitportal = *portal;
2460 splitportal->chain = temp;
2461 splitportal->winding = backwinding;
2462 FreeWinding (portal->winding);
2463 portal->winding = frontwinding;
2467 AddPortalToNodes (portal, front, other_node);
2468 AddPortalToNodes (splitportal, back, other_node);
2472 AddPortalToNodes (portal, other_node, front);
2473 AddPortalToNodes (splitportal, other_node, back);
2477 Mod_RecursiveNodePortals(front);
2478 Mod_RecursiveNodePortals(back);
2482 static void Mod_MakePortals(void)
2485 Mod_RecursiveNodePortals (loadmodel->nodes);
2486 Mod_FinalizePortals();
2494 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2499 mempool_t *mainmempool;
2502 mod->type = mod_brush;
2504 header = (dheader_t *)buffer;
2506 i = LittleLong (header->version);
2507 if (i != BSPVERSION && i != 30)
2508 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2509 mod->ishlbsp = i == 30;
2510 if (loadmodel->isworldmodel)
2512 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2513 // until we get a texture for it...
2517 // swap all the lumps
2518 mod_base = (qbyte *)header;
2520 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2521 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2525 // store which lightmap format to use
2526 mod->lightmaprgba = r_lightmaprgba.integer;
2528 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2529 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2530 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2531 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2532 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2533 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2534 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2535 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2536 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2537 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2538 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2539 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2540 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2541 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2542 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2547 mod->numframes = 2; // regular and alternate animation
2549 mainmempool = mod->mempool;
2550 loadname = mod->name;
2552 Mod_LoadLightList ();
2555 // set up the submodels (FIXME: this is confusing)
2557 for (i = 0;i < mod->numsubmodels;i++)
2560 float dist, modelyawradius, modelradius, *vec;
2563 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2564 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2568 bm = &mod->submodels[i];
2570 mod->hulls[0].firstclipnode = bm->headnode[0];
2571 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2573 mod->hulls[j].firstclipnode = bm->headnode[j];
2574 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2577 mod->firstmodelsurface = bm->firstface;
2578 mod->nummodelsurfaces = bm->numfaces;
2580 mod->DrawSky = NULL;
2581 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2582 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2584 // we only need to have a drawsky function if it is used (usually only on world model)
2585 if (surf->shader == &Cshader_sky)
2586 mod->DrawSky = R_DrawBrushModelSky;
2587 for (k = 0;k < surf->numedges;k++)
2589 l = mod->surfedges[k + surf->firstedge];
2591 vec = mod->vertexes[mod->edges[l].v[0]].position;
2593 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2594 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2595 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2596 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2597 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2598 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2599 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2600 dist = vec[0]*vec[0]+vec[1]*vec[1];
2601 if (modelyawradius < dist)
2602 modelyawradius = dist;
2603 dist += vec[2]*vec[2];
2604 if (modelradius < dist)
2608 modelyawradius = sqrt(modelyawradius);
2609 modelradius = sqrt(modelradius);
2610 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2611 mod->yawmins[2] = mod->normalmins[2];
2612 mod->yawmaxs[2] = mod->normalmaxs[2];
2613 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2614 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2615 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2616 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2618 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2619 VectorClear(mod->normalmins);
2620 VectorClear(mod->normalmaxs);
2621 VectorClear(mod->yawmins);
2622 VectorClear(mod->yawmaxs);
2623 VectorClear(mod->rotatedmins);
2624 VectorClear(mod->rotatedmaxs);
2627 mod->numleafs = bm->visleafs;
2629 mod->Draw = R_DrawBrushModelNormal;
2630 mod->DrawShadow = NULL;
2632 Mod_BrushSortedSurfaces(mod, mainmempool);
2634 // LordHavoc: only register submodels if it is the world
2635 // (prevents bsp models from replacing world submodels)
2636 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2639 // duplicate the basic information
2640 sprintf (name, "*%i", i+1);
2641 loadmodel = Mod_FindName (name);
2643 strcpy (loadmodel->name, name);
2644 // textures and memory belong to the main model
2645 loadmodel->texturepool = NULL;
2646 loadmodel->mempool = NULL;