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.
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0"};
31 cvar_t r_novis = {0, "r_novis", "0"};
32 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
33 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
34 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
35 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
37 void Mod_BrushInit(void)
39 // Cvar_RegisterVariable(&r_subdivide_size);
40 Cvar_RegisterVariable(&halflifebsp);
41 Cvar_RegisterVariable(&r_novis);
42 Cvar_RegisterVariable(&r_miplightmaps);
43 Cvar_RegisterVariable(&r_lightmaprgba);
44 Cvar_RegisterVariable(&r_nosurftextures);
45 Cvar_RegisterVariable(&r_sortsurfaces);
46 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
56 Mod_CheckLoaded(model);
58 // LordHavoc: modified to start at first clip node,
59 // in other words: first node of the (sub)model
60 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
61 while (node->contents == 0)
62 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
64 return (mleaf_t *)node;
67 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
72 return CONTENTS_EMPTY;
74 Mod_CheckLoaded(model);
76 // LordHavoc: modified to start at first clip node,
77 // in other words: first node of the (sub)model
78 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
79 while (node->contents == 0)
80 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
82 return ((mleaf_t *)node)->contents;
85 typedef struct findnonsolidlocationinfo_s
93 findnonsolidlocationinfo_t;
96 extern cvar_t samelevel;
98 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
100 int i, surfnum, k, *tri, *mark;
101 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
107 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
109 surf = info->model->brushq1.surfaces + *mark;
110 if (surf->flags & SURF_SOLIDCLIP)
113 VectorCopy(surf->plane->normal, surfnormal);
114 if (surf->flags & SURF_PLANEBACK)
115 VectorNegate(surfnormal, surfnormal);
117 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
119 for (k = 0;k < mesh->numtriangles;k++)
121 tri = mesh->element3i + k * 3;
122 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
123 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
124 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
125 VectorSubtract(vert[1], vert[0], edge[0]);
126 VectorSubtract(vert[2], vert[1], edge[1]);
127 CrossProduct(edge[1], edge[0], facenormal);
128 if (facenormal[0] || facenormal[1] || facenormal[2])
130 VectorNormalize(facenormal);
132 if (VectorDistance(facenormal, surfnormal) > 0.01f)
133 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
135 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
136 if (f <= info->bestdist && f >= -info->bestdist)
138 VectorSubtract(vert[0], vert[2], edge[2]);
139 VectorNormalize(edge[0]);
140 VectorNormalize(edge[1]);
141 VectorNormalize(edge[2]);
142 CrossProduct(facenormal, edge[0], edgenormal[0]);
143 CrossProduct(facenormal, edge[1], edgenormal[1]);
144 CrossProduct(facenormal, edge[2], edgenormal[2]);
146 if (samelevel.integer & 1)
147 VectorNegate(edgenormal[0], edgenormal[0]);
148 if (samelevel.integer & 2)
149 VectorNegate(edgenormal[1], edgenormal[1]);
150 if (samelevel.integer & 4)
151 VectorNegate(edgenormal[2], edgenormal[2]);
152 for (i = 0;i < 3;i++)
153 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
154 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
155 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
156 Con_Printf("a! %i : %f %f %f (%f %f %f)\n", i, edgenormal[i][0], edgenormal[i][1], edgenormal[i][2], facenormal[0], facenormal[1], facenormal[2]);
159 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
160 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
161 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
163 // we got lucky, the center is within the face
164 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
168 if (info->bestdist > dist)
170 info->bestdist = dist;
171 VectorScale(facenormal, (info->radius - -dist), info->nudge);
176 if (info->bestdist > dist)
178 info->bestdist = dist;
179 VectorScale(facenormal, (info->radius - dist), info->nudge);
185 // check which edge or vertex the center is nearest
186 for (i = 0;i < 3;i++)
188 f = DotProduct(info->center, edge[i]);
189 if (f >= DotProduct(vert[0], edge[i])
190 && f <= DotProduct(vert[1], edge[i]))
193 VectorMA(info->center, -f, edge[i], point);
194 dist = sqrt(DotProduct(point, point));
195 if (info->bestdist > dist)
197 info->bestdist = dist;
198 VectorScale(point, (info->radius / dist), info->nudge);
200 // skip both vertex checks
201 // (both are further away than this edge)
206 // not on edge, check first vertex of edge
207 VectorSubtract(info->center, vert[i], point);
208 dist = sqrt(DotProduct(point, point));
209 if (info->bestdist > dist)
211 info->bestdist = dist;
212 VectorScale(point, (info->radius / dist), info->nudge);
225 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
229 if (((mleaf_t *)node)->nummarksurfaces)
230 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
234 float f = PlaneDiff(info->center, node->plane);
235 if (f >= -info->bestdist)
236 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
237 if (f <= info->bestdist)
238 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
242 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
245 findnonsolidlocationinfo_t info;
251 VectorCopy(in, info.center);
252 info.radius = radius;
257 VectorClear(info.nudge);
258 info.bestdist = radius;
259 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
260 VectorAdd(info.center, info.nudge, info.center);
262 while (info.bestdist < radius && ++i < 10);
263 VectorCopy(info.center, out);
266 static qbyte *Mod_Q1BSP_DecompressVis(model_t *model, qbyte *in)
268 static qbyte decompressed[MAX_MAP_LEAFS/8];
273 row = (model->brushq1.numleafs+7)>>3;
291 } while (out - decompressed < row);
296 static qbyte *Mod_Q1BSP_LeafPVS(model_t *model, mleaf_t *leaf)
298 if (r_novis.integer || leaf == model->brushq1.leafs || leaf->compressed_vis == NULL)
299 return mod_q1bsp_novis;
300 return Mod_Q1BSP_DecompressVis(model, leaf->compressed_vis);
303 static void Mod_Q1BSP_LoadTextures(lump_t *l)
305 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
307 texture_t *tx, *tx2, *anims[10], *altanims[10];
309 qbyte *data, *mtdata;
312 loadmodel->brushq1.textures = NULL;
317 m = (dmiptexlump_t *)(mod_base + l->fileofs);
319 m->nummiptex = LittleLong (m->nummiptex);
321 // add two slots for notexture walls and notexture liquids
322 loadmodel->brushq1.numtextures = m->nummiptex + 2;
323 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
325 // fill out all slots with notexture
326 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
329 strcpy(tx->name, "NO TEXTURE FOUND");
332 tx->skin.base = r_notexture;
333 tx->shader = &Cshader_wall_lightmap;
334 tx->flags = SURF_SOLIDCLIP;
335 if (i == loadmodel->brushq1.numtextures - 1)
337 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
338 tx->shader = &Cshader_water;
340 tx->currentframe = tx;
343 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
345 // LordHavoc: mostly rewritten map texture loader
346 for (i = 0;i < m->nummiptex;i++)
348 dofs[i] = LittleLong(dofs[i]);
349 if (dofs[i] == -1 || r_nosurftextures.integer)
351 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
353 // make sure name is no more than 15 characters
354 for (j = 0;dmiptex->name[j] && j < 15;j++)
355 name[j] = dmiptex->name[j];
358 mtwidth = LittleLong(dmiptex->width);
359 mtheight = LittleLong(dmiptex->height);
361 j = LittleLong(dmiptex->offsets[0]);
365 if (j < 40 || j + mtwidth * mtheight > l->filelen)
367 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
370 mtdata = (qbyte *)dmiptex + j;
373 if ((mtwidth & 15) || (mtheight & 15))
374 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
376 // LordHavoc: force all names to lowercase
377 for (j = 0;name[j];j++)
378 if (name[j] >= 'A' && name[j] <= 'Z')
379 name[j] += 'a' - 'A';
381 tx = loadmodel->brushq1.textures + i;
382 strcpy(tx->name, name);
384 tx->height = mtheight;
388 sprintf(tx->name, "unnamed%i", i);
389 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
392 // LordHavoc: HL sky textures are entirely different than quake
393 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
395 if (loadmodel->isworldmodel)
397 data = loadimagepixels(tx->name, false, 0, 0);
400 if (image_width == 256 && image_height == 128)
408 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
410 R_InitSky(mtdata, 1);
413 else if (mtdata != NULL)
414 R_InitSky(mtdata, 1);
419 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
421 // did not find external texture, load it from the bsp or wad3
422 if (loadmodel->brushq1.ishlbsp)
424 // internal texture overrides wad
425 qbyte *pixels, *freepixels, *fogpixels;
426 pixels = freepixels = NULL;
428 pixels = W_ConvertWAD3Texture(dmiptex);
430 pixels = freepixels = W_GetTexture(tx->name);
433 tx->width = image_width;
434 tx->height = image_height;
435 tx->skin.base = tx->skin.merged = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
436 if (Image_CheckAlpha(pixels, image_width * image_height, true))
438 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
439 for (j = 0;j < image_width * image_height * 4;j += 4)
441 fogpixels[j + 0] = 255;
442 fogpixels[j + 1] = 255;
443 fogpixels[j + 2] = 255;
444 fogpixels[j + 3] = pixels[j + 3];
446 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
451 Mem_Free(freepixels);
453 else if (mtdata) // texture included
454 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
457 if (tx->skin.base == NULL)
462 tx->skin.base = r_notexture;
465 if (tx->name[0] == '*')
467 // turb does not block movement
468 tx->flags &= ~SURF_SOLIDCLIP;
469 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
470 // LordHavoc: some turbulent textures should be fullbright and solid
471 if (!strncmp(tx->name,"*lava",5)
472 || !strncmp(tx->name,"*teleport",9)
473 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
474 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
476 tx->flags |= SURF_WATERALPHA;
477 tx->shader = &Cshader_water;
479 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
481 tx->flags |= SURF_DRAWSKY;
482 tx->shader = &Cshader_sky;
486 tx->flags |= SURF_LIGHTMAP;
488 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
489 tx->shader = &Cshader_wall_lightmap;
492 // start out with no animation
493 tx->currentframe = tx;
496 // sequence the animations
497 for (i = 0;i < m->nummiptex;i++)
499 tx = loadmodel->brushq1.textures + i;
500 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
502 if (tx->anim_total[0] || tx->anim_total[1])
503 continue; // already sequenced
505 // find the number of frames in the animation
506 memset(anims, 0, sizeof(anims));
507 memset(altanims, 0, sizeof(altanims));
509 for (j = i;j < m->nummiptex;j++)
511 tx2 = loadmodel->brushq1.textures + j;
512 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
516 if (num >= '0' && num <= '9')
517 anims[num - '0'] = tx2;
518 else if (num >= 'a' && num <= 'j')
519 altanims[num - 'a'] = tx2;
521 Con_Printf("Bad animating texture %s\n", tx->name);
525 for (j = 0;j < 10;j++)
532 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
535 for (j = 0;j < max;j++)
539 Con_Printf("Missing frame %i of %s\n", j, tx->name);
543 for (j = 0;j < altmax;j++)
547 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
556 // if there is no alternate animation, duplicate the primary
557 // animation into the alternate
559 for (k = 0;k < 10;k++)
560 altanims[k] = anims[k];
563 // link together the primary animation
564 for (j = 0;j < max;j++)
567 tx2->animated = true;
568 tx2->anim_total[0] = max;
569 tx2->anim_total[1] = altmax;
570 for (k = 0;k < 10;k++)
572 tx2->anim_frames[0][k] = anims[k];
573 tx2->anim_frames[1][k] = altanims[k];
577 // if there really is an alternate anim...
578 if (anims[0] != altanims[0])
580 // link together the alternate animation
581 for (j = 0;j < altmax;j++)
584 tx2->animated = true;
585 // the primary/alternate are reversed here
586 tx2->anim_total[0] = altmax;
587 tx2->anim_total[1] = max;
588 for (k = 0;k < 10;k++)
590 tx2->anim_frames[0][k] = altanims[k];
591 tx2->anim_frames[1][k] = anims[k];
598 static void Mod_Q1BSP_LoadLighting(lump_t *l)
601 qbyte *in, *out, *data, d;
602 char litfilename[1024];
603 loadmodel->brushq1.lightdata = NULL;
604 if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
606 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
607 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
609 else // LordHavoc: bsp version 29 (normal white lighting)
611 // LordHavoc: hope is not lost yet, check for a .lit file to load
612 strcpy(litfilename, loadmodel->name);
613 FS_StripExtension(litfilename, litfilename);
614 strcat(litfilename, ".lit");
615 data = (qbyte*) FS_LoadFile(litfilename, false);
618 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
620 i = LittleLong(((int *)data)[1]);
623 Con_DPrintf("loaded %s\n", litfilename);
624 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
625 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
631 Con_Printf("Unknown .lit file version (%d)\n", i);
637 if (fs_filesize == 8)
638 Con_Printf("Empty .lit file, ignoring\n");
640 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
644 // LordHavoc: oh well, expand the white lighting data
647 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
648 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
649 out = loadmodel->brushq1.lightdata;
650 memcpy(in, mod_base + l->fileofs, l->filelen);
651 for (i = 0;i < l->filelen;i++)
661 static void Mod_Q1BSP_LoadLightList(void)
664 char lightsfilename[1024], *s, *t, *lightsstring;
667 strcpy(lightsfilename, loadmodel->name);
668 FS_StripExtension(lightsfilename, lightsfilename);
669 strcat(lightsfilename, ".lights");
670 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
676 while (*s && *s != '\n')
680 Mem_Free(lightsstring);
681 Host_Error("lights file must end with a newline\n");
686 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
689 while (*s && n < numlights)
692 while (*s && *s != '\n')
696 Mem_Free(lightsstring);
697 Host_Error("misparsed lights file!\n");
699 e = loadmodel->brushq1.lights + n;
701 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);
705 Mem_Free(lightsstring);
706 Host_Error("invalid lights file, found %d parameters on line %i, should be 14 parameters (origin[0] origin[1] origin[2] falloff light[0] light[1] light[2] subtract spotdir[0] spotdir[1] spotdir[2] spotcone distancebias style)\n", a, n + 1);
713 Mem_Free(lightsstring);
714 Host_Error("misparsed lights file!\n");
716 loadmodel->brushq1.numlights = numlights;
717 Mem_Free(lightsstring);
722 static int castshadowcount = 0;
723 static void Mod_Q1BSP_ProcessLightList(void)
725 int j, k, l, *mark, lnum;
733 for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
735 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
736 if (e->cullradius2 > 4096.0f * 4096.0f)
737 e->cullradius2 = 4096.0f * 4096.0f;
738 e->cullradius = e->lightradius = sqrt(e->cullradius2);
739 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
740 if (leaf->compressed_vis)
741 pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
743 pvs = mod_q1bsp_novis;
744 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
745 loadmodel->brushq1.surfacevisframes[j] = -1;
746 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
748 if (pvs[j >> 3] & (1 << (j & 7)))
750 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
752 surf = loadmodel->brushq1.surfaces + *mark;
753 if (surf->number != *mark)
754 Con_Printf("%d != %d\n", surf->number, *mark);
755 dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
756 if (surf->flags & SURF_PLANEBACK)
758 if (dist > 0 && dist < e->cullradius)
760 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
761 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
762 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
763 if (DotProduct(temp, temp) < lightradius2)
764 loadmodel->brushq1.surfacevisframes[*mark] = -2;
769 // build list of light receiving surfaces
771 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
772 if (loadmodel->brushq1.surfacevisframes[j] == -2)
775 if (e->numsurfaces > 0)
777 e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
779 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
780 if (loadmodel->brushq1.surfacevisframes[j] == -2)
781 e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
783 // find bounding box and sphere of lit surfaces
784 // (these will be used for creating a shape to clip the light)
786 for (j = 0;j < e->numsurfaces;j++)
788 surf = e->surfaces[j];
791 VectorCopy(surf->poly_verts, e->mins);
792 VectorCopy(surf->poly_verts, e->maxs);
794 for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
796 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
797 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
798 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
799 VectorSubtract(v, e->origin, temp);
800 dist = DotProduct(temp, temp);
805 if (e->cullradius2 > radius2)
807 e->cullradius2 = radius2;
808 e->cullradius = sqrt(e->cullradius2);
810 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
811 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
812 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
813 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
814 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
815 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
816 // clip shadow volumes against eachother to remove unnecessary
817 // polygons(and sections of polygons)
819 //vec3_t polymins, polymaxs;
821 float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
822 float f, *v0, *v1, projectdistance;
824 e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
827 vec3_t outermins, outermaxs, innermins, innermaxs;
828 innermins[0] = e->mins[0] - 1;
829 innermins[1] = e->mins[1] - 1;
830 innermins[2] = e->mins[2] - 1;
831 innermaxs[0] = e->maxs[0] + 1;
832 innermaxs[1] = e->maxs[1] + 1;
833 innermaxs[2] = e->maxs[2] + 1;
834 outermins[0] = loadmodel->normalmins[0] - 1;
835 outermins[1] = loadmodel->normalmins[1] - 1;
836 outermins[2] = loadmodel->normalmins[2] - 1;
837 outermaxs[0] = loadmodel->normalmaxs[0] + 1;
838 outermaxs[1] = loadmodel->normalmaxs[1] + 1;
839 outermaxs[2] = loadmodel->normalmaxs[2] + 1;
840 // add bounding box around the whole shadow volume set,
841 // facing inward to limit light area, with an outer bounding box
842 // facing outward (this is needed by the shadow rendering method)
844 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
845 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
846 verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
847 verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
848 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
849 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
850 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
851 verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
852 verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
853 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
855 verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
856 verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
857 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
858 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
859 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
860 verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
861 verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
862 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
863 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
864 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
866 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
867 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
868 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
869 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
870 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
871 verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
872 verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
873 verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
874 verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
875 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
877 verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
878 verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
879 verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
880 verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
881 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
882 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
883 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
884 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
885 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
886 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
888 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
889 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
890 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
891 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
892 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
893 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
894 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
895 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
896 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
897 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
899 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
900 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
901 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
902 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
903 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
904 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
905 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
906 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
907 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
908 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
912 for (j = 0;j < e->numsurfaces;j++)
914 surf = e->surfaces[j];
915 if (surf->flags & SURF_SHADOWCAST)
916 surf->castshadow = castshadowcount;
918 for (j = 0;j < e->numsurfaces;j++)
920 surf = e->surfaces[j];
921 if (surf->castshadow != castshadowcount)
923 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
924 if (surf->flags & SURF_PLANEBACK)
926 projectdistance = e->lightradius;
927 if (maxverts < surf->poly_numverts)
929 maxverts = surf->poly_numverts;
932 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
934 // copy the original polygon, for the front cap of the volume
935 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
937 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
938 // project the original polygon, reversed, for the back cap of the volume
939 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
941 VectorSubtract(v0, e->origin, temp);
942 VectorNormalize(temp);
943 VectorMA(v0, projectdistance, temp, v1);
945 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
946 // project the shadow volume sides
947 for (l = surf->poly_numverts - 1, k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;k < surf->poly_numverts;l = k, k++, v0 = v1, v1 += 3)
949 if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
951 VectorCopy(v1, &verts[0]);
952 VectorCopy(v0, &verts[3]);
953 VectorCopy(v0, &verts[6]);
954 VectorCopy(v1, &verts[9]);
955 VectorSubtract(&verts[6], e->origin, temp);
956 VectorNormalize(temp);
957 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
958 VectorSubtract(&verts[9], e->origin, temp);
959 VectorNormalize(temp);
960 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
961 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
965 // build the triangle mesh
966 e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
970 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
971 l += mesh->numtriangles;
972 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
980 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
982 loadmodel->brushq1.visdata = NULL;
985 loadmodel->brushq1.visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
986 memcpy(loadmodel->brushq1.visdata, mod_base + l->fileofs, l->filelen);
989 // used only for HalfLife maps
990 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
992 char key[128], value[4096];
997 if (!COM_ParseToken(&data))
999 if (com_token[0] != '{')
1003 if (!COM_ParseToken(&data))
1005 if (com_token[0] == '}')
1006 break; // end of worldspawn
1007 if (com_token[0] == '_')
1008 strcpy(key, com_token + 1);
1010 strcpy(key, com_token);
1011 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1012 key[strlen(key)-1] = 0;
1013 if (!COM_ParseToken(&data))
1015 strcpy(value, com_token);
1016 if (!strcmp("wad", key)) // for HalfLife maps
1018 if (loadmodel->brushq1.ishlbsp)
1021 for (i = 0;i < 4096;i++)
1022 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1028 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1029 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1031 else if (value[i] == ';' || value[i] == 0)
1035 strcpy(wadname, "textures/");
1036 strcat(wadname, &value[j]);
1037 W_LoadTextureWadFile(wadname, false);
1049 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1051 loadmodel->brush.entities = NULL;
1054 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1055 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1056 if (loadmodel->brushq1.ishlbsp)
1057 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1061 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1067 in = (void *)(mod_base + l->fileofs);
1068 if (l->filelen % sizeof(*in))
1069 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1070 count = l->filelen / sizeof(*in);
1071 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1073 loadmodel->brushq1.vertexes = out;
1074 loadmodel->brushq1.numvertexes = count;
1076 for ( i=0 ; i<count ; i++, in++, out++)
1078 out->position[0] = LittleFloat(in->point[0]);
1079 out->position[1] = LittleFloat(in->point[1]);
1080 out->position[2] = LittleFloat(in->point[2]);
1084 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1090 in = (void *)(mod_base + l->fileofs);
1091 if (l->filelen % sizeof(*in))
1092 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1093 count = l->filelen / sizeof(*in);
1094 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1096 loadmodel->brushq1.submodels = out;
1097 loadmodel->brushq1.numsubmodels = count;
1099 for ( i=0 ; i<count ; i++, in++, out++)
1101 for (j=0 ; j<3 ; j++)
1103 // spread the mins / maxs by a pixel
1104 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1105 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1106 out->origin[j] = LittleFloat(in->origin[j]);
1108 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1109 out->headnode[j] = LittleLong(in->headnode[j]);
1110 out->visleafs = LittleLong(in->visleafs);
1111 out->firstface = LittleLong(in->firstface);
1112 out->numfaces = LittleLong(in->numfaces);
1116 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1122 in = (void *)(mod_base + l->fileofs);
1123 if (l->filelen % sizeof(*in))
1124 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1125 count = l->filelen / sizeof(*in);
1126 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1128 loadmodel->brushq1.edges = out;
1129 loadmodel->brushq1.numedges = count;
1131 for ( i=0 ; i<count ; i++, in++, out++)
1133 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1134 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1138 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1142 int i, j, k, count, miptex;
1144 in = (void *)(mod_base + l->fileofs);
1145 if (l->filelen % sizeof(*in))
1146 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1147 count = l->filelen / sizeof(*in);
1148 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1150 loadmodel->brushq1.texinfo = out;
1151 loadmodel->brushq1.numtexinfo = count;
1153 for (i = 0;i < count;i++, in++, out++)
1155 for (k = 0;k < 2;k++)
1156 for (j = 0;j < 4;j++)
1157 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1159 miptex = LittleLong(in->miptex);
1160 out->flags = LittleLong(in->flags);
1162 out->texture = NULL;
1163 if (loadmodel->brushq1.textures)
1165 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1166 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1168 out->texture = loadmodel->brushq1.textures + miptex;
1170 if (out->flags & TEX_SPECIAL)
1172 // if texture chosen is NULL or the shader needs a lightmap,
1173 // force to notexture water shader
1174 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1175 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1179 // if texture chosen is NULL, force to notexture
1180 if (out->texture == NULL)
1181 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1187 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1192 mins[0] = mins[1] = mins[2] = 9999;
1193 maxs[0] = maxs[1] = maxs[2] = -9999;
1195 for (i = 0;i < numverts;i++)
1197 for (j = 0;j < 3;j++, v++)
1207 #define MAX_SUBDIVPOLYTRIANGLES 4096
1208 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1210 static int subdivpolyverts, subdivpolytriangles;
1211 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1212 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1214 static int subdivpolylookupvert(vec3_t v)
1217 for (i = 0;i < subdivpolyverts;i++)
1218 if (subdivpolyvert[i][0] == v[0]
1219 && subdivpolyvert[i][1] == v[1]
1220 && subdivpolyvert[i][2] == v[2])
1222 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1223 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1224 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1225 return subdivpolyverts++;
1228 static void SubdividePolygon(int numverts, float *verts)
1230 int i, i1, i2, i3, f, b, c, p;
1231 vec3_t mins, maxs, front[256], back[256];
1232 float m, *pv, *cv, dist[256], frac;
1235 Host_Error("SubdividePolygon: ran out of verts in buffer");
1237 BoundPoly(numverts, verts, mins, maxs);
1239 for (i = 0;i < 3;i++)
1241 m = (mins[i] + maxs[i]) * 0.5;
1242 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1243 if (maxs[i] - m < 8)
1245 if (m - mins[i] < 8)
1249 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1250 dist[c] = cv[i] - m;
1253 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1257 VectorCopy(pv, front[f]);
1262 VectorCopy(pv, back[b]);
1265 if (dist[p] == 0 || dist[c] == 0)
1267 if ((dist[p] > 0) != (dist[c] > 0) )
1270 frac = dist[p] / (dist[p] - dist[c]);
1271 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1272 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1273 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1279 SubdividePolygon(f, front[0]);
1280 SubdividePolygon(b, back[0]);
1284 i1 = subdivpolylookupvert(verts);
1285 i2 = subdivpolylookupvert(verts + 3);
1286 for (i = 2;i < numverts;i++)
1288 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1290 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1294 i3 = subdivpolylookupvert(verts + i * 3);
1295 subdivpolyindex[subdivpolytriangles][0] = i1;
1296 subdivpolyindex[subdivpolytriangles][1] = i2;
1297 subdivpolyindex[subdivpolytriangles][2] = i3;
1299 subdivpolytriangles++;
1303 //Breaks a polygon up along axial 64 unit
1304 //boundaries so that turbulent and sky warps
1305 //can be done reasonably.
1306 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1312 subdivpolytriangles = 0;
1313 subdivpolyverts = 0;
1314 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1315 if (subdivpolytriangles < 1)
1316 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1318 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1319 mesh->numverts = subdivpolyverts;
1320 mesh->numtriangles = subdivpolytriangles;
1321 mesh->vertex = (surfvertex_t *)(mesh + 1);
1322 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1323 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1325 for (i = 0;i < mesh->numtriangles;i++)
1326 for (j = 0;j < 3;j++)
1327 mesh->index[i*3+j] = subdivpolyindex[i][j];
1329 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1331 VectorCopy(subdivpolyvert[i], v->v);
1332 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1333 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1338 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1341 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1342 mesh->numverts = numverts;
1343 mesh->numtriangles = numtriangles;
1344 mesh->vertex3f = (float *)(mesh + 1);
1345 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1346 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1347 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1348 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1349 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1350 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1351 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1352 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1353 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1357 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1360 float *vec, *vert, mins[3], maxs[3], val, *v;
1363 // convert edges back to a normal polygon
1364 surf->poly_numverts = numedges;
1365 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1366 for (i = 0;i < numedges;i++)
1368 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1370 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1372 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1373 VectorCopy(vec, vert);
1377 // calculate polygon bounding box and center
1378 vert = surf->poly_verts;
1379 VectorCopy(vert, mins);
1380 VectorCopy(vert, maxs);
1382 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1384 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1385 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1386 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1388 VectorCopy(mins, surf->poly_mins);
1389 VectorCopy(maxs, surf->poly_maxs);
1390 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1391 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1392 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1394 // generate surface extents information
1395 tex = surf->texinfo;
1396 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1397 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1398 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1400 for (j = 0;j < 2;j++)
1402 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1409 for (i = 0;i < 2;i++)
1411 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1412 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1416 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1420 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1424 in = (void *)(mod_base + l->fileofs);
1425 if (l->filelen % sizeof(*in))
1426 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1427 count = l->filelen / sizeof(*in);
1428 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1430 loadmodel->brushq1.numsurfaces = count;
1431 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1432 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1433 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1435 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, in++, surf++)
1437 surf->number = surfnum;
1438 // FIXME: validate edges, texinfo, etc?
1439 firstedge = LittleLong(in->firstedge);
1440 numedges = LittleShort(in->numedges);
1441 if ((unsigned int) firstedge > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges || (unsigned int) firstedge + (unsigned int) numedges > (unsigned int) loadmodel->brushq1.numsurfedges)
1442 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1443 i = LittleShort(in->texinfo);
1444 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1445 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1446 surf->texinfo = loadmodel->brushq1.texinfo + i;
1447 surf->flags = surf->texinfo->texture->flags;
1449 planenum = LittleShort(in->planenum);
1450 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1451 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1453 if (LittleShort(in->side))
1454 surf->flags |= SURF_PLANEBACK;
1456 surf->plane = loadmodel->brushq1.planes + planenum;
1458 // clear lightmap (filled in later)
1459 surf->lightmaptexture = NULL;
1461 // force lightmap upload on first time seeing the surface
1462 surf->cached_dlight = true;
1464 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1466 ssize = (surf->extents[0] >> 4) + 1;
1467 tsize = (surf->extents[1] >> 4) + 1;
1470 for (i = 0;i < MAXLIGHTMAPS;i++)
1471 surf->styles[i] = in->styles[i];
1472 i = LittleLong(in->lightofs);
1474 surf->samples = NULL;
1475 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1476 surf->samples = loadmodel->brushq1.lightdata + i;
1477 else // LordHavoc: white lighting (bsp version 29)
1478 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1480 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1482 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1483 Host_Error("Bad surface extents");
1484 // stainmap for permanent marks on walls
1485 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1487 memset(surf->stainsamples, 255, ssize * tsize * 3);
1491 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1492 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1494 for (surfnum = 0, surf = loadmodel->brushq1.surfaces, totalverts = 0, totaltris = 0, totalmeshes = 0;surfnum < count;surfnum++, totalverts += surf->poly_numverts, totaltris += surf->poly_numverts - 2, totalmeshes++, surf++)
1496 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1497 mesh->numverts = surf->poly_numverts;
1498 mesh->numtriangles = surf->poly_numverts - 2;
1499 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1500 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1501 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1502 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1503 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1504 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1505 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1506 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1507 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1508 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1510 surf->lightmaptexturestride = 0;
1511 surf->lightmaptexture = NULL;
1513 for (i = 0;i < mesh->numverts;i++)
1515 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1516 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1517 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1518 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1519 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1520 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1521 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1522 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1523 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1524 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1525 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1526 mesh->lightmapoffsets[i] = 0;
1529 for (i = 0;i < mesh->numtriangles;i++)
1531 mesh->element3i[i * 3 + 0] = 0;
1532 mesh->element3i[i * 3 + 1] = i + 1;
1533 mesh->element3i[i * 3 + 2] = i + 2;
1536 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1537 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1539 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1541 int i, iu, iv, smax, tmax;
1542 float u, v, ubase, vbase, uscale, vscale;
1544 smax = surf->extents[0] >> 4;
1545 tmax = surf->extents[1] >> 4;
1547 surf->flags |= SURF_LIGHTMAP;
1548 if (r_miplightmaps.integer)
1550 surf->lightmaptexturestride = smax+1;
1551 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_MIPMAP | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1555 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1556 surf->lightmaptexture = R_LoadTexture2D(loadmodel->texturepool, NULL, surf->lightmaptexturestride, (surf->extents[1]>>4)+1, NULL, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, TEXF_FRAGMENT | TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
1558 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1559 uscale = (uscale - ubase) / (smax + 1);
1560 vscale = (vscale - vbase) / (tmax + 1);
1562 for (i = 0;i < mesh->numverts;i++)
1564 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1565 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1566 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1567 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1568 // LordHavoc: calc lightmap data offset for vertex lighting to use
1571 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1577 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1579 node->parent = parent;
1580 if (node->contents < 0)
1582 Mod_Q1BSP_SetParent(node->children[0], node);
1583 Mod_Q1BSP_SetParent(node->children[1], node);
1586 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1592 in = (void *)(mod_base + l->fileofs);
1593 if (l->filelen % sizeof(*in))
1594 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1595 count = l->filelen / sizeof(*in);
1596 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1598 loadmodel->brushq1.nodes = out;
1599 loadmodel->brushq1.numnodes = count;
1601 for ( i=0 ; i<count ; i++, in++, out++)
1603 for (j=0 ; j<3 ; j++)
1605 out->mins[j] = LittleShort(in->mins[j]);
1606 out->maxs[j] = LittleShort(in->maxs[j]);
1609 p = LittleLong(in->planenum);
1610 out->plane = loadmodel->brushq1.planes + p;
1612 out->firstsurface = LittleShort(in->firstface);
1613 out->numsurfaces = LittleShort(in->numfaces);
1615 for (j=0 ; j<2 ; j++)
1617 p = LittleShort(in->children[j]);
1619 out->children[j] = loadmodel->brushq1.nodes + p;
1621 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1625 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1628 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1634 in = (void *)(mod_base + l->fileofs);
1635 if (l->filelen % sizeof(*in))
1636 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1637 count = l->filelen / sizeof(*in);
1638 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1640 loadmodel->brushq1.leafs = out;
1641 loadmodel->brushq1.numleafs = count;
1643 for ( i=0 ; i<count ; i++, in++, out++)
1645 for (j=0 ; j<3 ; j++)
1647 out->mins[j] = LittleShort(in->mins[j]);
1648 out->maxs[j] = LittleShort(in->maxs[j]);
1651 p = LittleLong(in->contents);
1654 out->firstmarksurface = loadmodel->brushq1.marksurfaces +
1655 LittleShort(in->firstmarksurface);
1656 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1658 p = LittleLong(in->visofs);
1660 out->compressed_vis = NULL;
1662 out->compressed_vis = loadmodel->brushq1.visdata + p;
1664 for (j=0 ; j<4 ; j++)
1665 out->ambient_sound_level[j] = in->ambient_level[j];
1667 // FIXME: Insert caustics here
1671 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1673 dclipnode_t *in, *out;
1677 in = (void *)(mod_base + l->fileofs);
1678 if (l->filelen % sizeof(*in))
1679 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1680 count = l->filelen / sizeof(*in);
1681 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1683 loadmodel->brushq1.clipnodes = out;
1684 loadmodel->brushq1.numclipnodes = count;
1686 if (loadmodel->brushq1.ishlbsp)
1688 hull = &loadmodel->brushq1.hulls[1];
1689 hull->clipnodes = out;
1690 hull->firstclipnode = 0;
1691 hull->lastclipnode = count-1;
1692 hull->planes = loadmodel->brushq1.planes;
1693 hull->clip_mins[0] = -16;
1694 hull->clip_mins[1] = -16;
1695 hull->clip_mins[2] = -36;
1696 hull->clip_maxs[0] = 16;
1697 hull->clip_maxs[1] = 16;
1698 hull->clip_maxs[2] = 36;
1699 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1701 hull = &loadmodel->brushq1.hulls[2];
1702 hull->clipnodes = out;
1703 hull->firstclipnode = 0;
1704 hull->lastclipnode = count-1;
1705 hull->planes = loadmodel->brushq1.planes;
1706 hull->clip_mins[0] = -32;
1707 hull->clip_mins[1] = -32;
1708 hull->clip_mins[2] = -32;
1709 hull->clip_maxs[0] = 32;
1710 hull->clip_maxs[1] = 32;
1711 hull->clip_maxs[2] = 32;
1712 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1714 hull = &loadmodel->brushq1.hulls[3];
1715 hull->clipnodes = out;
1716 hull->firstclipnode = 0;
1717 hull->lastclipnode = count-1;
1718 hull->planes = loadmodel->brushq1.planes;
1719 hull->clip_mins[0] = -16;
1720 hull->clip_mins[1] = -16;
1721 hull->clip_mins[2] = -18;
1722 hull->clip_maxs[0] = 16;
1723 hull->clip_maxs[1] = 16;
1724 hull->clip_maxs[2] = 18;
1725 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1729 hull = &loadmodel->brushq1.hulls[1];
1730 hull->clipnodes = out;
1731 hull->firstclipnode = 0;
1732 hull->lastclipnode = count-1;
1733 hull->planes = loadmodel->brushq1.planes;
1734 hull->clip_mins[0] = -16;
1735 hull->clip_mins[1] = -16;
1736 hull->clip_mins[2] = -24;
1737 hull->clip_maxs[0] = 16;
1738 hull->clip_maxs[1] = 16;
1739 hull->clip_maxs[2] = 32;
1740 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1742 hull = &loadmodel->brushq1.hulls[2];
1743 hull->clipnodes = out;
1744 hull->firstclipnode = 0;
1745 hull->lastclipnode = count-1;
1746 hull->planes = loadmodel->brushq1.planes;
1747 hull->clip_mins[0] = -32;
1748 hull->clip_mins[1] = -32;
1749 hull->clip_mins[2] = -24;
1750 hull->clip_maxs[0] = 32;
1751 hull->clip_maxs[1] = 32;
1752 hull->clip_maxs[2] = 64;
1753 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1756 for (i=0 ; i<count ; i++, out++, in++)
1758 out->planenum = LittleLong(in->planenum);
1759 out->children[0] = LittleShort(in->children[0]);
1760 out->children[1] = LittleShort(in->children[1]);
1761 if (out->children[0] >= count || out->children[1] >= count)
1762 Host_Error("Corrupt clipping hull(out of range child)\n");
1766 //Duplicate the drawing hull structure as a clipping hull
1767 static void Mod_Q1BSP_MakeHull0(void)
1774 hull = &loadmodel->brushq1.hulls[0];
1776 in = loadmodel->brushq1.nodes;
1777 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1779 hull->clipnodes = out;
1780 hull->firstclipnode = 0;
1781 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1782 hull->planes = loadmodel->brushq1.planes;
1784 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1786 out->planenum = in->plane - loadmodel->brushq1.planes;
1787 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1788 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1792 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1797 in = (void *)(mod_base + l->fileofs);
1798 if (l->filelen % sizeof(*in))
1799 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1800 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1801 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1803 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1805 j = (unsigned) LittleShort(in[i]);
1806 if (j >= loadmodel->brushq1.numsurfaces)
1807 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1808 loadmodel->brushq1.marksurfaces[i] = j;
1812 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1817 in = (void *)(mod_base + l->fileofs);
1818 if (l->filelen % sizeof(*in))
1819 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
1820 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
1821 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
1823 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
1824 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
1828 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
1834 in = (void *)(mod_base + l->fileofs);
1835 if (l->filelen % sizeof(*in))
1836 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
1838 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
1839 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
1841 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
1843 out->normal[0] = LittleFloat(in->normal[0]);
1844 out->normal[1] = LittleFloat(in->normal[1]);
1845 out->normal[2] = LittleFloat(in->normal[2]);
1846 out->dist = LittleFloat(in->dist);
1852 #define MAX_POINTS_ON_WINDING 64
1858 double points[8][3]; // variable sized
1867 static winding_t *NewWinding(int points)
1872 if (points > MAX_POINTS_ON_WINDING)
1873 Sys_Error("NewWinding: too many points\n");
1875 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
1876 w = Mem_Alloc(loadmodel->mempool, size);
1882 static void FreeWinding(winding_t *w)
1892 static winding_t *BaseWindingForPlane(mplane_t *p)
1894 double org[3], vright[3], vup[3], normal[3];
1897 VectorCopy(p->normal, normal);
1898 VectorVectorsDouble(normal, vright, vup);
1900 VectorScale(vup, 1024.0*1024.0*1024.0, vup);
1901 VectorScale(vright, 1024.0*1024.0*1024.0, vright);
1903 // project a really big axis aligned box onto the plane
1906 VectorScale(p->normal, p->dist, org);
1908 VectorSubtract(org, vright, w->points[0]);
1909 VectorAdd(w->points[0], vup, w->points[0]);
1911 VectorAdd(org, vright, w->points[1]);
1912 VectorAdd(w->points[1], vup, w->points[1]);
1914 VectorAdd(org, vright, w->points[2]);
1915 VectorSubtract(w->points[2], vup, w->points[2]);
1917 VectorSubtract(org, vright, w->points[3]);
1918 VectorSubtract(w->points[3], vup, w->points[3]);
1929 Clips the winding to the plane, returning the new winding on the positive side
1930 Frees the input winding.
1931 If keepon is true, an exactly on-plane winding will be saved, otherwise
1932 it will be clipped away.
1935 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
1937 double dists[MAX_POINTS_ON_WINDING + 1];
1938 int sides[MAX_POINTS_ON_WINDING + 1];
1947 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
1949 // determine sides for each point
1950 for (i = 0;i < in->numpoints;i++)
1952 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
1953 if (dot > ON_EPSILON)
1954 sides[i] = SIDE_FRONT;
1955 else if (dot < -ON_EPSILON)
1956 sides[i] = SIDE_BACK;
1961 sides[i] = sides[0];
1962 dists[i] = dists[0];
1964 if (keepon && !counts[0] && !counts[1])
1975 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
1976 if (maxpts > MAX_POINTS_ON_WINDING)
1977 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
1979 neww = NewWinding(maxpts);
1981 for (i = 0;i < in->numpoints;i++)
1983 if (neww->numpoints >= maxpts)
1984 Sys_Error("ClipWinding: points exceeded estimate");
1988 if (sides[i] == SIDE_ON)
1990 VectorCopy(p1, neww->points[neww->numpoints]);
1995 if (sides[i] == SIDE_FRONT)
1997 VectorCopy(p1, neww->points[neww->numpoints]);
2001 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2004 // generate a split point
2005 p2 = in->points[(i+1)%in->numpoints];
2007 dot = dists[i] / (dists[i]-dists[i+1]);
2008 for (j = 0;j < 3;j++)
2009 { // avoid round off error when possible
2010 if (split->normal[j] == 1)
2011 mid[j] = split->dist;
2012 else if (split->normal[j] == -1)
2013 mid[j] = -split->dist;
2015 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2018 VectorCopy(mid, neww->points[neww->numpoints]);
2022 // free the original winding
2033 Divides a winding by a plane, producing one or two windings. The
2034 original winding is not damaged or freed. If only on one side, the
2035 returned winding will be the input winding. If on both sides, two
2036 new windings will be created.
2039 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2041 double dists[MAX_POINTS_ON_WINDING + 1];
2042 int sides[MAX_POINTS_ON_WINDING + 1];
2051 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2053 // determine sides for each point
2054 for (i = 0;i < in->numpoints;i++)
2056 dot = DotProduct(in->points[i], split->normal);
2059 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2060 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2061 else sides[i] = SIDE_ON;
2064 sides[i] = sides[0];
2065 dists[i] = dists[0];
2067 *front = *back = NULL;
2080 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2082 if (maxpts > MAX_POINTS_ON_WINDING)
2083 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2085 *front = f = NewWinding(maxpts);
2086 *back = b = NewWinding(maxpts);
2088 for (i = 0;i < in->numpoints;i++)
2090 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2091 Sys_Error("DivideWinding: points exceeded estimate");
2095 if (sides[i] == SIDE_ON)
2097 VectorCopy(p1, f->points[f->numpoints]);
2099 VectorCopy(p1, b->points[b->numpoints]);
2104 if (sides[i] == SIDE_FRONT)
2106 VectorCopy(p1, f->points[f->numpoints]);
2109 else if (sides[i] == SIDE_BACK)
2111 VectorCopy(p1, b->points[b->numpoints]);
2115 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2118 // generate a split point
2119 p2 = in->points[(i+1)%in->numpoints];
2121 dot = dists[i] / (dists[i]-dists[i+1]);
2122 for (j = 0;j < 3;j++)
2123 { // avoid round off error when possible
2124 if (split->normal[j] == 1)
2125 mid[j] = split->dist;
2126 else if (split->normal[j] == -1)
2127 mid[j] = -split->dist;
2129 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2132 VectorCopy(mid, f->points[f->numpoints]);
2134 VectorCopy(mid, b->points[b->numpoints]);
2139 typedef struct portal_s
2142 mnode_t *nodes[2]; // [0] = front side of plane
2143 struct portal_s *next[2];
2145 struct portal_s *chain; // all portals are linked into a list
2149 static portal_t *portalchain;
2156 static portal_t *AllocPortal(void)
2159 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2160 p->chain = portalchain;
2165 static void FreePortal(portal_t *p)
2170 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2172 // calculate children first
2173 if (node->children[0]->contents >= 0)
2174 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2175 if (node->children[1]->contents >= 0)
2176 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2178 // make combined bounding box from children
2179 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2180 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2181 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2182 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2183 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2184 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2187 static void Mod_Q1BSP_FinalizePortals(void)
2189 int i, j, numportals, numpoints;
2190 portal_t *p, *pnext;
2193 mleaf_t *leaf, *endleaf;
2196 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2197 leaf = loadmodel->brushq1.leafs;
2198 endleaf = leaf + loadmodel->brushq1.numleafs;
2199 for (;leaf < endleaf;leaf++)
2201 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2202 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2209 for (i = 0;i < 2;i++)
2211 leaf = (mleaf_t *)p->nodes[i];
2213 for (j = 0;j < w->numpoints;j++)
2215 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2216 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2217 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2218 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2219 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2220 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2227 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2229 // tally up portal and point counts
2235 // note: this check must match the one below or it will usually corrupt memory
2236 // 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
2237 if (p->winding && p->nodes[0] != p->nodes[1]
2238 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2239 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2242 numpoints += p->winding->numpoints * 2;
2246 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2247 loadmodel->brushq1.numportals = numportals;
2248 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2249 loadmodel->brushq1.numportalpoints = numpoints;
2250 // clear all leaf portal chains
2251 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2252 loadmodel->brushq1.leafs[i].portals = NULL;
2253 // process all portals in the global portal chain, while freeing them
2254 portal = loadmodel->brushq1.portals;
2255 point = loadmodel->brushq1.portalpoints;
2264 // note: this check must match the one above or it will usually corrupt memory
2265 // 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
2266 if (p->nodes[0] != p->nodes[1]
2267 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2268 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2270 // first make the back to front portal(forward portal)
2271 portal->points = point;
2272 portal->numpoints = p->winding->numpoints;
2273 portal->plane.dist = p->plane.dist;
2274 VectorCopy(p->plane.normal, portal->plane.normal);
2275 portal->here = (mleaf_t *)p->nodes[1];
2276 portal->past = (mleaf_t *)p->nodes[0];
2278 for (j = 0;j < portal->numpoints;j++)
2280 VectorCopy(p->winding->points[j], point->position);
2283 PlaneClassify(&portal->plane);
2285 // link into leaf's portal chain
2286 portal->next = portal->here->portals;
2287 portal->here->portals = portal;
2289 // advance to next portal
2292 // then make the front to back portal(backward portal)
2293 portal->points = point;
2294 portal->numpoints = p->winding->numpoints;
2295 portal->plane.dist = -p->plane.dist;
2296 VectorNegate(p->plane.normal, portal->plane.normal);
2297 portal->here = (mleaf_t *)p->nodes[0];
2298 portal->past = (mleaf_t *)p->nodes[1];
2300 for (j = portal->numpoints - 1;j >= 0;j--)
2302 VectorCopy(p->winding->points[j], point->position);
2305 PlaneClassify(&portal->plane);
2307 // link into leaf's portal chain
2308 portal->next = portal->here->portals;
2309 portal->here->portals = portal;
2311 // advance to next portal
2314 FreeWinding(p->winding);
2326 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2329 Host_Error("AddPortalToNodes: NULL front node");
2331 Host_Error("AddPortalToNodes: NULL back node");
2332 if (p->nodes[0] || p->nodes[1])
2333 Host_Error("AddPortalToNodes: already included");
2334 // 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
2336 p->nodes[0] = front;
2337 p->next[0] = (portal_t *)front->portals;
2338 front->portals = (mportal_t *)p;
2341 p->next[1] = (portal_t *)back->portals;
2342 back->portals = (mportal_t *)p;
2347 RemovePortalFromNode
2350 static void RemovePortalFromNodes(portal_t *portal)
2354 void **portalpointer;
2356 for (i = 0;i < 2;i++)
2358 node = portal->nodes[i];
2360 portalpointer = (void **) &node->portals;
2365 Host_Error("RemovePortalFromNodes: portal not in leaf");
2369 if (portal->nodes[0] == node)
2371 *portalpointer = portal->next[0];
2372 portal->nodes[0] = NULL;
2374 else if (portal->nodes[1] == node)
2376 *portalpointer = portal->next[1];
2377 portal->nodes[1] = NULL;
2380 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2384 if (t->nodes[0] == node)
2385 portalpointer = (void **) &t->next[0];
2386 else if (t->nodes[1] == node)
2387 portalpointer = (void **) &t->next[1];
2389 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2394 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2397 mnode_t *front, *back, *other_node;
2398 mplane_t clipplane, *plane;
2399 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2400 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2402 // if a leaf, we're done
2406 plane = node->plane;
2408 front = node->children[0];
2409 back = node->children[1];
2411 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2413 // create the new portal by generating a polygon for the node plane,
2414 // and clipping it by all of the other portals(which came from nodes above this one)
2415 nodeportal = AllocPortal();
2416 nodeportal->plane = *node->plane;
2418 nodeportalwinding = BaseWindingForPlane(node->plane);
2419 side = 0; // shut up compiler warning
2420 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2422 clipplane = portal->plane;
2423 if (portal->nodes[0] == portal->nodes[1])
2424 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2425 if (portal->nodes[0] == node)
2427 else if (portal->nodes[1] == node)
2429 clipplane.dist = -clipplane.dist;
2430 VectorNegate(clipplane.normal, clipplane.normal);
2434 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2436 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2437 if (!nodeportalwinding)
2439 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2444 if (nodeportalwinding)
2446 // if the plane was not clipped on all sides, there was an error
2447 nodeportal->winding = nodeportalwinding;
2448 AddPortalToNodes(nodeportal, front, back);
2451 // split the portals of this node along this node's plane and assign them to the children of this node
2452 // (migrating the portals downward through the tree)
2453 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2455 if (portal->nodes[0] == portal->nodes[1])
2456 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2457 if (portal->nodes[0] == node)
2459 else if (portal->nodes[1] == node)
2462 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2463 nextportal = portal->next[side];
2465 other_node = portal->nodes[!side];
2466 RemovePortalFromNodes(portal);
2468 // cut the portal into two portals, one on each side of the node plane
2469 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2474 AddPortalToNodes(portal, back, other_node);
2476 AddPortalToNodes(portal, other_node, back);
2482 AddPortalToNodes(portal, front, other_node);
2484 AddPortalToNodes(portal, other_node, front);
2488 // the winding is split
2489 splitportal = AllocPortal();
2490 temp = splitportal->chain;
2491 *splitportal = *portal;
2492 splitportal->chain = temp;
2493 splitportal->winding = backwinding;
2494 FreeWinding(portal->winding);
2495 portal->winding = frontwinding;
2499 AddPortalToNodes(portal, front, other_node);
2500 AddPortalToNodes(splitportal, back, other_node);
2504 AddPortalToNodes(portal, other_node, front);
2505 AddPortalToNodes(splitportal, other_node, back);
2509 Mod_Q1BSP_RecursiveNodePortals(front);
2510 Mod_Q1BSP_RecursiveNodePortals(back);
2514 static void Mod_Q1BSP_MakePortals(void)
2517 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2518 Mod_Q1BSP_FinalizePortals();
2521 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2524 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2525 msurface_t *surf, *s;
2526 float *v0, *v1, *v2, *v3;
2527 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2528 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2529 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2531 for (vertnum = surf->poly_numverts - 1, vertnum2 = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = surf->poly_verts;vertnum2 < surf->poly_numverts;vertnum = vertnum2, vertnum2++, v0 = v1, v1 += 3)
2533 if (surf->neighborsurfaces[vertnum])
2535 surf->neighborsurfaces[vertnum] = NULL;
2536 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2538 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2539 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2540 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2543 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2544 if (s->neighborsurfaces[vnum] == surf)
2546 if (vnum < s->poly_numverts)
2548 for (vnum = s->poly_numverts - 1, vnum2 = 0, v2 = s->poly_verts + (s->poly_numverts - 1) * 3, v3 = s->poly_verts;vnum2 < s->poly_numverts;vnum = vnum2, vnum2++, v2 = v3, v3 += 3)
2550 if (s->neighborsurfaces[vnum] == NULL
2551 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2552 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2554 surf->neighborsurfaces[vertnum] = s;
2555 s->neighborsurfaces[vnum] = surf;
2559 if (vnum < s->poly_numverts)
2567 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2569 int i, j, stylecounts[256], totalcount, remapstyles[256];
2571 memset(stylecounts, 0, sizeof(stylecounts));
2572 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2574 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2575 for (j = 0;j < MAXLIGHTMAPS;j++)
2576 stylecounts[surf->styles[j]]++;
2579 model->brushq1.light_styles = 0;
2580 for (i = 0;i < 255;i++)
2584 remapstyles[i] = model->brushq1.light_styles++;
2585 totalcount += stylecounts[i] + 1;
2590 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2591 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2592 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2593 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2594 model->brushq1.light_styles = 0;
2595 for (i = 0;i < 255;i++)
2597 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2599 for (i = 0;i < model->brushq1.light_styles;i++)
2601 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2602 j += stylecounts[model->brushq1.light_style[i]] + 1;
2604 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2606 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2607 for (j = 0;j < MAXLIGHTMAPS;j++)
2608 if (surf->styles[j] != 255)
2609 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2612 for (i = 0;i < model->brushq1.light_styles;i++)
2614 *model->brushq1.light_styleupdatechains[i] = NULL;
2615 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2616 j += stylecounts[model->brushq1.light_style[i]] + 1;
2620 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2623 for (i = 0;i < model->brushq1.numtextures;i++)
2624 model->brushq1.pvstexturechainslength[i] = 0;
2625 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2627 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2629 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2630 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2633 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2635 if (model->brushq1.pvstexturechainslength[i])
2637 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2638 j += model->brushq1.pvstexturechainslength[i] + 1;
2641 model->brushq1.pvstexturechains[i] = NULL;
2643 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2644 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2645 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2646 for (i = 0;i < model->brushq1.numtextures;i++)
2648 if (model->brushq1.pvstexturechainslength[i])
2650 *model->brushq1.pvstexturechains[i] = NULL;
2651 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2656 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2657 extern void R_Model_Brush_Draw(entity_render_t *ent);
2658 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2659 extern void R_Model_Brush_DrawLight(entity_render_t *ent, vec3_t relativelightorigin, vec3_t relativeeyeorigin, float lightradius, float *lightcolor, const matrix4x4_t *matrix_modeltofilter, const matrix4x4_t *matrix_modeltoattenuationxyz, const matrix4x4_t *matrix_modeltoattenuationz);
2660 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2665 mempool_t *mainmempool;
2667 model_t *originalloadmodel;
2668 float dist, modelyawradius, modelradius, *vec;
2672 mod->type = mod_brush;
2674 header = (dheader_t *)buffer;
2676 i = LittleLong(header->version);
2677 if (i != BSPVERSION && i != 30)
2678 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2679 mod->brushq1.ishlbsp = i == 30;
2681 mod->brushq1.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2682 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2683 mod->brushq1.PointContents = Mod_Q1BSP_PointContents;
2684 mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
2685 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2687 if (loadmodel->isworldmodel)
2689 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
2690 // until we get a texture for it...
2694 // swap all the lumps
2695 mod_base = (qbyte *)header;
2697 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2698 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2702 // store which lightmap format to use
2703 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2705 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2706 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2707 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2708 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2709 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2710 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2711 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2712 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2713 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2714 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2715 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2716 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2717 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2718 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2719 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2721 Mod_Q1BSP_MakeHull0();
2722 Mod_Q1BSP_MakePortals();
2724 mod->numframes = 2; // regular and alternate animation
2726 mainmempool = mod->mempool;
2727 loadname = mod->name;
2729 Mod_Q1BSP_LoadLightList();
2730 originalloadmodel = loadmodel;
2733 // set up the submodels(FIXME: this is confusing)
2735 for (i = 0;i < mod->brushq1.numsubmodels;i++)
2737 bm = &mod->brushq1.submodels[i];
2739 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2740 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2742 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2743 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2746 mod->brushq1.firstmodelsurface = bm->firstface;
2747 mod->brushq1.nummodelsurfaces = bm->numfaces;
2749 // this gets altered below if sky is used
2750 mod->DrawSky = NULL;
2751 mod->Draw = R_Model_Brush_Draw;
2752 mod->DrawFakeShadow = NULL;
2753 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2754 mod->DrawLight = R_Model_Brush_DrawLight;
2755 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2756 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2757 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2758 Mod_Q1BSP_BuildPVSTextureChains(mod);
2759 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2760 if (mod->brushq1.nummodelsurfaces)
2762 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2763 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2764 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2767 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2769 // we only need to have a drawsky function if it is used(usually only on world model)
2770 if (surf->texinfo->texture->shader == &Cshader_sky)
2771 mod->DrawSky = R_Model_Brush_DrawSky;
2772 // LordHavoc: submodels always clip, even if water
2773 if (mod->brushq1.numsubmodels - 1)
2774 surf->flags |= SURF_SOLIDCLIP;
2775 // calculate bounding shapes
2776 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2778 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2780 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2781 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2782 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2783 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2784 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2785 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2786 dist = vec[0]*vec[0]+vec[1]*vec[1];
2787 if (modelyawradius < dist)
2788 modelyawradius = dist;
2789 dist += vec[2]*vec[2];
2790 if (modelradius < dist)
2795 modelyawradius = sqrt(modelyawradius);
2796 modelradius = sqrt(modelradius);
2797 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2798 mod->yawmins[2] = mod->normalmins[2];
2799 mod->yawmaxs[2] = mod->normalmaxs[2];
2800 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
2801 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
2802 mod->radius = modelradius;
2803 mod->radius2 = modelradius * modelradius;
2807 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
2808 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
2810 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
2812 mod->brushq1.numleafs = bm->visleafs;
2814 // LordHavoc: only register submodels if it is the world
2815 // (prevents bsp models from replacing world submodels)
2816 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
2819 // duplicate the basic information
2820 sprintf(name, "*%i", i+1);
2821 loadmodel = Mod_FindName(name);
2823 strcpy(loadmodel->name, name);
2824 // textures and memory belong to the main model
2825 loadmodel->texturepool = NULL;
2826 loadmodel->mempool = NULL;
2831 loadmodel = originalloadmodel;
2832 //Mod_Q1BSP_ProcessLightList();
2835 static void Mod_Q2BSP_LoadEntities(lump_t *l)
2839 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
2846 in = (void *)(mod_base + l->fileofs);
2847 if (l->filelen % sizeof(*in))
2848 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
2849 count = l->filelen / sizeof(*in);
2850 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2853 loadmodel->num = count;
2855 for (i = 0;i < count;i++, in++, out++)
2861 static void Mod_Q2BSP_LoadVertices(lump_t *l)
2868 in = (void *)(mod_base + l->fileofs);
2869 if (l->filelen % sizeof(*in))
2870 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
2871 count = l->filelen / sizeof(*in);
2872 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2875 loadmodel->num = count;
2877 for (i = 0;i < count;i++, in++, out++)
2883 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
2890 in = (void *)(mod_base + l->fileofs);
2891 if (l->filelen % sizeof(*in))
2892 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
2893 count = l->filelen / sizeof(*in);
2894 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2897 loadmodel->num = count;
2899 for (i = 0;i < count;i++, in++, out++)
2905 static void Mod_Q2BSP_LoadNodes(lump_t *l)
2912 in = (void *)(mod_base + l->fileofs);
2913 if (l->filelen % sizeof(*in))
2914 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
2915 count = l->filelen / sizeof(*in);
2916 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2919 loadmodel->num = count;
2921 for (i = 0;i < count;i++, in++, out++)
2927 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
2934 in = (void *)(mod_base + l->fileofs);
2935 if (l->filelen % sizeof(*in))
2936 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
2937 count = l->filelen / sizeof(*in);
2938 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2941 loadmodel->num = count;
2943 for (i = 0;i < count;i++, in++, out++)
2949 static void Mod_Q2BSP_LoadFaces(lump_t *l)
2956 in = (void *)(mod_base + l->fileofs);
2957 if (l->filelen % sizeof(*in))
2958 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
2959 count = l->filelen / sizeof(*in);
2960 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2963 loadmodel->num = count;
2965 for (i = 0;i < count;i++, in++, out++)
2971 static void Mod_Q2BSP_LoadLighting(lump_t *l)
2978 in = (void *)(mod_base + l->fileofs);
2979 if (l->filelen % sizeof(*in))
2980 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
2981 count = l->filelen / sizeof(*in);
2982 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
2985 loadmodel->num = count;
2987 for (i = 0;i < count;i++, in++, out++)
2993 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3000 in = (void *)(mod_base + l->fileofs);
3001 if (l->filelen % sizeof(*in))
3002 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3003 count = l->filelen / sizeof(*in);
3004 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3007 loadmodel->num = count;
3009 for (i = 0;i < count;i++, in++, out++)
3015 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3022 in = (void *)(mod_base + l->fileofs);
3023 if (l->filelen % sizeof(*in))
3024 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3025 count = l->filelen / sizeof(*in);
3026 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3029 loadmodel->num = count;
3031 for (i = 0;i < count;i++, in++, out++)
3037 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3044 in = (void *)(mod_base + l->fileofs);
3045 if (l->filelen % sizeof(*in))
3046 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3047 count = l->filelen / sizeof(*in);
3048 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3051 loadmodel->num = count;
3053 for (i = 0;i < count;i++, in++, out++)
3059 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3066 in = (void *)(mod_base + l->fileofs);
3067 if (l->filelen % sizeof(*in))
3068 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3069 count = l->filelen / sizeof(*in);
3070 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3073 loadmodel->num = count;
3075 for (i = 0;i < count;i++, in++, out++)
3081 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3088 in = (void *)(mod_base + l->fileofs);
3089 if (l->filelen % sizeof(*in))
3090 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3091 count = l->filelen / sizeof(*in);
3092 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3095 loadmodel->num = count;
3097 for (i = 0;i < count;i++, in++, out++)
3103 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3110 in = (void *)(mod_base + l->fileofs);
3111 if (l->filelen % sizeof(*in))
3112 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3113 count = l->filelen / sizeof(*in);
3114 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3117 loadmodel->num = count;
3119 for (i = 0;i < count;i++, in++, out++)
3125 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3132 in = (void *)(mod_base + l->fileofs);
3133 if (l->filelen % sizeof(*in))
3134 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3135 count = l->filelen / sizeof(*in);
3136 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3139 loadmodel->num = count;
3141 for (i = 0;i < count;i++, in++, out++)
3147 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3154 in = (void *)(mod_base + l->fileofs);
3155 if (l->filelen % sizeof(*in))
3156 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3157 count = l->filelen / sizeof(*in);
3158 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3161 loadmodel->num = count;
3163 for (i = 0;i < count;i++, in++, out++)
3169 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3176 in = (void *)(mod_base + l->fileofs);
3177 if (l->filelen % sizeof(*in))
3178 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3179 count = l->filelen / sizeof(*in);
3180 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3183 loadmodel->num = count;
3185 for (i = 0;i < count;i++, in++, out++)
3191 static void Mod_Q2BSP_LoadModels(lump_t *l)
3198 in = (void *)(mod_base + l->fileofs);
3199 if (l->filelen % sizeof(*in))
3200 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3201 count = l->filelen / sizeof(*in);
3202 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3205 loadmodel->num = count;
3207 for (i = 0;i < count;i++, in++, out++)
3213 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3216 q2dheader_t *header;
3218 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3220 mod->type = mod_brushq2;
3222 header = (q2dheader_t *)buffer;
3224 i = LittleLong(header->version);
3225 if (i != Q2BSPVERSION)
3226 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3227 mod->brushq1.ishlbsp = false;
3228 if (loadmodel->isworldmodel)
3230 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3231 // until we get a texture for it...
3235 mod_base = (qbyte *)header;
3237 // swap all the lumps
3238 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3239 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3241 // store which lightmap format to use
3242 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3244 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3245 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3246 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3247 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3248 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3249 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3250 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3251 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3252 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3253 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3254 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3255 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3256 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3257 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3258 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3259 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3260 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3261 // LordHavoc: must go last because this makes the submodels
3262 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3266 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3269 char key[128], value[4096];
3271 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3272 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3273 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3276 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3277 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3278 data = loadmodel->brush.entities;
3279 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3280 if (data && COM_ParseToken(&data) && com_token[0] == '{')
3284 if (!COM_ParseToken(&data))
3286 if (com_token[0] == '}')
3287 break; // end of worldspawn
3288 if (com_token[0] == '_')
3289 strcpy(key, com_token + 1);
3291 strcpy(key, com_token);
3292 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3293 key[strlen(key)-1] = 0;
3294 if (!COM_ParseToken(&data))
3296 strcpy(value, com_token);
3297 if (!strcmp("gridsize", key))
3299 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3300 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3306 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3312 in = (void *)(mod_base + l->fileofs);
3313 if (l->filelen % sizeof(*in))
3314 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3315 count = l->filelen / sizeof(*in);
3316 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3318 loadmodel->brushq3.data_textures = out;
3319 loadmodel->brushq3.num_textures = count;
3321 for (i = 0;i < count;i++, in++, out++)
3323 strncpy(out->name, in->name, sizeof(out->name) - 1);
3324 out->surfaceflags = LittleLong(in->surfaceflags);
3325 out->contents = LittleLong(in->contents);
3328 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3332 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3338 in = (void *)(mod_base + l->fileofs);
3339 if (l->filelen % sizeof(*in))
3340 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3341 count = l->filelen / sizeof(*in);
3342 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3344 loadmodel->brushq3.data_planes = out;
3345 loadmodel->brushq3.num_planes = count;
3347 for (i = 0;i < count;i++, in++, out++)
3349 out->normal[0] = LittleLong(in->normal[0]);
3350 out->normal[1] = LittleLong(in->normal[1]);
3351 out->normal[2] = LittleLong(in->normal[2]);
3352 out->dist = LittleLong(in->dist);
3357 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3360 q3mbrushside_t *out;
3363 in = (void *)(mod_base + l->fileofs);
3364 if (l->filelen % sizeof(*in))
3365 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3366 count = l->filelen / sizeof(*in);
3367 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3369 loadmodel->brushq3.data_brushsides = out;
3370 loadmodel->brushq3.num_brushsides = count;
3372 for (i = 0;i < count;i++, in++, out++)
3374 n = LittleLong(in->planeindex);
3375 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3376 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3377 out->plane = loadmodel->brushq3.data_planes + n;
3378 n = LittleLong(in->textureindex);
3379 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3380 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3381 out->texture = loadmodel->brushq3.data_textures + n;
3385 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3391 in = (void *)(mod_base + l->fileofs);
3392 if (l->filelen % sizeof(*in))
3393 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3394 count = l->filelen / sizeof(*in);
3395 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3397 loadmodel->brushq3.data_brushes = out;
3398 loadmodel->brushq3.num_brushes = count;
3400 for (i = 0;i < count;i++, in++, out++)
3402 n = LittleLong(in->firstbrushside);
3403 c = LittleLong(in->numbrushsides);
3404 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3405 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3406 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3407 out->numbrushsides = c;
3408 n = LittleLong(in->textureindex);
3409 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3410 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3411 out->texture = loadmodel->brushq3.data_textures + n;
3415 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3421 in = (void *)(mod_base + l->fileofs);
3422 if (l->filelen % sizeof(*in))
3423 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3424 count = l->filelen / sizeof(*in);
3425 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3427 loadmodel->brushq3.data_effects = out;
3428 loadmodel->brushq3.num_effects = count;
3430 for (i = 0;i < count;i++, in++, out++)
3432 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3433 n = LittleLong(in->brushindex);
3434 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3435 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3436 out->brush = loadmodel->brushq3.data_brushes + n;
3437 out->unknown = LittleLong(in->unknown);
3441 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3446 in = (void *)(mod_base + l->fileofs);
3447 if (l->filelen % sizeof(*in))
3448 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3449 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3450 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3451 loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3452 loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3453 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3454 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3455 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3456 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3458 for (i = 0;i < count;i++, in++)
3460 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3461 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3462 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3463 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3464 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3465 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3466 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3467 // svector/tvector are calculated later in face loading
3468 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3469 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3470 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3471 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3472 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3473 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3474 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3475 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3476 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3477 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3478 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3479 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3480 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3484 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3490 in = (void *)(mod_base + l->fileofs);
3491 if (l->filelen % sizeof(int[3]))
3492 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3493 count = l->filelen / sizeof(*in);
3494 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3496 loadmodel->brushq3.num_triangles = count / 3;
3497 loadmodel->brushq3.data_element3i = out;
3498 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3500 for (i = 0;i < count;i++, in++, out++)
3502 *out = LittleLong(*in);
3503 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3504 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
3508 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3514 in = (void *)(mod_base + l->fileofs);
3515 if (l->filelen % sizeof(*in))
3516 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3517 count = l->filelen / sizeof(*in);
3518 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3520 loadmodel->brushq3.data_lightmaps = out;
3521 loadmodel->brushq3.num_lightmaps = count;
3523 for (i = 0;i < count;i++, in++, out++)
3524 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3527 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3531 int i, j, n, count, invalidelements, patchsize[2];
3533 in = (void *)(mod_base + l->fileofs);
3534 if (l->filelen % sizeof(*in))
3535 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3536 count = l->filelen / sizeof(*in);
3537 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3539 loadmodel->brushq3.data_faces = out;
3540 loadmodel->brushq3.num_faces = count;
3542 for (i = 0;i < count;i++, in++, out++)
3544 // check face type first
3545 out->type = LittleLong(in->type);
3546 if (out->type != Q3FACETYPE_POLYGON
3547 && out->type != Q3FACETYPE_PATCH
3548 && out->type != Q3FACETYPE_MESH
3549 && out->type != Q3FACETYPE_FLARE)
3551 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, n);
3552 out->type = 0; // error
3556 n = LittleLong(in->textureindex);
3557 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3559 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3560 out->type = 0; // error
3564 out->texture = loadmodel->brushq3.data_textures + n;
3565 n = LittleLong(in->effectindex);
3566 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3568 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3574 out->effect = loadmodel->brushq3.data_effects + n;
3575 n = LittleLong(in->lightmapindex);
3576 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3578 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3582 out->lightmaptexture = NULL;
3584 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3586 out->firstvertex = LittleLong(in->firstvertex);
3587 out->numvertices = LittleLong(in->numvertices);
3588 out->firstelement = LittleLong(in->firstelement);
3589 out->numelements = LittleLong(in->numelements);
3590 out->numtriangles = out->numelements / 3;
3591 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3593 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid vertex range %i : %i (%i vertices)\n", i, out->texture->name, out->firstvertex, out->firstvertex + out->numvertices, loadmodel->brushq3.num_vertices);
3594 out->type = 0; // error
3597 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3599 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid element range %i : %i (%i elements)\n", i, out->texture->name, out->firstelement, out->firstelement + out->numelements, loadmodel->brushq3.num_triangles * 3);
3600 out->type = 0; // error
3603 if (out->numtriangles * 3 != out->numelements)
3605 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3606 out->type = 0; // error
3611 case Q3FACETYPE_POLYGON:
3612 case Q3FACETYPE_MESH:
3613 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3614 out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
3615 out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
3616 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3617 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3618 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3619 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3620 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3621 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3623 case Q3FACETYPE_PATCH:
3624 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3625 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3626 if (patchsize[0] < 1 || patchsize[1] < 1)
3628 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3629 out->type = 0; // error
3632 // FIXME: convert patch to triangles here!
3633 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
3637 case Q3FACETYPE_FLARE:
3638 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3643 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3644 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3646 if (invalidelements)
3648 Con_Printf("Mod_Q3BSP_LoadFaces: Warning: face #%i has %i invalid elements, type = %i, texture->name = \"%s\", texture->surfaceflags = %i, texture->contents = %i, firstvertex = %i, numvertices = %i, firstelement = %i, numelements = %i, elements list:\n", i, invalidelements, out->type, out->texture->name, out->texture->surfaceflags, out->texture->contents, out->firstvertex, out->numvertices, out->firstelement, out->numelements);
3649 for (j = 0;j < out->numelements;j++)
3651 Con_Printf(" %i", out->data_element3i[j]);
3652 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3653 out->data_element3i[j] = 0;
3660 static void Mod_Q3BSP_LoadModels(lump_t *l)
3664 int i, j, n, c, count;
3666 in = (void *)(mod_base + l->fileofs);
3667 if (l->filelen % sizeof(*in))
3668 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3669 count = l->filelen / sizeof(*in);
3670 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3672 loadmodel->brushq3.data_models = out;
3673 loadmodel->brushq3.num_models = count;
3675 for (i = 0;i < count;i++, in++, out++)
3677 for (j = 0;j < 3;j++)
3679 out->mins[j] = LittleFloat(in->mins[j]);
3680 out->maxs[j] = LittleFloat(in->maxs[j]);
3682 n = LittleLong(in->firstface);
3683 c = LittleLong(in->numfaces);
3684 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3685 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3686 out->firstface = loadmodel->brushq3.data_faces + n;
3688 n = LittleLong(in->firstbrush);
3689 c = LittleLong(in->numbrushes);
3690 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3691 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3692 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3693 out->numbrushes = c;
3697 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3703 in = (void *)(mod_base + l->fileofs);
3704 if (l->filelen % sizeof(*in))
3705 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3706 count = l->filelen / sizeof(*in);
3707 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3709 loadmodel->brushq3.data_leafbrushes = out;
3710 loadmodel->brushq3.num_leafbrushes = count;
3712 for (i = 0;i < count;i++, in++, out++)
3714 n = LittleLong(*in);
3715 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3716 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3717 *out = loadmodel->brushq3.data_brushes + n;
3721 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3727 in = (void *)(mod_base + l->fileofs);
3728 if (l->filelen % sizeof(*in))
3729 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3730 count = l->filelen / sizeof(*in);
3731 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3733 loadmodel->brushq3.data_leaffaces = out;
3734 loadmodel->brushq3.num_leaffaces = count;
3736 for (i = 0;i < count;i++, in++, out++)
3738 n = LittleLong(*in);
3739 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3740 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3741 *out = loadmodel->brushq3.data_faces + n;
3745 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3749 int i, j, n, c, count;
3751 in = (void *)(mod_base + l->fileofs);
3752 if (l->filelen % sizeof(*in))
3753 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3754 count = l->filelen / sizeof(*in);
3755 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3757 loadmodel->brushq3.data_leafs = out;
3758 loadmodel->brushq3.num_leafs = count;
3760 for (i = 0;i < count;i++, in++, out++)
3762 out->isnode = false;
3764 out->clusterindex = LittleLong(in->clusterindex);
3765 out->areaindex = LittleLong(in->areaindex);
3766 for (j = 0;j < 3;j++)
3768 // yes the mins/maxs are ints
3769 out->mins[j] = LittleLong(in->mins[j]);
3770 out->maxs[j] = LittleLong(in->maxs[j]);
3772 n = LittleLong(in->firstleafface);
3773 c = LittleLong(in->numleaffaces);
3774 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3775 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3776 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3777 out->numleaffaces = c;
3778 n = LittleLong(in->firstleafbrush);
3779 c = LittleLong(in->numleafbrushes);
3780 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3781 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3782 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3783 out->numleafbrushes = c;
3787 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3790 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3791 node->parent = parent;
3794 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3795 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
3799 static void Mod_Q3BSP_LoadNodes(lump_t *l)
3805 in = (void *)(mod_base + l->fileofs);
3806 if (l->filelen % sizeof(*in))
3807 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3808 count = l->filelen / sizeof(*in);
3809 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3811 loadmodel->brushq3.data_nodes = out;
3812 loadmodel->brushq3.num_nodes = count;
3814 for (i = 0;i < count;i++, in++, out++)
3818 n = LittleLong(in->planeindex);
3819 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3820 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3821 out->plane = loadmodel->brushq3.data_planes + n;
3822 for (j = 0;j < 2;j++)
3824 n = LittleLong(in->childrenindex[j]);
3827 if (n >= loadmodel->brushq3.num_nodes)
3828 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
3829 out->children[j] = loadmodel->brushq3.data_nodes + n;
3834 if (n >= loadmodel->brushq3.num_leafs)
3835 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
3836 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
3839 // we don't load the mins/maxs
3842 // set the parent pointers
3843 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
3846 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
3849 q3dlightgrid_t *out;
3852 in = (void *)(mod_base + l->fileofs);
3853 if (l->filelen % sizeof(*in))
3854 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
3855 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
3856 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
3857 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
3858 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3859 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3860 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3861 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
3862 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
3863 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
3864 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
3865 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
3866 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
3867 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
3868 if (l->filelen < count * (int)sizeof(*in))
3869 Host_Error("Mod_Q3BSP_LoadLightGrid: invalid lightgrid lump size %i bytes, should be %i bytes (%ix%ix%i)\n", l->filelen, count * sizeof(*in), loadmodel->brushq3.num_lightgrid_dimensions[0], loadmodel->brushq3.num_lightgrid_dimensions[1], loadmodel->brushq3.num_lightgrid_dimensions[2]);
3870 if (l->filelen != count * (int)sizeof(*in))
3871 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
3873 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3874 loadmodel->brushq3.data_lightgrid = out;
3875 loadmodel->brushq3.num_lightgrid = count;
3877 // no swapping or validation necessary
3878 memcpy(out, in, count * (int)sizeof(*out));
3880 Matrix4x4_CreateScale3(&loadmodel->brushq3.num_lightgrid_indexfromworld, loadmodel->brushq3.num_lightgrid_scale[0], loadmodel->brushq3.num_lightgrid_scale[1], loadmodel->brushq3.num_lightgrid_scale[2]);
3881 Matrix4x4_ConcatTranslate(&loadmodel->brushq3.num_lightgrid_indexfromworld, -loadmodel->brushq3.num_lightgrid_imins[0] * loadmodel->brushq3.num_lightgrid_cellsize[0], -loadmodel->brushq3.num_lightgrid_imins[1] * loadmodel->brushq3.num_lightgrid_cellsize[1], -loadmodel->brushq3.num_lightgrid_imins[2] * loadmodel->brushq3.num_lightgrid_cellsize[2]);
3884 static void Mod_Q3BSP_LoadPVS(lump_t *l)
3889 in = (void *)(mod_base + l->fileofs);
3891 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
3893 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
3894 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
3895 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
3896 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
3897 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
3898 if (l->filelen < totalchains + (int)sizeof(*in))
3899 Host_Error("Mod_Q3BSP_LoadPVS: lump too small ((numclusters = %i) * (chainlength = %i) + sizeof(q3dpvs_t) == %i bytes, lump is %i bytes)\n", loadmodel->brushq3.num_pvsclusters, loadmodel->brushq3.num_pvschainlength, totalchains + sizeof(*in), l->filelen);
3901 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
3902 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
3905 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
3908 q3dheader_t *header;
3910 mod->type = mod_brushq2;
3912 header = (q3dheader_t *)buffer;
3914 i = LittleLong(header->version);
3915 if (i != Q3BSPVERSION)
3916 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
3917 if (loadmodel->isworldmodel)
3919 Cvar_SetValue("halflifebsp", false);
3920 // until we get a texture for it...
3924 mod_base = (qbyte *)header;
3926 // swap all the lumps
3927 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3928 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3930 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
3931 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
3932 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
3933 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
3934 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
3935 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
3936 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
3937 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
3938 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
3939 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
3940 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
3941 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
3942 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
3943 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
3944 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
3945 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
3946 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
3949 void Mod_IBSP_Load(model_t *mod, void *buffer)
3951 int i = LittleLong(((int *)buffer)[1]);
3952 if (i == Q3BSPVERSION)
3953 Mod_Q3BSP_Load(mod,buffer);
3954 else if (i == Q2BSPVERSION)
3955 Mod_Q2BSP_Load(mod,buffer);
3957 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
3960 void Mod_MAP_Load(model_t *mod, void *buffer)
3962 Host_Error("Mod_MAP_Load: not yet implemented\n");