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 (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)
1256 // convert edges back to a normal polygon
1257 surf->poly_numverts = surf->numedges;
1258 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * surf->numedges);
1259 for (i = 0;i < surf->numedges;i++)
1261 lindex = loadmodel->surfedges[surf->firstedge + i];
1263 vec = loadmodel->vertexes[loadmodel->edges[lindex].v[0]].position;
1265 vec = loadmodel->vertexes[loadmodel->edges[-lindex].v[1]].position;
1266 VectorCopy (vec, vert);
1271 static void Mod_SplitSurfMeshIfTooBig(msurface_t *s)
1273 int j, base, tricount, newvertexcount, *index, *vertexremap;
1274 surfmesh_t *newmesh, *oldmesh, *firstmesh;
1275 if (s->mesh->numtriangles > 1000)
1277 vertexremap = Mem_Alloc(tempmempool, s->mesh->numverts * sizeof(int));
1282 while (base < s->mesh->numtriangles)
1284 tricount = s->mesh->numtriangles - base;
1285 if (tricount > 1000)
1287 index = s->mesh->index + base * 3;
1291 memset(vertexremap, -1, s->mesh->numverts * sizeof(int));
1292 for (j = 0;j < tricount * 3;j++)
1293 if (vertexremap[index[j]] < 0)
1294 vertexremap[index[j]] = newvertexcount++;
1296 newmesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + newvertexcount * sizeof(surfvertex_t) + tricount * sizeof(int[3]));
1297 newmesh->chain = NULL;
1298 newmesh->numverts = newvertexcount;
1299 newmesh->numtriangles = tricount;
1300 newmesh->vertex = (surfvertex_t *)(newmesh + 1);
1301 newmesh->index = (int *)(newmesh->vertex + newvertexcount);
1302 for (j = 0;j < tricount * 3;j++)
1304 newmesh->index[j] = vertexremap[index[j]];
1305 // yes this copies the same vertex multiple times in many cases... but that's ok...
1306 memcpy(&newmesh->vertex[newmesh->index[j]], &s->mesh->vertex[index[j]], sizeof(surfvertex_t));
1309 oldmesh->chain = newmesh;
1311 firstmesh = newmesh;
1314 Mem_Free(vertexremap);
1316 s->mesh = firstmesh;
1325 static void Mod_LoadFaces (lump_t *l)
1329 int i, count, surfnum, planenum, ssize, tsize;
1331 in = (void *)(mod_base + l->fileofs);
1332 if (l->filelen % sizeof(*in))
1333 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1334 count = l->filelen / sizeof(*in);
1335 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1337 loadmodel->surfaces = out;
1338 loadmodel->numsurfaces = count;
1340 for (surfnum = 0;surfnum < count;surfnum++, in++, out++)
1342 // FIXME: validate edges, texinfo, etc?
1343 out->firstedge = LittleLong(in->firstedge);
1344 out->numedges = LittleShort(in->numedges);
1345 if ((unsigned int) out->firstedge + (unsigned int) out->numedges > (unsigned int) loadmodel->numsurfedges)
1346 Host_Error("Mod_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", out->firstedge, out->numedges, loadmodel->numsurfedges);
1348 i = LittleShort (in->texinfo);
1349 if ((unsigned int) i >= (unsigned int) loadmodel->numtexinfo)
1350 Host_Error("Mod_LoadFaces: invalid texinfo index %i (model has %i texinfos)\n", i, loadmodel->numtexinfo);
1351 out->texinfo = loadmodel->texinfo + i;
1352 out->flags = out->texinfo->texture->flags;
1354 planenum = LittleShort(in->planenum);
1355 if ((unsigned int) planenum >= (unsigned int) loadmodel->numplanes)
1356 Host_Error("Mod_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->numplanes);
1358 if (LittleShort(in->side))
1359 out->flags |= SURF_PLANEBACK;
1361 out->plane = loadmodel->planes + planenum;
1363 // clear lightmap (filled in later)
1364 out->lightmaptexture = NULL;
1366 // force lightmap upload on first time seeing the surface
1367 out->cached_dlight = true;
1368 out->cached_ambient = -1000;
1369 out->cached_lightscalebit = -1000;
1371 CalcSurfaceExtents (out);
1373 ssize = (out->extents[0] >> 4) + 1;
1374 tsize = (out->extents[1] >> 4) + 1;
1377 for (i = 0;i < MAXLIGHTMAPS;i++)
1378 out->styles[i] = in->styles[i];
1379 i = LittleLong(in->lightofs);
1381 out->samples = NULL;
1382 else if (loadmodel->ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1383 out->samples = loadmodel->lightdata + i;
1384 else // LordHavoc: white lighting (bsp version 29)
1385 out->samples = loadmodel->lightdata + (i * 3);
1387 Mod_GenerateSurfacePolygon(out);
1389 if (out->texinfo->texture->flags & SURF_DRAWSKY)
1391 out->shader = &Cshader_sky;
1392 out->samples = NULL;
1393 Mod_GenerateVertexMesh (out);
1395 else if (out->texinfo->texture->flags & SURF_DRAWTURB)
1397 out->shader = &Cshader_water;
1398 out->samples = NULL;
1399 Mod_GenerateVertexMesh (out);
1403 if (!R_TextureHasAlpha(out->texinfo->texture->texture))
1404 out->flags |= SURF_CLIPSOLID;
1405 if (out->texinfo->flags & TEX_SPECIAL)
1407 // qbsp couldn't find the texture for this surface, but it was either turb or sky... assume turb
1408 out->shader = &Cshader_water;
1409 out->shader = &Cshader_water;
1410 out->samples = NULL;
1411 Mod_GenerateVertexMesh (out);
1413 else if ((out->extents[0] >> 4) + 1 > (256) || (out->extents[1] >> 4) + 1 > (256))
1415 Con_Printf ("Bad surface extents, converting to fullbright polygon");
1416 out->shader = &Cshader_wall_fullbright;
1417 out->samples = NULL;
1418 Mod_GenerateVertexMesh(out);
1422 // stainmap for permanent marks on walls
1423 out->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1425 memset(out->stainsamples, 255, ssize * tsize * 3);
1426 if (out->extents[0] < r_vertexsurfacesthreshold.integer && out->extents[1] < r_vertexsurfacesthreshold.integer)
1428 out->shader = &Cshader_wall_vertex;
1429 Mod_GenerateVertexLitMesh(out);
1433 out->shader = &Cshader_wall_lightmap;
1434 Mod_GenerateLightmappedMesh(out);
1438 Mod_SplitSurfMeshIfTooBig(out);
1442 static model_t *sortmodel;
1444 static int Mod_SurfaceQSortCompare(const void *voida, const void *voidb)
1446 const msurface_t *a, *b;
1447 a = *((const msurface_t **)voida);
1448 b = *((const msurface_t **)voidb);
1449 if (a->shader != b->shader)
1450 return (qbyte *) a->shader - (qbyte *) b->shader;
1451 if (a->texinfo->texture != b->texinfo->texture);
1452 return a->texinfo->texture - b->texinfo->texture;
1456 static void Mod_BrushSortedSurfaces(model_t *model, mempool_t *pool)
1460 sortmodel->modelsortedsurfaces = Mem_Alloc(pool, sortmodel->nummodelsurfaces * sizeof(msurface_t *));
1461 for (surfnum = 0;surfnum < sortmodel->nummodelsurfaces;surfnum++)
1462 sortmodel->modelsortedsurfaces[surfnum] = &sortmodel->surfaces[surfnum + sortmodel->firstmodelsurface];
1464 if (r_sortsurfaces.integer)
1465 qsort(sortmodel->modelsortedsurfaces, sortmodel->nummodelsurfaces, sizeof(msurface_t *), Mod_SurfaceQSortCompare);
1474 static void Mod_SetParent (mnode_t *node, mnode_t *parent)
1476 node->parent = parent;
1477 if (node->contents < 0)
1479 Mod_SetParent (node->children[0], node);
1480 Mod_SetParent (node->children[1], node);
1488 static void Mod_LoadNodes (lump_t *l)
1494 in = (void *)(mod_base + l->fileofs);
1495 if (l->filelen % sizeof(*in))
1496 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1497 count = l->filelen / sizeof(*in);
1498 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1500 loadmodel->nodes = out;
1501 loadmodel->numnodes = count;
1503 for ( i=0 ; i<count ; i++, in++, out++)
1505 for (j=0 ; j<3 ; j++)
1507 out->mins[j] = LittleShort (in->mins[j]);
1508 out->maxs[j] = LittleShort (in->maxs[j]);
1511 p = LittleLong(in->planenum);
1512 out->plane = loadmodel->planes + p;
1514 out->firstsurface = LittleShort (in->firstface);
1515 out->numsurfaces = LittleShort (in->numfaces);
1517 for (j=0 ; j<2 ; j++)
1519 p = LittleShort (in->children[j]);
1521 out->children[j] = loadmodel->nodes + p;
1523 out->children[j] = (mnode_t *)(loadmodel->leafs + (-1 - p));
1527 Mod_SetParent (loadmodel->nodes, NULL); // sets nodes and leafs
1535 static void Mod_LoadLeafs (lump_t *l)
1541 in = (void *)(mod_base + l->fileofs);
1542 if (l->filelen % sizeof(*in))
1543 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1544 count = l->filelen / sizeof(*in);
1545 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1547 loadmodel->leafs = out;
1548 loadmodel->numleafs = count;
1550 for ( i=0 ; i<count ; i++, in++, out++)
1552 for (j=0 ; j<3 ; j++)
1554 out->mins[j] = LittleShort (in->mins[j]);
1555 out->maxs[j] = LittleShort (in->maxs[j]);
1558 p = LittleLong(in->contents);
1561 out->firstmarksurface = loadmodel->marksurfaces +
1562 LittleShort(in->firstmarksurface);
1563 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1565 p = LittleLong(in->visofs);
1567 out->compressed_vis = NULL;
1569 out->compressed_vis = loadmodel->visdata + p;
1571 for (j=0 ; j<4 ; j++)
1572 out->ambient_sound_level[j] = in->ambient_level[j];
1574 // FIXME: Insert caustics here
1583 static void Mod_LoadClipnodes (lump_t *l)
1585 dclipnode_t *in, *out;
1589 in = (void *)(mod_base + l->fileofs);
1590 if (l->filelen % sizeof(*in))
1591 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1592 count = l->filelen / sizeof(*in);
1593 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1595 loadmodel->clipnodes = out;
1596 loadmodel->numclipnodes = count;
1598 if (loadmodel->ishlbsp)
1600 hull = &loadmodel->hulls[1];
1601 hull->clipnodes = out;
1602 hull->firstclipnode = 0;
1603 hull->lastclipnode = count-1;
1604 hull->planes = loadmodel->planes;
1605 hull->clip_mins[0] = -16;
1606 hull->clip_mins[1] = -16;
1607 hull->clip_mins[2] = -36;
1608 hull->clip_maxs[0] = 16;
1609 hull->clip_maxs[1] = 16;
1610 hull->clip_maxs[2] = 36;
1611 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1613 hull = &loadmodel->hulls[2];
1614 hull->clipnodes = out;
1615 hull->firstclipnode = 0;
1616 hull->lastclipnode = count-1;
1617 hull->planes = loadmodel->planes;
1618 hull->clip_mins[0] = -32;
1619 hull->clip_mins[1] = -32;
1620 hull->clip_mins[2] = -32;
1621 hull->clip_maxs[0] = 32;
1622 hull->clip_maxs[1] = 32;
1623 hull->clip_maxs[2] = 32;
1624 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1626 hull = &loadmodel->hulls[3];
1627 hull->clipnodes = out;
1628 hull->firstclipnode = 0;
1629 hull->lastclipnode = count-1;
1630 hull->planes = loadmodel->planes;
1631 hull->clip_mins[0] = -16;
1632 hull->clip_mins[1] = -16;
1633 hull->clip_mins[2] = -18;
1634 hull->clip_maxs[0] = 16;
1635 hull->clip_maxs[1] = 16;
1636 hull->clip_maxs[2] = 18;
1637 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1641 hull = &loadmodel->hulls[1];
1642 hull->clipnodes = out;
1643 hull->firstclipnode = 0;
1644 hull->lastclipnode = count-1;
1645 hull->planes = loadmodel->planes;
1646 hull->clip_mins[0] = -16;
1647 hull->clip_mins[1] = -16;
1648 hull->clip_mins[2] = -24;
1649 hull->clip_maxs[0] = 16;
1650 hull->clip_maxs[1] = 16;
1651 hull->clip_maxs[2] = 32;
1652 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1654 hull = &loadmodel->hulls[2];
1655 hull->clipnodes = out;
1656 hull->firstclipnode = 0;
1657 hull->lastclipnode = count-1;
1658 hull->planes = loadmodel->planes;
1659 hull->clip_mins[0] = -32;
1660 hull->clip_mins[1] = -32;
1661 hull->clip_mins[2] = -24;
1662 hull->clip_maxs[0] = 32;
1663 hull->clip_maxs[1] = 32;
1664 hull->clip_maxs[2] = 64;
1665 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1668 for (i=0 ; i<count ; i++, out++, in++)
1670 out->planenum = LittleLong(in->planenum);
1671 out->children[0] = LittleShort(in->children[0]);
1672 out->children[1] = LittleShort(in->children[1]);
1673 if (out->children[0] >= count || out->children[1] >= count)
1674 Host_Error("Corrupt clipping hull (out of range child)\n");
1682 Duplicate the drawing hull structure as a clipping hull
1685 static void Mod_MakeHull0 (void)
1692 hull = &loadmodel->hulls[0];
1694 in = loadmodel->nodes;
1695 out = Mem_Alloc(loadmodel->mempool, loadmodel->numnodes * sizeof(dclipnode_t));
1697 hull->clipnodes = out;
1698 hull->firstclipnode = 0;
1699 hull->lastclipnode = loadmodel->numnodes - 1;
1700 hull->planes = loadmodel->planes;
1702 for (i = 0;i < loadmodel->numnodes;i++, out++, in++)
1704 out->planenum = in->plane - loadmodel->planes;
1705 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->nodes;
1706 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->nodes;
1712 Mod_LoadMarksurfaces
1715 static void Mod_LoadMarksurfaces (lump_t *l)
1720 in = (void *)(mod_base + l->fileofs);
1721 if (l->filelen % sizeof(*in))
1722 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1723 loadmodel->nummarksurfaces = l->filelen / sizeof(*in);
1724 loadmodel->marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->nummarksurfaces * sizeof(msurface_t *));
1726 for (i = 0;i < loadmodel->nummarksurfaces;i++)
1728 j = (unsigned) LittleShort(in[i]);
1729 if (j >= loadmodel->numsurfaces)
1730 Host_Error ("Mod_ParseMarksurfaces: bad surface number");
1731 loadmodel->marksurfaces[i] = loadmodel->surfaces + j;
1740 static void Mod_LoadSurfedges (lump_t *l)
1745 in = (void *)(mod_base + l->fileofs);
1746 if (l->filelen % sizeof(*in))
1747 Host_Error ("MOD_LoadBmodel: funny lump size in %s",loadmodel->name);
1748 loadmodel->numsurfedges = l->filelen / sizeof(*in);
1749 loadmodel->surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->numsurfedges * sizeof(int));
1751 for (i = 0;i < loadmodel->numsurfedges;i++)
1752 loadmodel->surfedges[i] = LittleLong (in[i]);
1761 static void Mod_LoadPlanes (lump_t *l)
1767 in = (void *)(mod_base + l->fileofs);
1768 if (l->filelen % sizeof(*in))
1769 Host_Error ("MOD_LoadBmodel: funny lump size in %s", loadmodel->name);
1771 loadmodel->numplanes = l->filelen / sizeof(*in);
1772 loadmodel->planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->numplanes * sizeof(*out));
1774 for (i = 0;i < loadmodel->numplanes;i++, in++, out++)
1776 out->normal[0] = LittleFloat (in->normal[0]);
1777 out->normal[1] = LittleFloat (in->normal[1]);
1778 out->normal[2] = LittleFloat (in->normal[2]);
1779 out->dist = LittleFloat (in->dist);
1785 #define MAX_POINTS_ON_WINDING 64
1791 double points[8][3]; // variable sized
1800 static winding_t *NewWinding (int points)
1805 if (points > MAX_POINTS_ON_WINDING)
1806 Sys_Error("NewWinding: too many points\n");
1808 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1809 w = Mem_Alloc(loadmodel->mempool, size);
1810 memset (w, 0, size);
1815 static void FreeWinding (winding_t *w)
1825 static winding_t *BaseWindingForPlane (mplane_t *p)
1827 double org[3], vright[3], vup[3], normal[3];
1830 VectorCopy(p->normal, normal);
1831 VectorVectorsDouble(normal, vright, vup);
1833 VectorScale (vup, 1024.0*1024.0*1024.0, vup);
1834 VectorScale (vright, 1024.0*1024.0*1024.0, vright);
1836 // project a really big axis aligned box onto the plane
1839 VectorScale (p->normal, p->dist, org);
1841 VectorSubtract (org, vright, w->points[0]);
1842 VectorAdd (w->points[0], vup, w->points[0]);
1844 VectorAdd (org, vright, w->points[1]);
1845 VectorAdd (w->points[1], vup, w->points[1]);
1847 VectorAdd (org, vright, w->points[2]);
1848 VectorSubtract (w->points[2], vup, w->points[2]);
1850 VectorSubtract (org, vright, w->points[3]);
1851 VectorSubtract (w->points[3], vup, w->points[3]);
1862 Clips the winding to the plane, returning the new winding on the positive side
1863 Frees the input winding.
1864 If keepon is true, an exactly on-plane winding will be saved, otherwise
1865 it will be clipped away.
1868 static winding_t *ClipWinding (winding_t *in, mplane_t *split, int keepon)
1870 double dists[MAX_POINTS_ON_WINDING + 1];
1871 int sides[MAX_POINTS_ON_WINDING + 1];
1880 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1882 // determine sides for each point
1883 for (i = 0;i < in->numpoints;i++)
1885 dists[i] = dot = DotProduct (in->points[i], split->normal) - split->dist;
1886 if (dot > ON_EPSILON)
1887 sides[i] = SIDE_FRONT;
1888 else if (dot < -ON_EPSILON)
1889 sides[i] = SIDE_BACK;
1894 sides[i] = sides[0];
1895 dists[i] = dists[0];
1897 if (keepon && !counts[0] && !counts[1])
1908 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1909 if (maxpts > MAX_POINTS_ON_WINDING)
1910 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1912 neww = NewWinding (maxpts);
1914 for (i = 0;i < in->numpoints;i++)
1916 if (neww->numpoints >= maxpts)
1917 Sys_Error ("ClipWinding: points exceeded estimate");
1921 if (sides[i] == SIDE_ON)
1923 VectorCopy (p1, neww->points[neww->numpoints]);
1928 if (sides[i] == SIDE_FRONT)
1930 VectorCopy (p1, neww->points[neww->numpoints]);
1934 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
1937 // generate a split point
1938 p2 = in->points[(i+1)%in->numpoints];
1940 dot = dists[i] / (dists[i]-dists[i+1]);
1941 for (j = 0;j < 3;j++)
1942 { // avoid round off error when possible
1943 if (split->normal[j] == 1)
1944 mid[j] = split->dist;
1945 else if (split->normal[j] == -1)
1946 mid[j] = -split->dist;
1948 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
1951 VectorCopy (mid, neww->points[neww->numpoints]);
1955 // free the original winding
1966 Divides a winding by a plane, producing one or two windings. The
1967 original winding is not damaged or freed. If only on one side, the
1968 returned winding will be the input winding. If on both sides, two
1969 new windings will be created.
1972 static void DivideWinding (winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
1974 double dists[MAX_POINTS_ON_WINDING + 1];
1975 int sides[MAX_POINTS_ON_WINDING + 1];
1984 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1986 // determine sides for each point
1987 for (i = 0;i < in->numpoints;i++)
1989 dot = DotProduct (in->points[i], split->normal);
1992 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
1993 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
1994 else sides[i] = SIDE_ON;
1997 sides[i] = sides[0];
1998 dists[i] = dists[0];
2000 *front = *back = NULL;
2013 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2015 if (maxpts > MAX_POINTS_ON_WINDING)
2016 Sys_Error ("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2018 *front = f = NewWinding (maxpts);
2019 *back = b = NewWinding (maxpts);
2021 for (i = 0;i < in->numpoints;i++)
2023 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2024 Sys_Error ("DivideWinding: points exceeded estimate");
2028 if (sides[i] == SIDE_ON)
2030 VectorCopy (p1, f->points[f->numpoints]);
2032 VectorCopy (p1, b->points[b->numpoints]);
2037 if (sides[i] == SIDE_FRONT)
2039 VectorCopy (p1, f->points[f->numpoints]);
2042 else if (sides[i] == SIDE_BACK)
2044 VectorCopy (p1, b->points[b->numpoints]);
2048 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2051 // generate a split point
2052 p2 = in->points[(i+1)%in->numpoints];
2054 dot = dists[i] / (dists[i]-dists[i+1]);
2055 for (j = 0;j < 3;j++)
2056 { // avoid round off error when possible
2057 if (split->normal[j] == 1)
2058 mid[j] = split->dist;
2059 else if (split->normal[j] == -1)
2060 mid[j] = -split->dist;
2062 mid[j] = p1[j] + dot*(p2[j]-p1[j]);
2065 VectorCopy (mid, f->points[f->numpoints]);
2067 VectorCopy (mid, b->points[b->numpoints]);
2072 typedef struct portal_s
2075 mnode_t *nodes[2]; // [0] = front side of plane
2076 struct portal_s *next[2];
2078 struct portal_s *chain; // all portals are linked into a list
2082 static portal_t *portalchain;
2089 static portal_t *AllocPortal (void)
2092 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2093 p->chain = portalchain;
2098 static void FreePortal(portal_t *p)
2103 static void Mod_RecursiveRecalcNodeBBox(mnode_t *node)
2105 // calculate children first
2106 if (node->children[0]->contents >= 0)
2107 Mod_RecursiveRecalcNodeBBox(node->children[0]);
2108 if (node->children[1]->contents >= 0)
2109 Mod_RecursiveRecalcNodeBBox(node->children[1]);
2111 // make combined bounding box from children
2112 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2113 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2114 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2115 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2116 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2117 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2120 static void Mod_FinalizePortals(void)
2122 int i, j, numportals, numpoints;
2123 portal_t *p, *pnext;
2126 mleaf_t *leaf, *endleaf;
2129 // recalculate bounding boxes for all leafs (because qbsp is very sloppy)
2130 leaf = loadmodel->leafs;
2131 endleaf = leaf + loadmodel->numleafs;
2132 for (;leaf < endleaf;leaf++)
2134 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2135 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2142 for (i = 0;i < 2;i++)
2144 leaf = (mleaf_t *)p->nodes[i];
2146 for (j = 0;j < w->numpoints;j++)
2148 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2149 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2150 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2151 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2152 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2153 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2160 Mod_RecursiveRecalcNodeBBox(loadmodel->nodes);
2162 // tally up portal and point counts
2168 // note: this check must match the one below or it will usually corrupt memory
2169 // 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
2170 if (p->winding && p->nodes[0] != p->nodes[1]
2171 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2172 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2175 numpoints += p->winding->numpoints * 2;
2179 loadmodel->portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2180 loadmodel->numportals = numportals;
2181 loadmodel->portalpoints = (void *) ((qbyte *) loadmodel->portals + numportals * sizeof(mportal_t));
2182 loadmodel->numportalpoints = numpoints;
2183 // clear all leaf portal chains
2184 for (i = 0;i < loadmodel->numleafs;i++)
2185 loadmodel->leafs[i].portals = NULL;
2186 // process all portals in the global portal chain, while freeing them
2187 portal = loadmodel->portals;
2188 point = loadmodel->portalpoints;
2197 // note: this check must match the one above or it will usually corrupt memory
2198 // 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
2199 if (p->nodes[0] != p->nodes[1]
2200 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2201 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2203 // first make the back to front portal (forward portal)
2204 portal->points = point;
2205 portal->numpoints = p->winding->numpoints;
2206 portal->plane.dist = p->plane.dist;
2207 VectorCopy(p->plane.normal, portal->plane.normal);
2208 portal->here = (mleaf_t *)p->nodes[1];
2209 portal->past = (mleaf_t *)p->nodes[0];
2211 for (j = 0;j < portal->numpoints;j++)
2213 VectorCopy(p->winding->points[j], point->position);
2216 PlaneClassify(&portal->plane);
2218 // link into leaf's portal chain
2219 portal->next = portal->here->portals;
2220 portal->here->portals = portal;
2222 // advance to next portal
2225 // then make the front to back portal (backward portal)
2226 portal->points = point;
2227 portal->numpoints = p->winding->numpoints;
2228 portal->plane.dist = -p->plane.dist;
2229 VectorNegate(p->plane.normal, portal->plane.normal);
2230 portal->here = (mleaf_t *)p->nodes[0];
2231 portal->past = (mleaf_t *)p->nodes[1];
2233 for (j = portal->numpoints - 1;j >= 0;j--)
2235 VectorCopy(p->winding->points[j], point->position);
2238 PlaneClassify(&portal->plane);
2240 // link into leaf's portal chain
2241 portal->next = portal->here->portals;
2242 portal->here->portals = portal;
2244 // advance to next portal
2247 FreeWinding(p->winding);
2259 static void AddPortalToNodes (portal_t *p, mnode_t *front, mnode_t *back)
2262 Host_Error ("AddPortalToNodes: NULL front node");
2264 Host_Error ("AddPortalToNodes: NULL back node");
2265 if (p->nodes[0] || p->nodes[1])
2266 Host_Error ("AddPortalToNodes: already included");
2267 // 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
2269 p->nodes[0] = front;
2270 p->next[0] = (portal_t *)front->portals;
2271 front->portals = (mportal_t *)p;
2274 p->next[1] = (portal_t *)back->portals;
2275 back->portals = (mportal_t *)p;
2280 RemovePortalFromNode
2283 static void RemovePortalFromNodes(portal_t *portal)
2287 void **portalpointer;
2289 for (i = 0;i < 2;i++)
2291 node = portal->nodes[i];
2293 portalpointer = (void **) &node->portals;
2298 Host_Error ("RemovePortalFromNodes: portal not in leaf");
2302 if (portal->nodes[0] == node)
2304 *portalpointer = portal->next[0];
2305 portal->nodes[0] = NULL;
2307 else if (portal->nodes[1] == node)
2309 *portalpointer = portal->next[1];
2310 portal->nodes[1] = NULL;
2313 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2317 if (t->nodes[0] == node)
2318 portalpointer = (void **) &t->next[0];
2319 else if (t->nodes[1] == node)
2320 portalpointer = (void **) &t->next[1];
2322 Host_Error ("RemovePortalFromNodes: portal not bounding leaf");
2327 static void Mod_RecursiveNodePortals (mnode_t *node)
2330 mnode_t *front, *back, *other_node;
2331 mplane_t clipplane, *plane;
2332 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2333 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2335 // if a leaf, we're done
2339 plane = node->plane;
2341 front = node->children[0];
2342 back = node->children[1];
2344 Host_Error("Mod_RecursiveNodePortals: corrupt node hierarchy");
2346 // create the new portal by generating a polygon for the node plane,
2347 // and clipping it by all of the other portals (which came from nodes above this one)
2348 nodeportal = AllocPortal ();
2349 nodeportal->plane = *node->plane;
2351 nodeportalwinding = BaseWindingForPlane (node->plane);
2352 side = 0; // shut up compiler warning
2353 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2355 clipplane = portal->plane;
2356 if (portal->nodes[0] == portal->nodes[1])
2357 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (1)");
2358 if (portal->nodes[0] == node)
2360 else if (portal->nodes[1] == node)
2362 clipplane.dist = -clipplane.dist;
2363 VectorNegate (clipplane.normal, clipplane.normal);
2367 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2369 nodeportalwinding = ClipWinding (nodeportalwinding, &clipplane, true);
2370 if (!nodeportalwinding)
2372 printf ("Mod_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2377 if (nodeportalwinding)
2379 // if the plane was not clipped on all sides, there was an error
2380 nodeportal->winding = nodeportalwinding;
2381 AddPortalToNodes (nodeportal, front, back);
2384 // split the portals of this node along this node's plane and assign them to the children of this node
2385 // (migrating the portals downward through the tree)
2386 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2388 if (portal->nodes[0] == portal->nodes[1])
2389 Host_Error("Mod_RecursiveNodePortals: portal has same node on both sides (2)");
2390 if (portal->nodes[0] == node)
2392 else if (portal->nodes[1] == node)
2395 Host_Error ("Mod_RecursiveNodePortals: mislinked portal");
2396 nextportal = portal->next[side];
2398 other_node = portal->nodes[!side];
2399 RemovePortalFromNodes (portal);
2401 // cut the portal into two portals, one on each side of the node plane
2402 DivideWinding (portal->winding, plane, &frontwinding, &backwinding);
2407 AddPortalToNodes (portal, back, other_node);
2409 AddPortalToNodes (portal, other_node, back);
2415 AddPortalToNodes (portal, front, other_node);
2417 AddPortalToNodes (portal, other_node, front);
2421 // the winding is split
2422 splitportal = AllocPortal ();
2423 temp = splitportal->chain;
2424 *splitportal = *portal;
2425 splitportal->chain = temp;
2426 splitportal->winding = backwinding;
2427 FreeWinding (portal->winding);
2428 portal->winding = frontwinding;
2432 AddPortalToNodes (portal, front, other_node);
2433 AddPortalToNodes (splitportal, back, other_node);
2437 AddPortalToNodes (portal, other_node, front);
2438 AddPortalToNodes (splitportal, other_node, back);
2442 Mod_RecursiveNodePortals(front);
2443 Mod_RecursiveNodePortals(back);
2447 static void Mod_MakePortals(void)
2450 Mod_RecursiveNodePortals (loadmodel->nodes);
2451 Mod_FinalizePortals();
2459 void Mod_LoadBrushModel (model_t *mod, void *buffer)
2464 mempool_t *mainmempool;
2467 mod->type = mod_brush;
2469 header = (dheader_t *)buffer;
2471 i = LittleLong (header->version);
2472 if (i != BSPVERSION && i != 30)
2473 Host_Error ("Mod_LoadBrushModel: %s has wrong version number (%i should be %i (Quake) or 30 (HalfLife))", mod->name, i, BSPVERSION);
2474 mod->ishlbsp = i == 30;
2475 if (loadmodel->isworldmodel)
2476 Cvar_SetValue("halflifebsp", mod->ishlbsp);
2478 // swap all the lumps
2479 mod_base = (qbyte *)header;
2481 for (i=0 ; i<sizeof(dheader_t)/4 ; i++)
2482 ((int *)header)[i] = LittleLong ( ((int *)header)[i]);
2486 // store which lightmap format to use
2487 mod->lightmaprgba = r_lightmaprgba.integer;
2489 Mod_LoadEntities (&header->lumps[LUMP_ENTITIES]);
2490 Mod_LoadVertexes (&header->lumps[LUMP_VERTEXES]);
2491 Mod_LoadEdges (&header->lumps[LUMP_EDGES]);
2492 Mod_LoadSurfedges (&header->lumps[LUMP_SURFEDGES]);
2493 Mod_LoadTextures (&header->lumps[LUMP_TEXTURES]);
2494 Mod_LoadLighting (&header->lumps[LUMP_LIGHTING]);
2495 Mod_LoadPlanes (&header->lumps[LUMP_PLANES]);
2496 Mod_LoadTexinfo (&header->lumps[LUMP_TEXINFO]);
2497 Mod_LoadFaces (&header->lumps[LUMP_FACES]);
2498 Mod_LoadMarksurfaces (&header->lumps[LUMP_MARKSURFACES]);
2499 Mod_LoadVisibility (&header->lumps[LUMP_VISIBILITY]);
2500 Mod_LoadLeafs (&header->lumps[LUMP_LEAFS]);
2501 Mod_LoadNodes (&header->lumps[LUMP_NODES]);
2502 Mod_LoadClipnodes (&header->lumps[LUMP_CLIPNODES]);
2503 Mod_LoadSubmodels (&header->lumps[LUMP_MODELS]);
2508 mod->numframes = 2; // regular and alternate animation
2510 mainmempool = mod->mempool;
2511 loadname = mod->name;
2513 Mod_LoadLightList ();
2516 // set up the submodels (FIXME: this is confusing)
2518 for (i = 0;i < mod->numsubmodels;i++)
2521 float dist, modelyawradius, modelradius, *vec;
2524 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2525 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2529 bm = &mod->submodels[i];
2531 mod->hulls[0].firstclipnode = bm->headnode[0];
2532 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2534 mod->hulls[j].firstclipnode = bm->headnode[j];
2535 mod->hulls[j].lastclipnode = mod->numclipnodes - 1;
2538 mod->firstmodelsurface = bm->firstface;
2539 mod->nummodelsurfaces = bm->numfaces;
2541 mod->DrawSky = NULL;
2542 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2543 for (j = 0, surf = &mod->surfaces[mod->firstmodelsurface];j < mod->nummodelsurfaces;j++, surf++)
2545 // we only need to have a drawsky function if it is used (usually only on world model)
2546 if (surf->shader == &Cshader_sky)
2547 mod->DrawSky = R_DrawBrushModelSky;
2548 for (k = 0;k < surf->numedges;k++)
2550 l = mod->surfedges[k + surf->firstedge];
2552 vec = mod->vertexes[mod->edges[l].v[0]].position;
2554 vec = mod->vertexes[mod->edges[-l].v[1]].position;
2555 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2556 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2557 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2558 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2559 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2560 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2561 dist = vec[0]*vec[0]+vec[1]*vec[1];
2562 if (modelyawradius < dist)
2563 modelyawradius = dist;
2564 dist += vec[2]*vec[2];
2565 if (modelradius < dist)
2569 modelyawradius = sqrt(modelyawradius);
2570 modelradius = sqrt(modelradius);
2571 mod->yawmins[0] = mod->yawmins[1] = -(mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2572 mod->yawmins[2] = mod->normalmins[2];
2573 mod->yawmaxs[2] = mod->normalmaxs[2];
2574 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2575 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2576 // LordHavoc: check for empty submodels (lacrima.bsp has such a glitch)
2577 if (mod->normalmins[0] > mod->normalmaxs[0] || mod->normalmins[1] > mod->normalmaxs[1] || mod->normalmins[2] > mod->normalmaxs[2])
2579 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2580 VectorClear(mod->normalmins);
2581 VectorClear(mod->normalmaxs);
2582 VectorClear(mod->yawmins);
2583 VectorClear(mod->yawmaxs);
2584 VectorClear(mod->rotatedmins);
2585 VectorClear(mod->rotatedmaxs);
2588 mod->numleafs = bm->visleafs;
2590 mod->Draw = R_DrawBrushModelNormal;
2591 mod->DrawShadow = NULL;
2593 Mod_BrushSortedSurfaces(mod, mainmempool);
2595 // LordHavoc: only register submodels if it is the world
2596 // (prevents bsp models from replacing world submodels)
2597 if (loadmodel->isworldmodel && i < (mod->numsubmodels - 1))
2600 // duplicate the basic information
2601 sprintf (name, "*%i", i+1);
2602 loadmodel = Mod_FindName (name);
2604 strcpy (loadmodel->name, name);
2605 // textures and memory belong to the main model
2606 loadmodel->texturepool = NULL;
2607 loadmodel->mempool = NULL;