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;
68 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
73 return CONTENTS_EMPTY;
75 Mod_CheckLoaded(model);
77 // LordHavoc: modified to start at first clip node,
78 // in other words: first node of the (sub)model
79 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
80 while (node->contents == 0)
81 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
83 return ((mleaf_t *)node)->contents;
87 typedef struct findnonsolidlocationinfo_s
95 findnonsolidlocationinfo_t;
98 extern cvar_t samelevel;
100 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
102 int i, surfnum, k, *tri, *mark;
103 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
109 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
111 surf = info->model->brushq1.surfaces + *mark;
112 if (surf->flags & SURF_SOLIDCLIP)
115 VectorCopy(surf->plane->normal, surfnormal);
116 if (surf->flags & SURF_PLANEBACK)
117 VectorNegate(surfnormal, surfnormal);
119 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
121 for (k = 0;k < mesh->numtriangles;k++)
123 tri = mesh->element3i + k * 3;
124 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
125 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
126 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
127 VectorSubtract(vert[1], vert[0], edge[0]);
128 VectorSubtract(vert[2], vert[1], edge[1]);
129 CrossProduct(edge[1], edge[0], facenormal);
130 if (facenormal[0] || facenormal[1] || facenormal[2])
132 VectorNormalize(facenormal);
134 if (VectorDistance(facenormal, surfnormal) > 0.01f)
135 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
137 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
138 if (f <= info->bestdist && f >= -info->bestdist)
140 VectorSubtract(vert[0], vert[2], edge[2]);
141 VectorNormalize(edge[0]);
142 VectorNormalize(edge[1]);
143 VectorNormalize(edge[2]);
144 CrossProduct(facenormal, edge[0], edgenormal[0]);
145 CrossProduct(facenormal, edge[1], edgenormal[1]);
146 CrossProduct(facenormal, edge[2], edgenormal[2]);
148 if (samelevel.integer & 1)
149 VectorNegate(edgenormal[0], edgenormal[0]);
150 if (samelevel.integer & 2)
151 VectorNegate(edgenormal[1], edgenormal[1]);
152 if (samelevel.integer & 4)
153 VectorNegate(edgenormal[2], edgenormal[2]);
154 for (i = 0;i < 3;i++)
155 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
156 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
157 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
158 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]);
161 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
162 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
163 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
165 // we got lucky, the center is within the face
166 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
170 if (info->bestdist > dist)
172 info->bestdist = dist;
173 VectorScale(facenormal, (info->radius - -dist), info->nudge);
178 if (info->bestdist > dist)
180 info->bestdist = dist;
181 VectorScale(facenormal, (info->radius - dist), info->nudge);
187 // check which edge or vertex the center is nearest
188 for (i = 0;i < 3;i++)
190 f = DotProduct(info->center, edge[i]);
191 if (f >= DotProduct(vert[0], edge[i])
192 && f <= DotProduct(vert[1], edge[i]))
195 VectorMA(info->center, -f, edge[i], point);
196 dist = sqrt(DotProduct(point, point));
197 if (info->bestdist > dist)
199 info->bestdist = dist;
200 VectorScale(point, (info->radius / dist), info->nudge);
202 // skip both vertex checks
203 // (both are further away than this edge)
208 // not on edge, check first vertex of edge
209 VectorSubtract(info->center, vert[i], point);
210 dist = sqrt(DotProduct(point, point));
211 if (info->bestdist > dist)
213 info->bestdist = dist;
214 VectorScale(point, (info->radius / dist), info->nudge);
227 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
231 if (((mleaf_t *)node)->nummarksurfaces)
232 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
236 float f = PlaneDiff(info->center, node->plane);
237 if (f >= -info->bestdist)
238 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
239 if (f <= info->bestdist)
240 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
244 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
247 findnonsolidlocationinfo_t info;
253 VectorCopy(in, info.center);
254 info.radius = radius;
259 VectorClear(info.nudge);
260 info.bestdist = radius;
261 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
262 VectorAdd(info.center, info.nudge, info.center);
264 while (info.bestdist < radius && ++i < 10);
265 VectorCopy(info.center, out);
270 // the hull we're tracing through
273 // the trace structure to fill in
276 // start, end, and end - start (in model space)
281 RecursiveHullCheckTraceInfo_t;
283 // 1/32 epsilon to keep floating point happy
284 #define DIST_EPSILON (0.03125)
286 #define HULLCHECKSTATE_EMPTY 0
287 #define HULLCHECKSTATE_SOLID 1
288 #define HULLCHECKSTATE_DONE 2
290 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
292 // status variables, these don't need to be saved on the stack when
293 // recursing... but are because this should be thread-safe
294 // (note: tracing against a bbox is not thread-safe, yet)
299 // variables that need to be stored on the stack when recursing
304 // LordHavoc: a goto! everyone flee in terror... :)
309 t->trace->endcontents = num;
310 if (t->trace->thiscontents)
312 if (num == t->trace->thiscontents)
313 t->trace->allsolid = false;
316 // if the first leaf is solid, set startsolid
317 if (t->trace->allsolid)
318 t->trace->startsolid = true;
319 return HULLCHECKSTATE_SOLID;
321 return HULLCHECKSTATE_EMPTY;
325 if (num != CONTENTS_SOLID)
327 t->trace->allsolid = false;
328 if (num == CONTENTS_EMPTY)
329 t->trace->inopen = true;
331 t->trace->inwater = true;
335 // if the first leaf is solid, set startsolid
336 if (t->trace->allsolid)
337 t->trace->startsolid = true;
338 return HULLCHECKSTATE_SOLID;
340 return HULLCHECKSTATE_EMPTY;
344 // find the point distances
345 node = t->hull->clipnodes + num;
347 plane = t->hull->planes + node->planenum;
350 t1 = p1[plane->type] - plane->dist;
351 t2 = p2[plane->type] - plane->dist;
355 t1 = DotProduct (plane->normal, p1) - plane->dist;
356 t2 = DotProduct (plane->normal, p2) - plane->dist;
363 num = node->children[1];
372 num = node->children[0];
378 // the line intersects, find intersection point
379 // LordHavoc: this uses the original trace for maximum accuracy
382 t1 = t->start[plane->type] - plane->dist;
383 t2 = t->end[plane->type] - plane->dist;
387 t1 = DotProduct (plane->normal, t->start) - plane->dist;
388 t2 = DotProduct (plane->normal, t->end) - plane->dist;
391 midf = t1 / (t1 - t2);
392 midf = bound(p1f, midf, p2f);
393 VectorMA(t->start, midf, t->dist, mid);
395 // recurse both sides, front side first
396 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
397 // if this side is not empty, return what it is (solid or done)
398 if (ret != HULLCHECKSTATE_EMPTY)
401 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
402 // if other side is not solid, return what it is (empty or done)
403 if (ret != HULLCHECKSTATE_SOLID)
406 // front is air and back is solid, this is the impact point...
409 t->trace->plane.dist = -plane->dist;
410 VectorNegate (plane->normal, t->trace->plane.normal);
414 t->trace->plane.dist = plane->dist;
415 VectorCopy (plane->normal, t->trace->plane.normal);
418 // bias away from surface a bit
419 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
420 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
422 midf = t1 / (t1 - t2);
423 t->trace->fraction = bound(0.0f, midf, 1.0);
425 return HULLCHECKSTATE_DONE;
428 static void Mod_Q1BSP_TraceBox(struct model_s *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
430 // this function currently only supports same size start and end
432 RecursiveHullCheckTraceInfo_t rhc;
434 memset(&rhc, 0, sizeof(rhc));
435 memset(trace, 0, sizeof(trace_t));
437 rhc.trace->fraction = 1;
438 rhc.trace->allsolid = true;
439 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
441 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
442 else if (model->brushq1.ishlbsp)
444 if (boxsize[0] <= 32)
446 if (boxsize[2] < 54) // pick the nearest of 36 or 72
447 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
449 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
452 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
456 if (boxsize[0] <= 32)
457 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
459 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
461 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
462 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
463 VectorSubtract(rhc.end, rhc.start, rhc.dist);
464 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
467 static qbyte *Mod_Q1BSP_DecompressVis(model_t *model, qbyte *in)
469 static qbyte decompressed[MAX_MAP_LEAFS/8];
474 row = (model->brushq1.numleafs+7)>>3;
492 } while (out - decompressed < row);
497 static qbyte *Mod_Q1BSP_LeafPVS(model_t *model, mleaf_t *leaf)
499 if (r_novis.integer || leaf == model->brushq1.leafs || leaf->compressed_vis == NULL)
500 return mod_q1bsp_novis;
501 return Mod_Q1BSP_DecompressVis(model, leaf->compressed_vis);
504 static void Mod_Q1BSP_LoadTextures(lump_t *l)
506 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
508 texture_t *tx, *tx2, *anims[10], *altanims[10];
510 qbyte *data, *mtdata;
513 loadmodel->brushq1.textures = NULL;
518 m = (dmiptexlump_t *)(mod_base + l->fileofs);
520 m->nummiptex = LittleLong (m->nummiptex);
522 // add two slots for notexture walls and notexture liquids
523 loadmodel->brushq1.numtextures = m->nummiptex + 2;
524 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
526 // fill out all slots with notexture
527 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
530 strcpy(tx->name, "NO TEXTURE FOUND");
533 tx->skin.base = r_notexture;
534 tx->shader = &Cshader_wall_lightmap;
535 tx->flags = SURF_SOLIDCLIP;
536 if (i == loadmodel->brushq1.numtextures - 1)
538 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
539 tx->shader = &Cshader_water;
541 tx->currentframe = tx;
544 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
546 // LordHavoc: mostly rewritten map texture loader
547 for (i = 0;i < m->nummiptex;i++)
549 dofs[i] = LittleLong(dofs[i]);
550 if (dofs[i] == -1 || r_nosurftextures.integer)
552 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
554 // make sure name is no more than 15 characters
555 for (j = 0;dmiptex->name[j] && j < 15;j++)
556 name[j] = dmiptex->name[j];
559 mtwidth = LittleLong(dmiptex->width);
560 mtheight = LittleLong(dmiptex->height);
562 j = LittleLong(dmiptex->offsets[0]);
566 if (j < 40 || j + mtwidth * mtheight > l->filelen)
568 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
571 mtdata = (qbyte *)dmiptex + j;
574 if ((mtwidth & 15) || (mtheight & 15))
575 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
577 // LordHavoc: force all names to lowercase
578 for (j = 0;name[j];j++)
579 if (name[j] >= 'A' && name[j] <= 'Z')
580 name[j] += 'a' - 'A';
582 tx = loadmodel->brushq1.textures + i;
583 strcpy(tx->name, name);
585 tx->height = mtheight;
589 sprintf(tx->name, "unnamed%i", i);
590 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
593 // LordHavoc: HL sky textures are entirely different than quake
594 if (!loadmodel->brushq1.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
596 if (loadmodel->isworldmodel)
598 data = loadimagepixels(tx->name, false, 0, 0);
601 if (image_width == 256 && image_height == 128)
609 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
611 R_InitSky(mtdata, 1);
614 else if (mtdata != NULL)
615 R_InitSky(mtdata, 1);
620 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
622 // did not find external texture, load it from the bsp or wad3
623 if (loadmodel->brushq1.ishlbsp)
625 // internal texture overrides wad
626 qbyte *pixels, *freepixels, *fogpixels;
627 pixels = freepixels = NULL;
629 pixels = W_ConvertWAD3Texture(dmiptex);
631 pixels = freepixels = W_GetTexture(tx->name);
634 tx->width = image_width;
635 tx->height = image_height;
636 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);
637 if (Image_CheckAlpha(pixels, image_width * image_height, true))
639 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
640 for (j = 0;j < image_width * image_height * 4;j += 4)
642 fogpixels[j + 0] = 255;
643 fogpixels[j + 1] = 255;
644 fogpixels[j + 2] = 255;
645 fogpixels[j + 3] = pixels[j + 3];
647 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
652 Mem_Free(freepixels);
654 else if (mtdata) // texture included
655 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
658 if (tx->skin.base == NULL)
663 tx->skin.base = r_notexture;
666 if (tx->name[0] == '*')
668 // turb does not block movement
669 tx->flags &= ~SURF_SOLIDCLIP;
670 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
671 // LordHavoc: some turbulent textures should be fullbright and solid
672 if (!strncmp(tx->name,"*lava",5)
673 || !strncmp(tx->name,"*teleport",9)
674 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
675 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
677 tx->flags |= SURF_WATERALPHA;
678 tx->shader = &Cshader_water;
680 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
682 tx->flags |= SURF_DRAWSKY;
683 tx->shader = &Cshader_sky;
687 tx->flags |= SURF_LIGHTMAP;
689 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
690 tx->shader = &Cshader_wall_lightmap;
693 // start out with no animation
694 tx->currentframe = tx;
697 // sequence the animations
698 for (i = 0;i < m->nummiptex;i++)
700 tx = loadmodel->brushq1.textures + i;
701 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
703 if (tx->anim_total[0] || tx->anim_total[1])
704 continue; // already sequenced
706 // find the number of frames in the animation
707 memset(anims, 0, sizeof(anims));
708 memset(altanims, 0, sizeof(altanims));
710 for (j = i;j < m->nummiptex;j++)
712 tx2 = loadmodel->brushq1.textures + j;
713 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
717 if (num >= '0' && num <= '9')
718 anims[num - '0'] = tx2;
719 else if (num >= 'a' && num <= 'j')
720 altanims[num - 'a'] = tx2;
722 Con_Printf("Bad animating texture %s\n", tx->name);
726 for (j = 0;j < 10;j++)
733 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
736 for (j = 0;j < max;j++)
740 Con_Printf("Missing frame %i of %s\n", j, tx->name);
744 for (j = 0;j < altmax;j++)
748 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
757 // if there is no alternate animation, duplicate the primary
758 // animation into the alternate
760 for (k = 0;k < 10;k++)
761 altanims[k] = anims[k];
764 // link together the primary animation
765 for (j = 0;j < max;j++)
768 tx2->animated = true;
769 tx2->anim_total[0] = max;
770 tx2->anim_total[1] = altmax;
771 for (k = 0;k < 10;k++)
773 tx2->anim_frames[0][k] = anims[k];
774 tx2->anim_frames[1][k] = altanims[k];
778 // if there really is an alternate anim...
779 if (anims[0] != altanims[0])
781 // link together the alternate animation
782 for (j = 0;j < altmax;j++)
785 tx2->animated = true;
786 // the primary/alternate are reversed here
787 tx2->anim_total[0] = altmax;
788 tx2->anim_total[1] = max;
789 for (k = 0;k < 10;k++)
791 tx2->anim_frames[0][k] = altanims[k];
792 tx2->anim_frames[1][k] = anims[k];
799 static void Mod_Q1BSP_LoadLighting(lump_t *l)
802 qbyte *in, *out, *data, d;
803 char litfilename[1024];
804 loadmodel->brushq1.lightdata = NULL;
805 if (loadmodel->brushq1.ishlbsp) // LordHavoc: load the colored lighting data straight
807 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
808 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
810 else // LordHavoc: bsp version 29 (normal white lighting)
812 // LordHavoc: hope is not lost yet, check for a .lit file to load
813 strcpy(litfilename, loadmodel->name);
814 FS_StripExtension(litfilename, litfilename);
815 strcat(litfilename, ".lit");
816 data = (qbyte*) FS_LoadFile(litfilename, false);
819 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
821 i = LittleLong(((int *)data)[1]);
824 Con_DPrintf("loaded %s\n", litfilename);
825 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
826 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
832 Con_Printf("Unknown .lit file version (%d)\n", i);
838 if (fs_filesize == 8)
839 Con_Printf("Empty .lit file, ignoring\n");
841 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
845 // LordHavoc: oh well, expand the white lighting data
848 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
849 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
850 out = loadmodel->brushq1.lightdata;
851 memcpy(in, mod_base + l->fileofs, l->filelen);
852 for (i = 0;i < l->filelen;i++)
862 static void Mod_Q1BSP_LoadLightList(void)
865 char lightsfilename[1024], *s, *t, *lightsstring;
868 strcpy(lightsfilename, loadmodel->name);
869 FS_StripExtension(lightsfilename, lightsfilename);
870 strcat(lightsfilename, ".lights");
871 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
877 while (*s && *s != '\n')
881 Mem_Free(lightsstring);
882 Host_Error("lights file must end with a newline\n");
887 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
890 while (*s && n < numlights)
893 while (*s && *s != '\n')
897 Mem_Free(lightsstring);
898 Host_Error("misparsed lights file!\n");
900 e = loadmodel->brushq1.lights + n;
902 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);
906 Mem_Free(lightsstring);
907 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);
914 Mem_Free(lightsstring);
915 Host_Error("misparsed lights file!\n");
917 loadmodel->brushq1.numlights = numlights;
918 Mem_Free(lightsstring);
923 static int castshadowcount = 0;
924 static void Mod_Q1BSP_ProcessLightList(void)
926 int j, k, l, *mark, lnum;
934 for (lnum = 0, e = loadmodel->brushq1.lights;lnum < loadmodel->brushq1.numlights;lnum++, e++)
936 e->cullradius2 = DotProduct(e->light, e->light) / (e->falloff * e->falloff * 8192.0f * 8192.0f * 2.0f * 2.0f);// + 4096.0f;
937 if (e->cullradius2 > 4096.0f * 4096.0f)
938 e->cullradius2 = 4096.0f * 4096.0f;
939 e->cullradius = e->lightradius = sqrt(e->cullradius2);
940 leaf = Mod_Q1BSP_PointInLeaf(e->origin, loadmodel);
941 if (leaf->compressed_vis)
942 pvs = Mod_Q1BSP_DecompressVis(leaf->compressed_vis, loadmodel);
944 pvs = mod_q1bsp_novis;
945 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
946 loadmodel->brushq1.surfacevisframes[j] = -1;
947 for (j = 0, leaf = loadmodel->brushq1.leafs + 1;j < loadmodel->brushq1.numleafs - 1;j++, leaf++)
949 if (pvs[j >> 3] & (1 << (j & 7)))
951 for (k = 0, mark = leaf->firstmarksurface;k < leaf->nummarksurfaces;k++, mark++)
953 surf = loadmodel->brushq1.surfaces + *mark;
954 if (surf->number != *mark)
955 Con_Printf("%d != %d\n", surf->number, *mark);
956 dist = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
957 if (surf->flags & SURF_PLANEBACK)
959 if (dist > 0 && dist < e->cullradius)
961 temp[0] = bound(surf->poly_mins[0], e->origin[0], surf->poly_maxs[0]) - e->origin[0];
962 temp[1] = bound(surf->poly_mins[1], e->origin[1], surf->poly_maxs[1]) - e->origin[1];
963 temp[2] = bound(surf->poly_mins[2], e->origin[2], surf->poly_maxs[2]) - e->origin[2];
964 if (DotProduct(temp, temp) < lightradius2)
965 loadmodel->brushq1.surfacevisframes[*mark] = -2;
970 // build list of light receiving surfaces
972 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
973 if (loadmodel->brushq1.surfacevisframes[j] == -2)
976 if (e->numsurfaces > 0)
978 e->surfaces = Mem_Alloc(loadmodel->mempool, sizeof(msurface_t *) * e->numsurfaces);
980 for (j = 0;j < loadmodel->brushq1.numsurfaces;j++)
981 if (loadmodel->brushq1.surfacevisframes[j] == -2)
982 e->surfaces[e->numsurfaces++] = loadmodel->brushq1.surfaces + j;
984 // find bounding box and sphere of lit surfaces
985 // (these will be used for creating a shape to clip the light)
987 for (j = 0;j < e->numsurfaces;j++)
989 surf = e->surfaces[j];
992 VectorCopy(surf->poly_verts, e->mins);
993 VectorCopy(surf->poly_verts, e->maxs);
995 for (k = 0, v = surf->poly_verts;k < surf->poly_numverts;k++, v += 3)
997 if (e->mins[0] > v[0]) e->mins[0] = v[0];if (e->maxs[0] < v[0]) e->maxs[0] = v[0];
998 if (e->mins[1] > v[1]) e->mins[1] = v[1];if (e->maxs[1] < v[1]) e->maxs[1] = v[1];
999 if (e->mins[2] > v[2]) e->mins[2] = v[2];if (e->maxs[2] < v[2]) e->maxs[2] = v[2];
1000 VectorSubtract(v, e->origin, temp);
1001 dist = DotProduct(temp, temp);
1006 if (e->cullradius2 > radius2)
1008 e->cullradius2 = radius2;
1009 e->cullradius = sqrt(e->cullradius2);
1011 if (e->mins[0] < e->origin[0] - e->lightradius) e->mins[0] = e->origin[0] - e->lightradius;
1012 if (e->maxs[0] > e->origin[0] + e->lightradius) e->maxs[0] = e->origin[0] + e->lightradius;
1013 if (e->mins[1] < e->origin[1] - e->lightradius) e->mins[1] = e->origin[1] - e->lightradius;
1014 if (e->maxs[1] > e->origin[1] + e->lightradius) e->maxs[1] = e->origin[1] + e->lightradius;
1015 if (e->mins[2] < e->origin[2] - e->lightradius) e->mins[2] = e->origin[2] - e->lightradius;
1016 if (e->maxs[2] > e->origin[2] + e->lightradius) e->maxs[2] = e->origin[2] + e->lightradius;
1017 // clip shadow volumes against eachother to remove unnecessary
1018 // polygons(and sections of polygons)
1020 //vec3_t polymins, polymaxs;
1022 float *verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1023 float f, *v0, *v1, projectdistance;
1025 e->shadowvolume = Mod_ShadowMesh_Begin(loadmodel->mempool, 1024);
1028 vec3_t outermins, outermaxs, innermins, innermaxs;
1029 innermins[0] = e->mins[0] - 1;
1030 innermins[1] = e->mins[1] - 1;
1031 innermins[2] = e->mins[2] - 1;
1032 innermaxs[0] = e->maxs[0] + 1;
1033 innermaxs[1] = e->maxs[1] + 1;
1034 innermaxs[2] = e->maxs[2] + 1;
1035 outermins[0] = loadmodel->normalmins[0] - 1;
1036 outermins[1] = loadmodel->normalmins[1] - 1;
1037 outermins[2] = loadmodel->normalmins[2] - 1;
1038 outermaxs[0] = loadmodel->normalmaxs[0] + 1;
1039 outermaxs[1] = loadmodel->normalmaxs[1] + 1;
1040 outermaxs[2] = loadmodel->normalmaxs[2] + 1;
1041 // add bounding box around the whole shadow volume set,
1042 // facing inward to limit light area, with an outer bounding box
1043 // facing outward (this is needed by the shadow rendering method)
1045 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1046 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1047 verts[ 6] = innermaxs[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1048 verts[ 9] = innermaxs[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1049 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1050 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1051 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1052 verts[ 6] = outermaxs[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1053 verts[ 9] = outermaxs[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1054 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1056 verts[ 0] = innermins[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1057 verts[ 3] = innermins[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1058 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1059 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1060 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1061 verts[ 0] = outermins[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1062 verts[ 3] = outermins[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1063 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1064 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1065 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1067 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermaxs[2];
1068 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermins[2];
1069 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermins[2];
1070 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermaxs[2];
1071 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1072 verts[ 0] = outermins[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1073 verts[ 3] = outermins[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1074 verts[ 6] = outermaxs[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1075 verts[ 9] = outermaxs[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1076 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1078 verts[ 0] = innermins[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1079 verts[ 3] = innermins[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1080 verts[ 6] = innermaxs[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1081 verts[ 9] = innermaxs[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1082 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1083 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermaxs[2];
1084 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermins[2];
1085 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermins[2];
1086 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermaxs[2];
1087 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1089 verts[ 0] = innermaxs[0];verts[ 1] = innermins[1];verts[ 2] = innermaxs[2];
1090 verts[ 3] = innermaxs[0];verts[ 4] = innermaxs[1];verts[ 5] = innermaxs[2];
1091 verts[ 6] = innermins[0];verts[ 7] = innermaxs[1];verts[ 8] = innermaxs[2];
1092 verts[ 9] = innermins[0];verts[10] = innermins[1];verts[11] = innermaxs[2];
1093 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1094 verts[ 0] = outermaxs[0];verts[ 1] = outermaxs[1];verts[ 2] = outermaxs[2];
1095 verts[ 3] = outermaxs[0];verts[ 4] = outermins[1];verts[ 5] = outermaxs[2];
1096 verts[ 6] = outermins[0];verts[ 7] = outermins[1];verts[ 8] = outermaxs[2];
1097 verts[ 9] = outermins[0];verts[10] = outermaxs[1];verts[11] = outermaxs[2];
1098 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1100 verts[ 0] = innermaxs[0];verts[ 1] = innermaxs[1];verts[ 2] = innermins[2];
1101 verts[ 3] = innermaxs[0];verts[ 4] = innermins[1];verts[ 5] = innermins[2];
1102 verts[ 6] = innermins[0];verts[ 7] = innermins[1];verts[ 8] = innermins[2];
1103 verts[ 9] = innermins[0];verts[10] = innermaxs[1];verts[11] = innermins[2];
1104 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1105 verts[ 0] = outermaxs[0];verts[ 1] = outermins[1];verts[ 2] = outermins[2];
1106 verts[ 3] = outermaxs[0];verts[ 4] = outermaxs[1];verts[ 5] = outermins[2];
1107 verts[ 6] = outermins[0];verts[ 7] = outermaxs[1];verts[ 8] = outermins[2];
1108 verts[ 9] = outermins[0];verts[10] = outermins[1];verts[11] = outermins[2];
1109 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1113 for (j = 0;j < e->numsurfaces;j++)
1115 surf = e->surfaces[j];
1116 if (surf->flags & SURF_SHADOWCAST)
1117 surf->castshadow = castshadowcount;
1119 for (j = 0;j < e->numsurfaces;j++)
1121 surf = e->surfaces[j];
1122 if (surf->castshadow != castshadowcount)
1124 f = DotProduct(e->origin, surf->plane->normal) - surf->plane->dist;
1125 if (surf->flags & SURF_PLANEBACK)
1127 projectdistance = e->lightradius;
1128 if (maxverts < surf->poly_numverts)
1130 maxverts = surf->poly_numverts;
1133 verts = Mem_Alloc(loadmodel->mempool, maxverts * sizeof(float[3]));
1135 // copy the original polygon, for the front cap of the volume
1136 for (k = 0, v0 = surf->poly_verts, v1 = verts;k < surf->poly_numverts;k++, v0 += 3, v1 += 3)
1138 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1139 // project the original polygon, reversed, for the back cap of the volume
1140 for (k = 0, v0 = surf->poly_verts + (surf->poly_numverts - 1) * 3, v1 = verts;k < surf->poly_numverts;k++, v0 -= 3, v1 += 3)
1142 VectorSubtract(v0, e->origin, temp);
1143 VectorNormalize(temp);
1144 VectorMA(v0, projectdistance, temp, v1);
1146 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, surf->poly_numverts, verts);
1147 // project the shadow volume sides
1148 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)
1150 if (!surf->neighborsurfaces[l] || surf->neighborsurfaces[l]->castshadow != castshadowcount)
1152 VectorCopy(v1, &verts[0]);
1153 VectorCopy(v0, &verts[3]);
1154 VectorCopy(v0, &verts[6]);
1155 VectorCopy(v1, &verts[9]);
1156 VectorSubtract(&verts[6], e->origin, temp);
1157 VectorNormalize(temp);
1158 VectorMA(&verts[6], projectdistance, temp, &verts[6]);
1159 VectorSubtract(&verts[9], e->origin, temp);
1160 VectorNormalize(temp);
1161 VectorMA(&verts[9], projectdistance, temp, &verts[9]);
1162 Mod_ShadowMesh_AddPolygon(loadmodel->mempool, e->shadowvolume, 4, verts);
1166 // build the triangle mesh
1167 e->shadowvolume = Mod_ShadowMesh_Finish(loadmodel->mempool, e->shadowvolume);
1171 for (mesh = e->shadowvolume;mesh;mesh = mesh->next)
1172 l += mesh->numtriangles;
1173 Con_Printf("light %i shadow volume built containing %i triangles\n", lnum, l);
1181 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1183 loadmodel->brushq1.visdata = NULL;
1186 loadmodel->brushq1.visdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1187 memcpy(loadmodel->brushq1.visdata, mod_base + l->fileofs, l->filelen);
1190 // used only for HalfLife maps
1191 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1193 char key[128], value[4096];
1198 if (!COM_ParseToken(&data))
1200 if (com_token[0] != '{')
1204 if (!COM_ParseToken(&data))
1206 if (com_token[0] == '}')
1207 break; // end of worldspawn
1208 if (com_token[0] == '_')
1209 strcpy(key, com_token + 1);
1211 strcpy(key, com_token);
1212 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1213 key[strlen(key)-1] = 0;
1214 if (!COM_ParseToken(&data))
1216 strcpy(value, com_token);
1217 if (!strcmp("wad", key)) // for HalfLife maps
1219 if (loadmodel->brushq1.ishlbsp)
1222 for (i = 0;i < 4096;i++)
1223 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1229 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1230 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1232 else if (value[i] == ';' || value[i] == 0)
1236 strcpy(wadname, "textures/");
1237 strcat(wadname, &value[j]);
1238 W_LoadTextureWadFile(wadname, false);
1250 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1252 loadmodel->brush.entities = NULL;
1255 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1256 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1257 if (loadmodel->brushq1.ishlbsp)
1258 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1262 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1268 in = (void *)(mod_base + l->fileofs);
1269 if (l->filelen % sizeof(*in))
1270 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1271 count = l->filelen / sizeof(*in);
1272 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1274 loadmodel->brushq1.vertexes = out;
1275 loadmodel->brushq1.numvertexes = count;
1277 for ( i=0 ; i<count ; i++, in++, out++)
1279 out->position[0] = LittleFloat(in->point[0]);
1280 out->position[1] = LittleFloat(in->point[1]);
1281 out->position[2] = LittleFloat(in->point[2]);
1285 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1291 in = (void *)(mod_base + l->fileofs);
1292 if (l->filelen % sizeof(*in))
1293 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1294 count = l->filelen / sizeof(*in);
1295 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1297 loadmodel->brushq1.submodels = out;
1298 loadmodel->brushq1.numsubmodels = count;
1300 for ( i=0 ; i<count ; i++, in++, out++)
1302 for (j=0 ; j<3 ; j++)
1304 // spread the mins / maxs by a pixel
1305 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1306 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1307 out->origin[j] = LittleFloat(in->origin[j]);
1309 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1310 out->headnode[j] = LittleLong(in->headnode[j]);
1311 out->visleafs = LittleLong(in->visleafs);
1312 out->firstface = LittleLong(in->firstface);
1313 out->numfaces = LittleLong(in->numfaces);
1317 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1323 in = (void *)(mod_base + l->fileofs);
1324 if (l->filelen % sizeof(*in))
1325 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1326 count = l->filelen / sizeof(*in);
1327 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1329 loadmodel->brushq1.edges = out;
1330 loadmodel->brushq1.numedges = count;
1332 for ( i=0 ; i<count ; i++, in++, out++)
1334 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1335 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1339 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1343 int i, j, k, count, miptex;
1345 in = (void *)(mod_base + l->fileofs);
1346 if (l->filelen % sizeof(*in))
1347 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1348 count = l->filelen / sizeof(*in);
1349 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1351 loadmodel->brushq1.texinfo = out;
1352 loadmodel->brushq1.numtexinfo = count;
1354 for (i = 0;i < count;i++, in++, out++)
1356 for (k = 0;k < 2;k++)
1357 for (j = 0;j < 4;j++)
1358 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1360 miptex = LittleLong(in->miptex);
1361 out->flags = LittleLong(in->flags);
1363 out->texture = NULL;
1364 if (loadmodel->brushq1.textures)
1366 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1367 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1369 out->texture = loadmodel->brushq1.textures + miptex;
1371 if (out->flags & TEX_SPECIAL)
1373 // if texture chosen is NULL or the shader needs a lightmap,
1374 // force to notexture water shader
1375 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1376 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1380 // if texture chosen is NULL, force to notexture
1381 if (out->texture == NULL)
1382 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1388 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1393 mins[0] = mins[1] = mins[2] = 9999;
1394 maxs[0] = maxs[1] = maxs[2] = -9999;
1396 for (i = 0;i < numverts;i++)
1398 for (j = 0;j < 3;j++, v++)
1408 #define MAX_SUBDIVPOLYTRIANGLES 4096
1409 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1411 static int subdivpolyverts, subdivpolytriangles;
1412 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1413 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1415 static int subdivpolylookupvert(vec3_t v)
1418 for (i = 0;i < subdivpolyverts;i++)
1419 if (subdivpolyvert[i][0] == v[0]
1420 && subdivpolyvert[i][1] == v[1]
1421 && subdivpolyvert[i][2] == v[2])
1423 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1424 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1425 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1426 return subdivpolyverts++;
1429 static void SubdividePolygon(int numverts, float *verts)
1431 int i, i1, i2, i3, f, b, c, p;
1432 vec3_t mins, maxs, front[256], back[256];
1433 float m, *pv, *cv, dist[256], frac;
1436 Host_Error("SubdividePolygon: ran out of verts in buffer");
1438 BoundPoly(numverts, verts, mins, maxs);
1440 for (i = 0;i < 3;i++)
1442 m = (mins[i] + maxs[i]) * 0.5;
1443 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1444 if (maxs[i] - m < 8)
1446 if (m - mins[i] < 8)
1450 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1451 dist[c] = cv[i] - m;
1454 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1458 VectorCopy(pv, front[f]);
1463 VectorCopy(pv, back[b]);
1466 if (dist[p] == 0 || dist[c] == 0)
1468 if ((dist[p] > 0) != (dist[c] > 0) )
1471 frac = dist[p] / (dist[p] - dist[c]);
1472 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1473 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1474 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1480 SubdividePolygon(f, front[0]);
1481 SubdividePolygon(b, back[0]);
1485 i1 = subdivpolylookupvert(verts);
1486 i2 = subdivpolylookupvert(verts + 3);
1487 for (i = 2;i < numverts;i++)
1489 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1491 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1495 i3 = subdivpolylookupvert(verts + i * 3);
1496 subdivpolyindex[subdivpolytriangles][0] = i1;
1497 subdivpolyindex[subdivpolytriangles][1] = i2;
1498 subdivpolyindex[subdivpolytriangles][2] = i3;
1500 subdivpolytriangles++;
1504 //Breaks a polygon up along axial 64 unit
1505 //boundaries so that turbulent and sky warps
1506 //can be done reasonably.
1507 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1513 subdivpolytriangles = 0;
1514 subdivpolyverts = 0;
1515 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1516 if (subdivpolytriangles < 1)
1517 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1519 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1520 mesh->numverts = subdivpolyverts;
1521 mesh->numtriangles = subdivpolytriangles;
1522 mesh->vertex = (surfvertex_t *)(mesh + 1);
1523 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1524 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1526 for (i = 0;i < mesh->numtriangles;i++)
1527 for (j = 0;j < 3;j++)
1528 mesh->index[i*3+j] = subdivpolyindex[i][j];
1530 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1532 VectorCopy(subdivpolyvert[i], v->v);
1533 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1534 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1539 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1542 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1543 mesh->numverts = numverts;
1544 mesh->numtriangles = numtriangles;
1545 mesh->vertex3f = (float *)(mesh + 1);
1546 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1547 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1548 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1549 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1550 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1551 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1552 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1553 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1554 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1558 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1561 float *vec, *vert, mins[3], maxs[3], val, *v;
1564 // convert edges back to a normal polygon
1565 surf->poly_numverts = numedges;
1566 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1567 for (i = 0;i < numedges;i++)
1569 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1571 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1573 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1574 VectorCopy(vec, vert);
1578 // calculate polygon bounding box and center
1579 vert = surf->poly_verts;
1580 VectorCopy(vert, mins);
1581 VectorCopy(vert, maxs);
1583 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1585 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1586 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1587 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1589 VectorCopy(mins, surf->poly_mins);
1590 VectorCopy(maxs, surf->poly_maxs);
1591 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1592 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1593 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1595 // generate surface extents information
1596 tex = surf->texinfo;
1597 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1598 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1599 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1601 for (j = 0;j < 2;j++)
1603 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1610 for (i = 0;i < 2;i++)
1612 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1613 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1617 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1621 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1625 in = (void *)(mod_base + l->fileofs);
1626 if (l->filelen % sizeof(*in))
1627 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1628 count = l->filelen / sizeof(*in);
1629 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1631 loadmodel->brushq1.numsurfaces = count;
1632 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1633 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1634 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1636 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++)
1638 surf->number = surfnum;
1639 // FIXME: validate edges, texinfo, etc?
1640 firstedge = LittleLong(in->firstedge);
1641 numedges = LittleShort(in->numedges);
1642 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)
1643 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1644 i = LittleShort(in->texinfo);
1645 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1646 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1647 surf->texinfo = loadmodel->brushq1.texinfo + i;
1648 surf->flags = surf->texinfo->texture->flags;
1650 planenum = LittleShort(in->planenum);
1651 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1652 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1654 if (LittleShort(in->side))
1655 surf->flags |= SURF_PLANEBACK;
1657 surf->plane = loadmodel->brushq1.planes + planenum;
1659 // clear lightmap (filled in later)
1660 surf->lightmaptexture = NULL;
1662 // force lightmap upload on first time seeing the surface
1663 surf->cached_dlight = true;
1665 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1667 ssize = (surf->extents[0] >> 4) + 1;
1668 tsize = (surf->extents[1] >> 4) + 1;
1671 for (i = 0;i < MAXLIGHTMAPS;i++)
1672 surf->styles[i] = in->styles[i];
1673 i = LittleLong(in->lightofs);
1675 surf->samples = NULL;
1676 else if (loadmodel->brushq1.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1677 surf->samples = loadmodel->brushq1.lightdata + i;
1678 else // LordHavoc: white lighting (bsp version 29)
1679 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1681 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1683 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1684 Host_Error("Bad surface extents");
1685 // stainmap for permanent marks on walls
1686 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1688 memset(surf->stainsamples, 255, ssize * tsize * 3);
1692 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1693 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1695 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++)
1697 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1698 mesh->numverts = surf->poly_numverts;
1699 mesh->numtriangles = surf->poly_numverts - 2;
1700 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1701 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1702 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1703 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1704 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1705 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1706 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1707 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1708 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1709 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1711 surf->lightmaptexturestride = 0;
1712 surf->lightmaptexture = NULL;
1714 for (i = 0;i < mesh->numverts;i++)
1716 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1717 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1718 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1719 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1720 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1721 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1722 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1723 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1724 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1725 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1726 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1727 mesh->lightmapoffsets[i] = 0;
1730 for (i = 0;i < mesh->numtriangles;i++)
1732 mesh->element3i[i * 3 + 0] = 0;
1733 mesh->element3i[i * 3 + 1] = i + 1;
1734 mesh->element3i[i * 3 + 2] = i + 2;
1737 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1738 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1740 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1742 int i, iu, iv, smax, tmax;
1743 float u, v, ubase, vbase, uscale, vscale;
1745 smax = surf->extents[0] >> 4;
1746 tmax = surf->extents[1] >> 4;
1748 surf->flags |= SURF_LIGHTMAP;
1749 if (r_miplightmaps.integer)
1751 surf->lightmaptexturestride = smax+1;
1752 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);
1756 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1757 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);
1759 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1760 uscale = (uscale - ubase) / (smax + 1);
1761 vscale = (vscale - vbase) / (tmax + 1);
1763 for (i = 0;i < mesh->numverts;i++)
1765 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1766 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1767 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1768 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1769 // LordHavoc: calc lightmap data offset for vertex lighting to use
1772 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1778 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1780 node->parent = parent;
1781 if (node->contents < 0)
1783 Mod_Q1BSP_SetParent(node->children[0], node);
1784 Mod_Q1BSP_SetParent(node->children[1], node);
1787 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1793 in = (void *)(mod_base + l->fileofs);
1794 if (l->filelen % sizeof(*in))
1795 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1796 count = l->filelen / sizeof(*in);
1797 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1799 loadmodel->brushq1.nodes = out;
1800 loadmodel->brushq1.numnodes = count;
1802 for ( i=0 ; i<count ; i++, in++, out++)
1804 for (j=0 ; j<3 ; j++)
1806 out->mins[j] = LittleShort(in->mins[j]);
1807 out->maxs[j] = LittleShort(in->maxs[j]);
1810 p = LittleLong(in->planenum);
1811 out->plane = loadmodel->brushq1.planes + p;
1813 out->firstsurface = LittleShort(in->firstface);
1814 out->numsurfaces = LittleShort(in->numfaces);
1816 for (j=0 ; j<2 ; j++)
1818 p = LittleShort(in->children[j]);
1820 out->children[j] = loadmodel->brushq1.nodes + p;
1822 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1826 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1829 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1835 in = (void *)(mod_base + l->fileofs);
1836 if (l->filelen % sizeof(*in))
1837 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1838 count = l->filelen / sizeof(*in);
1839 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1841 loadmodel->brushq1.leafs = out;
1842 loadmodel->brushq1.numleafs = count;
1844 for ( i=0 ; i<count ; i++, in++, out++)
1846 for (j=0 ; j<3 ; j++)
1848 out->mins[j] = LittleShort(in->mins[j]);
1849 out->maxs[j] = LittleShort(in->maxs[j]);
1852 p = LittleLong(in->contents);
1855 out->firstmarksurface = loadmodel->brushq1.marksurfaces +
1856 LittleShort(in->firstmarksurface);
1857 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1859 p = LittleLong(in->visofs);
1861 out->compressed_vis = NULL;
1863 out->compressed_vis = loadmodel->brushq1.visdata + p;
1865 for (j=0 ; j<4 ; j++)
1866 out->ambient_sound_level[j] = in->ambient_level[j];
1868 // FIXME: Insert caustics here
1872 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1874 dclipnode_t *in, *out;
1878 in = (void *)(mod_base + l->fileofs);
1879 if (l->filelen % sizeof(*in))
1880 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1881 count = l->filelen / sizeof(*in);
1882 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1884 loadmodel->brushq1.clipnodes = out;
1885 loadmodel->brushq1.numclipnodes = count;
1887 if (loadmodel->brushq1.ishlbsp)
1889 hull = &loadmodel->brushq1.hulls[1];
1890 hull->clipnodes = out;
1891 hull->firstclipnode = 0;
1892 hull->lastclipnode = count-1;
1893 hull->planes = loadmodel->brushq1.planes;
1894 hull->clip_mins[0] = -16;
1895 hull->clip_mins[1] = -16;
1896 hull->clip_mins[2] = -36;
1897 hull->clip_maxs[0] = 16;
1898 hull->clip_maxs[1] = 16;
1899 hull->clip_maxs[2] = 36;
1900 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1902 hull = &loadmodel->brushq1.hulls[2];
1903 hull->clipnodes = out;
1904 hull->firstclipnode = 0;
1905 hull->lastclipnode = count-1;
1906 hull->planes = loadmodel->brushq1.planes;
1907 hull->clip_mins[0] = -32;
1908 hull->clip_mins[1] = -32;
1909 hull->clip_mins[2] = -32;
1910 hull->clip_maxs[0] = 32;
1911 hull->clip_maxs[1] = 32;
1912 hull->clip_maxs[2] = 32;
1913 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1915 hull = &loadmodel->brushq1.hulls[3];
1916 hull->clipnodes = out;
1917 hull->firstclipnode = 0;
1918 hull->lastclipnode = count-1;
1919 hull->planes = loadmodel->brushq1.planes;
1920 hull->clip_mins[0] = -16;
1921 hull->clip_mins[1] = -16;
1922 hull->clip_mins[2] = -18;
1923 hull->clip_maxs[0] = 16;
1924 hull->clip_maxs[1] = 16;
1925 hull->clip_maxs[2] = 18;
1926 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1930 hull = &loadmodel->brushq1.hulls[1];
1931 hull->clipnodes = out;
1932 hull->firstclipnode = 0;
1933 hull->lastclipnode = count-1;
1934 hull->planes = loadmodel->brushq1.planes;
1935 hull->clip_mins[0] = -16;
1936 hull->clip_mins[1] = -16;
1937 hull->clip_mins[2] = -24;
1938 hull->clip_maxs[0] = 16;
1939 hull->clip_maxs[1] = 16;
1940 hull->clip_maxs[2] = 32;
1941 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1943 hull = &loadmodel->brushq1.hulls[2];
1944 hull->clipnodes = out;
1945 hull->firstclipnode = 0;
1946 hull->lastclipnode = count-1;
1947 hull->planes = loadmodel->brushq1.planes;
1948 hull->clip_mins[0] = -32;
1949 hull->clip_mins[1] = -32;
1950 hull->clip_mins[2] = -24;
1951 hull->clip_maxs[0] = 32;
1952 hull->clip_maxs[1] = 32;
1953 hull->clip_maxs[2] = 64;
1954 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1957 for (i=0 ; i<count ; i++, out++, in++)
1959 out->planenum = LittleLong(in->planenum);
1960 out->children[0] = LittleShort(in->children[0]);
1961 out->children[1] = LittleShort(in->children[1]);
1962 if (out->children[0] >= count || out->children[1] >= count)
1963 Host_Error("Corrupt clipping hull(out of range child)\n");
1967 //Duplicate the drawing hull structure as a clipping hull
1968 static void Mod_Q1BSP_MakeHull0(void)
1975 hull = &loadmodel->brushq1.hulls[0];
1977 in = loadmodel->brushq1.nodes;
1978 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1980 hull->clipnodes = out;
1981 hull->firstclipnode = 0;
1982 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1983 hull->planes = loadmodel->brushq1.planes;
1985 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1987 out->planenum = in->plane - loadmodel->brushq1.planes;
1988 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1989 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1993 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1998 in = (void *)(mod_base + l->fileofs);
1999 if (l->filelen % sizeof(*in))
2000 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
2001 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
2002 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
2004 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
2006 j = (unsigned) LittleShort(in[i]);
2007 if (j >= loadmodel->brushq1.numsurfaces)
2008 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
2009 loadmodel->brushq1.marksurfaces[i] = j;
2013 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
2018 in = (void *)(mod_base + l->fileofs);
2019 if (l->filelen % sizeof(*in))
2020 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
2021 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
2022 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
2024 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
2025 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
2029 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
2035 in = (void *)(mod_base + l->fileofs);
2036 if (l->filelen % sizeof(*in))
2037 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
2039 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
2040 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
2042 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
2044 out->normal[0] = LittleFloat(in->normal[0]);
2045 out->normal[1] = LittleFloat(in->normal[1]);
2046 out->normal[2] = LittleFloat(in->normal[2]);
2047 out->dist = LittleFloat(in->dist);
2053 #define MAX_POINTS_ON_WINDING 64
2059 double points[8][3]; // variable sized
2068 static winding_t *NewWinding(int points)
2073 if (points > MAX_POINTS_ON_WINDING)
2074 Sys_Error("NewWinding: too many points\n");
2076 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2077 w = Mem_Alloc(loadmodel->mempool, size);
2083 static void FreeWinding(winding_t *w)
2093 static winding_t *BaseWindingForPlane(mplane_t *p)
2095 double org[3], vright[3], vup[3], normal[3];
2098 VectorCopy(p->normal, normal);
2099 VectorVectorsDouble(normal, vright, vup);
2101 VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2102 VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2104 // project a really big axis aligned box onto the plane
2107 VectorScale(p->normal, p->dist, org);
2109 VectorSubtract(org, vright, w->points[0]);
2110 VectorAdd(w->points[0], vup, w->points[0]);
2112 VectorAdd(org, vright, w->points[1]);
2113 VectorAdd(w->points[1], vup, w->points[1]);
2115 VectorAdd(org, vright, w->points[2]);
2116 VectorSubtract(w->points[2], vup, w->points[2]);
2118 VectorSubtract(org, vright, w->points[3]);
2119 VectorSubtract(w->points[3], vup, w->points[3]);
2130 Clips the winding to the plane, returning the new winding on the positive side
2131 Frees the input winding.
2132 If keepon is true, an exactly on-plane winding will be saved, otherwise
2133 it will be clipped away.
2136 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2138 double dists[MAX_POINTS_ON_WINDING + 1];
2139 int sides[MAX_POINTS_ON_WINDING + 1];
2148 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2150 // determine sides for each point
2151 for (i = 0;i < in->numpoints;i++)
2153 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2154 if (dot > ON_EPSILON)
2155 sides[i] = SIDE_FRONT;
2156 else if (dot < -ON_EPSILON)
2157 sides[i] = SIDE_BACK;
2162 sides[i] = sides[0];
2163 dists[i] = dists[0];
2165 if (keepon && !counts[0] && !counts[1])
2176 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2177 if (maxpts > MAX_POINTS_ON_WINDING)
2178 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2180 neww = NewWinding(maxpts);
2182 for (i = 0;i < in->numpoints;i++)
2184 if (neww->numpoints >= maxpts)
2185 Sys_Error("ClipWinding: points exceeded estimate");
2189 if (sides[i] == SIDE_ON)
2191 VectorCopy(p1, neww->points[neww->numpoints]);
2196 if (sides[i] == SIDE_FRONT)
2198 VectorCopy(p1, neww->points[neww->numpoints]);
2202 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2205 // generate a split point
2206 p2 = in->points[(i+1)%in->numpoints];
2208 dot = dists[i] / (dists[i]-dists[i+1]);
2209 for (j = 0;j < 3;j++)
2210 { // avoid round off error when possible
2211 if (split->normal[j] == 1)
2212 mid[j] = split->dist;
2213 else if (split->normal[j] == -1)
2214 mid[j] = -split->dist;
2216 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2219 VectorCopy(mid, neww->points[neww->numpoints]);
2223 // free the original winding
2234 Divides a winding by a plane, producing one or two windings. The
2235 original winding is not damaged or freed. If only on one side, the
2236 returned winding will be the input winding. If on both sides, two
2237 new windings will be created.
2240 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2242 double dists[MAX_POINTS_ON_WINDING + 1];
2243 int sides[MAX_POINTS_ON_WINDING + 1];
2252 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2254 // determine sides for each point
2255 for (i = 0;i < in->numpoints;i++)
2257 dot = DotProduct(in->points[i], split->normal);
2260 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2261 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2262 else sides[i] = SIDE_ON;
2265 sides[i] = sides[0];
2266 dists[i] = dists[0];
2268 *front = *back = NULL;
2281 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2283 if (maxpts > MAX_POINTS_ON_WINDING)
2284 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2286 *front = f = NewWinding(maxpts);
2287 *back = b = NewWinding(maxpts);
2289 for (i = 0;i < in->numpoints;i++)
2291 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2292 Sys_Error("DivideWinding: points exceeded estimate");
2296 if (sides[i] == SIDE_ON)
2298 VectorCopy(p1, f->points[f->numpoints]);
2300 VectorCopy(p1, b->points[b->numpoints]);
2305 if (sides[i] == SIDE_FRONT)
2307 VectorCopy(p1, f->points[f->numpoints]);
2310 else if (sides[i] == SIDE_BACK)
2312 VectorCopy(p1, b->points[b->numpoints]);
2316 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2319 // generate a split point
2320 p2 = in->points[(i+1)%in->numpoints];
2322 dot = dists[i] / (dists[i]-dists[i+1]);
2323 for (j = 0;j < 3;j++)
2324 { // avoid round off error when possible
2325 if (split->normal[j] == 1)
2326 mid[j] = split->dist;
2327 else if (split->normal[j] == -1)
2328 mid[j] = -split->dist;
2330 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2333 VectorCopy(mid, f->points[f->numpoints]);
2335 VectorCopy(mid, b->points[b->numpoints]);
2340 typedef struct portal_s
2343 mnode_t *nodes[2]; // [0] = front side of plane
2344 struct portal_s *next[2];
2346 struct portal_s *chain; // all portals are linked into a list
2350 static portal_t *portalchain;
2357 static portal_t *AllocPortal(void)
2360 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2361 p->chain = portalchain;
2366 static void FreePortal(portal_t *p)
2371 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2373 // calculate children first
2374 if (node->children[0]->contents >= 0)
2375 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2376 if (node->children[1]->contents >= 0)
2377 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2379 // make combined bounding box from children
2380 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2381 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2382 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2383 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2384 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2385 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2388 static void Mod_Q1BSP_FinalizePortals(void)
2390 int i, j, numportals, numpoints;
2391 portal_t *p, *pnext;
2394 mleaf_t *leaf, *endleaf;
2397 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2398 leaf = loadmodel->brushq1.leafs;
2399 endleaf = leaf + loadmodel->brushq1.numleafs;
2400 for (;leaf < endleaf;leaf++)
2402 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2403 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2410 for (i = 0;i < 2;i++)
2412 leaf = (mleaf_t *)p->nodes[i];
2414 for (j = 0;j < w->numpoints;j++)
2416 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2417 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2418 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2419 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2420 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2421 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2428 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2430 // tally up portal and point counts
2436 // note: this check must match the one below or it will usually corrupt memory
2437 // 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
2438 if (p->winding && p->nodes[0] != p->nodes[1]
2439 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2440 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2443 numpoints += p->winding->numpoints * 2;
2447 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2448 loadmodel->brushq1.numportals = numportals;
2449 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2450 loadmodel->brushq1.numportalpoints = numpoints;
2451 // clear all leaf portal chains
2452 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2453 loadmodel->brushq1.leafs[i].portals = NULL;
2454 // process all portals in the global portal chain, while freeing them
2455 portal = loadmodel->brushq1.portals;
2456 point = loadmodel->brushq1.portalpoints;
2465 // note: this check must match the one above or it will usually corrupt memory
2466 // 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
2467 if (p->nodes[0] != p->nodes[1]
2468 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2469 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2471 // first make the back to front portal(forward portal)
2472 portal->points = point;
2473 portal->numpoints = p->winding->numpoints;
2474 portal->plane.dist = p->plane.dist;
2475 VectorCopy(p->plane.normal, portal->plane.normal);
2476 portal->here = (mleaf_t *)p->nodes[1];
2477 portal->past = (mleaf_t *)p->nodes[0];
2479 for (j = 0;j < portal->numpoints;j++)
2481 VectorCopy(p->winding->points[j], point->position);
2484 PlaneClassify(&portal->plane);
2486 // link into leaf's portal chain
2487 portal->next = portal->here->portals;
2488 portal->here->portals = portal;
2490 // advance to next portal
2493 // then make the front to back portal(backward portal)
2494 portal->points = point;
2495 portal->numpoints = p->winding->numpoints;
2496 portal->plane.dist = -p->plane.dist;
2497 VectorNegate(p->plane.normal, portal->plane.normal);
2498 portal->here = (mleaf_t *)p->nodes[0];
2499 portal->past = (mleaf_t *)p->nodes[1];
2501 for (j = portal->numpoints - 1;j >= 0;j--)
2503 VectorCopy(p->winding->points[j], point->position);
2506 PlaneClassify(&portal->plane);
2508 // link into leaf's portal chain
2509 portal->next = portal->here->portals;
2510 portal->here->portals = portal;
2512 // advance to next portal
2515 FreeWinding(p->winding);
2527 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2530 Host_Error("AddPortalToNodes: NULL front node");
2532 Host_Error("AddPortalToNodes: NULL back node");
2533 if (p->nodes[0] || p->nodes[1])
2534 Host_Error("AddPortalToNodes: already included");
2535 // 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
2537 p->nodes[0] = front;
2538 p->next[0] = (portal_t *)front->portals;
2539 front->portals = (mportal_t *)p;
2542 p->next[1] = (portal_t *)back->portals;
2543 back->portals = (mportal_t *)p;
2548 RemovePortalFromNode
2551 static void RemovePortalFromNodes(portal_t *portal)
2555 void **portalpointer;
2557 for (i = 0;i < 2;i++)
2559 node = portal->nodes[i];
2561 portalpointer = (void **) &node->portals;
2566 Host_Error("RemovePortalFromNodes: portal not in leaf");
2570 if (portal->nodes[0] == node)
2572 *portalpointer = portal->next[0];
2573 portal->nodes[0] = NULL;
2575 else if (portal->nodes[1] == node)
2577 *portalpointer = portal->next[1];
2578 portal->nodes[1] = NULL;
2581 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2585 if (t->nodes[0] == node)
2586 portalpointer = (void **) &t->next[0];
2587 else if (t->nodes[1] == node)
2588 portalpointer = (void **) &t->next[1];
2590 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2595 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2598 mnode_t *front, *back, *other_node;
2599 mplane_t clipplane, *plane;
2600 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2601 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2603 // if a leaf, we're done
2607 plane = node->plane;
2609 front = node->children[0];
2610 back = node->children[1];
2612 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2614 // create the new portal by generating a polygon for the node plane,
2615 // and clipping it by all of the other portals(which came from nodes above this one)
2616 nodeportal = AllocPortal();
2617 nodeportal->plane = *node->plane;
2619 nodeportalwinding = BaseWindingForPlane(node->plane);
2620 side = 0; // shut up compiler warning
2621 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2623 clipplane = portal->plane;
2624 if (portal->nodes[0] == portal->nodes[1])
2625 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2626 if (portal->nodes[0] == node)
2628 else if (portal->nodes[1] == node)
2630 clipplane.dist = -clipplane.dist;
2631 VectorNegate(clipplane.normal, clipplane.normal);
2635 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2637 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2638 if (!nodeportalwinding)
2640 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2645 if (nodeportalwinding)
2647 // if the plane was not clipped on all sides, there was an error
2648 nodeportal->winding = nodeportalwinding;
2649 AddPortalToNodes(nodeportal, front, back);
2652 // split the portals of this node along this node's plane and assign them to the children of this node
2653 // (migrating the portals downward through the tree)
2654 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2656 if (portal->nodes[0] == portal->nodes[1])
2657 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2658 if (portal->nodes[0] == node)
2660 else if (portal->nodes[1] == node)
2663 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2664 nextportal = portal->next[side];
2666 other_node = portal->nodes[!side];
2667 RemovePortalFromNodes(portal);
2669 // cut the portal into two portals, one on each side of the node plane
2670 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2675 AddPortalToNodes(portal, back, other_node);
2677 AddPortalToNodes(portal, other_node, back);
2683 AddPortalToNodes(portal, front, other_node);
2685 AddPortalToNodes(portal, other_node, front);
2689 // the winding is split
2690 splitportal = AllocPortal();
2691 temp = splitportal->chain;
2692 *splitportal = *portal;
2693 splitportal->chain = temp;
2694 splitportal->winding = backwinding;
2695 FreeWinding(portal->winding);
2696 portal->winding = frontwinding;
2700 AddPortalToNodes(portal, front, other_node);
2701 AddPortalToNodes(splitportal, back, other_node);
2705 AddPortalToNodes(portal, other_node, front);
2706 AddPortalToNodes(splitportal, other_node, back);
2710 Mod_Q1BSP_RecursiveNodePortals(front);
2711 Mod_Q1BSP_RecursiveNodePortals(back);
2715 static void Mod_Q1BSP_MakePortals(void)
2718 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2719 Mod_Q1BSP_FinalizePortals();
2722 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2725 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2726 msurface_t *surf, *s;
2727 float *v0, *v1, *v2, *v3;
2728 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2729 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2730 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2732 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)
2734 if (surf->neighborsurfaces[vertnum])
2736 surf->neighborsurfaces[vertnum] = NULL;
2737 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2739 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2740 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2741 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2744 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2745 if (s->neighborsurfaces[vnum] == surf)
2747 if (vnum < s->poly_numverts)
2749 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)
2751 if (s->neighborsurfaces[vnum] == NULL
2752 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2753 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2755 surf->neighborsurfaces[vertnum] = s;
2756 s->neighborsurfaces[vnum] = surf;
2760 if (vnum < s->poly_numverts)
2768 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2770 int i, j, stylecounts[256], totalcount, remapstyles[256];
2772 memset(stylecounts, 0, sizeof(stylecounts));
2773 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2775 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2776 for (j = 0;j < MAXLIGHTMAPS;j++)
2777 stylecounts[surf->styles[j]]++;
2780 model->brushq1.light_styles = 0;
2781 for (i = 0;i < 255;i++)
2785 remapstyles[i] = model->brushq1.light_styles++;
2786 totalcount += stylecounts[i] + 1;
2791 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2792 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2793 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2794 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2795 model->brushq1.light_styles = 0;
2796 for (i = 0;i < 255;i++)
2798 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2800 for (i = 0;i < model->brushq1.light_styles;i++)
2802 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2803 j += stylecounts[model->brushq1.light_style[i]] + 1;
2805 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2807 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2808 for (j = 0;j < MAXLIGHTMAPS;j++)
2809 if (surf->styles[j] != 255)
2810 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2813 for (i = 0;i < model->brushq1.light_styles;i++)
2815 *model->brushq1.light_styleupdatechains[i] = NULL;
2816 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2817 j += stylecounts[model->brushq1.light_style[i]] + 1;
2821 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2824 for (i = 0;i < model->brushq1.numtextures;i++)
2825 model->brushq1.pvstexturechainslength[i] = 0;
2826 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2828 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2830 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2831 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2834 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2836 if (model->brushq1.pvstexturechainslength[i])
2838 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2839 j += model->brushq1.pvstexturechainslength[i] + 1;
2842 model->brushq1.pvstexturechains[i] = NULL;
2844 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2845 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2846 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2847 for (i = 0;i < model->brushq1.numtextures;i++)
2849 if (model->brushq1.pvstexturechainslength[i])
2851 *model->brushq1.pvstexturechains[i] = NULL;
2852 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2857 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2858 extern void R_Model_Brush_Draw(entity_render_t *ent);
2859 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2860 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);
2861 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2866 mempool_t *mainmempool;
2868 model_t *originalloadmodel;
2869 float dist, modelyawradius, modelradius, *vec;
2873 mod->type = mod_brush;
2875 header = (dheader_t *)buffer;
2877 i = LittleLong(header->version);
2878 if (i != BSPVERSION && i != 30)
2879 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2880 mod->brushq1.ishlbsp = i == 30;
2882 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2883 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2884 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2885 mod->brushq1.LeafPVS = Mod_Q1BSP_LeafPVS;
2886 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2888 if (loadmodel->isworldmodel)
2890 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
2891 // until we get a texture for it...
2895 // swap all the lumps
2896 mod_base = (qbyte *)header;
2898 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2899 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2903 // store which lightmap format to use
2904 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2906 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2907 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2908 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2909 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2910 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2911 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2912 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2913 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2914 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2915 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2916 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2917 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2918 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2919 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2920 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2922 Mod_Q1BSP_MakeHull0();
2923 Mod_Q1BSP_MakePortals();
2925 mod->numframes = 2; // regular and alternate animation
2927 mainmempool = mod->mempool;
2928 loadname = mod->name;
2930 Mod_Q1BSP_LoadLightList();
2931 originalloadmodel = loadmodel;
2934 // set up the submodels(FIXME: this is confusing)
2936 for (i = 0;i < mod->brushq1.numsubmodels;i++)
2938 bm = &mod->brushq1.submodels[i];
2940 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2941 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2943 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2944 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2947 mod->brushq1.firstmodelsurface = bm->firstface;
2948 mod->brushq1.nummodelsurfaces = bm->numfaces;
2950 // this gets altered below if sky is used
2951 mod->DrawSky = NULL;
2952 mod->Draw = R_Model_Brush_Draw;
2953 mod->DrawFakeShadow = NULL;
2954 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2955 mod->DrawLight = R_Model_Brush_DrawLight;
2956 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2957 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2958 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2959 Mod_Q1BSP_BuildPVSTextureChains(mod);
2960 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2961 if (mod->brushq1.nummodelsurfaces)
2963 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2964 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2965 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2968 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
2970 // we only need to have a drawsky function if it is used(usually only on world model)
2971 if (surf->texinfo->texture->shader == &Cshader_sky)
2972 mod->DrawSky = R_Model_Brush_DrawSky;
2973 // LordHavoc: submodels always clip, even if water
2974 if (mod->brushq1.numsubmodels - 1)
2975 surf->flags |= SURF_SOLIDCLIP;
2976 // calculate bounding shapes
2977 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
2979 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
2981 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
2982 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
2983 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
2984 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
2985 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
2986 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
2987 dist = vec[0]*vec[0]+vec[1]*vec[1];
2988 if (modelyawradius < dist)
2989 modelyawradius = dist;
2990 dist += vec[2]*vec[2];
2991 if (modelradius < dist)
2996 modelyawradius = sqrt(modelyawradius);
2997 modelradius = sqrt(modelradius);
2998 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
2999 mod->yawmins[2] = mod->normalmins[2];
3000 mod->yawmaxs[2] = mod->normalmaxs[2];
3001 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3002 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3003 mod->radius = modelradius;
3004 mod->radius2 = modelradius * modelradius;
3008 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3009 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3011 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3013 mod->brushq1.numleafs = bm->visleafs;
3015 // LordHavoc: only register submodels if it is the world
3016 // (prevents bsp models from replacing world submodels)
3017 if (loadmodel->isworldmodel && i < (mod->brushq1.numsubmodels - 1))
3020 // duplicate the basic information
3021 sprintf(name, "*%i", i+1);
3022 loadmodel = Mod_FindName(name);
3024 strcpy(loadmodel->name, name);
3025 // textures and memory belong to the main model
3026 loadmodel->texturepool = NULL;
3027 loadmodel->mempool = NULL;
3032 loadmodel = originalloadmodel;
3033 //Mod_Q1BSP_ProcessLightList();
3036 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3040 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3047 in = (void *)(mod_base + l->fileofs);
3048 if (l->filelen % sizeof(*in))
3049 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3050 count = l->filelen / sizeof(*in);
3051 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3054 loadmodel->num = count;
3056 for (i = 0;i < count;i++, in++, out++)
3062 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3069 in = (void *)(mod_base + l->fileofs);
3070 if (l->filelen % sizeof(*in))
3071 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3072 count = l->filelen / sizeof(*in);
3073 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3076 loadmodel->num = count;
3078 for (i = 0;i < count;i++, in++, out++)
3084 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3091 in = (void *)(mod_base + l->fileofs);
3092 if (l->filelen % sizeof(*in))
3093 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3094 count = l->filelen / sizeof(*in);
3095 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3098 loadmodel->num = count;
3100 for (i = 0;i < count;i++, in++, out++)
3106 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3113 in = (void *)(mod_base + l->fileofs);
3114 if (l->filelen % sizeof(*in))
3115 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3116 count = l->filelen / sizeof(*in);
3117 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3120 loadmodel->num = count;
3122 for (i = 0;i < count;i++, in++, out++)
3128 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3135 in = (void *)(mod_base + l->fileofs);
3136 if (l->filelen % sizeof(*in))
3137 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3138 count = l->filelen / sizeof(*in);
3139 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3142 loadmodel->num = count;
3144 for (i = 0;i < count;i++, in++, out++)
3150 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3157 in = (void *)(mod_base + l->fileofs);
3158 if (l->filelen % sizeof(*in))
3159 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3160 count = l->filelen / sizeof(*in);
3161 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3164 loadmodel->num = count;
3166 for (i = 0;i < count;i++, in++, out++)
3172 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3179 in = (void *)(mod_base + l->fileofs);
3180 if (l->filelen % sizeof(*in))
3181 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3182 count = l->filelen / sizeof(*in);
3183 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3186 loadmodel->num = count;
3188 for (i = 0;i < count;i++, in++, out++)
3194 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3201 in = (void *)(mod_base + l->fileofs);
3202 if (l->filelen % sizeof(*in))
3203 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3204 count = l->filelen / sizeof(*in);
3205 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3208 loadmodel->num = count;
3210 for (i = 0;i < count;i++, in++, out++)
3216 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3223 in = (void *)(mod_base + l->fileofs);
3224 if (l->filelen % sizeof(*in))
3225 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3226 count = l->filelen / sizeof(*in);
3227 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3230 loadmodel->num = count;
3232 for (i = 0;i < count;i++, in++, out++)
3238 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3245 in = (void *)(mod_base + l->fileofs);
3246 if (l->filelen % sizeof(*in))
3247 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3248 count = l->filelen / sizeof(*in);
3249 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3252 loadmodel->num = count;
3254 for (i = 0;i < count;i++, in++, out++)
3260 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3267 in = (void *)(mod_base + l->fileofs);
3268 if (l->filelen % sizeof(*in))
3269 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3270 count = l->filelen / sizeof(*in);
3271 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3274 loadmodel->num = count;
3276 for (i = 0;i < count;i++, in++, out++)
3282 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3289 in = (void *)(mod_base + l->fileofs);
3290 if (l->filelen % sizeof(*in))
3291 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3292 count = l->filelen / sizeof(*in);
3293 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3296 loadmodel->num = count;
3298 for (i = 0;i < count;i++, in++, out++)
3304 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3311 in = (void *)(mod_base + l->fileofs);
3312 if (l->filelen % sizeof(*in))
3313 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3314 count = l->filelen / sizeof(*in);
3315 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3318 loadmodel->num = count;
3320 for (i = 0;i < count;i++, in++, out++)
3326 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3333 in = (void *)(mod_base + l->fileofs);
3334 if (l->filelen % sizeof(*in))
3335 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3336 count = l->filelen / sizeof(*in);
3337 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3340 loadmodel->num = count;
3342 for (i = 0;i < count;i++, in++, out++)
3348 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3355 in = (void *)(mod_base + l->fileofs);
3356 if (l->filelen % sizeof(*in))
3357 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3358 count = l->filelen / sizeof(*in);
3359 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3362 loadmodel->num = count;
3364 for (i = 0;i < count;i++, in++, out++)
3370 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3377 in = (void *)(mod_base + l->fileofs);
3378 if (l->filelen % sizeof(*in))
3379 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3380 count = l->filelen / sizeof(*in);
3381 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3384 loadmodel->num = count;
3386 for (i = 0;i < count;i++, in++, out++)
3392 static void Mod_Q2BSP_LoadModels(lump_t *l)
3399 in = (void *)(mod_base + l->fileofs);
3400 if (l->filelen % sizeof(*in))
3401 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3402 count = l->filelen / sizeof(*in);
3403 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3406 loadmodel->num = count;
3408 for (i = 0;i < count;i++, in++, out++)
3414 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3417 q2dheader_t *header;
3419 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3421 mod->type = mod_brushq2;
3423 header = (q2dheader_t *)buffer;
3425 i = LittleLong(header->version);
3426 if (i != Q2BSPVERSION)
3427 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3428 mod->brushq1.ishlbsp = false;
3429 if (loadmodel->isworldmodel)
3431 Cvar_SetValue("halflifebsp", mod->brushq1.ishlbsp);
3432 // until we get a texture for it...
3436 mod_base = (qbyte *)header;
3438 // swap all the lumps
3439 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3440 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3442 // store which lightmap format to use
3443 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3445 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3446 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3447 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3448 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3449 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3450 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3451 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3452 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3453 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3454 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3455 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3456 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3457 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3458 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3459 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3460 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3461 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3462 // LordHavoc: must go last because this makes the submodels
3463 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3467 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3470 char key[128], value[4096];
3472 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3473 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3474 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3477 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3478 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3479 data = loadmodel->brush.entities;
3480 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3481 if (data && COM_ParseToken(&data) && com_token[0] == '{')
3485 if (!COM_ParseToken(&data))
3487 if (com_token[0] == '}')
3488 break; // end of worldspawn
3489 if (com_token[0] == '_')
3490 strcpy(key, com_token + 1);
3492 strcpy(key, com_token);
3493 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3494 key[strlen(key)-1] = 0;
3495 if (!COM_ParseToken(&data))
3497 strcpy(value, com_token);
3498 if (!strcmp("gridsize", key))
3500 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3501 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3507 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3513 in = (void *)(mod_base + l->fileofs);
3514 if (l->filelen % sizeof(*in))
3515 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3516 count = l->filelen / sizeof(*in);
3517 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3519 loadmodel->brushq3.data_textures = out;
3520 loadmodel->brushq3.num_textures = count;
3522 for (i = 0;i < count;i++, in++, out++)
3524 strncpy(out->name, in->name, sizeof(out->name) - 1);
3525 out->surfaceflags = LittleLong(in->surfaceflags);
3526 out->contents = LittleLong(in->contents);
3529 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3533 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3539 in = (void *)(mod_base + l->fileofs);
3540 if (l->filelen % sizeof(*in))
3541 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3542 count = l->filelen / sizeof(*in);
3543 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3545 loadmodel->brushq3.data_planes = out;
3546 loadmodel->brushq3.num_planes = count;
3548 for (i = 0;i < count;i++, in++, out++)
3550 out->normal[0] = LittleLong(in->normal[0]);
3551 out->normal[1] = LittleLong(in->normal[1]);
3552 out->normal[2] = LittleLong(in->normal[2]);
3553 out->dist = LittleLong(in->dist);
3558 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3561 q3mbrushside_t *out;
3564 in = (void *)(mod_base + l->fileofs);
3565 if (l->filelen % sizeof(*in))
3566 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3567 count = l->filelen / sizeof(*in);
3568 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3570 loadmodel->brushq3.data_brushsides = out;
3571 loadmodel->brushq3.num_brushsides = count;
3573 for (i = 0;i < count;i++, in++, out++)
3575 n = LittleLong(in->planeindex);
3576 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3577 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3578 out->plane = loadmodel->brushq3.data_planes + n;
3579 n = LittleLong(in->textureindex);
3580 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3581 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3582 out->texture = loadmodel->brushq3.data_textures + n;
3586 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3592 in = (void *)(mod_base + l->fileofs);
3593 if (l->filelen % sizeof(*in))
3594 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3595 count = l->filelen / sizeof(*in);
3596 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3598 loadmodel->brushq3.data_brushes = out;
3599 loadmodel->brushq3.num_brushes = count;
3601 for (i = 0;i < count;i++, in++, out++)
3603 n = LittleLong(in->firstbrushside);
3604 c = LittleLong(in->numbrushsides);
3605 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3606 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3607 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3608 out->numbrushsides = c;
3609 n = LittleLong(in->textureindex);
3610 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3611 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3612 out->texture = loadmodel->brushq3.data_textures + n;
3616 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3622 in = (void *)(mod_base + l->fileofs);
3623 if (l->filelen % sizeof(*in))
3624 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3625 count = l->filelen / sizeof(*in);
3626 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3628 loadmodel->brushq3.data_effects = out;
3629 loadmodel->brushq3.num_effects = count;
3631 for (i = 0;i < count;i++, in++, out++)
3633 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3634 n = LittleLong(in->brushindex);
3635 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3636 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3637 out->brush = loadmodel->brushq3.data_brushes + n;
3638 out->unknown = LittleLong(in->unknown);
3642 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3647 in = (void *)(mod_base + l->fileofs);
3648 if (l->filelen % sizeof(*in))
3649 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3650 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3651 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3652 loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3653 loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3654 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3655 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3656 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3657 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3659 for (i = 0;i < count;i++, in++)
3661 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3662 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3663 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3664 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3665 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3666 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3667 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3668 // svector/tvector are calculated later in face loading
3669 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3670 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3671 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3672 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3673 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3674 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3675 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3676 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3677 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3678 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3679 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3680 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3681 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3685 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3691 in = (void *)(mod_base + l->fileofs);
3692 if (l->filelen % sizeof(int[3]))
3693 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3694 count = l->filelen / sizeof(*in);
3695 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3697 loadmodel->brushq3.num_triangles = count / 3;
3698 loadmodel->brushq3.data_element3i = out;
3699 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3701 for (i = 0;i < count;i++, in++, out++)
3703 *out = LittleLong(*in);
3704 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3705 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
3709 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3715 in = (void *)(mod_base + l->fileofs);
3716 if (l->filelen % sizeof(*in))
3717 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3718 count = l->filelen / sizeof(*in);
3719 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3721 loadmodel->brushq3.data_lightmaps = out;
3722 loadmodel->brushq3.num_lightmaps = count;
3724 for (i = 0;i < count;i++, in++, out++)
3725 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3728 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3732 int i, j, n, count, invalidelements, patchsize[2];
3734 in = (void *)(mod_base + l->fileofs);
3735 if (l->filelen % sizeof(*in))
3736 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3737 count = l->filelen / sizeof(*in);
3738 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3740 loadmodel->brushq3.data_faces = out;
3741 loadmodel->brushq3.num_faces = count;
3743 for (i = 0;i < count;i++, in++, out++)
3745 // check face type first
3746 out->type = LittleLong(in->type);
3747 if (out->type != Q3FACETYPE_POLYGON
3748 && out->type != Q3FACETYPE_PATCH
3749 && out->type != Q3FACETYPE_MESH
3750 && out->type != Q3FACETYPE_FLARE)
3752 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3753 out->type = 0; // error
3757 n = LittleLong(in->textureindex);
3758 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3760 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3761 out->type = 0; // error
3765 out->texture = loadmodel->brushq3.data_textures + n;
3766 n = LittleLong(in->effectindex);
3767 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3769 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3775 out->effect = loadmodel->brushq3.data_effects + n;
3776 n = LittleLong(in->lightmapindex);
3777 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3779 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3783 out->lightmaptexture = NULL;
3785 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3787 out->firstvertex = LittleLong(in->firstvertex);
3788 out->numvertices = LittleLong(in->numvertices);
3789 out->firstelement = LittleLong(in->firstelement);
3790 out->numelements = LittleLong(in->numelements);
3791 out->numtriangles = out->numelements / 3;
3792 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3794 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);
3795 out->type = 0; // error
3798 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3800 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);
3801 out->type = 0; // error
3804 if (out->numtriangles * 3 != out->numelements)
3806 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3807 out->type = 0; // error
3812 case Q3FACETYPE_POLYGON:
3813 case Q3FACETYPE_MESH:
3814 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3815 out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
3816 out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
3817 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3818 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3819 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3820 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3821 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3822 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3824 case Q3FACETYPE_PATCH:
3825 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3826 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3827 if (patchsize[0] < 1 || patchsize[1] < 1)
3829 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3830 out->type = 0; // error
3833 // FIXME: convert patch to triangles here!
3834 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
3838 case Q3FACETYPE_FLARE:
3839 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3844 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3845 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3847 if (invalidelements)
3849 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);
3850 for (j = 0;j < out->numelements;j++)
3852 Con_Printf(" %i", out->data_element3i[j]);
3853 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3854 out->data_element3i[j] = 0;
3861 static void Mod_Q3BSP_LoadModels(lump_t *l)
3865 int i, j, n, c, count;
3867 in = (void *)(mod_base + l->fileofs);
3868 if (l->filelen % sizeof(*in))
3869 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3870 count = l->filelen / sizeof(*in);
3871 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3873 loadmodel->brushq3.data_models = out;
3874 loadmodel->brushq3.num_models = count;
3876 for (i = 0;i < count;i++, in++, out++)
3878 for (j = 0;j < 3;j++)
3880 out->mins[j] = LittleFloat(in->mins[j]);
3881 out->maxs[j] = LittleFloat(in->maxs[j]);
3883 n = LittleLong(in->firstface);
3884 c = LittleLong(in->numfaces);
3885 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3886 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3887 out->firstface = loadmodel->brushq3.data_faces + n;
3889 n = LittleLong(in->firstbrush);
3890 c = LittleLong(in->numbrushes);
3891 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3892 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3893 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3894 out->numbrushes = c;
3898 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
3904 in = (void *)(mod_base + l->fileofs);
3905 if (l->filelen % sizeof(*in))
3906 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3907 count = l->filelen / sizeof(*in);
3908 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3910 loadmodel->brushq3.data_leafbrushes = out;
3911 loadmodel->brushq3.num_leafbrushes = count;
3913 for (i = 0;i < count;i++, in++, out++)
3915 n = LittleLong(*in);
3916 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3917 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3918 *out = loadmodel->brushq3.data_brushes + n;
3922 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
3928 in = (void *)(mod_base + l->fileofs);
3929 if (l->filelen % sizeof(*in))
3930 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3931 count = l->filelen / sizeof(*in);
3932 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3934 loadmodel->brushq3.data_leaffaces = out;
3935 loadmodel->brushq3.num_leaffaces = count;
3937 for (i = 0;i < count;i++, in++, out++)
3939 n = LittleLong(*in);
3940 if (n < 0 || n >= loadmodel->brushq3.num_faces)
3941 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
3942 *out = loadmodel->brushq3.data_faces + n;
3946 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
3950 int i, j, n, c, count;
3952 in = (void *)(mod_base + l->fileofs);
3953 if (l->filelen % sizeof(*in))
3954 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3955 count = l->filelen / sizeof(*in);
3956 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3958 loadmodel->brushq3.data_leafs = out;
3959 loadmodel->brushq3.num_leafs = count;
3961 for (i = 0;i < count;i++, in++, out++)
3963 out->isnode = false;
3965 out->clusterindex = LittleLong(in->clusterindex);
3966 out->areaindex = LittleLong(in->areaindex);
3967 for (j = 0;j < 3;j++)
3969 // yes the mins/maxs are ints
3970 out->mins[j] = LittleLong(in->mins[j]);
3971 out->maxs[j] = LittleLong(in->maxs[j]);
3973 n = LittleLong(in->firstleafface);
3974 c = LittleLong(in->numleaffaces);
3975 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
3976 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
3977 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
3978 out->numleaffaces = c;
3979 n = LittleLong(in->firstleafbrush);
3980 c = LittleLong(in->numleafbrushes);
3981 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
3982 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
3983 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
3984 out->numleafbrushes = c;
3988 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
3991 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
3992 node->parent = parent;
3995 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
3996 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4000 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4006 in = (void *)(mod_base + l->fileofs);
4007 if (l->filelen % sizeof(*in))
4008 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4009 count = l->filelen / sizeof(*in);
4010 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4012 loadmodel->brushq3.data_nodes = out;
4013 loadmodel->brushq3.num_nodes = count;
4015 for (i = 0;i < count;i++, in++, out++)
4019 n = LittleLong(in->planeindex);
4020 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4021 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4022 out->plane = loadmodel->brushq3.data_planes + n;
4023 for (j = 0;j < 2;j++)
4025 n = LittleLong(in->childrenindex[j]);
4028 if (n >= loadmodel->brushq3.num_nodes)
4029 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4030 out->children[j] = loadmodel->brushq3.data_nodes + n;
4035 if (n >= loadmodel->brushq3.num_leafs)
4036 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4037 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4040 // we don't load the mins/maxs
4043 // set the parent pointers
4044 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4047 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4050 q3dlightgrid_t *out;
4053 in = (void *)(mod_base + l->fileofs);
4054 if (l->filelen % sizeof(*in))
4055 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4056 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4057 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4058 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4059 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4060 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4061 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4062 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4063 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4064 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4065 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4066 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4067 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4068 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4069 if (l->filelen < count * (int)sizeof(*in))
4070 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]);
4071 if (l->filelen != count * (int)sizeof(*in))
4072 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4074 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4075 loadmodel->brushq3.data_lightgrid = out;
4076 loadmodel->brushq3.num_lightgrid = count;
4078 // no swapping or validation necessary
4079 memcpy(out, in, count * (int)sizeof(*out));
4081 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]);
4082 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]);
4085 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4090 in = (void *)(mod_base + l->fileofs);
4092 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4094 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4095 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4096 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4097 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4098 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4099 if (l->filelen < totalchains + (int)sizeof(*in))
4100 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);
4102 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4103 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4106 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4109 q3dheader_t *header;
4111 mod->type = mod_brushq2;
4113 header = (q3dheader_t *)buffer;
4115 i = LittleLong(header->version);
4116 if (i != Q3BSPVERSION)
4117 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4118 if (loadmodel->isworldmodel)
4120 Cvar_SetValue("halflifebsp", false);
4121 // until we get a texture for it...
4125 mod_base = (qbyte *)header;
4127 // swap all the lumps
4128 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4129 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4131 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4132 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4133 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4134 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4135 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4136 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4137 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4138 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4139 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4140 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4141 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4142 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4143 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4144 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4145 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4146 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4147 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4150 void Mod_IBSP_Load(model_t *mod, void *buffer)
4152 int i = LittleLong(((int *)buffer)[1]);
4153 if (i == Q3BSPVERSION)
4154 Mod_Q3BSP_Load(mod,buffer);
4155 else if (i == Q2BSPVERSION)
4156 Mod_Q2BSP_Load(mod,buffer);
4158 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4161 void Mod_MAP_Load(model_t *mod, void *buffer)
4163 Host_Error("Mod_MAP_Load: not yet implemented\n");