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)
116 Mod_CheckLoaded(model);
118 // LordHavoc: modified to start at first clip node,
119 // in other words: first node of the (sub)model
120 node = model->nodes + model->hulls[0].firstclipnode;
121 while (node->contents == 0)
122 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct (p,node->plane->normal)) < node->plane->dist];
124 return (mleaf_t *)node;
127 void Mod_FindNonSolidLocation(vec3_t pos, model_t *mod)
129 if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
130 pos[0]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
131 pos[0]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
133 pos[1]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
134 pos[1]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
136 pos[2]-=1;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
137 pos[2]+=2;if (Mod_PointInLeaf(pos, mod)->contents != CONTENTS_SOLID) return;
147 static qbyte *Mod_DecompressVis (qbyte *in, model_t *model)
149 static qbyte decompressed[MAX_MAP_LEAFS/8];
154 row = (model->numleafs+7)>>3;
172 } while (out - decompressed < row);
177 qbyte *Mod_LeafPVS (mleaf_t *leaf, model_t *model)
179 if (r_novis.integer || leaf == model->leafs || leaf->compressed_vis == NULL)
181 return Mod_DecompressVis (leaf->compressed_vis, model);
189 static void Mod_LoadTextures (lump_t *l)
191 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
193 texture_t *tx, *tx2, *anims[10], *altanims[10];
195 qbyte *data, *mtdata, *data2;
198 loadmodel->textures = NULL;
203 m = (dmiptexlump_t *)(mod_base + l->fileofs);
205 m->nummiptex = LittleLong (m->nummiptex);
207 // add two slots for notexture walls and notexture liquids
208 loadmodel->numtextures = m->nummiptex + 2;
209 loadmodel->textures = Mem_Alloc(loadmodel->mempool, loadmodel->numtextures * sizeof(*loadmodel->textures));
211 // fill out all slots with notexture
212 for (i = 0;i < loadmodel->numtextures;i++)
214 loadmodel->textures[i] = tx = Mem_Alloc(loadmodel->mempool, sizeof(texture_t));
217 tx->texture = r_notexture;
218 if (i == loadmodel->numtextures - 1)
219 tx->flags = SURF_DRAWTURB | SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID;
222 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
224 // LordHavoc: mostly rewritten map texture loader
225 for (i = 0;i < m->nummiptex;i++)
227 dofs[i] = LittleLong(dofs[i]);
228 if (dofs[i] == -1 || r_nosurftextures.integer)
230 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
232 // make sure name is no more than 15 characters
233 for (j = 0;dmiptex->name[j] && j < 15;j++)
234 name[j] = dmiptex->name[j];
237 mtwidth = LittleLong (dmiptex->width);
238 mtheight = LittleLong (dmiptex->height);
240 j = LittleLong (dmiptex->offsets[0]);
244 if (j < 40 || j + mtwidth * mtheight > l->filelen)
246 Con_Printf ("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
249 mtdata = (qbyte *)dmiptex + j;
252 if ((mtwidth & 15) || (mtheight & 15))
253 Con_Printf ("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
255 // LordHavoc: force all names to lowercase
256 for (j = 0;name[j];j++)
257 if (name[j] >= 'A' && name[j] <= 'Z')
258 name[j] += 'a' - 'A';
260 tx = loadmodel->textures[i];
261 strcpy(tx->name, name);
263 tx->height = mtheight;
265 tx->glowtexture = NULL;
266 tx->fogtexture = NULL;
270 sprintf(tx->name, "unnamed%i", i);
271 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
274 // LordHavoc: HL sky textures are entirely different than quake
275 if (!loadmodel->ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
277 if (loadmodel->isworldmodel)
279 data = loadimagepixels(tx->name, false, 0, 0);
282 if (image_width == 256 && image_height == 128)
290 Con_Printf ("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
292 R_InitSky (mtdata, 1);
295 else if (mtdata != NULL)
296 R_InitSky (mtdata, 1);
299 else if ((tx->texture = loadtextureimagewithmask(loadmodel->texturepool, tx->name, 0, 0, false, true, true)))
301 tx->fogtexture = image_masktex;
302 strcpy(name, tx->name);
303 strcat(name, "_glow");
304 tx->glowtexture = loadtextureimage(loadmodel->texturepool, name, 0, 0, false, true, true);
308 if (loadmodel->ishlbsp)
310 if (mtdata && (data = W_ConvertWAD3Texture(dmiptex)))
313 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
314 if (R_TextureHasAlpha(tx->texture))
317 for (j = 0;j < image_width * image_height;j++)
318 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
319 strcpy(name, tx->name);
320 strcat(name, "_fog");
321 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
325 else if ((data = W_GetTexture(tx->name)))
327 // get the size from the wad texture
328 tx->width = image_width;
329 tx->height = image_height;
330 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
331 if (R_TextureHasAlpha(tx->texture))
334 for (j = 0;j < image_width * image_height;j++)
335 data[j*4+0] = data[j*4+1] = data[j*4+2] = 255;
336 strcpy(name, tx->name);
337 strcat(name, "_fog");
338 tx->fogtexture = R_LoadTexture (loadmodel->texturepool, name, image_width, image_height, data, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE);
346 tx->texture = r_notexture;
351 if (mtdata) // texture included
356 if (r_fullbrights.value && tx->name[0] != '*')
358 for (j = 0;j < tx->width*tx->height;j++)
360 if (data[j] >= 224) // fullbright
369 data2 = Mem_Alloc(loadmodel->mempool, tx->width*tx->height);
370 for (j = 0;j < tx->width*tx->height;j++)
371 data2[j] = data[j] >= 224 ? 0 : data[j]; // no fullbrights
372 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
373 strcpy(name, tx->name);
374 strcat(name, "_glow");
375 for (j = 0;j < tx->width*tx->height;j++)
376 data2[j] = data[j] >= 224 ? data[j] : 0; // only fullbrights
377 tx->glowtexture = R_LoadTexture (loadmodel->texturepool, name, tx->width, tx->height, data2, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
381 tx->texture = R_LoadTexture (loadmodel->texturepool, tx->name, tx->width, tx->height, data, TEXTYPE_QPALETTE, TEXF_MIPMAP | TEXF_PRECACHE);
383 else // no texture, and no external replacement texture was found
387 tx->texture = r_notexture;
392 if (tx->name[0] == '*')
394 tx->flags |= (SURF_DRAWTURB | SURF_LIGHTBOTHSIDES);
395 // LordHavoc: some turbulent textures should be fullbright and solid
396 if (!strncmp(tx->name,"*lava",5)
397 || !strncmp(tx->name,"*teleport",9)
398 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
399 tx->flags |= (SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA | SURF_CLIPSOLID);
401 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
402 tx->flags |= (SURF_DRAWSKY | SURF_CLIPSOLID);
405 tx->flags |= SURF_LIGHTMAP;
406 if (!R_TextureHasAlpha(tx->texture))
407 tx->flags |= SURF_CLIPSOLID;
410 tx->detailtexture = detailtextures[i % NUM_DETAILTEXTURES];
413 // sequence the animations
414 for (i = 0;i < m->nummiptex;i++)
416 tx = loadmodel->textures[i];
417 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
419 if (tx->anim_total[0] || tx->anim_total[1])
420 continue; // already sequenced
422 // find the number of frames in the animation
423 memset (anims, 0, sizeof(anims));
424 memset (altanims, 0, sizeof(altanims));
426 for (j = i;j < m->nummiptex;j++)
428 tx2 = loadmodel->textures[j];
429 if (!tx2 || tx2->name[0] != '+' || strcmp (tx2->name+2, tx->name+2))
433 if (num >= '0' && num <= '9')
434 anims[num - '0'] = tx2;
435 else if (num >= 'a' && num <= 'j')
436 altanims[num - 'a'] = tx2;
438 Con_Printf ("Bad animating texture %s\n", tx->name);
442 for (j = 0;j < 10;j++)
449 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
452 for (j = 0;j < max;j++)
456 Con_Printf ("Missing frame %i of %s\n", j, tx->name);
460 for (j = 0;j < altmax;j++)
464 Con_Printf ("Missing altframe %i of %s\n", j, tx->name);
473 // if there is no alternate animation, duplicate the primary
474 // animation into the alternate
476 for (k = 0;k < 10;k++)
477 altanims[k] = anims[k];
480 // link together the primary animation
481 for (j = 0;j < max;j++)
484 tx2->animated = true;
485 tx2->anim_total[0] = max;
486 tx2->anim_total[1] = altmax;
487 for (k = 0;k < 10;k++)
489 tx2->anim_frames[0][k] = anims[k];
490 tx2->anim_frames[1][k] = altanims[k];
494 // if there really is an alternate anim...
495 if (anims[0] != altanims[0])
497 // link together the alternate animation
498 for (j = 0;j < altmax;j++)
501 tx2->animated = true;
502 // the primary/alternate are reversed here
503 tx2->anim_total[0] = altmax;
504 tx2->anim_total[1] = max;
505 for (k = 0;k < 10;k++)
507 tx2->anim_frames[0][k] = altanims[k];
508 tx2->anim_frames[1][k] = anims[k];
520 static void Mod_LoadLighting (lump_t *l)
523 qbyte *in, *out, *data, d;
524 char litfilename[1024];
525 loadmodel->lightdata = NULL;
526 if (loadmodel->ishlbsp) // LordHavoc: load the colored lighting data straight
528 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
529 memcpy (loadmodel->lightdata, mod_base + l->fileofs, l->filelen);
531 else // LordHavoc: bsp version 29 (normal white lighting)
533 // LordHavoc: hope is not lost yet, check for a .lit file to load
534 strcpy(litfilename, loadmodel->name);
535 COM_StripExtension(litfilename, litfilename);
536 strcat(litfilename, ".lit");
537 data = (qbyte*) COM_LoadFile (litfilename, false);
540 if (loadsize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
542 i = LittleLong(((int *)data)[1]);
545 Con_DPrintf("%s loaded", litfilename);
546 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, loadsize - 8);
547 memcpy(loadmodel->lightdata, data + 8, loadsize - 8);
553 Con_Printf("Unknown .lit file version (%d)\n", i);
560 Con_Printf("Empty .lit file, ignoring\n");
562 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
566 // LordHavoc: oh well, expand the white lighting data
569 loadmodel->lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
570 in = loadmodel->lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
571 out = loadmodel->lightdata;
572 memcpy (in, mod_base + l->fileofs, l->filelen);
573 for (i = 0;i < l->filelen;i++)
583 void Mod_LoadLightList(void)
586 char lightsfilename[1024], *s, *t, *lightsstring;
589 strcpy(lightsfilename, loadmodel->name);
590 COM_StripExtension(lightsfilename, lightsfilename);
591 strcat(lightsfilename, ".lights");
592 s = lightsstring = (char *) COM_LoadFile (lightsfilename, false);
598 while (*s && *s != '\n')
602 Mem_Free(lightsstring);
603 Host_Error("lights file must end with a newline\n");
608 loadmodel->lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
611 while (*s && n < numlights)
614 while (*s && *s != '\n')
618 Mem_Free(lightsstring);
619 Host_Error("misparsed lights file!\n");
621 e = loadmodel->lights + n;
623 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);
627 Mem_Free(lightsstring);
628 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);
635 Mem_Free(lightsstring);
636 Host_Error("misparsed lights file!\n");
638 loadmodel->numlights = numlights;
639 Mem_Free(lightsstring);
649 static void Mod_LoadVisibility (lump_t *l)
651 loadmodel->visdata = NULL;
654 loadmodel->visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
655 memcpy (loadmodel->visdata, mod_base + l->fileofs, l->filelen);
658 // used only for HalfLife maps
659 void Mod_ParseWadsFromEntityLump(char *data)
661 char key[128], value[4096];
666 data = COM_Parse(data);
669 if (com_token[0] != '{')
673 data = COM_Parse(data);
676 if (com_token[0] == '}')
677 break; // end of worldspawn
678 if (com_token[0] == '_')
679 strcpy(key, com_token + 1);
681 strcpy(key, com_token);
682 while (key[strlen(key)-1] == ' ') // remove trailing spaces
683 key[strlen(key)-1] = 0;
684 data = COM_Parse(data);
687 strcpy(value, com_token);
688 if (!strcmp("wad", key)) // for HalfLife maps
690 if (loadmodel->ishlbsp)
693 for (i = 0;i < 4096;i++)
694 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
700 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
701 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
703 else if (value[i] == ';' || value[i] == 0)
707 strcpy(wadname, "textures/");
708 strcat(wadname, &value[j]);
709 W_LoadTextureWadFile (wadname, false);
726 static void Mod_LoadEntities (lump_t *l)
728 loadmodel->entities = NULL;
731 loadmodel->entities = Mem_Alloc(loadmodel->mempool, l->filelen);
732 memcpy (loadmodel->entities, mod_base + l->fileofs, l->filelen);
733 if (loadmodel->ishlbsp)
734 Mod_ParseWadsFromEntityLump(loadmodel->entities);
743 static void Mod_LoadVertexes (lump_t *l)
749 in = (void *)(mod_base + l->fileofs);
750 if (l->filelen % sizeof(*in))
751 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
752 count = l->filelen / sizeof(*in);
753 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
755 loadmodel->vertexes = out;
756 loadmodel->numvertexes = count;
758 for ( i=0 ; i<count ; i++, in++, out++)
760 out->position[0] = LittleFloat (in->point[0]);
761 out->position[1] = LittleFloat (in->point[1]);
762 out->position[2] = LittleFloat (in->point[2]);
771 static void Mod_LoadSubmodels (lump_t *l)
777 in = (void *)(mod_base + l->fileofs);
778 if (l->filelen % sizeof(*in))
779 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
780 count = l->filelen / sizeof(*in);
781 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
783 loadmodel->submodels = out;
784 loadmodel->numsubmodels = count;
786 for ( i=0 ; i<count ; i++, in++, out++)
788 for (j=0 ; j<3 ; j++)
790 // spread the mins / maxs by a pixel
791 out->mins[j] = LittleFloat (in->mins[j]) - 1;
792 out->maxs[j] = LittleFloat (in->maxs[j]) + 1;
793 out->origin[j] = LittleFloat (in->origin[j]);
795 for (j=0 ; j<MAX_MAP_HULLS ; j++)
796 out->headnode[j] = LittleLong (in->headnode[j]);
797 out->visleafs = LittleLong (in->visleafs);
798 out->firstface = LittleLong (in->firstface);
799 out->numfaces = LittleLong (in->numfaces);
808 static void Mod_LoadEdges (lump_t *l)
814 in = (void *)(mod_base + l->fileofs);
815 if (l->filelen % sizeof(*in))
816 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
817 count = l->filelen / sizeof(*in);
818 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
820 loadmodel->edges = out;
821 loadmodel->numedges = count;
823 for ( i=0 ; i<count ; i++, in++, out++)
825 out->v[0] = (unsigned short)LittleShort(in->v[0]);
826 out->v[1] = (unsigned short)LittleShort(in->v[1]);
835 static void Mod_LoadTexinfo (lump_t *l)
839 int i, j, k, count, miptex;
841 in = (void *)(mod_base + l->fileofs);
842 if (l->filelen % sizeof(*in))
843 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
844 count = l->filelen / sizeof(*in);
845 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
847 loadmodel->texinfo = out;
848 loadmodel->numtexinfo = count;
850 for (i = 0;i < count;i++, in++, out++)
852 for (k = 0;k < 2;k++)
853 for (j = 0;j < 4;j++)
854 out->vecs[k][j] = LittleFloat (in->vecs[k][j]);
856 miptex = LittleLong (in->miptex);
857 out->flags = LittleLong (in->flags);
860 if (loadmodel->textures)
862 if ((unsigned int) miptex >= (unsigned int) loadmodel->numtextures)
863 Con_Printf ("error in model \"%s\": invalid miptex index %i (of %i)\n", loadmodel->name, miptex, loadmodel->numtextures);
865 out->texture = loadmodel->textures[miptex];
867 if (out->texture == NULL)
869 // choose either the liquid notexture, or the normal notexture
870 if (out->flags & TEX_SPECIAL)
871 out->texture = loadmodel->textures[loadmodel->numtextures - 1];
873 out->texture = loadmodel->textures[loadmodel->numtextures - 2];
882 Fills in s->texturemins[] and s->extents[]
885 static void CalcSurfaceExtents (msurface_t *s)
887 float mins[2], maxs[2], val;
891 int bmins[2], bmaxs[2];
893 mins[0] = mins[1] = 999999999;
894 maxs[0] = maxs[1] = -999999999;
898 for (i=0 ; i<s->numedges ; i++)
900 e = loadmodel->surfedges[s->firstedge+i];
902 v = &loadmodel->vertexes[loadmodel->edges[e].v[0]];
904 v = &loadmodel->vertexes[loadmodel->edges[-e].v[1]];
906 for (j=0 ; j<2 ; j++)
908 val = v->position[0] * tex->vecs[j][0] +
909 v->position[1] * tex->vecs[j][1] +
910 v->position[2] * tex->vecs[j][2] +
919 for (i=0 ; i<2 ; i++)
921 bmins[i] = floor(mins[i]/16);
922 bmaxs[i] = ceil(maxs[i]/16);
924 s->texturemins[i] = bmins[i] * 16;
925 s->extents[i] = (bmaxs[i] - bmins[i]) * 16;
930 void BoundPoly (int numverts, float *verts, vec3_t mins, vec3_t maxs)
935 mins[0] = mins[1] = mins[2] = 9999;
936 maxs[0] = maxs[1] = maxs[2] = -9999;
938 for (i = 0;i < numverts;i++)
940 for (j = 0;j < 3;j++, v++)
951 #define MAX_SUBDIVPOLYTRIANGLES 4096
952 #define MAX_SUBDIVPOLYVERTS (MAX_SUBDIVPOLYTRIANGLES * 3)
954 static int subdivpolyverts, subdivpolytriangles;
955 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
956 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
958 static int subdivpolylookupvert(vec3_t v)
961 for (i = 0;i < subdivpolyverts;i++)
962 if (subdivpolyvert[i][0] == v[0]
963 && subdivpolyvert[i][1] == v[1]
964 && subdivpolyvert[i][2] == v[2])
966 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
967 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
968 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
969 return subdivpolyverts++;
972 static void SubdividePolygon (int numverts, float *verts)
974 int i, i1, i2, i3, f, b, c, p;
975 vec3_t mins, maxs, front[256], back[256];
976 float m, *pv, *cv, dist[256], frac;
979 Host_Error ("SubdividePolygon: ran out of verts in buffer");
981 BoundPoly (numverts, verts, mins, maxs);
983 for (i = 0;i < 3;i++)
985 m = (mins[i] + maxs[i]) * 0.5;
986 m = r_subdivide_size.value * floor (m/r_subdivide_size.value + 0.5);
993 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
997 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1001 VectorCopy (pv, front[f]);
1006 VectorCopy (pv, back[b]);
1009 if (dist[p] == 0 || dist[c] == 0)
1011 if ( (dist[p] > 0) != (dist[c] > 0) )
1014 frac = dist[p] / (dist[p] - dist[c]);
1015 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1016 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1017 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1023 SubdividePolygon (f, front[0]);
1024 SubdividePolygon (b, back[0]);
1028 i1 = subdivpolylookupvert(verts);
1029 i2 = subdivpolylookupvert(verts + 3);
1030 for (i = 2;i < numverts;i++)
1032 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1034 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1038 i3 = subdivpolylookupvert(verts + i * 3);
1039 subdivpolyindex[subdivpolytriangles][0] = i1;
1040 subdivpolyindex[subdivpolytriangles][1] = i2;
1041 subdivpolyindex[subdivpolytriangles][2] = i3;
1043 subdivpolytriangles++;
1049 Mod_GenerateWarpMesh
1051 Breaks a polygon up along axial 64 unit
1052 boundaries so that turbulent and sky warps
1053 can be done reasonably.
1056 void Mod_GenerateWarpMesh (msurface_t *surf)
1062 subdivpolytriangles = 0;
1063 subdivpolyverts = 0;
1064 SubdividePolygon (surf->poly_numverts, surf->poly_verts);
1065 if (subdivpolytriangles < 1)
1066 Host_Error("Mod_GenerateWarpMesh: no triangles?\n");
1068 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1069 mesh->numverts = subdivpolyverts;
1070 mesh->numtriangles = subdivpolytriangles;
1071 mesh->vertex = (surfvertex_t *)(mesh + 1);
1072 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1073 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1075 for (i = 0;i < mesh->numtriangles;i++)
1076 for (j = 0;j < 3;j++)
1077 mesh->index[i*3+j] = subdivpolyindex[i][j];
1079 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1081 VectorCopy(subdivpolyvert[i], v->v);
1082 v->st[0] = DotProduct (v->v, surf->texinfo->vecs[0]);
1083 v->st[1] = DotProduct (v->v, surf->texinfo->vecs[1]);
1088 void Mod_GenerateVertexLitMesh (msurface_t *surf)
1090 int i, is, it, *index, smax, tmax;
1095 smax = surf->extents[0] >> 4;
1096 tmax = surf->extents[1] >> 4;
1097 surf->lightmaptexturestride = 0;
1098 surf->lightmaptexture = NULL;
1100 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1101 mesh->numverts = surf->poly_numverts;
1102 mesh->numtriangles = surf->poly_numverts - 2;
1103 mesh->vertex = (surfvertex_t *)(mesh + 1);
1104 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1105 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1107 index = mesh->index;
1108 for (i = 0;i < mesh->numtriangles;i++)
1115 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1117 VectorCopy (in, out->v);
1119 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1120 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1122 out->ab[0] = s * (1.0f / 16.0f);
1123 out->ab[1] = t * (1.0f / 16.0f);
1125 out->st[0] = s / surf->texinfo->texture->width;
1126 out->st[1] = t / surf->texinfo->texture->height;
1128 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1129 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1131 // lightmap coordinates
1135 // LordHavoc: calc lightmap data offset for vertex lighting to use
1138 is = bound(0, is, smax);
1139 it = bound(0, it, tmax);
1140 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1144 void Mod_GenerateLightmappedMesh (msurface_t *surf)
1146 int i, is, it, *index, smax, tmax;
1147 float *in, s, t, xbase, ybase, xscale, yscale;
1151 surf->flags |= SURF_LIGHTMAP;
1152 smax = surf->extents[0] >> 4;
1153 tmax = surf->extents[1] >> 4;
1154 if (r_miplightmaps.integer)
1156 surf->lightmaptexturestride = (surf->extents[0]>>4)+1;
1157 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);
1161 surf->lightmaptexturestride = R_CompatibleFragmentWidth((surf->extents[0]>>4)+1, loadmodel->lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1162 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);
1164 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &xbase, &ybase, &xscale, &yscale);
1165 xscale = (xscale - xbase) * 16.0 / ((surf->extents[0] & ~15) + 16);
1166 yscale = (yscale - ybase) * 16.0 / ((surf->extents[1] & ~15) + 16);
1168 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1169 mesh->numverts = surf->poly_numverts;
1170 mesh->numtriangles = surf->poly_numverts - 2;
1171 mesh->vertex = (surfvertex_t *)(mesh + 1);
1172 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1173 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1175 index = mesh->index;
1176 for (i = 0;i < mesh->numtriangles;i++)
1183 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1185 VectorCopy (in, out->v);
1187 s = DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1188 t = DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1190 out->ab[0] = s * (1.0f / 16.0f);
1191 out->ab[1] = t * (1.0f / 16.0f);
1193 out->st[0] = s / surf->texinfo->texture->width;
1194 out->st[1] = t / surf->texinfo->texture->height;
1196 s = (s + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1197 t = (t + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1199 // lightmap coordinates
1200 out->uv[0] = s * xscale + xbase;
1201 out->uv[1] = t * yscale + ybase;
1203 // LordHavoc: calc lightmap data offset for vertex lighting to use
1206 is = bound(0, is, smax);
1207 it = bound(0, it, tmax);
1208 out->lightmapoffset = ((it * (smax+1) + is) * 3);
1212 void Mod_GenerateVertexMesh (msurface_t *surf)
1219 surf->lightmaptexturestride = 0;
1220 surf->lightmaptexture = NULL;
1222 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + (surf->poly_numverts - 2) * sizeof(int[3]) + surf->poly_numverts * sizeof(surfvertex_t));
1223 mesh->numverts = surf->poly_numverts;
1224 mesh->numtriangles = surf->poly_numverts - 2;
1225 mesh->vertex = (surfvertex_t *)(mesh + 1);
1226 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1227 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1229 index = mesh->index;
1230 for (i = 0;i < mesh->numtriangles;i++)
1237 for (i = 0, in = surf->poly_verts, out = mesh->vertex;i < mesh->numverts;i++, in += 3, out++)
1239 VectorCopy (in, out->v);
1240 s = (DotProduct (out->v, surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]);
1241 t = (DotProduct (out->v, surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]);
1242 out->st[0] = s / surf->texinfo->texture->width;
1243 out->st[1] = t / surf->texinfo->texture->height;
1244 out->ab[0] = s * (1.0f / 16.0f);
1245 out->ab[1] = t * (1.0f / 16.0f);
1249 void Mod_GenerateSurfacePolygon (msurface_t *surf)
1252 float *vec, *vert, mins[3], maxs[3];
1254 // convert edges back to a normal polygon
1255 surf->poly_numverts = surf->numedges;
1256 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1257 for (i = 0;i < surf->numedges;i++)
1259 lindex = loadmodel->surfedges[surf->firstedge + i];
1261 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1263 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1264 VectorCopy (vec, vert);
1267 vert = surf->poly_verts;
1268 VectorCopy(vert, mins);
1269 VectorCopy(vert, maxs);
1271 for (i = 1;i < surf->poly_numverts;i++)
1273 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1274 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1275 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1278 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1279 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1280 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1283 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1285 int j, base, tricount, newvertexcount, *index, *vertexremap;
1286 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1287 if (s->mesh->numtriangles > 1000)
1289 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1294 while (base < s->mesh->numtriangles)
1296 tricount = s->mesh->numtriangles - base;
1297 if (tricount > 1000)
1299 index = s->mesh->index + base * 3;
1303 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1304 for (j = 0;j < tricount * 3;j++)
1305 if (vertexremap[index[j]] < 0)
1306 vertexremap[index[j]] = newvertexcount++;
1308 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1309 newmesh->chain = NULL;
1310 newmesh->numverts = newvertexcount;
1311 newmesh->numtriangles = tricount;
1312 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1313 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1314 for (j = 0;j < tricount * 3;j++)
1316 newmesh->index[j] = vertexremap[index[j]];
1317 // yes this copies the same vertex multiple times in many cases... but that's ok...
1318 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1321 oldmesh->chain = newmesh;
1323 firstmesh = newmesh;
1326 Mem_Free(vertexremap);
1328 s->mesh = firstmesh;
1337 static void Mod_LoadFaces (lump_t *l)
1341 int i, count, surfnum, planenum, ssize, tsize;
1343 in = (void *)(mod_base + l->fileofs);
1344 if (l->filelen % sizeof(*in))
1345 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1346 count = l->filelen / sizeof(*in);
1347 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1349 loadmodel->surfaces = out;
1350 loadmodel->numsurfaces = count;
1352 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1354 // FIXME: validate edges, texinfo, etc?
1355 out->firstedge = LittleLong(in->firstedge);
1356 out->numedges = LittleShort(in->numedges);
1357 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1358 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1360 i = LittleShort (in->texinfo);
1361 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1362 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1363 out->texinfo = loadmodel->texinfo + i;
1364 out->flags = out->texinfo->texture->flags;
1366 planenum = LittleShort(in->planenum);
1367 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1368 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1370 if (LittleShort(in->side))
1371 out->flags |= SURF_PLANEBACK;
1373 out->plane = loadmodel->planes + planenum;
1375 // clear lightmap (filled in later)
1376 out->lightmaptexture = NULL;
1378 // force lightmap upload on first time seeing the surface
1379 out->cached_dlight = true;
1380 out->cached_ambient = -1000;
1381 out->cached_lightscalebit = -1000;
1383 CalcSurfaceExtents (out);
1385 ssize = (out->extents[0] >> 4) + 1;
1386 tsize = (out->extents[1] >> 4) + 1;
1389 for (i = 0;i < MAXLIGHTMAPS;i++)
1390 out->styles[i] = in->styles[i];
1391 i = LittleLong(in->lightofs);
1393 out->samples = NULL;
1394 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1395 out->samples = loadmodel->lightdata + i;
1396 else // LordHavoc: white lighting (bsp version 29)
1397 out->samples = loadmodel->lightdata + (i * 3);
1399 Mod_GenerateSurfacePolygon(out);
1401 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1403 out->shader = &Cshader_sky;
1404 out->samples = NULL;
1405 Mod_GenerateVertexMesh (out);
1407 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1409 out->shader = &Cshader_water;
1410 out->samples = NULL;
1411 Mod_GenerateVertexMesh (out);
1415 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1416 out->flags |= SURF_CLIPSOLID;
1417 if (out->texinfo->flags & TEX_SPECIAL)
1419 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1420 out->shader = &Cshader_water;
1421 out->shader = &Cshader_water;
1422 out->samples = NULL;
1423 Mod_GenerateVertexMesh (out);
1425 else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1427 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1428 out->shader = &Cshader_wall_fullbright;
1429 out->samples = NULL;
1430 Mod_GenerateVertexMesh(out);
1434 // stainmap for permanent marks on walls
1435 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1437 memset(out->stainsamples, 255, ssize * tsize * 3);
1438 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1440 out->shader = &Cshader_wall_vertex;
1441 Mod_GenerateVertexLitMesh(out);
1445 out->shader = &Cshader_wall_lightmap;
1446 Mod_GenerateLightmappedMesh(out);
1450 Mod_SplitSurfMeshIfTooBig(out);
1454 static model_t *sortmodel;
1456 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1458 const msurface_t *a, *b;
1459 a = *((const msurface_t **)voida);
1460 b = *((const msurface_t **)voidb);
1461 if (a->shader != b->shader)
1462 return (qbyte *) a->shader - (qbyte *) b->shader;
1463 if (a->texinfo->texture != b->texinfo->texture);
1464 return a->texinfo->texture - b->texinfo->texture;
1468 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1472 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1473 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1474 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1476 if (r_sortsurfaces.integer)
1477 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1486 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1488 node->parent = parent;
1489 if (node->contents < 0)
1491 Mod_SetParent (node->children[0], node);
1492 Mod_SetParent (node->children[1], node);
1500 static void Mod_LoadNodes (lump_t *l)
1506 in = (void *)(mod_base + l->fileofs);
1507 if (l->filelen % sizeof(*in))
1508 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1509 count = l->filelen / sizeof(*in);
1510 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1512 loadmodel->nodes = out;
1513 loadmodel->numnodes = count;
1515 for ( i=0 ; i<count ; i++, in++, out++)
1517 for (j=0 ; j<3 ; j++)
1519 out->mins[j] = LittleShort (in->mins[j]);
1520 out->maxs[j] = LittleShort (in->maxs[j]);
1523 p = LittleLong(in->planenum);
1524 out->plane = loadmodel->planes + p;
1526 out->firstsurface = LittleShort (in->firstface);
1527 out->numsurfaces = LittleShort (in->numfaces);
1529 for (j=0 ; j<2 ; j++)
1531 p = LittleShort (in->children[j]);
1533 out->children[j] = loadmodel->nodes + p;
1535 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1539 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1547 static void Mod_LoadLeafs (lump_t *l)
1553 in = (void *)(mod_base + l->fileofs);
1554 if (l->filelen % sizeof(*in))
1555 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1556 count = l->filelen / sizeof(*in);
1557 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1559 loadmodel->leafs = out;
1560 loadmodel->numleafs = count;
1562 for ( i=0 ; i<count ; i++, in++, out++)
1564 for (j=0 ; j<3 ; j++)
1566 out->mins[j] = LittleShort (in->mins[j]);
1567 out->maxs[j] = LittleShort (in->maxs[j]);
1570 p = LittleLong(in->contents);
1573 out->firstmarksurface = loadmodel->marksurfaces +
1574 LittleShort(in->firstmarksurface);
1575 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1577 p = LittleLong(in->visofs);
1579 out->compressed_vis = NULL;
1581 out->compressed_vis = loadmodel->visdata + p;
1583 for (j=0 ; j<4 ; j++)
1584 out->ambient_sound_level[j] = in->ambient_level[j];
1586 // FIXME: Insert caustics here
1595 static void Mod_LoadClipnodes (lump_t *l)
1597 dclipnode_t *in, *out;
1601 in = (void *)(mod_base + l->fileofs);
1602 if (l->filelen % sizeof(*in))
1603 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1604 count = l->filelen / sizeof(*in);
1605 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1607 loadmodel->clipnodes = out;
1608 loadmodel->numclipnodes = count;
1610 if (loadmodel->ishlbsp)
1612 hull = &loadmodel->hulls[1];
1613 hull->clipnodes = out;
1614 hull->firstclipnode = 0;
1615 hull->lastclipnode = count-1;
1616 hull->planes = loadmodel->planes;
1617 hull->clip_mins[0] = -16;
1618 hull->clip_mins[1] = -16;
1619 hull->clip_mins[2] = -36;
1620 hull->clip_maxs[0] = 16;
1621 hull->clip_maxs[1] = 16;
1622 hull->clip_maxs[2] = 36;
1623 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1625 hull = &loadmodel->hulls[2];
1626 hull->clipnodes = out;
1627 hull->firstclipnode = 0;
1628 hull->lastclipnode = count-1;
1629 hull->planes = loadmodel->planes;
1630 hull->clip_mins[0] = -32;
1631 hull->clip_mins[1] = -32;
1632 hull->clip_mins[2] = -32;
1633 hull->clip_maxs[0] = 32;
1634 hull->clip_maxs[1] = 32;
1635 hull->clip_maxs[2] = 32;
1636 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1638 hull = &loadmodel->hulls[3];
1639 hull->clipnodes = out;
1640 hull->firstclipnode = 0;
1641 hull->lastclipnode = count-1;
1642 hull->planes = loadmodel->planes;
1643 hull->clip_mins[0] = -16;
1644 hull->clip_mins[1] = -16;
1645 hull->clip_mins[2] = -18;
1646 hull->clip_maxs[0] = 16;
1647 hull->clip_maxs[1] = 16;
1648 hull->clip_maxs[2] = 18;
1649 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1653 hull = &loadmodel->hulls[1];
1654 hull->clipnodes = out;
1655 hull->firstclipnode = 0;
1656 hull->lastclipnode = count-1;
1657 hull->planes = loadmodel->planes;
1658 hull->clip_mins[0] = -16;
1659 hull->clip_mins[1] = -16;
1660 hull->clip_mins[2] = -24;
1661 hull->clip_maxs[0] = 16;
1662 hull->clip_maxs[1] = 16;
1663 hull->clip_maxs[2] = 32;
1664 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1666 hull = &loadmodel->hulls[2];
1667 hull->clipnodes = out;
1668 hull->firstclipnode = 0;
1669 hull->lastclipnode = count-1;
1670 hull->planes = loadmodel->planes;
1671 hull->clip_mins[0] = -32;
1672 hull->clip_mins[1] = -32;
1673 hull->clip_mins[2] = -24;
1674 hull->clip_maxs[0] = 32;
1675 hull->clip_maxs[1] = 32;
1676 hull->clip_maxs[2] = 64;
1677 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1680 for (i=0 ; i<count ; i++, out++, in++)
1682 out->planenum = LittleLong(in->planenum);
1683 out->children[0] = LittleShort(in->children[0]);
1684 out->children[1] = LittleShort(in->children[1]);
1685 if (out->children[0] >= count || out->children[1] >= count)
1686 Host_Error("Corrupt clipping hull (out of range child)\n");
1694 Duplicate the drawing hull structure as a clipping hull
1697 static void Mod_MakeHull0 (void)
1704 hull = &loadmodel->hulls[0];
1706 in = loadmodel->nodes;
1707 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1709 hull->clipnodes = out;
1710 hull->firstclipnode = 0;
1711 hull->lastclipnode = loadmodel->numnodes - 1;
1712 hull->planes = loadmodel->planes;
1714 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1716 out->planenum = in->plane - loadmodel->planes;
1717 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1718 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1724 Mod_LoadMarksurfaces
1727 static void Mod_LoadMarksurfaces (lump_t *l)
1732 in = (void *)(mod_base + l->fileofs);
1733 if (l->filelen % sizeof(*in))
1734 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1735 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1736 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1738 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1740 j = (unsigned) LittleShort(in[i]);
1741 if (j >= loadmodel->numsurfaces)
1742 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1743 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1752 static void Mod_LoadSurfedges (lump_t *l)
1757 in = (void *)(mod_base + l->fileofs);
1758 if (l->filelen % sizeof(*in))
1759 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1760 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1761 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1763 for (i = 0;i < loadmodel->numsurfedges;i++)
1764 loadmodel->surfedges[i] = LittleLong (in[i]);
1773 static void Mod_LoadPlanes (lump_t *l)
1779 in = (void *)(mod_base + l->fileofs);
1780 if (l->filelen % sizeof(*in))
1781 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1783 loadmodel->numplanes = l->filelen / sizeof(*in);
1784 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1786 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1788 out->normal[0] = LittleFloat (in->normal[0]);
1789 out->normal[1] = LittleFloat (in->normal[1]);
1790 out->normal[2] = LittleFloat (in->normal[2]);
1791 out->dist = LittleFloat (in->dist);
1797 #define MAX_POINTS_ON_WINDING 64
1803 double points[8][3]; // variable sized
1812 static winding_t *NewWinding (int points)
1817 if (points > MAX_POINTS_ON_WINDING)
1818 Sys_Error("NewWinding: too many points\n");
1820 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1821 w = Mem_Alloc(loadmodel->mempool, size);
1822 memset (w, 0, size);
1827 static void FreeWinding (winding_t *w)
1837 static winding_t *BaseWindingForPlane (mplane_t *p)
1839 double org[3], vright[3], vup[3], normal[3];
1842 VectorCopy(p->normal, normal);
1843 VectorVectorsDouble(normal, vright, vup);
1845 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1846 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1848 // project a really big axis aligned box onto the plane
1851 VectorScale (p->normal, p->dist, org);
1853 VectorSubtract (org, vright, w->points[0]);
1854 VectorAdd (w->points[0], vup, w->points[0]);
1856 VectorAdd (org, vright, w->points[1]);
1857 VectorAdd (w->points[1], vup, w->points[1]);
1859 VectorAdd (org, vright, w->points[2]);
1860 VectorSubtract (w->points[2], vup, w->points[2]);
1862 VectorSubtract (org, vright, w->points[3]);
1863 VectorSubtract (w->points[3], vup, w->points[3]);
1874 Clips the winding to the plane, returning the new winding on the positive side
1875 Frees the input winding.
1876 If keepon is true, an exactly on-plane winding will be saved, otherwise
1877 it will be clipped away.
1880 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1882 double dists[MAX_POINTS_ON_WINDING + 1];
1883 int sides[MAX_POINTS_ON_WINDING + 1];
1892 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1894 // determine sides for each point
1895 for (i = 0;i < in->numpoints;i++)
1897 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1898 if (dot > ON_EPSILON)
1899 sides[i] = SIDE_FRONT;
1900 else if (dot < -ON_EPSILON)
1901 sides[i] = SIDE_BACK;
1906 sides[i] = sides[0];
1907 dists[i] = dists[0];
1909 if (keepon && !counts[0] && !counts[1])
1920 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1921 if (maxpts > MAX_POINTS_ON_WINDING)
1922 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1924 neww = NewWinding (maxpts);
1926 for (i = 0;i < in->numpoints;i++)
1928 if (neww->numpoints >= maxpts)
1929 Sys_Error ("ClipWinding: points exceeded estimate");
1933 if (sides[i] == SIDE_ON)
1935 VectorCopy (p1, neww->points[neww->numpoints]);
1940 if (sides[i] == SIDE_FRONT)
1942 VectorCopy (p1, neww->points[neww->numpoints]);
1946 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1949 // generate a split point
1950 p2 = in->points[(i+1)%in->numpoints];
1952 dot = dists[i] / (dists[i]-dists[i+1]);
1953 for (j = 0;j < 3;j++)
1954 { // avoid round off error when possible
1955 if (split->normal[j] == 1)
1956 mid[j] = split->dist;
1957 else if (split->normal[j] == -1)
1958 mid[j] = -split->dist;
1960 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1963 VectorCopy (mid, neww->points[neww->numpoints]);
1967 // free the original winding
1978 Divides a winding by a plane, producing one or two windings. The
1979 original winding is not damaged or freed. If only on one side, the
1980 returned winding will be the input winding. If on both sides, two
1981 new windings will be created.
1984 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1986 double dists[MAX_POINTS_ON_WINDING + 1];
1987 int sides[MAX_POINTS_ON_WINDING + 1];
1996 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1998 // determine sides for each point
1999 for (i = 0;i < in->numpoints;i++)
2001 dot = DotProduct (in->points[i], split->normal);
2004 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2005 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2006 else sides[i] = SIDE_ON;
2009 sides[i] = sides[0];
2010 dists[i] = dists[0];
2012 *front = *back = NULL;
2025 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2027 if (maxpts > MAX_POINTS_ON_WINDING)
2028 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2030 *front = f = NewWinding (maxpts);
2031 *back = b = NewWinding (maxpts);
2033 for (i = 0;i < in->numpoints;i++)
2035 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2036 Sys_Error ("DivideWinding: points exceeded estimate");
2040 if (sides[i] == SIDE_ON)
2042 VectorCopy (p1, f->points[f->numpoints]);
2044 VectorCopy (p1, b->points[b->numpoints]);
2049 if (sides[i] == SIDE_FRONT)
2051 VectorCopy (p1, f->points[f->numpoints]);
2054 else if (sides[i] == SIDE_BACK)
2056 VectorCopy (p1, b->points[b->numpoints]);
2060 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2063 // generate a split point
2064 p2 = in->points[(i+1)%in->numpoints];
2066 dot = dists[i] / (dists[i]-dists[i+1]);
2067 for (j = 0;j < 3;j++)
2068 { // avoid round off error when possible
2069 if (split->normal[j] == 1)
2070 mid[j] = split->dist;
2071 else if (split->normal[j] == -1)
2072 mid[j] = -split->dist;
2074 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2077 VectorCopy (mid, f->points[f->numpoints]);
2079 VectorCopy (mid, b->points[b->numpoints]);
2084 typedef struct portal_s
2087 mnode_t *nodes[2]; // [0] = front side of plane
2088 struct portal_s *next[2];
2090 struct portal_s *chain; // all portals are linked into a list
2094 static portal_t *portalchain;
2101 static portal_t *AllocPortal (void)
2104 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2105 p->chain = portalchain;
2110 static void FreePortal(portal_t *p)
2115 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2117 // calculate children first
2118 if (node->children[0]->contents >= 0)
2119 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2120 if (node->children[1]->contents >= 0)
2121 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2123 // make combined bounding box from children
2124 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2125 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2126 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2127 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2128 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2129 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2132 static void Mod_FinalizePortals(void)
2134 int i, j, numportals, numpoints;
2135 portal_t *p, *pnext;
2138 mleaf_t *leaf, *endleaf;
2141 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2142 leaf = loadmodel->leafs;
2143 endleaf = leaf + loadmodel->numleafs;
2144 for (;leaf < endleaf;leaf++)
2146 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2147 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2154 for (i = 0;i < 2;i++)
2156 leaf = (mleaf_t *)p->nodes[i];
2158 for (j = 0;j < w->numpoints;j++)
2160 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2161 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2162 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2163 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2164 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2165 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2172 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2174 // tally up portal and point counts
2180 // note: this check must match the one below or it will usually corrupt memory
2181 // 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
2182 if (p->winding && p->nodes[0] != p->nodes[1]
2183 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2184 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2187 numpoints += p->winding->numpoints * 2;
2191 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2192 loadmodel->numportals = numportals;
2193 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2194 loadmodel->numportalpoints = numpoints;
2195 // clear all leaf portal chains
2196 for (i = 0;i < loadmodel->numleafs;i++)
2197 loadmodel->leafs[i].portals = NULL;
2198 // process all portals in the global portal chain, while freeing them
2199 portal = loadmodel->portals;
2200 point = loadmodel->portalpoints;
2209 // note: this check must match the one above or it will usually corrupt memory
2210 // 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
2211 if (p->nodes[0] != p->nodes[1]
2212 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2213 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2215 // first make the back to front portal (forward portal)
2216 portal->points = point;
2217 portal->numpoints = p->winding->numpoints;
2218 portal->plane.dist = p->plane.dist;
2219 VectorCopy(p->plane.normal, portal->plane.normal);
2220 portal->here = (mleaf_t *)p->nodes[1];
2221 portal->past = (mleaf_t *)p->nodes[0];
2223 for (j = 0;j < portal->numpoints;j++)
2225 VectorCopy(p->winding->points[j], point->position);
2228 PlaneClassify(&portal->plane);
2230 // link into leaf's portal chain
2231 portal->next = portal->here->portals;
2232 portal->here->portals = portal;
2234 // advance to next portal
2237 // then make the front to back portal (backward portal)
2238 portal->points = point;
2239 portal->numpoints = p->winding->numpoints;
2240 portal->plane.dist = -p->plane.dist;
2241 VectorNegate(p->plane.normal, portal->plane.normal);
2242 portal->here = (mleaf_t *)p->nodes[0];
2243 portal->past = (mleaf_t *)p->nodes[1];
2245 for (j = portal->numpoints - 1;j >= 0;j--)
2247 VectorCopy(p->winding->points[j], point->position);
2250 PlaneClassify(&portal->plane);
2252 // link into leaf's portal chain
2253 portal->next = portal->here->portals;
2254 portal->here->portals = portal;
2256 // advance to next portal
2259 FreeWinding(p->winding);
2271 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2274 Host_Error ("AddPortalToNodes: NULL front node");
2276 Host_Error ("AddPortalToNodes: NULL back node");
2277 if (p->nodes[0] || p->nodes[1])
2278 Host_Error ("AddPortalToNodes: already included");
2279 // 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
2281 p->nodes[0] = front;
2282 p->next[0] = (portal_t *)front->portals;
2283 front->portals = (mportal_t *)p;
2286 p->next[1] = (portal_t *)back->portals;
2287 back->portals = (mportal_t *)p;
2292 RemovePortalFromNode
2295 static void RemovePortalFromNodes(portal_t *portal)
2299 void **portalpointer;
2301 for (i = 0;i < 2;i++)
2303 node = portal->nodes[i];
2305 portalpointer = (void **) &node->portals;
2310 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2314 if (portal->nodes[0] == node)
2316 *portalpointer = portal->next[0];
2317 portal->nodes[0] = NULL;
2319 else if (portal->nodes[1] == node)
2321 *portalpointer = portal->next[1];
2322 portal->nodes[1] = NULL;
2325 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2329 if (t->nodes[0] == node)
2330 portalpointer = (void **) &t->next[0];
2331 else if (t->nodes[1] == node)
2332 portalpointer = (void **) &t->next[1];
2334 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2339 static void Mod_RecursiveNodePortals (mnode_t *node)
2342 mnode_t *front, *back, *other_node;
2343 mplane_t clipplane, *plane;
2344 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2345 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2347 // if a leaf, we're done
2351 plane = node->plane;
2353 front = node->children[0];
2354 back = node->children[1];
2356 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2358 // create the new portal by generating a polygon for the node plane,
2359 // and clipping it by all of the other portals (which came from nodes above this one)
2360 nodeportal = AllocPortal ();
2361 nodeportal->plane = *node->plane;
2363 nodeportalwinding = BaseWindingForPlane (node->plane);
2364 side = 0; // shut up compiler warning
2365 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2367 clipplane = portal->plane;
2368 if (portal->nodes[0] == portal->nodes[1])
2369 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2370 if (portal->nodes[0] == node)
2372 else if (portal->nodes[1] == node)
2374 clipplane.dist = -clipplane.dist;
2375 VectorNegate (clipplane.normal, clipplane.normal);
2379 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2381 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2382 if (!nodeportalwinding)
2384 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2389 if (nodeportalwinding)
2391 // if the plane was not clipped on all sides, there was an error
2392 nodeportal->winding = nodeportalwinding;
2393 AddPortalToNodes (nodeportal, front, back);
2396 // split the portals of this node along this node's plane and assign them to the children of this node
2397 // (migrating the portals downward through the tree)
2398 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2400 if (portal->nodes[0] == portal->nodes[1])
2401 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2402 if (portal->nodes[0] == node)
2404 else if (portal->nodes[1] == node)
2407 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2408 nextportal = portal->next[side];
2410 other_node = portal->nodes[!side];
2411 RemovePortalFromNodes (portal);
2413 // cut the portal into two portals, one on each side of the node plane
2414 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2419 AddPortalToNodes (portal, back, other_node);
2421 AddPortalToNodes (portal, other_node, back);
2427 AddPortalToNodes (portal, front, other_node);
2429 AddPortalToNodes (portal, other_node, front);
2433 // the winding is split
2434 splitportal = AllocPortal ();
2435 temp = splitportal->chain;
2436 *splitportal = *portal;
2437 splitportal->chain = temp;
2438 splitportal->winding = backwinding;
2439 FreeWinding (portal->winding);
2440 portal->winding = frontwinding;
2444 AddPortalToNodes (portal, front, other_node);
2445 AddPortalToNodes (splitportal, back, other_node);
2449 AddPortalToNodes (portal, other_node, front);
2450 AddPortalToNodes (splitportal, other_node, back);
2454 Mod_RecursiveNodePortals(front);
2455 Mod_RecursiveNodePortals(back);
2459 static void Mod_MakePortals(void)
2462 Mod_RecursiveNodePortals (loadmodel->nodes);
2463 Mod_FinalizePortals();
2471 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2476 mempool_t *mainmempool;
2479 mod->type = mod_brush;
2481 header = (dheader_t *)buffer;
2483 i = LittleLong (header->version);
2484 if (i != BSPVERSION && i != 30)
2485 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2486 mod->ishlbsp = i == 30;
2487 if (loadmodel->isworldmodel)
2488 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2490 // swap all the lumps
2491 mod_base = (qbyte *)header;
2493 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2494 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2498 // store which lightmap format to use
2499 mod->lightmaprgba = r_lightmaprgba.integer;
2501 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2502 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2503 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2504 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2505 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2506 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2507 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2508 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2509 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2510 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2511 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2512 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2513 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2514 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2515 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2520 mod->numframes = 2; // regular and alternate animation
2522 mainmempool = mod->mempool;
2523 loadname = mod->name;
2525 Mod_LoadLightList ();
2528 // set up the submodels (FIXME: this is confusing)
2530 for (i = 0;i < mod->numsubmodels;i++)
2533 float dist, modelyawradius, modelradius, *vec;
2536 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2537 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2541 bm = &mod->submodels[i];
2543 mod->hulls[0].firstclipnode = bm->headnode[0];
2544 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2546 mod->hulls[j].firstclipnode = bm->headnode[j];
2547 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2550 mod->firstmodelsurface = bm->firstface;
2551 mod->nummodelsurfaces = bm->numfaces;
2553 mod->DrawSky = NULL;
2554 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2555 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2557 // we only need to have a drawsky function if it is used (usually only on world model)
2558 if (surf->shader == &Cshader_sky)
2559 mod->DrawSky = R_DrawBrushModelSky;
2560 for (k = 0;k < surf->numedges;k++)
2562 l = mod->surfedges[k + surf->firstedge];
2564 vec = mod->vertexes[mod->edges[l].v[0]].position;
2566 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2567 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2568 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2569 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2570 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2571 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2572 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2573 dist = vec[0]*vec[0]+vec[1]*vec[1];
2574 if (modelyawradius < dist)
2575 modelyawradius = dist;
2576 dist += vec[2]*vec[2];
2577 if (modelradius < dist)
2581 modelyawradius = sqrt(modelyawradius);
2582 modelradius = sqrt(modelradius);
2583 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2584 mod->yawmins[2] = mod->normalmins[2];
2585 mod->yawmaxs[2] = mod->normalmaxs[2];
2586 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2587 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2588 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2589 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2591 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2592 VectorClear(mod->normalmins);
2593 VectorClear(mod->normalmaxs);
2594 VectorClear(mod->yawmins);
2595 VectorClear(mod->yawmaxs);
2596 VectorClear(mod->rotatedmins);
2597 VectorClear(mod->rotatedmaxs);
2600 mod->numleafs = bm->visleafs;
2602 mod->Draw = R_DrawBrushModelNormal;
2603 mod->DrawShadow = NULL;
2605 Mod_BrushSortedSurfaces(mod, mainmempool);
2607 // LordHavoc: only register submodels if it is the world
2608 // (prevents bsp models from replacing world submodels)
2609 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2612 // duplicate the basic information
2613 sprintf (name, "*%i", i+1);
2614 loadmodel = Mod_FindName (name);
2616 strcpy (loadmodel->name, name);
2617 // textures and memory belong to the main model
2618 loadmodel->texturepool = NULL;
2619 loadmodel->mempool = NULL;