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;
3622 colpointf_t pointsbuf[256*3];
3623 colplanef_t planesbuf[256], colplanef;
3625 in = (void *)(mod_base + l->fileofs);
3626 if (l->filelen % sizeof(*in))
3627 Host_Error("Mod_Q3BSP_LoadBrushes: funny lump size in %s",loadmodel->name);
3628 count = l->filelen / sizeof(*in);
3629 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3631 loadmodel->brushq3.data_brushes = out;
3632 loadmodel->brushq3.num_brushes = count;
3634 for (i = 0;i < count;i++, in++, out++)
3636 n = LittleLong(in->firstbrushside);
3637 c = LittleLong(in->numbrushsides);
3638 if (n < 0 || n + c > loadmodel->brushq3.num_brushsides)
3639 Host_Error("Mod_Q3BSP_LoadBrushes: invalid brushside range %i : %i (%i brushsides)\n", n, n + c, loadmodel->brushq3.num_brushsides);
3640 out->firstbrushside = loadmodel->brushq3.data_brushsides + n;
3641 out->numbrushsides = c;
3642 n = LittleLong(in->textureindex);
3643 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3644 Host_Error("Mod_Q3BSP_LoadBrushes: invalid textureindex %i (%i textures)\n", n, loadmodel->brushq3.num_textures);
3645 out->texture = loadmodel->brushq3.data_textures + n;
3647 // construct a collision brush, which needs points and planes...
3648 // each point and plane should be unique, and they don't refer to
3649 // eachother in any way, so keeping them unique is fairly easy
3652 for (j = 0;j < out->numbrushsides;j++)
3654 // create a huge polygon for the plane
3655 w = BaseWindingForPlane(out->firstbrushside[j].plane);
3656 // clip it by all other planes
3657 for (k = 0;k < out->numbrushsides && w;k++)
3659 w = ClipWinding(w, out->firstbrushside[k].plane, true);
3660 // if nothing is left, skip it
3661 // FIXME: should keep count of how many were skipped and report
3662 // it, just for sake of statistics
3665 // add the points uniquely (no duplicates)
3666 for (k = 0;k < w->numpoints;k++)
3668 for (m = 0;m < numpoints;m++)
3669 if (VectorDistance2(w->points[k * 3], pointsbuf[m * 3].v) < DIST_EPSILON)
3673 // check if there are too many and skip the brush
3674 if (numpoints >= 256)
3676 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many points for buffer\n");
3678 goto failedtomakecolbrush;
3681 VectorCopy(w->points[k * 3], pointsbuf[numpoints * 3].v);
3685 // add the plane uniquely (no duplicates)
3686 memset(&colplanef, 0, sizeof(colplanef));
3687 VectorCopy(out->firstbrushside[k].plane->normal, colplanef.normal);
3688 colplanef.dist = out->firstbrushside[k].plane->dist;
3689 for (k = 0;k < numplanes;k++)
3690 if (VectorCompare(planesbuf[k].normal, colplanef.normal) && planesbuf[k].dist == colplanef.dist)
3694 // check if there are too many and skip the brush
3695 if (numplanes >= 256)
3697 Con_Printf("Mod_Q3BSP_LoadBrushes: failed to build collision brush: too many planes for buffer\n");
3699 goto failedtomakecolbrush;
3702 planesbuf[numplanes++] = colplanef;
3706 // if anything is left, create the collision brush
3707 if (numplanes && numpoints)
3709 out->colbrushf = Collision_AllocBrushFloat(loadmodel->mempool, numpoints, numplanes);
3710 memcpy(out->colbrushf->points, pointsbuf, numpoints * sizeof(float[3]));
3711 memcpy(out->colbrushf->planes, planesbuf, numplanes * sizeof(mplane_t));
3713 // return from errors to here
3714 failedtomakecolbrush:;
3718 static void Mod_Q3BSP_LoadEffects(lump_t *l)
3724 in = (void *)(mod_base + l->fileofs);
3725 if (l->filelen % sizeof(*in))
3726 Host_Error("Mod_Q3BSP_LoadEffects: funny lump size in %s",loadmodel->name);
3727 count = l->filelen / sizeof(*in);
3728 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3730 loadmodel->brushq3.data_effects = out;
3731 loadmodel->brushq3.num_effects = count;
3733 for (i = 0;i < count;i++, in++, out++)
3735 strncpy(out->shadername, in->shadername, sizeof(out->shadername) - 1);
3736 n = LittleLong(in->brushindex);
3737 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
3738 Host_Error("Mod_Q3BSP_LoadEffects: invalid brushindex %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
3739 out->brush = loadmodel->brushq3.data_brushes + n;
3740 out->unknown = LittleLong(in->unknown);
3744 static void Mod_Q3BSP_LoadVertices(lump_t *l)
3749 in = (void *)(mod_base + l->fileofs);
3750 if (l->filelen % sizeof(*in))
3751 Host_Error("Mod_Q3BSP_LoadVertices: funny lump size in %s",loadmodel->name);
3752 loadmodel->brushq3.num_vertices = count = l->filelen / sizeof(*in);
3753 loadmodel->brushq3.data_vertex3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3754 loadmodel->brushq3.data_texturetexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3755 loadmodel->brushq3.data_lightmaptexcoord2f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[2]));
3756 loadmodel->brushq3.data_svector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3757 loadmodel->brushq3.data_tvector3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3758 loadmodel->brushq3.data_normal3f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[3]));
3759 loadmodel->brushq3.data_color4f = Mem_Alloc(loadmodel->mempool, count * sizeof(float[4]));
3761 for (i = 0;i < count;i++, in++)
3763 loadmodel->brushq3.data_vertex3f[i * 3 + 0] = LittleFloat(in->origin3f[0]);
3764 loadmodel->brushq3.data_vertex3f[i * 3 + 1] = LittleFloat(in->origin3f[1]);
3765 loadmodel->brushq3.data_vertex3f[i * 3 + 2] = LittleFloat(in->origin3f[2]);
3766 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 0] = LittleFloat(in->texcoord2f[0]);
3767 loadmodel->brushq3.data_texturetexcoord2f[i * 2 + 1] = LittleFloat(in->texcoord2f[1]);
3768 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 0] = LittleFloat(in->lightmap2f[0]);
3769 loadmodel->brushq3.data_lightmaptexcoord2f[i * 2 + 1] = LittleFloat(in->lightmap2f[1]);
3770 // svector/tvector are calculated later in face loading
3771 loadmodel->brushq3.data_svector3f[i * 3 + 0] = 0;
3772 loadmodel->brushq3.data_svector3f[i * 3 + 1] = 0;
3773 loadmodel->brushq3.data_svector3f[i * 3 + 2] = 0;
3774 loadmodel->brushq3.data_tvector3f[i * 3 + 0] = 0;
3775 loadmodel->brushq3.data_tvector3f[i * 3 + 1] = 0;
3776 loadmodel->brushq3.data_tvector3f[i * 3 + 2] = 0;
3777 loadmodel->brushq3.data_normal3f[i * 3 + 0] = LittleFloat(in->normal3f[0]);
3778 loadmodel->brushq3.data_normal3f[i * 3 + 1] = LittleFloat(in->normal3f[1]);
3779 loadmodel->brushq3.data_normal3f[i * 3 + 2] = LittleFloat(in->normal3f[2]);
3780 loadmodel->brushq3.data_color4f[i * 4 + 0] = in->color4ub[0] * (1.0f / 255.0f);
3781 loadmodel->brushq3.data_color4f[i * 4 + 1] = in->color4ub[1] * (1.0f / 255.0f);
3782 loadmodel->brushq3.data_color4f[i * 4 + 2] = in->color4ub[2] * (1.0f / 255.0f);
3783 loadmodel->brushq3.data_color4f[i * 4 + 3] = in->color4ub[3] * (1.0f / 255.0f);
3787 static void Mod_Q3BSP_LoadTriangles(lump_t *l)
3793 in = (void *)(mod_base + l->fileofs);
3794 if (l->filelen % sizeof(int[3]))
3795 Host_Error("Mod_Q3BSP_LoadTriangles: funny lump size in %s",loadmodel->name);
3796 count = l->filelen / sizeof(*in);
3797 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3799 loadmodel->brushq3.num_triangles = count / 3;
3800 loadmodel->brushq3.data_element3i = out;
3801 loadmodel->brushq3.data_neighbor3i = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3803 for (i = 0;i < count;i++, in++, out++)
3805 *out = LittleLong(*in);
3806 if (*out < 0 || *out >= loadmodel->brushq3.num_vertices)
3807 Host_Error("Mod_Q3BSP_LoadTriangles: invalid vertexindex %i (%i vertices)\n", *out, loadmodel->brushq3.num_vertices);
3811 static void Mod_Q3BSP_LoadLightmaps(lump_t *l)
3817 in = (void *)(mod_base + l->fileofs);
3818 if (l->filelen % sizeof(*in))
3819 Host_Error("Mod_Q3BSP_LoadLightmaps: funny lump size in %s",loadmodel->name);
3820 count = l->filelen / sizeof(*in);
3821 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3823 loadmodel->brushq3.data_lightmaps = out;
3824 loadmodel->brushq3.num_lightmaps = count;
3826 for (i = 0;i < count;i++, in++, out++)
3827 *out = R_LoadTexture2D(loadmodel->texturepool, va("lightmap%04i", i), 128, 128, in->rgb, TEXTYPE_RGB, TEXF_FORCELINEAR | TEXF_PRECACHE, NULL);
3830 static void Mod_Q3BSP_LoadFaces(lump_t *l)
3834 int i, j, n, count, invalidelements, patchsize[2];
3836 in = (void *)(mod_base + l->fileofs);
3837 if (l->filelen % sizeof(*in))
3838 Host_Error("Mod_Q3BSP_LoadFaces: funny lump size in %s",loadmodel->name);
3839 count = l->filelen / sizeof(*in);
3840 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3842 loadmodel->brushq3.data_faces = out;
3843 loadmodel->brushq3.num_faces = count;
3845 for (i = 0;i < count;i++, in++, out++)
3847 // check face type first
3848 out->type = LittleLong(in->type);
3849 if (out->type != Q3FACETYPE_POLYGON
3850 && out->type != Q3FACETYPE_PATCH
3851 && out->type != Q3FACETYPE_MESH
3852 && out->type != Q3FACETYPE_FLARE)
3854 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: unknown face type %i\n", i, out->type);
3855 out->type = 0; // error
3859 n = LittleLong(in->textureindex);
3860 if (n < 0 || n >= loadmodel->brushq3.num_textures)
3862 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i: invalid textureindex %i (%i textures)\n", i, n, loadmodel->brushq3.num_textures);
3863 out->type = 0; // error
3867 out->texture = loadmodel->brushq3.data_textures + n;
3868 n = LittleLong(in->effectindex);
3869 if (n < -1 || n >= loadmodel->brushq3.num_effects)
3871 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid effectindex %i (%i effects)\n", i, out->texture->name, n, loadmodel->brushq3.num_effects);
3877 out->effect = loadmodel->brushq3.data_effects + n;
3878 n = LittleLong(in->lightmapindex);
3879 if (n < -1 || n >= loadmodel->brushq3.num_lightmaps)
3881 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid lightmapindex %i (%i lightmaps)\n", i, out->texture->name, n, loadmodel->brushq3.num_lightmaps);
3885 out->lightmaptexture = NULL;
3887 out->lightmaptexture = loadmodel->brushq3.data_lightmaps[n];
3889 out->firstvertex = LittleLong(in->firstvertex);
3890 out->numvertices = LittleLong(in->numvertices);
3891 out->firstelement = LittleLong(in->firstelement);
3892 out->numelements = LittleLong(in->numelements);
3893 out->numtriangles = out->numelements / 3;
3894 if (out->firstvertex < 0 || out->firstvertex + out->numvertices > loadmodel->brushq3.num_vertices)
3896 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);
3897 out->type = 0; // error
3900 if (out->firstelement < 0 || out->firstelement + out->numelements > loadmodel->brushq3.num_triangles * 3)
3902 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);
3903 out->type = 0; // error
3906 if (out->numtriangles * 3 != out->numelements)
3908 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): numelements %i is not a multiple of 3\n", i, out->texture->name, out->numelements);
3909 out->type = 0; // error
3914 case Q3FACETYPE_POLYGON:
3915 case Q3FACETYPE_MESH:
3916 out->data_vertex3f = loadmodel->brushq3.data_vertex3f + out->firstvertex * 3;
3917 out->data_texturetexcoord2f = loadmodel->brushq3.data_texturetexcoord2f + out->firstvertex * 2;
3918 out->data_lightmaptexcoord2f = loadmodel->brushq3.data_lightmaptexcoord2f + out->firstvertex * 2;
3919 out->data_svector3f = loadmodel->brushq3.data_svector3f + out->firstvertex * 3;
3920 out->data_tvector3f = loadmodel->brushq3.data_tvector3f + out->firstvertex * 3;
3921 out->data_normal3f = loadmodel->brushq3.data_normal3f + out->firstvertex * 3;
3922 out->data_color4f = loadmodel->brushq3.data_color4f + out->firstvertex * 4;
3923 out->data_element3i = loadmodel->brushq3.data_element3i + out->firstelement;
3924 out->data_neighbor3i = loadmodel->brushq3.data_neighbor3i + out->firstelement;
3926 case Q3FACETYPE_PATCH:
3927 patchsize[0] = LittleLong(in->specific.patch.patchsize[0]);
3928 patchsize[1] = LittleLong(in->specific.patch.patchsize[1]);
3929 if (patchsize[0] < 1 || patchsize[1] < 1)
3931 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): invalid patchsize %ix%i\n", i, out->texture->name, patchsize[0], patchsize[1]);
3932 out->type = 0; // error
3935 // FIXME: convert patch to triangles here!
3936 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_PATCH not supported (yet)\n", i, out->texture->name);
3940 case Q3FACETYPE_FLARE:
3941 Con_Printf("Mod_Q3BSP_LoadFaces: face #%i (texture \"%s\"): Q3FACETYPE_FLARE not supported (yet)\n", i, out->texture->name);
3946 for (j = 0, invalidelements = 0;j < out->numelements;j++)
3947 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3949 if (invalidelements)
3951 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);
3952 for (j = 0;j < out->numelements;j++)
3954 Con_Printf(" %i", out->data_element3i[j]);
3955 if (out->data_element3i[j] < 0 || out->data_element3i[j] >= out->numvertices)
3956 out->data_element3i[j] = 0;
3963 static void Mod_Q3BSP_LoadModels(lump_t *l)
3967 int i, j, n, c, count;
3969 in = (void *)(mod_base + l->fileofs);
3970 if (l->filelen % sizeof(*in))
3971 Host_Error("Mod_Q3BSP_LoadModels: funny lump size in %s",loadmodel->name);
3972 count = l->filelen / sizeof(*in);
3973 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
3975 loadmodel->brushq3.data_models = out;
3976 loadmodel->brushq3.num_models = count;
3978 for (i = 0;i < count;i++, in++, out++)
3980 for (j = 0;j < 3;j++)
3982 out->mins[j] = LittleFloat(in->mins[j]);
3983 out->maxs[j] = LittleFloat(in->maxs[j]);
3985 n = LittleLong(in->firstface);
3986 c = LittleLong(in->numfaces);
3987 if (n < 0 || n + c > loadmodel->brushq3.num_faces)
3988 Host_Error("Mod_Q3BSP_LoadModels: invalid face range %i : %i (%i faces)\n", n, n + c, loadmodel->brushq3.num_faces);
3989 out->firstface = loadmodel->brushq3.data_faces + n;
3991 n = LittleLong(in->firstbrush);
3992 c = LittleLong(in->numbrushes);
3993 if (n < 0 || n + c > loadmodel->brushq3.num_brushes)
3994 Host_Error("Mod_Q3BSP_LoadModels: invalid brush range %i : %i (%i brushes)\n", n, n + c, loadmodel->brushq3.num_brushes);
3995 out->firstbrush = loadmodel->brushq3.data_brushes + n;
3996 out->numbrushes = c;
4000 static void Mod_Q3BSP_LoadLeafBrushes(lump_t *l)
4006 in = (void *)(mod_base + l->fileofs);
4007 if (l->filelen % sizeof(*in))
4008 Host_Error("Mod_Q3BSP_LoadLeafBrushes: funny lump size in %s",loadmodel->name);
4009 count = l->filelen / sizeof(*in);
4010 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4012 loadmodel->brushq3.data_leafbrushes = out;
4013 loadmodel->brushq3.num_leafbrushes = count;
4015 for (i = 0;i < count;i++, in++, out++)
4017 n = LittleLong(*in);
4018 if (n < 0 || n >= loadmodel->brushq3.num_brushes)
4019 Host_Error("Mod_Q3BSP_LoadLeafBrushes: invalid brush index %i (%i brushes)\n", n, loadmodel->brushq3.num_brushes);
4020 *out = loadmodel->brushq3.data_brushes + n;
4024 static void Mod_Q3BSP_LoadLeafFaces(lump_t *l)
4030 in = (void *)(mod_base + l->fileofs);
4031 if (l->filelen % sizeof(*in))
4032 Host_Error("Mod_Q3BSP_LoadLeafFaces: funny lump size in %s",loadmodel->name);
4033 count = l->filelen / sizeof(*in);
4034 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4036 loadmodel->brushq3.data_leaffaces = out;
4037 loadmodel->brushq3.num_leaffaces = count;
4039 for (i = 0;i < count;i++, in++, out++)
4041 n = LittleLong(*in);
4042 if (n < 0 || n >= loadmodel->brushq3.num_faces)
4043 Host_Error("Mod_Q3BSP_LoadLeafFaces: invalid face index %i (%i faces)\n", n, loadmodel->brushq3.num_faces);
4044 *out = loadmodel->brushq3.data_faces + n;
4048 static void Mod_Q3BSP_LoadLeafs(lump_t *l)
4052 int i, j, n, c, count;
4054 in = (void *)(mod_base + l->fileofs);
4055 if (l->filelen % sizeof(*in))
4056 Host_Error("Mod_Q3BSP_LoadLeafs: funny lump size in %s",loadmodel->name);
4057 count = l->filelen / sizeof(*in);
4058 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4060 loadmodel->brushq3.data_leafs = out;
4061 loadmodel->brushq3.num_leafs = count;
4063 for (i = 0;i < count;i++, in++, out++)
4065 out->isnode = false;
4067 out->clusterindex = LittleLong(in->clusterindex);
4068 out->areaindex = LittleLong(in->areaindex);
4069 for (j = 0;j < 3;j++)
4071 // yes the mins/maxs are ints
4072 out->mins[j] = LittleLong(in->mins[j]);
4073 out->maxs[j] = LittleLong(in->maxs[j]);
4075 n = LittleLong(in->firstleafface);
4076 c = LittleLong(in->numleaffaces);
4077 if (n < 0 || n + c > loadmodel->brushq3.num_leaffaces)
4078 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafface range %i : %i (%i leaffaces)\n", n, n + c, loadmodel->brushq3.num_leaffaces);
4079 out->firstleafface = loadmodel->brushq3.data_leaffaces + n;
4080 out->numleaffaces = c;
4081 n = LittleLong(in->firstleafbrush);
4082 c = LittleLong(in->numleafbrushes);
4083 if (n < 0 || n + c > loadmodel->brushq3.num_leafbrushes)
4084 Host_Error("Mod_Q3BSP_LoadLeafs: invalid leafbrush range %i : %i (%i leafbrushes)\n", n, n + c, loadmodel->brushq3.num_leafbrushes);
4085 out->firstleafbrush = loadmodel->brushq3.data_leafbrushes + n;
4086 out->numleafbrushes = c;
4090 static void Mod_Q3BSP_LoadNodes_RecursiveSetParent(q3mnode_t *node, q3mnode_t *parent)
4093 Host_Error("Mod_Q3BSP_LoadNodes_RecursiveSetParent: runaway recursion\n");
4094 node->parent = parent;
4097 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[0], node);
4098 Mod_Q3BSP_LoadNodes_RecursiveSetParent(node->children[1], node);
4102 static void Mod_Q3BSP_LoadNodes(lump_t *l)
4108 in = (void *)(mod_base + l->fileofs);
4109 if (l->filelen % sizeof(*in))
4110 Host_Error("Mod_Q3BSP_LoadNodes: funny lump size in %s",loadmodel->name);
4111 count = l->filelen / sizeof(*in);
4112 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4114 loadmodel->brushq3.data_nodes = out;
4115 loadmodel->brushq3.num_nodes = count;
4117 for (i = 0;i < count;i++, in++, out++)
4121 n = LittleLong(in->planeindex);
4122 if (n < 0 || n >= loadmodel->brushq3.num_planes)
4123 Host_Error("Mod_Q3BSP_LoadNodes: invalid planeindex %i (%i planes)\n", n, loadmodel->brushq3.num_planes);
4124 out->plane = loadmodel->brushq3.data_planes + n;
4125 for (j = 0;j < 2;j++)
4127 n = LittleLong(in->childrenindex[j]);
4130 if (n >= loadmodel->brushq3.num_nodes)
4131 Host_Error("Mod_Q3BSP_LoadNodes: invalid child node index %i (%i nodes)\n", n, loadmodel->brushq3.num_nodes);
4132 out->children[j] = loadmodel->brushq3.data_nodes + n;
4137 if (n >= loadmodel->brushq3.num_leafs)
4138 Host_Error("Mod_Q3BSP_LoadNodes: invalid child leaf index %i (%i leafs)\n", n, loadmodel->brushq3.num_leafs);
4139 out->children[j] = (q3mnode_t *)(loadmodel->brushq3.data_leafs + n);
4142 // we don't load the mins/maxs
4145 // set the parent pointers
4146 Mod_Q3BSP_LoadNodes_RecursiveSetParent(loadmodel->brushq3.data_nodes, NULL);
4149 static void Mod_Q3BSP_LoadLightGrid(lump_t *l)
4152 q3dlightgrid_t *out;
4155 in = (void *)(mod_base + l->fileofs);
4156 if (l->filelen % sizeof(*in))
4157 Host_Error("Mod_Q3BSP_LoadLightGrid: funny lump size in %s",loadmodel->name);
4158 loadmodel->brushq3.num_lightgrid_scale[0] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[0];
4159 loadmodel->brushq3.num_lightgrid_scale[1] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[1];
4160 loadmodel->brushq3.num_lightgrid_scale[2] = 1.0f / loadmodel->brushq3.num_lightgrid_cellsize[2];
4161 loadmodel->brushq3.num_lightgrid_imins[0] = ceil(loadmodel->brushq3.data_models->mins[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4162 loadmodel->brushq3.num_lightgrid_imins[1] = ceil(loadmodel->brushq3.data_models->mins[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4163 loadmodel->brushq3.num_lightgrid_imins[2] = ceil(loadmodel->brushq3.data_models->mins[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4164 loadmodel->brushq3.num_lightgrid_imaxs[0] = floor(loadmodel->brushq3.data_models->maxs[0] * loadmodel->brushq3.num_lightgrid_scale[0]);
4165 loadmodel->brushq3.num_lightgrid_imaxs[1] = floor(loadmodel->brushq3.data_models->maxs[1] * loadmodel->brushq3.num_lightgrid_scale[1]);
4166 loadmodel->brushq3.num_lightgrid_imaxs[2] = floor(loadmodel->brushq3.data_models->maxs[2] * loadmodel->brushq3.num_lightgrid_scale[2]);
4167 loadmodel->brushq3.num_lightgrid_isize[0] = loadmodel->brushq3.num_lightgrid_imaxs[0] - loadmodel->brushq3.num_lightgrid_imins[0] + 1;
4168 loadmodel->brushq3.num_lightgrid_isize[1] = loadmodel->brushq3.num_lightgrid_imaxs[1] - loadmodel->brushq3.num_lightgrid_imins[1] + 1;
4169 loadmodel->brushq3.num_lightgrid_isize[2] = loadmodel->brushq3.num_lightgrid_imaxs[2] - loadmodel->brushq3.num_lightgrid_imins[2] + 1;
4170 count = loadmodel->brushq3.num_lightgrid_isize[0] * loadmodel->brushq3.num_lightgrid_isize[1] * loadmodel->brushq3.num_lightgrid_isize[2];
4171 if (l->filelen < count * (int)sizeof(*in))
4172 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]);
4173 if (l->filelen != count * (int)sizeof(*in))
4174 Con_Printf("Mod_Q3BSP_LoadLightGrid: Warning: calculated lightgrid size %i bytes does not match lump size %i\n", count * sizeof(*in), l->filelen);
4176 out = Mem_Alloc(loadmodel->mempool, count * sizeof(*out));
4177 loadmodel->brushq3.data_lightgrid = out;
4178 loadmodel->brushq3.num_lightgrid = count;
4180 // no swapping or validation necessary
4181 memcpy(out, in, count * (int)sizeof(*out));
4183 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]);
4184 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]);
4187 static void Mod_Q3BSP_LoadPVS(lump_t *l)
4192 in = (void *)(mod_base + l->fileofs);
4194 Host_Error("Mod_Q3BSP_LoadPVS: funny lump size in %s",loadmodel->name);
4196 loadmodel->brushq3.num_pvsclusters = LittleLong(in->numclusters);
4197 loadmodel->brushq3.num_pvschainlength = LittleLong(in->chainlength);
4198 if (loadmodel->brushq3.num_pvschainlength < ((loadmodel->brushq3.num_pvsclusters + 7) / 8))
4199 Host_Error("Mod_Q3BSP_LoadPVS: (chainlength = %i) < ((numclusters = %i) + 7) / 8\n", loadmodel->brushq3.num_pvschainlength, loadmodel->brushq3.num_pvsclusters);
4200 totalchains = loadmodel->brushq3.num_pvschainlength * loadmodel->brushq3.num_pvsclusters;
4201 if (l->filelen < totalchains + (int)sizeof(*in))
4202 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);
4204 loadmodel->brushq3.data_pvschains = Mem_Alloc(loadmodel->mempool, totalchains);
4205 memcpy(loadmodel->brushq3.data_pvschains, (qbyte *)(in + 1), totalchains);
4208 void Mod_Q3BSP_FindNonSolidLocation(model_t *model, const vec3_t in, vec3_t out, vec_t radius)
4210 // FIXME: finish this code
4211 VectorCopy(in, out);
4214 void Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace_t *trace, q3mnode_t *node, const colbrushf_t *thisbrush_start, const colbrushf_t *thisbrush_end)
4218 // recurse down node sides
4221 colpointf_t *ps, *pe;
4222 // FIXME? if TraceBrushPolygonTransform were to be made usable, the
4223 // node planes would need to be transformed too
4224 dist = node->plane->dist - (1.0f / 8.0f);
4225 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4227 if (DotProduct(ps->v, node->plane->normal) > dist || DotProduct(pe->v, node->plane->normal) > dist)
4229 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[0], thisbrush_start, thisbrush_end);
4233 dist = node->plane->dist + (1.0f / 8.0f);
4234 for (i = 0, ps = thisbrush_start->points, pe = thisbrush_end->points;i < thisbrush_start->numpoints;i++, ps++, pe++)
4236 if (DotProduct(ps->v, node->plane->normal) < dist || DotProduct(pe->v, node->plane->normal) < dist)
4238 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, node->children[1], thisbrush_start, thisbrush_end);
4243 sides = BoxOnPlaneSide(boxstartmins, boxstartmaxs, node->plane) | BoxOnPlaneSide(boxendmins, boxendmaxs, node->plane);
4245 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[0], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4247 Mod_Q3BSP_TraceBox_RecursiveBSPNode(trace, node->children[1], boxstartmins, boxstartmaxs, boxendmins, boxendmaxs);
4254 leaf = (q3mleaf_t *)node;
4255 for (i = 0;i < leaf->numleafbrushes;i++)
4256 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, leaf->firstleafbrush[i]->colbrushf, leaf->firstleafbrush[i]->colbrushf);
4260 void Mod_Q3BSP_LightPoint(model_t *model, const vec3_t p, vec3_t ambientcolor, vec3_t diffusecolor, vec3_t diffusenormal)
4262 // FIXME: write this
4263 ambientcolor[0] += 255;
4264 ambientcolor[1] += 255;
4265 ambientcolor[2] += 255;
4268 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)
4271 colbrushf_t *thisbrush_start, *thisbrush_end;
4272 matrix4x4_t startmatrix, endmatrix;
4273 // FIXME: finish this code
4274 Matrix4x4_CreateIdentity(&startmatrix);
4275 Matrix4x4_CreateIdentity(&endmatrix);
4276 thisbrush_start = Collision_BrushForBox(&startmatrix, boxstartmins, boxstartmaxs);
4277 thisbrush_end = Collision_BrushForBox(&endmatrix, boxendmins, boxendmaxs);
4278 memset(trace, 0, sizeof(*trace));
4279 trace->fraction = 1;
4280 if (model->brushq3.num_nodes)
4281 Mod_Q3BSP_TraceBrush_RecursiveBSPNode(trace, model->brushq3.data_nodes, thisbrush_start, thisbrush_end);
4283 for (i = 0;i < model->brushq3.num_brushes;i++)
4284 Collision_TraceBrushBrushFloat(trace, thisbrush_start, thisbrush_end, model->brushq3.data_brushes[i].colbrushf, model->brushq3.data_brushes[i].colbrushf);
4288 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)
4295 clusterindex = ((q3mleaf_t *)node)->clusterindex;
4296 return pvs[clusterindex >> 3] & (1 << (clusterindex & 7));
4299 // node - recurse down the BSP tree
4300 switch (BoxOnPlaneSide(mins, maxs, node->plane))
4303 node = node->children[0];
4306 node = node->children[1];
4308 default: // crossing
4309 if (Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, node->children[0], pvs, mins, maxs))
4311 node = node->children[1];
4318 int Mod_Q3BSP_BoxTouchingPVS(model_t *model, const qbyte *pvs, const vec3_t mins, const vec3_t maxs)
4320 return Mod_Q3BSP_BoxTouchingPVS_RecursiveBSPNode(model, model->brushq3.data_nodes, pvs, mins, maxs);
4323 int Mod_Q3BSP_FatPVS(model_t *model, const vec3_t org, vec_t radius, qbyte *pvsbuffer, int pvsbufferlength)
4325 // FIXME: write this
4326 memset(pvsbuffer, 0xFF, pvsbufferlength);
4327 return pvsbufferlength;
4330 //extern void R_Q3BSP_DrawSky(struct entity_render_s *ent);
4331 extern void R_Q3BSP_Draw(struct entity_render_s *ent);
4332 //extern void R_Q3BSP_DrawFakeShadow(struct entity_render_s *ent);
4333 //extern void R_Q3BSP_DrawShadowVolume(struct entity_render_s *ent, vec3_t relativelightorigin, float lightradius);
4334 //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);
4335 void Mod_Q3BSP_Load(model_t *mod, void *buffer)
4338 q3dheader_t *header;
4340 mod->type = mod_brushq2;
4342 header = (q3dheader_t *)buffer;
4344 i = LittleLong(header->version);
4345 if (i != Q3BSPVERSION)
4346 Host_Error("Mod_Q3BSP_Load: %s has wrong version number (%i, should be %i)", mod->name, i, Q3BSPVERSION);
4347 if (loadmodel->isworldmodel)
4349 Cvar_SetValue("halflifebsp", false);
4350 // until we get a texture for it...
4354 mod->brush.FatPVS = Mod_Q3BSP_FatPVS;
4355 mod->brush.BoxTouchingPVS = Mod_Q3BSP_BoxTouchingPVS;
4356 mod->brush.LightPoint = Mod_Q3BSP_LightPoint;
4357 mod->brush.FindNonSolidLocation = Mod_Q3BSP_FindNonSolidLocation;
4358 mod->brush.TraceBox = Mod_Q3BSP_TraceBox;
4359 //mod->DrawSky = R_Q3BSP_DrawSky;
4360 mod->Draw = R_Q3BSP_Draw;
4361 //mod->DrawFakeShadow = R_Q3BSP_DrawFakeShadow;
4362 //mod->DrawShadowVolume = R_Q3BSP_DrawShadowVolume;
4363 //mod->DrawLight = R_Q3BSP_DrawLight;
4365 mod_base = (qbyte *)header;
4367 // swap all the lumps
4368 for (i = 0;i < (int) sizeof(*header) / 4;i++)
4369 ((int *)header)[i] = LittleLong(((int *)header)[i]);
4371 Mod_Q3BSP_LoadEntities(&header->lumps[Q3LUMP_ENTITIES]);
4372 Mod_Q3BSP_LoadTextures(&header->lumps[Q3LUMP_TEXTURES]);
4373 Mod_Q3BSP_LoadPlanes(&header->lumps[Q3LUMP_PLANES]);
4374 Mod_Q3BSP_LoadBrushSides(&header->lumps[Q3LUMP_BRUSHSIDES]);
4375 Mod_Q3BSP_LoadBrushes(&header->lumps[Q3LUMP_BRUSHES]);
4376 Mod_Q3BSP_LoadEffects(&header->lumps[Q3LUMP_EFFECTS]);
4377 Mod_Q3BSP_LoadVertices(&header->lumps[Q3LUMP_VERTICES]);
4378 Mod_Q3BSP_LoadTriangles(&header->lumps[Q3LUMP_TRIANGLES]);
4379 Mod_Q3BSP_LoadLightmaps(&header->lumps[Q3LUMP_LIGHTMAPS]);
4380 Mod_Q3BSP_LoadFaces(&header->lumps[Q3LUMP_FACES]);
4381 Mod_Q3BSP_LoadModels(&header->lumps[Q3LUMP_MODELS]);
4382 Mod_Q3BSP_LoadLeafBrushes(&header->lumps[Q3LUMP_LEAFBRUSHES]);
4383 Mod_Q3BSP_LoadLeafFaces(&header->lumps[Q3LUMP_LEAFFACES]);
4384 Mod_Q3BSP_LoadLeafs(&header->lumps[Q3LUMP_LEAFS]);
4385 Mod_Q3BSP_LoadNodes(&header->lumps[Q3LUMP_NODES]);
4386 Mod_Q3BSP_LoadLightGrid(&header->lumps[Q3LUMP_LIGHTGRID]);
4387 Mod_Q3BSP_LoadPVS(&header->lumps[Q3LUMP_PVS]);
4390 void Mod_IBSP_Load(model_t *mod, void *buffer)
4392 int i = LittleLong(((int *)buffer)[1]);
4393 if (i == Q3BSPVERSION)
4394 Mod_Q3BSP_Load(mod,buffer);
4395 else if (i == Q2BSPVERSION)
4396 Mod_Q2BSP_Load(mod,buffer);
4398 Host_Error("Mod_IBSP_Load: unknown/unsupported version %i\n", i);
4401 void Mod_MAP_Load(model_t *mod, void *buffer)
4403 Host_Error("Mod_MAP_Load: not yet implemented\n");