2 Copyright (C) 1996-1997 Id Software, Inc.
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 // note: model_shared.c sets up r_notexture, and r_surf_notexture
27 qbyte mod_q1bsp_novis[(MAX_MAP_LEAFS + 7)/ 8];
29 //cvar_t r_subdivide_size = {CVAR_SAVE, "r_subdivide_size", "128"};
30 cvar_t halflifebsp = {0, "halflifebsp", "0"};
31 cvar_t r_novis = {0, "r_novis", "0"};
32 cvar_t r_miplightmaps = {CVAR_SAVE, "r_miplightmaps", "0"};
33 cvar_t r_lightmaprgba = {0, "r_lightmaprgba", "1"};
34 cvar_t r_nosurftextures = {0, "r_nosurftextures", "0"};
35 cvar_t r_sortsurfaces = {0, "r_sortsurfaces", "0"};
37 void Mod_BrushInit(void)
39 // Cvar_RegisterVariable(&r_subdivide_size);
40 Cvar_RegisterVariable(&halflifebsp);
41 Cvar_RegisterVariable(&r_novis);
42 Cvar_RegisterVariable(&r_miplightmaps);
43 Cvar_RegisterVariable(&r_lightmaprgba);
44 Cvar_RegisterVariable(&r_nosurftextures);
45 Cvar_RegisterVariable(&r_sortsurfaces);
46 memset(mod_q1bsp_novis, 0xff, sizeof(mod_q1bsp_novis));
49 static mleaf_t *Mod_Q1BSP_PointInLeaf(model_t *model, const vec3_t p)
56 Mod_CheckLoaded(model);
58 // LordHavoc: modified to start at first clip node,
59 // in other words: first node of the (sub)model
60 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
61 while (node->contents == 0)
62 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
64 return (mleaf_t *)node;
67 static void Mod_Q1BSP_AmbientSoundLevelsForPoint(model_t *model, const vec3_t p, qbyte *out, int outsize)
71 leaf = Mod_Q1BSP_PointInLeaf(model, p);
74 i = min(outsize, (int)sizeof(leaf->ambient_sound_level));;
77 memcpy(out, leaf->ambient_sound_level, i);
83 memset(out, 0, outsize);
87 static int Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
91 if (node->contents < 0)
94 if (node->contents == CONTENTS_SOLID)
96 leafnum = (mleaf_t *)node - model->brushq1.leafs - 1;
97 return pvs[leafnum >> 3] & (1 << (leafnum & 7));
100 // node - recurse down the BSP tree
101 switch (BoxOnPlaneSide(mins, maxs, node->plane))
104 node = node->children[0];
107 node = node->children[1];
110 if (node->children[0]->contents != CONTENTS_SOLID)
111 if (Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
113 node = node->children[1];
120 int Mod_Q1BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
122 return Mod_Q1BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode, pvs, mins, maxs);
126 static int Mod_Q1BSP_PointContents(model_t *model, const vec3_t p)
131 return CONTENTS_EMPTY;
133 Mod_CheckLoaded(model);
135 // LordHavoc: modified to start at first clip node,
136 // in other words: first node of the (sub)model
137 node = model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode;
138 while (node->contents == 0)
139 node = node->children[(node->plane->type < 3 ? p[node->plane->type] : DotProduct(p,node->plane->normal)) < node->plane->dist];
141 return ((mleaf_t *)node)->contents;
145 typedef struct findnonsolidlocationinfo_s
153 findnonsolidlocationinfo_t;
156 extern cvar_t samelevel;
158 static void Mod_Q1BSP_FindNonSolidLocation_r_Leaf(findnonsolidlocationinfo_t *info, mleaf_t *leaf)
160 int i, surfnum, k, *tri, *mark;
161 float dist, f, vert[3][3], edge[3][3], facenormal[3], edgenormal[3][3], point[3];
167 for (surfnum = 0, mark = leaf->firstmarksurface;surfnum < leaf->nummarksurfaces;surfnum++, mark++)
169 surf = info->model->brushq1.surfaces + *mark;
170 if (surf->flags & SURF_SOLIDCLIP)
173 VectorCopy(surf->plane->normal, surfnormal);
174 if (surf->flags & SURF_PLANEBACK)
175 VectorNegate(surfnormal, surfnormal);
177 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
179 for (k = 0;k < mesh->numtriangles;k++)
181 tri = mesh->element3i + k * 3;
182 VectorCopy((mesh->vertex3f + tri[0] * 3), vert[0]);
183 VectorCopy((mesh->vertex3f + tri[1] * 3), vert[1]);
184 VectorCopy((mesh->vertex3f + tri[2] * 3), vert[2]);
185 VectorSubtract(vert[1], vert[0], edge[0]);
186 VectorSubtract(vert[2], vert[1], edge[1]);
187 CrossProduct(edge[1], edge[0], facenormal);
188 if (facenormal[0] || facenormal[1] || facenormal[2])
190 VectorNormalize(facenormal);
192 if (VectorDistance(facenormal, surfnormal) > 0.01f)
193 Con_Printf("a2! %f %f %f != %f %f %f\n", facenormal[0], facenormal[1], facenormal[2], surfnormal[0], surfnormal[1], surfnormal[2]);
195 f = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
196 if (f <= info->bestdist && f >= -info->bestdist)
198 VectorSubtract(vert[0], vert[2], edge[2]);
199 VectorNormalize(edge[0]);
200 VectorNormalize(edge[1]);
201 VectorNormalize(edge[2]);
202 CrossProduct(facenormal, edge[0], edgenormal[0]);
203 CrossProduct(facenormal, edge[1], edgenormal[1]);
204 CrossProduct(facenormal, edge[2], edgenormal[2]);
206 if (samelevel.integer & 1)
207 VectorNegate(edgenormal[0], edgenormal[0]);
208 if (samelevel.integer & 2)
209 VectorNegate(edgenormal[1], edgenormal[1]);
210 if (samelevel.integer & 4)
211 VectorNegate(edgenormal[2], edgenormal[2]);
212 for (i = 0;i < 3;i++)
213 if (DotProduct(vert[0], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
214 || DotProduct(vert[1], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f
215 || DotProduct(vert[2], edgenormal[i]) > DotProduct(vert[i], edgenormal[i]) + 0.1f)
216 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]);
219 if (DotProduct(info->center, edgenormal[0]) < DotProduct(vert[0], edgenormal[0])
220 && DotProduct(info->center, edgenormal[1]) < DotProduct(vert[1], edgenormal[1])
221 && DotProduct(info->center, edgenormal[2]) < DotProduct(vert[2], edgenormal[2]))
223 // we got lucky, the center is within the face
224 dist = DotProduct(info->center, facenormal) - DotProduct(vert[0], facenormal);
228 if (info->bestdist > dist)
230 info->bestdist = dist;
231 VectorScale(facenormal, (info->radius - -dist), info->nudge);
236 if (info->bestdist > dist)
238 info->bestdist = dist;
239 VectorScale(facenormal, (info->radius - dist), info->nudge);
245 // check which edge or vertex the center is nearest
246 for (i = 0;i < 3;i++)
248 f = DotProduct(info->center, edge[i]);
249 if (f >= DotProduct(vert[0], edge[i])
250 && f <= DotProduct(vert[1], edge[i]))
253 VectorMA(info->center, -f, edge[i], point);
254 dist = sqrt(DotProduct(point, point));
255 if (info->bestdist > dist)
257 info->bestdist = dist;
258 VectorScale(point, (info->radius / dist), info->nudge);
260 // skip both vertex checks
261 // (both are further away than this edge)
266 // not on edge, check first vertex of edge
267 VectorSubtract(info->center, vert[i], point);
268 dist = sqrt(DotProduct(point, point));
269 if (info->bestdist > dist)
271 info->bestdist = dist;
272 VectorScale(point, (info->radius / dist), info->nudge);
285 static void Mod_Q1BSP_FindNonSolidLocation_r(findnonsolidlocationinfo_t *info, mnode_t *node)
289 if (((mleaf_t *)node)->nummarksurfaces)
290 Mod_Q1BSP_FindNonSolidLocation_r_Leaf(info, (mleaf_t *)node);
294 float f = PlaneDiff(info->center, node->plane);
295 if (f >= -info->bestdist)
296 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[0]);
297 if (f <= info->bestdist)
298 Mod_Q1BSP_FindNonSolidLocation_r(info, node->children[1]);
302 static void Mod_Q1BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, float radius)
305 findnonsolidlocationinfo_t info;
311 VectorCopy(in, info.center);
312 info.radius = radius;
317 VectorClear(info.nudge);
318 info.bestdist = radius;
319 Mod_Q1BSP_FindNonSolidLocation_r(&info, model->brushq1.nodes + model->brushq1.hulls[0].firstclipnode);
320 VectorAdd(info.center, info.nudge, info.center);
322 while (info.bestdist < radius && ++i < 10);
323 VectorCopy(info.center, out);
328 // the hull we're tracing through
331 // the trace structure to fill in
334 // start, end, and end - start (in model space)
339 RecursiveHullCheckTraceInfo_t;
341 // 1/32 epsilon to keep floating point happy
342 #define DIST_EPSILON (0.03125)
344 #define HULLCHECKSTATE_EMPTY 0
345 #define HULLCHECKSTATE_SOLID 1
346 #define HULLCHECKSTATE_DONE 2
348 static int Mod_Q1BSP_RecursiveHullCheck(RecursiveHullCheckTraceInfo_t *t, int num, double p1f, double p2f, double p1[3], double p2[3])
350 // status variables, these don't need to be saved on the stack when
351 // recursing... but are because this should be thread-safe
352 // (note: tracing against a bbox is not thread-safe, yet)
357 // variables that need to be stored on the stack when recursing
362 // LordHavoc: a goto! everyone flee in terror... :)
367 t->trace->endcontents = num;
368 if (t->trace->thiscontents)
370 if (num == t->trace->thiscontents)
371 t->trace->allsolid = false;
374 // if the first leaf is solid, set startsolid
375 if (t->trace->allsolid)
376 t->trace->startsolid = true;
377 return HULLCHECKSTATE_SOLID;
379 return HULLCHECKSTATE_EMPTY;
383 if (num != CONTENTS_SOLID)
385 t->trace->allsolid = false;
386 if (num == CONTENTS_EMPTY)
387 t->trace->inopen = true;
389 t->trace->inwater = true;
393 // if the first leaf is solid, set startsolid
394 if (t->trace->allsolid)
395 t->trace->startsolid = true;
396 return HULLCHECKSTATE_SOLID;
398 return HULLCHECKSTATE_EMPTY;
402 // find the point distances
403 node = t->hull->clipnodes + num;
405 plane = t->hull->planes + node->planenum;
408 t1 = p1[plane->type] - plane->dist;
409 t2 = p2[plane->type] - plane->dist;
413 t1 = DotProduct (plane->normal, p1) - plane->dist;
414 t2 = DotProduct (plane->normal, p2) - plane->dist;
421 num = node->children[1];
430 num = node->children[0];
436 // the line intersects, find intersection point
437 // LordHavoc: this uses the original trace for maximum accuracy
440 t1 = t->start[plane->type] - plane->dist;
441 t2 = t->end[plane->type] - plane->dist;
445 t1 = DotProduct (plane->normal, t->start) - plane->dist;
446 t2 = DotProduct (plane->normal, t->end) - plane->dist;
449 midf = t1 / (t1 - t2);
450 midf = bound(p1f, midf, p2f);
451 VectorMA(t->start, midf, t->dist, mid);
453 // recurse both sides, front side first
454 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side], p1f, midf, p1, mid);
455 // if this side is not empty, return what it is (solid or done)
456 if (ret != HULLCHECKSTATE_EMPTY)
459 ret = Mod_Q1BSP_RecursiveHullCheck(t, node->children[side ^ 1], midf, p2f, mid, p2);
460 // if other side is not solid, return what it is (empty or done)
461 if (ret != HULLCHECKSTATE_SOLID)
464 // front is air and back is solid, this is the impact point...
467 t->trace->plane.dist = -plane->dist;
468 VectorNegate (plane->normal, t->trace->plane.normal);
472 t->trace->plane.dist = plane->dist;
473 VectorCopy (plane->normal, t->trace->plane.normal);
476 // bias away from surface a bit
477 t1 = DotProduct(t->trace->plane.normal, t->start) - (t->trace->plane.dist + DIST_EPSILON);
478 t2 = DotProduct(t->trace->plane.normal, t->end) - (t->trace->plane.dist + DIST_EPSILON);
480 midf = t1 / (t1 - t2);
481 t->trace->fraction = bound(0.0f, midf, 1.0);
483 return HULLCHECKSTATE_DONE;
486 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)
488 // this function currently only supports same size start and end
490 RecursiveHullCheckTraceInfo_t rhc;
492 memset(&rhc, 0, sizeof(rhc));
493 memset(trace, 0, sizeof(trace_t));
495 rhc.trace->fraction = 1;
496 rhc.trace->allsolid = true;
497 VectorSubtract(boxstartmaxs, boxstartmins, boxsize);
499 rhc.hull = &model->brushq1.hulls[0]; // 0x0x0
500 else if (model->brush.ishlbsp)
502 if (boxsize[0] <= 32)
504 if (boxsize[2] < 54) // pick the nearest of 36 or 72
505 rhc.hull = &model->brushq1.hulls[3]; // 32x32x36
507 rhc.hull = &model->brushq1.hulls[1]; // 32x32x72
510 rhc.hull = &model->brushq1.hulls[2]; // 64x64x64
514 if (boxsize[0] <= 32)
515 rhc.hull = &model->brushq1.hulls[1]; // 32x32x56
517 rhc.hull = &model->brushq1.hulls[2]; // 64x64x88
519 VectorSubtract(boxstartmins, rhc.hull->clip_mins, rhc.start);
520 VectorSubtract(boxendmins, rhc.hull->clip_mins, rhc.end);
521 VectorSubtract(rhc.end, rhc.start, rhc.dist);
522 Mod_Q1BSP_RecursiveHullCheck(&rhc, rhc.hull->firstclipnode, 0, 1, rhc.start, rhc.end);
525 static int Mod_Q1BSP_LightPoint_RecursiveBSPNode(vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal, const mnode_t *node, float x, float y, float startz, float endz)
527 int side, distz = endz - startz;
532 if (node->contents < 0)
533 return false; // didn't hit anything
535 switch (node->plane->type)
538 node = node->children[x < node->plane->dist];
541 node = node->children[y < node->plane->dist];
544 side = startz < node->plane->dist;
545 if ((endz < node->plane->dist) == side)
547 node = node->children[side];
550 // found an intersection
551 mid = node->plane->dist;
554 back = front = x * node->plane->normal[0] + y * node->plane->normal[1];
555 front += startz * node->plane->normal[2];
556 back += endz * node->plane->normal[2];
557 side = front < node->plane->dist;
558 if ((back < node->plane->dist) == side)
560 node = node->children[side];
563 // found an intersection
564 mid = startz + distz * (front - node->plane->dist) / (front - back);
568 // go down front side
569 if (node->children[side]->contents >= 0 && Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, node->children[side], x, y, startz, mid))
570 return true; // hit something
573 // check for impact on this node
574 if (node->numsurfaces)
579 surf = cl.worldmodel->brushq1.surfaces + node->firstsurface;
580 for (i = 0;i < node->numsurfaces;i++, surf++)
582 if (!(surf->flags & SURF_LIGHTMAP))
583 continue; // no lightmaps
585 ds = (int) (x * surf->texinfo->vecs[0][0] + y * surf->texinfo->vecs[0][1] + mid * surf->texinfo->vecs[0][2] + surf->texinfo->vecs[0][3]);
586 dt = (int) (x * surf->texinfo->vecs[1][0] + y * surf->texinfo->vecs[1][1] + mid * surf->texinfo->vecs[1][2] + surf->texinfo->vecs[1][3]);
588 if (ds < surf->texturemins[0] || dt < surf->texturemins[1])
591 ds -= surf->texturemins[0];
592 dt -= surf->texturemins[1];
594 if (ds > surf->extents[0] || dt > surf->extents[1])
600 int maps, line3, size3, dsfrac = ds & 15, dtfrac = dt & 15, scale = 0, r00 = 0, g00 = 0, b00 = 0, r01 = 0, g01 = 0, b01 = 0, r10 = 0, g10 = 0, b10 = 0, r11 = 0, g11 = 0, b11 = 0;
601 line3 = ((surf->extents[0]>>4)+1)*3;
602 size3 = ((surf->extents[0]>>4)+1) * ((surf->extents[1]>>4)+1)*3; // LordHavoc: *3 for colored lighting
604 lightmap = surf->samples + ((dt>>4) * ((surf->extents[0]>>4)+1) + (ds>>4))*3; // LordHavoc: *3 for color
606 for (maps = 0;maps < MAXLIGHTMAPS && surf->styles[maps] != 255;maps++)
608 scale = d_lightstylevalue[surf->styles[maps]];
609 r00 += lightmap[ 0] * scale;g00 += lightmap[ 1] * scale;b00 += lightmap[ 2] * scale;
610 r01 += lightmap[ 3] * scale;g01 += lightmap[ 4] * scale;b01 += lightmap[ 5] * scale;
611 r10 += lightmap[line3+0] * scale;g10 += lightmap[line3+1] * scale;b10 += lightmap[line3+2] * scale;
612 r11 += lightmap[line3+3] * scale;g11 += lightmap[line3+4] * scale;b11 += lightmap[line3+5] * scale;
617 LordHavoc: here's the readable version of the interpolation
618 code, not quite as easy for the compiler to optimize...
620 dsfrac is the X position in the lightmap pixel, * 16
621 dtfrac is the Y position in the lightmap pixel, * 16
622 r00 is top left corner, r01 is top right corner
623 r10 is bottom left corner, r11 is bottom right corner
624 g and b are the same layout.
625 r0 and r1 are the top and bottom intermediate results
627 first we interpolate the top two points, to get the top
630 r0 = (((r01-r00) * dsfrac) >> 4) + r00;
631 g0 = (((g01-g00) * dsfrac) >> 4) + g00;
632 b0 = (((b01-b00) * dsfrac) >> 4) + b00;
634 then we interpolate the bottom two points, to get the
637 r1 = (((r11-r10) * dsfrac) >> 4) + r10;
638 g1 = (((g11-g10) * dsfrac) >> 4) + g10;
639 b1 = (((b11-b10) * dsfrac) >> 4) + b10;
641 then we interpolate the top and bottom samples to get the
642 middle sample (the one which was requested)
644 r = (((r1-r0) * dtfrac) >> 4) + r0;
645 g = (((g1-g0) * dtfrac) >> 4) + g0;
646 b = (((b1-b0) * dtfrac) >> 4) + b0;
649 ambientcolor[0] += (float) ((((((((r11-r10) * dsfrac) >> 4) + r10)-((((r01-r00) * dsfrac) >> 4) + r00)) * dtfrac) >> 4) + ((((r01-r00) * dsfrac) >> 4) + r00)) * (1.0f / 32768.0f);
650 ambientcolor[1] += (float) ((((((((g11-g10) * dsfrac) >> 4) + g10)-((((g01-g00) * dsfrac) >> 4) + g00)) * dtfrac) >> 4) + ((((g01-g00) * dsfrac) >> 4) + g00)) * (1.0f / 32768.0f);
651 ambientcolor[2] += (float) ((((((((b11-b10) * dsfrac) >> 4) + b10)-((((b01-b00) * dsfrac) >> 4) + b00)) * dtfrac) >> 4) + ((((b01-b00) * dsfrac) >> 4) + b00)) * (1.0f / 32768.0f);
653 return true; // success
658 node = node->children[side ^ 1];
660 distz = endz - startz;
665 void Mod_Q1BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
667 Mod_Q1BSP_LightPoint_RecursiveBSPNode(ambientcolor, diffusecolor, diffusenormal, cl.worldmodel->brushq1.nodes + cl.worldmodel->brushq1.hulls[0].firstclipnode, p[0], p[1], p[2], p[2] - 65536);
670 static void Mod_Q1BSP_DecompressVis(const qbyte *in, const qbyte *inend, qbyte *out, qbyte *outend)
677 Con_Printf("Mod_Q1BSP_DecompressVis: input underrun\n");
685 for (c = *in++;c > 0;c--)
689 Con_Printf("Mod_Q1BSP_DecompressVis: output overrun\n");
698 static void Mod_Q1BSP_LoadTextures(lump_t *l)
700 int i, j, k, num, max, altmax, mtwidth, mtheight, *dofs, incomplete;
702 texture_t *tx, *tx2, *anims[10], *altanims[10];
704 qbyte *data, *mtdata;
707 loadmodel->brushq1.textures = NULL;
712 m = (dmiptexlump_t *)(mod_base + l->fileofs);
714 m->nummiptex = LittleLong (m->nummiptex);
716 // add two slots for notexture walls and notexture liquids
717 loadmodel->brushq1.numtextures = m->nummiptex + 2;
718 loadmodel->brushq1.textures = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numtextures * sizeof(texture_t));
720 // fill out all slots with notexture
721 for (i = 0, tx = loadmodel->brushq1.textures;i < loadmodel->brushq1.numtextures;i++, tx++)
724 strcpy(tx->name, "NO TEXTURE FOUND");
727 tx->skin.base = r_notexture;
728 tx->shader = &Cshader_wall_lightmap;
729 tx->flags = SURF_SOLIDCLIP;
730 if (i == loadmodel->brushq1.numtextures - 1)
732 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
733 tx->shader = &Cshader_water;
735 tx->currentframe = tx;
738 // just to work around bounds checking when debugging with it (array index out of bounds error thing)
740 // LordHavoc: mostly rewritten map texture loader
741 for (i = 0;i < m->nummiptex;i++)
743 dofs[i] = LittleLong(dofs[i]);
744 if (dofs[i] == -1 || r_nosurftextures.integer)
746 dmiptex = (miptex_t *)((qbyte *)m + dofs[i]);
748 // make sure name is no more than 15 characters
749 for (j = 0;dmiptex->name[j] && j < 15;j++)
750 name[j] = dmiptex->name[j];
753 mtwidth = LittleLong(dmiptex->width);
754 mtheight = LittleLong(dmiptex->height);
756 j = LittleLong(dmiptex->offsets[0]);
760 if (j < 40 || j + mtwidth * mtheight > l->filelen)
762 Con_Printf("Texture \"%s\" in \"%s\"is corrupt or incomplete\n", dmiptex->name, loadmodel->name);
765 mtdata = (qbyte *)dmiptex + j;
768 if ((mtwidth & 15) || (mtheight & 15))
769 Con_Printf("warning: texture \"%s\" in \"%s\" is not 16 aligned", dmiptex->name, loadmodel->name);
771 // LordHavoc: force all names to lowercase
772 for (j = 0;name[j];j++)
773 if (name[j] >= 'A' && name[j] <= 'Z')
774 name[j] += 'a' - 'A';
776 tx = loadmodel->brushq1.textures + i;
777 strcpy(tx->name, name);
779 tx->height = mtheight;
783 sprintf(tx->name, "unnamed%i", i);
784 Con_Printf("warning: unnamed texture in %s, renaming to %s\n", loadmodel->name, tx->name);
787 // LordHavoc: HL sky textures are entirely different than quake
788 if (!loadmodel->brush.ishlbsp && !strncmp(tx->name, "sky", 3) && mtwidth == 256 && mtheight == 128)
790 if (loadmodel->isworldmodel)
792 data = loadimagepixels(tx->name, false, 0, 0);
795 if (image_width == 256 && image_height == 128)
803 Con_Printf("Invalid replacement texture for sky \"%s\" in %\"%s\", must be 256x128 pixels\n", tx->name, loadmodel->name);
805 R_InitSky(mtdata, 1);
808 else if (mtdata != NULL)
809 R_InitSky(mtdata, 1);
814 if (!Mod_LoadSkinFrame(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true))
816 // did not find external texture, load it from the bsp or wad3
817 if (loadmodel->brush.ishlbsp)
819 // internal texture overrides wad
820 qbyte *pixels, *freepixels, *fogpixels;
821 pixels = freepixels = NULL;
823 pixels = W_ConvertWAD3Texture(dmiptex);
825 pixels = freepixels = W_GetTexture(tx->name);
828 tx->width = image_width;
829 tx->height = image_height;
830 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);
831 if (Image_CheckAlpha(pixels, image_width * image_height, true))
833 fogpixels = Mem_Alloc(tempmempool, image_width * image_height * 4);
834 for (j = 0;j < image_width * image_height * 4;j += 4)
836 fogpixels[j + 0] = 255;
837 fogpixels[j + 1] = 255;
838 fogpixels[j + 2] = 255;
839 fogpixels[j + 3] = pixels[j + 3];
841 tx->skin.fog = R_LoadTexture2D(loadmodel->texturepool, tx->name, image_width, image_height, pixels, TEXTYPE_RGBA, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, NULL);
846 Mem_Free(freepixels);
848 else if (mtdata) // texture included
849 Mod_LoadSkinFrame_Internal(&tx->skin, tx->name, TEXF_MIPMAP | TEXF_PRECACHE, false, true, tx->name[0] != '*' && r_fullbrights.integer, mtdata, tx->width, tx->height);
852 if (tx->skin.base == NULL)
857 tx->skin.base = r_notexture;
860 if (tx->name[0] == '*')
862 // turb does not block movement
863 tx->flags &= ~SURF_SOLIDCLIP;
864 tx->flags |= SURF_DRAWTURB | SURF_LIGHTBOTHSIDES;
865 // LordHavoc: some turbulent textures should be fullbright and solid
866 if (!strncmp(tx->name,"*lava",5)
867 || !strncmp(tx->name,"*teleport",9)
868 || !strncmp(tx->name,"*rift",5)) // Scourge of Armagon texture
869 tx->flags |= SURF_DRAWFULLBRIGHT | SURF_DRAWNOALPHA;
871 tx->flags |= SURF_WATERALPHA;
872 tx->shader = &Cshader_water;
874 else if (tx->name[0] == 's' && tx->name[1] == 'k' && tx->name[2] == 'y')
876 tx->flags |= SURF_DRAWSKY;
877 tx->shader = &Cshader_sky;
881 tx->flags |= SURF_LIGHTMAP;
883 tx->flags |= SURF_SHADOWCAST | SURF_SHADOWLIGHT;
884 tx->shader = &Cshader_wall_lightmap;
887 // start out with no animation
888 tx->currentframe = tx;
891 // sequence the animations
892 for (i = 0;i < m->nummiptex;i++)
894 tx = loadmodel->brushq1.textures + i;
895 if (!tx || tx->name[0] != '+' || tx->name[1] == 0 || tx->name[2] == 0)
897 if (tx->anim_total[0] || tx->anim_total[1])
898 continue; // already sequenced
900 // find the number of frames in the animation
901 memset(anims, 0, sizeof(anims));
902 memset(altanims, 0, sizeof(altanims));
904 for (j = i;j < m->nummiptex;j++)
906 tx2 = loadmodel->brushq1.textures + j;
907 if (!tx2 || tx2->name[0] != '+' || strcmp(tx2->name+2, tx->name+2))
911 if (num >= '0' && num <= '9')
912 anims[num - '0'] = tx2;
913 else if (num >= 'a' && num <= 'j')
914 altanims[num - 'a'] = tx2;
916 Con_Printf("Bad animating texture %s\n", tx->name);
920 for (j = 0;j < 10;j++)
927 //Con_Printf("linking animation %s (%i:%i frames)\n\n", tx->name, max, altmax);
930 for (j = 0;j < max;j++)
934 Con_Printf("Missing frame %i of %s\n", j, tx->name);
938 for (j = 0;j < altmax;j++)
942 Con_Printf("Missing altframe %i of %s\n", j, tx->name);
951 // if there is no alternate animation, duplicate the primary
952 // animation into the alternate
954 for (k = 0;k < 10;k++)
955 altanims[k] = anims[k];
958 // link together the primary animation
959 for (j = 0;j < max;j++)
962 tx2->animated = true;
963 tx2->anim_total[0] = max;
964 tx2->anim_total[1] = altmax;
965 for (k = 0;k < 10;k++)
967 tx2->anim_frames[0][k] = anims[k];
968 tx2->anim_frames[1][k] = altanims[k];
972 // if there really is an alternate anim...
973 if (anims[0] != altanims[0])
975 // link together the alternate animation
976 for (j = 0;j < altmax;j++)
979 tx2->animated = true;
980 // the primary/alternate are reversed here
981 tx2->anim_total[0] = altmax;
982 tx2->anim_total[1] = max;
983 for (k = 0;k < 10;k++)
985 tx2->anim_frames[0][k] = altanims[k];
986 tx2->anim_frames[1][k] = anims[k];
993 static void Mod_Q1BSP_LoadLighting(lump_t *l)
996 qbyte *in, *out, *data, d;
997 char litfilename[1024];
998 loadmodel->brushq1.lightdata = NULL;
999 if (loadmodel->brush.ishlbsp) // LordHavoc: load the colored lighting data straight
1001 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen);
1002 memcpy(loadmodel->brushq1.lightdata, mod_base + l->fileofs, l->filelen);
1004 else // LordHavoc: bsp version 29 (normal white lighting)
1006 // LordHavoc: hope is not lost yet, check for a .lit file to load
1007 strcpy(litfilename, loadmodel->name);
1008 FS_StripExtension(litfilename, litfilename);
1009 strcat(litfilename, ".lit");
1010 data = (qbyte*) FS_LoadFile(litfilename, false);
1013 if (fs_filesize > 8 && data[0] == 'Q' && data[1] == 'L' && data[2] == 'I' && data[3] == 'T')
1015 i = LittleLong(((int *)data)[1]);
1018 Con_DPrintf("loaded %s\n", litfilename);
1019 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, fs_filesize - 8);
1020 memcpy(loadmodel->brushq1.lightdata, data + 8, fs_filesize - 8);
1026 Con_Printf("Unknown .lit file version (%d)\n", i);
1032 if (fs_filesize == 8)
1033 Con_Printf("Empty .lit file, ignoring\n");
1035 Con_Printf("Corrupt .lit file (old version?), ignoring\n");
1039 // LordHavoc: oh well, expand the white lighting data
1042 loadmodel->brushq1.lightdata = Mem_Alloc(loadmodel->mempool, l->filelen*3);
1043 in = loadmodel->brushq1.lightdata + l->filelen*2; // place the file at the end, so it will not be overwritten until the very last write
1044 out = loadmodel->brushq1.lightdata;
1045 memcpy(in, mod_base + l->fileofs, l->filelen);
1046 for (i = 0;i < l->filelen;i++)
1056 static void Mod_Q1BSP_LoadLightList(void)
1058 int a, n, numlights;
1059 char lightsfilename[1024], *s, *t, *lightsstring;
1062 strcpy(lightsfilename, loadmodel->name);
1063 FS_StripExtension(lightsfilename, lightsfilename);
1064 strcat(lightsfilename, ".lights");
1065 s = lightsstring = (char *) FS_LoadFile(lightsfilename, false);
1071 while (*s && *s != '\n')
1075 Mem_Free(lightsstring);
1076 Host_Error("lights file must end with a newline\n");
1081 loadmodel->brushq1.lights = Mem_Alloc(loadmodel->mempool, numlights * sizeof(mlight_t));
1084 while (*s && n < numlights)
1087 while (*s && *s != '\n')
1091 Mem_Free(lightsstring);
1092 Host_Error("misparsed lights file!\n");
1094 e = loadmodel->brushq1.lights + n;
1096 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);
1100 Mem_Free(lightsstring);
1101 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);
1108 Mem_Free(lightsstring);
1109 Host_Error("misparsed lights file!\n");
1111 loadmodel->brushq1.numlights = numlights;
1112 Mem_Free(lightsstring);
1116 static void Mod_Q1BSP_LoadVisibility(lump_t *l)
1118 loadmodel->brushq1.num_compressedpvs = 0;
1119 loadmodel->brushq1.data_compressedpvs = NULL;
1122 loadmodel->brushq1.num_compressedpvs = l->filelen;
1123 loadmodel->brushq1.data_compressedpvs = Mem_Alloc(loadmodel->mempool, l->filelen);
1124 memcpy(loadmodel->brushq1.data_compressedpvs, mod_base + l->fileofs, l->filelen);
1127 // used only for HalfLife maps
1128 static void Mod_Q1BSP_ParseWadsFromEntityLump(const char *data)
1130 char key[128], value[4096];
1135 if (!COM_ParseToken(&data, false))
1137 if (com_token[0] != '{')
1141 if (!COM_ParseToken(&data, false))
1143 if (com_token[0] == '}')
1144 break; // end of worldspawn
1145 if (com_token[0] == '_')
1146 strcpy(key, com_token + 1);
1148 strcpy(key, com_token);
1149 while (key[strlen(key)-1] == ' ') // remove trailing spaces
1150 key[strlen(key)-1] = 0;
1151 if (!COM_ParseToken(&data, false))
1153 strcpy(value, com_token);
1154 if (!strcmp("wad", key)) // for HalfLife maps
1156 if (loadmodel->brush.ishlbsp)
1159 for (i = 0;i < 4096;i++)
1160 if (value[i] != ';' && value[i] != '\\' && value[i] != '/' && value[i] != ':')
1166 // ignore path - the \\ check is for HalfLife... stupid windoze 'programmers'...
1167 if (value[i] == '\\' || value[i] == '/' || value[i] == ':')
1169 else if (value[i] == ';' || value[i] == 0)
1173 strcpy(wadname, "textures/");
1174 strcat(wadname, &value[j]);
1175 W_LoadTextureWadFile(wadname, false);
1187 static void Mod_Q1BSP_LoadEntities(lump_t *l)
1189 loadmodel->brush.entities = NULL;
1192 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
1193 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
1194 if (loadmodel->brush.ishlbsp)
1195 Mod_Q1BSP_ParseWadsFromEntityLump(loadmodel->brush.entities);
1199 static void Mod_Q1BSP_LoadVertexes(lump_t *l)
1205 in = (void *)(mod_base + l->fileofs);
1206 if (l->filelen % sizeof(*in))
1207 Host_Error("Mod_Q1BSP_LoadVertexes: funny lump size in %s",loadmodel->name);
1208 count = l->filelen / sizeof(*in);
1209 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1211 loadmodel->brushq1.vertexes = out;
1212 loadmodel->brushq1.numvertexes = count;
1214 for ( i=0 ; i<count ; i++, in++, out++)
1216 out->position[0] = LittleFloat(in->point[0]);
1217 out->position[1] = LittleFloat(in->point[1]);
1218 out->position[2] = LittleFloat(in->point[2]);
1222 static void Mod_Q1BSP_LoadSubmodels(lump_t *l)
1228 in = (void *)(mod_base + l->fileofs);
1229 if (l->filelen % sizeof(*in))
1230 Host_Error("Mod_Q1BSP_LoadSubmodels: funny lump size in %s",loadmodel->name);
1231 count = l->filelen / sizeof(*in);
1232 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1234 loadmodel->brushq1.submodels = out;
1235 loadmodel->brush.numsubmodels = count;
1237 for ( i=0 ; i<count ; i++, in++, out++)
1239 for (j=0 ; j<3 ; j++)
1241 // spread the mins / maxs by a pixel
1242 out->mins[j] = LittleFloat(in->mins[j]) - 1;
1243 out->maxs[j] = LittleFloat(in->maxs[j]) + 1;
1244 out->origin[j] = LittleFloat(in->origin[j]);
1246 for (j=0 ; j<MAX_MAP_HULLS ; j++)
1247 out->headnode[j] = LittleLong(in->headnode[j]);
1248 out->visleafs = LittleLong(in->visleafs);
1249 out->firstface = LittleLong(in->firstface);
1250 out->numfaces = LittleLong(in->numfaces);
1254 static void Mod_Q1BSP_LoadEdges(lump_t *l)
1260 in = (void *)(mod_base + l->fileofs);
1261 if (l->filelen % sizeof(*in))
1262 Host_Error("Mod_Q1BSP_LoadEdges: funny lump size in %s",loadmodel->name);
1263 count = l->filelen / sizeof(*in);
1264 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1266 loadmodel->brushq1.edges = out;
1267 loadmodel->brushq1.numedges = count;
1269 for ( i=0 ; i<count ; i++, in++, out++)
1271 out->v[0] = (unsigned short)LittleShort(in->v[0]);
1272 out->v[1] = (unsigned short)LittleShort(in->v[1]);
1276 static void Mod_Q1BSP_LoadTexinfo(lump_t *l)
1280 int i, j, k, count, miptex;
1282 in = (void *)(mod_base + l->fileofs);
1283 if (l->filelen % sizeof(*in))
1284 Host_Error("Mod_Q1BSP_LoadTexinfo: funny lump size in %s",loadmodel->name);
1285 count = l->filelen / sizeof(*in);
1286 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
1288 loadmodel->brushq1.texinfo = out;
1289 loadmodel->brushq1.numtexinfo = count;
1291 for (i = 0;i < count;i++, in++, out++)
1293 for (k = 0;k < 2;k++)
1294 for (j = 0;j < 4;j++)
1295 out->vecs[k][j] = LittleFloat(in->vecs[k][j]);
1297 miptex = LittleLong(in->miptex);
1298 out->flags = LittleLong(in->flags);
1300 out->texture = NULL;
1301 if (loadmodel->brushq1.textures)
1303 if ((unsigned int) miptex >= (unsigned int) loadmodel->brushq1.numtextures)
1304 Con_Printf("error in model \"%s\": invalid miptex index %i(of %i)\n", loadmodel->name, miptex, loadmodel->brushq1.numtextures);
1306 out->texture = loadmodel->brushq1.textures + miptex;
1308 if (out->flags & TEX_SPECIAL)
1310 // if texture chosen is NULL or the shader needs a lightmap,
1311 // force to notexture water shader
1312 if (out->texture == NULL || out->texture->shader->flags & SHADERFLAGS_NEEDLIGHTMAP)
1313 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 1);
1317 // if texture chosen is NULL, force to notexture
1318 if (out->texture == NULL)
1319 out->texture = loadmodel->brushq1.textures + (loadmodel->brushq1.numtextures - 2);
1325 void BoundPoly(int numverts, float *verts, vec3_t mins, vec3_t maxs)
1330 mins[0] = mins[1] = mins[2] = 9999;
1331 maxs[0] = maxs[1] = maxs[2] = -9999;
1333 for (i = 0;i < numverts;i++)
1335 for (j = 0;j < 3;j++, v++)
1345 #define MAX_SUBDIVPOLYTRIANGLES 4096
1346 #define MAX_SUBDIVPOLYVERTS(MAX_SUBDIVPOLYTRIANGLES * 3)
1348 static int subdivpolyverts, subdivpolytriangles;
1349 static int subdivpolyindex[MAX_SUBDIVPOLYTRIANGLES][3];
1350 static float subdivpolyvert[MAX_SUBDIVPOLYVERTS][3];
1352 static int subdivpolylookupvert(vec3_t v)
1355 for (i = 0;i < subdivpolyverts;i++)
1356 if (subdivpolyvert[i][0] == v[0]
1357 && subdivpolyvert[i][1] == v[1]
1358 && subdivpolyvert[i][2] == v[2])
1360 if (subdivpolyverts >= MAX_SUBDIVPOLYVERTS)
1361 Host_Error("SubDividePolygon: ran out of vertices in buffer, please increase your r_subdivide_size");
1362 VectorCopy(v, subdivpolyvert[subdivpolyverts]);
1363 return subdivpolyverts++;
1366 static void SubdividePolygon(int numverts, float *verts)
1368 int i, i1, i2, i3, f, b, c, p;
1369 vec3_t mins, maxs, front[256], back[256];
1370 float m, *pv, *cv, dist[256], frac;
1373 Host_Error("SubdividePolygon: ran out of verts in buffer");
1375 BoundPoly(numverts, verts, mins, maxs);
1377 for (i = 0;i < 3;i++)
1379 m = (mins[i] + maxs[i]) * 0.5;
1380 m = r_subdivide_size.value * floor(m/r_subdivide_size.value + 0.5);
1381 if (maxs[i] - m < 8)
1383 if (m - mins[i] < 8)
1387 for (cv = verts, c = 0;c < numverts;c++, cv += 3)
1388 dist[c] = cv[i] - m;
1391 for (p = numverts - 1, c = 0, pv = verts + p * 3, cv = verts;c < numverts;p = c, c++, pv = cv, cv += 3)
1395 VectorCopy(pv, front[f]);
1400 VectorCopy(pv, back[b]);
1403 if (dist[p] == 0 || dist[c] == 0)
1405 if ((dist[p] > 0) != (dist[c] > 0) )
1408 frac = dist[p] / (dist[p] - dist[c]);
1409 front[f][0] = back[b][0] = pv[0] + frac * (cv[0] - pv[0]);
1410 front[f][1] = back[b][1] = pv[1] + frac * (cv[1] - pv[1]);
1411 front[f][2] = back[b][2] = pv[2] + frac * (cv[2] - pv[2]);
1417 SubdividePolygon(f, front[0]);
1418 SubdividePolygon(b, back[0]);
1422 i1 = subdivpolylookupvert(verts);
1423 i2 = subdivpolylookupvert(verts + 3);
1424 for (i = 2;i < numverts;i++)
1426 if (subdivpolytriangles >= MAX_SUBDIVPOLYTRIANGLES)
1428 Con_Printf("SubdividePolygon: ran out of triangles in buffer, please increase your r_subdivide_size\n");
1432 i3 = subdivpolylookupvert(verts + i * 3);
1433 subdivpolyindex[subdivpolytriangles][0] = i1;
1434 subdivpolyindex[subdivpolytriangles][1] = i2;
1435 subdivpolyindex[subdivpolytriangles][2] = i3;
1437 subdivpolytriangles++;
1441 //Breaks a polygon up along axial 64 unit
1442 //boundaries so that turbulent and sky warps
1443 //can be done reasonably.
1444 static void Mod_Q1BSP_GenerateWarpMesh(msurface_t *surf)
1450 subdivpolytriangles = 0;
1451 subdivpolyverts = 0;
1452 SubdividePolygon(surf->poly_numverts, surf->poly_verts);
1453 if (subdivpolytriangles < 1)
1454 Host_Error("Mod_Q1BSP_GenerateWarpMesh: no triangles?\n");
1456 surf->mesh = mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + subdivpolytriangles * sizeof(int[3]) + subdivpolyverts * sizeof(surfvertex_t));
1457 mesh->numverts = subdivpolyverts;
1458 mesh->numtriangles = subdivpolytriangles;
1459 mesh->vertex = (surfvertex_t *)(mesh + 1);
1460 mesh->index = (int *)(mesh->vertex + mesh->numverts);
1461 memset(mesh->vertex, 0, mesh->numverts * sizeof(surfvertex_t));
1463 for (i = 0;i < mesh->numtriangles;i++)
1464 for (j = 0;j < 3;j++)
1465 mesh->index[i*3+j] = subdivpolyindex[i][j];
1467 for (i = 0, v = mesh->vertex;i < subdivpolyverts;i++, v++)
1469 VectorCopy(subdivpolyvert[i], v->v);
1470 v->st[0] = DotProduct(v->v, surf->texinfo->vecs[0]);
1471 v->st[1] = DotProduct(v->v, surf->texinfo->vecs[1]);
1476 static surfmesh_t *Mod_Q1BSP_AllocSurfMesh(int numverts, int numtriangles)
1479 mesh = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) + numtriangles * sizeof(int[6]) + numverts * (3 + 2 + 2 + 2 + 3 + 3 + 3 + 1) * sizeof(float));
1480 mesh->numverts = numverts;
1481 mesh->numtriangles = numtriangles;
1482 mesh->vertex3f = (float *)(mesh + 1);
1483 mesh->texcoordtexture2f = mesh->vertex3f + mesh->numverts * 3;
1484 mesh->texcoordlightmap2f = mesh->texcoordtexture2f + mesh->numverts * 2;
1485 mesh->texcoorddetail2f = mesh->texcoordlightmap2f + mesh->numverts * 2;
1486 mesh->svector3f = (float *)(mesh->texcoorddetail2f + mesh->numverts * 2);
1487 mesh->tvector3f = mesh->svector3f + mesh->numverts * 3;
1488 mesh->normal3f = mesh->tvector3f + mesh->numverts * 3;
1489 mesh->lightmapoffsets = (int *)(mesh->normal3f + mesh->numverts * 3);
1490 mesh->element3i = mesh->lightmapoffsets + mesh->numverts;
1491 mesh->neighbor3i = mesh->element3i + mesh->numtriangles * 3;
1495 static void Mod_Q1BSP_GenerateSurfacePolygon(msurface_t *surf, int firstedge, int numedges)
1498 float *vec, *vert, mins[3], maxs[3], val, *v;
1501 // convert edges back to a normal polygon
1502 surf->poly_numverts = numedges;
1503 vert = surf->poly_verts = Mem_Alloc(loadmodel->mempool, sizeof(float[3]) * numedges);
1504 for (i = 0;i < numedges;i++)
1506 lindex = loadmodel->brushq1.surfedges[firstedge + i];
1508 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[lindex].v[0]].position;
1510 vec = loadmodel->brushq1.vertexes[loadmodel->brushq1.edges[-lindex].v[1]].position;
1511 VectorCopy(vec, vert);
1515 // calculate polygon bounding box and center
1516 vert = surf->poly_verts;
1517 VectorCopy(vert, mins);
1518 VectorCopy(vert, maxs);
1520 for (i = 1;i < surf->poly_numverts;i++, vert += 3)
1522 if (mins[0] > vert[0]) mins[0] = vert[0];if (maxs[0] < vert[0]) maxs[0] = vert[0];
1523 if (mins[1] > vert[1]) mins[1] = vert[1];if (maxs[1] < vert[1]) maxs[1] = vert[1];
1524 if (mins[2] > vert[2]) mins[2] = vert[2];if (maxs[2] < vert[2]) maxs[2] = vert[2];
1526 VectorCopy(mins, surf->poly_mins);
1527 VectorCopy(maxs, surf->poly_maxs);
1528 surf->poly_center[0] = (mins[0] + maxs[0]) * 0.5f;
1529 surf->poly_center[1] = (mins[1] + maxs[1]) * 0.5f;
1530 surf->poly_center[2] = (mins[2] + maxs[2]) * 0.5f;
1532 // generate surface extents information
1533 tex = surf->texinfo;
1534 mins[0] = maxs[0] = DotProduct(surf->poly_verts, tex->vecs[0]) + tex->vecs[0][3];
1535 mins[1] = maxs[1] = DotProduct(surf->poly_verts, tex->vecs[1]) + tex->vecs[1][3];
1536 for (i = 1, v = surf->poly_verts + 3;i < surf->poly_numverts;i++, v += 3)
1538 for (j = 0;j < 2;j++)
1540 val = DotProduct(v, tex->vecs[j]) + tex->vecs[j][3];
1547 for (i = 0;i < 2;i++)
1549 surf->texturemins[i] = (int) floor(mins[i] / 16) * 16;
1550 surf->extents[i] = (int) ceil(maxs[i] / 16) * 16 - surf->texturemins[i];
1554 static void Mod_Q1BSP_LoadFaces(lump_t *l)
1558 int i, count, surfnum, planenum, ssize, tsize, firstedge, numedges, totalverts, totaltris, totalmeshes;
1562 in = (void *)(mod_base + l->fileofs);
1563 if (l->filelen % sizeof(*in))
1564 Host_Error("Mod_Q1BSP_LoadFaces: funny lump size in %s",loadmodel->name);
1565 count = l->filelen / sizeof(*in);
1566 loadmodel->brushq1.surfaces = Mem_Alloc(loadmodel->mempool, count*sizeof(msurface_t));
1568 loadmodel->brushq1.numsurfaces = count;
1569 loadmodel->brushq1.surfacevisframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1570 loadmodel->brushq1.surfacepvsframes = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1571 loadmodel->brushq1.pvssurflist = Mem_Alloc(loadmodel->mempool, count * sizeof(int));
1573 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++)
1575 surf->number = surfnum;
1576 // FIXME: validate edges, texinfo, etc?
1577 firstedge = LittleLong(in->firstedge);
1578 numedges = LittleShort(in->numedges);
1579 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)
1580 Host_Error("Mod_Q1BSP_LoadFaces: invalid edge range (firstedge %i, numedges %i, model edges %i)\n", firstedge, numedges, loadmodel->brushq1.numsurfedges);
1581 i = LittleShort(in->texinfo);
1582 if ((unsigned int) i >= (unsigned int) loadmodel->brushq1.numtexinfo)
1583 Host_Error("Mod_Q1BSP_LoadFaces: invalid texinfo index %i(model has %i texinfos)\n", i, loadmodel->brushq1.numtexinfo);
1584 surf->texinfo = loadmodel->brushq1.texinfo + i;
1585 surf->flags = surf->texinfo->texture->flags;
1587 planenum = LittleShort(in->planenum);
1588 if ((unsigned int) planenum >= (unsigned int) loadmodel->brushq1.numplanes)
1589 Host_Error("Mod_Q1BSP_LoadFaces: invalid plane index %i (model has %i planes)\n", planenum, loadmodel->brushq1.numplanes);
1591 if (LittleShort(in->side))
1592 surf->flags |= SURF_PLANEBACK;
1594 surf->plane = loadmodel->brushq1.planes + planenum;
1596 // clear lightmap (filled in later)
1597 surf->lightmaptexture = NULL;
1599 // force lightmap upload on first time seeing the surface
1600 surf->cached_dlight = true;
1602 Mod_Q1BSP_GenerateSurfacePolygon(surf, firstedge, numedges);
1604 ssize = (surf->extents[0] >> 4) + 1;
1605 tsize = (surf->extents[1] >> 4) + 1;
1608 for (i = 0;i < MAXLIGHTMAPS;i++)
1609 surf->styles[i] = in->styles[i];
1610 i = LittleLong(in->lightofs);
1612 surf->samples = NULL;
1613 else if (loadmodel->brush.ishlbsp) // LordHavoc: HalfLife map (bsp version 30)
1614 surf->samples = loadmodel->brushq1.lightdata + i;
1615 else // LordHavoc: white lighting (bsp version 29)
1616 surf->samples = loadmodel->brushq1.lightdata + (i * 3);
1618 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1620 if ((surf->extents[0] >> 4) + 1 > (256) || (surf->extents[1] >> 4) + 1 > (256))
1621 Host_Error("Bad surface extents");
1622 // stainmap for permanent marks on walls
1623 surf->stainsamples = Mem_Alloc(loadmodel->mempool, ssize * tsize * 3);
1625 memset(surf->stainsamples, 255, ssize * tsize * 3);
1629 loadmodel->brushq1.entiremesh = Mod_Q1BSP_AllocSurfMesh(totalverts, totaltris);
1630 loadmodel->brushq1.surfmeshes = Mem_Alloc(loadmodel->mempool, sizeof(surfmesh_t) * totalmeshes);
1632 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++)
1634 mesh = surf->mesh = loadmodel->brushq1.surfmeshes + totalmeshes;
1635 mesh->numverts = surf->poly_numverts;
1636 mesh->numtriangles = surf->poly_numverts - 2;
1637 mesh->vertex3f = loadmodel->brushq1.entiremesh->vertex3f + totalverts * 3;
1638 mesh->texcoordtexture2f = loadmodel->brushq1.entiremesh->texcoordtexture2f + totalverts * 2;
1639 mesh->texcoordlightmap2f = loadmodel->brushq1.entiremesh->texcoordlightmap2f + totalverts * 2;
1640 mesh->texcoorddetail2f = loadmodel->brushq1.entiremesh->texcoorddetail2f + totalverts * 2;
1641 mesh->svector3f = loadmodel->brushq1.entiremesh->svector3f + totalverts * 3;
1642 mesh->tvector3f = loadmodel->brushq1.entiremesh->tvector3f + totalverts * 3;
1643 mesh->normal3f = loadmodel->brushq1.entiremesh->normal3f + totalverts * 3;
1644 mesh->lightmapoffsets = loadmodel->brushq1.entiremesh->lightmapoffsets + totalverts;
1645 mesh->element3i = loadmodel->brushq1.entiremesh->element3i + totaltris * 3;
1646 mesh->neighbor3i = loadmodel->brushq1.entiremesh->neighbor3i + totaltris * 3;
1648 surf->lightmaptexturestride = 0;
1649 surf->lightmaptexture = NULL;
1651 for (i = 0;i < mesh->numverts;i++)
1653 mesh->vertex3f[i * 3 + 0] = surf->poly_verts[i * 3 + 0];
1654 mesh->vertex3f[i * 3 + 1] = surf->poly_verts[i * 3 + 1];
1655 mesh->vertex3f[i * 3 + 2] = surf->poly_verts[i * 3 + 2];
1656 s = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3];
1657 t = DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3];
1658 mesh->texcoordtexture2f[i * 2 + 0] = s / surf->texinfo->texture->width;
1659 mesh->texcoordtexture2f[i * 2 + 1] = t / surf->texinfo->texture->height;
1660 mesh->texcoorddetail2f[i * 2 + 0] = s * (1.0f / 16.0f);
1661 mesh->texcoorddetail2f[i * 2 + 1] = t * (1.0f / 16.0f);
1662 mesh->texcoordlightmap2f[i * 2 + 0] = 0;
1663 mesh->texcoordlightmap2f[i * 2 + 1] = 0;
1664 mesh->lightmapoffsets[i] = 0;
1667 for (i = 0;i < mesh->numtriangles;i++)
1669 mesh->element3i[i * 3 + 0] = 0;
1670 mesh->element3i[i * 3 + 1] = i + 1;
1671 mesh->element3i[i * 3 + 2] = i + 2;
1674 Mod_BuildTriangleNeighbors(mesh->neighbor3i, mesh->element3i, mesh->numtriangles);
1675 Mod_BuildTextureVectorsAndNormals(mesh->numverts, mesh->numtriangles, mesh->vertex3f, mesh->texcoordtexture2f, mesh->element3i, mesh->svector3f, mesh->tvector3f, mesh->normal3f);
1677 if (surf->texinfo->texture->shader == &Cshader_wall_lightmap)
1679 int i, iu, iv, smax, tmax;
1680 float u, v, ubase, vbase, uscale, vscale;
1682 smax = surf->extents[0] >> 4;
1683 tmax = surf->extents[1] >> 4;
1685 surf->flags |= SURF_LIGHTMAP;
1686 if (r_miplightmaps.integer)
1688 surf->lightmaptexturestride = smax+1;
1689 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);
1693 surf->lightmaptexturestride = R_CompatibleFragmentWidth(smax+1, loadmodel->brushq1.lightmaprgba ? TEXTYPE_RGBA : TEXTYPE_RGB, 0);
1694 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);
1696 R_FragmentLocation(surf->lightmaptexture, NULL, NULL, &ubase, &vbase, &uscale, &vscale);
1697 uscale = (uscale - ubase) / (smax + 1);
1698 vscale = (vscale - vbase) / (tmax + 1);
1700 for (i = 0;i < mesh->numverts;i++)
1702 u = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[0]) + surf->texinfo->vecs[0][3]) + 8 - surf->texturemins[0]) * (1.0 / 16.0);
1703 v = ((DotProduct((mesh->vertex3f + i * 3), surf->texinfo->vecs[1]) + surf->texinfo->vecs[1][3]) + 8 - surf->texturemins[1]) * (1.0 / 16.0);
1704 mesh->texcoordlightmap2f[i * 2 + 0] = u * uscale + ubase;
1705 mesh->texcoordlightmap2f[i * 2 + 1] = v * vscale + vbase;
1706 // LordHavoc: calc lightmap data offset for vertex lighting to use
1709 mesh->lightmapoffsets[i] = (bound(0, iv, tmax) * (smax+1) + bound(0, iu, smax)) * 3;
1715 static void Mod_Q1BSP_SetParent(mnode_t *node, mnode_t *parent)
1717 node->parent = parent;
1718 if (node->contents < 0)
1720 Mod_Q1BSP_SetParent(node->children[0], node);
1721 Mod_Q1BSP_SetParent(node->children[1], node);
1724 static void Mod_Q1BSP_LoadNodes(lump_t *l)
1730 in = (void *)(mod_base + l->fileofs);
1731 if (l->filelen % sizeof(*in))
1732 Host_Error("Mod_Q1BSP_LoadNodes: funny lump size in %s",loadmodel->name);
1733 count = l->filelen / sizeof(*in);
1734 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1736 loadmodel->brushq1.nodes = out;
1737 loadmodel->brushq1.numnodes = count;
1739 for ( i=0 ; i<count ; i++, in++, out++)
1741 for (j=0 ; j<3 ; j++)
1743 out->mins[j] = LittleShort(in->mins[j]);
1744 out->maxs[j] = LittleShort(in->maxs[j]);
1747 p = LittleLong(in->planenum);
1748 out->plane = loadmodel->brushq1.planes + p;
1750 out->firstsurface = LittleShort(in->firstface);
1751 out->numsurfaces = LittleShort(in->numfaces);
1753 for (j=0 ; j<2 ; j++)
1755 p = LittleShort(in->children[j]);
1757 out->children[j] = loadmodel->brushq1.nodes + p;
1759 out->children[j] = (mnode_t *)(loadmodel->brushq1.leafs + (-1 - p));
1763 Mod_Q1BSP_SetParent(loadmodel->brushq1.nodes, NULL); // sets nodes and leafs
1766 static void Mod_Q1BSP_LoadLeafs(lump_t *l)
1770 int i, j, count, p, pvschainbytes;
1773 in = (void *)(mod_base + l->fileofs);
1774 if (l->filelen % sizeof(*in))
1775 Host_Error("Mod_Q1BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
1776 count = l->filelen / sizeof(*in);
1777 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1779 loadmodel->brushq1.leafs = out;
1780 loadmodel->brushq1.numleafs = count;
1781 pvschainbytes = ((loadmodel->brushq1.numleafs - 1)+7)>>3;
1782 loadmodel->brushq1.data_decompressedpvs = pvs = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numleafs * pvschainbytes);
1784 for ( i=0 ; i<count ; i++, in++, out++)
1786 for (j=0 ; j<3 ; j++)
1788 out->mins[j] = LittleShort(in->mins[j]);
1789 out->maxs[j] = LittleShort(in->maxs[j]);
1792 // FIXME: this function could really benefit from some error checking
1794 out->contents = LittleLong(in->contents);
1796 out->firstmarksurface = loadmodel->brushq1.marksurfaces + LittleShort(in->firstmarksurface);
1797 out->nummarksurfaces = LittleShort(in->nummarksurfaces);
1800 pvs += pvschainbytes;
1802 p = LittleLong(in->visofs);
1804 Mod_Q1BSP_DecompressVis(loadmodel->brushq1.data_compressedpvs + p, loadmodel->brushq1.data_compressedpvs + loadmodel->brushq1.num_compressedpvs, out->pvsdata, out->pvsdata + pvschainbytes);
1806 memset(out->pvsdata, 0xFF, pvschainbytes);
1808 for (j = 0;j < 4;j++)
1809 out->ambient_sound_level[j] = in->ambient_level[j];
1811 // FIXME: Insert caustics here
1815 static void Mod_Q1BSP_LoadClipnodes(lump_t *l)
1817 dclipnode_t *in, *out;
1821 in = (void *)(mod_base + l->fileofs);
1822 if (l->filelen % sizeof(*in))
1823 Host_Error("Mod_Q1BSP_LoadClipnodes: funny lump size in %s",loadmodel->name);
1824 count = l->filelen / sizeof(*in);
1825 out = Mem_Alloc(loadmodel->mempool, count*sizeof(*out));
1827 loadmodel->brushq1.clipnodes = out;
1828 loadmodel->brushq1.numclipnodes = count;
1830 if (loadmodel->brush.ishlbsp)
1832 hull = &loadmodel->brushq1.hulls[1];
1833 hull->clipnodes = out;
1834 hull->firstclipnode = 0;
1835 hull->lastclipnode = count-1;
1836 hull->planes = loadmodel->brushq1.planes;
1837 hull->clip_mins[0] = -16;
1838 hull->clip_mins[1] = -16;
1839 hull->clip_mins[2] = -36;
1840 hull->clip_maxs[0] = 16;
1841 hull->clip_maxs[1] = 16;
1842 hull->clip_maxs[2] = 36;
1843 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1845 hull = &loadmodel->brushq1.hulls[2];
1846 hull->clipnodes = out;
1847 hull->firstclipnode = 0;
1848 hull->lastclipnode = count-1;
1849 hull->planes = loadmodel->brushq1.planes;
1850 hull->clip_mins[0] = -32;
1851 hull->clip_mins[1] = -32;
1852 hull->clip_mins[2] = -32;
1853 hull->clip_maxs[0] = 32;
1854 hull->clip_maxs[1] = 32;
1855 hull->clip_maxs[2] = 32;
1856 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1858 hull = &loadmodel->brushq1.hulls[3];
1859 hull->clipnodes = out;
1860 hull->firstclipnode = 0;
1861 hull->lastclipnode = count-1;
1862 hull->planes = loadmodel->brushq1.planes;
1863 hull->clip_mins[0] = -16;
1864 hull->clip_mins[1] = -16;
1865 hull->clip_mins[2] = -18;
1866 hull->clip_maxs[0] = 16;
1867 hull->clip_maxs[1] = 16;
1868 hull->clip_maxs[2] = 18;
1869 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1873 hull = &loadmodel->brushq1.hulls[1];
1874 hull->clipnodes = out;
1875 hull->firstclipnode = 0;
1876 hull->lastclipnode = count-1;
1877 hull->planes = loadmodel->brushq1.planes;
1878 hull->clip_mins[0] = -16;
1879 hull->clip_mins[1] = -16;
1880 hull->clip_mins[2] = -24;
1881 hull->clip_maxs[0] = 16;
1882 hull->clip_maxs[1] = 16;
1883 hull->clip_maxs[2] = 32;
1884 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1886 hull = &loadmodel->brushq1.hulls[2];
1887 hull->clipnodes = out;
1888 hull->firstclipnode = 0;
1889 hull->lastclipnode = count-1;
1890 hull->planes = loadmodel->brushq1.planes;
1891 hull->clip_mins[0] = -32;
1892 hull->clip_mins[1] = -32;
1893 hull->clip_mins[2] = -24;
1894 hull->clip_maxs[0] = 32;
1895 hull->clip_maxs[1] = 32;
1896 hull->clip_maxs[2] = 64;
1897 VectorSubtract(hull->clip_maxs, hull->clip_mins, hull->clip_size);
1900 for (i=0 ; i<count ; i++, out++, in++)
1902 out->planenum = LittleLong(in->planenum);
1903 out->children[0] = LittleShort(in->children[0]);
1904 out->children[1] = LittleShort(in->children[1]);
1905 if (out->children[0] >= count || out->children[1] >= count)
1906 Host_Error("Corrupt clipping hull(out of range child)\n");
1910 //Duplicate the drawing hull structure as a clipping hull
1911 static void Mod_Q1BSP_MakeHull0(void)
1918 hull = &loadmodel->brushq1.hulls[0];
1920 in = loadmodel->brushq1.nodes;
1921 out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numnodes * sizeof(dclipnode_t));
1923 hull->clipnodes = out;
1924 hull->firstclipnode = 0;
1925 hull->lastclipnode = loadmodel->brushq1.numnodes - 1;
1926 hull->planes = loadmodel->brushq1.planes;
1928 for (i = 0;i < loadmodel->brushq1.numnodes;i++, out++, in++)
1930 out->planenum = in->plane - loadmodel->brushq1.planes;
1931 out->children[0] = in->children[0]->contents < 0 ? in->children[0]->contents : in->children[0] - loadmodel->brushq1.nodes;
1932 out->children[1] = in->children[1]->contents < 0 ? in->children[1]->contents : in->children[1] - loadmodel->brushq1.nodes;
1936 static void Mod_Q1BSP_LoadMarksurfaces(lump_t *l)
1941 in = (void *)(mod_base + l->fileofs);
1942 if (l->filelen % sizeof(*in))
1943 Host_Error("Mod_Q1BSP_LoadMarksurfaces: funny lump size in %s",loadmodel->name);
1944 loadmodel->brushq1.nummarksurfaces = l->filelen / sizeof(*in);
1945 loadmodel->brushq1.marksurfaces = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.nummarksurfaces * sizeof(int));
1947 for (i = 0;i < loadmodel->brushq1.nummarksurfaces;i++)
1949 j = (unsigned) LittleShort(in[i]);
1950 if (j >= loadmodel->brushq1.numsurfaces)
1951 Host_Error("Mod_Q1BSP_LoadMarksurfaces: bad surface number");
1952 loadmodel->brushq1.marksurfaces[i] = j;
1956 static void Mod_Q1BSP_LoadSurfedges(lump_t *l)
1961 in = (void *)(mod_base + l->fileofs);
1962 if (l->filelen % sizeof(*in))
1963 Host_Error("Mod_Q1BSP_LoadSurfedges: funny lump size in %s",loadmodel->name);
1964 loadmodel->brushq1.numsurfedges = l->filelen / sizeof(*in);
1965 loadmodel->brushq1.surfedges = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numsurfedges * sizeof(int));
1967 for (i = 0;i < loadmodel->brushq1.numsurfedges;i++)
1968 loadmodel->brushq1.surfedges[i] = LittleLong(in[i]);
1972 static void Mod_Q1BSP_LoadPlanes(lump_t *l)
1978 in = (void *)(mod_base + l->fileofs);
1979 if (l->filelen % sizeof(*in))
1980 Host_Error("Mod_Q1BSP_LoadPlanes: funny lump size in %s", loadmodel->name);
1982 loadmodel->brushq1.numplanes = l->filelen / sizeof(*in);
1983 loadmodel->brushq1.planes = out = Mem_Alloc(loadmodel->mempool, loadmodel->brushq1.numplanes * sizeof(*out));
1985 for (i = 0;i < loadmodel->brushq1.numplanes;i++, in++, out++)
1987 out->normal[0] = LittleFloat(in->normal[0]);
1988 out->normal[1] = LittleFloat(in->normal[1]);
1989 out->normal[2] = LittleFloat(in->normal[2]);
1990 out->dist = LittleFloat(in->dist);
1996 #define MAX_POINTS_ON_WINDING 64
2002 double points[8][3]; // variable sized
2011 static winding_t *NewWinding(int points)
2016 if (points > MAX_POINTS_ON_WINDING)
2017 Sys_Error("NewWinding: too many points\n");
2019 size = sizeof(winding_t) + sizeof(double[3]) * (points - 8);
2020 w = Mem_Alloc(loadmodel->mempool, size);
2026 static void FreeWinding(winding_t *w)
2036 static winding_t *BaseWindingForPlane(mplane_t *p)
2038 double org[3], vright[3], vup[3], normal[3];
2041 VectorCopy(p->normal, normal);
2042 VectorVectorsDouble(normal, vright, vup);
2044 VectorScale(vup, 1024.0*1024.0*1024.0, vup);
2045 VectorScale(vright, 1024.0*1024.0*1024.0, vright);
2047 // project a really big axis aligned box onto the plane
2050 VectorScale(p->normal, p->dist, org);
2052 VectorSubtract(org, vright, w->points[0]);
2053 VectorAdd(w->points[0], vup, w->points[0]);
2055 VectorAdd(org, vright, w->points[1]);
2056 VectorAdd(w->points[1], vup, w->points[1]);
2058 VectorAdd(org, vright, w->points[2]);
2059 VectorSubtract(w->points[2], vup, w->points[2]);
2061 VectorSubtract(org, vright, w->points[3]);
2062 VectorSubtract(w->points[3], vup, w->points[3]);
2073 Clips the winding to the plane, returning the new winding on the positive side
2074 Frees the input winding.
2075 If keepon is true, an exactly on-plane winding will be saved, otherwise
2076 it will be clipped away.
2079 static winding_t *ClipWinding(winding_t *in, mplane_t *split, int keepon)
2081 double dists[MAX_POINTS_ON_WINDING + 1];
2082 int sides[MAX_POINTS_ON_WINDING + 1];
2091 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2093 // determine sides for each point
2094 for (i = 0;i < in->numpoints;i++)
2096 dists[i] = dot = DotProduct(in->points[i], split->normal) - split->dist;
2097 if (dot > ON_EPSILON)
2098 sides[i] = SIDE_FRONT;
2099 else if (dot < -ON_EPSILON)
2100 sides[i] = SIDE_BACK;
2105 sides[i] = sides[0];
2106 dists[i] = dists[0];
2108 if (keepon && !counts[0] && !counts[1])
2119 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2120 if (maxpts > MAX_POINTS_ON_WINDING)
2121 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2123 neww = NewWinding(maxpts);
2125 for (i = 0;i < in->numpoints;i++)
2127 if (neww->numpoints >= maxpts)
2128 Sys_Error("ClipWinding: points exceeded estimate");
2132 if (sides[i] == SIDE_ON)
2134 VectorCopy(p1, neww->points[neww->numpoints]);
2139 if (sides[i] == SIDE_FRONT)
2141 VectorCopy(p1, neww->points[neww->numpoints]);
2145 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2148 // generate a split point
2149 p2 = in->points[(i+1)%in->numpoints];
2151 dot = dists[i] / (dists[i]-dists[i+1]);
2152 for (j = 0;j < 3;j++)
2153 { // avoid round off error when possible
2154 if (split->normal[j] == 1)
2155 mid[j] = split->dist;
2156 else if (split->normal[j] == -1)
2157 mid[j] = -split->dist;
2159 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2162 VectorCopy(mid, neww->points[neww->numpoints]);
2166 // free the original winding
2177 Divides a winding by a plane, producing one or two windings. The
2178 original winding is not damaged or freed. If only on one side, the
2179 returned winding will be the input winding. If on both sides, two
2180 new windings will be created.
2183 static void DivideWinding(winding_t *in, mplane_t *split, winding_t **front, winding_t **back)
2185 double dists[MAX_POINTS_ON_WINDING + 1];
2186 int sides[MAX_POINTS_ON_WINDING + 1];
2195 counts[SIDE_FRONT] = counts[SIDE_BACK] = counts[SIDE_ON] = 0;
2197 // determine sides for each point
2198 for (i = 0;i < in->numpoints;i++)
2200 dot = DotProduct(in->points[i], split->normal);
2203 if (dot > ON_EPSILON) sides[i] = SIDE_FRONT;
2204 else if (dot < -ON_EPSILON) sides[i] = SIDE_BACK;
2205 else sides[i] = SIDE_ON;
2208 sides[i] = sides[0];
2209 dists[i] = dists[0];
2211 *front = *back = NULL;
2224 maxpts = in->numpoints+4; // can't use counts[0]+2 because of fp grouping errors
2226 if (maxpts > MAX_POINTS_ON_WINDING)
2227 Sys_Error("ClipWinding: maxpts > MAX_POINTS_ON_WINDING");
2229 *front = f = NewWinding(maxpts);
2230 *back = b = NewWinding(maxpts);
2232 for (i = 0;i < in->numpoints;i++)
2234 if (f->numpoints >= maxpts || b->numpoints >= maxpts)
2235 Sys_Error("DivideWinding: points exceeded estimate");
2239 if (sides[i] == SIDE_ON)
2241 VectorCopy(p1, f->points[f->numpoints]);
2243 VectorCopy(p1, b->points[b->numpoints]);
2248 if (sides[i] == SIDE_FRONT)
2250 VectorCopy(p1, f->points[f->numpoints]);
2253 else if (sides[i] == SIDE_BACK)
2255 VectorCopy(p1, b->points[b->numpoints]);
2259 if (sides[i+1] == SIDE_ON || sides[i+1] == sides[i])
2262 // generate a split point
2263 p2 = in->points[(i+1)%in->numpoints];
2265 dot = dists[i] / (dists[i]-dists[i+1]);
2266 for (j = 0;j < 3;j++)
2267 { // avoid round off error when possible
2268 if (split->normal[j] == 1)
2269 mid[j] = split->dist;
2270 else if (split->normal[j] == -1)
2271 mid[j] = -split->dist;
2273 mid[j] = p1[j] + dot* (p2[j]-p1[j]);
2276 VectorCopy(mid, f->points[f->numpoints]);
2278 VectorCopy(mid, b->points[b->numpoints]);
2283 typedef struct portal_s
2286 mnode_t *nodes[2]; // [0] = front side of plane
2287 struct portal_s *next[2];
2289 struct portal_s *chain; // all portals are linked into a list
2293 static portal_t *portalchain;
2300 static portal_t *AllocPortal(void)
2303 p = Mem_Alloc(loadmodel->mempool, sizeof(portal_t));
2304 p->chain = portalchain;
2309 static void FreePortal(portal_t *p)
2314 static void Mod_Q1BSP_RecursiveRecalcNodeBBox(mnode_t *node)
2316 // calculate children first
2317 if (node->children[0]->contents >= 0)
2318 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[0]);
2319 if (node->children[1]->contents >= 0)
2320 Mod_Q1BSP_RecursiveRecalcNodeBBox(node->children[1]);
2322 // make combined bounding box from children
2323 node->mins[0] = min(node->children[0]->mins[0], node->children[1]->mins[0]);
2324 node->mins[1] = min(node->children[0]->mins[1], node->children[1]->mins[1]);
2325 node->mins[2] = min(node->children[0]->mins[2], node->children[1]->mins[2]);
2326 node->maxs[0] = max(node->children[0]->maxs[0], node->children[1]->maxs[0]);
2327 node->maxs[1] = max(node->children[0]->maxs[1], node->children[1]->maxs[1]);
2328 node->maxs[2] = max(node->children[0]->maxs[2], node->children[1]->maxs[2]);
2331 static void Mod_Q1BSP_FinalizePortals(void)
2333 int i, j, numportals, numpoints;
2334 portal_t *p, *pnext;
2337 mleaf_t *leaf, *endleaf;
2340 // recalculate bounding boxes for all leafs(because qbsp is very sloppy)
2341 leaf = loadmodel->brushq1.leafs;
2342 endleaf = leaf + loadmodel->brushq1.numleafs;
2343 for (;leaf < endleaf;leaf++)
2345 VectorSet(leaf->mins, 2000000000, 2000000000, 2000000000);
2346 VectorSet(leaf->maxs, -2000000000, -2000000000, -2000000000);
2353 for (i = 0;i < 2;i++)
2355 leaf = (mleaf_t *)p->nodes[i];
2357 for (j = 0;j < w->numpoints;j++)
2359 if (leaf->mins[0] > w->points[j][0]) leaf->mins[0] = w->points[j][0];
2360 if (leaf->mins[1] > w->points[j][1]) leaf->mins[1] = w->points[j][1];
2361 if (leaf->mins[2] > w->points[j][2]) leaf->mins[2] = w->points[j][2];
2362 if (leaf->maxs[0] < w->points[j][0]) leaf->maxs[0] = w->points[j][0];
2363 if (leaf->maxs[1] < w->points[j][1]) leaf->maxs[1] = w->points[j][1];
2364 if (leaf->maxs[2] < w->points[j][2]) leaf->maxs[2] = w->points[j][2];
2371 Mod_Q1BSP_RecursiveRecalcNodeBBox(loadmodel->brushq1.nodes);
2373 // tally up portal and point counts
2379 // note: this check must match the one below or it will usually corrupt memory
2380 // 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
2381 if (p->winding && p->nodes[0] != p->nodes[1]
2382 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2383 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2386 numpoints += p->winding->numpoints * 2;
2390 loadmodel->brushq1.portals = Mem_Alloc(loadmodel->mempool, numportals * sizeof(mportal_t) + numpoints * sizeof(mvertex_t));
2391 loadmodel->brushq1.numportals = numportals;
2392 loadmodel->brushq1.portalpoints = (void *)((qbyte *) loadmodel->brushq1.portals + numportals * sizeof(mportal_t));
2393 loadmodel->brushq1.numportalpoints = numpoints;
2394 // clear all leaf portal chains
2395 for (i = 0;i < loadmodel->brushq1.numleafs;i++)
2396 loadmodel->brushq1.leafs[i].portals = NULL;
2397 // process all portals in the global portal chain, while freeing them
2398 portal = loadmodel->brushq1.portals;
2399 point = loadmodel->brushq1.portalpoints;
2408 // note: this check must match the one above or it will usually corrupt memory
2409 // 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
2410 if (p->nodes[0] != p->nodes[1]
2411 && p->nodes[0]->contents != CONTENTS_SOLID && p->nodes[1]->contents != CONTENTS_SOLID
2412 && p->nodes[0]->contents != CONTENTS_SKY && p->nodes[1]->contents != CONTENTS_SKY)
2414 // first make the back to front portal(forward portal)
2415 portal->points = point;
2416 portal->numpoints = p->winding->numpoints;
2417 portal->plane.dist = p->plane.dist;
2418 VectorCopy(p->plane.normal, portal->plane.normal);
2419 portal->here = (mleaf_t *)p->nodes[1];
2420 portal->past = (mleaf_t *)p->nodes[0];
2422 for (j = 0;j < portal->numpoints;j++)
2424 VectorCopy(p->winding->points[j], point->position);
2427 PlaneClassify(&portal->plane);
2429 // link into leaf's portal chain
2430 portal->next = portal->here->portals;
2431 portal->here->portals = portal;
2433 // advance to next portal
2436 // then make the front to back portal(backward portal)
2437 portal->points = point;
2438 portal->numpoints = p->winding->numpoints;
2439 portal->plane.dist = -p->plane.dist;
2440 VectorNegate(p->plane.normal, portal->plane.normal);
2441 portal->here = (mleaf_t *)p->nodes[0];
2442 portal->past = (mleaf_t *)p->nodes[1];
2444 for (j = portal->numpoints - 1;j >= 0;j--)
2446 VectorCopy(p->winding->points[j], point->position);
2449 PlaneClassify(&portal->plane);
2451 // link into leaf's portal chain
2452 portal->next = portal->here->portals;
2453 portal->here->portals = portal;
2455 // advance to next portal
2458 FreeWinding(p->winding);
2470 static void AddPortalToNodes(portal_t *p, mnode_t *front, mnode_t *back)
2473 Host_Error("AddPortalToNodes: NULL front node");
2475 Host_Error("AddPortalToNodes: NULL back node");
2476 if (p->nodes[0] || p->nodes[1])
2477 Host_Error("AddPortalToNodes: already included");
2478 // 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
2480 p->nodes[0] = front;
2481 p->next[0] = (portal_t *)front->portals;
2482 front->portals = (mportal_t *)p;
2485 p->next[1] = (portal_t *)back->portals;
2486 back->portals = (mportal_t *)p;
2491 RemovePortalFromNode
2494 static void RemovePortalFromNodes(portal_t *portal)
2498 void **portalpointer;
2500 for (i = 0;i < 2;i++)
2502 node = portal->nodes[i];
2504 portalpointer = (void **) &node->portals;
2509 Host_Error("RemovePortalFromNodes: portal not in leaf");
2513 if (portal->nodes[0] == node)
2515 *portalpointer = portal->next[0];
2516 portal->nodes[0] = NULL;
2518 else if (portal->nodes[1] == node)
2520 *portalpointer = portal->next[1];
2521 portal->nodes[1] = NULL;
2524 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2528 if (t->nodes[0] == node)
2529 portalpointer = (void **) &t->next[0];
2530 else if (t->nodes[1] == node)
2531 portalpointer = (void **) &t->next[1];
2533 Host_Error("RemovePortalFromNodes: portal not bounding leaf");
2538 static void Mod_Q1BSP_RecursiveNodePortals(mnode_t *node)
2541 mnode_t *front, *back, *other_node;
2542 mplane_t clipplane, *plane;
2543 portal_t *portal, *nextportal, *nodeportal, *splitportal, *temp;
2544 winding_t *nodeportalwinding, *frontwinding, *backwinding;
2546 // if a leaf, we're done
2550 plane = node->plane;
2552 front = node->children[0];
2553 back = node->children[1];
2555 Host_Error("Mod_Q1BSP_RecursiveNodePortals: corrupt node hierarchy");
2557 // create the new portal by generating a polygon for the node plane,
2558 // and clipping it by all of the other portals(which came from nodes above this one)
2559 nodeportal = AllocPortal();
2560 nodeportal->plane = *node->plane;
2562 nodeportalwinding = BaseWindingForPlane(node->plane);
2563 side = 0; // shut up compiler warning
2564 for (portal = (portal_t *)node->portals;portal;portal = portal->next[side])
2566 clipplane = portal->plane;
2567 if (portal->nodes[0] == portal->nodes[1])
2568 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(1)");
2569 if (portal->nodes[0] == node)
2571 else if (portal->nodes[1] == node)
2573 clipplane.dist = -clipplane.dist;
2574 VectorNegate(clipplane.normal, clipplane.normal);
2578 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2580 nodeportalwinding = ClipWinding(nodeportalwinding, &clipplane, true);
2581 if (!nodeportalwinding)
2583 Con_Printf("Mod_Q1BSP_RecursiveNodePortals: WARNING: new portal was clipped away\n");
2588 if (nodeportalwinding)
2590 // if the plane was not clipped on all sides, there was an error
2591 nodeportal->winding = nodeportalwinding;
2592 AddPortalToNodes(nodeportal, front, back);
2595 // split the portals of this node along this node's plane and assign them to the children of this node
2596 // (migrating the portals downward through the tree)
2597 for (portal = (portal_t *)node->portals;portal;portal = nextportal)
2599 if (portal->nodes[0] == portal->nodes[1])
2600 Host_Error("Mod_Q1BSP_RecursiveNodePortals: portal has same node on both sides(2)");
2601 if (portal->nodes[0] == node)
2603 else if (portal->nodes[1] == node)
2606 Host_Error("Mod_Q1BSP_RecursiveNodePortals: mislinked portal");
2607 nextportal = portal->next[side];
2609 other_node = portal->nodes[!side];
2610 RemovePortalFromNodes(portal);
2612 // cut the portal into two portals, one on each side of the node plane
2613 DivideWinding(portal->winding, plane, &frontwinding, &backwinding);
2618 AddPortalToNodes(portal, back, other_node);
2620 AddPortalToNodes(portal, other_node, back);
2626 AddPortalToNodes(portal, front, other_node);
2628 AddPortalToNodes(portal, other_node, front);
2632 // the winding is split
2633 splitportal = AllocPortal();
2634 temp = splitportal->chain;
2635 *splitportal = *portal;
2636 splitportal->chain = temp;
2637 splitportal->winding = backwinding;
2638 FreeWinding(portal->winding);
2639 portal->winding = frontwinding;
2643 AddPortalToNodes(portal, front, other_node);
2644 AddPortalToNodes(splitportal, back, other_node);
2648 AddPortalToNodes(portal, other_node, front);
2649 AddPortalToNodes(splitportal, other_node, back);
2653 Mod_Q1BSP_RecursiveNodePortals(front);
2654 Mod_Q1BSP_RecursiveNodePortals(back);
2658 static void Mod_Q1BSP_MakePortals(void)
2661 Mod_Q1BSP_RecursiveNodePortals(loadmodel->brushq1.nodes);
2662 Mod_Q1BSP_FinalizePortals();
2665 static void Mod_Q1BSP_BuildSurfaceNeighbors(msurface_t *surfaces, int numsurfaces, mempool_t *mempool)
2668 int surfnum, vertnum, vertnum2, snum, vnum, vnum2;
2669 msurface_t *surf, *s;
2670 float *v0, *v1, *v2, *v3;
2671 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2672 surf->neighborsurfaces = Mem_Alloc(mempool, surf->poly_numverts * sizeof(msurface_t *));
2673 for (surf = surfaces, surfnum = 0;surfnum < numsurfaces;surf++, surfnum++)
2675 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)
2677 if (surf->neighborsurfaces[vertnum])
2679 surf->neighborsurfaces[vertnum] = NULL;
2680 for (s = surfaces, snum = 0;snum < numsurfaces;s++, snum++)
2682 if (s->poly_mins[0] > (surf->poly_maxs[0] + 1) || s->poly_maxs[0] < (surf->poly_mins[0] - 1)
2683 || s->poly_mins[1] > (surf->poly_maxs[1] + 1) || s->poly_maxs[1] < (surf->poly_mins[1] - 1)
2684 || s->poly_mins[2] > (surf->poly_maxs[2] + 1) || s->poly_maxs[2] < (surf->poly_mins[2] - 1)
2687 for (vnum = 0;vnum < s->poly_numverts;vnum++)
2688 if (s->neighborsurfaces[vnum] == surf)
2690 if (vnum < s->poly_numverts)
2692 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)
2694 if (s->neighborsurfaces[vnum] == NULL
2695 && ((v0[0] == v2[0] && v0[1] == v2[1] && v0[2] == v2[2] && v1[0] == v3[0] && v1[1] == v3[1] && v1[2] == v3[2])
2696 || (v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v0[0] == v3[0] && v0[1] == v3[1] && v0[2] == v3[2])))
2698 surf->neighborsurfaces[vertnum] = s;
2699 s->neighborsurfaces[vnum] = surf;
2703 if (vnum < s->poly_numverts)
2711 static void Mod_Q1BSP_BuildLightmapUpdateChains(mempool_t *mempool, model_t *model)
2713 int i, j, stylecounts[256], totalcount, remapstyles[256];
2715 memset(stylecounts, 0, sizeof(stylecounts));
2716 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2718 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2719 for (j = 0;j < MAXLIGHTMAPS;j++)
2720 stylecounts[surf->styles[j]]++;
2723 model->brushq1.light_styles = 0;
2724 for (i = 0;i < 255;i++)
2728 remapstyles[i] = model->brushq1.light_styles++;
2729 totalcount += stylecounts[i] + 1;
2734 model->brushq1.light_style = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(qbyte));
2735 model->brushq1.light_stylevalue = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(int));
2736 model->brushq1.light_styleupdatechains = Mem_Alloc(mempool, model->brushq1.light_styles * sizeof(msurface_t **));
2737 model->brushq1.light_styleupdatechainsbuffer = Mem_Alloc(mempool, totalcount * sizeof(msurface_t *));
2738 model->brushq1.light_styles = 0;
2739 for (i = 0;i < 255;i++)
2741 model->brushq1.light_style[model->brushq1.light_styles++] = i;
2743 for (i = 0;i < model->brushq1.light_styles;i++)
2745 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2746 j += stylecounts[model->brushq1.light_style[i]] + 1;
2748 for (i = 0;i < model->brushq1.nummodelsurfaces;i++)
2750 surf = model->brushq1.surfaces + model->brushq1.firstmodelsurface + i;
2751 for (j = 0;j < MAXLIGHTMAPS;j++)
2752 if (surf->styles[j] != 255)
2753 *model->brushq1.light_styleupdatechains[remapstyles[surf->styles[j]]]++ = surf;
2756 for (i = 0;i < model->brushq1.light_styles;i++)
2758 *model->brushq1.light_styleupdatechains[i] = NULL;
2759 model->brushq1.light_styleupdatechains[i] = model->brushq1.light_styleupdatechainsbuffer + j;
2760 j += stylecounts[model->brushq1.light_style[i]] + 1;
2764 static void Mod_Q1BSP_BuildPVSTextureChains(model_t *model)
2767 for (i = 0;i < model->brushq1.numtextures;i++)
2768 model->brushq1.pvstexturechainslength[i] = 0;
2769 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2771 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2773 model->brushq1.pvssurflist[model->brushq1.pvssurflistlength++] = j;
2774 model->brushq1.pvstexturechainslength[model->brushq1.surfaces[j].texinfo->texture->number]++;
2777 for (i = 0, j = 0;i < model->brushq1.numtextures;i++)
2779 if (model->brushq1.pvstexturechainslength[i])
2781 model->brushq1.pvstexturechains[i] = model->brushq1.pvstexturechainsbuffer + j;
2782 j += model->brushq1.pvstexturechainslength[i] + 1;
2785 model->brushq1.pvstexturechains[i] = NULL;
2787 for (i = 0, j = model->brushq1.firstmodelsurface;i < model->brushq1.nummodelsurfaces;i++, j++)
2788 if (model->brushq1.surfacepvsframes[j] == model->brushq1.pvsframecount)
2789 *model->brushq1.pvstexturechains[model->brushq1.surfaces[j].texinfo->texture->number]++ = model->brushq1.surfaces + j;
2790 for (i = 0;i < model->brushq1.numtextures;i++)
2792 if (model->brushq1.pvstexturechainslength[i])
2794 *model->brushq1.pvstexturechains[i] = NULL;
2795 model->brushq1.pvstexturechains[i] -= model->brushq1.pvstexturechainslength[i];
2800 void Mod_Q1BSP_FatPVS_RecursiveBSPNode(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbytes, mnode_t *node)
2808 // if this is a leaf, accumulate the pvs bits
2809 if (node->contents < 0)
2811 if (node->contents != CONTENTS_SOLID && ((mleaf_t *)node)->pvsdata)
2812 for (i = 0;i < pvsbytes;i++)
2813 pvsbuffer[i] |= ((mleaf_t *)node)->pvsdata[i];
2817 plane = node->plane;
2818 d = DotProduct(org, plane->normal) - plane->dist;
2820 node = node->children[0];
2821 else if (d < -radius)
2822 node = node->children[1];
2825 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, pvsbytes, node->children[0]);
2826 node = node->children[1];
2831 //Calculates a PVS that is the inclusive or of all leafs within radius pixels
2832 //of the given point.
2833 int Mod_Q1BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
2835 int bytes = ((model->brushq1.numleafs - 1) + 7) >> 3;
2836 bytes = min(bytes, pvsbufferlength);
2837 memset(pvsbuffer, 0, bytes);
2838 Mod_Q1BSP_FatPVS_RecursiveBSPNode(model, org, radius, pvsbuffer, bytes, sv.worldmodel->brushq1.nodes);
2842 static void Mod_Q1BSP_RoundUpToHullSize(model_t *cmodel, const vec3_t inmins, const vec3_t inmaxs, vec3_t outmins, vec3_t outmaxs)
2847 VectorSubtract(inmaxs, inmins, size);
2848 if (cmodel->brush.ishlbsp)
2851 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2852 else if (size[0] <= 32)
2854 if (size[2] < 54) // pick the nearest of 36 or 72
2855 hull = &cmodel->brushq1.hulls[3]; // 32x32x36
2857 hull = &cmodel->brushq1.hulls[1]; // 32x32x72
2860 hull = &cmodel->brushq1.hulls[2]; // 64x64x64
2865 hull = &cmodel->brushq1.hulls[0]; // 0x0x0
2866 else if (size[0] <= 32)
2867 hull = &cmodel->brushq1.hulls[1]; // 32x32x56
2869 hull = &cmodel->brushq1.hulls[2]; // 64x64x88
2871 VectorCopy(inmins, outmins);
2872 VectorAdd(inmins, hull->clip_size, outmaxs);
2875 extern void R_Model_Brush_DrawSky(entity_render_t *ent);
2876 extern void R_Model_Brush_Draw(entity_render_t *ent);
2877 extern void R_Model_Brush_DrawShadowVolume(entity_render_t *ent, vec3_t relativelightorigin, float lightradius);
2878 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);
2879 void Mod_Q1BSP_Load(model_t *mod, void *buffer)
2884 mempool_t *mainmempool;
2886 model_t *originalloadmodel;
2887 float dist, modelyawradius, modelradius, *vec;
2891 mod->type = mod_brush;
2893 header = (dheader_t *)buffer;
2895 i = LittleLong(header->version);
2896 if (i != BSPVERSION && i != 30)
2897 Host_Error("Mod_Q1BSP_Load: %s has wrong version number(%i should be %i(Quake) or 30(HalfLife))", mod->name, i, BSPVERSION);
2898 mod->brush.ishlbsp = i == 30;
2900 mod->brush.AmbientSoundLevelsForPoint = Mod_Q1BSP_AmbientSoundLevelsForPoint;
2901 mod->brush.FatPVS = Mod_Q1BSP_FatPVS;
2902 mod->brush.BoxTouchingPVS = Mod_Q1BSP_BoxTouchingPVS;
2903 mod->brush.LightPoint = Mod_Q1BSP_LightPoint;
2904 mod->brush.FindNonSolidLocation = Mod_Q1BSP_FindNonSolidLocation;
2905 mod->brush.TraceBox = Mod_Q1BSP_TraceBox;
2906 mod->brush.RoundUpToHullSize = Mod_Q1BSP_RoundUpToHullSize;
2907 mod->brushq1.PointInLeaf = Mod_Q1BSP_PointInLeaf;
2908 mod->brushq1.BuildPVSTextureChains = Mod_Q1BSP_BuildPVSTextureChains;
2910 if (loadmodel->isworldmodel)
2912 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
2913 // until we get a texture for it...
2917 // swap all the lumps
2918 mod_base = (qbyte *)header;
2920 for (i = 0;i < (int) sizeof(dheader_t) / 4;i++)
2921 ((int *)header)[i] = LittleLong(((int *)header)[i]);
2925 // store which lightmap format to use
2926 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
2928 Mod_Q1BSP_LoadEntities(&header->lumps[LUMP_ENTITIES]);
2929 Mod_Q1BSP_LoadVertexes(&header->lumps[LUMP_VERTEXES]);
2930 Mod_Q1BSP_LoadEdges(&header->lumps[LUMP_EDGES]);
2931 Mod_Q1BSP_LoadSurfedges(&header->lumps[LUMP_SURFEDGES]);
2932 Mod_Q1BSP_LoadTextures(&header->lumps[LUMP_TEXTURES]);
2933 Mod_Q1BSP_LoadLighting(&header->lumps[LUMP_LIGHTING]);
2934 Mod_Q1BSP_LoadPlanes(&header->lumps[LUMP_PLANES]);
2935 Mod_Q1BSP_LoadTexinfo(&header->lumps[LUMP_TEXINFO]);
2936 Mod_Q1BSP_LoadFaces(&header->lumps[LUMP_FACES]);
2937 Mod_Q1BSP_LoadMarksurfaces(&header->lumps[LUMP_MARKSURFACES]);
2938 Mod_Q1BSP_LoadVisibility(&header->lumps[LUMP_VISIBILITY]);
2939 Mod_Q1BSP_LoadLeafs(&header->lumps[LUMP_LEAFS]);
2940 Mod_Q1BSP_LoadNodes(&header->lumps[LUMP_NODES]);
2941 Mod_Q1BSP_LoadClipnodes(&header->lumps[LUMP_CLIPNODES]);
2942 Mod_Q1BSP_LoadSubmodels(&header->lumps[LUMP_MODELS]);
2944 if (mod->brushq1.data_compressedpvs)
2945 Mem_Free(mod->brushq1.data_compressedpvs);
2946 mod->brushq1.data_compressedpvs = NULL;
2947 mod->brushq1.num_compressedpvs = 0;
2949 Mod_Q1BSP_MakeHull0();
2950 Mod_Q1BSP_MakePortals();
2952 if (developer.integer)
2953 Con_Printf("Some stats for q1bsp model \"%s\": %i faces, %i nodes, %i leafs, %i visleafs, %i visleafportals\n", loadmodel->name, loadmodel->brushq1.numsurfaces, loadmodel->brushq1.numnodes, loadmodel->brushq1.numleafs, loadmodel->brushq1.numleafs - 1, loadmodel->brushq1.numportals);
2955 mod->numframes = 2; // regular and alternate animation
2957 mainmempool = mod->mempool;
2958 loadname = mod->name;
2960 Mod_Q1BSP_LoadLightList();
2961 originalloadmodel = loadmodel;
2964 // set up the submodels(FIXME: this is confusing)
2966 for (i = 0;i < mod->brush.numsubmodels;i++)
2968 bm = &mod->brushq1.submodels[i];
2970 mod->brushq1.hulls[0].firstclipnode = bm->headnode[0];
2971 for (j=1 ; j<MAX_MAP_HULLS ; j++)
2973 mod->brushq1.hulls[j].firstclipnode = bm->headnode[j];
2974 mod->brushq1.hulls[j].lastclipnode = mod->brushq1.numclipnodes - 1;
2977 mod->brushq1.firstmodelsurface = bm->firstface;
2978 mod->brushq1.nummodelsurfaces = bm->numfaces;
2980 // this gets altered below if sky is used
2981 mod->DrawSky = NULL;
2982 mod->Draw = R_Model_Brush_Draw;
2983 mod->DrawFakeShadow = NULL;
2984 mod->DrawShadowVolume = R_Model_Brush_DrawShadowVolume;
2985 mod->DrawLight = R_Model_Brush_DrawLight;
2986 mod->brushq1.pvstexturechains = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(msurface_t **));
2987 mod->brushq1.pvstexturechainsbuffer = Mem_Alloc(originalloadmodel->mempool,(mod->brushq1.nummodelsurfaces + mod->brushq1.numtextures) * sizeof(msurface_t *));
2988 mod->brushq1.pvstexturechainslength = Mem_Alloc(originalloadmodel->mempool, mod->brushq1.numtextures * sizeof(int));
2989 Mod_Q1BSP_BuildPVSTextureChains(mod);
2990 Mod_Q1BSP_BuildLightmapUpdateChains(originalloadmodel->mempool, mod);
2991 if (mod->brushq1.nummodelsurfaces)
2993 // LordHavoc: calculate bmodel bounding box rather than trusting what it says
2994 mod->normalmins[0] = mod->normalmins[1] = mod->normalmins[2] = 1000000000.0f;
2995 mod->normalmaxs[0] = mod->normalmaxs[1] = mod->normalmaxs[2] = -1000000000.0f;
2998 for (j = 0, surf = &mod->brushq1.surfaces[mod->brushq1.firstmodelsurface];j < mod->brushq1.nummodelsurfaces;j++, surf++)
3000 // we only need to have a drawsky function if it is used(usually only on world model)
3001 if (surf->texinfo->texture->shader == &Cshader_sky)
3002 mod->DrawSky = R_Model_Brush_DrawSky;
3003 // LordHavoc: submodels always clip, even if water
3004 if (mod->brush.numsubmodels - 1)
3005 surf->flags |= SURF_SOLIDCLIP;
3006 // calculate bounding shapes
3007 for (mesh = surf->mesh;mesh;mesh = mesh->chain)
3009 for (k = 0, vec = mesh->vertex3f;k < mesh->numverts;k++, vec += 3)
3011 if (mod->normalmins[0] > vec[0]) mod->normalmins[0] = vec[0];
3012 if (mod->normalmins[1] > vec[1]) mod->normalmins[1] = vec[1];
3013 if (mod->normalmins[2] > vec[2]) mod->normalmins[2] = vec[2];
3014 if (mod->normalmaxs[0] < vec[0]) mod->normalmaxs[0] = vec[0];
3015 if (mod->normalmaxs[1] < vec[1]) mod->normalmaxs[1] = vec[1];
3016 if (mod->normalmaxs[2] < vec[2]) mod->normalmaxs[2] = vec[2];
3017 dist = vec[0]*vec[0]+vec[1]*vec[1];
3018 if (modelyawradius < dist)
3019 modelyawradius = dist;
3020 dist += vec[2]*vec[2];
3021 if (modelradius < dist)
3026 modelyawradius = sqrt(modelyawradius);
3027 modelradius = sqrt(modelradius);
3028 mod->yawmins[0] = mod->yawmins[1] = - (mod->yawmaxs[0] = mod->yawmaxs[1] = modelyawradius);
3029 mod->yawmins[2] = mod->normalmins[2];
3030 mod->yawmaxs[2] = mod->normalmaxs[2];
3031 mod->rotatedmins[0] = mod->rotatedmins[1] = mod->rotatedmins[2] = -modelradius;
3032 mod->rotatedmaxs[0] = mod->rotatedmaxs[1] = mod->rotatedmaxs[2] = modelradius;
3033 mod->radius = modelradius;
3034 mod->radius2 = modelradius * modelradius;
3038 // LordHavoc: empty submodel(lacrima.bsp has such a glitch)
3039 Con_Printf("warning: empty submodel *%i in %s\n", i+1, loadname);
3041 Mod_Q1BSP_BuildSurfaceNeighbors(mod->brushq1.surfaces + mod->brushq1.firstmodelsurface, mod->brushq1.nummodelsurfaces, originalloadmodel->mempool);
3043 mod->brushq1.visleafs = bm->visleafs;
3045 // LordHavoc: only register submodels if it is the world
3046 // (prevents bsp models from replacing world submodels)
3047 if (loadmodel->isworldmodel && i < (mod->brush.numsubmodels - 1))
3050 // duplicate the basic information
3051 sprintf(name, "*%i", i+1);
3052 loadmodel = Mod_FindName(name);
3054 strcpy(loadmodel->name, name);
3055 // textures and memory belong to the main model
3056 loadmodel->texturepool = NULL;
3057 loadmodel->mempool = NULL;
3062 loadmodel = originalloadmodel;
3063 //Mod_Q1BSP_ProcessLightList();
3066 static void Mod_Q2BSP_LoadEntities(lump_t *l)
3070 static void Mod_Q2BSP_LoadPlanes(lump_t *l)
3077 in = (void *)(mod_base + l->fileofs);
3078 if (l->filelen % sizeof(*in))
3079 Host_Error("Mod_Q2BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3080 count = l->filelen / sizeof(*in);
3081 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3084 loadmodel->num = count;
3086 for (i = 0;i < count;i++, in++, out++)
3092 static void Mod_Q2BSP_LoadVertices(lump_t *l)
3099 in = (void *)(mod_base + l->fileofs);
3100 if (l->filelen % sizeof(*in))
3101 Host_Error("Mod_Q2BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3102 count = l->filelen / sizeof(*in);
3103 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3106 loadmodel->num = count;
3108 for (i = 0;i < count;i++, in++, out++)
3114 static void Mod_Q2BSP_LoadVisibility(lump_t *l)
3121 in = (void *)(mod_base + l->fileofs);
3122 if (l->filelen % sizeof(*in))
3123 Host_Error("Mod_Q2BSP_LoadVisibility: funny lump size in %s",loadmodel->name);
3124 count = l->filelen / sizeof(*in);
3125 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3128 loadmodel->num = count;
3130 for (i = 0;i < count;i++, in++, out++)
3136 static void Mod_Q2BSP_LoadNodes(lump_t *l)
3143 in = (void *)(mod_base + l->fileofs);
3144 if (l->filelen % sizeof(*in))
3145 Host_Error("Mod_Q2BSP_LoadNodes: funny lump size in %s",loadmodel->name);
3146 count = l->filelen / sizeof(*in);
3147 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3150 loadmodel->num = count;
3152 for (i = 0;i < count;i++, in++, out++)
3158 static void Mod_Q2BSP_LoadTexInfo(lump_t *l)
3165 in = (void *)(mod_base + l->fileofs);
3166 if (l->filelen % sizeof(*in))
3167 Host_Error("Mod_Q2BSP_LoadTexInfo: funny lump size in %s",loadmodel->name);
3168 count = l->filelen / sizeof(*in);
3169 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3172 loadmodel->num = count;
3174 for (i = 0;i < count;i++, in++, out++)
3180 static void Mod_Q2BSP_LoadFaces(lump_t *l)
3187 in = (void *)(mod_base + l->fileofs);
3188 if (l->filelen % sizeof(*in))
3189 Host_Error("Mod_Q2BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3190 count = l->filelen / sizeof(*in);
3191 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3194 loadmodel->num = count;
3196 for (i = 0;i < count;i++, in++, out++)
3202 static void Mod_Q2BSP_LoadLighting(lump_t *l)
3209 in = (void *)(mod_base + l->fileofs);
3210 if (l->filelen % sizeof(*in))
3211 Host_Error("Mod_Q2BSP_LoadLighting: funny lump size in %s",loadmodel->name);
3212 count = l->filelen / sizeof(*in);
3213 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3216 loadmodel->num = count;
3218 for (i = 0;i < count;i++, in++, out++)
3224 static void Mod_Q2BSP_LoadLeafs(lump_t *l)
3231 in = (void *)(mod_base + l->fileofs);
3232 if (l->filelen % sizeof(*in))
3233 Host_Error("Mod_Q2BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
3234 count = l->filelen / sizeof(*in);
3235 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3238 loadmodel->num = count;
3240 for (i = 0;i < count;i++, in++, out++)
3246 static void Mod_Q2BSP_LoadLeafFaces(lump_t *l)
3253 in = (void *)(mod_base + l->fileofs);
3254 if (l->filelen % sizeof(*in))
3255 Host_Error("Mod_Q2BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
3256 count = l->filelen / sizeof(*in);
3257 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3260 loadmodel->num = count;
3262 for (i = 0;i < count;i++, in++, out++)
3268 static void Mod_Q2BSP_LoadLeafBrushes(lump_t *l)
3275 in = (void *)(mod_base + l->fileofs);
3276 if (l->filelen % sizeof(*in))
3277 Host_Error("Mod_Q2BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
3278 count = l->filelen / sizeof(*in);
3279 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3282 loadmodel->num = count;
3284 for (i = 0;i < count;i++, in++, out++)
3290 static void Mod_Q2BSP_LoadEdges(lump_t *l)
3297 in = (void *)(mod_base + l->fileofs);
3298 if (l->filelen % sizeof(*in))
3299 Host_Error("Mod_Q2BSP_LoadEdges: funny lump size in %s",loadmodel->name);
3300 count = l->filelen / sizeof(*in);
3301 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3304 loadmodel->num = count;
3306 for (i = 0;i < count;i++, in++, out++)
3312 static void Mod_Q2BSP_LoadSurfEdges(lump_t *l)
3319 in = (void *)(mod_base + l->fileofs);
3320 if (l->filelen % sizeof(*in))
3321 Host_Error("Mod_Q2BSP_LoadSurfEdges: funny lump size in %s",loadmodel->name);
3322 count = l->filelen / sizeof(*in);
3323 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3326 loadmodel->num = count;
3328 for (i = 0;i < count;i++, in++, out++)
3334 static void Mod_Q2BSP_LoadBrushes(lump_t *l)
3341 in = (void *)(mod_base + l->fileofs);
3342 if (l->filelen % sizeof(*in))
3343 Host_Error("Mod_Q2BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3344 count = l->filelen / sizeof(*in);
3345 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3348 loadmodel->num = count;
3350 for (i = 0;i < count;i++, in++, out++)
3356 static void Mod_Q2BSP_LoadBrushSides(lump_t *l)
3363 in = (void *)(mod_base + l->fileofs);
3364 if (l->filelen % sizeof(*in))
3365 Host_Error("Mod_Q2BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3366 count = l->filelen / sizeof(*in);
3367 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3370 loadmodel->num = count;
3372 for (i = 0;i < count;i++, in++, out++)
3378 static void Mod_Q2BSP_LoadAreas(lump_t *l)
3385 in = (void *)(mod_base + l->fileofs);
3386 if (l->filelen % sizeof(*in))
3387 Host_Error("Mod_Q2BSP_LoadAreas: funny lump size in %s",loadmodel->name);
3388 count = l->filelen / sizeof(*in);
3389 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3392 loadmodel->num = count;
3394 for (i = 0;i < count;i++, in++, out++)
3400 static void Mod_Q2BSP_LoadAreaPortals(lump_t *l)
3407 in = (void *)(mod_base + l->fileofs);
3408 if (l->filelen % sizeof(*in))
3409 Host_Error("Mod_Q2BSP_LoadAreaPortals: funny lump size in %s",loadmodel->name);
3410 count = l->filelen / sizeof(*in);
3411 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3414 loadmodel->num = count;
3416 for (i = 0;i < count;i++, in++, out++)
3422 static void Mod_Q2BSP_LoadModels(lump_t *l)
3429 in = (void *)(mod_base + l->fileofs);
3430 if (l->filelen % sizeof(*in))
3431 Host_Error("Mod_Q2BSP_LoadModels: funny lump size in %s",loadmodel->name);
3432 count = l->filelen / sizeof(*in);
3433 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3436 loadmodel->num = count;
3438 for (i = 0;i < count;i++, in++, out++)
3444 void Mod_Q2BSP_Load(model_t *mod, void *buffer)
3447 q2dheader_t *header;
3449 Host_Error("Mod_Q2BSP_Load: not yet implemented\n");
3451 mod->type = mod_brushq2;
3453 header = (q2dheader_t *)buffer;
3455 i = LittleLong(header->version);
3456 if (i != Q2BSPVERSION)
3457 Host_Error("Mod_Q2BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q2BSPVERSION);
3458 mod->brush.ishlbsp = false;
3459 if (loadmodel->isworldmodel)
3461 Cvar_SetValue("halflifebsp", mod->brush.ishlbsp);
3462 // until we get a texture for it...
3466 mod_base = (qbyte *)header;
3468 // swap all the lumps
3469 for (i = 0;i < (int) sizeof(*header) / 4;i++)
3470 ((int *)header)[i] = LittleLong(((int *)header)[i]);
3472 // store which lightmap format to use
3473 mod->brushq1.lightmaprgba = r_lightmaprgba.integer;
3475 Mod_Q2BSP_LoadEntities(&header->lumps[Q2LUMP_ENTITIES]);
3476 Mod_Q2BSP_LoadPlanes(&header->lumps[Q2LUMP_PLANES]);
3477 Mod_Q2BSP_LoadVertices(&header->lumps[Q2LUMP_VERTEXES]);
3478 Mod_Q2BSP_LoadVisibility(&header->lumps[Q2LUMP_VISIBILITY]);
3479 Mod_Q2BSP_LoadNodes(&header->lumps[Q2LUMP_NODES]);
3480 Mod_Q2BSP_LoadTexInfo(&header->lumps[Q2LUMP_TEXINFO]);
3481 Mod_Q2BSP_LoadFaces(&header->lumps[Q2LUMP_FACES]);
3482 Mod_Q2BSP_LoadLighting(&header->lumps[Q2LUMP_LIGHTING]);
3483 Mod_Q2BSP_LoadLeafs(&header->lumps[Q2LUMP_LEAFS]);
3484 Mod_Q2BSP_LoadLeafFaces(&header->lumps[Q2LUMP_LEAFFACES]);
3485 Mod_Q2BSP_LoadLeafBrushes(&header->lumps[Q2LUMP_LEAFBRUSHES]);
3486 Mod_Q2BSP_LoadEdges(&header->lumps[Q2LUMP_EDGES]);
3487 Mod_Q2BSP_LoadSurfEdges(&header->lumps[Q2LUMP_SURFEDGES]);
3488 Mod_Q2BSP_LoadBrushes(&header->lumps[Q2LUMP_BRUSHES]);
3489 Mod_Q2BSP_LoadBrushSides(&header->lumps[Q2LUMP_BRUSHSIDES]);
3490 Mod_Q2BSP_LoadAreas(&header->lumps[Q2LUMP_AREAS]);
3491 Mod_Q2BSP_LoadAreaPortals(&header->lumps[Q2LUMP_AREAPORTALS]);
3492 // LordHavoc: must go last because this makes the submodels
3493 Mod_Q2BSP_LoadModels(&header->lumps[Q2LUMP_MODELS]);
3497 static void Mod_Q3BSP_LoadEntities(lump_t *l)
3500 char key[128], value[4096];
3502 loadmodel->brushq3.num_lightgrid_cellsize[0] = 64;
3503 loadmodel->brushq3.num_lightgrid_cellsize[1] = 64;
3504 loadmodel->brushq3.num_lightgrid_cellsize[2] = 128;
3507 loadmodel->brush.entities = Mem_Alloc(loadmodel->mempool, l->filelen);
3508 memcpy(loadmodel->brush.entities, mod_base + l->fileofs, l->filelen);
3509 data = loadmodel->brush.entities;
3510 // some Q3 maps override the lightgrid_cellsize with a worldspawn key
3511 if (data && COM_ParseToken(&data, false) && com_token[0] == '{')
3515 if (!COM_ParseToken(&data, false))
3517 if (com_token[0] == '}')
3518 break; // end of worldspawn
3519 if (com_token[0] == '_')
3520 strcpy(key, com_token + 1);
3522 strcpy(key, com_token);
3523 while (key[strlen(key)-1] == ' ') // remove trailing spaces
3524 key[strlen(key)-1] = 0;
3525 if (!COM_ParseToken(&data, false))
3527 strcpy(value, com_token);
3528 if (!strcmp("gridsize", key))
3530 if (sscanf(value, "%f %f %f", &v[0], &v[1], &v[2]) == 3 && v[0] != 0 && v[1] != 0 && v[2] != 0)
3531 VectorCopy(v, loadmodel->brushq3.num_lightgrid_cellsize);
3537 static void Mod_Q3BSP_LoadTextures(lump_t *l)
3543 in = (void *)(mod_base + l->fileofs);
3544 if (l->filelen % sizeof(*in))
3545 Host_Error("Mod_Q3BSP_LoadTextures: funny lump size in %s",loadmodel->name);
3546 count = l->filelen / sizeof(*in);
3547 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3549 loadmodel->brushq3.data_textures = out;
3550 loadmodel->brushq3.num_textures = count;
3552 for (i = 0;i < count;i++, in++, out++)
3554 strncpy(out->name, in->name, sizeof(out->name) - 1);
3555 out->surfaceflags = LittleLong(in->surfaceflags);
3556 out->contents = LittleLong(in->contents);
3559 Mod_LoadSkinFrame(&out->skin, out->name, TEXF_MIPMAP | TEXF_ALPHA | TEXF_PRECACHE, false, true, true);
3563 static void Mod_Q3BSP_LoadPlanes(lump_t *l)
3569 in = (void *)(mod_base + l->fileofs);
3570 if (l->filelen % sizeof(*in))
3571 Host_Error("Mod_Q3BSP_LoadPlanes: funny lump size in %s",loadmodel->name);
3572 count = l->filelen / sizeof(*in);
3573 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3575 loadmodel->brushq3.data_planes = out;
3576 loadmodel->brushq3.num_planes = count;
3578 for (i = 0;i < count;i++, in++, out++)
3580 out->normal[0] = LittleLong(in->normal[0]);
3581 out->normal[1] = LittleLong(in->normal[1]);
3582 out->normal[2] = LittleLong(in->normal[2]);
3583 out->dist = LittleLong(in->dist);
3588 static void Mod_Q3BSP_LoadBrushSides(lump_t *l)
3591 q3mbrushside_t *out;
3594 in = (void *)(mod_base + l->fileofs);
3595 if (l->filelen % sizeof(*in))
3596 Host_Error("Mod_Q3BSP_LoadBrushSides: funny lump size in %s",loadmodel->name);
3597 count = l->filelen / sizeof(*in);
3598 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3600 loadmodel->brushq3.data_brushsides = out;
3601 loadmodel->brushq3.num_brushsides = count;
3603 for (i = 0;i < count;i++, in++, out++)
3605 n = LittleLong(in->planeindex);
3606 if (n < 0 || n >= loadmodel->brushq3.num_planes)
3607 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
3608 out->plane = loadmodel->brushq3.data_planes + n;
3609 n = LittleLong(in->textureindex);
3610 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3611 Host_Error("Mod_Q3BSP_LoadBrushSides: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3612 out->texture = loadmodel->brushq3.data_textures + n;
3616 static void Mod_Q3BSP_LoadBrushes(lump_t *l)
3620 int i, j, k, m, n, c, count, numpoints, numplanes;
3623 colpointf_t pointsbuf[256*3];
3624 colplanef_t planesbuf[256], colplanef;
3626 in = (void *)(mod_base + l->fileofs);
3627 if (l->filelen % sizeof(*in))
3628 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3629 count = l->filelen / sizeof(*in);
3630 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3632 loadmodel->brushq3.data_brushes = out;
3633 loadmodel->brushq3.num_brushes = count;
3635 for (i = 0;i < count;i++, in++, out++)
3637 n = LittleLong(in->firstbrushside);
3638 c = LittleLong(in->numbrushsides);
3639 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3640 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3641 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3642 out->numbrushsides = c;
3643 n = LittleLong(in->textureindex);
3644 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3645 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3646 out->texture = loadmodel->brushq3.data_textures + n;
3648 // construct a collision brush, which needs points and planes...
3649 // each point and plane should be unique, and they don't refer to
3650 // eachother in any way, so keeping them unique is fairly easy
3653 for (j = 0;j < out->numbrushsides;j++)
3655 // for some reason the planes are all flipped compared to what I
3656 // would expect, so this has to negate them...
3658 // create a huge polygon for the plane
3659 VectorNegate(out->firstbrushside[j].plane->normal, plane.normal);
3660 plane.dist = -out->firstbrushside[j].plane->dist;
3661 w = BaseWindingForPlane(&plane);
3662 // clip it by all other planes
3663 for (k = 0;k < out->numbrushsides && w;k++)
3667 VectorNegate(out->firstbrushside[k].plane->normal, plane.normal);
3668 plane.dist = -out->firstbrushside[k].plane->dist;
3669 w = ClipWinding(w, &plane, true);
3672 // if nothing is left, skip it
3673 // FIXME: should keep count of how many were skipped and report
3674 // it, just for sake of statistics
3677 // add the points uniquely (no duplicates)
3678 for (k = 0;k < w->numpoints;k++)
3680 for (m = 0;m < numpoints;m++)
3681 if (VectorDistance2(w->points[k], pointsbuf[m].v) < DIST_EPSILON)
3685 // check if there are too many and skip the brush
3686 if (numpoints >= 256)
3688 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3690 goto failedtomakecolbrush;
3693 VectorCopy(w->points[k], pointsbuf[numpoints].v);
3697 // add the plane uniquely (no duplicates)
3698 memset(&colplanef, 0, sizeof(colplanef));
3699 VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3700 colplanef.dist = out->firstbrushside[k].plane->dist;
3701 for (k = 0;k < numplanes;k++)
3702 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3706 // check if there are too many and skip the brush
3707 if (numplanes >= 256)
3709 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3711 goto failedtomakecolbrush;
3714 planesbuf[numplanes++] = colplanef;
3718 // if anything is left, create the collision brush
3719 if (numplanes && numpoints)
3721 out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3722 memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(colpointf_t));
3723 memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(colplanef_t));
3725 // return from errors to here
3726 failedtomakecolbrush:;
3730 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3736 in = (void *)(mod_base + l->fileofs);
3737 if (l->filelen % sizeof(*in))
3738 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3739 count = l->filelen / sizeof(*in);
3740 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3742 loadmodel->brushq3.data_effects = out;
3743 loadmodel->brushq3.num_effects = count;
3745 for (i = 0;i < count;i++, in++, out++)
3747 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3748 n = LittleLong(in->brushindex);
3749 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3750 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3751 out->brush = loadmodel->brushq3.data_brushes + n;
3752 out->unknown = LittleLong(in->unknown);
3756 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3761 in = (void *)(mod_base + l->fileofs);
3762 if (l->filelen % sizeof(*in))
3763 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3764 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3765 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3766 loadmodel->brushq3.data_texcoordtexture2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3767 loadmodel->brushq3.data_texcoordlightmap2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3768 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3769 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3770 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3771 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3773 for (i = 0;i < count;i++, in++)
3775 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3776 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3777 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3778 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3779 loadmodel->brushq3.data_texcoordtexture2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3780 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3781 loadmodel->brushq3.data_texcoordlightmap2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3782 // svector/tvector are calculated later in face loading
3783 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3784 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3785 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3786 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3787 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3788 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3789 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3790 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3791 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3792 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3793 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3794 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3795 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3799 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3805 in = (void *)(mod_base + l->fileofs);
3806 if (l->filelen % sizeof(int[3]))
3807 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3808 count = l->filelen / sizeof(*in);
3809 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3811 loadmodel->brushq3.num_triangles = count / 3;
3812 loadmodel->brushq3.data_element3i = out;
3813 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3815 for (i = 0;i < count;i++, in++, out++)
3817 *out = LittleLong(*in);
3818 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3819 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
3823 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3829 in = (void *)(mod_base + l->fileofs);
3830 if (l->filelen % sizeof(*in))
3831 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3832 count = l->filelen / sizeof(*in);
3833 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3835 loadmodel->brushq3.data_lightmaps = out;
3836 loadmodel->brushq3.num_lightmaps = count;
3838 for (i = 0;i < count;i++, in++, out++)
3839 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3842 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3846 int i, j, n, count, invalidelements, patchsize[2];
3848 in = (void *)(mod_base + l->fileofs);
3849 if (l->filelen % sizeof(*in))
3850 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3851 count = l->filelen / sizeof(*in);
3852 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3854 loadmodel->brushq3.data_faces = out;
3855 loadmodel->brushq3.num_faces = count;
3857 for (i = 0;i < count;i++, in++, out++)
3859 // check face type first
3860 out->type = LittleLong(in->type);
3861 if (out->type != Q3FACETYPE_POLYGON
3862 && out->type != Q3FACETYPE_PATCH
3863 && out->type != Q3FACETYPE_MESH
3864 && out->type != Q3FACETYPE_FLARE)
3866 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3867 out->type = 0; // error
3871 n = LittleLong(in->textureindex);
3872 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3874 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3875 out->type = 0; // error
3879 out->texture = loadmodel->brushq3.data_textures + n;
3880 n = LittleLong(in->effectindex);
3881 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3883 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3889 out->effect = loadmodel->brushq3.data_effects + n;
3890 n = LittleLong(in->lightmapindex);
3891 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3893 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3897 out->lightmaptexture = NULL;
3899 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3901 out->firstvertex = LittleLong(in->firstvertex);
3902 out->numvertices = LittleLong(in->numvertices);
3903 out->firstelement = LittleLong(in->firstelement);
3904 out->numelements = LittleLong(in->numelements);
3905 out->numtriangles = out->numelements / 3;
3906 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3908 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);
3909 out->type = 0; // error
3912 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3914 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);
3915 out->type = 0; // error
3918 if (out->numtriangles * 3 != out->numelements)
3920 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3921 out->type = 0; // error
3926 case Q3FACETYPE_POLYGON:
3927 case Q3FACETYPE_MESH:
3928 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3929 out->data_texcoordtexture2f = loadmodel->brushq3.data_texcoordtexture2f + out->firstvertex * 2;
3930 out->data_texcoordlightmap2f = loadmodel->brushq3.data_texcoordlightmap2f + out->firstvertex * 2;
3931 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3932 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3933 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3934 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3935 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3936 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3938 case Q3FACETYPE_PATCH:
3939 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3940 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3941 if (patchsize[0] < 1 || patchsize[1] < 1)
3943 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3944 out->type = 0; // error
3947 // FIXME: convert patch to triangles here!
3948 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
3952 case Q3FACETYPE_FLARE:
3953 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3958 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3959 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3961 if (invalidelements)
3963 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);
3964 for (j = 0;j < out->numelements;j++)
3966 Con_Printf(" %i", out->data_element3i[j]);
3967 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3968 out->data_element3i[j] = 0;
3975 static void Mod_Q3BSP_LoadModels(lump_t *l)
3979 int i, j, n, c, count;
3981 in = (void *)(mod_base + l->fileofs);
3982 if (l->filelen % sizeof(*in))
3983 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3984 count = l->filelen / sizeof(*in);
3985 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3987 loadmodel->brushq3.data_models = out;
3988 loadmodel->brushq3.num_models = count;
3990 for (i = 0;i < count;i++, in++, out++)
3992 for (j = 0;j < 3;j++)
3994 out->mins[j] = LittleFloat(in->mins[j]);
3995 out->maxs[j] = LittleFloat(in->maxs[j]);
3997 n = LittleLong(in->firstface);
3998 c = LittleLong(in->numfaces);
3999 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
4000 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
4001 out->firstface = loadmodel->brushq3.data_faces + n;
4003 n = LittleLong(in->firstbrush);
4004 c = LittleLong(in->numbrushes);
4005 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
4006 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
4007 out->firstbrush = loadmodel->brushq3.data_brushes + n;
4008 out->numbrushes = c;
4012 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4018 in = (void *)(mod_base + l->fileofs);
4019 if (l->filelen % sizeof(*in))
4020 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4021 count = l->filelen / sizeof(*in);
4022 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4024 loadmodel->brushq3.data_leafbrushes = out;
4025 loadmodel->brushq3.num_leafbrushes = count;
4027 for (i = 0;i < count;i++, in++, out++)
4029 n = LittleLong(*in);
4030 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4031 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4032 *out = loadmodel->brushq3.data_brushes + n;
4036 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4042 in = (void *)(mod_base + l->fileofs);
4043 if (l->filelen % sizeof(*in))
4044 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4045 count = l->filelen / sizeof(*in);
4046 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4048 loadmodel->brushq3.data_leaffaces = out;
4049 loadmodel->brushq3.num_leaffaces = count;
4051 for (i = 0;i < count;i++, in++, out++)
4053 n = LittleLong(*in);
4054 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4055 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4056 *out = loadmodel->brushq3.data_faces + n;
4060 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4064 int i, j, n, c, count;
4066 in = (void *)(mod_base + l->fileofs);
4067 if (l->filelen % sizeof(*in))
4068 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4069 count = l->filelen / sizeof(*in);
4070 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4072 loadmodel->brushq3.data_leafs = out;
4073 loadmodel->brushq3.num_leafs = count;
4075 for (i = 0;i < count;i++, in++, out++)
4077 out->isnode = false;
4079 out->clusterindex = LittleLong(in->clusterindex);
4080 out->areaindex = LittleLong(in->areaindex);
4081 for (j = 0;j < 3;j++)
4083 // yes the mins/maxs are ints
4084 out->mins[j] = LittleLong(in->mins[j]);
4085 out->maxs[j] = LittleLong(in->maxs[j]);
4087 n = LittleLong(in->firstleafface);
4088 c = LittleLong(in->numleaffaces);
4089 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4090 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4091 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4092 out->numleaffaces = c;
4093 n = LittleLong(in->firstleafbrush);
4094 c = LittleLong(in->numleafbrushes);
4095 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4096 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4097 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4098 out->numleafbrushes = c;
4102 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4105 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4106 node->parent = parent;
4109 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4110 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4114 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4120 in = (void *)(mod_base + l->fileofs);
4121 if (l->filelen % sizeof(*in))
4122 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4123 count = l->filelen / sizeof(*in);
4124 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4126 loadmodel->brushq3.data_nodes = out;
4127 loadmodel->brushq3.num_nodes = count;
4129 for (i = 0;i < count;i++, in++, out++)
4133 n = LittleLong(in->planeindex);
4134 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4135 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4136 out->plane = loadmodel->brushq3.data_planes + n;
4137 for (j = 0;j < 2;j++)
4139 n = LittleLong(in->childrenindex[j]);
4142 if (n >= loadmodel->brushq3.num_nodes)
4143 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4144 out->children[j] = loadmodel->brushq3.data_nodes + n;
4149 if (n >= loadmodel->brushq3.num_leafs)
4150 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4151 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4154 // we don't load the mins/maxs
4157 // set the parent pointers
4158 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4161 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4164 q3dlightgrid_t *out;
4167 in = (void *)(mod_base + l->fileofs);
4168 if (l->filelen % sizeof(*in))
4169 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4170 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4171 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4172 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4173 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4174 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4175 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4176 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4177 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4178 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4179 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4180 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4181 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4182 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4183 if (l->filelen < count * (int)sizeof(*in))
4184 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]);
4185 if (l->filelen != count * (int)sizeof(*in))
4186 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4188 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4189 loadmodel->brushq3.data_lightgrid = out;
4190 loadmodel->brushq3.num_lightgrid = count;
4192 // no swapping or validation necessary
4193 memcpy(out, in, count * (int)sizeof(*out));
4195 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]);
4196 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]);
4199 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4204 in = (void *)(mod_base + l->fileofs);
4206 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4208 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4209 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4210 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4211 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4212 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4213 if (l->filelen < totalchains + (int)sizeof(*in))
4214 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);
4216 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4217 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4220 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4222 // FIXME: finish this code
4223 VectorCopy(in, out);
4226 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4230 // recurse down node sides
4233 colpointf_t *ps, *pe;
4234 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4235 // node planes would need to be transformed too
4236 dist = node->plane->dist - (1.0f / 8.0f);
4237 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4239 if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4241 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4245 dist = node->plane->dist + (1.0f / 8.0f);
4246 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4248 if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4250 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4255 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4257 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4259 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4266 leaf = (q3mleaf_t *)node;
4267 for (i = 0;i < leaf->numleafbrushes;i++)
4268 if (leaf->firstleafbrush[i]->colbrushf)
4269 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4273 void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4275 // FIXME: write this
4276 ambientcolor[0] += 255;
4277 ambientcolor[1] += 255;
4278 ambientcolor[2] += 255;
4281 void Mod_Q3BSP_TraceBox(model_t *model, trace_t *trace, const vec3_t boxstartmins, const vec3_t boxstartmaxs, const vec3_t boxendmins, const vec3_t boxendmaxs)
4284 colbrushf_t *thisbrush_start, *thisbrush_end;
4285 matrix4x4_t startmatrix, endmatrix;
4286 // FIXME: finish this code
4287 Matrix4x4_CreateIdentity(&startmatrix);
4288 Matrix4x4_CreateIdentity(&endmatrix);
4289 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4290 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4291 memset(trace, 0, sizeof(*trace));
4292 trace->fraction = 1;
4293 if (model->brushq3.num_nodes)
4294 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4296 for (i = 0;i < model->brushq3.num_brushes;i++)
4297 if (model->brushq3.data_brushes[i].colbrushf)
4298 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4302 static int Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(const model_t *model, const q3mnode_t *node, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4309 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4310 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4313 // node - recurse down the BSP tree
4314 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4317 node = node->children[0];
4320 node = node->children[1];
4322 default: // crossing
4323 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4325 node = node->children[1];
4332 int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4334 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4337 int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4339 // FIXME: write this
4340 memset(pvsbuffer, 0xFF, pvsbufferlength);
4341 return pvsbufferlength;
4344 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4345 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4346 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4347 //extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4348 //extern void R_Q3BSP_DrawLight(struct entity_render_s *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);
4349 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4352 q3dheader_t *header;
4354 mod->type = mod_brushq3;
4356 header = (q3dheader_t *)buffer;
4358 i = LittleLong(header->version);
4359 if (i != Q3BSPVERSION)
4360 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4361 if (loadmodel->isworldmodel)
4363 Cvar_SetValue("halflifebsp", false);
4364 // until we get a texture for it...
4368 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4369 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4370 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4371 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4372 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4373 //mod->DrawSky = R_Q3BSP_DrawSky;
4374 mod->Draw = R_Q3BSP_Draw;
4375 //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4376 //mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4377 //mod->DrawLight = R_Q3BSP_DrawLight;
4379 mod_base = (qbyte *)header;
4381 // swap all the lumps
4382 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4383 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4385 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4386 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4387 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4388 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4389 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4390 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4391 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4392 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4393 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4394 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4395 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4396 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4397 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4398 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4399 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4400 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4401 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4402 loadmodel->brush.numsubmodels = loadmodel->brushq3.num_models;
4404 for (i = 0;i < loadmodel->brushq3.num_models;i++)
4411 // LordHavoc: only register submodels if it is the world
4412 // (prevents bsp models from replacing world submodels)
4413 if (!loadmodel->isworldmodel)
4415 // duplicate the basic information
4416 sprintf(name, "*%i", i);
4417 mod = Mod_FindName(name);
4419 strcpy(mod->name, name);
4420 // textures and memory belong to the main model
4421 mod->texturepool = NULL;
4422 mod->mempool = NULL;
4424 mod->brushq3.data_thismodel = loadmodel->brushq3.data_models + i;
4428 void Mod_IBSP_Load(model_t *mod, void *buffer)
4430 int i = LittleLong(((int *)buffer)[1]);
4431 if (i == Q3BSPVERSION)
4432 Mod_Q3BSP_Load(mod,buffer);
4433 else if (i == Q2BSPVERSION)
4434 Mod_Q2BSP_Load(mod,buffer);
4436 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4439 void Mod_MAP_Load(model_t *mod, void *buffer)
4441 Host_Error("Mod_MAP_Load: not yet implemented\n");